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