xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/mech/k5sealv3.c (revision 40cb5e5daa7b80bb70fcf8dadfb20f9281566331)
1 /*
2  * Copyright 2004 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 	tok_id = 0x0405;
317 	message = message2 = &empty_message;
318 	goto wrap_with_checksum;
319     } else {
320 	err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
321 	goto error;
322     }
323 
324     token->value = outbuf;
325     token->length = bufsize;
326     return 0;
327 
328 error:
329     FREE(outbuf, bufsize);
330     token->value = NULL;
331     token->length = 0;
332     return err;
333 }
334 
335 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
336    conf_state is only valid if SEAL. */
337 
338 OM_uint32
339 gss_krb5int_unseal_token_v3(krb5_context context,
340 			    OM_uint32 *minor_status,
341 			    krb5_gss_ctx_id_rec *ctx,
342 			    unsigned char *ptr, int bodysize,
343 			    gss_buffer_t message_buffer,
344 			    int *conf_state, int *qop_state, int toktype)
345 {
346     krb5_data plain;
347     gssint_uint64 seqnum;
348     size_t ec, rrc;
349     int key_usage;
350     unsigned char acceptor_flag;
351     krb5_checksum sum;
352     krb5_error_code err;
353     krb5_boolean valid;
354     krb5_keyblock *key;
355 
356     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
357     ASSERT(ctx->big_endian == 0);
358     ASSERT(ctx->proto == 1);
359 
360     if (qop_state)
361 	*qop_state = GSS_C_QOP_DEFAULT;
362 
363     acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
364     key_usage = (toktype == KG_TOK_WRAP_MSG
365 		 ? (!ctx->initiate
366 		    ? KG_USAGE_INITIATOR_SEAL
367 		    : KG_USAGE_ACCEPTOR_SEAL)
368 		 : (!ctx->initiate
369 		    ? KG_USAGE_INITIATOR_SIGN
370 		    : KG_USAGE_ACCEPTOR_SIGN));
371 
372     /* Oops.  I wrote this code assuming ptr would be at the start of
373        the token header.  */
374     ptr -= 2;
375     bodysize += 2;
376 
377     if (bodysize < 16) {
378     defective:
379 	*minor_status = 0;
380 	return GSS_S_DEFECTIVE_TOKEN;
381     }
382     if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
383 	*minor_status = (OM_uint32)G_BAD_DIRECTION;
384 	return GSS_S_BAD_SIG;
385     }
386 
387     /* Two things to note here.
388 
389        First, we can't really enforce the use of the acceptor's subkey,
390        if we're the acceptor; the initiator may have sent messages
391        before getting the subkey.  We could probably enforce it if
392        we're the initiator.
393 
394        Second, if someone tweaks the code to not set the flag telling
395        the krb5 library to generate a new subkey in the AP-REP
396        message, the MIT library may include a subkey anyways --
397        namely, a copy of the AP-REQ subkey, if it was provided.  So
398        the initiator may think we wanted a subkey, and set the flag,
399        even though we weren't trying to set the subkey.  The "other"
400        key, the one not ASSERTed by the acceptor, will have the same
401        value in that case, though, so we can just ignore the flag.  */
402     if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
403 	key = ctx->acceptor_subkey;
404     } else {
405 	key = ctx->enc;
406     }
407 
408 #ifdef _KERNEL
409     context->kef_cipher_mt = get_cipher_mech_type(context, key);
410     context->kef_hash_mt = get_hash_mech_type(context, key);
411 
412     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
413 	return (GSS_S_FAILURE);
414     }
415 #endif /* _KERNEL */
416 
417     if (toktype == KG_TOK_WRAP_MSG) {
418 	if (load_16_be(ptr) != 0x0504)
419 	    goto defective;
420 	if (ptr[3] != 0xff)
421 	    goto defective;
422 	ec = load_16_be(ptr+4);
423 	rrc = load_16_be(ptr+6);
424 	seqnum = load_64_be(ptr+8);
425 	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
426     no_mem:
427 	    *minor_status = ENOMEM;
428 	    return GSS_S_FAILURE;
429 	}
430 	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
431 	    /* confidentiality */
432 	    krb5_enc_data cipher;
433 	    unsigned char *althdr;
434             size_t plainlen;
435 
436 	    if (conf_state)
437 		*conf_state = 1;
438 	    /* Do we have no decrypt_size function?
439 
440 	       For all current cryptosystems, the ciphertext size will
441 	       be larger than the plaintext size.  */
442 	    cipher.enctype = key->enctype;
443 	    cipher.ciphertext.length = bodysize - 16;
444 	    cipher.ciphertext.data = (char *)ptr + 16;
445 	    plain.length = plainlen = bodysize - 16;
446 	    plain.data = MALLOC(plain.length);
447 	    if (plain.data == NULL)
448 		goto no_mem;
449 	    err = krb5_c_decrypt(context, key, key_usage, 0,
450 				 &cipher, &plain);
451 	    if (err) {
452 		goto error;
453 	    }
454 	    /* Don't use bodysize here!  Use the fact that
455 	       plain.length has been adjusted to the
456 	       correct length.  */
457 	    althdr = (uchar_t *)plain.data + plain.length - 16;
458 	    if (load_16_be(althdr) != 0x0504
459 		|| althdr[2] != ptr[2]
460 		|| althdr[3] != ptr[3]
461 		|| memcmp(althdr+8, ptr+8, 8)) {
462 		FREE(plain.data, plainlen);
463 		goto defective;
464 	    }
465 	    message_buffer->length = plain.length - ec - 16;
466 	    message_buffer->value = MALLOC(message_buffer->length);
467 	    if (message_buffer->value == NULL) {
468 		FREE(plain.data, plainlen);
469 		goto no_mem;
470 	    }
471 	    (void) memcpy(message_buffer->value, plain.data,
472 			message_buffer->length);
473 	    FREE(plain.data, plainlen);
474 	} else {
475 	    /* no confidentiality */
476 	    if (conf_state)
477 		*conf_state = 0;
478 	    if (ec + 16 < ec)
479 		/* overflow check */
480 		goto defective;
481 	    if (ec + 16 > bodysize)
482 		goto defective;
483 	    /* We have: header | msg | cksum.
484 	       We need cksum(msg | header).
485 	       Rotate the first two.  */
486 	    store_16_be(0, ptr+4);
487 	    store_16_be(0, ptr+6);
488 	    plain.length = bodysize - ec;
489 	    plain.data = (char *)ptr;
490 	    if (!rotate_left(ptr, bodysize-ec, 16))
491 		goto no_mem;
492 	    sum.length = ec;
493 	    if (sum.length != ctx->cksum_size) {
494 		*minor_status = 0;
495 		return GSS_S_BAD_SIG;
496 	    }
497 	    sum.contents = ptr+bodysize-ec;
498 	    sum.checksum_type = ctx->cksumtype;
499 	    err = krb5_c_verify_checksum(context, key, key_usage,
500 					 &plain, &sum, &valid);
501 	    if (err) {
502 		*minor_status = err;
503 		return GSS_S_BAD_SIG;
504 	    }
505 	    if (!valid) {
506 		*minor_status = 0;
507 		return GSS_S_BAD_SIG;
508 	    }
509 	    message_buffer->length = plain.length - 16;
510 	    message_buffer->value = MALLOC(message_buffer->length);
511 	    if (message_buffer->value == NULL)
512 		goto no_mem;
513 	    (void) memcpy(message_buffer->value,
514 		plain.data, message_buffer->length);
515 	}
516 	err = g_order_check(&ctx->seqstate, seqnum);
517 	*minor_status = 0;
518 	return err;
519     } else if (toktype == KG_TOK_MIC_MSG) {
520 	/* wrap token, no confidentiality */
521 	if (load_16_be(ptr) != 0x0404)
522 	    goto defective;
523     verify_mic_1:
524 	if (ptr[3] != 0xff)
525 	    goto defective;
526 	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
527 	    goto defective;
528 	seqnum = load_64_be(ptr+8);
529 	plain.length = message_buffer->length + 16;
530 	plain.data = MALLOC(plain.length);
531 	if (plain.data == NULL)
532 	    goto no_mem;
533 	if (message_buffer->length)
534 	    (void) memcpy(plain.data,
535 		message_buffer->value, message_buffer->length);
536 	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
537 	sum.length = bodysize - 16;
538 	sum.contents = ptr + 16;
539 	sum.checksum_type = ctx->cksumtype;
540 	err = krb5_c_verify_checksum(context, key, key_usage,
541 				     &plain, &sum, &valid);
542 	if (err) {
543 error:
544 	    FREE(plain.data, plain.length);
545 	    *minor_status = err;
546 	    return GSS_S_BAD_SIG; /* XXX */
547 	}
548 	FREE(plain.data, plain.length);
549 	if (!valid) {
550 	    *minor_status = 0;
551 	    return GSS_S_BAD_SIG;
552 	}
553 	err = g_order_check(&ctx->seqstate, seqnum);
554 	*minor_status = 0;
555 	return err;
556     } else if (toktype == KG_TOK_DEL_CTX) {
557 	if (load_16_be(ptr) != 0x0405)
558 	    goto defective;
559 	message_buffer = (gss_buffer_t)&empty_message;
560 	goto verify_mic_1;
561     } else {
562 	goto defective;
563     }
564 }
565