aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc
diff options
context:
space:
mode:
authorKevin Coffman <kwc@citi.umich.edu>2010-03-17 13:02:59 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2010-05-14 15:09:18 -0400
commitde9c17eb4a912c9028f7b470eb80815144883b26 (patch)
tree3e681897cff2db43f91c1e5e2f7dcad9598165c9 /net/sunrpc
parentc43abaedaff92a7bcbfe04b593164bb5faba3078 (diff)
gss_krb5: add support for new token formats in rfc4121
This is a step toward support for AES encryption types which are required to use the new token formats defined in rfc4121. Signed-off-by: Kevin Coffman <kwc@citi.umich.edu> [SteveD: Fixed a typo in gss_verify_mic_v2()] Signed-off-by: Steve Dickson <steved@redhat.com> [Trond: Got rid of the TEST_ROTATE/TEST_EXTRA_COUNT crap] Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc')
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c74
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seal.c69
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c61
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_wrap.c174
4 files changed, 378 insertions, 0 deletions
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index bb76873aa019..ca52ac28a537 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -197,6 +197,80 @@ out:
197 return err ? GSS_S_FAILURE : 0; 197 return err ? GSS_S_FAILURE : 0;
198} 198}
199 199
200/*
201 * checksum the plaintext data and hdrlen bytes of the token header
202 * Per rfc4121, sec. 4.2.4, the checksum is performed over the data
203 * body then over the first 16 octets of the MIC token
204 * Inclusion of the header data in the calculation of the
205 * checksum is optional.
206 */
207u32
208make_checksum_v2(struct krb5_ctx *kctx, char *header, int hdrlen,
209 struct xdr_buf *body, int body_offset, u8 *cksumkey,
210 struct xdr_netobj *cksumout)
211{
212 struct hash_desc desc;
213 struct scatterlist sg[1];
214 int err;
215 u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN];
216 unsigned int checksumlen;
217
218 if (kctx->gk5e->keyed_cksum == 0) {
219 dprintk("%s: expected keyed hash for %s\n",
220 __func__, kctx->gk5e->name);
221 return GSS_S_FAILURE;
222 }
223 if (cksumkey == NULL) {
224 dprintk("%s: no key supplied for %s\n",
225 __func__, kctx->gk5e->name);
226 return GSS_S_FAILURE;
227 }
228
229 desc.tfm = crypto_alloc_hash(kctx->gk5e->cksum_name, 0,
230 CRYPTO_ALG_ASYNC);
231 if (IS_ERR(desc.tfm))
232 return GSS_S_FAILURE;
233 checksumlen = crypto_hash_digestsize(desc.tfm);
234 desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
235
236 err = crypto_hash_setkey(desc.tfm, cksumkey, kctx->gk5e->keylength);
237 if (err)
238 goto out;
239
240 err = crypto_hash_init(&desc);
241 if (err)
242 goto out;
243 err = xdr_process_buf(body, body_offset, body->len - body_offset,
244 checksummer, &desc);
245 if (err)
246 goto out;
247 if (header != NULL) {
248 sg_init_one(sg, header, hdrlen);
249 err = crypto_hash_update(&desc, sg, hdrlen);
250 if (err)
251 goto out;
252 }
253 err = crypto_hash_final(&desc, checksumdata);
254 if (err)
255 goto out;
256
257 cksumout->len = kctx->gk5e->cksumlength;
258
259 switch (kctx->gk5e->ctype) {
260 case CKSUMTYPE_HMAC_SHA1_96_AES128:
261 case CKSUMTYPE_HMAC_SHA1_96_AES256:
262 /* note that this truncates the hash */
263 memcpy(cksumout->data, checksumdata, kctx->gk5e->cksumlength);
264 break;
265 default:
266 BUG();
267 break;
268 }
269out:
270 crypto_free_hash(desc.tfm);
271 return err ? GSS_S_FAILURE : 0;
272}
273
200struct encryptor_desc { 274struct encryptor_desc {
201 u8 iv[GSS_KRB5_MAX_BLOCKSIZE]; 275 u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
202 struct blkcipher_desc desc; 276 struct blkcipher_desc desc;
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c
index 7ede900049a7..477a546d19bb 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seal.c
@@ -91,6 +91,33 @@ setup_token(struct krb5_ctx *ctx, struct xdr_netobj *token)
91 return (char *)krb5_hdr; 91 return (char *)krb5_hdr;
92} 92}
93 93
94static void *
95setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
96{
97 __be16 *ptr, *krb5_hdr;
98 u8 *p, flags = 0x00;
99
100 if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
101 flags |= 0x01;
102 if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
103 flags |= 0x04;
104
105 /* Per rfc 4121, sec 4.2.6.1, there is no header,
106 * just start the token */
107 krb5_hdr = ptr = (__be16 *)token->data;
108
109 *ptr++ = KG2_TOK_MIC;
110 p = (u8 *)ptr;
111 *p++ = flags;
112 *p++ = 0xff;
113 ptr = (__be16 *)p;
114 *ptr++ = 0xffff;
115 *ptr++ = 0xffff;
116
117 token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
118 return krb5_hdr;
119}
120
94static u32 121static u32
95gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text, 122gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text,
96 struct xdr_netobj *token) 123 struct xdr_netobj *token)
@@ -133,6 +160,45 @@ gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text,
133} 160}
134 161
135u32 162u32
163gss_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
164 struct xdr_netobj *token)
165{
166 char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
167 struct xdr_netobj cksumobj = { .len = sizeof(cksumdata),
168 .data = cksumdata};
169 void *krb5_hdr;
170 s32 now;
171 u64 seq_send;
172 u8 *cksumkey;
173
174 dprintk("RPC: %s\n", __func__);
175
176 krb5_hdr = setup_token_v2(ctx, token);
177
178 /* Set up the sequence number. Now 64-bits in clear
179 * text and w/o direction indicator */
180 spin_lock(&krb5_seq_lock);
181 seq_send = ctx->seq_send64++;
182 spin_unlock(&krb5_seq_lock);
183 *((u64 *)(krb5_hdr + 8)) = cpu_to_be64(seq_send);
184
185 if (ctx->initiate)
186 cksumkey = ctx->initiator_sign;
187 else
188 cksumkey = ctx->acceptor_sign;
189
190 if (make_checksum_v2(ctx, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
191 text, 0, cksumkey, &cksumobj))
192 return GSS_S_FAILURE;
193
194 memcpy(krb5_hdr + GSS_KRB5_TOK_HDR_LEN, cksumobj.data, cksumobj.len);
195
196 now = get_seconds();
197
198 return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
199}
200
201u32
136gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, 202gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text,
137 struct xdr_netobj *token) 203 struct xdr_netobj *token)
138{ 204{
@@ -144,6 +210,9 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text,
144 case ENCTYPE_DES_CBC_RAW: 210 case ENCTYPE_DES_CBC_RAW:
145 case ENCTYPE_DES3_CBC_RAW: 211 case ENCTYPE_DES3_CBC_RAW:
146 return gss_get_mic_v1(ctx, text, token); 212 return gss_get_mic_v1(ctx, text, token);
213 case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
214 case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
215 return gss_get_mic_v2(ctx, text, token);
147 } 216 }
148} 217}
149 218
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c
index 3e15bdb5a9eb..4ede4cc4391f 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -141,6 +141,64 @@ gss_verify_mic_v1(struct krb5_ctx *ctx,
141 return GSS_S_COMPLETE; 141 return GSS_S_COMPLETE;
142} 142}
143 143
144static u32
145gss_verify_mic_v2(struct krb5_ctx *ctx,
146 struct xdr_buf *message_buffer, struct xdr_netobj *read_token)
147{
148 char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
149 struct xdr_netobj cksumobj = {.len = sizeof(cksumdata),
150 .data = cksumdata};
151 s32 now;
152 u64 seqnum;
153 u8 *ptr = read_token->data;
154 u8 *cksumkey;
155 u8 flags;
156 int i;
157
158 dprintk("RPC: %s\n", __func__);
159
160 if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_MIC)
161 return GSS_S_DEFECTIVE_TOKEN;
162
163 flags = ptr[2];
164 if ((!ctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
165 (ctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
166 return GSS_S_BAD_SIG;
167
168 if (flags & KG2_TOKEN_FLAG_SEALED) {
169 dprintk("%s: token has unexpected sealed flag\n", __func__);
170 return GSS_S_FAILURE;
171 }
172
173 for (i = 3; i < 8; i++)
174 if (ptr[i] != 0xff)
175 return GSS_S_DEFECTIVE_TOKEN;
176
177 if (ctx->initiate)
178 cksumkey = ctx->acceptor_sign;
179 else
180 cksumkey = ctx->initiator_sign;
181
182 if (make_checksum_v2(ctx, ptr, GSS_KRB5_TOK_HDR_LEN, message_buffer, 0,
183 cksumkey, &cksumobj))
184 return GSS_S_FAILURE;
185
186 if (memcmp(cksumobj.data, ptr + GSS_KRB5_TOK_HDR_LEN,
187 ctx->gk5e->cksumlength))
188 return GSS_S_BAD_SIG;
189
190 /* it got through unscathed. Make sure the context is unexpired */
191 now = get_seconds();
192 if (now > ctx->endtime)
193 return GSS_S_CONTEXT_EXPIRED;
194
195 /* do sequencing checks */
196
197 seqnum = be64_to_cpup((__be64 *)ptr + 8);
198
199 return GSS_S_COMPLETE;
200}
201
144u32 202u32
145gss_verify_mic_kerberos(struct gss_ctx *gss_ctx, 203gss_verify_mic_kerberos(struct gss_ctx *gss_ctx,
146 struct xdr_buf *message_buffer, 204 struct xdr_buf *message_buffer,
@@ -154,6 +212,9 @@ gss_verify_mic_kerberos(struct gss_ctx *gss_ctx,
154 case ENCTYPE_DES_CBC_RAW: 212 case ENCTYPE_DES_CBC_RAW:
155 case ENCTYPE_DES3_CBC_RAW: 213 case ENCTYPE_DES3_CBC_RAW:
156 return gss_verify_mic_v1(ctx, message_buffer, read_token); 214 return gss_verify_mic_v1(ctx, message_buffer, read_token);
215 case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
216 case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
217 return gss_verify_mic_v2(ctx, message_buffer, read_token);
157 } 218 }
158} 219}
159 220
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index 1c8ebd3dbd3c..4aa46b28298c 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -340,6 +340,174 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
340 return GSS_S_COMPLETE; 340 return GSS_S_COMPLETE;
341} 341}
342 342
343/*
344 * We cannot currently handle tokens with rotated data. We need a
345 * generalized routine to rotate the data in place. It is anticipated
346 * that we won't encounter rotated data in the general case.
347 */
348static u32
349rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
350{
351 unsigned int realrrc = rrc % (buf->len - offset - GSS_KRB5_TOK_HDR_LEN);
352
353 if (realrrc == 0)
354 return 0;
355
356 dprintk("%s: cannot process token with rotated data: "
357 "rrc %u, realrrc %u\n", __func__, rrc, realrrc);
358 return 1;
359}
360
361static u32
362gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
363 struct xdr_buf *buf, struct page **pages)
364{
365 int blocksize;
366 u8 *ptr, *plainhdr;
367 s32 now;
368 u8 flags = 0x00;
369 __be16 *be16ptr, ec = 0;
370 __be64 *be64ptr;
371 u32 err;
372
373 dprintk("RPC: %s\n", __func__);
374
375 if (kctx->gk5e->encrypt_v2 == NULL)
376 return GSS_S_FAILURE;
377
378 /* make room for gss token header */
379 if (xdr_extend_head(buf, offset, GSS_KRB5_TOK_HDR_LEN))
380 return GSS_S_FAILURE;
381
382 /* construct gss token header */
383 ptr = plainhdr = buf->head[0].iov_base + offset;
384 *ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
385 *ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
386
387 if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
388 flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
389 if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0)
390 flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
391 /* We always do confidentiality in wrap tokens */
392 flags |= KG2_TOKEN_FLAG_SEALED;
393
394 *ptr++ = flags;
395 *ptr++ = 0xff;
396 be16ptr = (__be16 *)ptr;
397
398 blocksize = crypto_blkcipher_blocksize(kctx->acceptor_enc);
399 *be16ptr++ = cpu_to_be16(ec);
400 /* "inner" token header always uses 0 for RRC */
401 *be16ptr++ = cpu_to_be16(0);
402
403 be64ptr = (__be64 *)be16ptr;
404 spin_lock(&krb5_seq_lock);
405 *be64ptr = cpu_to_be64(kctx->seq_send64++);
406 spin_unlock(&krb5_seq_lock);
407
408 err = (*kctx->gk5e->encrypt_v2)(kctx, offset, buf, ec, pages);
409 if (err)
410 return err;
411
412 now = get_seconds();
413 return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
414}
415
416static u32
417gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
418{
419 s32 now;
420 u64 seqnum;
421 u8 *ptr;
422 u8 flags = 0x00;
423 u16 ec, rrc;
424 int err;
425 u32 headskip, tailskip;
426 u8 decrypted_hdr[GSS_KRB5_TOK_HDR_LEN];
427 unsigned int movelen;
428
429
430 dprintk("RPC: %s\n", __func__);
431
432 if (kctx->gk5e->decrypt_v2 == NULL)
433 return GSS_S_FAILURE;
434
435 ptr = buf->head[0].iov_base + offset;
436
437 if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
438 return GSS_S_DEFECTIVE_TOKEN;
439
440 flags = ptr[2];
441 if ((!kctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
442 (kctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
443 return GSS_S_BAD_SIG;
444
445 if ((flags & KG2_TOKEN_FLAG_SEALED) == 0) {
446 dprintk("%s: token missing expected sealed flag\n", __func__);
447 return GSS_S_DEFECTIVE_TOKEN;
448 }
449
450 if (ptr[3] != 0xff)
451 return GSS_S_DEFECTIVE_TOKEN;
452
453 ec = be16_to_cpup((__be16 *)(ptr + 4));
454 rrc = be16_to_cpup((__be16 *)(ptr + 6));
455
456 seqnum = be64_to_cpup((__be64 *)(ptr + 8));
457
458 if (rrc != 0) {
459 err = rotate_left(kctx, offset, buf, rrc);
460 if (err)
461 return GSS_S_FAILURE;
462 }
463
464 err = (*kctx->gk5e->decrypt_v2)(kctx, offset, buf,
465 &headskip, &tailskip);
466 if (err)
467 return GSS_S_FAILURE;
468
469 /*
470 * Retrieve the decrypted gss token header and verify
471 * it against the original
472 */
473 err = read_bytes_from_xdr_buf(buf,
474 buf->len - GSS_KRB5_TOK_HDR_LEN - tailskip,
475 decrypted_hdr, GSS_KRB5_TOK_HDR_LEN);
476 if (err) {
477 dprintk("%s: error %u getting decrypted_hdr\n", __func__, err);
478 return GSS_S_FAILURE;
479 }
480 if (memcmp(ptr, decrypted_hdr, 6)
481 || memcmp(ptr + 8, decrypted_hdr + 8, 8)) {
482 dprintk("%s: token hdr, plaintext hdr mismatch!\n", __func__);
483 return GSS_S_FAILURE;
484 }
485
486 /* do sequencing checks */
487
488 /* it got through unscathed. Make sure the context is unexpired */
489 now = get_seconds();
490 if (now > kctx->endtime)
491 return GSS_S_CONTEXT_EXPIRED;
492
493 /*
494 * Move the head data back to the right position in xdr_buf.
495 * We ignore any "ec" data since it might be in the head or
496 * the tail, and we really don't need to deal with it.
497 * Note that buf->head[0].iov_len may indicate the available
498 * head buffer space rather than that actually occupied.
499 */
500 movelen = min_t(unsigned int, buf->head[0].iov_len, buf->len);
501 movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
502 BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
503 buf->head[0].iov_len);
504 memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
505 buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
506 buf->len -= GSS_KRB5_TOK_HDR_LEN + headskip;
507
508 return GSS_S_COMPLETE;
509}
510
343u32 511u32
344gss_wrap_kerberos(struct gss_ctx *gctx, int offset, 512gss_wrap_kerberos(struct gss_ctx *gctx, int offset,
345 struct xdr_buf *buf, struct page **pages) 513 struct xdr_buf *buf, struct page **pages)
@@ -352,6 +520,9 @@ gss_wrap_kerberos(struct gss_ctx *gctx, int offset,
352 case ENCTYPE_DES_CBC_RAW: 520 case ENCTYPE_DES_CBC_RAW:
353 case ENCTYPE_DES3_CBC_RAW: 521 case ENCTYPE_DES3_CBC_RAW:
354 return gss_wrap_kerberos_v1(kctx, offset, buf, pages); 522 return gss_wrap_kerberos_v1(kctx, offset, buf, pages);
523 case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
524 case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
525 return gss_wrap_kerberos_v2(kctx, offset, buf, pages);
355 } 526 }
356} 527}
357 528
@@ -366,6 +537,9 @@ gss_unwrap_kerberos(struct gss_ctx *gctx, int offset, struct xdr_buf *buf)
366 case ENCTYPE_DES_CBC_RAW: 537 case ENCTYPE_DES_CBC_RAW:
367 case ENCTYPE_DES3_CBC_RAW: 538 case ENCTYPE_DES3_CBC_RAW:
368 return gss_unwrap_kerberos_v1(kctx, offset, buf); 539 return gss_unwrap_kerberos_v1(kctx, offset, buf);
540 case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
541 case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
542 return gss_unwrap_kerberos_v2(kctx, offset, buf);
369 } 543 }
370} 544}
371 545