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