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