| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 1 | /* Kerberos-based RxRPC security | 
 | 2 |  * | 
 | 3 |  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | 
 | 4 |  * Written by David Howells (dhowells@redhat.com) | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or | 
 | 7 |  * modify it under the terms of the GNU General Public License | 
 | 8 |  * as published by the Free Software Foundation; either version | 
 | 9 |  * 2 of the License, or (at your option) any later version. | 
 | 10 |  */ | 
 | 11 |  | 
 | 12 | #include <linux/module.h> | 
 | 13 | #include <linux/net.h> | 
 | 14 | #include <linux/skbuff.h> | 
 | 15 | #include <linux/udp.h> | 
 | 16 | #include <linux/crypto.h> | 
 | 17 | #include <linux/scatterlist.h> | 
 | 18 | #include <linux/ctype.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 19 | #include <linux/slab.h> | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 20 | #include <net/sock.h> | 
 | 21 | #include <net/af_rxrpc.h> | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 22 | #include <keys/rxrpc-type.h> | 
| David Howells | b1bdb69 | 2007-04-27 15:28:45 -0700 | [diff] [blame] | 23 | #define rxrpc_debug rxkad_debug | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 24 | #include "ar-internal.h" | 
 | 25 |  | 
 | 26 | #define RXKAD_VERSION			2 | 
 | 27 | #define MAXKRB5TICKETLEN		1024 | 
 | 28 | #define RXKAD_TKT_TYPE_KERBEROS_V5	256 | 
 | 29 | #define ANAME_SZ			40	/* size of authentication name */ | 
 | 30 | #define INST_SZ				40	/* size of principal's instance */ | 
 | 31 | #define REALM_SZ			40	/* size of principal's auth domain */ | 
 | 32 | #define SNAME_SZ			40	/* size of service name */ | 
 | 33 |  | 
| Eric Dumazet | 95c9617 | 2012-04-15 05:58:06 +0000 | [diff] [blame] | 34 | unsigned int rxrpc_debug; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 35 | module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO); | 
| Paul Bolle | 424b00e | 2008-04-16 11:08:22 +0100 | [diff] [blame] | 36 | MODULE_PARM_DESC(debug, "rxkad debugging mask"); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 37 |  | 
 | 38 | struct rxkad_level1_hdr { | 
 | 39 | 	__be32	data_size;	/* true data size (excluding padding) */ | 
 | 40 | }; | 
 | 41 |  | 
 | 42 | struct rxkad_level2_hdr { | 
 | 43 | 	__be32	data_size;	/* true data size (excluding padding) */ | 
 | 44 | 	__be32	checksum;	/* decrypted data checksum */ | 
 | 45 | }; | 
 | 46 |  | 
