xref: /openbmc/linux/net/rxrpc/rxkad.c (revision 1a931707ad4a46e79d4ecfee56d8f6e8cc8d4f28)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
217926a79SDavid Howells /* Kerberos-based RxRPC security
317926a79SDavid Howells  *
417926a79SDavid Howells  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
517926a79SDavid Howells  * Written by David Howells (dhowells@redhat.com)
617926a79SDavid Howells  */
717926a79SDavid Howells 
89b6d5398SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
99b6d5398SJoe Perches 
101afe593bSHerbert Xu #include <crypto/skcipher.h>
1117926a79SDavid Howells #include <linux/module.h>
1217926a79SDavid Howells #include <linux/net.h>
1317926a79SDavid Howells #include <linux/skbuff.h>
1417926a79SDavid Howells #include <linux/udp.h>
1517926a79SDavid Howells #include <linux/scatterlist.h>
1617926a79SDavid Howells #include <linux/ctype.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
1812da59fcSDavid Howells #include <linux/key-type.h>
1917926a79SDavid Howells #include <net/sock.h>
2017926a79SDavid Howells #include <net/af_rxrpc.h>
2133941284SDavid Howells #include <keys/rxrpc-type.h>
2217926a79SDavid Howells #include "ar-internal.h"
2317926a79SDavid Howells 
2417926a79SDavid Howells #define RXKAD_VERSION			2
2517926a79SDavid Howells #define MAXKRB5TICKETLEN		1024
2617926a79SDavid Howells #define RXKAD_TKT_TYPE_KERBEROS_V5	256
2717926a79SDavid Howells #define ANAME_SZ			40	/* size of authentication name */
2817926a79SDavid Howells #define INST_SZ				40	/* size of principal's instance */
2917926a79SDavid Howells #define REALM_SZ			40	/* size of principal's auth domain */
3017926a79SDavid Howells #define SNAME_SZ			40	/* size of service name */
31d7d775b1SDavid Howells #define RXKAD_ALIGN			8
3217926a79SDavid Howells 
3317926a79SDavid Howells struct rxkad_level1_hdr {
3417926a79SDavid Howells 	__be32	data_size;	/* true data size (excluding padding) */
3517926a79SDavid Howells };
3617926a79SDavid Howells 
3717926a79SDavid Howells struct rxkad_level2_hdr {
3817926a79SDavid Howells 	__be32	data_size;	/* true data size (excluding padding) */
3917926a79SDavid Howells 	__be32	checksum;	/* decrypted data checksum */
4017926a79SDavid Howells };
4117926a79SDavid Howells 
428d47a43cSDavid Howells static int rxkad_prime_packet_security(struct rxrpc_connection *conn,
438d47a43cSDavid Howells 				       struct crypto_sync_skcipher *ci);
448d47a43cSDavid Howells 
4517926a79SDavid Howells /*
4617926a79SDavid Howells  * this holds a pinned cipher so that keventd doesn't get called by the cipher
4717926a79SDavid Howells  * alloc routine, but since we have it to hand, we use it to decrypt RESPONSE
4817926a79SDavid Howells  * packets
4917926a79SDavid Howells  */
5069d826faSKees Cook static struct crypto_sync_skcipher *rxkad_ci;
511db88c53SDavid Howells static struct skcipher_request *rxkad_ci_req;
5217926a79SDavid Howells static DEFINE_MUTEX(rxkad_ci_mutex);
5317926a79SDavid Howells 
5417926a79SDavid Howells /*
5512da59fcSDavid Howells  * Parse the information from a server key
5612da59fcSDavid Howells  *
5712da59fcSDavid Howells  * The data should be the 8-byte secret key.
5812da59fcSDavid Howells  */
rxkad_preparse_server_key(struct key_preparsed_payload * prep)5912da59fcSDavid Howells static int rxkad_preparse_server_key(struct key_preparsed_payload *prep)
6012da59fcSDavid Howells {
6112da59fcSDavid Howells 	struct crypto_skcipher *ci;
6212da59fcSDavid Howells 
6312da59fcSDavid Howells 	if (prep->datalen != 8)
6412da59fcSDavid Howells 		return -EINVAL;
6512da59fcSDavid Howells 
6612da59fcSDavid Howells 	memcpy(&prep->payload.data[2], prep->data, 8);
6712da59fcSDavid Howells 
6812da59fcSDavid Howells 	ci = crypto_alloc_skcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
6912da59fcSDavid Howells 	if (IS_ERR(ci)) {
7012da59fcSDavid Howells 		_leave(" = %ld", PTR_ERR(ci));
7112da59fcSDavid Howells 		return PTR_ERR(ci);
7212da59fcSDavid Howells 	}
7312da59fcSDavid Howells 
7412da59fcSDavid Howells 	if (crypto_skcipher_setkey(ci, prep->data, 8) < 0)
7512da59fcSDavid Howells 		BUG();
7612da59fcSDavid Howells 
7712da59fcSDavid Howells 	prep->payload.data[0] = ci;
7812da59fcSDavid Howells 	_leave(" = 0");
7912da59fcSDavid Howells 	return 0;
8012da59fcSDavid Howells }
8112da59fcSDavid Howells 
rxkad_free_preparse_server_key(struct key_preparsed_payload * prep)8212da59fcSDavid Howells static void rxkad_free_preparse_server_key(struct key_preparsed_payload *prep)
8312da59fcSDavid Howells {
8412da59fcSDavid Howells 
8512da59fcSDavid Howells 	if (prep->payload.data[0])
8612da59fcSDavid Howells 		crypto_free_skcipher(prep->payload.data[0]);
8712da59fcSDavid Howells }
8812da59fcSDavid Howells 
rxkad_destroy_server_key(struct key * key)8912da59fcSDavid Howells static void rxkad_destroy_server_key(struct key *key)
9012da59fcSDavid Howells {
9112da59fcSDavid Howells 	if (key->payload.data[0]) {
9212da59fcSDavid Howells 		crypto_free_skcipher(key->payload.data[0]);
9312da59fcSDavid Howells 		key->payload.data[0] = NULL;
9412da59fcSDavid Howells 	}
9512da59fcSDavid Howells }
9612da59fcSDavid Howells 
9712da59fcSDavid Howells /*
9817926a79SDavid Howells  * initialise connection security
9917926a79SDavid Howells  */
rxkad_init_connection_security(struct rxrpc_connection * conn,struct rxrpc_key_token * token)10041057ebdSDavid Howells static int rxkad_init_connection_security(struct rxrpc_connection *conn,
10141057ebdSDavid Howells 					  struct rxrpc_key_token *token)
10217926a79SDavid Howells {
10369d826faSKees Cook 	struct crypto_sync_skcipher *ci;
10417926a79SDavid Howells 	int ret;
10517926a79SDavid Howells 
106*2cc80086SDavid Howells 	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
10717926a79SDavid Howells 
10833941284SDavid Howells 	conn->security_ix = token->security_index;
10917926a79SDavid Howells 
11069d826faSKees Cook 	ci = crypto_alloc_sync_skcipher("pcbc(fcrypt)", 0, 0);
11117926a79SDavid Howells 	if (IS_ERR(ci)) {
11217926a79SDavid Howells 		_debug("no cipher");
11317926a79SDavid Howells 		ret = PTR_ERR(ci);
11417926a79SDavid Howells 		goto error;
11517926a79SDavid Howells 	}
11617926a79SDavid Howells 
11769d826faSKees Cook 	if (crypto_sync_skcipher_setkey(ci, token->kad->session_key,
11833941284SDavid Howells 				   sizeof(token->kad->session_key)) < 0)
11917926a79SDavid Howells 		BUG();
12017926a79SDavid Howells 
121*2cc80086SDavid Howells 	switch (conn->security_level) {
12217926a79SDavid Howells 	case RXRPC_SECURITY_PLAIN:
12317926a79SDavid Howells 	case RXRPC_SECURITY_AUTH:
12417926a79SDavid Howells 	case RXRPC_SECURITY_ENCRYPT:
12517926a79SDavid Howells 		break;
12617926a79SDavid Howells 	default:
12717926a79SDavid Howells 		ret = -EKEYREJECTED;
12817926a79SDavid Howells 		goto error;
12917926a79SDavid Howells 	}
13017926a79SDavid Howells 
1318d47a43cSDavid Howells 	ret = rxkad_prime_packet_security(conn, ci);
1328d47a43cSDavid Howells 	if (ret < 0)
1338d47a43cSDavid Howells 		goto error_ci;
1348d47a43cSDavid Howells 
135521bb304SDavid Howells 	conn->rxkad.cipher = ci;
1368d47a43cSDavid Howells 	return 0;
1378d47a43cSDavid Howells 
1388d47a43cSDavid Howells error_ci:
1398d47a43cSDavid Howells 	crypto_free_sync_skcipher(ci);
14017926a79SDavid Howells error:
14117926a79SDavid Howells 	_leave(" = %d", ret);
14217926a79SDavid Howells 	return ret;
14317926a79SDavid Howells }
14417926a79SDavid Howells 
14517926a79SDavid Howells /*
146d7d775b1SDavid Howells  * Work out how much data we can put in a packet.
147d7d775b1SDavid Howells  */
rxkad_how_much_data(struct rxrpc_call * call,size_t remain,size_t * _buf_size,size_t * _data_size,size_t * _offset)148d7d775b1SDavid Howells static int rxkad_how_much_data(struct rxrpc_call *call, size_t remain,
149d7d775b1SDavid Howells 			       size_t *_buf_size, size_t *_data_size, size_t *_offset)
150d7d775b1SDavid Howells {
151d7d775b1SDavid Howells 	size_t shdr, buf_size, chunk;
152d7d775b1SDavid Howells 
153*2cc80086SDavid Howells 	switch (call->conn->security_level) {
154d7d775b1SDavid Howells 	default:
155d7d775b1SDavid Howells 		buf_size = chunk = min_t(size_t, remain, RXRPC_JUMBO_DATALEN);
156d7d775b1SDavid Howells 		shdr = 0;
157d7d775b1SDavid Howells 		goto out;
158d7d775b1SDavid Howells 	case RXRPC_SECURITY_AUTH:
159d7d775b1SDavid Howells 		shdr = sizeof(struct rxkad_level1_hdr);
160d7d775b1SDavid Howells 		break;
161d7d775b1SDavid Howells 	case RXRPC_SECURITY_ENCRYPT:
162d7d775b1SDavid Howells 		shdr = sizeof(struct rxkad_level2_hdr);
163d7d775b1SDavid Howells 		break;
164d7d775b1SDavid Howells 	}
165d7d775b1SDavid Howells 
166d7d775b1SDavid Howells 	buf_size = round_down(RXRPC_JUMBO_DATALEN, RXKAD_ALIGN);
167d7d775b1SDavid Howells 
168d7d775b1SDavid Howells 	chunk = buf_size - shdr;
169d7d775b1SDavid Howells 	if (remain < chunk)
170d7d775b1SDavid Howells 		buf_size = round_up(shdr + remain, RXKAD_ALIGN);
171d7d775b1SDavid Howells 
172d7d775b1SDavid Howells out:
173d7d775b1SDavid Howells 	*_buf_size = buf_size;
174d7d775b1SDavid Howells 	*_data_size = chunk;
175d7d775b1SDavid Howells 	*_offset = shdr;
176d7d775b1SDavid Howells 	return 0;
177d7d775b1SDavid Howells }
178d7d775b1SDavid Howells 
179d7d775b1SDavid Howells /*
18017926a79SDavid Howells  * prime the encryption state with the invariant parts of a connection's
18117926a79SDavid Howells  * description
18217926a79SDavid Howells  */
rxkad_prime_packet_security(struct rxrpc_connection * conn,struct crypto_sync_skcipher * ci)1838d47a43cSDavid Howells static int rxkad_prime_packet_security(struct rxrpc_connection *conn,
1848d47a43cSDavid Howells 				       struct crypto_sync_skcipher *ci)
18517926a79SDavid Howells {
1861db88c53SDavid Howells 	struct skcipher_request *req;
18733941284SDavid Howells 	struct rxrpc_key_token *token;
188a263629dSHerbert Xu 	struct scatterlist sg;
18917926a79SDavid Howells 	struct rxrpc_crypt iv;
190a263629dSHerbert Xu 	__be32 *tmpbuf;
191a263629dSHerbert Xu 	size_t tmpsize = 4 * sizeof(__be32);
19217926a79SDavid Howells 
19317926a79SDavid Howells 	_enter("");
19417926a79SDavid Howells 
195*2cc80086SDavid Howells 	if (!conn->key)
196a263629dSHerbert Xu 		return 0;
197a263629dSHerbert Xu 
198a263629dSHerbert Xu 	tmpbuf = kmalloc(tmpsize, GFP_KERNEL);
199a263629dSHerbert Xu 	if (!tmpbuf)
200a263629dSHerbert Xu 		return -ENOMEM;
20117926a79SDavid Howells 
2028d47a43cSDavid Howells 	req = skcipher_request_alloc(&ci->base, GFP_NOFS);
2031db88c53SDavid Howells 	if (!req) {
2041db88c53SDavid Howells 		kfree(tmpbuf);
2051db88c53SDavid Howells 		return -ENOMEM;
2061db88c53SDavid Howells 	}
2071db88c53SDavid Howells 
208*2cc80086SDavid Howells 	token = conn->key->payload.data[0];
20933941284SDavid Howells 	memcpy(&iv, token->kad->session_key, sizeof(iv));
21017926a79SDavid Howells 
211a263629dSHerbert Xu 	tmpbuf[0] = htonl(conn->proto.epoch);
212a263629dSHerbert Xu 	tmpbuf[1] = htonl(conn->proto.cid);
213a263629dSHerbert Xu 	tmpbuf[2] = 0;
214a263629dSHerbert Xu 	tmpbuf[3] = htonl(conn->security_ix);
21517926a79SDavid Howells 
216a263629dSHerbert Xu 	sg_init_one(&sg, tmpbuf, tmpsize);
2178d47a43cSDavid Howells 	skcipher_request_set_sync_tfm(req, ci);
2181afe593bSHerbert Xu 	skcipher_request_set_callback(req, 0, NULL, NULL);
219a263629dSHerbert Xu 	skcipher_request_set_crypt(req, &sg, &sg, tmpsize, iv.x);
2201afe593bSHerbert Xu 	crypto_skcipher_encrypt(req);
2211db88c53SDavid Howells 	skcipher_request_free(req);
22217926a79SDavid Howells 
223521bb304SDavid Howells 	memcpy(&conn->rxkad.csum_iv, tmpbuf + 2, sizeof(conn->rxkad.csum_iv));
224a263629dSHerbert Xu 	kfree(tmpbuf);
225a263629dSHerbert Xu 	_leave(" = 0");
226a263629dSHerbert Xu 	return 0;
22717926a79SDavid Howells }
22817926a79SDavid Howells 
22917926a79SDavid Howells /*
2301db88c53SDavid Howells  * Allocate and prepare the crypto request on a call.  For any particular call,
2311db88c53SDavid Howells  * this is called serially for the packets, so no lock should be necessary.
2321db88c53SDavid Howells  */
rxkad_get_call_crypto(struct rxrpc_call * call)2331db88c53SDavid Howells static struct skcipher_request *rxkad_get_call_crypto(struct rxrpc_call *call)
2341db88c53SDavid Howells {
235521bb304SDavid Howells 	struct crypto_skcipher *tfm = &call->conn->rxkad.cipher->base;
2361db88c53SDavid Howells 
23730d95efeSDavid Howells 	return skcipher_request_alloc(tfm, GFP_NOFS);
2381db88c53SDavid Howells }
2391db88c53SDavid Howells 
2401db88c53SDavid Howells /*
2411db88c53SDavid Howells  * Clean up the crypto on a call.
2421db88c53SDavid Howells  */
rxkad_free_call_crypto(struct rxrpc_call * call)2431db88c53SDavid Howells static void rxkad_free_call_crypto(struct rxrpc_call *call)
2441db88c53SDavid Howells {
2451db88c53SDavid Howells }
2461db88c53SDavid Howells 
2471db88c53SDavid Howells /*
24817926a79SDavid Howells  * partially encrypt a packet (level 1 security)
24917926a79SDavid Howells  */
rxkad_secure_packet_auth(const struct rxrpc_call * call,struct rxrpc_txbuf * txb,struct skcipher_request * req)25017926a79SDavid Howells static int rxkad_secure_packet_auth(const struct rxrpc_call *call,
251a4ea4c47SDavid Howells 				    struct rxrpc_txbuf *txb,
25254424d38SKees Cook 				    struct skcipher_request *req)
25317926a79SDavid Howells {
254a4ea4c47SDavid Howells 	struct rxkad_level1_hdr *hdr = (void *)txb->data;
255a263629dSHerbert Xu 	struct rxrpc_crypt iv;
256a263629dSHerbert Xu 	struct scatterlist sg;
257d7d775b1SDavid Howells 	size_t pad;
25817926a79SDavid Howells 	u16 check;
25917926a79SDavid Howells 
26017926a79SDavid Howells 	_enter("");
26117926a79SDavid Howells 
262a4ea4c47SDavid Howells 	check = txb->seq ^ call->call_id;
263a4ea4c47SDavid Howells 	hdr->data_size = htonl((u32)check << 16 | txb->len);
26417926a79SDavid Howells 
265a4ea4c47SDavid Howells 	txb->len += sizeof(struct rxkad_level1_hdr);
266a4ea4c47SDavid Howells 	pad = txb->len;
267d7d775b1SDavid Howells 	pad = RXKAD_ALIGN - pad;
268d7d775b1SDavid Howells 	pad &= RXKAD_ALIGN - 1;
269a4ea4c47SDavid Howells 	if (pad) {
270a4ea4c47SDavid Howells 		memset(txb->data + txb->offset, 0, pad);
271a4ea4c47SDavid Howells 		txb->len += pad;
272a4ea4c47SDavid Howells 	}
273d7d775b1SDavid Howells 
27417926a79SDavid Howells 	/* start the encryption afresh */
27517926a79SDavid Howells 	memset(&iv, 0, sizeof(iv));
27617926a79SDavid Howells 
277a4ea4c47SDavid Howells 	sg_init_one(&sg, txb->data, 8);
278521bb304SDavid Howells 	skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
2791afe593bSHerbert Xu 	skcipher_request_set_callback(req, 0, NULL, NULL);
280a263629dSHerbert Xu 	skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x);
2811afe593bSHerbert Xu 	crypto_skcipher_encrypt(req);
2821afe593bSHerbert Xu 	skcipher_request_zero(req);
28317926a79SDavid Howells 
28417926a79SDavid Howells 	_leave(" = 0");
28517926a79SDavid Howells 	return 0;
28617926a79SDavid Howells }
28717926a79SDavid Howells 
28817926a79SDavid Howells /*
28917926a79SDavid Howells  * wholly encrypt a packet (level 2 security)
29017926a79SDavid Howells  */
rxkad_secure_packet_encrypt(const struct rxrpc_call * call,struct rxrpc_txbuf * txb,struct skcipher_request * req)29117926a79SDavid Howells static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call,
292a4ea4c47SDavid Howells 				       struct rxrpc_txbuf *txb,
29354424d38SKees Cook 				       struct skcipher_request *req)
29417926a79SDavid Howells {
29533941284SDavid Howells 	const struct rxrpc_key_token *token;
296a4ea4c47SDavid Howells 	struct rxkad_level2_hdr *rxkhdr = (void *)txb->data;
29717926a79SDavid Howells 	struct rxrpc_crypt iv;
298a4ea4c47SDavid Howells 	struct scatterlist sg;
299d7d775b1SDavid Howells 	size_t pad;
30017926a79SDavid Howells 	u16 check;
301a4ea4c47SDavid Howells 	int ret;
30217926a79SDavid Howells 
30317926a79SDavid Howells 	_enter("");
30417926a79SDavid Howells 
305a4ea4c47SDavid Howells 	check = txb->seq ^ call->call_id;
30617926a79SDavid Howells 
307a4ea4c47SDavid Howells 	rxkhdr->data_size = htonl(txb->len | (u32)check << 16);
308a4ea4c47SDavid Howells 	rxkhdr->checksum = 0;
30917926a79SDavid Howells 
310a4ea4c47SDavid Howells 	txb->len += sizeof(struct rxkad_level2_hdr);
311a4ea4c47SDavid Howells 	pad = txb->len;
312d7d775b1SDavid Howells 	pad = RXKAD_ALIGN - pad;
313d7d775b1SDavid Howells 	pad &= RXKAD_ALIGN - 1;
314a4ea4c47SDavid Howells 	if (pad) {
315a4ea4c47SDavid Howells 		memset(txb->data + txb->offset, 0, pad);
316a4ea4c47SDavid Howells 		txb->len += pad;
317a4ea4c47SDavid Howells 	}
318d7d775b1SDavid Howells 
31917926a79SDavid Howells 	/* encrypt from the session key */
320*2cc80086SDavid Howells 	token = call->conn->key->payload.data[0];
32133941284SDavid Howells 	memcpy(&iv, token->kad->session_key, sizeof(iv));
32217926a79SDavid Howells 
323a4ea4c47SDavid Howells 	sg_init_one(&sg, txb->data, txb->len);
324521bb304SDavid Howells 	skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
3251afe593bSHerbert Xu 	skcipher_request_set_callback(req, 0, NULL, NULL);
326a4ea4c47SDavid Howells 	skcipher_request_set_crypt(req, &sg, &sg, txb->len, iv.x);
327a4ea4c47SDavid Howells 	ret = crypto_skcipher_encrypt(req);
3281afe593bSHerbert Xu 	skcipher_request_zero(req);
329a4ea4c47SDavid Howells 	return ret;
33017926a79SDavid Howells }
33117926a79SDavid Howells 
33217926a79SDavid Howells /*
33317926a79SDavid Howells  * checksum an RxRPC packet header
33417926a79SDavid Howells  */
rxkad_secure_packet(struct rxrpc_call * call,struct rxrpc_txbuf * txb)335a4ea4c47SDavid Howells static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb)
33617926a79SDavid Howells {
3371db88c53SDavid Howells 	struct skcipher_request	*req;
33817926a79SDavid Howells 	struct rxrpc_crypt iv;
339a263629dSHerbert Xu 	struct scatterlist sg;
34030d95efeSDavid Howells 	union {
34130d95efeSDavid Howells 		__be32 buf[2];
34230d95efeSDavid Howells 	} crypto __aligned(8);
3430d12f8a4SDavid Howells 	u32 x, y;
34417926a79SDavid Howells 	int ret;
34517926a79SDavid Howells 
346a4ea4c47SDavid Howells 	_enter("{%d{%x}},{#%u},%u,",
347*2cc80086SDavid Howells 	       call->debug_id, key_serial(call->conn->key),
348a4ea4c47SDavid Howells 	       txb->seq, txb->len);
34917926a79SDavid Howells 
350521bb304SDavid Howells 	if (!call->conn->rxkad.cipher)
35117926a79SDavid Howells 		return 0;
35217926a79SDavid Howells 
353*2cc80086SDavid Howells 	ret = key_validate(call->conn->key);
35417926a79SDavid Howells 	if (ret < 0)
35517926a79SDavid Howells 		return ret;
35617926a79SDavid Howells 
3571db88c53SDavid Howells 	req = rxkad_get_call_crypto(call);
3581db88c53SDavid Howells 	if (!req)
3591db88c53SDavid Howells 		return -ENOMEM;
3601db88c53SDavid Howells 
36117926a79SDavid Howells 	/* continue encrypting from where we left off */
362521bb304SDavid Howells 	memcpy(&iv, call->conn->rxkad.csum_iv.x, sizeof(iv));
36317926a79SDavid Howells 
36417926a79SDavid Howells 	/* calculate the security checksum */
365a4ea4c47SDavid Howells 	x = (call->cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT);
366a4ea4c47SDavid Howells 	x |= txb->seq & 0x3fffffff;
36730d95efeSDavid Howells 	crypto.buf[0] = htonl(call->call_id);
36830d95efeSDavid Howells 	crypto.buf[1] = htonl(x);
36917926a79SDavid Howells 
37030d95efeSDavid Howells 	sg_init_one(&sg, crypto.buf, 8);
371521bb304SDavid Howells 	skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
3721afe593bSHerbert Xu 	skcipher_request_set_callback(req, 0, NULL, NULL);
373a263629dSHerbert Xu 	skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x);
3741afe593bSHerbert Xu 	crypto_skcipher_encrypt(req);
3751afe593bSHerbert Xu 	skcipher_request_zero(req);
37617926a79SDavid Howells 
37730d95efeSDavid Howells 	y = ntohl(crypto.buf[1]);
37891e916cfSAl Viro 	y = (y >> 16) & 0xffff;
37991e916cfSAl Viro 	if (y == 0)
38091e916cfSAl Viro 		y = 1; /* zero checksums are not permitted */
381a4ea4c47SDavid Howells 	txb->wire.cksum = htons(y);
38217926a79SDavid Howells 
383*2cc80086SDavid Howells 	switch (call->conn->security_level) {
38417926a79SDavid Howells 	case RXRPC_SECURITY_PLAIN:
38517926a79SDavid Howells 		ret = 0;
38617926a79SDavid Howells 		break;
38717926a79SDavid Howells 	case RXRPC_SECURITY_AUTH:
388a4ea4c47SDavid Howells 		ret = rxkad_secure_packet_auth(call, txb, req);
38917926a79SDavid Howells 		break;
39017926a79SDavid Howells 	case RXRPC_SECURITY_ENCRYPT:
391a4ea4c47SDavid Howells 		ret = rxkad_secure_packet_encrypt(call, txb, req);
39217926a79SDavid Howells 		break;
39317926a79SDavid Howells 	default:
39417926a79SDavid Howells 		ret = -EPERM;
39517926a79SDavid Howells 		break;
39617926a79SDavid Howells 	}
39717926a79SDavid Howells 
39830d95efeSDavid Howells 	skcipher_request_free(req);
3995b47d236SJustin Stitt 	_leave(" = %d [set %x]", ret, y);
40017926a79SDavid Howells 	return ret;
40117926a79SDavid Howells }
40217926a79SDavid Howells 
40317926a79SDavid Howells /*
40417926a79SDavid Howells  * decrypt partial encryption on a packet (level 1 security)
40517926a79SDavid Howells  */
rxkad_verify_packet_1(struct rxrpc_call * call,struct sk_buff * skb,rxrpc_seq_t seq,struct skcipher_request * req)4065a42976dSDavid Howells static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
40754424d38SKees Cook 				 rxrpc_seq_t seq,
40854424d38SKees Cook 				 struct skcipher_request *req)
40917926a79SDavid Howells {
41017926a79SDavid Howells 	struct rxkad_level1_hdr sechdr;
411d4d02d8bSDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
41217926a79SDavid Howells 	struct rxrpc_crypt iv;
41368e3f5ddSHerbert Xu 	struct scatterlist sg[16];
414fb46f6eeSDavid Howells 	u32 data_size, buf;
41517926a79SDavid Howells 	u16 check;
41617926a79SDavid Howells 	int ret;
417d0d5c0cdSDavid Howells 
41817926a79SDavid Howells 	_enter("");
41917926a79SDavid Howells 
42017926a79SDavid Howells 	if (sp->len < 8)
421d4d02d8bSDavid Howells 		return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
422fb46f6eeSDavid Howells 					  rxkad_abort_1_short_header);
423fb46f6eeSDavid Howells 
4245a42976dSDavid Howells 	/* Decrypt the skbuff in-place.  TODO: We really want to decrypt
4255a42976dSDavid Howells 	 * directly into the target buffer.
42617926a79SDavid Howells 	 */
427248f219cSDavid Howells 	sg_init_table(sg, ARRAY_SIZE(sg));
428248f219cSDavid Howells 	ret = skb_to_sgvec(skb, sg, sp->offset, 8);
429248f219cSDavid Howells 	if (unlikely(ret < 0))
430d0d5c0cdSDavid Howells 		return ret;
431d4d02d8bSDavid Howells 
43289a5ea99SJason A. Donenfeld 	/* start the decryption afresh */
43389a5ea99SJason A. Donenfeld 	memset(&iv, 0, sizeof(iv));
43417926a79SDavid Howells 
43517926a79SDavid Howells 	skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
43617926a79SDavid Howells 	skcipher_request_set_callback(req, 0, NULL, NULL);
43717926a79SDavid Howells 	skcipher_request_set_crypt(req, sg, sg, 8, iv.x);
438521bb304SDavid Howells 	crypto_skcipher_decrypt(req);
4391afe593bSHerbert Xu 	skcipher_request_zero(req);
4401afe593bSHerbert Xu 
4411afe593bSHerbert Xu 	/* Extract the decrypted packet length */
4421afe593bSHerbert Xu 	if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
44317926a79SDavid Howells 		return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
4445a42976dSDavid Howells 					  rxkad_abort_1_short_encdata);
445d4d02d8bSDavid Howells 	sp->offset += sizeof(sechdr);
446fb46f6eeSDavid Howells 	sp->len    -= sizeof(sechdr);
447fb46f6eeSDavid Howells 
4485a42976dSDavid Howells 	buf = ntohl(sechdr.data_size);
4495a42976dSDavid Howells 	data_size = buf & 0xffff;
450d4d02d8bSDavid Howells 
451d4d02d8bSDavid Howells 	check = buf >> 16;
45217926a79SDavid Howells 	check ^= seq ^ call->call_id;
45317926a79SDavid Howells 	check &= 0xffff;
45417926a79SDavid Howells 	if (check != 0)
45517926a79SDavid Howells 		return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
45617926a79SDavid Howells 					  rxkad_abort_1_short_check);
4575a42976dSDavid Howells 	if (data_size > sp->len)
45817926a79SDavid Howells 		return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
45917926a79SDavid Howells 					  rxkad_abort_1_short_data);
460fb46f6eeSDavid Howells 	sp->len = data_size;
461fb46f6eeSDavid Howells 
46217926a79SDavid Howells 	_leave(" = 0 [dlen=%x]", data_size);
46317926a79SDavid Howells 	return 0;
46417926a79SDavid Howells }
465d4d02d8bSDavid Howells 
466fb46f6eeSDavid Howells /*
467fb46f6eeSDavid Howells  * wholly decrypt a packet (level 2 security)
4685a42976dSDavid Howells  */
rxkad_verify_packet_2(struct rxrpc_call * call,struct sk_buff * skb,rxrpc_seq_t seq,struct skcipher_request * req)4695a42976dSDavid Howells static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
470d4d02d8bSDavid Howells 				 rxrpc_seq_t seq,
47117926a79SDavid Howells 				 struct skcipher_request *req)
47217926a79SDavid Howells {
47317926a79SDavid Howells 	const struct rxrpc_key_token *token;
47417926a79SDavid Howells 	struct rxkad_level2_hdr sechdr;
47517926a79SDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
476fb46f6eeSDavid Howells 	struct rxrpc_crypt iv;
47726cb02aaSDavid Howells 	struct scatterlist _sg[4], *sg;
47817926a79SDavid Howells 	u32 data_size, buf;
47917926a79SDavid Howells 	u16 check;
48017926a79SDavid Howells 	int nsg, ret;
48117926a79SDavid Howells 
48217926a79SDavid Howells 	_enter(",{%d}", sp->len);
48317926a79SDavid Howells 
4845a42976dSDavid Howells 	if (sp->len < 8)
48554424d38SKees Cook 		return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
48654424d38SKees Cook 					  rxkad_abort_2_short_header);
48717926a79SDavid Howells 
48833941284SDavid Howells 	/* Decrypt the skbuff in-place.  TODO: We really want to decrypt
48917926a79SDavid Howells 	 * directly into the target buffer.
490d4d02d8bSDavid Howells 	 */
49117926a79SDavid Howells 	sg = _sg;
49217926a79SDavid Howells 	nsg = skb_shinfo(skb)->nr_frags + 1;
493fb46f6eeSDavid Howells 	if (nsg <= 4) {
49417926a79SDavid Howells 		nsg = 4;
49517926a79SDavid Howells 	} else {
49689a5ea99SJason A. Donenfeld 		sg = kmalloc_array(nsg, sizeof(*sg), GFP_NOIO);
49717926a79SDavid Howells 		if (!sg)
498d4d02d8bSDavid Howells 			return -ENOMEM;
49917926a79SDavid Howells 	}
500d4d02d8bSDavid Howells 
501fb46f6eeSDavid Howells 	sg_init_table(sg, nsg);
502fb46f6eeSDavid Howells 	ret = skb_to_sgvec(skb, sg, sp->offset, sp->len);
5035a42976dSDavid Howells 	if (unlikely(ret < 0)) {
5045a42976dSDavid Howells 		if (sg != _sg)
50517926a79SDavid Howells 			kfree(sg);
506248f219cSDavid Howells 		return ret;
507248f219cSDavid Howells 	}
508248f219cSDavid Howells 
50917926a79SDavid Howells 	/* decrypt from the session key */
5100d40f728SDavid Howells 	token = call->conn->key->payload.data[0];
511d0d5c0cdSDavid Howells 	memcpy(&iv, token->kad->session_key, sizeof(iv));
512d0d5c0cdSDavid Howells 
513d0d5c0cdSDavid Howells 	skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
5146da2ec56SKees Cook 	skcipher_request_set_callback(req, 0, NULL, NULL);
51517926a79SDavid Howells 	skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x);
51617926a79SDavid Howells 	crypto_skcipher_decrypt(req);
51717926a79SDavid Howells 	skcipher_request_zero(req);
51817926a79SDavid Howells 	if (sg != _sg)
51968e3f5ddSHerbert Xu 		kfree(sg);
520d4d02d8bSDavid Howells 
52189a5ea99SJason A. Donenfeld 	/* Extract the decrypted packet length */
52289a5ea99SJason A. Donenfeld 	if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
52389a5ea99SJason A. Donenfeld 		return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
52489a5ea99SJason A. Donenfeld 					  rxkad_abort_2_short_len);
52589a5ea99SJason A. Donenfeld 	sp->offset += sizeof(sechdr);
52617926a79SDavid Howells 	sp->len    -= sizeof(sechdr);
52717926a79SDavid Howells 
528*2cc80086SDavid Howells 	buf = ntohl(sechdr.data_size);
52933941284SDavid Howells 	data_size = buf & 0xffff;
53017926a79SDavid Howells 
531521bb304SDavid Howells 	check = buf >> 16;
5321afe593bSHerbert Xu 	check ^= seq ^ call->call_id;
533d4d02d8bSDavid Howells 	check &= 0xffff;
5341afe593bSHerbert Xu 	if (check != 0)
5351afe593bSHerbert Xu 		return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
53617926a79SDavid Howells 					  rxkad_abort_2_short_check);
53717926a79SDavid Howells 
53817926a79SDavid Howells 	if (data_size > sp->len)
5395a42976dSDavid Howells 		return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
540d4d02d8bSDavid Howells 					  rxkad_abort_2_short_data);
541fb46f6eeSDavid Howells 
542fb46f6eeSDavid Howells 	sp->len = data_size;
5435a42976dSDavid Howells 	_leave(" = 0 [dlen=%x]", data_size);
5445a42976dSDavid Howells 	return 0;
545d4d02d8bSDavid Howells }
546d4d02d8bSDavid Howells 
54717926a79SDavid Howells /*
54817926a79SDavid Howells  * Verify the security on a received packet and the subpackets therein.
54917926a79SDavid Howells  */
rxkad_verify_packet(struct rxrpc_call * call,struct sk_buff * skb)55017926a79SDavid Howells static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
55117926a79SDavid Howells {
5525a42976dSDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
55317926a79SDavid Howells 	struct skcipher_request	*req;
55417926a79SDavid Howells 	struct rxrpc_crypt iv;
555fb46f6eeSDavid Howells 	struct scatterlist sg;
556fb46f6eeSDavid Howells 	union {
55717926a79SDavid Howells 		__be32 buf[2];
55817926a79SDavid Howells 	} crypto __aligned(8);
55917926a79SDavid Howells 	rxrpc_seq_t seq = sp->hdr.seq;
560d4d02d8bSDavid Howells 	int ret;
561fb46f6eeSDavid Howells 	u16 cksum;
562fb46f6eeSDavid Howells 	u32 x, y;
5635a42976dSDavid Howells 
5645a42976dSDavid Howells 	_enter("{%d{%x}},{#%u}",
56517926a79SDavid Howells 	       call->debug_id, key_serial(call->conn->key), seq);
566d4d02d8bSDavid Howells 
56717926a79SDavid Howells 	if (!call->conn->rxkad.cipher)
56817926a79SDavid Howells 		return 0;
56917926a79SDavid Howells 
57017926a79SDavid Howells 	req = rxkad_get_call_crypto(call);
571fb46f6eeSDavid Howells 	if (!req)
57226cb02aaSDavid Howells 		return -ENOMEM;
57317926a79SDavid Howells 
57417926a79SDavid Howells 	/* continue encrypting from where we left off */
57517926a79SDavid Howells 	memcpy(&iv, call->conn->rxkad.csum_iv.x, sizeof(iv));
57617926a79SDavid Howells 
57717926a79SDavid Howells 	/* validate the security checksum */
57817926a79SDavid Howells 	x = (call->cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT);
57917926a79SDavid Howells 	x |= seq & 0x3fffffff;
58017926a79SDavid Howells 	crypto.buf[0] = htonl(call->call_id);
581d4d02d8bSDavid Howells 	crypto.buf[1] = htonl(x);
58217926a79SDavid Howells 
583d4d02d8bSDavid Howells 	sg_init_one(&sg, crypto.buf, 8);
58417926a79SDavid Howells 	skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
585d4d02d8bSDavid Howells 	skcipher_request_set_callback(req, 0, NULL, NULL);
5861db88c53SDavid Howells 	skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x);
58717926a79SDavid Howells 	crypto_skcipher_encrypt(req);
588a263629dSHerbert Xu 	skcipher_request_zero(req);
58930d95efeSDavid Howells 
59030d95efeSDavid Howells 	y = ntohl(crypto.buf[1]);
59130d95efeSDavid Howells 	cksum = (y >> 16) & 0xffff;
592d4d02d8bSDavid Howells 	if (cksum == 0)
593fb46f6eeSDavid Howells 		cksum = 1; /* zero checksums are not permitted */
59430d95efeSDavid Howells 
5950d12f8a4SDavid Howells 	if (cksum != sp->hdr.cksum) {
5960d12f8a4SDavid Howells 		ret = rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
59717926a79SDavid Howells 					 rxkad_abort_bad_checksum);
59817926a79SDavid Howells 		goto out;
599*2cc80086SDavid Howells 	}
60017926a79SDavid Howells 
601521bb304SDavid Howells 	switch (call->conn->security_level) {
60217926a79SDavid Howells 	case RXRPC_SECURITY_PLAIN:
60317926a79SDavid Howells 		ret = 0;
6041db88c53SDavid Howells 		break;
6051db88c53SDavid Howells 	case RXRPC_SECURITY_AUTH:
6061db88c53SDavid Howells 		ret = rxkad_verify_packet_1(call, skb, seq, req);
6071db88c53SDavid Howells 		break;
60817926a79SDavid Howells 	case RXRPC_SECURITY_ENCRYPT:
609521bb304SDavid Howells 		ret = rxkad_verify_packet_2(call, skb, seq, req);
61017926a79SDavid Howells 		break;
61117926a79SDavid Howells 	default:
61201a90a45SDavid Howells 		ret = -ENOANO;
6135a42976dSDavid Howells 		break;
61430d95efeSDavid Howells 	}
61530d95efeSDavid Howells 
61617926a79SDavid Howells out:
61730d95efeSDavid Howells 	skcipher_request_free(req);
618521bb304SDavid Howells 	return ret;
6191afe593bSHerbert Xu }
620a263629dSHerbert Xu 
6211afe593bSHerbert Xu /*
6221afe593bSHerbert Xu  * issue a challenge
62317926a79SDavid Howells  */
rxkad_issue_challenge(struct rxrpc_connection * conn)62430d95efeSDavid Howells static int rxkad_issue_challenge(struct rxrpc_connection *conn)
6250d12f8a4SDavid Howells {
6260d12f8a4SDavid Howells 	struct rxkad_challenge challenge;
6270d12f8a4SDavid Howells 	struct rxrpc_wire_header whdr;
62817926a79SDavid Howells 	struct msghdr msg;
629d4d02d8bSDavid Howells 	struct kvec iov[2];
630fb46f6eeSDavid Howells 	size_t len;
631fb46f6eeSDavid Howells 	u32 serial;
632fb46f6eeSDavid Howells 	int ret;
63317926a79SDavid Howells 
63417926a79SDavid Howells 	_enter("{%d}", conn->debug_id);
635*2cc80086SDavid Howells 
63617926a79SDavid Howells 	get_random_bytes(&conn->rxkad.nonce, sizeof(conn->rxkad.nonce));
63730d95efeSDavid Howells 
63830d95efeSDavid Howells 	challenge.version	= htonl(2);
63917926a79SDavid Howells 	challenge.nonce		= htonl(conn->rxkad.nonce);
64030d95efeSDavid Howells 	challenge.min_level	= htonl(0);
64130d95efeSDavid Howells 	challenge.__padding	= 0;
64217926a79SDavid Howells 
64330d95efeSDavid Howells 	msg.msg_name	= &conn->peer->srx.transport;
64430d95efeSDavid Howells 	msg.msg_namelen	= conn->peer->srx.transport_len;
64517926a79SDavid Howells 	msg.msg_control	= NULL;
64630d95efeSDavid Howells 	msg.msg_controllen = 0;
64730d95efeSDavid Howells 	msg.msg_flags	= 0;
64817926a79SDavid Howells 
649fb46f6eeSDavid Howells 	whdr.epoch	= htonl(conn->proto.epoch);
65030d95efeSDavid Howells 	whdr.cid	= htonl(conn->proto.cid);
65130d95efeSDavid Howells 	whdr.callNumber	= 0;
65230d95efeSDavid Howells 	whdr.seq	= 0;
653fb46f6eeSDavid Howells 	whdr.type	= RXRPC_PACKET_TYPE_CHALLENGE;
654fb46f6eeSDavid Howells 	whdr.flags	= conn->out_clientflag;
655fb46f6eeSDavid Howells 	whdr.userStatus	= 0;
656fb46f6eeSDavid Howells 	whdr.securityIndex = conn->security_ix;
65717926a79SDavid Howells 	whdr._rsvd	= 0;
65817926a79SDavid Howells 	whdr.serviceId	= htons(conn->service_id);
65917926a79SDavid Howells 
66017926a79SDavid Howells 	iov[0].iov_base	= &whdr;
66117926a79SDavid Howells 	iov[0].iov_len	= sizeof(whdr);
66217926a79SDavid Howells 	iov[1].iov_base	= &challenge;
66317926a79SDavid Howells 	iov[1].iov_len	= sizeof(challenge);
66417926a79SDavid Howells 
6650d12f8a4SDavid Howells 	len = iov[0].iov_len + iov[1].iov_len;
66617926a79SDavid Howells 
66717926a79SDavid Howells 	serial = rxrpc_get_next_serial(conn);
66817926a79SDavid Howells 	whdr.serial = htonl(serial);
6690d12f8a4SDavid Howells 
67017926a79SDavid Howells 	ret = kernel_sendmsg(conn->local->socket, &msg, iov, 2, len);
67117926a79SDavid Howells 	if (ret < 0) {
672ec832bd0SDavid Howells 		trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
67317926a79SDavid Howells 				    rxrpc_tx_point_rxkad_challenge);
674521bb304SDavid Howells 		return -EAGAIN;
67517926a79SDavid Howells 	}
67617926a79SDavid Howells 
677521bb304SDavid Howells 	conn->peer->last_tx_at = ktime_get_seconds();
67817926a79SDavid Howells 	trace_rxrpc_tx_packet(conn->debug_id, &whdr,
67917926a79SDavid Howells 			      rxrpc_tx_point_rxkad_challenge);
68017926a79SDavid Howells 	_leave(" = 0");
681*2cc80086SDavid Howells 	return 0;
682*2cc80086SDavid Howells }
68317926a79SDavid Howells 
68417926a79SDavid Howells /*
68517926a79SDavid Howells  * send a Kerberos security response
68617926a79SDavid Howells  */
rxkad_send_response(struct rxrpc_connection * conn,struct rxrpc_host_header * hdr,struct rxkad_response * resp,const struct rxkad_key * s2)68719ffa01cSDavid Howells static int rxkad_send_response(struct rxrpc_connection *conn,
68819ffa01cSDavid Howells 			       struct rxrpc_host_header *hdr,
6890d12f8a4SDavid Howells 			       struct rxkad_response *resp,
6900d12f8a4SDavid Howells 			       const struct rxkad_key *s2)
6910d12f8a4SDavid Howells {
6920d12f8a4SDavid Howells 	struct rxrpc_wire_header whdr;
6930d12f8a4SDavid Howells 	struct msghdr msg;
6940d12f8a4SDavid Howells 	struct kvec iov[3];
6950d12f8a4SDavid Howells 	size_t len;
69668d6d1aeSDavid Howells 	u32 serial;
69717926a79SDavid Howells 	int ret;
6980d12f8a4SDavid Howells 
6990d12f8a4SDavid Howells 	_enter("");
70017926a79SDavid Howells 
70117926a79SDavid Howells 	msg.msg_name	= &conn->peer->srx.transport;
70217926a79SDavid Howells 	msg.msg_namelen	= conn->peer->srx.transport_len;
70317926a79SDavid Howells 	msg.msg_control	= NULL;
70417926a79SDavid Howells 	msg.msg_controllen = 0;
7050d12f8a4SDavid Howells 	msg.msg_flags	= 0;
7060d12f8a4SDavid Howells 
70717926a79SDavid Howells 	memset(&whdr, 0, sizeof(whdr));
708*2cc80086SDavid Howells 	whdr.epoch	= htonl(hdr->epoch);
70917926a79SDavid Howells 	whdr.cid	= htonl(hdr->cid);
7106b47fe1dSDavid Howells 	whdr.type	= RXRPC_PACKET_TYPE_RESPONSE;
7114764c0daSDavid Howells 	whdr.flags	= conn->out_clientflag;
71217926a79SDavid Howells 	whdr.securityIndex = hdr->securityIndex;
71317926a79SDavid Howells 	whdr.serviceId	= htons(hdr->serviceId);
71417926a79SDavid Howells 
715*2cc80086SDavid Howells 	iov[0].iov_base	= &whdr;
7164764c0daSDavid Howells 	iov[0].iov_len	= sizeof(whdr);
7174764c0daSDavid Howells 	iov[1].iov_base	= resp;
71817926a79SDavid Howells 	iov[1].iov_len	= sizeof(*resp);
71917926a79SDavid Howells 	iov[2].iov_base	= (void *)s2->ticket;
72017926a79SDavid Howells 	iov[2].iov_len	= s2->ticket_len;
72117926a79SDavid Howells 
72217926a79SDavid Howells 	len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
72317926a79SDavid Howells 
72417926a79SDavid Howells 	serial = rxrpc_get_next_serial(conn);
72517926a79SDavid Howells 	whdr.serial = htonl(serial);
7260d12f8a4SDavid Howells 
72717926a79SDavid Howells 	rxrpc_local_dont_fragment(conn->local, false);
72817926a79SDavid Howells 	ret = kernel_sendmsg(conn->local->socket, &msg, iov, 3, len);
72917926a79SDavid Howells 	rxrpc_local_dont_fragment(conn->local, true);
7300d12f8a4SDavid Howells 	if (ret < 0) {
73117926a79SDavid Howells 		trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
73217926a79SDavid Howells 				    rxrpc_tx_point_rxkad_response);
73317926a79SDavid Howells 		return -EAGAIN;
7340d12f8a4SDavid Howells 	}
73517926a79SDavid Howells 
73617926a79SDavid Howells 	conn->peer->last_tx_at = ktime_get_seconds();
73717926a79SDavid Howells 	_leave(" = 0");
73817926a79SDavid Howells 	return 0;
739*2cc80086SDavid Howells }
740*2cc80086SDavid Howells 
74117926a79SDavid Howells /*
74217926a79SDavid Howells  * calculate the response checksum
74317926a79SDavid Howells  */
rxkad_calc_response_checksum(struct rxkad_response * response)74417926a79SDavid Howells static void rxkad_calc_response_checksum(struct rxkad_response *response)
7450d12f8a4SDavid Howells {
7460d12f8a4SDavid Howells 	u32 csum = 1000003;
7470d12f8a4SDavid Howells 	int loop;
7480d12f8a4SDavid Howells 	u8 *p = (u8 *) response;
7490d12f8a4SDavid Howells 
7500d12f8a4SDavid Howells 	for (loop = sizeof(*response); loop > 0; loop--)
7510d12f8a4SDavid Howells 		csum = csum * 0x10204081 + *p++;
75217926a79SDavid Howells 
7530d12f8a4SDavid Howells 	response->encrypted.checksum = htonl(csum);
7540d12f8a4SDavid Howells }
75517926a79SDavid Howells 
75617926a79SDavid Howells /*
75717926a79SDavid Howells  * encrypt the response packet
75817926a79SDavid Howells  */
rxkad_encrypt_response(struct rxrpc_connection * conn,struct rxkad_response * resp,const struct rxkad_key * s2)75917926a79SDavid Howells static int rxkad_encrypt_response(struct rxrpc_connection *conn,
76017926a79SDavid Howells 				  struct rxkad_response *resp,
76117926a79SDavid Howells 				  const struct rxkad_key *s2)
7620d12f8a4SDavid Howells {
7630d12f8a4SDavid Howells 	struct skcipher_request *req;
76417926a79SDavid Howells 	struct rxrpc_crypt iv;
765*2cc80086SDavid Howells 	struct scatterlist sg[1];
76617926a79SDavid Howells 
7676b47fe1dSDavid Howells 	req = skcipher_request_alloc(&conn->rxkad.cipher->base, GFP_NOFS);
7684764c0daSDavid Howells 	if (!req)
76917926a79SDavid Howells 		return -ENOMEM;
77017926a79SDavid Howells 
77117926a79SDavid Howells 	/* continue encrypting from where we left off */
772*2cc80086SDavid Howells 	memcpy(&iv, s2->session_key, sizeof(iv));
77317926a79SDavid Howells 
77417926a79SDavid Howells 	sg_init_table(sg, 1);
77517926a79SDavid Howells 	sg_set_buf(sg, &resp->encrypted, sizeof(resp->encrypted));
77617926a79SDavid Howells 	skcipher_request_set_sync_tfm(req, conn->rxkad.cipher);
77717926a79SDavid Howells 	skcipher_request_set_callback(req, 0, NULL, NULL);
77817926a79SDavid Howells 	skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x);
77917926a79SDavid Howells 	crypto_skcipher_encrypt(req);
78017926a79SDavid Howells 	skcipher_request_free(req);
78117926a79SDavid Howells 	return 0;
78217926a79SDavid Howells }
78317926a79SDavid Howells 
78417926a79SDavid Howells /*
78517926a79SDavid Howells  * respond to a challenge packet
78617926a79SDavid Howells  */
rxkad_respond_to_challenge(struct rxrpc_connection * conn,struct sk_buff * skb)78717926a79SDavid Howells static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
78817926a79SDavid Howells 				      struct sk_buff *skb)
78917926a79SDavid Howells {
79017926a79SDavid Howells 	const struct rxrpc_key_token *token;
79117926a79SDavid Howells 	struct rxkad_challenge challenge;
79217926a79SDavid Howells 	struct rxkad_response *resp;
79317926a79SDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
79417926a79SDavid Howells 	u32 version, nonce, min_level;
7951db88c53SDavid Howells 	int ret = -EPROTO;
79617926a79SDavid Howells 
79717926a79SDavid Howells 	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
79817926a79SDavid Howells 
7991db88c53SDavid Howells 	if (!conn->key)
80017926a79SDavid Howells 		return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
801a263629dSHerbert Xu 					rxkad_abort_chall_no_key);
80217926a79SDavid Howells 
803521bb304SDavid Howells 	ret = key_validate(conn->key);
8041db88c53SDavid Howells 	if (ret < 0)
8051db88c53SDavid Howells 		return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, ret,
8061db88c53SDavid Howells 					rxkad_abort_chall_key_expired);
80717926a79SDavid Howells 
80817926a79SDavid Howells 	if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
80917926a79SDavid Howells 			  &challenge, sizeof(challenge)) < 0)
810a263629dSHerbert Xu 		return rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
811a263629dSHerbert Xu 					rxkad_abort_chall_short);
812521bb304SDavid Howells 
8131afe593bSHerbert Xu 	version = ntohl(challenge.version);
8141afe593bSHerbert Xu 	nonce = ntohl(challenge.nonce);
8151afe593bSHerbert Xu 	min_level = ntohl(challenge.min_level);
8161db88c53SDavid Howells 
8171db88c53SDavid Howells 	trace_rxrpc_rx_challenge(conn, sp->hdr.serial, version, nonce, min_level);
81817926a79SDavid Howells 
81917926a79SDavid Howells 	if (version != RXKAD_VERSION)
82017926a79SDavid Howells 		return rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
82117926a79SDavid Howells 					rxkad_abort_chall_version);
82217926a79SDavid Howells 
82317926a79SDavid Howells 	if (conn->security_level < min_level)
82417926a79SDavid Howells 		return rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EACCES,
82517926a79SDavid Howells 					rxkad_abort_chall_level);
82617926a79SDavid Howells 
82733941284SDavid Howells 	token = conn->key->payload.data[0];
82817926a79SDavid Howells 
8298c2f826dSDavid Howells 	/* build the response packet */
830248f219cSDavid Howells 	resp = kzalloc(sizeof(struct rxkad_response), GFP_NOFS);
831fb46f6eeSDavid Howells 	if (!resp)
83217926a79SDavid Howells 		return -ENOMEM;
83317926a79SDavid Howells 
83417926a79SDavid Howells 	resp->version			= htonl(RXKAD_VERSION);
835*2cc80086SDavid Howells 	resp->encrypted.epoch		= htonl(conn->proto.epoch);
83617926a79SDavid Howells 	resp->encrypted.cid		= htonl(conn->proto.cid);
837fb46f6eeSDavid Howells 	resp->encrypted.securityIndex	= htonl(conn->security_ix);
838ef68622dSDavid Howells 	resp->encrypted.inc_nonce	= htonl(nonce + 1);
839*2cc80086SDavid Howells 	resp->encrypted.level		= htonl(conn->security_level);
840ef68622dSDavid Howells 	resp->kvno			= htonl(token->kad->kvno);
84117926a79SDavid Howells 	resp->ticket_len		= htonl(token->kad->ticket_len);
842ef68622dSDavid Howells 	resp->encrypted.call_id[0]	= htonl(conn->channels[0].call_counter);
843*2cc80086SDavid Howells 	resp->encrypted.call_id[1]	= htonl(conn->channels[1].call_counter);
844ef68622dSDavid Howells 	resp->encrypted.call_id[2]	= htonl(conn->channels[2].call_counter);
845ef68622dSDavid Howells 	resp->encrypted.call_id[3]	= htonl(conn->channels[3].call_counter);
84617926a79SDavid Howells 
847fb46f6eeSDavid Howells 	/* calculate the response checksum and then do the encryption */
84817926a79SDavid Howells 	rxkad_calc_response_checksum(resp);
849775e5b71SDavid Howells 	ret = rxkad_encrypt_response(conn, resp, token->kad);
850775e5b71SDavid Howells 	if (ret == 0)
85117926a79SDavid Howells 		ret = rxkad_send_response(conn, &sp->hdr, resp, token->kad);
85217926a79SDavid Howells 	kfree(resp);
85317926a79SDavid Howells 	return ret;
85417926a79SDavid Howells }
85517926a79SDavid Howells 
85617926a79SDavid Howells /*
8572ebdb26eSDavid Howells  * decrypt the kerberos IV ticket in the response
85817926a79SDavid Howells  */
rxkad_decrypt_ticket(struct rxrpc_connection * conn,struct key * server_key,struct sk_buff * skb,void * ticket,size_t ticket_len,struct rxrpc_crypt * _session_key,time64_t * _expiry)859fb46f6eeSDavid Howells static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
86017926a79SDavid Howells 				struct key *server_key,
86117926a79SDavid Howells 				struct sk_buff *skb,
86217926a79SDavid Howells 				void *ticket, size_t ticket_len,
86317926a79SDavid Howells 				struct rxrpc_crypt *_session_key,
86417926a79SDavid Howells 				time64_t *_expiry)
865ef68622dSDavid Howells {
866*2cc80086SDavid Howells 	struct skcipher_request *req;
867ef68622dSDavid Howells 	struct rxrpc_crypt iv, key;
86817926a79SDavid Howells 	struct scatterlist sg[1];
869*2cc80086SDavid Howells 	struct in_addr addr;
87017926a79SDavid Howells 	unsigned int life;
87117926a79SDavid Howells 	time64_t issue, now;
8728c2f826dSDavid Howells 	bool little_endian;
8738c2f826dSDavid Howells 	u8 *p, *q, *name, *end;
8748c2f826dSDavid Howells 
87517926a79SDavid Howells 	_enter("{%d},{%x}", conn->debug_id, key_serial(server_key));
8768c2f826dSDavid Howells 
8778c2f826dSDavid Howells 	*_expiry = 0;
8788c2f826dSDavid Howells 
8798c2f826dSDavid Howells 	ASSERT(server_key->payload.data[0] != NULL);
8808c2f826dSDavid Howells 	ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);
881*2cc80086SDavid Howells 
8828c2f826dSDavid Howells 	memcpy(&iv, &server_key->payload.data[2], sizeof(iv));
8838c2f826dSDavid Howells 
8848c2f826dSDavid Howells 	req = skcipher_request_alloc(server_key->payload.data[0], GFP_NOFS);
8858c2f826dSDavid Howells 	if (!req)
8868c2f826dSDavid Howells 		return -ENOMEM;
8878c2f826dSDavid Howells 
88817926a79SDavid Howells 	sg_init_one(&sg[0], ticket, ticket_len);
88917926a79SDavid Howells 	skcipher_request_set_callback(req, 0, NULL, NULL);
8908c2f826dSDavid Howells 	skcipher_request_set_crypt(req, sg, sg, ticket_len, iv.x);
8911db88c53SDavid Howells 	crypto_skcipher_decrypt(req);
8921db88c53SDavid Howells 	skcipher_request_free(req);
8938c2f826dSDavid Howells 
8948c2f826dSDavid Howells 	p = ticket;
8958c2f826dSDavid Howells 	end = p + ticket_len;
89617926a79SDavid Howells 
89717926a79SDavid Howells #define Z(field, fieldl)						\
898fb46f6eeSDavid Howells 	({								\
899ef68622dSDavid Howells 		u8 *__str = p;						\
900ef68622dSDavid Howells 		q = memchr(p, 0, end - p);				\
90117926a79SDavid Howells 		if (!q || q - p > field##_SZ)				\
902ef68622dSDavid Howells 			return rxrpc_abort_conn(			\
90317926a79SDavid Howells 				conn, skb, RXKADBADTICKET, -EPROTO,	\
90417926a79SDavid Howells 				rxkad_abort_resp_tkt_##fieldl);		\
90517926a79SDavid Howells 		for (; p < q; p++)					\
90617926a79SDavid Howells 			if (!isprint(*p))				\
90717926a79SDavid Howells 				return rxrpc_abort_conn(		\
90817926a79SDavid Howells 					conn, skb, RXKADBADTICKET, -EPROTO, \
909ec832bd0SDavid Howells 					rxkad_abort_resp_tkt_##fieldl);	\
910fb46f6eeSDavid Howells 		p++;							\
91117926a79SDavid Howells 		__str;							\
91217926a79SDavid Howells 	})
91310674a03SBaolin Wang 
91417926a79SDavid Howells 	/* extract the ticket flags */
91517926a79SDavid Howells 	_debug("KIV FLAGS: %x", *p);
9161afe593bSHerbert Xu 	little_endian = *p & 1;
917fb46f6eeSDavid Howells 	p++;
91817926a79SDavid Howells 
91968e3f5ddSHerbert Xu 	/* extract the authentication name */
92017926a79SDavid Howells 	name = Z(ANAME, aname);
92195c96174SEric Dumazet 	_debug("KIV ANAME: %s", name);
922fb46f6eeSDavid Howells 
92310674a03SBaolin Wang 	/* extract the principal's instance */
92417926a79SDavid Howells 	name = Z(INST, inst);
92517926a79SDavid Howells 	_debug("KIV INST : %s", name);
926fb46f6eeSDavid Howells 
92717926a79SDavid Howells 	/* extract the principal's authentication domain */
92817926a79SDavid Howells 	name = Z(REALM, realm);
929ec832bd0SDavid Howells 	_debug("KIV REALM: %s", name);
93017926a79SDavid Howells 
93117926a79SDavid Howells 	if (end - p < 4 + 8 + 4 + 2)
93217926a79SDavid Howells 		return rxrpc_abort_conn(conn, skb, RXKADBADTICKET, -EPROTO,
933ec832bd0SDavid Howells 					rxkad_abort_resp_tkt_short);
93417926a79SDavid Howells 
93517926a79SDavid Howells 	/* get the IPv4 address of the entity that requested the ticket */
936ec832bd0SDavid Howells 	memcpy(&addr, p, sizeof(addr));
93717926a79SDavid Howells 	p += 4;
938ef68622dSDavid Howells 	_debug("KIV ADDR : %pI4", &addr);
939ec832bd0SDavid Howells 
940ef68622dSDavid Howells 	/* get the session key from the ticket */
941ef68622dSDavid Howells 	memcpy(&key, p, sizeof(key));
94217926a79SDavid Howells 	p += 8;
94368e3f5ddSHerbert Xu 	_debug("KIV KEY  : %08x %08x", ntohl(key.n[0]), ntohl(key.n[1]));
9441afe593bSHerbert Xu 	memcpy(_session_key, &key, sizeof(key));
9451afe593bSHerbert Xu 
9461afe593bSHerbert Xu 	/* get the ticket's lifetime */
9471afe593bSHerbert Xu 	life = *p++ * 5 * 60;
94817926a79SDavid Howells 	_debug("KIV LIFE : %u", life);
94917926a79SDavid Howells 
95017926a79SDavid Howells 	/* get the issue time of the ticket */
95117926a79SDavid Howells 	if (little_endian) {
952fb46f6eeSDavid Howells 		__le32 stamp;
95317926a79SDavid Howells 		memcpy(&stamp, p, 4);
95417926a79SDavid Howells 		issue = rxrpc_u32_to_time64(le32_to_cpu(stamp));
955fb46f6eeSDavid Howells 	} else {
95617926a79SDavid Howells 		__be32 stamp;
957fb46f6eeSDavid Howells 		memcpy(&stamp, p, 4);
95817926a79SDavid Howells 		issue = rxrpc_u32_to_time64(be32_to_cpu(stamp));
95917926a79SDavid Howells 	}
96017926a79SDavid Howells 	p += 4;
96117926a79SDavid Howells 	now = ktime_get_real_seconds();
96217926a79SDavid Howells 	_debug("KIV ISSUE: %llx [%llx]", issue, now);
96317926a79SDavid Howells 
96417926a79SDavid Howells 	/* check the ticket is in date */
96517926a79SDavid Howells 	if (issue > now)
96617926a79SDavid Howells 		return rxrpc_abort_conn(conn, skb, RXKADNOAUTH, -EKEYREJECTED,
96717926a79SDavid Howells 					rxkad_abort_resp_tkt_future);
96817926a79SDavid Howells 	if (issue < now - life)
96917926a79SDavid Howells 		return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, -EKEYEXPIRED,
97017926a79SDavid Howells 					rxkad_abort_resp_tkt_expired);
97117926a79SDavid Howells 
972fb46f6eeSDavid Howells 	*_expiry = issue + life;
97317926a79SDavid Howells 
97417926a79SDavid Howells 	/* get the service name */
97517926a79SDavid Howells 	name = Z(SNAME, sname);
976fb46f6eeSDavid Howells 	_debug("KIV SNAME: %s", name);
97717926a79SDavid Howells 
97817926a79SDavid Howells 	/* get the service instance name */
97917926a79SDavid Howells 	name = Z(INST, sinst);
980fb46f6eeSDavid Howells 	_debug("KIV SINST: %s", name);
98117926a79SDavid Howells 	return 0;
98217926a79SDavid Howells }
983fb46f6eeSDavid Howells 
98417926a79SDavid Howells /*
98517926a79SDavid Howells  * decrypt the response packet
98617926a79SDavid Howells  */
rxkad_decrypt_response(struct rxrpc_connection * conn,struct rxkad_response * resp,const struct rxrpc_crypt * session_key)98717926a79SDavid Howells static void rxkad_decrypt_response(struct rxrpc_connection *conn,
98817926a79SDavid Howells 				   struct rxkad_response *resp,
98917926a79SDavid Howells 				   const struct rxrpc_crypt *session_key)
99021454aaaSHarvey Harrison {
99117926a79SDavid Howells 	struct skcipher_request *req = rxkad_ci_req;
99217926a79SDavid Howells 	struct scatterlist sg[1];
99317926a79SDavid Howells 	struct rxrpc_crypt iv;
99417926a79SDavid Howells 
99517926a79SDavid Howells 	_enter(",,%08x%08x",
99617926a79SDavid Howells 	       ntohl(session_key->n[0]), ntohl(session_key->n[1]));
99717926a79SDavid Howells 
99817926a79SDavid Howells 	mutex_lock(&rxkad_ci_mutex);
99917926a79SDavid Howells 	if (crypto_sync_skcipher_setkey(rxkad_ci, session_key->x,
100017926a79SDavid Howells 					sizeof(*session_key)) < 0)
100117926a79SDavid Howells 		BUG();
100217926a79SDavid Howells 
100317926a79SDavid Howells 	memcpy(&iv, session_key, sizeof(iv));
100417926a79SDavid Howells 
100517926a79SDavid Howells 	sg_init_table(sg, 1);
100610674a03SBaolin Wang 	sg_set_buf(sg, &resp->encrypted, sizeof(resp->encrypted));
100717926a79SDavid Howells 	skcipher_request_set_sync_tfm(req, rxkad_ci);
100817926a79SDavid Howells 	skcipher_request_set_callback(req, 0, NULL, NULL);
100917926a79SDavid Howells 	skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x);
101010674a03SBaolin Wang 	crypto_skcipher_decrypt(req);
101117926a79SDavid Howells 	skcipher_request_zero(req);
101217926a79SDavid Howells 
101310674a03SBaolin Wang 	mutex_unlock(&rxkad_ci_mutex);
101410674a03SBaolin Wang 
101517926a79SDavid Howells 	_leave("");
101617926a79SDavid Howells }
101717926a79SDavid Howells 
1018fb46f6eeSDavid Howells /*
101917926a79SDavid Howells  * verify a response
1020ef68622dSDavid Howells  */
rxkad_verify_response(struct rxrpc_connection * conn,struct sk_buff * skb)102117926a79SDavid Howells static int rxkad_verify_response(struct rxrpc_connection *conn,
102217926a79SDavid Howells 				 struct sk_buff *skb)
102317926a79SDavid Howells {
1024fb46f6eeSDavid Howells 	struct rxkad_response *response;
102517926a79SDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
1026ef68622dSDavid Howells 	struct rxrpc_crypt session_key;
102717926a79SDavid Howells 	struct key *server_key;
102817926a79SDavid Howells 	time64_t expiry;
102917926a79SDavid Howells 	void *ticket;
103017926a79SDavid Howells 	u32 version, kvno, ticket_len, level;
103117926a79SDavid Howells 	__be32 csum;
1032fb46f6eeSDavid Howells 	int ret, i;
103317926a79SDavid Howells 
103417926a79SDavid Howells 	_enter("{%d}", conn->debug_id);
103517926a79SDavid Howells 
1036fb46f6eeSDavid Howells 	server_key = rxrpc_look_up_server_security(conn, skb, 0, 0);
103717926a79SDavid Howells 	if (IS_ERR(server_key)) {
1038ef68622dSDavid Howells 		ret = PTR_ERR(server_key);
103917926a79SDavid Howells 		switch (ret) {
104017926a79SDavid Howells 		case -ENOKEY:
1041fb46f6eeSDavid Howells 			return rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, ret,
1042fb46f6eeSDavid Howells 						rxkad_abort_resp_nokey);
1043ef68622dSDavid Howells 		case -EKEYEXPIRED:
1044ef68622dSDavid Howells 			return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, ret,
1045fb46f6eeSDavid Howells 						rxkad_abort_resp_key_expired);
1046ef68622dSDavid Howells 		default:
1047ef68622dSDavid Howells 			return rxrpc_abort_conn(conn, skb, RXKADNOAUTH, ret,
1048ef68622dSDavid Howells 						rxkad_abort_resp_key_rejected);
104917926a79SDavid Howells 		}
105017926a79SDavid Howells 	}
105117926a79SDavid Howells 
105217926a79SDavid Howells 	ret = -ENOMEM;
105317926a79SDavid Howells 	response = kzalloc(sizeof(struct rxkad_response), GFP_NOFS);
105417926a79SDavid Howells 	if (!response)
105517926a79SDavid Howells 		goto temporary_error;
105617926a79SDavid Howells 
105717926a79SDavid Howells 	if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
10581db88c53SDavid Howells 			  response, sizeof(*response)) < 0) {
1059a263629dSHerbert Xu 		rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
106017926a79SDavid Howells 				 rxkad_abort_resp_short);
106117926a79SDavid Howells 		goto protocol_error;
106217926a79SDavid Howells 	}
106317926a79SDavid Howells 
106417926a79SDavid Howells 	version = ntohl(response->version);
106517926a79SDavid Howells 	ticket_len = ntohl(response->ticket_len);
106669d826faSKees Cook 	kvno = ntohl(response->kvno);
106717926a79SDavid Howells 
106817926a79SDavid Howells 	trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len);
106917926a79SDavid Howells 
107017926a79SDavid Howells 	if (version != RXKAD_VERSION) {
107117926a79SDavid Howells 		rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
1072a263629dSHerbert Xu 				 rxkad_abort_resp_version);
1073a263629dSHerbert Xu 		goto protocol_error;
107469d826faSKees Cook 	}
10751afe593bSHerbert Xu 
10761afe593bSHerbert Xu 	if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) {
10771afe593bSHerbert Xu 		rxrpc_abort_conn(conn, skb, RXKADTICKETLEN, -EPROTO,
10781afe593bSHerbert Xu 				 rxkad_abort_resp_tkt_len);
10791afe593bSHerbert Xu 		goto protocol_error;
108017926a79SDavid Howells 	}
108117926a79SDavid Howells 
108217926a79SDavid Howells 	if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) {
108317926a79SDavid Howells 		rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, -EPROTO,
108417926a79SDavid Howells 				 rxkad_abort_resp_unknown_tkt);
108517926a79SDavid Howells 		goto protocol_error;
108617926a79SDavid Howells 	}
108717926a79SDavid Howells 
108817926a79SDavid Howells 	/* extract the kerberos ticket and decrypt and decode it */
108917926a79SDavid Howells 	ret = -ENOMEM;
109017926a79SDavid Howells 	ticket = kmalloc(ticket_len, GFP_NOFS);
109117926a79SDavid Howells 	if (!ticket)
10928c2f826dSDavid Howells 		goto temporary_error_free_resp;
1093248f219cSDavid Howells 
109417926a79SDavid Howells 	if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response),
1095ec832bd0SDavid Howells 			  ticket, ticket_len) < 0) {
1096fb46f6eeSDavid Howells 		rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
109710674a03SBaolin Wang 				 rxkad_abort_resp_short_tkt);
109817926a79SDavid Howells 		goto protocol_error;
109991e916cfSAl Viro 	}
110091e916cfSAl Viro 
1101a1399f8bSDavid Howells 	ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len,
110217926a79SDavid Howells 				   &session_key, &expiry);
1103ec832bd0SDavid Howells 	if (ret < 0)
1104ec832bd0SDavid Howells 		goto temporary_error_free_ticket;
1105ec832bd0SDavid Howells 
1106ec832bd0SDavid Howells 	/* use the session key from inside the ticket to decrypt the
1107ec832bd0SDavid Howells 	 * response */
1108ec832bd0SDavid Howells 	rxkad_decrypt_response(conn, response, &session_key);
1109ec832bd0SDavid Howells 
1110ec832bd0SDavid Howells 	if (ntohl(response->encrypted.epoch) != conn->proto.epoch ||
1111ec832bd0SDavid Howells 	    ntohl(response->encrypted.cid) != conn->proto.cid ||
1112ec832bd0SDavid Howells 	    ntohl(response->encrypted.securityIndex) != conn->security_ix) {
1113ec832bd0SDavid Howells 		rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
1114ec832bd0SDavid Howells 				 rxkad_abort_resp_bad_param);
1115ec832bd0SDavid Howells 		goto protocol_error_free;
1116ec832bd0SDavid Howells 	}
1117ec832bd0SDavid Howells 
1118ec832bd0SDavid Howells 	csum = response->encrypted.checksum;
1119ec832bd0SDavid Howells 	response->encrypted.checksum = 0;
1120ec832bd0SDavid Howells 	rxkad_calc_response_checksum(response);
1121ec832bd0SDavid Howells 	if (response->encrypted.checksum != csum) {
1122ec832bd0SDavid Howells 		rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
1123ec832bd0SDavid Howells 				 rxkad_abort_resp_bad_checksum);
112417926a79SDavid Howells 		goto protocol_error_free;
11258c2f826dSDavid Howells 	}
11268c2f826dSDavid Howells 
11278c2f826dSDavid Howells 	for (i = 0; i < RXRPC_MAXCALLS; i++) {
11288c2f826dSDavid Howells 		u32 call_id = ntohl(response->encrypted.call_id[i]);
11298c2f826dSDavid Howells 		u32 counter = READ_ONCE(conn->channels[i].call_counter);
1130fb46f6eeSDavid Howells 
113117926a79SDavid Howells 		if (call_id > INT_MAX) {
1132775e5b71SDavid Howells 			rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
11338c2f826dSDavid Howells 					 rxkad_abort_resp_bad_callid);
113417926a79SDavid Howells 			goto protocol_error_free;
113517926a79SDavid Howells 		}
11368c2f826dSDavid Howells 
11378c2f826dSDavid Howells 		if (call_id < counter) {
11388c2f826dSDavid Howells 			rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
11392ebdb26eSDavid Howells 					 rxkad_abort_resp_call_ctr);
11402ebdb26eSDavid Howells 			goto protocol_error_free;
114117926a79SDavid Howells 		}
1142fb46f6eeSDavid Howells 
114317926a79SDavid Howells 		if (call_id > counter) {
114417926a79SDavid Howells 			if (conn->channels[i].call) {
11454aa9cb32SDavid Howells 				rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
114617926a79SDavid Howells 						 rxkad_abort_resp_call_state);
1147fb46f6eeSDavid Howells 				goto protocol_error_free;
114817926a79SDavid Howells 			}
114917926a79SDavid Howells 			conn->channels[i].call_counter = call_id;
115017926a79SDavid Howells 		}
115117926a79SDavid Howells 	}
1152fb46f6eeSDavid Howells 
115317926a79SDavid Howells 	if (ntohl(response->encrypted.inc_nonce) != conn->rxkad.nonce + 1) {
115417926a79SDavid Howells 		rxrpc_abort_conn(conn, skb, RXKADOUTOFSEQUENCE, -EPROTO,
115517926a79SDavid Howells 				 rxkad_abort_resp_ooseq);
115617926a79SDavid Howells 		goto protocol_error_free;
115717926a79SDavid Howells 	}
1158ef68622dSDavid Howells 
115917926a79SDavid Howells 	level = ntohl(response->encrypted.level);
116017926a79SDavid Howells 	if (level > RXRPC_SECURITY_ENCRYPT) {
1161b43c75abSDinghao Liu 		rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EPROTO,
116217926a79SDavid Howells 				 rxkad_abort_resp_level);
1163fb46f6eeSDavid Howells 		goto protocol_error_free;
116417926a79SDavid Howells 	}
1165101c1bb6SDan Carpenter 	conn->security_level = level;
1166101c1bb6SDan Carpenter 
1167101c1bb6SDan Carpenter 	/* create a key to hold the security data and expiration time - after
1168101c1bb6SDan Carpenter 	 * this the connection security can be handled in exactly the same way
116917926a79SDavid Howells 	 * as for a client connection */
1170ec832bd0SDavid Howells 	ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);
1171ec832bd0SDavid Howells 	if (ret < 0)
1172ef68622dSDavid Howells 		goto temporary_error_free_ticket;
1173f45d01f4SQiushi Wu 
117417926a79SDavid Howells 	kfree(ticket);
117517926a79SDavid Howells 	kfree(response);
117617926a79SDavid Howells 	_leave(" = 0");
11778c2f826dSDavid Howells 	return 0;
117817926a79SDavid Howells 
1179fb46f6eeSDavid Howells protocol_error_free:
118017926a79SDavid Howells 	kfree(ticket);
11818c2f826dSDavid Howells protocol_error:
118217926a79SDavid Howells 	kfree(response);
11838c2f826dSDavid Howells 	key_put(server_key);
118417926a79SDavid Howells 	return -EPROTO;
11858c2f826dSDavid Howells 
118617926a79SDavid Howells temporary_error_free_ticket:
11878c2f826dSDavid Howells 	kfree(ticket);
11888c2f826dSDavid Howells temporary_error_free_resp:
11898c2f826dSDavid Howells 	kfree(response);
1190fb46f6eeSDavid Howells temporary_error:
11918c2f826dSDavid Howells 	/* Ignore the response packet if we got a temporary error such as
119217926a79SDavid Howells 	 * ENOMEM.  We just want to send the challenge again.  Note that we
119317926a79SDavid Howells 	 * also come out this way if the ticket decryption fails.
1194245500d8SDavid Howells 	 */
1195a1399f8bSDavid Howells 	key_put(server_key);
1196a1399f8bSDavid Howells 	return ret;
11978c2f826dSDavid Howells }
1198a1399f8bSDavid Howells 
1199fb46f6eeSDavid Howells /*
1200a1399f8bSDavid Howells  * clear the connection security
1201a1399f8bSDavid Howells  */
rxkad_clear(struct rxrpc_connection * conn)1202a1399f8bSDavid Howells static void rxkad_clear(struct rxrpc_connection *conn)
1203fb46f6eeSDavid Howells {
1204a1399f8bSDavid Howells 	_enter("");
1205a1399f8bSDavid Howells 
1206fb46f6eeSDavid Howells 	if (conn->rxkad.cipher)
1207fb46f6eeSDavid Howells 		crypto_free_sync_skcipher(conn->rxkad.cipher);
1208a1399f8bSDavid Howells }
1209a1399f8bSDavid Howells 
1210a1399f8bSDavid Howells /*
1211245500d8SDavid Howells  * Initialise the rxkad security service.
1212a1399f8bSDavid Howells  */
rxkad_init(void)1213a1399f8bSDavid Howells static int rxkad_init(void)
1214a1399f8bSDavid Howells {
1215a1399f8bSDavid Howells 	struct crypto_sync_skcipher *tfm;
1216a1399f8bSDavid Howells 	struct skcipher_request *req;
1217245500d8SDavid Howells 
121817926a79SDavid Howells 	/* pin the cipher we need so that the crypto layer doesn't invoke
1219fb46f6eeSDavid Howells 	 * keventd to go get it */
122017926a79SDavid Howells 	tfm = crypto_alloc_sync_skcipher("pcbc(fcrypt)", 0, 0);
1221521bb304SDavid Howells 	if (IS_ERR(tfm))
122217926a79SDavid Howells 		return PTR_ERR(tfm);
122317926a79SDavid Howells 
1224fb46f6eeSDavid Howells 	req = skcipher_request_alloc(&tfm->base, GFP_KERNEL);
122517926a79SDavid Howells 	if (!req)
12268c2f826dSDavid Howells 		goto nomem_tfm;
122717926a79SDavid Howells 
122817926a79SDavid Howells 	rxkad_ci_req = req;
1229*2cc80086SDavid Howells 	rxkad_ci = tfm;
123017926a79SDavid Howells 	return 0;
123117926a79SDavid Howells 
123217926a79SDavid Howells nomem_tfm:
123317926a79SDavid Howells 	crypto_free_sync_skcipher(tfm);
123417926a79SDavid Howells 	return -ENOMEM;
1235ef68622dSDavid Howells }
12368c2f826dSDavid Howells 
123717926a79SDavid Howells /*
123817926a79SDavid Howells  * Clean up the rxkad security service.
12398c2f826dSDavid Howells  */
rxkad_exit(void)124017926a79SDavid Howells static void rxkad_exit(void)
124117926a79SDavid Howells {
124217926a79SDavid Howells 	crypto_free_sync_skcipher(rxkad_ci);
1243a1399f8bSDavid Howells 	skcipher_request_free(rxkad_ci_req);
1244245500d8SDavid Howells }
124517926a79SDavid Howells 
124617926a79SDavid Howells /*
124717926a79SDavid Howells  * RxRPC Kerberos-based security
12488c2f826dSDavid Howells  */
1249fb46f6eeSDavid Howells const struct rxrpc_security rxkad = {
1250ec832bd0SDavid Howells 	.name				= "rxkad",
125117926a79SDavid Howells 	.security_index			= RXRPC_SECURITY_RXKAD,
125217926a79SDavid Howells 	.no_key_abort			= RXKADUNKNOWNKEY,
1253ef68622dSDavid Howells 	.init				= rxkad_init,
12548c2f826dSDavid Howells 	.exit				= rxkad_exit,
1255ef68622dSDavid Howells 	.preparse_server_key		= rxkad_preparse_server_key,
1256b43c75abSDinghao Liu 	.free_preparse_server_key	= rxkad_free_preparse_server_key,
12578c2f826dSDavid Howells 	.destroy_server_key		= rxkad_destroy_server_key,
1258ef68622dSDavid Howells 	.init_connection_security	= rxkad_init_connection_security,
1259ef68622dSDavid Howells 	.how_much_data			= rxkad_how_much_data,
1260ef68622dSDavid Howells 	.secure_packet			= rxkad_secure_packet,
1261ef68622dSDavid Howells 	.verify_packet			= rxkad_verify_packet,
1262ef68622dSDavid Howells 	.free_call_crypto		= rxkad_free_call_crypto,
1263ec832bd0SDavid Howells 	.issue_challenge		= rxkad_issue_challenge,
1264ef68622dSDavid Howells 	.respond_to_challenge		= rxkad_respond_to_challenge,
126517926a79SDavid Howells 	.verify_response		= rxkad_verify_response,
126617926a79SDavid Howells 	.clear				= rxkad_clear,
126717926a79SDavid Howells };
126817926a79SDavid Howells