1/*
2 * Copyright (c) 1997-2011 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2010 - 2011 Apple Inc. 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 "kdc_locl.h"
37
38#define FAST_COOKIE_VERSION2 2
39
40static krb5_error_code
41get_fastuser_crypto(kdc_request_t r, const char *realm, krb5_enctype enctype, krb5_crypto *crypto)
42{
43    krb5_principal fast_princ;
44    hdb_entry_ex *fast_user = NULL;
45    Key *cookie_key = NULL;
46    krb5_error_code ret;
47
48    *crypto = NULL;
49
50    ret = krb5_make_principal(r->context, &fast_princ, realm,
51			      KRB5_WELLKNOWN_NAME, KRB5_FAST_COOKIE, NULL);
52    if (ret)
53	goto out;
54
55    ret = _kdc_db_fetch(r->context, r->config, fast_princ,
56			HDB_F_GET_CLIENT, NULL, NULL, &fast_user);
57    krb5_free_principal(r->context, fast_princ);
58    if (ret) {
59	/*
60	 * Fall back to krbtgt
61	 */
62	ret = krb5_make_principal(r->context, &fast_princ, realm,
63				  KRB5_TGS_NAME, KRB5_FAST_COOKIE, NULL);
64	if (ret)
65	    goto out;
66
67	ret = _kdc_db_fetch(r->context, r->config, fast_princ,
68			    HDB_F_GET_CLIENT, NULL, NULL, &fast_user);
69	krb5_free_principal(r->context, fast_princ);
70	if (ret)
71	    goto out;
72    }
73
74    if (enctype == KRB5_ENCTYPE_NULL)
75	ret = _kdc_get_preferred_key(r->context, r->config, fast_user,
76				     "fast-cookie", &enctype, &cookie_key);
77    else
78	ret = hdb_enctype2key(r->context, &fast_user->entry,
79			      enctype, &cookie_key);
80    if (ret)
81	goto out;
82
83    ret = krb5_crypto_init(r->context, &cookie_key->key, 0, crypto);
84    if (ret)
85	goto out;
86
87 out:
88    if (fast_user)
89	_kdc_free_ent(r->context, fast_user);
90
91    return ret;
92}
93
94
95static krb5_error_code
96fast_parse_cookie(kdc_request_t r, const PA_DATA *pa)
97{
98    krb5_crypto crypto = NULL;
99    krb5_error_code ret;
100    KDCFastCookie data;
101    krb5_data d1;
102    size_t len;
103
104    ret = decode_KDCFastCookie(pa->padata_value.data,
105			       pa->padata_value.length,
106			       &data, &len);
107    if (ret)
108	return ret;
109
110    if (len != pa->padata_value.length || data.version != FAST_COOKIE_VERSION2) {
111	free_KDCFastCookie(&data);
112	return KRB5KDC_ERR_POLICY;
113    }
114
115    ret = get_fastuser_crypto(r, data.realm, data.cookie.etype, &crypto);
116    if (ret)
117	goto out;
118
119    ret = krb5_decrypt_EncryptedData(r->context, crypto,
120				     KRB5_KU_H5L_COOKIE,
121				     &data.cookie, &d1);
122    krb5_crypto_destroy(r->context, crypto);
123    if (ret)
124	goto out;
125
126    ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len);
127    krb5_data_free(&d1);
128    if (ret)
129	goto out;
130
131    if (r->fast.expiration < kdc_time) {
132	kdc_log(r->context, r->config, 0, "fast cookie expired");
133	ret = KRB5KDC_ERR_POLICY;
134	free_KDCFastState(&r->fast);
135	goto out;
136    }
137
138 out:
139    free_KDCFastCookie(&data);
140
141    return ret;
142}
143
144static krb5_error_code
145add_fast_cookie(kdc_request_t r, const char *realm, METHOD_DATA *method_data)
146{
147    krb5_crypto crypto = NULL;
148    KDCFastCookie shell;
149    krb5_error_code ret;
150    krb5_data data;
151    size_t size = 0;
152
153    memset(&shell, 0, sizeof(shell));
154    krb5_data_zero(&data);
155
156    r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME;
157
158    ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length,
159		       &r->fast, &size, ret);
160    if (ret)
161	return ret;
162    heim_assert(size == data.length, "internal asn1 encoder error");
163
164    ret = get_fastuser_crypto(r, realm, KRB5_ENCTYPE_NULL, &crypto);
165    if (ret) {
166	kdc_log(r->context, r->config, 0, "Failed to find fastuser for cookie encryption: %d", ret);
167	goto out;
168    }
169
170    ret = krb5_encrypt_EncryptedData(r->context, crypto,
171				     KRB5_KU_H5L_COOKIE,
172				     data.data, data.length, 0,
173				     &shell.cookie);
174    krb5_crypto_destroy(r->context, crypto);
175    if (ret)
176	goto out;
177
178    free(data.data);
179    data.data = NULL;
180
181    shell.version = FAST_COOKIE_VERSION2;
182    shell.realm = rk_UNCONST(realm);
183
184    ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length,
185		       &shell, &size, ret);
186    free_EncryptedData(&shell.cookie);
187    if (ret)
188	goto out;
189    heim_assert(size == data.length, "internal asn1 encoder error");
190
191    ret = krb5_padata_add(r->context, method_data,
192			  KRB5_PADATA_FX_COOKIE,
193			  data.data, data.length);
194    if (ret == 0)
195	data.data = NULL;
196
197 out:
198    if (data.data)
199	free(data.data);
200    return ret;
201}
202
203krb5_error_code
204_kdc_fast_mk_response(krb5_context context,
205		      krb5_crypto armor_crypto,
206		      METHOD_DATA *pa_data,
207		      krb5_keyblock *strengthen_key,
208		      KrbFastFinished *finished,
209		      krb5uint32 nonce,
210		      krb5_data *data)
211{
212    PA_FX_FAST_REPLY fxfastrep;
213    KrbFastResponse fastrep;
214    krb5_error_code ret;
215    krb5_data buf;
216    size_t size = 0;
217
218    memset(&fxfastrep, 0, sizeof(fxfastrep));
219    memset(&fastrep, 0, sizeof(fastrep));
220    krb5_data_zero(data);
221
222    if (pa_data) {
223	fastrep.padata.val = pa_data->val;
224	fastrep.padata.len = pa_data->len;
225    }
226    fastrep.strengthen_key = strengthen_key;
227    fastrep.finished = finished;
228    fastrep.nonce = nonce;
229
230    ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length,
231		       &fastrep, &size, ret);
232    if (ret)
233	return ret;
234    if (buf.length != size)
235	krb5_abortx(context, "internal asn.1 error");
236
237    fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data;
238
239    ret = krb5_encrypt_EncryptedData(context,
240				     armor_crypto,
241				     KRB5_KU_FAST_REP,
242				     buf.data,
243				     buf.length,
244				     0,
245				     &fxfastrep.u.armored_data.enc_fast_rep);
246    krb5_data_free(&buf);
247    if (ret)
248	return ret;
249
250    ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length,
251		       &fxfastrep, &size, ret);
252    free_PA_FX_FAST_REPLY(&fxfastrep);
253    if (ret)
254	return ret;
255    if (data->length != size)
256	krb5_abortx(context, "internal asn.1 error");
257
258    return 0;
259}
260
261
262krb5_error_code
263_kdc_fast_mk_error(krb5_context context,
264		   kdc_request_t r,
265		   METHOD_DATA *error_method,
266		   krb5_crypto armor_crypto,
267		   const KDC_REQ_BODY *req_body,
268		   krb5_error_code outer_error,
269		   const char *e_text,
270		   krb5_principal error_client,
271		   krb5_principal error_server,
272		   time_t *csec, int *cusec,
273		   krb5_data *error_msg)
274{
275    krb5_error_code ret;
276    krb5_data e_data;
277    size_t size = 0;
278
279    krb5_data_zero(&e_data);
280
281    if (armor_crypto) {
282	PA_FX_FAST_REPLY fxfastrep;
283	KrbFastResponse fastrep;
284
285	memset(&fxfastrep, 0, sizeof(fxfastrep));
286	memset(&fastrep, 0, sizeof(fastrep));
287
288	/* first add the KRB-ERROR to the fast errors */
289
290	ret = krb5_mk_error(context,
291			    outer_error,
292			    e_text,
293			    NULL,
294			    error_client,
295			    error_server,
296			    NULL,
297			    NULL,
298			    &e_data);
299	if (ret)
300	    return ret;
301
302	ret = krb5_padata_add(context, error_method,
303			      KRB5_PADATA_FX_ERROR,
304			      e_data.data, e_data.length);
305	if (ret) {
306	    krb5_data_free(&e_data);
307	    return ret;
308	}
309
310	ret = add_fast_cookie(r, r->server_princ->realm, error_method);
311	if (ret) {
312	    kdc_log(r->context, r->config, 0, "failed to add fast cookie for FAST with: %d", ret);
313	    free_METHOD_DATA(error_method);
314	    return ret;
315	}
316
317	outer_error = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
318	e_text = NULL;
319	if (r->fast.flags.requested_hidden_names) {
320	    error_client = NULL;
321	    error_server = NULL;
322	}
323	csec = 0;
324	cusec = 0;
325
326	ret = _kdc_fast_mk_response(context, armor_crypto,
327				    error_method, NULL, NULL,
328				    req_body->nonce, &e_data);
329	free_METHOD_DATA(error_method);
330	if (ret)
331	    return ret;
332
333	ret = krb5_padata_add(context, error_method,
334			      KRB5_PADATA_FX_FAST,
335			      e_data.data, e_data.length);
336	if (ret)
337	    return ret;
338
339    } else if (r->use_fast_cookie) {
340
341	ret = add_fast_cookie(r, r->server_princ->realm, error_method);
342	if (ret) {
343	    kdc_log(r->context, r->config, 0, "failed to add fast cookie forced cookie with: %d", ret);
344	    free_METHOD_DATA(error_method);
345	    return ret;
346	}
347    }
348
349    if (error_method && error_method->len) {
350	ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length,
351			   error_method, &size, ret);
352	if (ret)
353	    return ret;
354	if (e_data.length != size)
355	    krb5_abortx(context, "internal asn.1 error");
356    }
357
358    ret = krb5_mk_error(context,
359			outer_error,
360			e_text,
361			(e_data.length ? &e_data : NULL),
362			error_client,
363			error_server,
364			csec,
365			cusec,
366			error_msg);
367    krb5_data_free(&e_data);
368
369    return ret;
370}
371
372krb5_error_code
373_kdc_fast_unwrap_request(kdc_request_t r,
374			 krb5_ticket *tgs_ticket,
375			 krb5_auth_context tgs_ac)
376{
377    krb5_principal armor_server = NULL;
378    hdb_entry_ex *armor_user = NULL;
379    PA_FX_FAST_REQUEST fxreq;
380    krb5_auth_context ac = NULL;
381    krb5_ticket *ticket = NULL;
382    krb5_flags ap_req_options;
383    Key *armor_key = NULL;
384    krb5_keyblock armorkey;
385    krb5_error_code ret;
386    krb5_ap_req ap_req;
387    unsigned char *buf;
388    KrbFastReq fastreq;
389    const PA_DATA *pa;
390    krb5_data data;
391    size_t len;
392    int i = 0;
393
394    i = 0;
395    pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST);
396    if (pa == NULL) {
397	kdc_log(r->context, r->config, 10,
398		"Not a fast request");
399
400	/*
401	 * Check for fx cookie in the outside (for SRP pa outside of FAST)
402	 */
403	i = 0;
404	pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
405	if (pa) {
406	    ret = fast_parse_cookie(r, pa);
407	    if (ret)
408		goto out;
409	}
410
411	return 0;
412    }
413
414    ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data,
415				    pa->padata_value.length,
416				    &fxreq,
417				    &len);
418    if (ret) {
419	kdc_log(r->context, r->config, 0,
420		"Failed to decode FX_FAST_REQUEST: %d", ret);
421	goto out;
422    }
423
424    if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) {
425	kdc_log(r->context, r->config, 0,
426		"FAST contain unknown type: %d", (int)fxreq.element);
427	ret = KRB5KDC_ERR_PREAUTH_FAILED;
428	goto out;
429    }
430
431    /*
432     * If check for armor data or its not a TGS-REQ with implicit
433     * armor.
434     */
435    if (fxreq.u.armored_data.armor == NULL && tgs_ac == NULL) {
436	kdc_log(r->context, r->config, 0,
437		"AS-REQ armor missing");
438	ret = KRB5KDC_ERR_PREAUTH_FAILED;
439	goto out;
440    }
441
442    /*
443     *
444     */
445
446    if (tgs_ac == NULL) {
447
448	if (fxreq.u.armored_data.armor->armor_type != 1) {
449	    kdc_log(r->context, r->config, 0,
450		    "AS-REQ armor type not ap-req");
451	    ret = KRB5KDC_ERR_PREAUTH_FAILED;
452	    goto out;
453	}
454
455	ret = krb5_decode_ap_req(r->context,
456				 &fxreq.u.armored_data.armor->armor_value,
457				 &ap_req);
458	if(ret) {
459	    kdc_log(r->context, r->config, 0, "AP-REQ decode failed");
460	    goto out;
461	}
462
463	/* Save that principal that was in the request */
464	ret = _krb5_principalname2krb5_principal(r->context,
465						 &armor_server,
466						 ap_req.ticket.sname,
467						 ap_req.ticket.realm);
468	if (ret) {
469	    free_AP_REQ(&ap_req);
470	    goto out;
471	}
472
473	ret = _kdc_db_fetch(r->context, r->config, armor_server,
474			    HDB_F_GET_SERVER, NULL, NULL, &armor_user);
475	if(ret == HDB_ERR_NOT_FOUND_HERE) {
476	    kdc_log(r->context, r->config, 5,
477		    "Armor key does not have secrets at this KDC, "
478		    "need to proxy");
479	    goto out;
480	} else if (ret) {
481	    free_AP_REQ(&ap_req);
482	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
483	    goto out;
484	}
485
486	ret = hdb_enctype2key(r->context, &armor_user->entry,
487			      ap_req.ticket.enc_part.etype,
488			      &armor_key);
489	if (ret) {
490	    free_AP_REQ(&ap_req);
491	    goto out;
492	}
493
494	ret = krb5_verify_ap_req2(r->context, &ac,
495				  &ap_req,
496				  armor_server,
497				  &armor_key->key,
498				  0,
499				  &ap_req_options,
500				  &ticket,
501				  KRB5_KU_AP_REQ_AUTH);
502	free_AP_REQ(&ap_req);
503	if (ret)
504	    goto out;
505    } else {
506	heim_assert(tgs_ticket != NULL, "tgs_ac but not ticket ?");
507	ac = tgs_ac;
508	ticket = tgs_ticket;
509    }
510
511    if (ac->remote_subkey == NULL) {
512	krb5_auth_con_free(r->context, ac);
513	kdc_log(r->context, r->config, 0,
514		"FAST AP-REQ remote subkey missing");
515	ret = KRB5KDC_ERR_PREAUTH_FAILED;
516	goto out;
517    }
518
519    ret = _krb5_fast_armor_key(r->context,
520			       ac->remote_subkey,
521			       &ticket->ticket.key,
522			       &armorkey,
523			       &r->armor_crypto);
524
525    if (ret)
526	goto out;
527
528    krb5_free_keyblock_contents(r->context, &armorkey);
529
530    ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto,
531				     KRB5_KU_FAST_ENC,
532				     &fxreq.u.armored_data.enc_fast_req,
533				     &data);
534    if (ret) {
535	kdc_log(r->context, r->config, 0,
536		"Failed to decrypt FAST request");
537	goto out;
538    }
539
540    ret = decode_KrbFastReq(data.data, data.length, &fastreq, NULL);
541    krb5_data_free(&data);
542    if (ret)
543	goto out;
544
545    /*
546     * Now look for the FX-COOKIE in the inner padata.
547     */
548
549    i = 0;
550    pa = krb5_find_padata(fastreq.padata.val, fastreq.padata.len, KRB5_PADATA_FX_COOKIE, &i);
551    if (pa) {
552	ret = fast_parse_cookie(r, pa);
553	if (ret)
554	    goto out;
555    }
556
557    /*
558     * verify req-checksum of the outer body
559     */
560    if (tgs_ac) {
561
562	/*
563	 * -- For TGS, contains the checksum performed over the type
564	 * -- AP-REQ in the PA-TGS-REQ padata.
565	 */
566
567	i = 0;
568	pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_TGS_REQ);
569	if (pa == NULL) {
570	    kdc_log(r->context, r->config, 0,
571		    "FAST TGS request missing TGS-REQ padata");
572	    ret = KRB5KRB_ERR_GENERIC;
573	    goto out;
574	}
575
576	ret = krb5_verify_checksum(r->context, r->armor_crypto,
577				   KRB5_KU_FAST_REQ_CHKSUM,
578				   pa->padata_value.data, pa->padata_value.length,
579				   &fxreq.u.armored_data.req_checksum);
580	if (ret) {
581	    kdc_log(r->context, r->config, 0,
582		    "FAST TGS request have a bad checksum");
583	    goto out;
584	}
585
586    } else {
587	size_t size = 0;
588
589	/*
590	 * -- For AS, contains the checksum performed over the type
591	 * -- KDC-REQ-BODY for the req-body field of the KDC-REQ
592	 * -- structure;
593	 */
594	ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret);
595	if (ret)
596	    goto out;
597	heim_assert(size == len, "internal asn1 error");
598
599	ret = krb5_verify_checksum(r->context, r->armor_crypto,
600				   KRB5_KU_FAST_REQ_CHKSUM,
601				   buf, len,
602				   &fxreq.u.armored_data.req_checksum);
603	free(buf);
604	if (ret) {
605	    kdc_log(r->context, r->config, 0,
606		    "FAST AS request have a bad checksum");
607	    goto out;
608	}
609    }
610
611    /*
612     * check for unsupported mandatory options
613     */
614    if (FastOptions2int(fastreq.fast_options) & 0xfffc) {
615	kdc_log(r->context, r->config, 0,
616		"FAST unsupported mandatory option set");
617	ret = KRB5KDC_ERR_PREAUTH_FAILED;
618	goto out;
619    }
620
621    r->fast.flags.requested_hidden_names = fastreq.fast_options.hide_client_names;
622
623    /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */
624    if (r->req.padata)
625	free_METHOD_DATA(r->req.padata);
626    else
627	ALLOC(r->req.padata);
628
629    ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata);
630    if (ret)
631	goto out;
632
633    free_KDC_REQ_BODY(&r->req.req_body);
634    ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body);
635    if (ret)
636	goto out;
637
638    free_KrbFastReq(&fastreq);
639    free_PA_FX_FAST_REQUEST(&fxreq);
640
641    kdc_log(r->context, r->config, 0, "Client uses FAST");
642 out:
643    if (ac && ac != tgs_ac)
644	krb5_auth_con_free(r->context, ac);
645    if (ticket && ticket != tgs_ticket)
646	krb5_free_ticket(r->context, ticket);
647
648    if (armor_server)
649	krb5_free_principal(r->context, armor_server);
650    if(armor_user)
651	_kdc_free_ent(r->context, armor_user);
652
653    return ret;
654}
655
656krb5_error_code
657_kdc_fast_strengthen_reply_key(kdc_request_t r)
658{
659    if (r->armor_crypto) {
660	krb5_keyblock new_reply_key;
661	krb5_error_code ret;
662
663	kdc_log(r->context, r->config, 0,
664		"FAST strengthen reply key with strengthen-key");
665
666	heim_assert(r->reply_key.keytype != KRB5_ENCTYPE_NULL, "null enctype ?");
667
668	ret = krb5_generate_random_keyblock(r->context, r->reply_key.keytype, &r->strengthen_key);
669	if (ret)
670	    krb5_abortx(r->context, "random generator fail");
671
672	_krb5_debug_keyblock(r->context, 10, "fast: strengthen_key", &r->strengthen_key);
673	_krb5_debug_keyblock(r->context, 10, "fast: old reply_key", &r->reply_key);
674
675	ret = _krb5_fast_cf2(r->context,
676			     &r->strengthen_key, "strengthenkey",
677			     &r->reply_key, "replykey",
678			     &new_reply_key, NULL);
679	if (ret)
680	    return ret;
681
682	krb5_free_keyblock_contents(r->context, &r->reply_key);
683	ret = krb5_copy_keyblock_contents(r->context, &new_reply_key, &r->reply_key);
684	krb5_free_keyblock_contents(r->context, &new_reply_key);
685	if (ret)
686	    return ret;
687    }
688
689    _krb5_debug_keyblock(r->context, 10, "fast: reply_key", &r->reply_key);
690
691    return 0;
692}
693