1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * lib/gssapi/krb5/k5sealv3.c
6 *
7 * Copyright 2003,2004 by the Massachusetts Institute of Technology.
8 * All Rights Reserved.
9 *
10 * Export of this software from the United States of America may
11 * require a specific license from the United States Government.
12 * It is the responsibility of any person or organization contemplating
13 * export to obtain such a license before exporting.
14 *
15 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16 * distribute this software and its documentation for any purpose and
17 * without fee is hereby granted, provided that the above copyright
18 * notice appear in all copies and that both that copyright notice and
19 * this permission notice appear in supporting documentation, and that
20 * the name of M.I.T. not be used in advertising or publicity pertaining
21 * to distribution of the software without specific, written prior
22 * permission. Furthermore if you modify this software you must label
23 * your software as modified software and not distribute it in such a
24 * fashion that it might be confused with the original M.I.T. software.
25 * M.I.T. makes no representations about the suitability of
26 * this software for any purpose. It is provided "as is" without express
27 * or implied warranty.
28 *
29 *
30 */
31 /* draft-ietf-krb-wg-gssapi-cfx-05 */
32
33 #ifndef _KERNEL
34 #include <assert.h>
35 #include <stdarg.h>
36
37 #define ASSERT assert
38 #endif
39
40 /* Solaris Kerberos */
41 #include "k5-int.h" /* for zap() */
42 #include "k5-platform.h"
43
44 /* Solaris Kerberos */
45 #include "k5-platform-store_16.h"
46 #include "k5-platform-store_64.h"
47 #include "k5-platform-load_16.h"
48 #include "k5-platform-load_64.h"
49
50 #include "gssapiP_krb5.h"
51 #include <sys/int_limits.h>
52
53 static int
rotate_left(void * ptr,size_t bufsiz,size_t rc)54 rotate_left (void *ptr, size_t bufsiz, size_t rc)
55 {
56 /* Optimize for receiving. After some debugging is done, the MIT
57 implementation won't do any rotates on sending, and while
58 debugging, they'll be randomly chosen.
59
60 Return 1 for success, 0 for failure (ENOMEM). */
61 void *tbuf;
62
63 if (bufsiz == 0)
64 return 1;
65 rc = rc % bufsiz;
66 if (rc == 0)
67 return 1;
68
69 tbuf = MALLOC(rc);
70 if (tbuf == 0)
71 return 0;
72 (void) memcpy(tbuf, ptr, rc);
73 (void) memmove(ptr, (char *)ptr + rc, bufsiz - rc);
74 (void) memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
75 FREE(tbuf, rc);
76 return 1;
77 }
78
79 static const gss_buffer_desc empty_message = { 0, 0 };
80
81 #define FLAG_SENDER_IS_ACCEPTOR 0x01
82 #define FLAG_WRAP_CONFIDENTIAL 0x02
83 #define FLAG_ACCEPTOR_SUBKEY 0x04
84
85 krb5_error_code
gss_krb5int_make_seal_token_v3(krb5_context context,krb5_gss_ctx_id_rec * ctx,const gss_buffer_desc * message,gss_buffer_t token,int conf_req_flag,int toktype)86 gss_krb5int_make_seal_token_v3 (krb5_context context,
87 krb5_gss_ctx_id_rec *ctx,
88 const gss_buffer_desc * message,
89 gss_buffer_t token,
90 int conf_req_flag, int toktype)
91 {
92 size_t bufsize = 16;
93 unsigned char *outbuf = 0;
94 krb5_error_code err;
95 int key_usage;
96 unsigned char acceptor_flag;
97 const gss_buffer_desc *message2 = message;
98 #ifdef CFX_EXERCISE
99 size_t rrc;
100 #endif
101 size_t ec;
102 unsigned short tok_id;
103 krb5_checksum sum;
104 krb5_keyblock *key;
105
106 ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
107 ASSERT(ctx->big_endian == 0);
108
109 acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
110 key_usage = (toktype == KG_TOK_WRAP_MSG
111 ? (ctx->initiate
112 ? KG_USAGE_INITIATOR_SEAL
113 : KG_USAGE_ACCEPTOR_SEAL)
114 : (ctx->initiate
115 ? KG_USAGE_INITIATOR_SIGN
116 : KG_USAGE_ACCEPTOR_SIGN));
117 if (ctx->have_acceptor_subkey) {
118 key = ctx->acceptor_subkey;
119 } else {
120 key = ctx->enc;
121 }
122
123 #ifdef _KERNEL
124 context->kef_cipher_mt = get_cipher_mech_type(context, key);
125 context->kef_hash_mt = get_hash_mech_type(context, key);
126
127 if ((err = init_key_kef(context->kef_cipher_mt, key))) {
128 return (GSS_S_FAILURE);
129 }
130
131 #endif /* _KERNEL */
132
133 #ifdef CFX_EXERCISE
134 {
135 static int initialized = 0;
136 if (!initialized) {
137 srand(time(0));
138 initialized = 1;
139 }
140 }
141 #endif
142
143 if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
144 krb5_data plain;
145 krb5_enc_data cipher;
146 size_t ec_max;
147 size_t tlen;
148
149 /* 300: Adds some slop. */
150 if (SIZE_MAX - 300 < message->length)
151 return ENOMEM;
152 ec_max = SIZE_MAX - message->length - 300;
153 if (ec_max > 0xffff)
154 ec_max = 0xffff;
155 /*
156 * EC should really be a multiple (1) of the number of octets that
157 * the cryptosystem would pad by if we didn't have the filler.
158 *
159 * For AES-CTS this will always be 0 and we expect no further
160 * enctypes, so there should be no issue here.
161 */
162 ec = 0;
163 plain.length = message->length + 16 + ec;
164 plain.data = MALLOC(plain.length);
165 if (plain.data == NULL)
166 return ENOMEM;
167
168 /* Get size of ciphertext. */
169 if ((err = krb5_c_encrypt_length(context,
170 ctx->enc->enctype, plain.length, &tlen))) {
171 FREE(plain.data, plain.length);
172 return (err);
173 }
174
175 bufsize = 16 + tlen;
176 /* Allocate space for header plus encrypted data. */
177 outbuf = MALLOC(bufsize);
178 if (outbuf == NULL) {
179 FREE(plain.data, plain.length);
180 return ENOMEM;
181 }
182
183 /* TOK_ID */
184 store_16_be(0x0504, outbuf);
185 /* flags */
186 outbuf[2] = (acceptor_flag
187 | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
188 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
189 /* filler */
190 outbuf[3] = 0xff;
191 /* EC */
192 store_16_be(ec, outbuf+4);
193 /* RRC */
194 store_16_be(0, outbuf+6);
195 store_64_be(ctx->seq_send, outbuf+8);
196
197 (void) memcpy(plain.data, message->value, message->length);
198 (void) memset(plain.data + message->length, 'x', ec);
199 (void) memcpy(plain.data + message->length + ec, outbuf, 16);
200
201 /* Should really use scatter/gather crypto interfaces */
202 cipher.ciphertext.data = (char *)outbuf + 16;
203 cipher.ciphertext.length = bufsize - 16;
204 cipher.enctype = key->enctype;
205 err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
206 (void) bzero(plain.data, plain.length);
207 FREE(plain.data, plain.length);
208 plain.data = 0;
209 if (err)
210 goto error;
211
212 /* Now that we know we're returning a valid token.... */
213 ctx->seq_send++;
214
215 #ifdef CFX_EXERCISE
216 rrc = rand() & 0xffff;
217 if (rotate_left(outbuf+16, bufsize-16,
218 (bufsize-16) - (rrc % (bufsize - 16))))
219 store_16_be(rrc, outbuf+6);
220 /* If the rotate fails, don't worry about it. */
221 #endif
222 } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
223 krb5_data plain;
224
225 /* Here, message is the application-supplied data; message2 is
226 what goes into the output token. They may be the same, or
227 message2 may be empty (for MIC). */
228
229 tok_id = 0x0504;
230
231 wrap_with_checksum:
232 plain.length = message->length + 16;
233 plain.data = MALLOC(message->length + 16);
234 if (plain.data == NULL)
235 return ENOMEM;
236
237 if (ctx->cksum_size > 0xffff) {
238 FREE(plain.data, plain.length);
239 return EINVAL;
240 }
241
242 bufsize = 16 + message2->length + ctx->cksum_size;
243 outbuf = MALLOC(bufsize);
244 if (outbuf == NULL) {
245 FREE(plain.data, plain.length);
246 plain.data = 0;
247 err = ENOMEM;
248 goto error;
249 }
250
251 /* TOK_ID */
252 store_16_be(tok_id, outbuf);
253 /* flags */
254 outbuf[2] = (acceptor_flag
255 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
256 /* filler */
257 outbuf[3] = 0xff;
258 if (toktype == KG_TOK_WRAP_MSG) {
259 /* Use 0 for checksum calculation, substitute
260 checksum length later. */
261 /* EC */
262 store_16_be(0, outbuf+4);
263 /* RRC */
264 store_16_be(0, outbuf+6);
265 } else {
266 /* MIC and DEL store 0xFF in EC and RRC. */
267 store_16_be(0xffff, outbuf+4);
268 store_16_be(0xffff, outbuf+6);
269 }
270 store_64_be(ctx->seq_send, outbuf+8);
271
272 (void) memcpy(plain.data, message->value, message->length);
273 (void) memcpy(plain.data + message->length, outbuf, 16);
274
275 /* Fill in the output token -- data contents, if any, and
276 space for the checksum. */
277 if (message2->length)
278 (void) memcpy(outbuf + 16, message2->value, message2->length);
279
280 sum.contents = outbuf + 16 + message2->length;
281 sum.length = ctx->cksum_size;
282
283 err = krb5_c_make_checksum(context, ctx->cksumtype, key,
284 key_usage, &plain, &sum);
285 bzero(plain.data, plain.length);
286 FREE(plain.data, plain.length);
287 plain.data = 0;
288 if (err) {
289 bzero(outbuf,bufsize);
290 err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
291 goto error;
292 }
293 if (sum.length != ctx->cksum_size) {
294 err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
295 goto error;
296 }
297 (void) memcpy(outbuf + 16 + message2->length, sum.contents,
298 ctx->cksum_size);
299 krb5_free_checksum_contents(context, &sum);
300 sum.contents = 0;
301 /* Now that we know we're actually generating the token... */
302 ctx->seq_send++;
303
304 if (toktype == KG_TOK_WRAP_MSG) {
305 #ifdef CFX_EXERCISE
306 rrc = rand() & 0xffff;
307 /* If the rotate fails, don't worry about it. */
308 if (rotate_left(outbuf+16, bufsize-16,
309 (bufsize-16) - (rrc % (bufsize - 16))))
310 store_16_be(rrc, outbuf+6);
311 #endif
312 /* Fix up EC field. */
313 store_16_be(ctx->cksum_size, outbuf+4);
314 } else {
315 store_16_be(0xffff, outbuf+6);
316 }
317 } else if (toktype == KG_TOK_MIC_MSG) {
318 tok_id = 0x0404;
319 message2 = &empty_message;
320 goto wrap_with_checksum;
321 } else if (toktype == KG_TOK_DEL_CTX) {
322 /*
323 * Solaris Kerberos:
324 * No token should be generated for context deletion. Just
325 * return.
326 */
327 return 0;
328 } else {
329 err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
330 goto error;
331 }
332
333 token->value = outbuf;
334 token->length = bufsize;
335 return 0;
336
337 error:
338 FREE(outbuf, bufsize);
339 token->value = NULL;
340 token->length = 0;
341 return err;
342 }
343
344 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
345 conf_state is only valid if SEAL. */
346
347 OM_uint32
gss_krb5int_unseal_token_v3(krb5_context * contextptr,OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,unsigned char * ptr,int bodysize,gss_buffer_t message_buffer,int * conf_state,int * qop_state,int toktype)348 gss_krb5int_unseal_token_v3(krb5_context *contextptr,
349 OM_uint32 *minor_status,
350 krb5_gss_ctx_id_rec *ctx,
351 unsigned char *ptr, int bodysize,
352 gss_buffer_t message_buffer,
353 int *conf_state, int *qop_state, int toktype)
354 {
355 krb5_context context = *contextptr;
356 krb5_data plain;
357 gssint_uint64 seqnum;
358 size_t ec, rrc;
359 int key_usage;
360 unsigned char acceptor_flag;
361 krb5_checksum sum;
362 krb5_error_code err;
363 krb5_boolean valid;
364 krb5_keyblock *key;
365
366 ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
367 ASSERT(ctx->big_endian == 0);
368 ASSERT(ctx->proto == 1);
369
370 if (qop_state)
371 *qop_state = GSS_C_QOP_DEFAULT;
372
373 acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
374 key_usage = (toktype == KG_TOK_WRAP_MSG
375 ? (!ctx->initiate
376 ? KG_USAGE_INITIATOR_SEAL
377 : KG_USAGE_ACCEPTOR_SEAL)
378 : (!ctx->initiate
379 ? KG_USAGE_INITIATOR_SIGN
380 : KG_USAGE_ACCEPTOR_SIGN));
381
382 /* Oops. I wrote this code assuming ptr would be at the start of
383 the token header. */
384 ptr -= 2;
385 bodysize += 2;
386
387 if (bodysize < 16) {
388 defective:
389 *minor_status = 0;
390 return GSS_S_DEFECTIVE_TOKEN;
391 }
392 if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
393 *minor_status = (OM_uint32)G_BAD_DIRECTION;
394 return GSS_S_BAD_SIG;
395 }
396
397 /* Two things to note here.
398
399 First, we can't really enforce the use of the acceptor's subkey,
400 if we're the acceptor; the initiator may have sent messages
401 before getting the subkey. We could probably enforce it if
402 we're the initiator.
403
404 Second, if someone tweaks the code to not set the flag telling
405 the krb5 library to generate a new subkey in the AP-REP
406 message, the MIT library may include a subkey anyways --
407 namely, a copy of the AP-REQ subkey, if it was provided. So
408 the initiator may think we wanted a subkey, and set the flag,
409 even though we weren't trying to set the subkey. The "other"
410 key, the one not asserted by the acceptor, will have the same
411 value in that case, though, so we can just ignore the flag. */
412 if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
413 key = ctx->acceptor_subkey;
414 } else {
415 key = ctx->enc;
416 }
417
418 #ifdef _KERNEL
419 context->kef_cipher_mt = get_cipher_mech_type(context, key);
420 context->kef_hash_mt = get_hash_mech_type(context, key);
421
422 if ((err = init_key_kef(context->kef_cipher_mt, key))) {
423 return (GSS_S_FAILURE);
424 }
425 #endif /* _KERNEL */
426
427 if (toktype == KG_TOK_WRAP_MSG) {
428 if (load_16_be(ptr) != 0x0504)
429 goto defective;
430 if (ptr[3] != 0xff)
431 goto defective;
432 ec = load_16_be(ptr+4);
433 rrc = load_16_be(ptr+6);
434 seqnum = load_64_be(ptr+8);
435 if (!rotate_left(ptr+16, bodysize-16, rrc)) {
436 no_mem:
437 *minor_status = ENOMEM;
438 return GSS_S_FAILURE;
439 }
440 if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
441 /* confidentiality */
442 krb5_enc_data cipher;
443 unsigned char *althdr;
444 size_t plainlen;
445
446 if (conf_state)
447 *conf_state = 1;
448 /* Do we have no decrypt_size function?
449
450 For all current cryptosystems, the ciphertext size will
451 be larger than the plaintext size. */
452 cipher.enctype = key->enctype;
453 cipher.ciphertext.length = bodysize - 16;
454 cipher.ciphertext.data = (char *)ptr + 16;
455 plain.length = plainlen = bodysize - 16;
456 plain.data = MALLOC(plain.length);
457 if (plain.data == NULL)
458 goto no_mem;
459 err = krb5_c_decrypt(context, key, key_usage, 0,
460 &cipher, &plain);
461 if (err) {
462 goto error;
463 }
464 /* Don't use bodysize here! Use the fact that
465 plain.length has been adjusted to the
466 correct length. */
467 althdr = (uchar_t *)plain.data + plain.length - 16;
468 if (load_16_be(althdr) != 0x0504
469 || althdr[2] != ptr[2]
470 || althdr[3] != ptr[3]
471 || memcmp(althdr+8, ptr+8, 8)) {
472 FREE(plain.data, plainlen);
473 goto defective;
474 }
475 message_buffer->length = plain.length - ec - 16;
476 message_buffer->value = MALLOC(message_buffer->length);
477 if (message_buffer->value == NULL) {
478 FREE(plain.data, plainlen);
479 goto no_mem;
480 }
481 (void) memcpy(message_buffer->value, plain.data,
482 message_buffer->length);
483 FREE(plain.data, plainlen);
484 } else {
485 /* no confidentiality */
486 if (conf_state)
487 *conf_state = 0;
488 if (ec + 16 < ec)
489 /* overflow check */
490 goto defective;
491 if (ec + 16 > bodysize)
492 goto defective;
493 /* We have: header | msg | cksum.
494 We need cksum(msg | header).
495 Rotate the first two. */
496 store_16_be(0, ptr+4);
497 store_16_be(0, ptr+6);
498 plain.length = bodysize-ec;
499 plain.data = (char *)ptr;
500 if (!rotate_left(ptr, bodysize-ec, 16))
501 goto no_mem;
502 sum.length = ec;
503 if (sum.length != ctx->cksum_size) {
504 *minor_status = 0;
505 return GSS_S_BAD_SIG;
506 }
507 sum.contents = ptr+bodysize-ec;
508 sum.checksum_type = ctx->cksumtype;
509 err = krb5_c_verify_checksum(context, key, key_usage,
510 &plain, &sum, &valid);
511 if (err) {
512 *minor_status = err;
513 return GSS_S_BAD_SIG;
514 }
515 if (!valid) {
516 *minor_status = 0;
517 return GSS_S_BAD_SIG;
518 }
519 message_buffer->length = plain.length - 16;
520 message_buffer->value = MALLOC(message_buffer->length);
521 if (message_buffer->value == NULL)
522 goto no_mem;
523 (void) memcpy(message_buffer->value,
524 plain.data, message_buffer->length);
525
526 /*
527 * Solaris Kerberos: Restore the original token.
528 * This allows the token to be detected as a duplicate if it
529 * is passed in to gss_unwrap() again.
530 */
531 if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
532 goto no_mem;
533 store_16_be(ec, ptr+4);
534 store_16_be(rrc, ptr+6);
535 }
536 err = g_order_check(&ctx->seqstate, seqnum);
537 *minor_status = 0;
538 return err;
539 } else if (toktype == KG_TOK_MIC_MSG) {
540 /* wrap token, no confidentiality */
541 if (load_16_be(ptr) != 0x0404)
542 goto defective;
543 verify_mic_1:
544 if (ptr[3] != 0xff)
545 goto defective;
546 if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
547 goto defective;
548 seqnum = load_64_be(ptr+8);
549 plain.length = message_buffer->length + 16;
550 plain.data = MALLOC(plain.length);
551 if (plain.data == NULL)
552 goto no_mem;
553 if (message_buffer->length)
554 (void) memcpy(plain.data,
555 message_buffer->value, message_buffer->length);
556 (void) memcpy(plain.data + message_buffer->length, ptr, 16);
557 sum.length = bodysize - 16;
558 sum.contents = ptr + 16;
559 sum.checksum_type = ctx->cksumtype;
560 err = krb5_c_verify_checksum(context, key, key_usage,
561 &plain, &sum, &valid);
562 if (err) {
563 error:
564 FREE(plain.data, plain.length);
565 *minor_status = err;
566 save_error_info(*minor_status, context);
567 return GSS_S_BAD_SIG; /* XXX */
568 }
569 FREE(plain.data, plain.length);
570 if (!valid) {
571 *minor_status = 0;
572 return GSS_S_BAD_SIG;
573 }
574 err = g_order_check(&ctx->seqstate, seqnum);
575 *minor_status = 0;
576 return err;
577 } else if (toktype == KG_TOK_DEL_CTX) {
578 if (load_16_be(ptr) != 0x0405)
579 goto defective;
580 message_buffer = (gss_buffer_t)&empty_message;
581 goto verify_mic_1;
582 } else {
583 goto defective;
584 }
585 }
586