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
53static int
54rotate_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
79static 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
85krb5_error_code
86gss_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
337error:
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
347OM_uint32
348gss_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