comment out dumping code
[mech_eap.orig] / util_krb.c
1 /*
2  * Copyright (c) 2011, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 /*
34  * Kerberos 5 helpers.
35  */
36
37 #include "gssapiP_eap.h"
38
39 static GSSEAP_THREAD_ONCE krbContextKeyOnce = GSSEAP_ONCE_INITIALIZER;
40 static GSSEAP_THREAD_KEY krbContextKey;
41
42 static void
43 destroyKrbContext(void *arg)
44 {
45     krb5_context context = (krb5_context)arg;
46
47     if (context != NULL)
48         krb5_free_context(context);
49 }
50
51 static void
52 createKrbContextKey(void)
53 {
54     GSSEAP_KEY_CREATE(&krbContextKey, destroyKrbContext);
55 }
56
57 static krb5_error_code
58 initKrbContext(krb5_context *pKrbContext)
59 {
60     krb5_context krbContext;
61     krb5_error_code code;
62     char *defaultRealm = NULL;
63
64     *pKrbContext = NULL;
65
66     code = krb5_init_context(&krbContext);
67     if (code != 0)
68         goto cleanup;
69
70     krb5_appdefault_string(krbContext, "eap_gss",
71                            NULL, "default_realm", "", &defaultRealm);
72
73     if (defaultRealm != NULL && defaultRealm[0] != '\0') {
74         code = krb5_set_default_realm(krbContext, defaultRealm);
75         if (code != 0)
76             goto cleanup;
77     }
78
79     *pKrbContext = krbContext;
80
81 cleanup:
82     if (code != 0 && krbContext != NULL)
83         krb5_free_context(krbContext);
84
85     if (defaultRealm != NULL)
86         GSSEAP_FREE(defaultRealm);
87
88     return code;
89 }
90
91 OM_uint32
92 gssEapKerberosInit(OM_uint32 *minor, krb5_context *context)
93 {
94     *minor = 0;
95
96     GSSEAP_ONCE(&krbContextKeyOnce, createKrbContextKey);
97
98     *context = GSSEAP_GETSPECIFIC(krbContextKey);
99     if (*context == NULL) {
100         *minor = initKrbContext(context);
101         if (*minor == 0) {
102             if (GSSEAP_SETSPECIFIC(krbContextKey, *context) != 0) {
103                 *minor = errno;
104                 krb5_free_context(*context);
105                 *context = NULL;
106             }
107         }
108     }
109
110     return *minor == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE;
111 }
112
113 /*
114  * Derive a key K for RFC 4121 use by using the following
115  * derivation function (based on RFC 4402);
116  *
117  * KMSK = random-to-key(MSK)
118  * Tn = pseudo-random(KMSK, n || "rfc4121-gss-eap")
119  * L = output key size
120  * K = truncate(L, T1 || T2 || .. || Tn)
121  */
122 OM_uint32
123 gssEapDeriveRfc3961Key(OM_uint32 *minor,
124                        const unsigned char *inputKey,
125                        size_t inputKeyLength,
126                        krb5_enctype encryptionType,
127                        krb5_keyblock *pKey)
128 {
129     krb5_context krbContext;
130 #ifndef HAVE_HEIMDAL_VERSION
131     krb5_data data;
132 #endif
133     krb5_data ns, t, prfOut;
134     krb5_keyblock kd;
135     krb5_error_code code;
136     size_t randomLength, keyLength, prfLength;
137     unsigned char constant[4 + sizeof("rfc4121-gss-eap") - 1], *p;
138     ssize_t i, remain;
139
140     assert(encryptionType != ENCTYPE_NULL);
141
142     memset(pKey, 0, sizeof(*pKey));
143
144     GSSEAP_KRB_INIT(&krbContext);
145
146     KRB_KEY_INIT(&kd);
147     KRB_KEY_TYPE(&kd) = encryptionType;
148
149     t.data = NULL;
150     t.length = 0;
151
152     prfOut.data = NULL;
153     prfOut.length = 0;
154
155     code = krb5_c_keylengths(krbContext, encryptionType,
156                              &randomLength, &keyLength);
157     if (code != 0)
158         goto cleanup;
159
160     KRB_KEY_DATA(&kd) = GSSEAP_MALLOC(keyLength);
161     if (KRB_KEY_DATA(&kd) == NULL) {
162         code = ENOMEM;
163         goto cleanup;
164     }
165     KRB_KEY_LENGTH(&kd) = keyLength;
166
167     /* Convert MSK into a Kerberos key */
168 #ifdef HAVE_HEIMDAL_VERSION
169     code = krb5_random_to_key(krbContext, encryptionType, inputKey,
170                               MIN(inputKeyLength, randomLength), &kd);
171 #else
172     data.length = MIN(inputKeyLength, randomLength);
173     data.data = (char *)inputKey;
174
175     code = krb5_c_random_to_key(krbContext, encryptionType, &data, &kd);
176 #endif
177     if (code != 0)
178         goto cleanup;
179
180     memset(&constant[0], 0, 4);
181     memcpy(&constant[4], "rfc4121-gss-eap", sizeof("rfc4121-gss-eap") - 1);
182
183     ns.length = sizeof(constant);
184     ns.data = (char *)constant;
185
186     /* Plug derivation constant and key into PRF */
187     code = krb5_c_prf_length(krbContext, encryptionType, &prfLength);
188     if (code != 0)
189         goto cleanup;
190
191     t.length = prfLength;
192     t.data = GSSEAP_MALLOC(t.length);
193     if (t.data == NULL) {
194         code = ENOMEM;
195         goto cleanup;
196     }
197
198     prfOut.length = randomLength;
199     prfOut.data = GSSEAP_MALLOC(prfOut.length);
200     if (prfOut.data == NULL) {
201         code = ENOMEM;
202         goto cleanup;
203     }
204
205     for (i = 0, p = (unsigned char *)prfOut.data, remain = randomLength;
206          remain > 0;
207          p += t.length, remain -= t.length, i++)
208     {
209         store_uint32_be(i, ns.data);
210
211         code = krb5_c_prf(krbContext, &kd, &ns, &t);
212         if (code != 0)
213             goto cleanup;
214
215         memcpy(p, t.data, MIN(t.length, remain));
216      }
217
218     /* Finally, convert PRF output into a new key which we will return */
219 #ifdef HAVE_HEIMDAL_VERSION
220     code = krb5_random_to_key(krbContext, encryptionType,
221                               prfOut.data, prfOut.length, &kd);
222 #else
223     code = krb5_c_random_to_key(krbContext, encryptionType, &prfOut, &kd);
224 #endif
225     if (code != 0)
226         goto cleanup;
227
228     *pKey = kd;
229     KRB_KEY_DATA(&kd) = NULL;
230
231 cleanup:
232     if (KRB_KEY_DATA(&kd) != NULL) {
233         memset(KRB_KEY_DATA(&kd), 0, KRB_KEY_LENGTH(&kd));
234         GSSEAP_FREE(KRB_KEY_DATA(&kd));
235     }
236     if (t.data != NULL) {
237         memset(t.data, 0, t.length);
238         GSSEAP_FREE(t.data);
239     }
240     if (prfOut.data != NULL) {
241         memset(prfOut.data, 0, prfOut.length);
242         GSSEAP_FREE(prfOut.data);
243     }
244     *minor = code;
245     return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
246 }
247
248 #ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE
249 extern krb5_error_code
250 krb5int_c_mandatory_cksumtype(krb5_context, krb5_enctype, krb5_cksumtype *);
251 #endif
252
253 OM_uint32
254 rfc3961ChecksumTypeForKey(OM_uint32 *minor,
255                           krb5_keyblock *key,
256                           krb5_cksumtype *cksumtype)
257 {
258     krb5_context krbContext;
259 #ifndef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE
260     krb5_data data;
261     krb5_checksum cksum;
262 #endif
263
264     GSSEAP_KRB_INIT(&krbContext);
265
266 #ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE
267     *minor = krb5int_c_mandatory_cksumtype(krbContext, KRB_KEY_TYPE(key),
268                                            cksumtype);
269     if (*minor != 0)
270         return GSS_S_FAILURE;
271 #else
272     data.length = 0;
273     data.data = NULL;
274
275     memset(&cksum, 0, sizeof(cksum));
276
277     /*
278      * This is a complete hack but it's the only way to work with
279      * MIT Kerberos pre-1.9 without using private API, as it does
280      * not support passing in zero as the checksum type.
281      */
282     *minor = krb5_c_make_checksum(krbContext, 0, key, 0, &data, &cksum);
283     if (*minor != 0)
284         return GSS_S_FAILURE;
285
286 #ifdef HAVE_HEIMDAL_VERSION
287     *cksumtype = cksum.cksumtype;
288 #else
289     *cksumtype = cksum.checksum_type;
290 #endif
291
292     krb5_free_checksum_contents(krbContext, &cksum);
293 #endif /* HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE */
294
295     if (!krb5_c_is_keyed_cksum(*cksumtype)) {
296         *minor = KRB5KRB_AP_ERR_INAPP_CKSUM;
297         return GSS_S_FAILURE;
298     }
299
300     return GSS_S_COMPLETE;
301 }
302
303 #ifdef HAVE_HEIMDAL_VERSION
304 static heim_general_string krbAnonymousPrincipalComponents[] =
305     { KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME };
306
307 static const Principal krbAnonymousPrincipalData = {
308     { KRB5_NT_WELLKNOWN, { 2, krbAnonymousPrincipalComponents } },
309     "WELLKNOWN:ANONYMOUS"
310 };
311 #endif
312
313 krb5_const_principal
314 krbAnonymousPrincipal(void)
315 {
316 #ifdef HAVE_HEIMDAL_VERSION
317     return &krbAnonymousPrincipalData;
318 #else
319     return krb5_anonymous_principal();
320 #endif
321 }
322
323 krb5_error_code
324 krbCryptoLength(krb5_context krbContext,
325 #ifdef HAVE_HEIMDAL_VERSION
326                 krb5_crypto krbCrypto,
327 #else
328                 krb5_keyblock *key,
329 #endif
330                 int type,
331                 size_t *length)
332 {
333 #ifdef HAVE_HEIMDAL_VERSION
334     return krb5_crypto_length(krbContext, krbCrypto, type, length);
335 #else
336     unsigned int len;
337     krb5_error_code code;
338
339     code = krb5_c_crypto_length(krbContext, KRB_KEY_TYPE(key), type, &len);
340     if (code == 0)
341         *length = (size_t)len;
342
343     return code;
344 #endif
345 }
346
347 krb5_error_code
348 krbPaddingLength(krb5_context krbContext,
349 #ifdef HAVE_HEIMDAL_VERSION
350                  krb5_crypto krbCrypto,
351 #else
352                  krb5_keyblock *key,
353 #endif
354                  size_t dataLength,
355                  size_t *padLength)
356 {
357     krb5_error_code code;
358 #ifdef HAVE_HEIMDAL_VERSION
359     size_t headerLength, paddingLength;
360
361     code = krbCryptoLength(krbContext, krbCrypto,
362                            KRB5_CRYPTO_TYPE_HEADER, &headerLength);
363     if (code != 0)
364         return code;
365
366     dataLength += headerLength;
367
368     code = krb5_crypto_length(krbContext, krbCrypto,
369                               KRB5_CRYPTO_TYPE_PADDING, &paddingLength);
370     if (code != 0)
371         return code;
372
373     if (paddingLength != 0 && (dataLength % paddingLength) != 0)
374         *padLength = paddingLength - (dataLength % paddingLength);
375     else
376         *padLength = 0;
377
378     return 0;
379 #else
380     unsigned int pad;
381
382     code = krb5_c_padding_length(krbContext, KRB_KEY_TYPE(key), dataLength, &pad);
383     if (code == 0)
384         *padLength = (size_t)pad;
385
386     return code;
387 #endif /* HAVE_HEIMDAL_VERSION */
388 }
389
390 krb5_error_code
391 krbBlockSize(krb5_context krbContext,
392 #ifdef HAVE_HEIMDAL_VERSION
393                  krb5_crypto krbCrypto,
394 #else
395                  krb5_keyblock *key,
396 #endif
397                  size_t *blockSize)
398 {
399 #ifdef HAVE_HEIMDAL_VERSION
400     return krb5_crypto_getblocksize(krbContext, krbCrypto, blockSize);
401 #else
402     return krb5_c_block_size(krbContext, KRB_KEY_TYPE(key), blockSize);
403 #endif
404 }
405
406 krb5_error_code
407 krbEnctypeToString(
408 #ifdef HAVE_HEIMDAL_VERSION
409                    krb5_context krbContext,
410 #else
411                    krb5_context krbContext GSSEAP_UNUSED,
412 #endif
413                    krb5_enctype enctype,
414                    const char *prefix,
415                    gss_buffer_t string)
416 {
417     krb5_error_code code;
418 #ifdef HAVE_HEIMDAL_VERSION
419     char *enctypeBuf = NULL;
420 #else
421     char enctypeBuf[128];
422 #endif
423     size_t prefixLength, enctypeLength;
424
425 #ifdef HAVE_HEIMDAL_VERSION
426     code = krb5_enctype_to_string(krbContext, enctype, &enctypeBuf);
427 #else
428     code = krb5_enctype_to_name(enctype, 0, enctypeBuf, sizeof(enctypeBuf));
429 #endif
430     if (code != 0)
431         return code;
432
433     prefixLength = (prefix != NULL) ? strlen(prefix) : 0;
434     enctypeLength = strlen(enctypeBuf);
435
436     string->value = GSSEAP_MALLOC(prefixLength + enctypeLength + 1);
437     if (string->value == NULL) {
438 #ifdef HAVE_HEIMDAL_VERSION
439         krb5_xfree(enctypeBuf);
440 #endif
441         return ENOMEM;
442     }
443
444     if (prefixLength != 0)
445         memcpy(string->value, prefix, prefixLength);
446     memcpy((char *)string->value + prefixLength, enctypeBuf, enctypeLength);
447
448     string->length = prefixLength + enctypeLength;
449     ((char *)string->value)[string->length] = '\0';
450
451 #ifdef HAVE_HEIMDAL_VERSION
452     krb5_xfree(enctypeBuf);
453 #endif
454
455     return 0;
456 }
457
458 krb5_error_code
459 krbMakeAuthDataKdcIssued(krb5_context context,
460                          const krb5_keyblock *key,
461                          krb5_const_principal issuer,
462 #ifdef HAVE_HEIMDAL_VERSION
463                          const AuthorizationData *authdata,
464                          AuthorizationData *adKdcIssued
465 #else
466                          krb5_authdata *const *authdata,
467                          krb5_authdata ***adKdcIssued
468 #endif
469                          )
470 {
471 #ifdef HAVE_HEIMDAL_VERSION
472     krb5_error_code code;
473     AD_KDCIssued kdcIssued;
474     AuthorizationDataElement adDatum;
475     unsigned char *buf;
476     size_t buf_size, len;
477     krb5_crypto crypto = NULL;
478
479     memset(&kdcIssued, 0, sizeof(kdcIssued));
480     memset(adKdcIssued, 0, sizeof(*adKdcIssued));
481
482     kdcIssued.i_realm = issuer->realm != NULL ? (Realm *)&issuer->realm : NULL;
483     kdcIssued.i_sname = (PrincipalName *)&issuer->name;
484     kdcIssued.elements = *authdata;
485
486     ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, &len, code);
487     if (code != 0)
488         goto cleanup;
489
490     code = krb5_crypto_init(context, key, 0, &crypto);
491     if (code != 0)
492         goto cleanup;
493
494     code = krb5_create_checksum(context, crypto, KRB5_KU_AD_KDC_ISSUED,
495                                 0, buf, buf_size, &kdcIssued.ad_checksum);
496     if (code != 0)
497         goto cleanup;
498
499     GSSEAP_FREE(buf);
500     buf = NULL;
501
502     ASN1_MALLOC_ENCODE(AD_KDCIssued, buf, buf_size, &kdcIssued, &len, code);
503     if (code != 0)
504         goto cleanup;
505
506     adDatum.ad_type = KRB5_AUTHDATA_KDC_ISSUED;
507     adDatum.ad_data.length = buf_size;
508     adDatum.ad_data.data = buf;
509
510     code = add_AuthorizationData(adKdcIssued, &adDatum);
511     if (code != 0)
512         goto cleanup;
513
514 cleanup:
515     if (buf != NULL)
516         GSSEAP_FREE(buf);
517     if (crypto != NULL)
518         krb5_crypto_destroy(context, crypto);
519     free_Checksum(&kdcIssued.ad_checksum);
520
521     return code;
522 #else
523     return krb5_make_authdata_kdc_issued(context, key, issuer, authdata,
524                                          adKdcIssued);
525 #endif /* HAVE_HEIMDAL_VERSION */
526 }
527
528 krb5_error_code
529 krbMakeCred(krb5_context krbContext,
530             krb5_auth_context authContext,
531             krb5_creds *creds,
532             krb5_data *data)
533 {
534     krb5_error_code code;
535 #ifdef HAVE_HEIMDAL_VERSION
536     KRB_CRED krbCred;
537     KrbCredInfo krbCredInfo;
538     EncKrbCredPart encKrbCredPart;
539     krb5_keyblock *key;
540     krb5_crypto krbCrypto = NULL;
541     krb5_data encKrbCredPartData;
542     krb5_replay_data rdata;
543     size_t len;
544 #else
545     krb5_data *d = NULL;
546 #endif
547
548     memset(data, 0, sizeof(*data));
549 #ifdef HAVE_HEIMDAL_VERSION
550     memset(&krbCred,        0, sizeof(krbCred));
551     memset(&krbCredInfo,    0, sizeof(krbCredInfo));
552     memset(&encKrbCredPart, 0, sizeof(encKrbCredPart));
553     memset(&rdata,          0, sizeof(rdata));
554
555     if (authContext->local_subkey)
556         key = authContext->local_subkey;
557     else if (authContext->remote_subkey)
558         key = authContext->remote_subkey;
559     else
560         key = authContext->keyblock;
561
562     krbCred.pvno = 5;
563     krbCred.msg_type = krb_cred;
564     krbCred.tickets.val = (Ticket *)GSSEAP_CALLOC(1, sizeof(Ticket));
565     if (krbCred.tickets.val == NULL) {
566         code = ENOMEM;
567         goto cleanup;
568     }
569     krbCred.tickets.len = 1;
570
571     code = decode_Ticket(creds->ticket.data,
572                          creds->ticket.length,
573                          krbCred.tickets.val, &len);
574     if (code != 0)
575         goto cleanup;
576
577     krbCredInfo.key         = creds->session;
578     krbCredInfo.prealm      = &creds->client->realm;
579     krbCredInfo.pname       = &creds->client->name;
580     krbCredInfo.flags       = &creds->flags.b;
581     krbCredInfo.authtime    = &creds->times.authtime;
582     krbCredInfo.starttime   = &creds->times.starttime;
583     krbCredInfo.endtime     = &creds->times.endtime;
584     krbCredInfo.renew_till  = &creds->times.renew_till;
585     krbCredInfo.srealm      = &creds->server->realm;
586     krbCredInfo.sname       = &creds->server->name;
587     krbCredInfo.caddr       = creds->addresses.len ? &creds->addresses : NULL;
588
589     encKrbCredPart.ticket_info.len = 1;
590     encKrbCredPart.ticket_info.val = &krbCredInfo;
591     if (authContext->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
592         rdata.seq                  = authContext->local_seqnumber;
593         encKrbCredPart.nonce       = (int32_t *)&rdata.seq;
594     } else {
595         encKrbCredPart.nonce       = NULL;
596     }
597     if (authContext->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
598         krb5_us_timeofday(krbContext, &rdata.timestamp, &rdata.usec);
599         encKrbCredPart.timestamp   = &rdata.timestamp;
600         encKrbCredPart.usec        = &rdata.usec;
601     } else {
602         encKrbCredPart.timestamp   = NULL;
603         encKrbCredPart.usec        = NULL;
604     }
605     encKrbCredPart.s_address       = authContext->local_address;
606     encKrbCredPart.r_address       = authContext->remote_address;
607
608     ASN1_MALLOC_ENCODE(EncKrbCredPart, encKrbCredPartData.data,
609                        encKrbCredPartData.length, &encKrbCredPart,
610                        &len, code);
611     if (code != 0)
612         goto cleanup;
613
614     code = krb5_crypto_init(krbContext, key, 0, &krbCrypto);
615     if (code != 0)
616         goto cleanup;
617
618     code = krb5_encrypt_EncryptedData(krbContext,
619                                       krbCrypto,
620                                       KRB5_KU_KRB_CRED,
621                                       encKrbCredPartData.data,
622                                       encKrbCredPartData.length,
623                                       0,
624                                       &krbCred.enc_part);
625     if (code != 0)
626         goto cleanup;
627
628     ASN1_MALLOC_ENCODE(KRB_CRED, data->data, data->length,
629                        &krbCred, &len, code);
630     if (code != 0)
631         goto cleanup;
632
633     if (authContext->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
634         authContext->local_seqnumber++;
635
636 cleanup:
637     if (krbCrypto != NULL)
638         krb5_crypto_destroy(krbContext, krbCrypto);
639     free_KRB_CRED(&krbCred);
640     krb5_data_free(&encKrbCredPartData);
641
642     return code;
643 #else
644     code = krb5_mk_1cred(krbContext, authContext, creds, &d, NULL);
645     if (code == 0) {
646         *data = *d;
647         GSSEAP_FREE(d);
648     }
649
650     return code;
651 #endif /* HAVE_HEIMDAL_VERSION */
652 }