xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/mech/k5seal.c (revision f48205be61a214698b763ff550ab9e657525104c)
1 /*
2  * Copyright 2006 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  * Copyright 1993 by OpenVision Technologies, Inc.
10  *
11  * Permission to use, copy, modify, distribute, and sell this software
12  * and its documentation for any purpose is hereby granted without fee,
13  * provided that the above copyright notice appears in all copies and
14  * that both that copyright notice and this permission notice appear in
15  * supporting documentation, and that the name of OpenVision not be used
16  * in advertising or publicity pertaining to distribution of the software
17  * without specific, written prior permission. OpenVision makes no
18  * representations about the suitability of this software for any
19  * purpose.  It is provided "as is" without express or implied warranty.
20  *
21  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
22  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
23  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
24  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
25  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
26  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
27  * PERFORMANCE OF THIS SOFTWARE.
28  */
29 
30 /*
31  * Copyright (C) 1998 by the FundsXpress, INC.
32  *
33  * All rights reserved.
34  *
35  * Export of this software from the United States of America may require
36  * a specific license from the United States Government.  It is the
37  * responsibility of any person or organization contemplating export to
38  * obtain such a license before exporting.
39  *
40  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
41  * distribute this software and its documentation for any purpose and
42  * without fee is hereby granted, provided that the above copyright
43  * notice appear in all copies and that both that copyright notice and
44  * this permission notice appear in supporting documentation, and that
45  * the name of FundsXpress. not be used in advertising or publicity pertaining
46  * to distribution of the software without specific, written prior
47  * permission.  FundsXpress makes no representations about the suitability of
48  * this software for any purpose.  It is provided "as is" without express
49  * or implied warranty.
50  *
51  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
52  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
53  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
54  */
55 
56 #include <gssapiP_krb5.h>
57 #include <k5-int.h>
58 
59 static krb5_error_code
60 make_seal_token_v1 PROTOTYPE((krb5_context context,
61 			      krb5_keyblock *enc,
62 			      krb5_keyblock *seq,
63 			      gssint_uint64 *seqnum,
64 			      int direction,
65 			      gss_buffer_t text,
66 			      gss_buffer_t token,
67 			      int signalg,
68 			      int cksum_size,
69 			      int sealalg,
70 			      int encrypt,
71 			      int toktype,
72 			      int bigend,
73 			      gss_OID oid));
74 
75 static krb5_error_code
76 make_seal_token_v1(context, enc, seq, seqnum, direction, text, token,
77 		   signalg, cksum_size, sealalg, encrypt, toktype,
78 		   bigend, oid)
79     krb5_context context;
80     krb5_keyblock *enc;
81     krb5_keyblock *seq;
82     gssint_uint64 *seqnum;
83     int direction;
84     gss_buffer_t text;
85     gss_buffer_t token;
86     int signalg;
87     int cksum_size;
88     int sealalg;
89     int encrypt;
90     int toktype;
91     int bigend;
92     gss_OID oid;
93 {
94     krb5_error_code code;
95     size_t sumlen;
96     char *data_ptr;
97     krb5_data plaind;
98     krb5_checksum md5cksum;
99     krb5_checksum cksum;
100 	/*
101 	 * msglen contains the message length
102 	 * we are signing/encrypting.  tmsglen
103 	 * contains the length of the message
104 	 * we plan to write out to the token.
105 	 * tlen is the length of the token
106 	 * including header.
107 	 */
108     int conflen=0, tmsglen, tlen, msglen;
109     unsigned char *t, *ptr;
110     unsigned char *plain;
111     unsigned char pad;
112     krb5_keyusage sign_usage = KG_USAGE_SIGN;
113     OM_uint32 seqnum32;
114 
115     /* Solaris Kerberos:  check for recognized signalg and sealalg */
116     KRB5_LOG0(KRB5_INFO, "make_seal_token_v1() start\n");
117 #ifdef _KERNEL
118 	/*
119          * Because the ARCFOUR code bypasses the standard
120 	 * crypto interfaces, we must make sure the kernel
121 	 * crypto framework mechanism types are properly
122 	 * initialized here.
123 	 */
124 	context->kef_cipher_mt = get_cipher_mech_type(context, seq);
125 	context->kef_hash_mt = get_hash_mech_type(context, seq);
126 	if ((code = init_key_kef(context->kef_cipher_mt, seq))) {
127 		return (code);
128 	}
129         if ((code = init_key_kef(context->kef_cipher_mt, enc))) {
130 		return (code);
131 	}
132 #endif /* _KERNEL */
133 
134     /* create the token buffer */
135     /* Do we need confounder? */
136     if (encrypt || (!bigend && (toktype == KG_TOK_SEAL_MSG)))
137 	conflen = kg_confounder_size(context, enc);
138     else
139 	conflen = 0;
140 
141     if (toktype == KG_TOK_SEAL_MSG) {
142 	switch (sealalg) {
143 		case SEAL_ALG_MICROSOFT_RC4:
144 			msglen = conflen + text->length+1;
145 			pad = 1;
146 			break;
147 		default:
148 			/* XXX knows that des block size is 8 */
149 			msglen = (conflen+text->length+8)&(~7);
150 			pad = 8-(text->length%8);
151 	}
152 	tmsglen = msglen;
153     } else {
154 	tmsglen = 0;
155 	msglen = text->length;
156 	pad = 0;
157     }
158 
159     tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen);
160 
161     if ((t = (unsigned char *) xmalloc(tlen)) == NULL)
162 	return(ENOMEM);
163 
164     /*** fill in the token */
165 
166     ptr = t;
167 
168     g_make_token_header((gss_OID) oid, 14+cksum_size+tmsglen, &ptr, toktype);
169 
170     /* 0..1 SIGN_ALG */
171 
172     ptr[0] = (unsigned char) (signalg & 0xff);
173     ptr[1] = (unsigned char) ((signalg >> 8) & 0xff);
174 
175     /* 2..3 SEAL_ALG or Filler */
176 
177     if ((toktype == KG_TOK_SEAL_MSG) && encrypt) {
178 	ptr[2] = (unsigned char) (sealalg & 0xff);
179 	ptr[3] = (unsigned char) ((sealalg >> 8) & 0xff);
180     } else {
181 	/* No seal */
182 	ptr[2] = 0xff;
183 	ptr[3] = 0xff;
184     }
185 
186     /* 4..5 Filler */
187 
188     ptr[4] = 0xff;
189     ptr[5] = 0xff;
190 
191     /* pad the plaintext, encrypt if needed, and stick it in the token */
192 
193     /* initialize the the cksum */
194     switch (signalg) {
195     case SGN_ALG_DES_MAC_MD5:
196     case SGN_ALG_MD2_5:
197 	md5cksum.checksum_type = CKSUMTYPE_RSA_MD5;
198 	break;
199     case SGN_ALG_HMAC_SHA1_DES3_KD:
200 	md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3;
201 	break;
202     case SGN_ALG_HMAC_MD5:
203 	md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR;
204 	if (toktype != KG_TOK_SEAL_MSG)
205 		sign_usage = 15;
206 	break;
207     default:
208 	KRB5_LOG(KRB5_ERR, "make_seal_token_v1() end, error2 signalg=%d\n",
209 		signalg);
210 #ifndef	_KERNEL
211 	abort ();
212 #else
213 	return (GSS_S_DEFECTIVE_TOKEN);
214 #endif /* _KERNEL */
215     }
216 
217     code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen);
218     if (code) {
219 	KRB5_LOG(KRB5_ERR, "make_seal_token_v1() end, krb5_c_checksum_length() "
220 		"error code=%d\n", code);
221 	return(code);
222     }
223     md5cksum.length = (size_t)sumlen;
224 
225     if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) {
226 	xfree_wrap(t, tlen);
227 	return(ENOMEM);
228     }
229 
230     if (conflen) {
231 	if ((code = kg_make_confounder(context, enc, plain))) {
232 		xfree_wrap(plain, msglen ? msglen : 1);
233 		xfree_wrap(t, tlen);
234 		KRB5_LOG(KRB5_ERR, "make_seal_token_v1() end, "
235 			"kg_make_confounder() error code=%d\n", code);
236 		return(code);
237 	}
238     }
239 
240     (void) memcpy(plain+conflen, text->value, text->length);
241     if (pad)
242 	(void) memset(plain+conflen+text->length, pad, pad);
243 
244     /* compute the checksum */
245 
246     /* 8 = head of token body as specified by mech spec */
247     if (! (data_ptr = (char *) xmalloc(8 +
248 		(bigend ? text->length : msglen)))) {
249 	xfree_wrap(plain, msglen ? msglen : 1);
250 	xfree_wrap(t, tlen);
251 	return(ENOMEM);
252     }
253     (void) memcpy(data_ptr, ptr-2, 8);
254     if (bigend)
255 	(void) memcpy(data_ptr+8, text->value, text->length);
256     else
257 	(void) memcpy(data_ptr+8, plain, msglen);
258 
259     plaind.length = 8 + (bigend ? text->length : msglen);
260     plaind.data = data_ptr;
261 
262     code = krb5_c_make_checksum(context, md5cksum.checksum_type, seq,
263 			    sign_usage, &plaind, &md5cksum);
264 
265     xfree_wrap(data_ptr,8 + (bigend ? text->length : msglen));
266 
267     if (code) {
268 	KRB5_LOG(KRB5_ERR, "make_seal_token_v1() end, "
269 		"krb5_c_make_checksum() error code=%d\n", code);
270 	xfree_wrap(plain, msglen ? msglen : 1);
271 	xfree_wrap(t, tlen);
272 	return(code);
273     }
274 
275     switch(signalg) {
276     case SGN_ALG_DES_MAC_MD5:
277     case 3:
278 
279        if ((code = kg_encrypt(context, seq, KG_USAGE_SEAL,
280 			       (g_OID_equal(oid, gss_mech_krb5_old) ?
281 				seq->contents : NULL),
282 			       md5cksum.contents, md5cksum.contents, 16))) {
283 	    xfree_wrap(md5cksum.contents, md5cksum.length);
284 	    xfree_wrap(t, tlen);
285 
286 	    KRB5_LOG(KRB5_ERR, "make_seal_token_v1() end, kg_encrypt() "
287 		    "error code=%d\n", code);
288 	    return code;
289 	}
290 
291 	cksum.length = cksum_size;
292 	cksum.contents = md5cksum.contents + 16 - cksum.length;
293 
294 	(void) memcpy(ptr+14, cksum.contents, cksum.length);
295 	break;
296 
297     case SGN_ALG_HMAC_SHA1_DES3_KD:
298 	/*
299 	 * Using key derivation, the call to krb5_c_make_checksum
300 	 * already dealt with encrypting.
301 	 */
302 	if (md5cksum.length != cksum_size)
303 	{
304 		KRB5_LOG1(KRB5_ERR, "make_seal_token_v1() end, error "
305 				   "md5cksum.length %d != "
306 				   "cksum_size %d\n",
307 				   md5cksum.length, cksum_size);
308 #ifndef	_KERNEL
309 		abort ();
310 #else
311 		return (GSS_S_DEFECTIVE_TOKEN);
312 #endif
313 	}
314 	(void) memcpy(ptr+14, md5cksum.contents, md5cksum.length);
315 	break;
316     case SGN_ALG_HMAC_MD5:
317 	KRB5_LOG(KRB5_INFO, "make_seal_token_v1() cksum_size = %d",
318 		cksum_size);
319 	(void) memcpy(ptr+14, md5cksum.contents, cksum_size);
320 	break;
321     }
322 
323     xfree_wrap(md5cksum.contents, md5cksum.length);
324 
325     /* create the seq_num */
326     seqnum32 = (OM_uint32)(*seqnum & 0xFFFFFFFF);
327     if ((code = kg_make_seq_num(context, seq, direction?0:0xff, seqnum32,
328 				ptr+14, ptr+6))) {
329 	xfree_wrap(t, tlen);
330 
331 	KRB5_LOG(KRB5_ERR, "make_seal_token_v1() end, kg_make_seq_num() "
332 		    "error code=%d\n", code);
333 	return(code);
334     }
335     if (encrypt) {
336 	switch(sealalg) {
337 	case SEAL_ALG_MICROSOFT_RC4:
338 	{
339 		unsigned char bigend_seqnum[4];
340 		krb5_keyblock *enc_key;
341 		int i;
342 		bigend_seqnum[0] = (*seqnum>>24) & 0xff;
343 		bigend_seqnum[1] = (*seqnum>>16) & 0xff;
344 		bigend_seqnum[2] = (*seqnum>>8) & 0xff;
345 		bigend_seqnum[3] = *seqnum & 0xff;
346 		code = krb5_copy_keyblock (context, enc, &enc_key);
347 		if (code)
348 		{
349 			xfree_wrap(plain, msglen ? msglen : 1);
350 			xfree_wrap(t, tlen);
351 			return(code);
352 		}
353 		for (i = 0; i <= 15; i++)
354 			((char *) enc_key->contents)[i] ^=0xf0;
355 		code = kg_arcfour_docrypt (context,
356 			enc_key, 0,
357 			bigend_seqnum, 4,
358 			plain, tmsglen,
359 			ptr+14+cksum_size);
360 		krb5_free_keyblock (context, enc_key);
361 		if (code)
362 		{
363 			xfree_wrap(plain, msglen ? msglen : 1);
364 			xfree_wrap(t, tlen);
365 			return(code);
366 		}
367 	}
368         break;
369 	default:
370 	    if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL,
371                                    (krb5_pointer) plain,
372                                    (krb5_pointer) (ptr+cksum_size+14),
373                                    tmsglen))) {
374 		xfree_wrap(plain, msglen ? msglen : 1);
375 		xfree_wrap(t, tlen);
376 		return(code);
377             }
378       }
379     }else {
380       if (tmsglen)
381 	(void) memcpy(ptr+14+cksum_size, plain, tmsglen);
382     }
383     xfree_wrap(plain, msglen ? msglen : 1);
384 
385     /* that's it.  return the token */
386 
387     (*seqnum)++;
388     *seqnum &= (ulong_t)0xffffffffU;
389 
390     token->length = tlen;
391     token->value = (void *) t;
392 
393     KRB5_LOG0(KRB5_INFO, "make_seal_token_v1() end\n");
394     return(0);
395 }
396 
397 /* if signonly is true, ignore conf_req, conf_state,
398    and do not encode the ENC_TYPE, MSG_LENGTH, or MSG_TEXT fields */
399 
400 OM_uint32
401 kg_seal(context, minor_status, context_handle, conf_req_flag, qop_req,
402 	input_message_buffer, conf_state, output_message_buffer, toktype)
403     krb5_context context;
404     OM_uint32 *minor_status;
405     gss_ctx_id_t context_handle;
406     int conf_req_flag;
407     int qop_req;
408     gss_buffer_t input_message_buffer;
409     int *conf_state;
410     gss_buffer_t output_message_buffer;
411     int toktype;
412 {
413     krb5_gss_ctx_id_rec *ctx;
414     krb5_error_code code;
415     krb5_timestamp now;
416 
417     KRB5_LOG0(KRB5_INFO, "kg_seal() start");
418 
419     output_message_buffer->length = 0;
420     output_message_buffer->value = NULL;
421 
422     /* Only default qop or matching established cryptosystem is allowed.
423 
424 	There are NO EXTENSIONS to this set for AES and friends!	 The
425 	new spec says "just use 0".  The old spec plus extensions would
426 	actually allow for certain non-zero values.  Fix this to handle
427 	them later.  */
428     if (qop_req != 0) {
429 	*minor_status = (OM_uint32) G_UNKNOWN_QOP;
430 	KRB5_LOG0(KRB5_ERR, "kg_seal() end, error G_UNKNOWN_QOP\n");
431 	return (GSS_S_BAD_QOP);
432     }
433 
434     /* validate the context handle */
435     if (! kg_validate_ctx_id(context_handle)) {
436 	*minor_status = (OM_uint32) G_VALIDATE_FAILED;
437 	KRB5_LOG0(KRB5_ERR, "kg_seal() kg_validate_ctx_id() end, "
438 		"error GSS_S_NO_CONTEXT\n");
439 	return (GSS_S_NO_CONTEXT);
440     }
441 
442     ctx = (krb5_gss_ctx_id_rec *) context_handle;
443 
444     if (ctx->subkey == NULL && !ctx->established) {
445 	*minor_status = KG_CTX_INCOMPLETE;
446 	return(GSS_S_NO_CONTEXT);
447     }
448 
449     if ((code = krb5_timeofday(context, &now))) {
450 	*minor_status = code;
451 	KRB5_LOG(KRB5_ERR, "kg_seal() end, krb5_timeofday() error code=%d\n", code);
452 	return (GSS_S_FAILURE);
453     }
454 
455     switch (ctx->proto)
456     {
457     case 0:
458 	code = make_seal_token_v1(context, ctx->enc, ctx->seq,
459                                   &ctx->seq_send, ctx->initiate,
460                                   input_message_buffer, output_message_buffer,
461                                   ctx->signalg, ctx->cksum_size, ctx->sealalg,
462                                   conf_req_flag, toktype, ctx->big_endian,
463                                   &ctx->mech_used);
464 	break;
465     case 1:
466 	code = gss_krb5int_make_seal_token_v3(context, ctx,
467                                               input_message_buffer,
468                                               output_message_buffer,
469                                               conf_req_flag, toktype);
470 	break;
471     default:
472 	code = G_UNKNOWN_QOP;
473 	break;
474     }
475 
476     if (code) {
477 	*minor_status = code;
478 	KRB5_LOG(KRB5_ERR, "kg_seal() end, make_seal_token_v1() "
479 		"error code=%d\n", code);
480 	return (GSS_S_FAILURE);
481     }
482 
483     if (conf_state)
484 	*conf_state = conf_req_flag;
485 
486    *minor_status = 0;
487    if (ctx->endtime < now) {
488 	(void) gss_release_buffer(minor_status, output_message_buffer);
489 	KRB5_LOG(KRB5_ERR, "kg_seal() end, error GSS_S_CONTEXT_EXPIRED "
490 		"ctx->endtime = %d\n", ctx->endtime);
491 	return (GSS_S_CONTEXT_EXPIRED);
492    }
493 
494    KRB5_LOG0(KRB5_INFO, "kg_seal() end\n");
495    return (GSS_S_COMPLETE);
496 }
497