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