| David Howells | 8b81547 | 2009-09-14 01:17:30 +0000 | [diff] [blame] | 47 | MODULE_DESCRIPTION("RxRPC network protocol type-2 security (Kerberos 4)"); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 48 | MODULE_AUTHOR("Red Hat, Inc."); | 
 | 49 | MODULE_LICENSE("GPL"); | 
 | 50 |  | 
 | 51 | /* | 
 | 52 |  * this holds a pinned cipher so that keventd doesn't get called by the cipher | 
 | 53 |  * alloc routine, but since we have it to hand, we use it to decrypt RESPONSE | 
 | 54 |  * packets | 
 | 55 |  */ | 
 | 56 | static struct crypto_blkcipher *rxkad_ci; | 
 | 57 | static DEFINE_MUTEX(rxkad_ci_mutex); | 
 | 58 |  | 
 | 59 | /* | 
 | 60 |  * initialise connection security | 
 | 61 |  */ | 
 | 62 | static int rxkad_init_connection_security(struct rxrpc_connection *conn) | 
 | 63 | { | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 64 | 	struct crypto_blkcipher *ci; | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 65 | 	struct rxrpc_key_token *token; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 66 | 	int ret; | 
 | 67 |  | 
 | 68 | 	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); | 
 | 69 |  | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 70 | 	token = conn->key->payload.data; | 
 | 71 | 	conn->security_ix = token->security_index; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 72 |  | 
 | 73 | 	ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); | 
 | 74 | 	if (IS_ERR(ci)) { | 
 | 75 | 		_debug("no cipher"); | 
 | 76 | 		ret = PTR_ERR(ci); | 
 | 77 | 		goto error; | 
 | 78 | 	} | 
 | 79 |  | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 80 | 	if (crypto_blkcipher_setkey(ci, token->kad->session_key, | 
 | 81 | 				    sizeof(token->kad->session_key)) < 0) | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 82 | 		BUG(); | 
 | 83 |  | 
 | 84 | 	switch (conn->security_level) { | 
 | 85 | 	case RXRPC_SECURITY_PLAIN: | 
 | 86 | 		break; | 
 | 87 | 	case RXRPC_SECURITY_AUTH: | 
 | 88 | 		conn->size_align = 8; | 
 | 89 | 		conn->security_size = sizeof(struct rxkad_level1_hdr); | 
 | 90 | 		conn->header_size += sizeof(struct rxkad_level1_hdr); | 
 | 91 | 		break; | 
 | 92 | 	case RXRPC_SECURITY_ENCRYPT: | 
 | 93 | 		conn->size_align = 8; | 
 | 94 | 		conn->security_size = sizeof(struct rxkad_level2_hdr); | 
 | 95 | 		conn->header_size += sizeof(struct rxkad_level2_hdr); | 
 | 96 | 		break; | 
 | 97 | 	default: | 
 | 98 | 		ret = -EKEYREJECTED; | 
 | 99 | 		goto error; | 
 | 100 | 	} | 
 | 101 |  | 
 | 102 | 	conn->cipher = ci; | 
 | 103 | 	ret = 0; | 
 | 104 | error: | 
 | 105 | 	_leave(" = %d", ret); | 
 | 106 | 	return ret; | 
 | 107 | } | 
 | 108 |  | 
 | 109 | /* | 
 | 110 |  * prime the encryption state with the invariant parts of a connection's | 
 | 111 |  * description | 
 | 112 |  */ | 
 | 113 | static void rxkad_prime_packet_security(struct rxrpc_connection *conn) | 
 | 114 | { | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 115 | 	struct rxrpc_key_token *token; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 116 | 	struct blkcipher_desc desc; | 
 | 117 | 	struct scatterlist sg[2]; | 
 | 118 | 	struct rxrpc_crypt iv; | 
 | 119 | 	struct { | 
 | 120 | 		__be32 x[4]; | 
 | 121 | 	} tmpbuf __attribute__((aligned(16))); /* must all be in same page */ | 
 | 122 |  | 
 | 123 | 	_enter(""); | 
 | 124 |  | 
 | 125 | 	if (!conn->key) | 
 | 126 | 		return; | 
 | 127 |  | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 128 | 	token = conn->key->payload.data; | 
 | 129 | 	memcpy(&iv, token->kad->session_key, sizeof(iv)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 130 |  | 
 | 131 | 	desc.tfm = conn->cipher; | 
 | 132 | 	desc.info = iv.x; | 
 | 133 | 	desc.flags = 0; | 
 | 134 |  | 
 | 135 | 	tmpbuf.x[0] = conn->epoch; | 
 | 136 | 	tmpbuf.x[1] = conn->cid; | 
 | 137 | 	tmpbuf.x[2] = 0; | 
 | 138 | 	tmpbuf.x[3] = htonl(conn->security_ix); | 
 | 139 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 140 | 	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); | 
 | 141 | 	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 142 | 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); | 
 | 143 |  | 
 | 144 | 	memcpy(&conn->csum_iv, &tmpbuf.x[2], sizeof(conn->csum_iv)); | 
 | 145 | 	ASSERTCMP(conn->csum_iv.n[0], ==, tmpbuf.x[2]); | 
 | 146 |  | 
 | 147 | 	_leave(""); | 
 | 148 | } | 
 | 149 |  | 
 | 150 | /* | 
 | 151 |  * partially encrypt a packet (level 1 security) | 
 | 152 |  */ | 
 | 153 | static int rxkad_secure_packet_auth(const struct rxrpc_call *call, | 
 | 154 | 				    struct sk_buff *skb, | 
 | 155 | 				    u32 data_size, | 
 | 156 | 				    void *sechdr) | 
 | 157 | { | 
 | 158 | 	struct rxrpc_skb_priv *sp; | 
 | 159 | 	struct blkcipher_desc desc; | 
 | 160 | 	struct rxrpc_crypt iv; | 
 | 161 | 	struct scatterlist sg[2]; | 
 | 162 | 	struct { | 
 | 163 | 		struct rxkad_level1_hdr hdr; | 
 | 164 | 		__be32	first;	/* first four bytes of data and padding */ | 
 | 165 | 	} tmpbuf __attribute__((aligned(8))); /* must all be in same page */ | 
 | 166 | 	u16 check; | 
 | 167 |  | 
 | 168 | 	sp = rxrpc_skb(skb); | 
 | 169 |  | 
 | 170 | 	_enter(""); | 
 | 171 |  | 
 | 172 | 	check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber); | 
 | 173 | 	data_size |= (u32) check << 16; | 
 | 174 |  | 
 | 175 | 	tmpbuf.hdr.data_size = htonl(data_size); | 
 | 176 | 	memcpy(&tmpbuf.first, sechdr + 4, sizeof(tmpbuf.first)); | 
 | 177 |  | 
 | 178 | 	/* start the encryption afresh */ | 
 | 179 | 	memset(&iv, 0, sizeof(iv)); | 
 | 180 | 	desc.tfm = call->conn->cipher; | 
 | 181 | 	desc.info = iv.x; | 
 | 182 | 	desc.flags = 0; | 
 | 183 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 184 | 	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); | 
 | 185 | 	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 186 | 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); | 
 | 187 |  | 
 | 188 | 	memcpy(sechdr, &tmpbuf, sizeof(tmpbuf)); | 
 | 189 |  | 
 | 190 | 	_leave(" = 0"); | 
 | 191 | 	return 0; | 
 | 192 | } | 
 | 193 |  | 
 | 194 | /* | 
 | 195 |  * wholly encrypt a packet (level 2 security) | 
 | 196 |  */ | 
 | 197 | static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, | 
 | 198 | 					struct sk_buff *skb, | 
 | 199 | 					u32 data_size, | 
 | 200 | 					void *sechdr) | 
 | 201 | { | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 202 | 	const struct rxrpc_key_token *token; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 203 | 	struct rxkad_level2_hdr rxkhdr | 
 | 204 | 		__attribute__((aligned(8))); /* must be all on one page */ | 
 | 205 | 	struct rxrpc_skb_priv *sp; | 
 | 206 | 	struct blkcipher_desc desc; | 
 | 207 | 	struct rxrpc_crypt iv; | 
 | 208 | 	struct scatterlist sg[16]; | 
 | 209 | 	struct sk_buff *trailer; | 
| Eric Dumazet | 95c9617 | 2012-04-15 05:58:06 +0000 | [diff] [blame] | 210 | 	unsigned int len; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 211 | 	u16 check; | 
 | 212 | 	int nsg; | 
 | 213 |  | 
 | 214 | 	sp = rxrpc_skb(skb); | 
 | 215 |  | 
 | 216 | 	_enter(""); | 
 | 217 |  | 
 | 218 | 	check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber); | 
 | 219 |  | 
 | 220 | 	rxkhdr.data_size = htonl(data_size | (u32) check << 16); | 
 | 221 | 	rxkhdr.checksum = 0; | 
 | 222 |  | 
 | 223 | 	/* encrypt from the session key */ | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 224 | 	token = call->conn->key->payload.data; | 
 | 225 | 	memcpy(&iv, token->kad->session_key, sizeof(iv)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 226 | 	desc.tfm = call->conn->cipher; | 
 | 227 | 	desc.info = iv.x; | 
 | 228 | 	desc.flags = 0; | 
 | 229 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 230 | 	sg_init_one(&sg[0], sechdr, sizeof(rxkhdr)); | 
 | 231 | 	sg_init_one(&sg[1], &rxkhdr, sizeof(rxkhdr)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 232 | 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(rxkhdr)); | 
 | 233 |  | 
 | 234 | 	/* we want to encrypt the skbuff in-place */ | 
 | 235 | 	nsg = skb_cow_data(skb, 0, &trailer); | 
 | 236 | 	if (nsg < 0 || nsg > 16) | 
 | 237 | 		return -ENOMEM; | 
 | 238 |  | 
 | 239 | 	len = data_size + call->conn->size_align - 1; | 
 | 240 | 	len &= ~(call->conn->size_align - 1); | 
 | 241 |  | 
| David S. Miller | 51c739d | 2007-10-30 21:29:29 -0700 | [diff] [blame] | 242 | 	sg_init_table(sg, nsg); | 
 | 243 | 	skb_to_sgvec(skb, sg, 0, len); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 244 | 	crypto_blkcipher_encrypt_iv(&desc, sg, sg, len); | 
 | 245 |  | 
 | 246 | 	_leave(" = 0"); | 
 | 247 | 	return 0; | 
 | 248 | } | 
 | 249 |  | 
 | 250 | /* | 
 | 251 |  * checksum an RxRPC packet header | 
 | 252 |  */ | 
 | 253 | static int rxkad_secure_packet(const struct rxrpc_call *call, | 
 | 254 | 				struct sk_buff *skb, | 
 | 255 | 				size_t data_size, | 
 | 256 | 				void *sechdr) | 
 | 257 | { | 
 | 258 | 	struct rxrpc_skb_priv *sp; | 
 | 259 | 	struct blkcipher_desc desc; | 
 | 260 | 	struct rxrpc_crypt iv; | 
 | 261 | 	struct scatterlist sg[2]; | 
 | 262 | 	struct { | 
 | 263 | 		__be32 x[2]; | 
 | 264 | 	} tmpbuf __attribute__((aligned(8))); /* must all be in same page */ | 
 | 265 | 	__be32 x; | 
| Al Viro | 91e916c | 2008-03-29 03:08:38 +0000 | [diff] [blame] | 266 | 	u32 y; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 267 | 	int ret; | 
 | 268 |  | 
 | 269 | 	sp = rxrpc_skb(skb); | 
 | 270 |  | 
 | 271 | 	_enter("{%d{%x}},{#%u},%zu,", | 
 | 272 | 	       call->debug_id, key_serial(call->conn->key), ntohl(sp->hdr.seq), | 
 | 273 | 	       data_size); | 
 | 274 |  | 
 | 275 | 	if (!call->conn->cipher) | 
 | 276 | 		return 0; | 
 | 277 |  | 
 | 278 | 	ret = key_validate(call->conn->key); | 
 | 279 | 	if (ret < 0) | 
 | 280 | 		return ret; | 
 | 281 |  | 
 | 282 | 	/* continue encrypting from where we left off */ | 
 | 283 | 	memcpy(&iv, call->conn->csum_iv.x, sizeof(iv)); | 
 | 284 | 	desc.tfm = call->conn->cipher; | 
 | 285 | 	desc.info = iv.x; | 
 | 286 | 	desc.flags = 0; | 
 | 287 |  | 
 | 288 | 	/* calculate the security checksum */ | 
 | 289 | 	x = htonl(call->channel << (32 - RXRPC_CIDSHIFT)); | 
| YOSHIFUJI Hideaki | ae445d1 | 2007-12-12 03:55:22 +0900 | [diff] [blame] | 290 | 	x |= sp->hdr.seq & cpu_to_be32(0x3fffffff); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 291 | 	tmpbuf.x[0] = sp->hdr.callNumber; | 
 | 292 | 	tmpbuf.x[1] = x; | 
 | 293 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 294 | 	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); | 
 | 295 | 	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 296 | 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); | 
 | 297 |  | 
