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