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