| Al Viro | 91e916c | 2008-03-29 03:08:38 +0000 | [diff] [blame] | 298 | 	y = ntohl(tmpbuf.x[1]); | 
 | 299 | 	y = (y >> 16) & 0xffff; | 
 | 300 | 	if (y == 0) | 
 | 301 | 		y = 1; /* zero checksums are not permitted */ | 
 | 302 | 	sp->hdr.cksum = htons(y); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 303 |  | 
 | 304 | 	switch (call->conn->security_level) { | 
 | 305 | 	case RXRPC_SECURITY_PLAIN: | 
 | 306 | 		ret = 0; | 
 | 307 | 		break; | 
 | 308 | 	case RXRPC_SECURITY_AUTH: | 
 | 309 | 		ret = rxkad_secure_packet_auth(call, skb, data_size, sechdr); | 
 | 310 | 		break; | 
 | 311 | 	case RXRPC_SECURITY_ENCRYPT: | 
 | 312 | 		ret = rxkad_secure_packet_encrypt(call, skb, data_size, | 
 | 313 | 						  sechdr); | 
 | 314 | 		break; | 
 | 315 | 	default: | 
 | 316 | 		ret = -EPERM; | 
 | 317 | 		break; | 
 | 318 | 	} | 
 | 319 |  | 
| Al Viro | 91e916c | 2008-03-29 03:08:38 +0000 | [diff] [blame] | 320 | 	_leave(" = %d [set %hx]", ret, y); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 321 | 	return ret; | 
 | 322 | } | 
 | 323 |  | 
 | 324 | /* | 
 | 325 |  * decrypt partial encryption on a packet (level 1 security) | 
 | 326 |  */ | 
 | 327 | static int rxkad_verify_packet_auth(const struct rxrpc_call *call, | 
 | 328 | 				    struct sk_buff *skb, | 
 | 329 | 				    u32 *_abort_code) | 
 | 330 | { | 
 | 331 | 	struct rxkad_level1_hdr sechdr; | 
 | 332 | 	struct rxrpc_skb_priv *sp; | 
 | 333 | 	struct blkcipher_desc desc; | 
 | 334 | 	struct rxrpc_crypt iv; | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 335 | 	struct scatterlist sg[16]; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 336 | 	struct sk_buff *trailer; | 
 | 337 | 	u32 data_size, buf; | 
 | 338 | 	u16 check; | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 339 | 	int nsg; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 340 |  | 
 | 341 | 	_enter(""); | 
 | 342 |  | 
 | 343 | 	sp = rxrpc_skb(skb); | 
 | 344 |  | 
 | 345 | 	/* we want to decrypt the skbuff in-place */ | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 346 | 	nsg = skb_cow_data(skb, 0, &trailer); | 
 | 347 | 	if (nsg < 0 || nsg > 16) | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 348 | 		goto nomem; | 
 | 349 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 350 | 	sg_init_table(sg, nsg); | 
| David S. Miller | 51c739d | 2007-10-30 21:29:29 -0700 | [diff] [blame] | 351 | 	skb_to_sgvec(skb, sg, 0, 8); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 352 |  | 
 | 353 | 	/* start the decryption afresh */ | 
 | 354 | 	memset(&iv, 0, sizeof(iv)); | 
 | 355 | 	desc.tfm = call->conn->cipher; | 
 | 356 | 	desc.info = iv.x; | 
 | 357 | 	desc.flags = 0; | 
 | 358 |  | 
 | 359 | 	crypto_blkcipher_decrypt_iv(&desc, sg, sg, 8); | 
 | 360 |  | 
 | 361 | 	/* remove the decrypted packet length */ | 
 | 362 | 	if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0) | 
 | 363 | 		goto datalen_error; | 
 | 364 | 	if (!skb_pull(skb, sizeof(sechdr))) | 
 | 365 | 		BUG(); | 
 | 366 |  | 
 | 367 | 	buf = ntohl(sechdr.data_size); | 
 | 368 | 	data_size = buf & 0xffff; | 
 | 369 |  | 
 | 370 | 	check = buf >> 16; | 
 | 371 | 	check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber); | 
 | 372 | 	check &= 0xffff; | 
 | 373 | 	if (check != 0) { | 
 | 374 | 		*_abort_code = RXKADSEALEDINCON; | 
 | 375 | 		goto protocol_error; | 
 | 376 | 	} | 
 | 377 |  | 
 | 378 | 	/* shorten the packet to remove the padding */ | 
 | 379 | 	if (data_size > skb->len) | 
 | 380 | 		goto datalen_error; | 
 | 381 | 	else if (data_size < skb->len) | 
 | 382 | 		skb->len = data_size; | 
 | 383 |  | 
 | 384 | 	_leave(" = 0 [dlen=%x]", data_size); | 
 | 385 | 	return 0; | 
 | 386 |  | 
 | 387 | datalen_error: | 
 | 388 | 	*_abort_code = RXKADDATALEN; | 
 | 389 | protocol_error: | 
 | 390 | 	_leave(" = -EPROTO"); | 
 | 391 | 	return -EPROTO; | 
 | 392 |  | 
 | 393 | nomem: | 
 | 394 | 	_leave(" = -ENOMEM"); | 
 | 395 | 	return -ENOMEM; | 
 | 396 | } | 
 | 397 |  | 
 | 398 | /* | 
 | 399 |  * wholly decrypt a packet (level 2 security) | 
 | 400 |  */ | 
 | 401 | static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call, | 
 | 402 | 				       struct sk_buff *skb, | 
 | 403 | 				       u32 *_abort_code) | 
 | 404 | { | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 405 | 	const struct rxrpc_key_token *token; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 406 | 	struct rxkad_level2_hdr sechdr; | 
 | 407 | 	struct rxrpc_skb_priv *sp; | 
 | 408 | 	struct blkcipher_desc desc; | 
 | 409 | 	struct rxrpc_crypt iv; | 
 | 410 | 	struct scatterlist _sg[4], *sg; | 
 | 411 | 	struct sk_buff *trailer; | 
 | 412 | 	u32 data_size, buf; | 
 | 413 | 	u16 check; | 
 | 414 | 	int nsg; | 
 | 415 |  | 
 | 416 | 	_enter(",{%d}", skb->len); | 
 | 417 |  | 
 | 418 | 	sp = rxrpc_skb(skb); | 
 | 419 |  | 
 | 420 | 	/* we want to decrypt the skbuff in-place */ | 
 | 421 | 	nsg = skb_cow_data(skb, 0, &trailer); | 
 | 422 | 	if (nsg < 0) | 
 | 423 | 		goto nomem; | 
 | 424 |  | 
 | 425 | 	sg = _sg; | 
 | 426 | 	if (unlikely(nsg > 4)) { | 
 | 427 | 		sg = kmalloc(sizeof(*sg) * nsg, GFP_NOIO); | 
 | 428 | 		if (!sg) | 
 | 429 | 			goto nomem; | 
 | 430 | 	} | 
 | 431 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 432 | 	sg_init_table(sg, nsg); | 
| David S. Miller | 51c739d | 2007-10-30 21:29:29 -0700 | [diff] [blame] | 433 | 	skb_to_sgvec(skb, sg, 0, skb->len); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 434 |  | 
 | 435 | 	/* decrypt from the session key */ | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 436 | 	token = call->conn->key->payload.data; | 
 | 437 | 	memcpy(&iv, token->kad->session_key, sizeof(iv)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 438 | 	desc.tfm = call->conn->cipher; | 
 | 439 | 	desc.info = iv.x; | 
 | 440 | 	desc.flags = 0; | 
 | 441 |  | 
 | 442 | 	crypto_blkcipher_decrypt_iv(&desc, sg, sg, skb->len); | 
 | 443 | 	if (sg != _sg) | 
 | 444 | 		kfree(sg); | 
 | 445 |  | 
 | 446 | 	/* remove the decrypted packet length */ | 
 | 447 | 	if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0) | 
 | 448 | 		goto datalen_error; | 
 | 449 | 	if (!skb_pull(skb, sizeof(sechdr))) | 
 | 450 | 		BUG(); | 
 | 451 |  | 
 | 452 | 	buf = ntohl(sechdr.data_size); | 
 | 453 | 	data_size = buf & 0xffff; | 
 | 454 |  | 
 | 455 | 	check = buf >> 16; | 
 | 456 | 	check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber); | 
 | 457 | 	check &= 0xffff; | 
 | 458 | 	if (check != 0) { | 
 | 459 | 		*_abort_code = RXKADSEALEDINCON; | 
 | 460 | 		goto protocol_error; | 
 | 461 | 	} | 
 | 462 |  | 
 | 463 | 	/* shorten the packet to remove the padding */ | 
 | 464 | 	if (data_size > skb->len) | 
 | 465 | 		goto datalen_error; | 
 | 466 | 	else if (data_size < skb->len) | 
 | 467 | 		skb->len = data_size; | 
 | 468 |  | 
 | 469 | 	_leave(" = 0 [dlen=%x]", data_size); | 
 | 470 | 	return 0; | 
 | 471 |  | 
 | 472 | datalen_error: | 
 | 473 | 	*_abort_code = RXKADDATALEN; | 
 | 474 | protocol_error: | 
 | 475 | 	_leave(" = -EPROTO"); | 
 | 476 | 	return -EPROTO; | 
 | 477 |  | 
 | 478 | nomem: | 
 | 479 | 	_leave(" = -ENOMEM"); | 
 | 480 | 	return -ENOMEM; | 
 | 481 | } | 
 | 482 |  | 
 | 483 | /* | 
 | 484 |  * verify the security on a received packet | 
 | 485 |  */ | 
 | 486 | static int rxkad_verify_packet(const struct rxrpc_call *call, | 
 | 487 | 			       struct sk_buff *skb, | 
 | 488 | 			       u32 *_abort_code) | 
 | 489 | { | 
 | 490 | 	struct blkcipher_desc desc; | 
 | 491 | 	struct rxrpc_skb_priv *sp; | 
 | 492 | 	struct rxrpc_crypt iv; | 
 | 493 | 	struct scatterlist sg[2]; | 
 | 494 | 	struct { | 
 | 495 | 		__be32 x[2]; | 
 | 496 | 	} tmpbuf __attribute__((aligned(8))); /* must all be in same page */ | 
 | 497 | 	__be32 x; | 
 | 498 | 	__be16 cksum; | 
| David Howells | 02f3705 | 2008-04-24 20:38:56 +0100 | [diff] [blame] | 499 | 	u32 y; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 500 | 	int ret; | 
 | 501 |  | 
 | 502 | 	sp = rxrpc_skb(skb); | 
 | 503 |  | 
 | 504 | 	_enter("{%d{%x}},{#%u}", | 
 | 505 | 	       call->debug_id, key_serial(call->conn->key), | 
 | 506 | 	       ntohl(sp->hdr.seq)); | 
 | 507 |  | 
 | 508 | 	if (!call->conn->cipher) | 
 | 509 | 		return 0; | 
 | 510 |  | 
| David Howells | 8b81547 | 2009-09-14 01:17:30 +0000 | [diff] [blame] | 511 | 	if (sp->hdr.securityIndex != RXRPC_SECURITY_RXKAD) { | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 512 | 		*_abort_code = RXKADINCONSISTENCY; | 
 | 513 | 		_leave(" = -EPROTO [not rxkad]"); | 
 | 514 | 		return -EPROTO; | 
 | 515 | 	} | 
 | 516 |  | 
 | 517 | 	/* continue encrypting from where we left off */ | 
 | 518 | 	memcpy(&iv, call->conn->csum_iv.x, sizeof(iv)); | 
 | 519 | 	desc.tfm = call->conn->cipher; | 
 | 520 | 	desc.info = iv.x; | 
 | 521 | 	desc.flags = 0; | 
 | 522 |  | 
 | 523 | 	/* validate the security checksum */ | 
 | 524 | 	x = htonl(call->channel << (32 - RXRPC_CIDSHIFT)); | 
| YOSHIFUJI Hideaki | ae445d1 | 2007-12-12 03:55:22 +0900 | [diff] [blame] | 525 | 	x |= sp->hdr.seq & cpu_to_be32(0x3fffffff); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 526 | 	tmpbuf.x[0] = call->call_id; | 
 | 527 | 	tmpbuf.x[1] = x; | 
 | 528 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 529 | 	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); | 
 | 530 | 	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 531 | 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); | 
 | 532 |  | 
