xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/mech/k5sealv3.c (revision 8c69cc8fbe729fa7b091e901c4b50508ccc6bb33)
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
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
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
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