xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/mech/k5sealv3.c (revision 7dacfc4494f6d14358974ef2830b5cd8c66a84de)
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 #ifdef CFX_EXERCISE
95     size_t rrc;
96 #endif
97     size_t ec;
98     unsigned short tok_id;
99     krb5_checksum sum;
100     krb5_keyblock *key;
101 
102     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
103     ASSERT(ctx->big_endian == 0);
104 
105     acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
106     key_usage = (toktype == KG_TOK_WRAP_MSG
107 		 ? (ctx->initiate
108 		    ? KG_USAGE_INITIATOR_SEAL
109 		    : KG_USAGE_ACCEPTOR_SEAL)
110 		 : (ctx->initiate
111 		    ? KG_USAGE_INITIATOR_SIGN
112 		    : KG_USAGE_ACCEPTOR_SIGN));
113     if (ctx->have_acceptor_subkey) {
114 	key = ctx->acceptor_subkey;
115     } else {
116 	key = ctx->enc;
117     }
118 
119 #ifdef _KERNEL
120     context->kef_cipher_mt = get_cipher_mech_type(context, key);
121     context->kef_hash_mt = get_hash_mech_type(context, key);
122 
123     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
124 	return (GSS_S_FAILURE);
125     }
126 
127 #endif /* _KERNEL */
128 
129 #ifdef CFX_EXERCISE
130     {
131 	static int initialized = 0;
132 	if (!initialized) {
133 	    srand(time(0));
134 	    initialized = 1;
135 	}
136     }
137 #endif
138 
139     if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
140 	krb5_data plain;
141 	krb5_enc_data cipher;
142 	size_t ec_max;
143 	size_t tlen;
144 
145 	/* 300: Adds some slop.  */
146 	if (SIZE_MAX - 300 < message->length)
147 	    return ENOMEM;
148 	ec_max = SIZE_MAX - message->length - 300;
149 	if (ec_max > 0xffff)
150 	    ec_max = 0xffff;
151 	/*
152 	 * EC should really be a multiple (1) of the number of octets that
153 	 * the cryptosystem would pad by if we didn't have the filler.
154 	 *
155 	 * For AES-CTS this will always be 0 and we expect no further
156 	 * enctypes, so there should be no issue here.
157 	 */
158 	ec = 0;
159 	plain.length = message->length + 16 + ec;
160 	plain.data = MALLOC(plain.length);
161 	if (plain.data == NULL)
162 	    return ENOMEM;
163 
164 	/* Get size of ciphertext.  */
165 	if ((err = krb5_c_encrypt_length(context,
166 		ctx->enc->enctype, plain.length, &tlen))) {
167 	    FREE(plain.data, plain.length);
168 	    return (err);
169         }
170 
171 	bufsize = 16 + tlen;
172 	/* Allocate space for header plus encrypted data.  */
173 	outbuf = MALLOC(bufsize);
174 	if (outbuf == NULL) {
175 	    FREE(plain.data, plain.length);
176 	    return ENOMEM;
177 	}
178 
179 	/* TOK_ID */
180 	store_16_be(0x0504, outbuf);
181 	/* flags */
182 	outbuf[2] = (acceptor_flag
183 		     | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
184 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
185 	/* filler */
186 	outbuf[3] = 0xff;
187 	/* EC */
188 	store_16_be(ec, outbuf+4);
189 	/* RRC */
190 	store_16_be(0, outbuf+6);
191 	store_64_be(ctx->seq_send, outbuf+8);
192 
193 	(void) memcpy(plain.data, message->value, message->length);
194 	(void) memset(plain.data + message->length, 'x', ec);
195 	(void) memcpy(plain.data + message->length + ec, outbuf, 16);
196 
197 	/* Should really use scatter/gather crypto interfaces */
198 	cipher.ciphertext.data = (char *)outbuf + 16;
199 	cipher.ciphertext.length = bufsize - 16;
200 	cipher.enctype = key->enctype;
201 	err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
202 	(void) bzero(plain.data, plain.length);
203 	FREE(plain.data, plain.length);
204 	plain.data = 0;
205 	if (err)
206 	    goto error;
207 
208 	/* Now that we know we're returning a valid token....  */
209 	ctx->seq_send++;
210 
211 #ifdef CFX_EXERCISE
212 	rrc = rand() & 0xffff;
213 	if (rotate_left(outbuf+16, bufsize-16,
214 			(bufsize-16) - (rrc % (bufsize - 16))))
215 	    store_16_be(rrc, outbuf+6);
216 	/* If the rotate fails, don't worry about it.  */
217 #endif
218     } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
219 	krb5_data plain;
220 
221 	/* Here, message is the application-supplied data; message2 is
222 	   what goes into the output token.  They may be the same, or
223 	   message2 may be empty (for MIC).  */
224 
225 	tok_id = 0x0504;
226 
227     wrap_with_checksum:
228 	plain.length = message->length + 16;
229 	plain.data = MALLOC(message->length + 16);
230 	if (plain.data == NULL)
231 	    return ENOMEM;
232 
233 	if (ctx->cksum_size > 0xffff) {
234 	    FREE(plain.data, plain.length);
235 	    return EINVAL;
236 	}
237 
238 	bufsize = 16 + message2->length + ctx->cksum_size;
239 	outbuf = MALLOC(bufsize);
240 	if (outbuf == NULL) {
241 	    FREE(plain.data, plain.length);
242 	    plain.data = 0;
243 	    err = ENOMEM;
244 	    goto error;
245 	}
246 
247 	/* TOK_ID */
248 	store_16_be(tok_id, outbuf);
249 	/* flags */
250 	outbuf[2] = (acceptor_flag
251 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
252 	/* filler */
253 	outbuf[3] = 0xff;
254 	if (toktype == KG_TOK_WRAP_MSG) {
255 	    /* Use 0 for checksum calculation, substitute
256 	       checksum length later.  */
257 	    /* EC */
258 	    store_16_be(0, outbuf+4);
259 	    /* RRC */
260 	    store_16_be(0, outbuf+6);
261 	} else {
262 	    /* MIC and DEL store 0xFF in EC and RRC.  */
263 	    store_16_be(0xffff, outbuf+4);
264 	    store_16_be(0xffff, outbuf+6);
265 	}
266 	store_64_be(ctx->seq_send, outbuf+8);
267 
268 	(void) memcpy(plain.data, message->value, message->length);
269 	(void) memcpy(plain.data + message->length, outbuf, 16);
270 
271 	/* Fill in the output token -- data contents, if any, and
272 	   space for the checksum.  */
273 	if (message2->length)
274 	    (void) memcpy(outbuf + 16, message2->value, message2->length);
275 
276 	sum.contents = outbuf + 16 + message2->length;
277 	sum.length = ctx->cksum_size;
278 
279 	err = krb5_c_make_checksum(context, ctx->cksumtype, key,
280 				   key_usage, &plain, &sum);
281 
282 	bzero(plain.data, plain.length);
283 	FREE(plain.data, plain.length);
284 	plain.data = 0;
285 	if (err) {
286 	    bzero(outbuf,bufsize);
287 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
288 	    goto error;
289 	}
290 	if (sum.length != ctx->cksum_size) {
291 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
292 	    goto error;
293 	}
294 	(void) memcpy(outbuf + 16 + message2->length, sum.contents,
295 	    ctx->cksum_size);
296 	krb5_free_checksum_contents(context, &sum);
297 	sum.contents = 0;
298 	/* Now that we know we're actually generating the token...  */
299 	ctx->seq_send++;
300 
301 	if (toktype == KG_TOK_WRAP_MSG) {
302 #ifdef CFX_EXERCISE
303 	    rrc = rand() & 0xffff;
304 	    /* If the rotate fails, don't worry about it.  */
305 	    if (rotate_left(outbuf+16, bufsize-16,
306 			    (bufsize-16) - (rrc % (bufsize - 16))))
307 		store_16_be(rrc, outbuf+6);
308 #endif
309 	    /* Fix up EC field.  */
310 	    store_16_be(ctx->cksum_size, outbuf+4);
311 	} else {
312 	    store_16_be(0xffff, outbuf+6);
313 	}
314     } else if (toktype == KG_TOK_MIC_MSG) {
315 	tok_id = 0x0404;
316 	message2 = &empty_message;
317 	goto wrap_with_checksum;
318     } else if (toktype == KG_TOK_DEL_CTX) {
319 	/*
320 	 * Solaris Kerberos:
321 	 * No token should be generated for context deletion. Just
322 	 * return.
323 	 */
324 	return 0;
325     } else {
326 	err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
327 	goto error;
328     }
329 
330     token->value = outbuf;
331     token->length = bufsize;
332     return 0;
333 
334 error:
335     FREE(outbuf, bufsize);
336     token->value = NULL;
337     token->length = 0;
338     return err;
339 }
340 
341 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
342    conf_state is only valid if SEAL. */
343 
344 OM_uint32
345 gss_krb5int_unseal_token_v3(krb5_context context,
346 			    OM_uint32 *minor_status,
347 			    krb5_gss_ctx_id_rec *ctx,
348 			    unsigned char *ptr, int bodysize,
349 			    gss_buffer_t message_buffer,
350 			    int *conf_state, int *qop_state, int toktype)
351 {
352     krb5_data plain;
353     gssint_uint64 seqnum;
354     size_t ec, rrc;
355     int key_usage;
356     unsigned char acceptor_flag;
357     krb5_checksum sum;
358     krb5_error_code err;
359     krb5_boolean valid;
360     krb5_keyblock *key;
361 
362     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
363     ASSERT(ctx->big_endian == 0);
364     ASSERT(ctx->proto == 1);
365 
366     if (qop_state)
367 	*qop_state = GSS_C_QOP_DEFAULT;
368 
369     acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
370     key_usage = (toktype == KG_TOK_WRAP_MSG
371 		 ? (!ctx->initiate
372 		    ? KG_USAGE_INITIATOR_SEAL
373 		    : KG_USAGE_ACCEPTOR_SEAL)
374 		 : (!ctx->initiate
375 		    ? KG_USAGE_INITIATOR_SIGN
376 		    : KG_USAGE_ACCEPTOR_SIGN));
377 
378     /* Oops.  I wrote this code assuming ptr would be at the start of
379        the token header.  */
380     ptr -= 2;
381     bodysize += 2;
382 
383     if (bodysize < 16) {
384     defective:
385 	*minor_status = 0;
386 	return GSS_S_DEFECTIVE_TOKEN;
387     }
388     if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
389 	*minor_status = (OM_uint32)G_BAD_DIRECTION;
390 	return GSS_S_BAD_SIG;
391     }
392 
393     /* Two things to note here.
394 
395        First, we can't really enforce the use of the acceptor's subkey,
396        if we're the acceptor; the initiator may have sent messages
397        before getting the subkey.  We could probably enforce it if
398        we're the initiator.
399 
400        Second, if someone tweaks the code to not set the flag telling
401        the krb5 library to generate a new subkey in the AP-REP
402        message, the MIT library may include a subkey anyways --
403        namely, a copy of the AP-REQ subkey, if it was provided.  So
404        the initiator may think we wanted a subkey, and set the flag,
405        even though we weren't trying to set the subkey.  The "other"
406        key, the one not ASSERTed by the acceptor, will have the same
407        value in that case, though, so we can just ignore the flag.  */
408     if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
409 	key = ctx->acceptor_subkey;
410     } else {
411 	key = ctx->enc;
412     }
413 
414 #ifdef _KERNEL
415     context->kef_cipher_mt = get_cipher_mech_type(context, key);
416     context->kef_hash_mt = get_hash_mech_type(context, key);
417 
418     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
419 	return (GSS_S_FAILURE);
420     }
421 #endif /* _KERNEL */
422 
423     if (toktype == KG_TOK_WRAP_MSG) {
424 	if (load_16_be(ptr) != 0x0504)
425 	    goto defective;
426 	if (ptr[3] != 0xff)
427 	    goto defective;
428 	ec = load_16_be(ptr+4);
429 	rrc = load_16_be(ptr+6);
430 	seqnum = load_64_be(ptr+8);
431 	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
432     no_mem:
433 	    *minor_status = ENOMEM;
434 	    return GSS_S_FAILURE;
435 	}
436 	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
437 	    /* confidentiality */
438 	    krb5_enc_data cipher;
439 	    unsigned char *althdr;
440             size_t plainlen;
441 
442 	    if (conf_state)
443 		*conf_state = 1;
444 	    /* Do we have no decrypt_size function?
445 
446 	       For all current cryptosystems, the ciphertext size will
447 	       be larger than the plaintext size.  */
448 	    cipher.enctype = key->enctype;
449 	    cipher.ciphertext.length = bodysize - 16;
450 	    cipher.ciphertext.data = (char *)ptr + 16;
451 	    plain.length = plainlen = bodysize - 16;
452 	    plain.data = MALLOC(plain.length);
453 	    if (plain.data == NULL)
454 		goto no_mem;
455 	    err = krb5_c_decrypt(context, key, key_usage, 0,
456 				 &cipher, &plain);
457 	    if (err) {
458 		goto error;
459 	    }
460 	    /* Don't use bodysize here!  Use the fact that
461 	       plain.length has been adjusted to the
462 	       correct length.  */
463 	    althdr = (uchar_t *)plain.data + plain.length - 16;
464 	    if (load_16_be(althdr) != 0x0504
465 		|| althdr[2] != ptr[2]
466 		|| althdr[3] != ptr[3]
467 		|| memcmp(althdr+8, ptr+8, 8)) {
468 		FREE(plain.data, plainlen);
469 		goto defective;
470 	    }
471 	    message_buffer->length = plain.length - ec - 16;
472 	    message_buffer->value = MALLOC(message_buffer->length);
473 	    if (message_buffer->value == NULL) {
474 		FREE(plain.data, plainlen);
475 		goto no_mem;
476 	    }
477 	    (void) memcpy(message_buffer->value, plain.data,
478 			message_buffer->length);
479 	    FREE(plain.data, plainlen);
480 	} else {
481 	    /* no confidentiality */
482 	    if (conf_state)
483 		*conf_state = 0;
484 	    if (ec + 16 < ec)
485 		/* overflow check */
486 		goto defective;
487 	    if (ec + 16 > bodysize)
488 		goto defective;
489 	    /* We have: header | msg | cksum.
490 	       We need cksum(msg | header).
491 	       Rotate the first two.  */
492 	    store_16_be(0, ptr+4);
493 	    store_16_be(0, ptr+6);
494 	    plain.length = bodysize - ec;
495 	    plain.data = (char *)ptr;
496 	    if (!rotate_left(ptr, bodysize-ec, 16))
497 		goto no_mem;
498 	    sum.length = ec;
499 	    if (sum.length != ctx->cksum_size) {
500 		*minor_status = 0;
501 		return GSS_S_BAD_SIG;
502 	    }
503 	    sum.contents = ptr+bodysize-ec;
504 	    sum.checksum_type = ctx->cksumtype;
505 	    err = krb5_c_verify_checksum(context, key, key_usage,
506 					 &plain, &sum, &valid);
507 	    if (err) {
508 		*minor_status = err;
509 		return GSS_S_BAD_SIG;
510 	    }
511 	    if (!valid) {
512 		*minor_status = 0;
513 		return GSS_S_BAD_SIG;
514 	    }
515 	    message_buffer->length = plain.length - 16;
516 	    message_buffer->value = MALLOC(message_buffer->length);
517 	    if (message_buffer->value == NULL)
518 		goto no_mem;
519 	    (void) memcpy(message_buffer->value,
520 		plain.data, message_buffer->length);
521 
522 		/*
523 		 * Solaris Kerberos: Restore the original token.
524 		 * This allows the token to be detected as a duplicate if it
525 		 * is passed in to gss_unwrap() again.
526 		 */
527 		if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
528 			goto no_mem;
529 		store_16_be(ec, ptr+4);
530 		store_16_be(rrc, ptr+6);
531 	}
532 	err = g_order_check(&ctx->seqstate, seqnum);
533 	*minor_status = 0;
534 	return err;
535     } else if (toktype == KG_TOK_MIC_MSG) {
536 	/* wrap token, no confidentiality */
537 	if (load_16_be(ptr) != 0x0404)
538 	    goto defective;
539     verify_mic_1:
540 	if (ptr[3] != 0xff)
541 	    goto defective;
542 	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
543 	    goto defective;
544 	seqnum = load_64_be(ptr+8);
545 	plain.length = message_buffer->length + 16;
546 	plain.data = MALLOC(plain.length);
547 	if (plain.data == NULL)
548 	    goto no_mem;
549 	if (message_buffer->length)
550 	    (void) memcpy(plain.data,
551 		message_buffer->value, message_buffer->length);
552 	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
553 	sum.length = bodysize - 16;
554 	sum.contents = ptr + 16;
555 	sum.checksum_type = ctx->cksumtype;
556 	err = krb5_c_verify_checksum(context, key, key_usage,
557 				     &plain, &sum, &valid);
558 	if (err) {
559 error:
560 	    FREE(plain.data, plain.length);
561 	    *minor_status = err;
562 	    return GSS_S_BAD_SIG; /* XXX */
563 	}
564 	FREE(plain.data, plain.length);
565 	if (!valid) {
566 	    *minor_status = 0;
567 	    return GSS_S_BAD_SIG;
568 	}
569 	err = g_order_check(&ctx->seqstate, seqnum);
570 	*minor_status = 0;
571 	return err;
572     } else if (toktype == KG_TOK_DEL_CTX) {
573 	if (load_16_be(ptr) != 0x0405)
574 	    goto defective;
575 	message_buffer = (gss_buffer_t)&empty_message;
576 	goto verify_mic_1;
577     } else {
578 	goto defective;
579     }
580 }
581