| Al Viro | 91e916c | 2008-03-29 03:08:38 +0000 | [diff] [blame] | 533 | 	y = ntohl(tmpbuf.x[1]); | 
 | 534 | 	y = (y >> 16) & 0xffff; | 
 | 535 | 	if (y == 0) | 
 | 536 | 		y = 1; /* zero checksums are not permitted */ | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 537 |  | 
| Al Viro | 91e916c | 2008-03-29 03:08:38 +0000 | [diff] [blame] | 538 | 	cksum = htons(y); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 539 | 	if (sp->hdr.cksum != cksum) { | 
 | 540 | 		*_abort_code = RXKADSEALEDINCON; | 
 | 541 | 		_leave(" = -EPROTO [csum failed]"); | 
 | 542 | 		return -EPROTO; | 
 | 543 | 	} | 
 | 544 |  | 
 | 545 | 	switch (call->conn->security_level) { | 
 | 546 | 	case RXRPC_SECURITY_PLAIN: | 
 | 547 | 		ret = 0; | 
 | 548 | 		break; | 
 | 549 | 	case RXRPC_SECURITY_AUTH: | 
 | 550 | 		ret = rxkad_verify_packet_auth(call, skb, _abort_code); | 
 | 551 | 		break; | 
 | 552 | 	case RXRPC_SECURITY_ENCRYPT: | 
 | 553 | 		ret = rxkad_verify_packet_encrypt(call, skb, _abort_code); | 
 | 554 | 		break; | 
 | 555 | 	default: | 
 | 556 | 		ret = -ENOANO; | 
 | 557 | 		break; | 
 | 558 | 	} | 
 | 559 |  | 
 | 560 | 	_leave(" = %d", ret); | 
 | 561 | 	return ret; | 
 | 562 | } | 
 | 563 |  | 
 | 564 | /* | 
 | 565 |  * issue a challenge | 
 | 566 |  */ | 
 | 567 | static int rxkad_issue_challenge(struct rxrpc_connection *conn) | 
 | 568 | { | 
 | 569 | 	struct rxkad_challenge challenge; | 
 | 570 | 	struct rxrpc_header hdr; | 
 | 571 | 	struct msghdr msg; | 
 | 572 | 	struct kvec iov[2]; | 
 | 573 | 	size_t len; | 
 | 574 | 	int ret; | 
 | 575 |  | 
 | 576 | 	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); | 
 | 577 |  | 
 | 578 | 	ret = key_validate(conn->key); | 
 | 579 | 	if (ret < 0) | 
 | 580 | 		return ret; | 
 | 581 |  | 
 | 582 | 	get_random_bytes(&conn->security_nonce, sizeof(conn->security_nonce)); | 
 | 583 |  | 
 | 584 | 	challenge.version	= htonl(2); | 
 | 585 | 	challenge.nonce		= htonl(conn->security_nonce); | 
 | 586 | 	challenge.min_level	= htonl(0); | 
 | 587 | 	challenge.__padding	= 0; | 
 | 588 |  | 
 | 589 | 	msg.msg_name	= &conn->trans->peer->srx.transport.sin; | 
 | 590 | 	msg.msg_namelen	= sizeof(conn->trans->peer->srx.transport.sin); | 
 | 591 | 	msg.msg_control	= NULL; | 
 | 592 | 	msg.msg_controllen = 0; | 
 | 593 | 	msg.msg_flags	= 0; | 
 | 594 |  | 
 | 595 | 	hdr.epoch	= conn->epoch; | 
 | 596 | 	hdr.cid		= conn->cid; | 
 | 597 | 	hdr.callNumber	= 0; | 
 | 598 | 	hdr.seq		= 0; | 
 | 599 | 	hdr.type	= RXRPC_PACKET_TYPE_CHALLENGE; | 
 | 600 | 	hdr.flags	= conn->out_clientflag; | 
 | 601 | 	hdr.userStatus	= 0; | 
 | 602 | 	hdr.securityIndex = conn->security_ix; | 
 | 603 | 	hdr._rsvd	= 0; | 
 | 604 | 	hdr.serviceId	= conn->service_id; | 
 | 605 |  | 
 | 606 | 	iov[0].iov_base	= &hdr; | 
 | 607 | 	iov[0].iov_len	= sizeof(hdr); | 
 | 608 | 	iov[1].iov_base	= &challenge; | 
 | 609 | 	iov[1].iov_len	= sizeof(challenge); | 
 | 610 |  | 
 | 611 | 	len = iov[0].iov_len + iov[1].iov_len; | 
 | 612 |  | 
 | 613 | 	hdr.serial = htonl(atomic_inc_return(&conn->serial)); | 
 | 614 | 	_proto("Tx CHALLENGE %%%u", ntohl(hdr.serial)); | 
 | 615 |  | 
 | 616 | 	ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len); | 
 | 617 | 	if (ret < 0) { | 
 | 618 | 		_debug("sendmsg failed: %d", ret); | 
 | 619 | 		return -EAGAIN; | 
 | 620 | 	} | 
 | 621 |  | 
 | 622 | 	_leave(" = 0"); | 
 | 623 | 	return 0; | 
 | 624 | } | 
 | 625 |  | 
 | 626 | /* | 
 | 627 |  * send a Kerberos security response | 
 | 628 |  */ | 
 | 629 | static int rxkad_send_response(struct rxrpc_connection *conn, | 
 | 630 | 			       struct rxrpc_header *hdr, | 
 | 631 | 			       struct rxkad_response *resp, | 
 | 632 | 			       const struct rxkad_key *s2) | 
 | 633 | { | 
 | 634 | 	struct msghdr msg; | 
 | 635 | 	struct kvec iov[3]; | 
 | 636 | 	size_t len; | 
 | 637 | 	int ret; | 
 | 638 |  | 
 | 639 | 	_enter(""); | 
 | 640 |  | 
 | 641 | 	msg.msg_name	= &conn->trans->peer->srx.transport.sin; | 
 | 642 | 	msg.msg_namelen	= sizeof(conn->trans->peer->srx.transport.sin); | 
 | 643 | 	msg.msg_control	= NULL; | 
 | 644 | 	msg.msg_controllen = 0; | 
 | 645 | 	msg.msg_flags	= 0; | 
 | 646 |  | 
 | 647 | 	hdr->epoch	= conn->epoch; | 
 | 648 | 	hdr->seq	= 0; | 
 | 649 | 	hdr->type	= RXRPC_PACKET_TYPE_RESPONSE; | 
 | 650 | 	hdr->flags	= conn->out_clientflag; | 
 | 651 | 	hdr->userStatus	= 0; | 
 | 652 | 	hdr->_rsvd	= 0; | 
 | 653 |  | 
 | 654 | 	iov[0].iov_base	= hdr; | 
 | 655 | 	iov[0].iov_len	= sizeof(*hdr); | 
 | 656 | 	iov[1].iov_base	= resp; | 
 | 657 | 	iov[1].iov_len	= sizeof(*resp); | 
 | 658 | 	iov[2].iov_base	= (void *) s2->ticket; | 
 | 659 | 	iov[2].iov_len	= s2->ticket_len; | 
 | 660 |  | 
 | 661 | 	len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; | 
 | 662 |  | 
 | 663 | 	hdr->serial = htonl(atomic_inc_return(&conn->serial)); | 
 | 664 | 	_proto("Tx RESPONSE %%%u", ntohl(hdr->serial)); | 
 | 665 |  | 
 | 666 | 	ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 3, len); | 
 | 667 | 	if (ret < 0) { | 
 | 668 | 		_debug("sendmsg failed: %d", ret); | 
 | 669 | 		return -EAGAIN; | 
 | 670 | 	} | 
 | 671 |  | 
 | 672 | 	_leave(" = 0"); | 
 | 673 | 	return 0; | 
 | 674 | } | 
 | 675 |  | 
 | 676 | /* | 
 | 677 |  * calculate the response checksum | 
 | 678 |  */ | 
 | 679 | static void rxkad_calc_response_checksum(struct rxkad_response *response) | 
 | 680 | { | 
 | 681 | 	u32 csum = 1000003; | 
 | 682 | 	int loop; | 
 | 683 | 	u8 *p = (u8 *) response; | 
 | 684 |  | 
 | 685 | 	for (loop = sizeof(*response); loop > 0; loop--) | 
 | 686 | 		csum = csum * 0x10204081 + *p++; | 
 | 687 |  | 
 | 688 | 	response->encrypted.checksum = htonl(csum); | 
 | 689 | } | 
 | 690 |  | 
 | 691 | /* | 
 | 692 |  * load a scatterlist with a potentially split-page buffer | 
 | 693 |  */ | 
 | 694 | static void rxkad_sg_set_buf2(struct scatterlist sg[2], | 
 | 695 | 			      void *buf, size_t buflen) | 
 | 696 | { | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 697 | 	int nsg = 1; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 698 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 699 | 	sg_init_table(sg, 2); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 700 |  | 
 | 701 | 	sg_set_buf(&sg[0], buf, buflen); | 
 | 702 | 	if (sg[0].offset + buflen > PAGE_SIZE) { | 
 | 703 | 		/* the buffer was split over two pages */ | 
 | 704 | 		sg[0].length = PAGE_SIZE - sg[0].offset; | 
 | 705 | 		sg_set_buf(&sg[1], buf + sg[0].length, buflen - sg[0].length); | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 706 | 		nsg++; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 707 | 	} | 
 | 708 |  | 
| Jens Axboe | c46f233 | 2007-10-31 12:06:37 +0100 | [diff] [blame] | 709 | 	sg_mark_end(&sg[nsg - 1]); | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 710 |  | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 711 | 	ASSERTCMP(sg[0].length + sg[1].length, ==, buflen); | 
 | 712 | } | 
 | 713 |  | 
 | 714 | /* | 
 | 715 |  * encrypt the response packet | 
 | 716 |  */ | 
 | 717 | static void rxkad_encrypt_response(struct rxrpc_connection *conn, | 
 | 718 | 				   struct rxkad_response *resp, | 
 | 719 | 				   const struct rxkad_key *s2) | 
 | 720 | { | 
 | 721 | 	struct blkcipher_desc desc; | 
 | 722 | 	struct rxrpc_crypt iv; | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 723 | 	struct scatterlist sg[2]; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 724 |  | 
 | 725 | 	/* continue encrypting from where we left off */ | 
 | 726 | 	memcpy(&iv, s2->session_key, sizeof(iv)); | 
 | 727 | 	desc.tfm = conn->cipher; | 
 | 728 | 	desc.info = iv.x; | 
 | 729 | 	desc.flags = 0; | 
 | 730 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 731 | 	rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted)); | 
 | 732 | 	crypto_blkcipher_encrypt_iv(&desc, sg, sg, sizeof(resp->encrypted)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 733 | } | 
 | 734 |  | 
 | 735 | /* | 
 | 736 |  * respond to a challenge packet | 
 | 737 |  */ | 
 | 738 | static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, | 
 | 739 | 				      struct sk_buff *skb, | 
 | 740 | 				      u32 *_abort_code) | 
 | 741 | { | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 742 | 	const struct rxrpc_key_token *token; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 743 | 	struct rxkad_challenge challenge; | 
 | 744 | 	struct rxkad_response resp | 
 | 745 | 		__attribute__((aligned(8))); /* must be aligned for crypto */ | 
 | 746 | 	struct rxrpc_skb_priv *sp; | 
 | 747 | 	u32 version, nonce, min_level, abort_code; | 
 | 748 | 	int ret; | 
 | 749 |  | 
 | 750 | 	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); | 
 | 751 |  | 
 | 752 | 	if (!conn->key) { | 
 | 753 | 		_leave(" = -EPROTO [no key]"); | 
 | 754 | 		return -EPROTO; | 
 | 755 | 	} | 
 | 756 |  | 
 | 757 | 	ret = key_validate(conn->key); | 
 | 758 | 	if (ret < 0) { | 
 | 759 | 		*_abort_code = RXKADEXPIRED; | 
 | 760 | 		return ret; | 
 | 761 | 	} | 
 | 762 |  | 
 | 763 | 	abort_code = RXKADPACKETSHORT; | 
 | 764 | 	sp = rxrpc_skb(skb); | 
 | 765 | 	if (skb_copy_bits(skb, 0, &challenge, sizeof(challenge)) < 0) | 
 | 766 | 		goto protocol_error; | 
 | 767 |  | 
 | 768 | 	version = ntohl(challenge.version); | 
 | 769 | 	nonce = ntohl(challenge.nonce); | 
 | 770 | 	min_level = ntohl(challenge.min_level); | 
 | 771 |  | 
 | 772 | 	_proto("Rx CHALLENGE %%%u { v=%u n=%u ml=%u }", | 
 | 773 | 	       ntohl(sp->hdr.serial), version, nonce, min_level); | 
 | 774 |  | 
 | 775 | 	abort_code = RXKADINCONSISTENCY; | 
 | 776 | 	if (version != RXKAD_VERSION) | 
 | 777 | 		goto protocol_error; | 
 | 778 |  | 
 | 779 | 	abort_code = RXKADLEVELFAIL; | 
 | 780 | 	if (conn->security_level < min_level) | 
 | 781 | 		goto protocol_error; | 
 | 782 |  | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 783 | 	token = conn->key->payload.data; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 784 |  | 
 | 785 | 	/* build the response packet */ | 
 | 786 | 	memset(&resp, 0, sizeof(resp)); | 
 | 787 |  | 
 | 788 | 	resp.version = RXKAD_VERSION; | 
 | 789 | 	resp.encrypted.epoch = conn->epoch; | 
 | 790 | 	resp.encrypted.cid = conn->cid; | 
 | 791 | 	resp.encrypted.securityIndex = htonl(conn->security_ix); | 
 | 792 | 	resp.encrypted.call_id[0] = | 
 | 793 | 		(conn->channels[0] ? conn->channels[0]->call_id : 0); | 
 | 794 | 	resp.encrypted.call_id[1] = | 
 | 795 | 		(conn->channels[1] ? conn->channels[1]->call_id : 0); | 
 | 796 | 	resp.encrypted.call_id[2] = | 
 | 797 | 		(conn->channels[2] ? conn->channels[2]->call_id : 0); | 
 | 798 | 	resp.encrypted.call_id[3] = | 
 | 799 | 		(conn->channels[3] ? conn->channels[3]->call_id : 0); | 
 | 800 | 	resp.encrypted.inc_nonce = htonl(nonce + 1); | 
 | 801 | 	resp.encrypted.level = htonl(conn->security_level); | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 802 | 	resp.kvno = htonl(token->kad->kvno); | 
 | 803 | 	resp.ticket_len = htonl(token->kad->ticket_len); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 804 |  | 
 | 805 | 	/* calculate the response checksum and then do the encryption */ | 
 | 806 | 	rxkad_calc_response_checksum(&resp); | 
