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