162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Kerberos-based RxRPC security 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <crypto/skcipher.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/net.h> 1362306a36Sopenharmony_ci#include <linux/skbuff.h> 1462306a36Sopenharmony_ci#include <linux/udp.h> 1562306a36Sopenharmony_ci#include <linux/scatterlist.h> 1662306a36Sopenharmony_ci#include <linux/ctype.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/key-type.h> 1962306a36Sopenharmony_ci#include <net/sock.h> 2062306a36Sopenharmony_ci#include <net/af_rxrpc.h> 2162306a36Sopenharmony_ci#include <keys/rxrpc-type.h> 2262306a36Sopenharmony_ci#include "ar-internal.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define RXKAD_VERSION 2 2562306a36Sopenharmony_ci#define MAXKRB5TICKETLEN 1024 2662306a36Sopenharmony_ci#define RXKAD_TKT_TYPE_KERBEROS_V5 256 2762306a36Sopenharmony_ci#define ANAME_SZ 40 /* size of authentication name */ 2862306a36Sopenharmony_ci#define INST_SZ 40 /* size of principal's instance */ 2962306a36Sopenharmony_ci#define REALM_SZ 40 /* size of principal's auth domain */ 3062306a36Sopenharmony_ci#define SNAME_SZ 40 /* size of service name */ 3162306a36Sopenharmony_ci#define RXKAD_ALIGN 8 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct rxkad_level1_hdr { 3462306a36Sopenharmony_ci __be32 data_size; /* true data size (excluding padding) */ 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct rxkad_level2_hdr { 3862306a36Sopenharmony_ci __be32 data_size; /* true data size (excluding padding) */ 3962306a36Sopenharmony_ci __be32 checksum; /* decrypted data checksum */ 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int rxkad_prime_packet_security(struct rxrpc_connection *conn, 4362306a36Sopenharmony_ci struct crypto_sync_skcipher *ci); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * this holds a pinned cipher so that keventd doesn't get called by the cipher 4762306a36Sopenharmony_ci * alloc routine, but since we have it to hand, we use it to decrypt RESPONSE 4862306a36Sopenharmony_ci * packets 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistatic struct crypto_sync_skcipher *rxkad_ci; 5162306a36Sopenharmony_cistatic struct skcipher_request *rxkad_ci_req; 5262306a36Sopenharmony_cistatic DEFINE_MUTEX(rxkad_ci_mutex); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Parse the information from a server key 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * The data should be the 8-byte secret key. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic int rxkad_preparse_server_key(struct key_preparsed_payload *prep) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct crypto_skcipher *ci; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (prep->datalen != 8) 6462306a36Sopenharmony_ci return -EINVAL; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci memcpy(&prep->payload.data[2], prep->data, 8); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ci = crypto_alloc_skcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); 6962306a36Sopenharmony_ci if (IS_ERR(ci)) { 7062306a36Sopenharmony_ci _leave(" = %ld", PTR_ERR(ci)); 7162306a36Sopenharmony_ci return PTR_ERR(ci); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (crypto_skcipher_setkey(ci, prep->data, 8) < 0) 7562306a36Sopenharmony_ci BUG(); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci prep->payload.data[0] = ci; 7862306a36Sopenharmony_ci _leave(" = 0"); 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void rxkad_free_preparse_server_key(struct key_preparsed_payload *prep) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (prep->payload.data[0]) 8662306a36Sopenharmony_ci crypto_free_skcipher(prep->payload.data[0]); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void rxkad_destroy_server_key(struct key *key) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci if (key->payload.data[0]) { 9262306a36Sopenharmony_ci crypto_free_skcipher(key->payload.data[0]); 9362306a36Sopenharmony_ci key->payload.data[0] = NULL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * initialise connection security 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic int rxkad_init_connection_security(struct rxrpc_connection *conn, 10162306a36Sopenharmony_ci struct rxrpc_key_token *token) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct crypto_sync_skcipher *ci; 10462306a36Sopenharmony_ci int ret; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci conn->security_ix = token->security_index; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci ci = crypto_alloc_sync_skcipher("pcbc(fcrypt)", 0, 0); 11162306a36Sopenharmony_ci if (IS_ERR(ci)) { 11262306a36Sopenharmony_ci _debug("no cipher"); 11362306a36Sopenharmony_ci ret = PTR_ERR(ci); 11462306a36Sopenharmony_ci goto error; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (crypto_sync_skcipher_setkey(ci, token->kad->session_key, 11862306a36Sopenharmony_ci sizeof(token->kad->session_key)) < 0) 11962306a36Sopenharmony_ci BUG(); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci switch (conn->security_level) { 12262306a36Sopenharmony_ci case RXRPC_SECURITY_PLAIN: 12362306a36Sopenharmony_ci case RXRPC_SECURITY_AUTH: 12462306a36Sopenharmony_ci case RXRPC_SECURITY_ENCRYPT: 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci default: 12762306a36Sopenharmony_ci ret = -EKEYREJECTED; 12862306a36Sopenharmony_ci goto error; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ret = rxkad_prime_packet_security(conn, ci); 13262306a36Sopenharmony_ci if (ret < 0) 13362306a36Sopenharmony_ci goto error_ci; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci conn->rxkad.cipher = ci; 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cierror_ci: 13962306a36Sopenharmony_ci crypto_free_sync_skcipher(ci); 14062306a36Sopenharmony_cierror: 14162306a36Sopenharmony_ci _leave(" = %d", ret); 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * Work out how much data we can put in a packet. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cistatic int rxkad_how_much_data(struct rxrpc_call *call, size_t remain, 14962306a36Sopenharmony_ci size_t *_buf_size, size_t *_data_size, size_t *_offset) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci size_t shdr, buf_size, chunk; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci switch (call->conn->security_level) { 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci buf_size = chunk = min_t(size_t, remain, RXRPC_JUMBO_DATALEN); 15662306a36Sopenharmony_ci shdr = 0; 15762306a36Sopenharmony_ci goto out; 15862306a36Sopenharmony_ci case RXRPC_SECURITY_AUTH: 15962306a36Sopenharmony_ci shdr = sizeof(struct rxkad_level1_hdr); 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case RXRPC_SECURITY_ENCRYPT: 16262306a36Sopenharmony_ci shdr = sizeof(struct rxkad_level2_hdr); 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci buf_size = round_down(RXRPC_JUMBO_DATALEN, RXKAD_ALIGN); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci chunk = buf_size - shdr; 16962306a36Sopenharmony_ci if (remain < chunk) 17062306a36Sopenharmony_ci buf_size = round_up(shdr + remain, RXKAD_ALIGN); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciout: 17362306a36Sopenharmony_ci *_buf_size = buf_size; 17462306a36Sopenharmony_ci *_data_size = chunk; 17562306a36Sopenharmony_ci *_offset = shdr; 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * prime the encryption state with the invariant parts of a connection's 18162306a36Sopenharmony_ci * description 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic int rxkad_prime_packet_security(struct rxrpc_connection *conn, 18462306a36Sopenharmony_ci struct crypto_sync_skcipher *ci) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct skcipher_request *req; 18762306a36Sopenharmony_ci struct rxrpc_key_token *token; 18862306a36Sopenharmony_ci struct scatterlist sg; 18962306a36Sopenharmony_ci struct rxrpc_crypt iv; 19062306a36Sopenharmony_ci __be32 *tmpbuf; 19162306a36Sopenharmony_ci size_t tmpsize = 4 * sizeof(__be32); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci _enter(""); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (!conn->key) 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci tmpbuf = kmalloc(tmpsize, GFP_KERNEL); 19962306a36Sopenharmony_ci if (!tmpbuf) 20062306a36Sopenharmony_ci return -ENOMEM; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci req = skcipher_request_alloc(&ci->base, GFP_NOFS); 20362306a36Sopenharmony_ci if (!req) { 20462306a36Sopenharmony_ci kfree(tmpbuf); 20562306a36Sopenharmony_ci return -ENOMEM; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci token = conn->key->payload.data[0]; 20962306a36Sopenharmony_ci memcpy(&iv, token->kad->session_key, sizeof(iv)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci tmpbuf[0] = htonl(conn->proto.epoch); 21262306a36Sopenharmony_ci tmpbuf[1] = htonl(conn->proto.cid); 21362306a36Sopenharmony_ci tmpbuf[2] = 0; 21462306a36Sopenharmony_ci tmpbuf[3] = htonl(conn->security_ix); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci sg_init_one(&sg, tmpbuf, tmpsize); 21762306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, ci); 21862306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 21962306a36Sopenharmony_ci skcipher_request_set_crypt(req, &sg, &sg, tmpsize, iv.x); 22062306a36Sopenharmony_ci crypto_skcipher_encrypt(req); 22162306a36Sopenharmony_ci skcipher_request_free(req); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci memcpy(&conn->rxkad.csum_iv, tmpbuf + 2, sizeof(conn->rxkad.csum_iv)); 22462306a36Sopenharmony_ci kfree(tmpbuf); 22562306a36Sopenharmony_ci _leave(" = 0"); 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * Allocate and prepare the crypto request on a call. For any particular call, 23162306a36Sopenharmony_ci * this is called serially for the packets, so no lock should be necessary. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_cistatic struct skcipher_request *rxkad_get_call_crypto(struct rxrpc_call *call) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct crypto_skcipher *tfm = &call->conn->rxkad.cipher->base; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return skcipher_request_alloc(tfm, GFP_NOFS); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* 24162306a36Sopenharmony_ci * Clean up the crypto on a call. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic void rxkad_free_call_crypto(struct rxrpc_call *call) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * partially encrypt a packet (level 1 security) 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic int rxkad_secure_packet_auth(const struct rxrpc_call *call, 25162306a36Sopenharmony_ci struct rxrpc_txbuf *txb, 25262306a36Sopenharmony_ci struct skcipher_request *req) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct rxkad_level1_hdr *hdr = (void *)txb->data; 25562306a36Sopenharmony_ci struct rxrpc_crypt iv; 25662306a36Sopenharmony_ci struct scatterlist sg; 25762306a36Sopenharmony_ci size_t pad; 25862306a36Sopenharmony_ci u16 check; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci _enter(""); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci check = txb->seq ^ ntohl(txb->wire.callNumber); 26362306a36Sopenharmony_ci hdr->data_size = htonl((u32)check << 16 | txb->len); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci txb->len += sizeof(struct rxkad_level1_hdr); 26662306a36Sopenharmony_ci pad = txb->len; 26762306a36Sopenharmony_ci pad = RXKAD_ALIGN - pad; 26862306a36Sopenharmony_ci pad &= RXKAD_ALIGN - 1; 26962306a36Sopenharmony_ci if (pad) { 27062306a36Sopenharmony_ci memset(txb->data + txb->offset, 0, pad); 27162306a36Sopenharmony_ci txb->len += pad; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* start the encryption afresh */ 27562306a36Sopenharmony_ci memset(&iv, 0, sizeof(iv)); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci sg_init_one(&sg, txb->data, 8); 27862306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); 27962306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 28062306a36Sopenharmony_ci skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); 28162306a36Sopenharmony_ci crypto_skcipher_encrypt(req); 28262306a36Sopenharmony_ci skcipher_request_zero(req); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci _leave(" = 0"); 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * wholly encrypt a packet (level 2 security) 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, 29262306a36Sopenharmony_ci struct rxrpc_txbuf *txb, 29362306a36Sopenharmony_ci struct skcipher_request *req) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci const struct rxrpc_key_token *token; 29662306a36Sopenharmony_ci struct rxkad_level2_hdr *rxkhdr = (void *)txb->data; 29762306a36Sopenharmony_ci struct rxrpc_crypt iv; 29862306a36Sopenharmony_ci struct scatterlist sg; 29962306a36Sopenharmony_ci size_t pad; 30062306a36Sopenharmony_ci u16 check; 30162306a36Sopenharmony_ci int ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci _enter(""); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci check = txb->seq ^ ntohl(txb->wire.callNumber); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci rxkhdr->data_size = htonl(txb->len | (u32)check << 16); 30862306a36Sopenharmony_ci rxkhdr->checksum = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci txb->len += sizeof(struct rxkad_level2_hdr); 31162306a36Sopenharmony_ci pad = txb->len; 31262306a36Sopenharmony_ci pad = RXKAD_ALIGN - pad; 31362306a36Sopenharmony_ci pad &= RXKAD_ALIGN - 1; 31462306a36Sopenharmony_ci if (pad) { 31562306a36Sopenharmony_ci memset(txb->data + txb->offset, 0, pad); 31662306a36Sopenharmony_ci txb->len += pad; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* encrypt from the session key */ 32062306a36Sopenharmony_ci token = call->conn->key->payload.data[0]; 32162306a36Sopenharmony_ci memcpy(&iv, token->kad->session_key, sizeof(iv)); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci sg_init_one(&sg, txb->data, txb->len); 32462306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); 32562306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 32662306a36Sopenharmony_ci skcipher_request_set_crypt(req, &sg, &sg, txb->len, iv.x); 32762306a36Sopenharmony_ci ret = crypto_skcipher_encrypt(req); 32862306a36Sopenharmony_ci skcipher_request_zero(req); 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * checksum an RxRPC packet header 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_cistatic int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct skcipher_request *req; 33862306a36Sopenharmony_ci struct rxrpc_crypt iv; 33962306a36Sopenharmony_ci struct scatterlist sg; 34062306a36Sopenharmony_ci union { 34162306a36Sopenharmony_ci __be32 buf[2]; 34262306a36Sopenharmony_ci } crypto __aligned(8); 34362306a36Sopenharmony_ci u32 x, y; 34462306a36Sopenharmony_ci int ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci _enter("{%d{%x}},{#%u},%u,", 34762306a36Sopenharmony_ci call->debug_id, key_serial(call->conn->key), 34862306a36Sopenharmony_ci txb->seq, txb->len); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!call->conn->rxkad.cipher) 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = key_validate(call->conn->key); 35462306a36Sopenharmony_ci if (ret < 0) 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci req = rxkad_get_call_crypto(call); 35862306a36Sopenharmony_ci if (!req) 35962306a36Sopenharmony_ci return -ENOMEM; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* continue encrypting from where we left off */ 36262306a36Sopenharmony_ci memcpy(&iv, call->conn->rxkad.csum_iv.x, sizeof(iv)); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* calculate the security checksum */ 36562306a36Sopenharmony_ci x = (ntohl(txb->wire.cid) & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); 36662306a36Sopenharmony_ci x |= txb->seq & 0x3fffffff; 36762306a36Sopenharmony_ci crypto.buf[0] = txb->wire.callNumber; 36862306a36Sopenharmony_ci crypto.buf[1] = htonl(x); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci sg_init_one(&sg, crypto.buf, 8); 37162306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); 37262306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 37362306a36Sopenharmony_ci skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); 37462306a36Sopenharmony_ci crypto_skcipher_encrypt(req); 37562306a36Sopenharmony_ci skcipher_request_zero(req); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci y = ntohl(crypto.buf[1]); 37862306a36Sopenharmony_ci y = (y >> 16) & 0xffff; 37962306a36Sopenharmony_ci if (y == 0) 38062306a36Sopenharmony_ci y = 1; /* zero checksums are not permitted */ 38162306a36Sopenharmony_ci txb->wire.cksum = htons(y); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci switch (call->conn->security_level) { 38462306a36Sopenharmony_ci case RXRPC_SECURITY_PLAIN: 38562306a36Sopenharmony_ci ret = 0; 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci case RXRPC_SECURITY_AUTH: 38862306a36Sopenharmony_ci ret = rxkad_secure_packet_auth(call, txb, req); 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci case RXRPC_SECURITY_ENCRYPT: 39162306a36Sopenharmony_ci ret = rxkad_secure_packet_encrypt(call, txb, req); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci default: 39462306a36Sopenharmony_ci ret = -EPERM; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci skcipher_request_free(req); 39962306a36Sopenharmony_ci _leave(" = %d [set %x]", ret, y); 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * decrypt partial encryption on a packet (level 1 security) 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_cistatic int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, 40762306a36Sopenharmony_ci rxrpc_seq_t seq, 40862306a36Sopenharmony_ci struct skcipher_request *req) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct rxkad_level1_hdr sechdr; 41162306a36Sopenharmony_ci struct rxrpc_skb_priv *sp = rxrpc_skb(skb); 41262306a36Sopenharmony_ci struct rxrpc_crypt iv; 41362306a36Sopenharmony_ci struct scatterlist sg[16]; 41462306a36Sopenharmony_ci u32 data_size, buf; 41562306a36Sopenharmony_ci u16 check; 41662306a36Sopenharmony_ci int ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci _enter(""); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (sp->len < 8) 42162306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, 42262306a36Sopenharmony_ci rxkad_abort_1_short_header); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Decrypt the skbuff in-place. TODO: We really want to decrypt 42562306a36Sopenharmony_ci * directly into the target buffer. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci sg_init_table(sg, ARRAY_SIZE(sg)); 42862306a36Sopenharmony_ci ret = skb_to_sgvec(skb, sg, sp->offset, 8); 42962306a36Sopenharmony_ci if (unlikely(ret < 0)) 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* start the decryption afresh */ 43362306a36Sopenharmony_ci memset(&iv, 0, sizeof(iv)); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); 43662306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 43762306a36Sopenharmony_ci skcipher_request_set_crypt(req, sg, sg, 8, iv.x); 43862306a36Sopenharmony_ci crypto_skcipher_decrypt(req); 43962306a36Sopenharmony_ci skcipher_request_zero(req); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Extract the decrypted packet length */ 44262306a36Sopenharmony_ci if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) 44362306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADDATALEN, 44462306a36Sopenharmony_ci rxkad_abort_1_short_encdata); 44562306a36Sopenharmony_ci sp->offset += sizeof(sechdr); 44662306a36Sopenharmony_ci sp->len -= sizeof(sechdr); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci buf = ntohl(sechdr.data_size); 44962306a36Sopenharmony_ci data_size = buf & 0xffff; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci check = buf >> 16; 45262306a36Sopenharmony_ci check ^= seq ^ call->call_id; 45362306a36Sopenharmony_ci check &= 0xffff; 45462306a36Sopenharmony_ci if (check != 0) 45562306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, 45662306a36Sopenharmony_ci rxkad_abort_1_short_check); 45762306a36Sopenharmony_ci if (data_size > sp->len) 45862306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADDATALEN, 45962306a36Sopenharmony_ci rxkad_abort_1_short_data); 46062306a36Sopenharmony_ci sp->len = data_size; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci _leave(" = 0 [dlen=%x]", data_size); 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* 46762306a36Sopenharmony_ci * wholly decrypt a packet (level 2 security) 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_cistatic int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, 47062306a36Sopenharmony_ci rxrpc_seq_t seq, 47162306a36Sopenharmony_ci struct skcipher_request *req) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci const struct rxrpc_key_token *token; 47462306a36Sopenharmony_ci struct rxkad_level2_hdr sechdr; 47562306a36Sopenharmony_ci struct rxrpc_skb_priv *sp = rxrpc_skb(skb); 47662306a36Sopenharmony_ci struct rxrpc_crypt iv; 47762306a36Sopenharmony_ci struct scatterlist _sg[4], *sg; 47862306a36Sopenharmony_ci u32 data_size, buf; 47962306a36Sopenharmony_ci u16 check; 48062306a36Sopenharmony_ci int nsg, ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci _enter(",{%d}", sp->len); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (sp->len < 8) 48562306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, 48662306a36Sopenharmony_ci rxkad_abort_2_short_header); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Decrypt the skbuff in-place. TODO: We really want to decrypt 48962306a36Sopenharmony_ci * directly into the target buffer. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci sg = _sg; 49262306a36Sopenharmony_ci nsg = skb_shinfo(skb)->nr_frags + 1; 49362306a36Sopenharmony_ci if (nsg <= 4) { 49462306a36Sopenharmony_ci nsg = 4; 49562306a36Sopenharmony_ci } else { 49662306a36Sopenharmony_ci sg = kmalloc_array(nsg, sizeof(*sg), GFP_NOIO); 49762306a36Sopenharmony_ci if (!sg) 49862306a36Sopenharmony_ci return -ENOMEM; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci sg_init_table(sg, nsg); 50262306a36Sopenharmony_ci ret = skb_to_sgvec(skb, sg, sp->offset, sp->len); 50362306a36Sopenharmony_ci if (unlikely(ret < 0)) { 50462306a36Sopenharmony_ci if (sg != _sg) 50562306a36Sopenharmony_ci kfree(sg); 50662306a36Sopenharmony_ci return ret; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* decrypt from the session key */ 51062306a36Sopenharmony_ci token = call->conn->key->payload.data[0]; 51162306a36Sopenharmony_ci memcpy(&iv, token->kad->session_key, sizeof(iv)); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); 51462306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 51562306a36Sopenharmony_ci skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x); 51662306a36Sopenharmony_ci crypto_skcipher_decrypt(req); 51762306a36Sopenharmony_ci skcipher_request_zero(req); 51862306a36Sopenharmony_ci if (sg != _sg) 51962306a36Sopenharmony_ci kfree(sg); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* Extract the decrypted packet length */ 52262306a36Sopenharmony_ci if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) 52362306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADDATALEN, 52462306a36Sopenharmony_ci rxkad_abort_2_short_len); 52562306a36Sopenharmony_ci sp->offset += sizeof(sechdr); 52662306a36Sopenharmony_ci sp->len -= sizeof(sechdr); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci buf = ntohl(sechdr.data_size); 52962306a36Sopenharmony_ci data_size = buf & 0xffff; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci check = buf >> 16; 53262306a36Sopenharmony_ci check ^= seq ^ call->call_id; 53362306a36Sopenharmony_ci check &= 0xffff; 53462306a36Sopenharmony_ci if (check != 0) 53562306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, 53662306a36Sopenharmony_ci rxkad_abort_2_short_check); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (data_size > sp->len) 53962306a36Sopenharmony_ci return rxrpc_abort_eproto(call, skb, RXKADDATALEN, 54062306a36Sopenharmony_ci rxkad_abort_2_short_data); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci sp->len = data_size; 54362306a36Sopenharmony_ci _leave(" = 0 [dlen=%x]", data_size); 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/* 54862306a36Sopenharmony_ci * Verify the security on a received packet and the subpackets therein. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_cistatic int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct rxrpc_skb_priv *sp = rxrpc_skb(skb); 55362306a36Sopenharmony_ci struct skcipher_request *req; 55462306a36Sopenharmony_ci struct rxrpc_crypt iv; 55562306a36Sopenharmony_ci struct scatterlist sg; 55662306a36Sopenharmony_ci union { 55762306a36Sopenharmony_ci __be32 buf[2]; 55862306a36Sopenharmony_ci } crypto __aligned(8); 55962306a36Sopenharmony_ci rxrpc_seq_t seq = sp->hdr.seq; 56062306a36Sopenharmony_ci int ret; 56162306a36Sopenharmony_ci u16 cksum; 56262306a36Sopenharmony_ci u32 x, y; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci _enter("{%d{%x}},{#%u}", 56562306a36Sopenharmony_ci call->debug_id, key_serial(call->conn->key), seq); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!call->conn->rxkad.cipher) 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci req = rxkad_get_call_crypto(call); 57162306a36Sopenharmony_ci if (!req) 57262306a36Sopenharmony_ci return -ENOMEM; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* continue encrypting from where we left off */ 57562306a36Sopenharmony_ci memcpy(&iv, call->conn->rxkad.csum_iv.x, sizeof(iv)); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* validate the security checksum */ 57862306a36Sopenharmony_ci x = (call->cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); 57962306a36Sopenharmony_ci x |= seq & 0x3fffffff; 58062306a36Sopenharmony_ci crypto.buf[0] = htonl(call->call_id); 58162306a36Sopenharmony_ci crypto.buf[1] = htonl(x); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci sg_init_one(&sg, crypto.buf, 8); 58462306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); 58562306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 58662306a36Sopenharmony_ci skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); 58762306a36Sopenharmony_ci crypto_skcipher_encrypt(req); 58862306a36Sopenharmony_ci skcipher_request_zero(req); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci y = ntohl(crypto.buf[1]); 59162306a36Sopenharmony_ci cksum = (y >> 16) & 0xffff; 59262306a36Sopenharmony_ci if (cksum == 0) 59362306a36Sopenharmony_ci cksum = 1; /* zero checksums are not permitted */ 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (cksum != sp->hdr.cksum) { 59662306a36Sopenharmony_ci ret = rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, 59762306a36Sopenharmony_ci rxkad_abort_bad_checksum); 59862306a36Sopenharmony_ci goto out; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci switch (call->conn->security_level) { 60262306a36Sopenharmony_ci case RXRPC_SECURITY_PLAIN: 60362306a36Sopenharmony_ci ret = 0; 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci case RXRPC_SECURITY_AUTH: 60662306a36Sopenharmony_ci ret = rxkad_verify_packet_1(call, skb, seq, req); 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci case RXRPC_SECURITY_ENCRYPT: 60962306a36Sopenharmony_ci ret = rxkad_verify_packet_2(call, skb, seq, req); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci default: 61262306a36Sopenharmony_ci ret = -ENOANO; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciout: 61762306a36Sopenharmony_ci skcipher_request_free(req); 61862306a36Sopenharmony_ci return ret; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/* 62262306a36Sopenharmony_ci * issue a challenge 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_cistatic int rxkad_issue_challenge(struct rxrpc_connection *conn) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct rxkad_challenge challenge; 62762306a36Sopenharmony_ci struct rxrpc_wire_header whdr; 62862306a36Sopenharmony_ci struct msghdr msg; 62962306a36Sopenharmony_ci struct kvec iov[2]; 63062306a36Sopenharmony_ci size_t len; 63162306a36Sopenharmony_ci u32 serial; 63262306a36Sopenharmony_ci int ret; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci _enter("{%d}", conn->debug_id); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci get_random_bytes(&conn->rxkad.nonce, sizeof(conn->rxkad.nonce)); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci challenge.version = htonl(2); 63962306a36Sopenharmony_ci challenge.nonce = htonl(conn->rxkad.nonce); 64062306a36Sopenharmony_ci challenge.min_level = htonl(0); 64162306a36Sopenharmony_ci challenge.__padding = 0; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci msg.msg_name = &conn->peer->srx.transport; 64462306a36Sopenharmony_ci msg.msg_namelen = conn->peer->srx.transport_len; 64562306a36Sopenharmony_ci msg.msg_control = NULL; 64662306a36Sopenharmony_ci msg.msg_controllen = 0; 64762306a36Sopenharmony_ci msg.msg_flags = 0; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci whdr.epoch = htonl(conn->proto.epoch); 65062306a36Sopenharmony_ci whdr.cid = htonl(conn->proto.cid); 65162306a36Sopenharmony_ci whdr.callNumber = 0; 65262306a36Sopenharmony_ci whdr.seq = 0; 65362306a36Sopenharmony_ci whdr.type = RXRPC_PACKET_TYPE_CHALLENGE; 65462306a36Sopenharmony_ci whdr.flags = conn->out_clientflag; 65562306a36Sopenharmony_ci whdr.userStatus = 0; 65662306a36Sopenharmony_ci whdr.securityIndex = conn->security_ix; 65762306a36Sopenharmony_ci whdr._rsvd = 0; 65862306a36Sopenharmony_ci whdr.serviceId = htons(conn->service_id); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci iov[0].iov_base = &whdr; 66162306a36Sopenharmony_ci iov[0].iov_len = sizeof(whdr); 66262306a36Sopenharmony_ci iov[1].iov_base = &challenge; 66362306a36Sopenharmony_ci iov[1].iov_len = sizeof(challenge); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci len = iov[0].iov_len + iov[1].iov_len; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci serial = rxrpc_get_next_serial(conn); 66862306a36Sopenharmony_ci whdr.serial = htonl(serial); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = kernel_sendmsg(conn->local->socket, &msg, iov, 2, len); 67162306a36Sopenharmony_ci if (ret < 0) { 67262306a36Sopenharmony_ci trace_rxrpc_tx_fail(conn->debug_id, serial, ret, 67362306a36Sopenharmony_ci rxrpc_tx_point_rxkad_challenge); 67462306a36Sopenharmony_ci return -EAGAIN; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci conn->peer->last_tx_at = ktime_get_seconds(); 67862306a36Sopenharmony_ci trace_rxrpc_tx_packet(conn->debug_id, &whdr, 67962306a36Sopenharmony_ci rxrpc_tx_point_rxkad_challenge); 68062306a36Sopenharmony_ci _leave(" = 0"); 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* 68562306a36Sopenharmony_ci * send a Kerberos security response 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_cistatic int rxkad_send_response(struct rxrpc_connection *conn, 68862306a36Sopenharmony_ci struct rxrpc_host_header *hdr, 68962306a36Sopenharmony_ci struct rxkad_response *resp, 69062306a36Sopenharmony_ci const struct rxkad_key *s2) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct rxrpc_wire_header whdr; 69362306a36Sopenharmony_ci struct msghdr msg; 69462306a36Sopenharmony_ci struct kvec iov[3]; 69562306a36Sopenharmony_ci size_t len; 69662306a36Sopenharmony_ci u32 serial; 69762306a36Sopenharmony_ci int ret; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci _enter(""); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci msg.msg_name = &conn->peer->srx.transport; 70262306a36Sopenharmony_ci msg.msg_namelen = conn->peer->srx.transport_len; 70362306a36Sopenharmony_ci msg.msg_control = NULL; 70462306a36Sopenharmony_ci msg.msg_controllen = 0; 70562306a36Sopenharmony_ci msg.msg_flags = 0; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci memset(&whdr, 0, sizeof(whdr)); 70862306a36Sopenharmony_ci whdr.epoch = htonl(hdr->epoch); 70962306a36Sopenharmony_ci whdr.cid = htonl(hdr->cid); 71062306a36Sopenharmony_ci whdr.type = RXRPC_PACKET_TYPE_RESPONSE; 71162306a36Sopenharmony_ci whdr.flags = conn->out_clientflag; 71262306a36Sopenharmony_ci whdr.securityIndex = hdr->securityIndex; 71362306a36Sopenharmony_ci whdr.serviceId = htons(hdr->serviceId); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci iov[0].iov_base = &whdr; 71662306a36Sopenharmony_ci iov[0].iov_len = sizeof(whdr); 71762306a36Sopenharmony_ci iov[1].iov_base = resp; 71862306a36Sopenharmony_ci iov[1].iov_len = sizeof(*resp); 71962306a36Sopenharmony_ci iov[2].iov_base = (void *)s2->ticket; 72062306a36Sopenharmony_ci iov[2].iov_len = s2->ticket_len; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci serial = rxrpc_get_next_serial(conn); 72562306a36Sopenharmony_ci whdr.serial = htonl(serial); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci rxrpc_local_dont_fragment(conn->local, false); 72862306a36Sopenharmony_ci ret = kernel_sendmsg(conn->local->socket, &msg, iov, 3, len); 72962306a36Sopenharmony_ci rxrpc_local_dont_fragment(conn->local, true); 73062306a36Sopenharmony_ci if (ret < 0) { 73162306a36Sopenharmony_ci trace_rxrpc_tx_fail(conn->debug_id, serial, ret, 73262306a36Sopenharmony_ci rxrpc_tx_point_rxkad_response); 73362306a36Sopenharmony_ci return -EAGAIN; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci conn->peer->last_tx_at = ktime_get_seconds(); 73762306a36Sopenharmony_ci _leave(" = 0"); 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/* 74262306a36Sopenharmony_ci * calculate the response checksum 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_cistatic void rxkad_calc_response_checksum(struct rxkad_response *response) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci u32 csum = 1000003; 74762306a36Sopenharmony_ci int loop; 74862306a36Sopenharmony_ci u8 *p = (u8 *) response; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci for (loop = sizeof(*response); loop > 0; loop--) 75162306a36Sopenharmony_ci csum = csum * 0x10204081 + *p++; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci response->encrypted.checksum = htonl(csum); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* 75762306a36Sopenharmony_ci * encrypt the response packet 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_cistatic int rxkad_encrypt_response(struct rxrpc_connection *conn, 76062306a36Sopenharmony_ci struct rxkad_response *resp, 76162306a36Sopenharmony_ci const struct rxkad_key *s2) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct skcipher_request *req; 76462306a36Sopenharmony_ci struct rxrpc_crypt iv; 76562306a36Sopenharmony_ci struct scatterlist sg[1]; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci req = skcipher_request_alloc(&conn->rxkad.cipher->base, GFP_NOFS); 76862306a36Sopenharmony_ci if (!req) 76962306a36Sopenharmony_ci return -ENOMEM; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* continue encrypting from where we left off */ 77262306a36Sopenharmony_ci memcpy(&iv, s2->session_key, sizeof(iv)); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci sg_init_table(sg, 1); 77562306a36Sopenharmony_ci sg_set_buf(sg, &resp->encrypted, sizeof(resp->encrypted)); 77662306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, conn->rxkad.cipher); 77762306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 77862306a36Sopenharmony_ci skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x); 77962306a36Sopenharmony_ci crypto_skcipher_encrypt(req); 78062306a36Sopenharmony_ci skcipher_request_free(req); 78162306a36Sopenharmony_ci return 0; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci/* 78562306a36Sopenharmony_ci * respond to a challenge packet 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_cistatic int rxkad_respond_to_challenge(struct rxrpc_connection *conn, 78862306a36Sopenharmony_ci struct sk_buff *skb) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci const struct rxrpc_key_token *token; 79162306a36Sopenharmony_ci struct rxkad_challenge challenge; 79262306a36Sopenharmony_ci struct rxkad_response *resp; 79362306a36Sopenharmony_ci struct rxrpc_skb_priv *sp = rxrpc_skb(skb); 79462306a36Sopenharmony_ci u32 version, nonce, min_level; 79562306a36Sopenharmony_ci int ret = -EPROTO; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci _enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (!conn->key) 80062306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO, 80162306a36Sopenharmony_ci rxkad_abort_chall_no_key); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci ret = key_validate(conn->key); 80462306a36Sopenharmony_ci if (ret < 0) 80562306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, ret, 80662306a36Sopenharmony_ci rxkad_abort_chall_key_expired); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), 80962306a36Sopenharmony_ci &challenge, sizeof(challenge)) < 0) 81062306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, 81162306a36Sopenharmony_ci rxkad_abort_chall_short); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci version = ntohl(challenge.version); 81462306a36Sopenharmony_ci nonce = ntohl(challenge.nonce); 81562306a36Sopenharmony_ci min_level = ntohl(challenge.min_level); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci trace_rxrpc_rx_challenge(conn, sp->hdr.serial, version, nonce, min_level); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (version != RXKAD_VERSION) 82062306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO, 82162306a36Sopenharmony_ci rxkad_abort_chall_version); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (conn->security_level < min_level) 82462306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EACCES, 82562306a36Sopenharmony_ci rxkad_abort_chall_level); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci token = conn->key->payload.data[0]; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* build the response packet */ 83062306a36Sopenharmony_ci resp = kzalloc(sizeof(struct rxkad_response), GFP_NOFS); 83162306a36Sopenharmony_ci if (!resp) 83262306a36Sopenharmony_ci return -ENOMEM; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci resp->version = htonl(RXKAD_VERSION); 83562306a36Sopenharmony_ci resp->encrypted.epoch = htonl(conn->proto.epoch); 83662306a36Sopenharmony_ci resp->encrypted.cid = htonl(conn->proto.cid); 83762306a36Sopenharmony_ci resp->encrypted.securityIndex = htonl(conn->security_ix); 83862306a36Sopenharmony_ci resp->encrypted.inc_nonce = htonl(nonce + 1); 83962306a36Sopenharmony_ci resp->encrypted.level = htonl(conn->security_level); 84062306a36Sopenharmony_ci resp->kvno = htonl(token->kad->kvno); 84162306a36Sopenharmony_ci resp->ticket_len = htonl(token->kad->ticket_len); 84262306a36Sopenharmony_ci resp->encrypted.call_id[0] = htonl(conn->channels[0].call_counter); 84362306a36Sopenharmony_ci resp->encrypted.call_id[1] = htonl(conn->channels[1].call_counter); 84462306a36Sopenharmony_ci resp->encrypted.call_id[2] = htonl(conn->channels[2].call_counter); 84562306a36Sopenharmony_ci resp->encrypted.call_id[3] = htonl(conn->channels[3].call_counter); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* calculate the response checksum and then do the encryption */ 84862306a36Sopenharmony_ci rxkad_calc_response_checksum(resp); 84962306a36Sopenharmony_ci ret = rxkad_encrypt_response(conn, resp, token->kad); 85062306a36Sopenharmony_ci if (ret == 0) 85162306a36Sopenharmony_ci ret = rxkad_send_response(conn, &sp->hdr, resp, token->kad); 85262306a36Sopenharmony_ci kfree(resp); 85362306a36Sopenharmony_ci return ret; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci/* 85762306a36Sopenharmony_ci * decrypt the kerberos IV ticket in the response 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_cistatic int rxkad_decrypt_ticket(struct rxrpc_connection *conn, 86062306a36Sopenharmony_ci struct key *server_key, 86162306a36Sopenharmony_ci struct sk_buff *skb, 86262306a36Sopenharmony_ci void *ticket, size_t ticket_len, 86362306a36Sopenharmony_ci struct rxrpc_crypt *_session_key, 86462306a36Sopenharmony_ci time64_t *_expiry) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct skcipher_request *req; 86762306a36Sopenharmony_ci struct rxrpc_crypt iv, key; 86862306a36Sopenharmony_ci struct scatterlist sg[1]; 86962306a36Sopenharmony_ci struct in_addr addr; 87062306a36Sopenharmony_ci unsigned int life; 87162306a36Sopenharmony_ci time64_t issue, now; 87262306a36Sopenharmony_ci bool little_endian; 87362306a36Sopenharmony_ci u8 *p, *q, *name, *end; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci _enter("{%d},{%x}", conn->debug_id, key_serial(server_key)); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci *_expiry = 0; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci ASSERT(server_key->payload.data[0] != NULL); 88062306a36Sopenharmony_ci ASSERTCMP((unsigned long) ticket & 7UL, ==, 0); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci memcpy(&iv, &server_key->payload.data[2], sizeof(iv)); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci req = skcipher_request_alloc(server_key->payload.data[0], GFP_NOFS); 88562306a36Sopenharmony_ci if (!req) 88662306a36Sopenharmony_ci return -ENOMEM; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci sg_init_one(&sg[0], ticket, ticket_len); 88962306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 89062306a36Sopenharmony_ci skcipher_request_set_crypt(req, sg, sg, ticket_len, iv.x); 89162306a36Sopenharmony_ci crypto_skcipher_decrypt(req); 89262306a36Sopenharmony_ci skcipher_request_free(req); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci p = ticket; 89562306a36Sopenharmony_ci end = p + ticket_len; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci#define Z(field, fieldl) \ 89862306a36Sopenharmony_ci ({ \ 89962306a36Sopenharmony_ci u8 *__str = p; \ 90062306a36Sopenharmony_ci q = memchr(p, 0, end - p); \ 90162306a36Sopenharmony_ci if (!q || q - p > field##_SZ) \ 90262306a36Sopenharmony_ci return rxrpc_abort_conn( \ 90362306a36Sopenharmony_ci conn, skb, RXKADBADTICKET, -EPROTO, \ 90462306a36Sopenharmony_ci rxkad_abort_resp_tkt_##fieldl); \ 90562306a36Sopenharmony_ci for (; p < q; p++) \ 90662306a36Sopenharmony_ci if (!isprint(*p)) \ 90762306a36Sopenharmony_ci return rxrpc_abort_conn( \ 90862306a36Sopenharmony_ci conn, skb, RXKADBADTICKET, -EPROTO, \ 90962306a36Sopenharmony_ci rxkad_abort_resp_tkt_##fieldl); \ 91062306a36Sopenharmony_ci p++; \ 91162306a36Sopenharmony_ci __str; \ 91262306a36Sopenharmony_ci }) 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* extract the ticket flags */ 91562306a36Sopenharmony_ci _debug("KIV FLAGS: %x", *p); 91662306a36Sopenharmony_ci little_endian = *p & 1; 91762306a36Sopenharmony_ci p++; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* extract the authentication name */ 92062306a36Sopenharmony_ci name = Z(ANAME, aname); 92162306a36Sopenharmony_ci _debug("KIV ANAME: %s", name); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* extract the principal's instance */ 92462306a36Sopenharmony_ci name = Z(INST, inst); 92562306a36Sopenharmony_ci _debug("KIV INST : %s", name); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* extract the principal's authentication domain */ 92862306a36Sopenharmony_ci name = Z(REALM, realm); 92962306a36Sopenharmony_ci _debug("KIV REALM: %s", name); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (end - p < 4 + 8 + 4 + 2) 93262306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADBADTICKET, -EPROTO, 93362306a36Sopenharmony_ci rxkad_abort_resp_tkt_short); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* get the IPv4 address of the entity that requested the ticket */ 93662306a36Sopenharmony_ci memcpy(&addr, p, sizeof(addr)); 93762306a36Sopenharmony_ci p += 4; 93862306a36Sopenharmony_ci _debug("KIV ADDR : %pI4", &addr); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* get the session key from the ticket */ 94162306a36Sopenharmony_ci memcpy(&key, p, sizeof(key)); 94262306a36Sopenharmony_ci p += 8; 94362306a36Sopenharmony_ci _debug("KIV KEY : %08x %08x", ntohl(key.n[0]), ntohl(key.n[1])); 94462306a36Sopenharmony_ci memcpy(_session_key, &key, sizeof(key)); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* get the ticket's lifetime */ 94762306a36Sopenharmony_ci life = *p++ * 5 * 60; 94862306a36Sopenharmony_ci _debug("KIV LIFE : %u", life); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* get the issue time of the ticket */ 95162306a36Sopenharmony_ci if (little_endian) { 95262306a36Sopenharmony_ci __le32 stamp; 95362306a36Sopenharmony_ci memcpy(&stamp, p, 4); 95462306a36Sopenharmony_ci issue = rxrpc_u32_to_time64(le32_to_cpu(stamp)); 95562306a36Sopenharmony_ci } else { 95662306a36Sopenharmony_ci __be32 stamp; 95762306a36Sopenharmony_ci memcpy(&stamp, p, 4); 95862306a36Sopenharmony_ci issue = rxrpc_u32_to_time64(be32_to_cpu(stamp)); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci p += 4; 96162306a36Sopenharmony_ci now = ktime_get_real_seconds(); 96262306a36Sopenharmony_ci _debug("KIV ISSUE: %llx [%llx]", issue, now); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* check the ticket is in date */ 96562306a36Sopenharmony_ci if (issue > now) 96662306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADNOAUTH, -EKEYREJECTED, 96762306a36Sopenharmony_ci rxkad_abort_resp_tkt_future); 96862306a36Sopenharmony_ci if (issue < now - life) 96962306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, -EKEYEXPIRED, 97062306a36Sopenharmony_ci rxkad_abort_resp_tkt_expired); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci *_expiry = issue + life; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* get the service name */ 97562306a36Sopenharmony_ci name = Z(SNAME, sname); 97662306a36Sopenharmony_ci _debug("KIV SNAME: %s", name); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* get the service instance name */ 97962306a36Sopenharmony_ci name = Z(INST, sinst); 98062306a36Sopenharmony_ci _debug("KIV SINST: %s", name); 98162306a36Sopenharmony_ci return 0; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci/* 98562306a36Sopenharmony_ci * decrypt the response packet 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_cistatic void rxkad_decrypt_response(struct rxrpc_connection *conn, 98862306a36Sopenharmony_ci struct rxkad_response *resp, 98962306a36Sopenharmony_ci const struct rxrpc_crypt *session_key) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci struct skcipher_request *req = rxkad_ci_req; 99262306a36Sopenharmony_ci struct scatterlist sg[1]; 99362306a36Sopenharmony_ci struct rxrpc_crypt iv; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci _enter(",,%08x%08x", 99662306a36Sopenharmony_ci ntohl(session_key->n[0]), ntohl(session_key->n[1])); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci mutex_lock(&rxkad_ci_mutex); 99962306a36Sopenharmony_ci if (crypto_sync_skcipher_setkey(rxkad_ci, session_key->x, 100062306a36Sopenharmony_ci sizeof(*session_key)) < 0) 100162306a36Sopenharmony_ci BUG(); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci memcpy(&iv, session_key, sizeof(iv)); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci sg_init_table(sg, 1); 100662306a36Sopenharmony_ci sg_set_buf(sg, &resp->encrypted, sizeof(resp->encrypted)); 100762306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, rxkad_ci); 100862306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 100962306a36Sopenharmony_ci skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x); 101062306a36Sopenharmony_ci crypto_skcipher_decrypt(req); 101162306a36Sopenharmony_ci skcipher_request_zero(req); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci mutex_unlock(&rxkad_ci_mutex); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci _leave(""); 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci/* 101962306a36Sopenharmony_ci * verify a response 102062306a36Sopenharmony_ci */ 102162306a36Sopenharmony_cistatic int rxkad_verify_response(struct rxrpc_connection *conn, 102262306a36Sopenharmony_ci struct sk_buff *skb) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct rxkad_response *response; 102562306a36Sopenharmony_ci struct rxrpc_skb_priv *sp = rxrpc_skb(skb); 102662306a36Sopenharmony_ci struct rxrpc_crypt session_key; 102762306a36Sopenharmony_ci struct key *server_key; 102862306a36Sopenharmony_ci time64_t expiry; 102962306a36Sopenharmony_ci void *ticket; 103062306a36Sopenharmony_ci u32 version, kvno, ticket_len, level; 103162306a36Sopenharmony_ci __be32 csum; 103262306a36Sopenharmony_ci int ret, i; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci _enter("{%d}", conn->debug_id); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci server_key = rxrpc_look_up_server_security(conn, skb, 0, 0); 103762306a36Sopenharmony_ci if (IS_ERR(server_key)) { 103862306a36Sopenharmony_ci ret = PTR_ERR(server_key); 103962306a36Sopenharmony_ci switch (ret) { 104062306a36Sopenharmony_ci case -ENOKEY: 104162306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, ret, 104262306a36Sopenharmony_ci rxkad_abort_resp_nokey); 104362306a36Sopenharmony_ci case -EKEYEXPIRED: 104462306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, ret, 104562306a36Sopenharmony_ci rxkad_abort_resp_key_expired); 104662306a36Sopenharmony_ci default: 104762306a36Sopenharmony_ci return rxrpc_abort_conn(conn, skb, RXKADNOAUTH, ret, 104862306a36Sopenharmony_ci rxkad_abort_resp_key_rejected); 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci ret = -ENOMEM; 105362306a36Sopenharmony_ci response = kzalloc(sizeof(struct rxkad_response), GFP_NOFS); 105462306a36Sopenharmony_ci if (!response) 105562306a36Sopenharmony_ci goto temporary_error; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), 105862306a36Sopenharmony_ci response, sizeof(*response)) < 0) { 105962306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, 106062306a36Sopenharmony_ci rxkad_abort_resp_short); 106162306a36Sopenharmony_ci goto protocol_error; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci version = ntohl(response->version); 106562306a36Sopenharmony_ci ticket_len = ntohl(response->ticket_len); 106662306a36Sopenharmony_ci kvno = ntohl(response->kvno); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (version != RXKAD_VERSION) { 107162306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO, 107262306a36Sopenharmony_ci rxkad_abort_resp_version); 107362306a36Sopenharmony_ci goto protocol_error; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) { 107762306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADTICKETLEN, -EPROTO, 107862306a36Sopenharmony_ci rxkad_abort_resp_tkt_len); 107962306a36Sopenharmony_ci goto protocol_error; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) { 108362306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, -EPROTO, 108462306a36Sopenharmony_ci rxkad_abort_resp_unknown_tkt); 108562306a36Sopenharmony_ci goto protocol_error; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* extract the kerberos ticket and decrypt and decode it */ 108962306a36Sopenharmony_ci ret = -ENOMEM; 109062306a36Sopenharmony_ci ticket = kmalloc(ticket_len, GFP_NOFS); 109162306a36Sopenharmony_ci if (!ticket) 109262306a36Sopenharmony_ci goto temporary_error_free_resp; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response), 109562306a36Sopenharmony_ci ticket, ticket_len) < 0) { 109662306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, 109762306a36Sopenharmony_ci rxkad_abort_resp_short_tkt); 109862306a36Sopenharmony_ci goto protocol_error; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len, 110262306a36Sopenharmony_ci &session_key, &expiry); 110362306a36Sopenharmony_ci if (ret < 0) 110462306a36Sopenharmony_ci goto temporary_error_free_ticket; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* use the session key from inside the ticket to decrypt the 110762306a36Sopenharmony_ci * response */ 110862306a36Sopenharmony_ci rxkad_decrypt_response(conn, response, &session_key); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (ntohl(response->encrypted.epoch) != conn->proto.epoch || 111162306a36Sopenharmony_ci ntohl(response->encrypted.cid) != conn->proto.cid || 111262306a36Sopenharmony_ci ntohl(response->encrypted.securityIndex) != conn->security_ix) { 111362306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, 111462306a36Sopenharmony_ci rxkad_abort_resp_bad_param); 111562306a36Sopenharmony_ci goto protocol_error_free; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci csum = response->encrypted.checksum; 111962306a36Sopenharmony_ci response->encrypted.checksum = 0; 112062306a36Sopenharmony_ci rxkad_calc_response_checksum(response); 112162306a36Sopenharmony_ci if (response->encrypted.checksum != csum) { 112262306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, 112362306a36Sopenharmony_ci rxkad_abort_resp_bad_checksum); 112462306a36Sopenharmony_ci goto protocol_error_free; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci for (i = 0; i < RXRPC_MAXCALLS; i++) { 112862306a36Sopenharmony_ci u32 call_id = ntohl(response->encrypted.call_id[i]); 112962306a36Sopenharmony_ci u32 counter = READ_ONCE(conn->channels[i].call_counter); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (call_id > INT_MAX) { 113262306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, 113362306a36Sopenharmony_ci rxkad_abort_resp_bad_callid); 113462306a36Sopenharmony_ci goto protocol_error_free; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (call_id < counter) { 113862306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, 113962306a36Sopenharmony_ci rxkad_abort_resp_call_ctr); 114062306a36Sopenharmony_ci goto protocol_error_free; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (call_id > counter) { 114462306a36Sopenharmony_ci if (conn->channels[i].call) { 114562306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, 114662306a36Sopenharmony_ci rxkad_abort_resp_call_state); 114762306a36Sopenharmony_ci goto protocol_error_free; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci conn->channels[i].call_counter = call_id; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (ntohl(response->encrypted.inc_nonce) != conn->rxkad.nonce + 1) { 115462306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADOUTOFSEQUENCE, -EPROTO, 115562306a36Sopenharmony_ci rxkad_abort_resp_ooseq); 115662306a36Sopenharmony_ci goto protocol_error_free; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci level = ntohl(response->encrypted.level); 116062306a36Sopenharmony_ci if (level > RXRPC_SECURITY_ENCRYPT) { 116162306a36Sopenharmony_ci rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EPROTO, 116262306a36Sopenharmony_ci rxkad_abort_resp_level); 116362306a36Sopenharmony_ci goto protocol_error_free; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci conn->security_level = level; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* create a key to hold the security data and expiration time - after 116862306a36Sopenharmony_ci * this the connection security can be handled in exactly the same way 116962306a36Sopenharmony_ci * as for a client connection */ 117062306a36Sopenharmony_ci ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno); 117162306a36Sopenharmony_ci if (ret < 0) 117262306a36Sopenharmony_ci goto temporary_error_free_ticket; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci kfree(ticket); 117562306a36Sopenharmony_ci kfree(response); 117662306a36Sopenharmony_ci _leave(" = 0"); 117762306a36Sopenharmony_ci return 0; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ciprotocol_error_free: 118062306a36Sopenharmony_ci kfree(ticket); 118162306a36Sopenharmony_ciprotocol_error: 118262306a36Sopenharmony_ci kfree(response); 118362306a36Sopenharmony_ci key_put(server_key); 118462306a36Sopenharmony_ci return -EPROTO; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_citemporary_error_free_ticket: 118762306a36Sopenharmony_ci kfree(ticket); 118862306a36Sopenharmony_citemporary_error_free_resp: 118962306a36Sopenharmony_ci kfree(response); 119062306a36Sopenharmony_citemporary_error: 119162306a36Sopenharmony_ci /* Ignore the response packet if we got a temporary error such as 119262306a36Sopenharmony_ci * ENOMEM. We just want to send the challenge again. Note that we 119362306a36Sopenharmony_ci * also come out this way if the ticket decryption fails. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci key_put(server_key); 119662306a36Sopenharmony_ci return ret; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci/* 120062306a36Sopenharmony_ci * clear the connection security 120162306a36Sopenharmony_ci */ 120262306a36Sopenharmony_cistatic void rxkad_clear(struct rxrpc_connection *conn) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci _enter(""); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (conn->rxkad.cipher) 120762306a36Sopenharmony_ci crypto_free_sync_skcipher(conn->rxkad.cipher); 120862306a36Sopenharmony_ci} 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci/* 121162306a36Sopenharmony_ci * Initialise the rxkad security service. 121262306a36Sopenharmony_ci */ 121362306a36Sopenharmony_cistatic int rxkad_init(void) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct crypto_sync_skcipher *tfm; 121662306a36Sopenharmony_ci struct skcipher_request *req; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci /* pin the cipher we need so that the crypto layer doesn't invoke 121962306a36Sopenharmony_ci * keventd to go get it */ 122062306a36Sopenharmony_ci tfm = crypto_alloc_sync_skcipher("pcbc(fcrypt)", 0, 0); 122162306a36Sopenharmony_ci if (IS_ERR(tfm)) 122262306a36Sopenharmony_ci return PTR_ERR(tfm); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci req = skcipher_request_alloc(&tfm->base, GFP_KERNEL); 122562306a36Sopenharmony_ci if (!req) 122662306a36Sopenharmony_ci goto nomem_tfm; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci rxkad_ci_req = req; 122962306a36Sopenharmony_ci rxkad_ci = tfm; 123062306a36Sopenharmony_ci return 0; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cinomem_tfm: 123362306a36Sopenharmony_ci crypto_free_sync_skcipher(tfm); 123462306a36Sopenharmony_ci return -ENOMEM; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci/* 123862306a36Sopenharmony_ci * Clean up the rxkad security service. 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_cistatic void rxkad_exit(void) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci crypto_free_sync_skcipher(rxkad_ci); 124362306a36Sopenharmony_ci skcipher_request_free(rxkad_ci_req); 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci/* 124762306a36Sopenharmony_ci * RxRPC Kerberos-based security 124862306a36Sopenharmony_ci */ 124962306a36Sopenharmony_ciconst struct rxrpc_security rxkad = { 125062306a36Sopenharmony_ci .name = "rxkad", 125162306a36Sopenharmony_ci .security_index = RXRPC_SECURITY_RXKAD, 125262306a36Sopenharmony_ci .no_key_abort = RXKADUNKNOWNKEY, 125362306a36Sopenharmony_ci .init = rxkad_init, 125462306a36Sopenharmony_ci .exit = rxkad_exit, 125562306a36Sopenharmony_ci .preparse_server_key = rxkad_preparse_server_key, 125662306a36Sopenharmony_ci .free_preparse_server_key = rxkad_free_preparse_server_key, 125762306a36Sopenharmony_ci .destroy_server_key = rxkad_destroy_server_key, 125862306a36Sopenharmony_ci .init_connection_security = rxkad_init_connection_security, 125962306a36Sopenharmony_ci .how_much_data = rxkad_how_much_data, 126062306a36Sopenharmony_ci .secure_packet = rxkad_secure_packet, 126162306a36Sopenharmony_ci .verify_packet = rxkad_verify_packet, 126262306a36Sopenharmony_ci .free_call_crypto = rxkad_free_call_crypto, 126362306a36Sopenharmony_ci .issue_challenge = rxkad_issue_challenge, 126462306a36Sopenharmony_ci .respond_to_challenge = rxkad_respond_to_challenge, 126562306a36Sopenharmony_ci .verify_response = rxkad_verify_response, 126662306a36Sopenharmony_ci .clear = rxkad_clear, 126762306a36Sopenharmony_ci}; 1268