| David Howells | 3394128 | 2009-09-14 01:17:35 +0000 | [diff] [blame] | 807 | 	rxkad_encrypt_response(conn, &resp, token->kad); | 
 | 808 | 	return rxkad_send_response(conn, &sp->hdr, &resp, token->kad); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 809 |  | 
 | 810 | protocol_error: | 
 | 811 | 	*_abort_code = abort_code; | 
 | 812 | 	_leave(" = -EPROTO [%d]", abort_code); | 
 | 813 | 	return -EPROTO; | 
 | 814 | } | 
 | 815 |  | 
 | 816 | /* | 
 | 817 |  * decrypt the kerberos IV ticket in the response | 
 | 818 |  */ | 
 | 819 | static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, | 
 | 820 | 				void *ticket, size_t ticket_len, | 
 | 821 | 				struct rxrpc_crypt *_session_key, | 
 | 822 | 				time_t *_expiry, | 
 | 823 | 				u32 *_abort_code) | 
 | 824 | { | 
 | 825 | 	struct blkcipher_desc desc; | 
 | 826 | 	struct rxrpc_crypt iv, key; | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 827 | 	struct scatterlist sg[1]; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 828 | 	struct in_addr addr; | 
| Eric Dumazet | 95c9617 | 2012-04-15 05:58:06 +0000 | [diff] [blame] | 829 | 	unsigned int life; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 830 | 	time_t issue, now; | 
 | 831 | 	bool little_endian; | 
 | 832 | 	int ret; | 
 | 833 | 	u8 *p, *q, *name, *end; | 
 | 834 |  | 
 | 835 | 	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->server_key)); | 
 | 836 |  | 
 | 837 | 	*_expiry = 0; | 
 | 838 |  | 
 | 839 | 	ret = key_validate(conn->server_key); | 
 | 840 | 	if (ret < 0) { | 
 | 841 | 		switch (ret) { | 
 | 842 | 		case -EKEYEXPIRED: | 
 | 843 | 			*_abort_code = RXKADEXPIRED; | 
 | 844 | 			goto error; | 
 | 845 | 		default: | 
 | 846 | 			*_abort_code = RXKADNOAUTH; | 
 | 847 | 			goto error; | 
 | 848 | 		} | 
 | 849 | 	} | 
 | 850 |  | 
 | 851 | 	ASSERT(conn->server_key->payload.data != NULL); | 
 | 852 | 	ASSERTCMP((unsigned long) ticket & 7UL, ==, 0); | 
 | 853 |  | 
 | 854 | 	memcpy(&iv, &conn->server_key->type_data, sizeof(iv)); | 
 | 855 |  | 
 | 856 | 	desc.tfm = conn->server_key->payload.data; | 
 | 857 | 	desc.info = iv.x; | 
 | 858 | 	desc.flags = 0; | 
 | 859 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 860 | 	sg_init_one(&sg[0], ticket, ticket_len); | 
 | 861 | 	crypto_blkcipher_decrypt_iv(&desc, sg, sg, ticket_len); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 862 |  | 
 | 863 | 	p = ticket; | 
 | 864 | 	end = p + ticket_len; | 
 | 865 |  | 
 | 866 | #define Z(size)						\ | 
 | 867 | 	({						\ | 
 | 868 | 		u8 *__str = p;				\ | 
 | 869 | 		q = memchr(p, 0, end - p);		\ | 
 | 870 | 		if (!q || q - p > (size))		\ | 
 | 871 | 			goto bad_ticket;		\ | 
 | 872 | 		for (; p < q; p++)			\ | 
 | 873 | 			if (!isprint(*p))		\ | 
 | 874 | 				goto bad_ticket;	\ | 
 | 875 | 		p++;					\ | 
 | 876 | 		__str;					\ | 
 | 877 | 	}) | 
 | 878 |  | 
 | 879 | 	/* extract the ticket flags */ | 
 | 880 | 	_debug("KIV FLAGS: %x", *p); | 
 | 881 | 	little_endian = *p & 1; | 
 | 882 | 	p++; | 
 | 883 |  | 
 | 884 | 	/* extract the authentication name */ | 
 | 885 | 	name = Z(ANAME_SZ); | 
 | 886 | 	_debug("KIV ANAME: %s", name); | 
 | 887 |  | 
 | 888 | 	/* extract the principal's instance */ | 
 | 889 | 	name = Z(INST_SZ); | 
 | 890 | 	_debug("KIV INST : %s", name); | 
 | 891 |  | 
 | 892 | 	/* extract the principal's authentication domain */ | 
 | 893 | 	name = Z(REALM_SZ); | 
 | 894 | 	_debug("KIV REALM: %s", name); | 
 | 895 |  | 
 | 896 | 	if (end - p < 4 + 8 + 4 + 2) | 
 | 897 | 		goto bad_ticket; | 
 | 898 |  | 
 | 899 | 	/* get the IPv4 address of the entity that requested the ticket */ | 
 | 900 | 	memcpy(&addr, p, sizeof(addr)); | 
 | 901 | 	p += 4; | 
