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 H5L_FAST_COOKIE "org.h5l.fast-cookie"
39#define FAST_COOKIE_VERSION1 "H5L1"
40
41static krb5_error_code
42get_fastuser_crypto(kdc_request_t r, krb5_enctype enctype, krb5_crypto *crypto)
43{
44    krb5_principal fast_princ;
45    hdb_entry_ex *fast_user = NULL;
46    Key *cookie_key = NULL;
47    krb5_error_code ret;
48
49    *crypto = NULL;
50
51    ret = krb5_make_principal(r->context, &fast_princ,
52			      KRB5_WELLKNOWN_ORG_H5L_REALM,
53			      KRB5_WELLKNOWN_NAME, H5L_FAST_COOKIE, NULL);
54    if (ret)
55	goto out;
56
57    ret = _kdc_db_fetch(r->context, r->config, fast_princ,
58			HDB_F_GET_CLIENT, NULL, NULL, &fast_user);
59    krb5_free_principal(r->context, fast_princ);
60    if (ret)
61	goto out;
62
63    if (enctype == KRB5_ENCTYPE_NULL)
64	ret = _kdc_get_preferred_key(r->context, r->config, fast_user,
65				     "fast-cookie", &enctype, &cookie_key);
66    else
67	ret = hdb_enctype2key(r->context, &fast_user->entry,
68			      enctype, &cookie_key);
69    if (ret)
70	goto out;
71
72    ret = krb5_crypto_init(r->context, &cookie_key->key, 0, crypto);
73    if (ret)
74	goto out;
75
76 out:
77    if (fast_user)
78	_kdc_free_ent(r->context, fast_user);
79
80    return ret;
81}
82
83
84static krb5_error_code
85fast_parse_cookie(kdc_request_t r, const PA_DATA *pa)
86{
87    krb5_crypto crypto = NULL;
88    krb5_error_code ret;
89    KDCFastCookie data;
90    krb5_data d1;
91    size_t len;
92
93    ret = decode_KDCFastCookie(pa->padata_value.data,
94			       pa->padata_value.length,
95			       &data, &len);
96    if (ret)
97	return ret;
98
99    if (len != pa->padata_value.length || strcmp(FAST_COOKIE_VERSION1, data.version) != 0) {
100	free_KDCFastCookie(&data);
101	return KRB5KDC_ERR_POLICY;
102    }
103
104    ret = get_fastuser_crypto(r, data.cookie.etype, &crypto);
105    if (ret)
106	goto out;
107
108    ret = krb5_decrypt_EncryptedData(r->context, crypto,
109				     KRB5_KU_H5L_COOKIE,
110				     &data.cookie, &d1);
111    krb5_crypto_destroy(r->context, crypto);
112    if (ret)
113	goto out;
114
115    ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len);
116    krb5_data_free(&d1);
117    if (ret)
118	goto out;
119
120    if (r->fast.expiration < kdc_time) {
121	kdc_log(r->context, r->config, 0, "fast cookie expired");
122	ret = KRB5KDC_ERR_POLICY;
123	goto out;
124    }
125
126 out:
127    free_KDCFastCookie(&data);
128
129    return ret;
130}
131
132static krb5_error_code
133fast_add_cookie(kdc_request_t r, METHOD_DATA *method_data)
134{
135    krb5_crypto crypto = NULL;
136    KDCFastCookie shell;
137    krb5_error_code ret;
138    krb5_data data;
139    size_t size;
140
141    memset(&shell, 0, sizeof(shell));
142
143    r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME;
144
145    ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length,
146		       &r->fast, &size, ret);
147    if (ret)
148	return ret;
149    heim_assert(size == data.length, "internal asn1 encoder error");
150
151    ret = get_fastuser_crypto(r, KRB5_ENCTYPE_NULL, &crypto);
152    if (ret)
153	goto out;
154
155    ret = krb5_encrypt_EncryptedData(r->context, crypto,
156				     KRB5_KU_H5L_COOKIE,
157				     data.data, data.length, 0,
158				     &shell.cookie);
159    krb5_crypto_destroy(r->context, crypto);
160    if (ret)
161	goto out;
162
163    free(data.data);
164
165    shell.version = FAST_COOKIE_VERSION1;
166
167    ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length,
168		       &shell, &size, ret);
169    free_EncryptedData(&shell.cookie);
170    if (ret)
171	goto out;
172    heim_assert(size == data.length, "internal asn1 encoder error");
173
174    ret = krb5_padata_add(r->context, method_data,
175			  KRB5_PADATA_FX_COOKIE,
176			  data.data, data.length);
177 out:
178    if (ret)
179	free(data.data);
180    return ret;
181}
182
183krb5_error_code
184_kdc_fast_mk_response(krb5_context context,
185		      krb5_crypto armor_crypto,
186		      METHOD_DATA *pa_data,
187		      krb5_keyblock *strengthen_key,
188		      KrbFastFinished *finished,
189		      krb5uint32 nonce,
190		      krb5_data *data)
191{
192    PA_FX_FAST_REPLY fxfastrep;
193    KrbFastResponse fastrep;
194    krb5_error_code ret;
195    krb5_data buf;
196    size_t size;
197
198    memset(&fxfastrep, 0, sizeof(fxfastrep));
199    memset(&fastrep, 0, sizeof(fastrep));
200    krb5_data_zero(data);
201
202    if (pa_data) {
203	fastrep.padata.val = pa_data->val;
204	fastrep.padata.len = pa_data->len;
205    }
206    fastrep.strengthen_key = strengthen_key;
207    fastrep.finished = finished;
208    fastrep.nonce = nonce;
209
210    ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length,
211		       &fastrep, &size, ret);
212    if (ret)
213	return ret;
214    if (buf.length != size)
215	krb5_abortx(context, "internal asn.1 error");
216
217    fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data;
218
219    ret = krb5_encrypt_EncryptedData(context,
220				     armor_crypto,
221				     KRB5_KU_FAST_REP,
222				     buf.data,
223				     buf.length,
224				     0,
225				     &fxfastrep.u.armored_data.enc_fast_rep);
226    krb5_data_free(&buf);
227    if (ret)
228	return ret;
229
230    ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length,
231		       &fxfastrep, &size, ret);
232    free_PA_FX_FAST_REPLY(&fxfastrep);
233    if (ret)
234	return ret;
235    if (data->length != size)
236	krb5_abortx(context, "internal asn.1 error");
237
238    return 0;
239}
240
241
242krb5_error_code
243_kdc_fast_mk_error(krb5_context context,
244		   kdc_request_t r,
245		   METHOD_DATA *error_method,
246		   krb5_crypto armor_crypto,
247		   const KDC_REQ_BODY *req_body,
248		   krb5_error_code outer_error,
249		   const char *e_text,
250		   krb5_principal error_client,
251		   krb5_principal error_server,
252		   time_t *csec, int *cusec,
253		   krb5_data *error_msg)
254{
255    krb5_error_code ret;
256    krb5_data e_data;
257    size_t size;
258
259    krb5_data_zero(&e_data);
260
261    if (armor_crypto) {
262	PA_FX_FAST_REPLY fxfastrep;
263	KrbFastResponse fastrep;
264
265	memset(&fxfastrep, 0, sizeof(fxfastrep));
266	memset(&fastrep, 0, sizeof(fastrep));
267
268	/* first add the KRB-ERROR to the fast errors */
269
270	ret = krb5_mk_error(context,
271			    outer_error,
272			    e_text,
273			    NULL,
274			    error_client,
275			    error_server,
276			    NULL,
277			    NULL,
278			    &e_data);
279	if (ret)
280	    return ret;
281
282	ret = krb5_padata_add(context, error_method,
283			      KRB5_PADATA_FX_ERROR,
284			      e_data.data, e_data.length);
285	if (ret) {
286	    krb5_data_free(&e_data);
287	    return ret;
288	}
289
290	if (/* hide_principal */ 0) {
291	    error_client = NULL;
292	    error_server = NULL;
293	    e_text = NULL;
294	}
295
296	if (r)
297	    ret = fast_add_cookie(r, error_method);
298	else
299	    ret = krb5_padata_add(context, error_method,
300				  KRB5_PADATA_FX_COOKIE,
301				  NULL, 0);
302	if (ret) {
303	    kdc_log(r->context, r->config, 0, "failed to add fast cookie with: %d", ret);
304	    free_METHOD_DATA(error_method);
305	    return ret;
306	}
307
308	ret = _kdc_fast_mk_response(context, armor_crypto,
309				    error_method, NULL, NULL,
310				    req_body->nonce, &e_data);
311	free_METHOD_DATA(error_method);
312	if (ret)
313	    return ret;
314
315	ret = krb5_padata_add(context, error_method,
316			      KRB5_PADATA_FX_FAST,
317			      e_data.data, e_data.length);
318	if (ret)
319	    return ret;
320    }
321
322    if (error_method && error_method->len) {
323	ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length,
324			   error_method, &size, ret);
325	if (ret)
326	    return ret;
327	if (e_data.length != size)
328	    krb5_abortx(context, "internal asn.1 error");
329    }
330
331    ret = krb5_mk_error(context,
332			outer_error,
333			e_text,
334			(e_data.length ? &e_data : NULL),
335			error_client,
336			error_server,
337			csec,
338			cusec,
339			error_msg);
340    krb5_data_free(&e_data);
341
342    return ret;
343}
344
345krb5_error_code
346_kdc_fast_unwrap_request(kdc_request_t r)
347{
348    krb5_principal armor_server = NULL;
349    hdb_entry_ex *armor_user = NULL;
350    PA_FX_FAST_REQUEST fxreq;
351    krb5_auth_context ac = NULL;
352    krb5_ticket *ticket = NULL;
353    krb5_flags ap_req_options;
354    Key *armor_key = NULL;
355    krb5_keyblock armorkey;
356    krb5_error_code ret;
357    krb5_ap_req ap_req;
358    unsigned char *buf;
359    KrbFastReq fastreq;
360    size_t len, size;
361    krb5_data data;
362    const PA_DATA *pa;
363    int i = 0;
364
365    /*
366     * First look for FX_COOKIE and and process it
367     */
368    pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
369    if (pa) {
370	ret = fast_parse_cookie(r, pa);
371	if (ret)
372	    goto out;
373    }
374
375    i = 0;
376    pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST);
377    if (pa == NULL)
378	return 0;
379
380    ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data,
381				    pa->padata_value.length,
382				    &fxreq,
383				    &len);
384    if (ret)
385	goto out;
386    if (len != pa->padata_value.length) {
387	ret = KRB5KDC_ERR_PREAUTH_FAILED;
388	goto out;
389    }
390
391    if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) {
392	kdc_log(r->context, r->config, 0,
393		"AS-REQ FAST contain unknown type: %d", (int)fxreq.element);
394	ret = KRB5KDC_ERR_PREAUTH_FAILED;
395	goto out;
396    }
397
398    /* pull out armor key */
399    if (fxreq.u.armored_data.armor == NULL) {
400	kdc_log(r->context, r->config, 0,
401		"AS-REQ armor missing");
402	ret = KRB5KDC_ERR_PREAUTH_FAILED;
403	goto out;
404    }
405
406    if (fxreq.u.armored_data.armor->armor_type != 1) {
407	kdc_log(r->context, r->config, 0,
408		"AS-REQ armor type not ap-req");
409	ret = KRB5KDC_ERR_PREAUTH_FAILED;
410	goto out;
411    }
412
413    ret = krb5_decode_ap_req(r->context,
414			     &fxreq.u.armored_data.armor->armor_value,
415			     &ap_req);
416    if(ret) {
417	kdc_log(r->context, r->config, 0, "AP-REQ decode failed");
418	goto out;
419    }
420
421    /* Save that principal that was in the request */
422    ret = _krb5_principalname2krb5_principal(r->context,
423					     &armor_server,
424					     ap_req.ticket.sname,
425					     ap_req.ticket.realm);
426    if (ret) {
427	free_AP_REQ(&ap_req);
428	goto out;
429    }
430
431    ret = _kdc_db_fetch(r->context, r->config, armor_server,
432			HDB_F_GET_SERVER, NULL, NULL, &armor_user);
433    if(ret == HDB_ERR_NOT_FOUND_HERE) {
434	kdc_log(r->context, r->config, 5,
435		"armor key does not have secrets at this KDC, "
436		"need to proxy");
437	goto out;
438    } else if (ret) {
439	free_AP_REQ(&ap_req);
440	ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
441	goto out;
442    }
443
444    ret = hdb_enctype2key(r->context, &armor_user->entry,
445			  ap_req.ticket.enc_part.etype,
446			  &armor_key);
447    if (ret) {
448	free_AP_REQ(&ap_req);
449	goto out;
450    }
451
452    ret = krb5_verify_ap_req2(r->context, &ac,
453			      &ap_req,
454			      armor_server,
455			      &armor_key->key,
456			      0,
457			      &ap_req_options,
458			      &ticket,
459			      KRB5_KU_AP_REQ_AUTH);
460    free_AP_REQ(&ap_req);
461    if (ret)
462	goto out;
463
464    if (ac->remote_subkey == NULL) {
465	krb5_auth_con_free(r->context, ac);
466	kdc_log(r->context, r->config, 0,
467		"FAST AP-REQ remote subkey missing");
468	ret = KRB5KDC_ERR_PREAUTH_FAILED;
469	goto out;
470    }
471
472    ret = _krb5_fast_armor_key(r->context,
473			       ac->remote_subkey,
474			       &ticket->ticket.key,
475			       &armorkey,
476			       &r->armor_crypto);
477    krb5_auth_con_free(r->context, ac);
478    krb5_free_ticket(r->context, ticket);
479    if (ret)
480	goto out;
481
482    krb5_free_keyblock_contents(r->context, &armorkey);
483
484    /* verify req-checksum of the outer body */
485
486    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret);
487    if (ret)
488	goto out;
489    if (size != len) {
490	ret = KRB5KDC_ERR_PREAUTH_FAILED;
491	goto out;
492    }
493
494    ret = krb5_verify_checksum(r->context, r->armor_crypto,
495			       KRB5_KU_FAST_REQ_CHKSUM,
496			       buf, len,
497			       &fxreq.u.armored_data.req_checksum);
498    free(buf);
499    if (ret) {
500	kdc_log(r->context, r->config, 0,
501		"FAST request have a bad checksum");
502	goto out;
503    }
504
505    ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto,
506				     KRB5_KU_FAST_ENC,
507				     &fxreq.u.armored_data.enc_fast_req,
508				     &data);
509    if (ret) {
510	kdc_log(r->context, r->config, 0,
511		"Failed to decrypt FAST request");
512	goto out;
513    }
514
515    ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size);
516    if (ret) {
517	krb5_data_free(&data);
518	goto out;
519    }
520    if (data.length != size) {
521	krb5_data_free(&data);
522	ret = KRB5KDC_ERR_PREAUTH_FAILED;
523	goto out;
524    }
525    krb5_data_free(&data);
526
527    free_KDC_REQ_BODY(&r->req.req_body);
528    ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body);
529    if (ret)
530	goto out;
531
532    /* check for unsupported mandatory options */
533    if (FastOptions2int(fastreq.fast_options) & 0xfffc) {
534	kdc_log(r->context, r->config, 0,
535		"FAST unsupported mandatory option set");
536	ret = KRB5KDC_ERR_PREAUTH_FAILED;
537	goto out;
538    }
539
540    /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */
541    if (r->req.padata)
542	free_METHOD_DATA(r->req.padata);
543    else
544	ALLOC(r->req.padata);
545
546    ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata);
547    if (ret)
548	goto out;
549
550    free_KrbFastReq(&fastreq);
551    free_PA_FX_FAST_REQUEST(&fxreq);
552
553 out:
554    if (armor_server)
555	krb5_free_principal(r->context, armor_server);
556    if(armor_user)
557	_kdc_free_ent(r->context, armor_user);
558
559    return ret;
560}
561