1/*
2 * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "gsskrb5_locl.h"
35
36/*
37 * Return initiator subkey, or if that doesn't exists, the subkey.
38 */
39
40krb5_error_code
41_gsskrb5i_get_initiator_subkey(const gsskrb5_ctx ctx,
42			       krb5_context context,
43			       krb5_keyblock **key)
44{
45    krb5_error_code ret;
46    *key = NULL;
47
48    if (ctx->more_flags & LOCAL) {
49	ret = krb5_auth_con_getlocalsubkey(context,
50				     ctx->auth_context,
51				     key);
52    } else {
53	ret = krb5_auth_con_getremotesubkey(context,
54				      ctx->auth_context,
55				      key);
56    }
57    if (ret == 0 && *key == NULL)
58	ret = krb5_auth_con_getkey(context,
59				   ctx->auth_context,
60				   key);
61    if (ret == 0 && *key == NULL) {
62	krb5_set_error_message(context, 0, "No initiator subkey available");
63	return GSS_KRB5_S_KG_NO_SUBKEY;
64    }
65    return ret;
66}
67
68krb5_error_code
69_gsskrb5i_get_acceptor_subkey(const gsskrb5_ctx ctx,
70			      krb5_context context,
71			      krb5_keyblock **key)
72{
73    krb5_error_code ret;
74    *key = NULL;
75
76    if (ctx->more_flags & LOCAL) {
77	ret = krb5_auth_con_getremotesubkey(context,
78				      ctx->auth_context,
79				      key);
80    } else {
81	ret = krb5_auth_con_getlocalsubkey(context,
82				     ctx->auth_context,
83				     key);
84    }
85    if (ret == 0 && *key == NULL) {
86	krb5_set_error_message(context, 0, "No acceptor subkey available");
87	return GSS_KRB5_S_KG_NO_SUBKEY;
88    }
89    return ret;
90}
91
92OM_uint32
93_gsskrb5i_get_token_key(const gsskrb5_ctx ctx,
94			krb5_context context,
95			krb5_keyblock **key)
96{
97    _gsskrb5i_get_acceptor_subkey(ctx, context, key);
98    if(*key == NULL) {
99	/*
100	 * Only use the initiator subkey or ticket session key if an
101	 * acceptor subkey was not required.
102	 */
103	if ((ctx->gk5c.flags & GK5C_ACCEPTOR_SUBKEY) == 0)
104	    _gsskrb5i_get_initiator_subkey(ctx, context, key);
105    }
106    if (*key == NULL) {
107	krb5_set_error_message(context, 0, "No token key available");
108	return GSS_KRB5_S_KG_NO_SUBKEY;
109    }
110    return 0;
111}
112
113#if defined(HEIM_KRB5_DES) || defined(HEIM_KRB5_DES3)
114static OM_uint32
115sub_wrap_size (
116            OM_uint32 req_output_size,
117            OM_uint32 * max_input_size,
118	    int blocksize,
119	    int extrasize
120           )
121{
122    size_t len, total_len;
123
124    len = 8 + req_output_size + blocksize + extrasize;
125
126    _gsskrb5_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
127
128    total_len -= req_output_size; /* token length */
129    if (total_len < req_output_size) {
130        *max_input_size = (OM_uint32)(req_output_size - total_len);
131        (*max_input_size) &= (~(OM_uint32)(blocksize - 1));
132    } else {
133        *max_input_size = 0;
134    }
135    return GSS_S_COMPLETE;
136}
137#endif
138
139OM_uint32 GSSAPI_CALLCONV
140_gsskrb5_wrap_size_limit (
141            OM_uint32 * minor_status,
142            const gss_ctx_id_t context_handle,
143            int conf_req_flag,
144            gss_qop_t qop_req,
145            OM_uint32 req_output_size,
146            OM_uint32 * max_input_size
147           )
148{
149  krb5_context context;
150  krb5_keyblock *key;
151  OM_uint32 ret;
152  const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
153
154  GSSAPI_KRB5_INIT (&context);
155
156  if (ctx->more_flags & IS_CFX)
157      return _gssapi_wrap_size_cfx(minor_status, &ctx->gk5c, context,
158				   conf_req_flag, qop_req,
159				   req_output_size, max_input_size);
160
161  HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
162  ret = _gsskrb5i_get_token_key(ctx, context, &key);
163  HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
164  if (ret) {
165      *minor_status = ret;
166      return GSS_S_FAILURE;
167  }
168
169  switch (key->keytype) {
170#ifdef HEIM_KRB5_DES
171  case KRB5_ENCTYPE_DES_CBC_CRC :
172  case KRB5_ENCTYPE_DES_CBC_MD4 :
173  case KRB5_ENCTYPE_DES_CBC_MD5 :
174      ret = sub_wrap_size(req_output_size, max_input_size, 8, 22);
175      break;
176#endif
177
178#ifdef HEIM_KRB5_DES3
179  case KRB5_ENCTYPE_DES3_CBC_MD5 :
180  case KRB5_ENCTYPE_DES3_CBC_SHA1 :
181      ret = sub_wrap_size(req_output_size, max_input_size, 8, 34);
182      break;
183#endif
184
185#ifdef HEIM_KRB5_ARCFOUR
186  case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
187  case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56:
188      ret = _gssapi_wrap_size_arcfour(minor_status, ctx, context,
189				      conf_req_flag, qop_req,
190				      req_output_size, max_input_size, key);
191      break;
192#endif
193  default :
194      ret = GSS_S_FAILURE;
195      break;
196  }
197  krb5_free_keyblock (context, key);
198  *minor_status = 0;
199  return ret;
200}
201
202#ifdef HEIM_KRB5_DES
203
204static OM_uint32
205wrap_des
206           (OM_uint32 * minor_status,
207            const gsskrb5_ctx ctx,
208	    krb5_context context,
209            int conf_req_flag,
210            gss_qop_t qop_req,
211            const gss_buffer_t input_message_buffer,
212            int * conf_state,
213            gss_buffer_t output_message_buffer,
214	    krb5_keyblock *key
215           )
216{
217  u_char *p;
218  CCDigestRef md5;
219  u_char hash[16];
220  DES_key_schedule schedule;
221  EVP_CIPHER_CTX des_ctx;
222  DES_cblock deskey;
223  DES_cblock zero;
224  size_t i;
225  int32_t seq_number;
226  size_t len, total_len, padlength, datalen;
227
228  if (IS_DCE_STYLE(ctx)) {
229    padlength = 0;
230    datalen = input_message_buffer->length;
231    len = 22 + 8;
232    _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
233    total_len += datalen;
234    datalen += 8;
235  } else {
236    padlength = 8 - (input_message_buffer->length % 8);
237    datalen = input_message_buffer->length + padlength + 8;
238    len = datalen + 22;
239    _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
240  }
241
242  output_message_buffer->length = total_len;
243  output_message_buffer->value  = malloc (total_len);
244  if (output_message_buffer->value == NULL) {
245    output_message_buffer->length = 0;
246    *minor_status = ENOMEM;
247    return GSS_S_FAILURE;
248  }
249
250  p = _gsskrb5_make_header(output_message_buffer->value,
251			      len,
252			      "\x02\x01", /* TOK_ID */
253			      GSS_KRB5_MECHANISM);
254
255  /* SGN_ALG */
256  memcpy (p, "\x00\x00", 2);
257  p += 2;
258  /* SEAL_ALG */
259  if(conf_req_flag)
260      memcpy (p, "\x00\x00", 2);
261  else
262      memcpy (p, "\xff\xff", 2);
263  p += 2;
264  /* Filler */
265  memcpy (p, "\xff\xff", 2);
266  p += 2;
267
268  /* fill in later */
269  memset (p, 0, 16);
270  p += 16;
271
272  /* confounder + data + pad */
273  krb5_generate_random_block(p, 8);
274  memcpy (p + 8, input_message_buffer->value,
275	  input_message_buffer->length);
276  memset (p + 8 + input_message_buffer->length, (int)padlength, padlength);
277
278  /* checksum */
279  md5 = CCDigestCreate(kCCDigestMD5);
280  CCDigestUpdate(md5, p - 24, 8);
281  CCDigestUpdate(md5, p, datalen);
282  CCDigestFinal(md5, hash);
283  CCDigestDestroy(md5);
284
285  memset (&zero, 0, sizeof(zero));
286  memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
287#ifndef __APPLE_PRIVATE__
288  DES_set_key_unchecked (&deskey, &schedule);
289  DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash),
290		 &schedule, &zero);
291#else
292  CCDesCBCCksum(hash, hash, sizeof(hash), deskey, sizeof(deskey), &zero);
293#endif
294  memcpy (p - 8, hash, 8);
295
296  /* sequence number */
297  HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
298  krb5_auth_con_getlocalseqnumber (context,
299				   ctx->auth_context,
300				   &seq_number);
301
302  p -= 16;
303  p[0] = (seq_number >> 0)  & 0xFF;
304  p[1] = (seq_number >> 8)  & 0xFF;
305  p[2] = (seq_number >> 16) & 0xFF;
306  p[3] = (seq_number >> 24) & 0xFF;
307  memset (p + 4,
308	  (ctx->more_flags & LOCAL) ? 0 : 0xFF,
309	  4);
310
311  EVP_CIPHER_CTX_init(&des_ctx);
312  EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, p + 8, 1);
313  EVP_Cipher(&des_ctx, p, p, 8);
314  EVP_CIPHER_CTX_cleanup(&des_ctx);
315
316  krb5_auth_con_setlocalseqnumber (context,
317			       ctx->auth_context,
318			       ++seq_number);
319  HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
320
321  /* encrypt the data */
322  p += 16;
323
324  if(conf_req_flag) {
325      memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
326
327      for (i = 0; i < sizeof(deskey); ++i)
328	  deskey[i] ^= 0xf0;
329
330      EVP_CIPHER_CTX_init(&des_ctx);
331      EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, deskey, zero, 1);
332      EVP_Cipher(&des_ctx, p, p, datalen);
333      EVP_CIPHER_CTX_cleanup(&des_ctx);
334  }
335  memset (deskey, 0, sizeof(deskey));
336  memset (&schedule, 0, sizeof(schedule));
337
338  if(conf_state != NULL)
339      *conf_state = conf_req_flag;
340  *minor_status = 0;
341  return GSS_S_COMPLETE;
342}
343
344#endif
345
346#ifdef HEIM_KRB5_DES3
347
348static OM_uint32
349wrap_des3
350           (OM_uint32 * minor_status,
351            const gsskrb5_ctx ctx,
352	    krb5_context context,
353            int conf_req_flag,
354            gss_qop_t qop_req,
355            const gss_buffer_t input_message_buffer,
356            int * conf_state,
357            gss_buffer_t output_message_buffer,
358	    krb5_keyblock *key
359           )
360{
361  u_char *p;
362  u_char seq[8];
363  int32_t seq_number;
364  size_t len, total_len, padlength, datalen;
365  uint32_t ret;
366  krb5_crypto crypto;
367  Checksum cksum;
368  krb5_data encdata;
369
370  if (IS_DCE_STYLE(ctx)) {
371    padlength = 0;
372    datalen = input_message_buffer->length;
373    len = 34 + 8;
374    _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
375    total_len += datalen;
376    datalen += 8;
377  } else {
378    padlength = 8 - (input_message_buffer->length % 8);
379    datalen = input_message_buffer->length + padlength + 8;
380    len = datalen + 34;
381    _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
382  }
383
384  output_message_buffer->length = total_len;
385  output_message_buffer->value  = malloc (total_len);
386  if (output_message_buffer->value == NULL) {
387    output_message_buffer->length = 0;
388    *minor_status = ENOMEM;
389    return GSS_S_FAILURE;
390  }
391
392  p = _gsskrb5_make_header(output_message_buffer->value,
393			      len,
394			      "\x02\x01", /* TOK_ID */
395			      GSS_KRB5_MECHANISM);
396
397  /* SGN_ALG */
398  memcpy (p, "\x04\x00", 2);	/* HMAC SHA1 DES3-KD */
399  p += 2;
400  /* SEAL_ALG */
401  if(conf_req_flag)
402      memcpy (p, "\x02\x00", 2); /* DES3-KD */
403  else
404      memcpy (p, "\xff\xff", 2);
405  p += 2;
406  /* Filler */
407  memcpy (p, "\xff\xff", 2);
408  p += 2;
409
410  /* calculate checksum (the above + confounder + data + pad) */
411
412  memcpy (p + 20, p - 8, 8);
413  krb5_generate_random_block(p + 28, 8);
414  memcpy (p + 28 + 8, input_message_buffer->value,
415	  input_message_buffer->length);
416  memset (p + 28 + 8 + input_message_buffer->length, (int)padlength, padlength);
417
418  ret = krb5_crypto_init(context, key, 0, &crypto);
419  if (ret) {
420      free (output_message_buffer->value);
421      output_message_buffer->length = 0;
422      output_message_buffer->value = NULL;
423      *minor_status = ret;
424      return GSS_S_FAILURE;
425  }
426
427  ret = krb5_create_checksum (context,
428			      crypto,
429			      KRB5_KU_USAGE_SIGN,
430			      0,
431			      p + 20,
432			      datalen + 8,
433			      &cksum);
434  krb5_crypto_destroy (context, crypto);
435  if (ret) {
436      free (output_message_buffer->value);
437      output_message_buffer->length = 0;
438      output_message_buffer->value = NULL;
439      *minor_status = ret;
440      return GSS_S_FAILURE;
441  }
442
443  /* zero out SND_SEQ + SGN_CKSUM in case */
444  memset (p, 0, 28);
445
446  memcpy (p + 8, cksum.checksum.data, cksum.checksum.length);
447  free_Checksum (&cksum);
448
449  HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
450  /* sequence number */
451  krb5_auth_con_getlocalseqnumber (context,
452			       ctx->auth_context,
453			       &seq_number);
454
455  seq[0] = (seq_number >> 0)  & 0xFF;
456  seq[1] = (seq_number >> 8)  & 0xFF;
457  seq[2] = (seq_number >> 16) & 0xFF;
458  seq[3] = (seq_number >> 24) & 0xFF;
459  memset (seq + 4,
460	  (ctx->more_flags & LOCAL) ? 0 : 0xFF,
461	  4);
462
463
464  ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE,
465			 &crypto);
466  if (ret) {
467      free (output_message_buffer->value);
468      output_message_buffer->length = 0;
469      output_message_buffer->value = NULL;
470      *minor_status = ret;
471      return GSS_S_FAILURE;
472  }
473
474  {
475      unsigned char ivec[8];
476
477      memcpy (&ivec, p + 8, 8);
478      ret = krb5_encrypt_ivec (context,
479			       crypto,
480			       KRB5_KU_USAGE_SEQ,
481			       seq, 8, &encdata,
482			       ivec);
483  }
484  krb5_crypto_destroy (context, crypto);
485  if (ret) {
486      free (output_message_buffer->value);
487      output_message_buffer->length = 0;
488      output_message_buffer->value = NULL;
489      *minor_status = ret;
490      return GSS_S_FAILURE;
491  }
492
493  assert (encdata.length == 8);
494
495  memcpy (p, encdata.data, encdata.length);
496  krb5_data_free (&encdata);
497
498  krb5_auth_con_setlocalseqnumber (context,
499			       ctx->auth_context,
500			       ++seq_number);
501  HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
502
503  /* encrypt the data */
504  p += 28;
505
506  if(conf_req_flag) {
507      krb5_data tmp;
508
509      ret = krb5_crypto_init(context, key,
510			     ETYPE_DES3_CBC_NONE, &crypto);
511      if (ret) {
512	  free (output_message_buffer->value);
513	  output_message_buffer->length = 0;
514	  output_message_buffer->value = NULL;
515	  *minor_status = ret;
516	  return GSS_S_FAILURE;
517      }
518      ret = krb5_encrypt(context, crypto, KRB5_KU_USAGE_SEAL,
519			 p, datalen, &tmp);
520      krb5_crypto_destroy(context, crypto);
521      if (ret) {
522	  free (output_message_buffer->value);
523	  output_message_buffer->length = 0;
524	  output_message_buffer->value = NULL;
525	  *minor_status = ret;
526	  return GSS_S_FAILURE;
527      }
528      assert (tmp.length == datalen);
529
530      memcpy (p, tmp.data, datalen);
531      krb5_data_free(&tmp);
532  }
533  if(conf_state != NULL)
534      *conf_state = conf_req_flag;
535  *minor_status = 0;
536  return GSS_S_COMPLETE;
537}
538#endif
539
540OM_uint32 GSSAPI_CALLCONV
541_gsskrb5_wrap
542           (OM_uint32 * minor_status,
543            const gss_ctx_id_t context_handle,
544            int conf_req_flag,
545            gss_qop_t qop_req,
546            const gss_buffer_t input_message_buffer,
547            int * conf_state,
548            gss_buffer_t output_message_buffer
549           )
550{
551  krb5_context context;
552  krb5_keyblock *key;
553  OM_uint32 ret;
554  const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
555
556  output_message_buffer->value = NULL;
557  output_message_buffer->length = 0;
558
559  GSSAPI_KRB5_INIT (&context);
560
561  if (ctx->more_flags & IS_CFX)
562      return _gssapi_wrap_cfx (minor_status, &ctx->gk5c,
563			       context, conf_req_flag,
564			       input_message_buffer, conf_state,
565			       output_message_buffer);
566
567  HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
568  ret = _gsskrb5i_get_token_key(ctx, context, &key);
569  HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
570  if (ret) {
571      *minor_status = ret;
572      return GSS_S_FAILURE;
573  }
574
575  switch (key->keytype) {
576#ifdef HEIM_KRB5_DES
577  case KRB5_ENCTYPE_DES_CBC_CRC :
578  case KRB5_ENCTYPE_DES_CBC_MD4 :
579  case KRB5_ENCTYPE_DES_CBC_MD5 :
580      ret = wrap_des (minor_status, ctx, context, conf_req_flag,
581		      qop_req, input_message_buffer, conf_state,
582		      output_message_buffer, key);
583      break;
584#endif
585
586#ifdef HEIM_KRB5_DES3
587  case KRB5_ENCTYPE_DES3_CBC_MD5 :
588  case KRB5_ENCTYPE_DES3_CBC_SHA1 :
589      ret = wrap_des3 (minor_status, ctx, context, conf_req_flag,
590		       qop_req, input_message_buffer, conf_state,
591		       output_message_buffer, key);
592      break;
593#endif
594
595#ifdef HEIM_KRB5_ARCFOUR
596  case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
597  case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56:
598      ret = _gssapi_wrap_arcfour (minor_status, ctx, context, conf_req_flag,
599				  qop_req, input_message_buffer, conf_state,
600				  output_message_buffer, key);
601      break;
602#endif
603  default :
604      ret = GSS_S_FAILURE;
605      break;
606  }
607  krb5_free_keyblock (context, key);
608  return ret;
609}
610