| Harvey Harrison | 21454aa | 2008-10-31 00:54:56 -0700 | [diff] [blame] | 902 | 	_debug("KIV ADDR : %pI4", &addr); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 903 |  | 
 | 904 | 	/* get the session key from the ticket */ | 
 | 905 | 	memcpy(&key, p, sizeof(key)); | 
 | 906 | 	p += 8; | 
 | 907 | 	_debug("KIV KEY  : %08x %08x", ntohl(key.n[0]), ntohl(key.n[1])); | 
 | 908 | 	memcpy(_session_key, &key, sizeof(key)); | 
 | 909 |  | 
 | 910 | 	/* get the ticket's lifetime */ | 
 | 911 | 	life = *p++ * 5 * 60; | 
 | 912 | 	_debug("KIV LIFE : %u", life); | 
 | 913 |  | 
 | 914 | 	/* get the issue time of the ticket */ | 
 | 915 | 	if (little_endian) { | 
 | 916 | 		__le32 stamp; | 
 | 917 | 		memcpy(&stamp, p, 4); | 
 | 918 | 		issue = le32_to_cpu(stamp); | 
 | 919 | 	} else { | 
 | 920 | 		__be32 stamp; | 
 | 921 | 		memcpy(&stamp, p, 4); | 
 | 922 | 		issue = be32_to_cpu(stamp); | 
 | 923 | 	} | 
 | 924 | 	p += 4; | 
| john stultz | 2c6b47d | 2007-07-24 17:47:43 -0700 | [diff] [blame] | 925 | 	now = get_seconds(); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 926 | 	_debug("KIV ISSUE: %lx [%lx]", issue, now); | 
 | 927 |  | 
 | 928 | 	/* check the ticket is in date */ | 
 | 929 | 	if (issue > now) { | 
 | 930 | 		*_abort_code = RXKADNOAUTH; | 
 | 931 | 		ret = -EKEYREJECTED; | 
 | 932 | 		goto error; | 
 | 933 | 	} | 
 | 934 |  | 
 | 935 | 	if (issue < now - life) { | 
 | 936 | 		*_abort_code = RXKADEXPIRED; | 
 | 937 | 		ret = -EKEYEXPIRED; | 
 | 938 | 		goto error; | 
 | 939 | 	} | 
 | 940 |  | 
 | 941 | 	*_expiry = issue + life; | 
 | 942 |  | 
 | 943 | 	/* get the service name */ | 
 | 944 | 	name = Z(SNAME_SZ); | 
 | 945 | 	_debug("KIV SNAME: %s", name); | 
 | 946 |  | 
 | 947 | 	/* get the service instance name */ | 
 | 948 | 	name = Z(INST_SZ); | 
 | 949 | 	_debug("KIV SINST: %s", name); | 
 | 950 |  | 
 | 951 | 	ret = 0; | 
 | 952 | error: | 
 | 953 | 	_leave(" = %d", ret); | 
 | 954 | 	return ret; | 
 | 955 |  | 
 | 956 | bad_ticket: | 
 | 957 | 	*_abort_code = RXKADBADTICKET; | 
 | 958 | 	ret = -EBADMSG; | 
 | 959 | 	goto error; | 
 | 960 | } | 
 | 961 |  | 
 | 962 | /* | 
 | 963 |  * decrypt the response packet | 
 | 964 |  */ | 
 | 965 | static void rxkad_decrypt_response(struct rxrpc_connection *conn, | 
 | 966 | 				   struct rxkad_response *resp, | 
 | 967 | 				   const struct rxrpc_crypt *session_key) | 
 | 968 | { | 
 | 969 | 	struct blkcipher_desc desc; | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 970 | 	struct scatterlist sg[2]; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 971 | 	struct rxrpc_crypt iv; | 
 | 972 |  | 
 | 973 | 	_enter(",,%08x%08x", | 
 | 974 | 	       ntohl(session_key->n[0]), ntohl(session_key->n[1])); | 
 | 975 |  | 
 | 976 | 	ASSERT(rxkad_ci != NULL); | 
 | 977 |  | 
 | 978 | 	mutex_lock(&rxkad_ci_mutex); | 
 | 979 | 	if (crypto_blkcipher_setkey(rxkad_ci, session_key->x, | 
 | 980 | 				    sizeof(*session_key)) < 0) | 
 | 981 | 		BUG(); | 
 | 982 |  | 
 | 983 | 	memcpy(&iv, session_key, sizeof(iv)); | 
 | 984 | 	desc.tfm = rxkad_ci; | 
 | 985 | 	desc.info = iv.x; | 
 | 986 | 	desc.flags = 0; | 
 | 987 |  | 
| Herbert Xu | 68e3f5d | 2007-10-27 00:52:07 -0700 | [diff] [blame] | 988 | 	rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted)); | 
 | 989 | 	crypto_blkcipher_decrypt_iv(&desc, sg, sg, sizeof(resp->encrypted)); | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 990 | 	mutex_unlock(&rxkad_ci_mutex); | 
 | 991 |  | 
 | 992 | 	_leave(""); | 
 | 993 | } | 
 | 994 |  | 
 | 995 | /* | 
 | 996 |  * verify a response | 
 | 997 |  */ | 
 | 998 | static int rxkad_verify_response(struct rxrpc_connection *conn, | 
 | 999 | 				 struct sk_buff *skb, | 
 | 1000 | 				 u32 *_abort_code) | 
 | 1001 | { | 
 | 1002 | 	struct rxkad_response response | 
 | 1003 | 		__attribute__((aligned(8))); /* must be aligned for crypto */ | 
 | 1004 | 	struct rxrpc_skb_priv *sp; | 
 | 1005 | 	struct rxrpc_crypt session_key; | 
 | 1006 | 	time_t expiry; | 
 | 1007 | 	void *ticket; | 
| Al Viro | 91e916c | 2008-03-29 03:08:38 +0000 | [diff] [blame] | 1008 | 	u32 abort_code, version, kvno, ticket_len, level; | 
 | 1009 | 	__be32 csum; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 1010 | 	int ret; | 
 | 1011 |  | 
 | 1012 | 	_enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key)); | 
 | 1013 |  | 
 | 1014 | 	abort_code = RXKADPACKETSHORT; | 
 | 1015 | 	if (skb_copy_bits(skb, 0, &response, sizeof(response)) < 0) | 
 | 1016 | 		goto protocol_error; | 
 | 1017 | 	if (!pskb_pull(skb, sizeof(response))) | 
 | 1018 | 		BUG(); | 
 | 1019 |  | 
 | 1020 | 	version = ntohl(response.version); | 
 | 1021 | 	ticket_len = ntohl(response.ticket_len); | 
 | 1022 | 	kvno = ntohl(response.kvno); | 
 | 1023 | 	sp = rxrpc_skb(skb); | 
 | 1024 | 	_proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }", | 
 | 1025 | 	       ntohl(sp->hdr.serial), version, kvno, ticket_len); | 
 | 1026 |  | 
 | 1027 | 	abort_code = RXKADINCONSISTENCY; | 
 | 1028 | 	if (version != RXKAD_VERSION) | 
| David Howells | 4aa9cb3 | 2007-12-07 04:31:47 -0800 | [diff] [blame] | 1029 | 		goto protocol_error; | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 1030 |  | 
 | 1031 | 	abort_code = RXKADTICKETLEN; | 
 | 1032 | 	if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) | 
 | 1033 | 		goto protocol_error; | 
 | 1034 |  | 
 | 1035 | 	abort_code = RXKADUNKNOWNKEY; | 
 | 1036 | 	if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) | 
 | 1037 | 		goto protocol_error; | 
 | 1038 |  | 
 | 1039 | 	/* extract the kerberos ticket and decrypt and decode it */ | 
 | 1040 | 	ticket = kmalloc(ticket_len, GFP_NOFS); | 
 | 1041 | 	if (!ticket) | 
 | 1042 | 		return -ENOMEM; | 
 | 1043 |  | 
 | 1044 | 	abort_code = RXKADPACKETSHORT; | 
 | 1045 | 	if (skb_copy_bits(skb, 0, ticket, ticket_len) < 0) | 
 | 1046 | 		goto protocol_error_free; | 
 | 1047 |  | 
 | 1048 | 	ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key, | 
 | 1049 | 				   &expiry, &abort_code); | 
 | 1050 | 	if (ret < 0) { | 
 | 1051 | 		*_abort_code = abort_code; | 
 | 1052 | 		kfree(ticket); | 
 | 1053 | 		return ret; | 
 | 1054 | 	} | 
 | 1055 |  | 
 | 1056 | 	/* use the session key from inside the ticket to decrypt the | 
 | 1057 | 	 * response */ | 
 | 1058 | 	rxkad_decrypt_response(conn, &response, &session_key); | 
 | 1059 |  | 
 | 1060 | 	abort_code = RXKADSEALEDINCON; | 
 | 1061 | 	if (response.encrypted.epoch != conn->epoch) | 
 | 1062 | 		goto protocol_error_free; | 
 | 1063 | 	if (response.encrypted.cid != conn->cid) | 
 | 1064 | 		goto protocol_error_free; | 
 | 1065 | 	if (ntohl(response.encrypted.securityIndex) != conn->security_ix) | 
 | 1066 | 		goto protocol_error_free; | 
 | 1067 | 	csum = response.encrypted.checksum; | 
 | 1068 | 	response.encrypted.checksum = 0; | 
 | 1069 | 	rxkad_calc_response_checksum(&response); | 
 | 1070 | 	if (response.encrypted.checksum != csum) | 
 | 1071 | 		goto protocol_error_free; | 
 | 1072 |  | 
 | 1073 | 	if (ntohl(response.encrypted.call_id[0]) > INT_MAX || | 
 | 1074 | 	    ntohl(response.encrypted.call_id[1]) > INT_MAX || | 
 | 1075 | 	    ntohl(response.encrypted.call_id[2]) > INT_MAX || | 
 | 1076 | 	    ntohl(response.encrypted.call_id[3]) > INT_MAX) | 
 | 1077 | 		goto protocol_error_free; | 
 | 1078 |  | 
 | 1079 | 	abort_code = RXKADOUTOFSEQUENCE; | 
 | 1080 | 	if (response.encrypted.inc_nonce != htonl(conn->security_nonce + 1)) | 
 | 1081 | 		goto protocol_error_free; | 
 | 1082 |  | 
 | 1083 | 	abort_code = RXKADLEVELFAIL; | 
 | 1084 | 	level = ntohl(response.encrypted.level); | 
 | 1085 | 	if (level > RXRPC_SECURITY_ENCRYPT) | 
 | 1086 | 		goto protocol_error_free; | 
 | 1087 | 	conn->security_level = level; | 
 | 1088 |  | 
 | 1089 | 	/* create a key to hold the security data and expiration time - after | 
 | 1090 | 	 * this the connection security can be handled in exactly the same way | 
 | 1091 | 	 * as for a client connection */ | 
 | 1092 | 	ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno); | 
 | 1093 | 	if (ret < 0) { | 
 | 1094 | 		kfree(ticket); | 
 | 1095 | 		return ret; | 
 | 1096 | 	} | 
 | 1097 |  | 
 | 1098 | 	kfree(ticket); | 
 | 1099 | 	_leave(" = 0"); | 
 | 1100 | 	return 0; | 
 | 1101 |  | 
 | 1102 | protocol_error_free: | 
 | 1103 | 	kfree(ticket); | 
 | 1104 | protocol_error: | 
 | 1105 | 	*_abort_code = abort_code; | 
 | 1106 | 	_leave(" = -EPROTO [%d]", abort_code); | 
 | 1107 | 	return -EPROTO; | 
 | 1108 | } | 
 | 1109 |  | 
 | 1110 | /* | 
 | 1111 |  * clear the connection security | 
 | 1112 |  */ | 
 | 1113 | static void rxkad_clear(struct rxrpc_connection *conn) | 
 | 1114 | { | 
 | 1115 | 	_enter(""); | 
 | 1116 |  | 
 | 1117 | 	if (conn->cipher) | 
 | 1118 | 		crypto_free_blkcipher(conn->cipher); | 
 | 1119 | } | 
 | 1120 |  | 
 | 1121 | /* | 
 | 1122 |  * RxRPC Kerberos-based security | 
 | 1123 |  */ | 
 | 1124 | static struct rxrpc_security rxkad = { | 
 | 1125 | 	.owner				= THIS_MODULE, | 
 | 1126 | 	.name				= "rxkad", | 
| David Howells | 8b81547 | 2009-09-14 01:17:30 +0000 | [diff] [blame] | 1127 | 	.security_index			= RXRPC_SECURITY_RXKAD, | 
| David Howells | 17926a7 | 2007-04-26 15:48:28 -0700 | [diff] [blame] | 1128 | 	.init_connection_security	= rxkad_init_connection_security, | 
 | 1129 | 	.prime_packet_security		= rxkad_prime_packet_security, | 
 | 1130 | 	.secure_packet			= rxkad_secure_packet, | 
 | 1131 | 	.verify_packet			= rxkad_verify_packet, | 
 | 1132 | 	.issue_challenge		= rxkad_issue_challenge, | 
 | 1133 | 	.respond_to_challenge		= rxkad_respond_to_challenge, | 
 | 1134 | 	.verify_response		= rxkad_verify_response, | 
 | 1135 | 	.clear				= rxkad_clear, | 
 | 1136 | }; | 
 | 1137 |  | 
 | 1138 | static __init int rxkad_init(void) | 
 | 1139 | { | 
 | 1140 | 	_enter(""); | 
 | 1141 |  | 
 | 1142 | 	/* pin the cipher we need so that the crypto layer doesn't invoke | 
 | 1143 | 	 * keventd to go get it */ | 
 | 1144 | 	rxkad_ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); | 
 | 1145 | 	if (IS_ERR(rxkad_ci)) | 
 | 1146 | 		return PTR_ERR(rxkad_ci); | 
 | 1147 |  | 
 | 1148 | 	return rxrpc_register_security(&rxkad); | 
 | 1149 | } | 
 | 1150 |  | 
 | 1151 | module_init(rxkad_init); | 
 | 1152 |  | 
 | 1153 | static __exit void rxkad_exit(void) | 
 | 1154 | { | 
 | 1155 | 	_enter(""); | 
 | 1156 |  | 
 | 1157 | 	rxrpc_unregister_security(&rxkad); | 
 | 1158 | 	crypto_free_blkcipher(rxkad_ci); | 
 | 1159 | } | 
 | 1160 |  | 
 | 1161 | module_exit(rxkad_exit); |