s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / third_party / heimdal / 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 salt_fastuser_crypto(astgs_request_t r,
40                      krb5_const_principal salt_principal,
41                      krb5_enctype enctype,
42                      krb5_crypto fast_crypto,
43                      krb5_crypto *salted_crypto)
44 {
45     krb5_error_code ret;
46     krb5_principal client_princ = NULL;
47     krb5_data salt;
48     krb5_keyblock dkey;
49     size_t size;
50
51     *salted_crypto = NULL;
52
53     krb5_data_zero(&salt);
54     krb5_keyblock_zero(&dkey);
55
56     if (salt_principal == NULL) {
57         if (r->req.req_body.cname == NULL) {
58             ret = KRB5KRB_ERR_GENERIC;
59             goto out;
60         }
61
62         ret = _krb5_principalname2krb5_principal(r->context, &client_princ,
63                                                  *(r->req.req_body.cname),
64                                                  r->req.req_body.realm);
65         if (ret)
66             goto out;
67
68         salt_principal = client_princ;
69     }
70
71     ret = krb5_unparse_name(r->context, salt_principal, (char **)&salt.data);
72     if (ret)
73         goto out;
74
75     salt.length = strlen(salt.data);
76
77     kdc_log(r->context, r->config, 10,
78             "salt_fastuser_crypto: salt principal is %s (%d)",
79             (char *)salt.data, enctype);
80
81     ret = krb5_enctype_keysize(r->context, enctype, &size);
82     if (ret)
83         goto out;
84
85     ret = krb5_crypto_prfplus(r->context, fast_crypto, &salt,
86                               size, &dkey.keyvalue);
87     if (ret)
88         goto out;
89
90     dkey.keytype = enctype;
91
92     ret = krb5_crypto_init(r->context, &dkey, ENCTYPE_NULL, salted_crypto);
93     if (ret)
94         goto out;
95
96 out:
97     krb5_free_keyblock_contents(r->context, &dkey);
98     krb5_data_free(&salt);
99     krb5_free_principal(r->context, client_princ);
100
101     return ret;
102 }
103
104 static krb5_error_code
105 get_fastuser_crypto(astgs_request_t r,
106                     krb5_const_principal ticket_client,
107                     krb5_enctype enctype,
108                     krb5_crypto *crypto)
109 {
110     krb5_principal fast_princ;
111     hdb_entry_ex *fast_user = NULL;
112     Key *cookie_key = NULL;
113     krb5_crypto fast_crypto = NULL;
114     krb5_error_code ret;
115
116     *crypto = NULL;
117
118     ret = krb5_make_principal(r->context, &fast_princ,
119                               KRB5_WELLKNOWN_ORG_H5L_REALM,
120                               KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL);
121     if (ret)
122         goto out;
123
124     ret = _kdc_db_fetch(r->context, r->config, fast_princ,
125                         HDB_F_GET_FAST_COOKIE, NULL, NULL, &fast_user);
126     if (ret)
127         goto out;
128
129     if (enctype == KRB5_ENCTYPE_NULL)
130         ret = _kdc_get_preferred_key(r->context, r->config, fast_user,
131                                      "fast-cookie", &enctype, &cookie_key);
132     else
133         ret = hdb_enctype2key(r->context, &fast_user->entry, NULL,
134                               enctype, &cookie_key);
135     if (ret)
136         goto out;
137
138     ret = krb5_crypto_init(r->context, &cookie_key->key,
139                            ENCTYPE_NULL, &fast_crypto);
140     if (ret)
141         goto out;
142
143     ret = salt_fastuser_crypto(r, ticket_client,
144                                cookie_key->key.keytype,
145                                fast_crypto, crypto);
146     if (ret)
147         goto out;
148
149  out:
150     if (fast_user)
151         _kdc_free_ent(r->context, fast_user);
152     if (fast_crypto)
153         krb5_crypto_destroy(r->context, fast_crypto);
154     krb5_free_principal(r->context, fast_princ);
155
156     return ret;
157 }
158
159
160 static krb5_error_code
161 fast_parse_cookie(astgs_request_t r,
162                   krb5_const_principal ticket_client,
163                   const PA_DATA *pa)
164 {
165     krb5_crypto crypto = NULL;
166     krb5_error_code ret;
167     KDCFastCookie data;
168     krb5_data d1;
169     size_t len;
170
171     ret = decode_KDCFastCookie(pa->padata_value.data,
172                                pa->padata_value.length,
173                                &data, &len);
174     if (ret)
175         return ret;
176
177     if (len != pa->padata_value.length || strcmp("H5L1", data.version) != 0) {
178         free_KDCFastCookie(&data);
179         return KRB5KDC_ERR_POLICY;
180     }
181
182     ret = get_fastuser_crypto(r, ticket_client, data.cookie.etype, &crypto);
183     if (ret)
184         goto out;
185
186     ret = krb5_decrypt_EncryptedData(r->context, crypto,
187                                      KRB5_KU_H5L_COOKIE,
188                                      &data.cookie, &d1);
189     krb5_crypto_destroy(r->context, crypto);
190     if (ret)
191         goto out;
192
193     ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len);
194     krb5_data_free(&d1);
195     if (ret)
196         goto out;
197
198     if (r->fast.expiration < kdc_time) {
199         kdc_log(r->context, r->config, 2, "FAST cookie expired");
200         ret = KRB5KDC_ERR_POLICY;
201         goto out;
202     }
203
204  out:
205     free_KDCFastCookie(&data);
206
207     return ret;
208 }
209
210 static krb5_error_code
211 fast_add_cookie(astgs_request_t r,
212                 krb5_const_principal ticket_client,
213                 METHOD_DATA *method_data)
214 {
215     krb5_crypto crypto = NULL;
216     KDCFastCookie shell;
217     krb5_error_code ret;
218     krb5_data data;
219     size_t size;
220
221     memset(&shell, 0, sizeof(shell));
222
223     r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME;
224
225     ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length,
226                        &r->fast, &size, ret);
227     if (ret)
228         return ret;
229     heim_assert(size == data.length, "internal asn.1 encoder error");
230
231     ret = get_fastuser_crypto(r, ticket_client, KRB5_ENCTYPE_NULL, &crypto);
232     if (ret) {
233         kdc_log(r->context, r->config, 0,
234                 "Failed to find FAST principal for cookie encryption: %d", ret);
235         goto out;
236     }
237
238     ret = krb5_encrypt_EncryptedData(r->context, crypto,
239                                      KRB5_KU_H5L_COOKIE,
240                                      data.data, data.length, 0,
241                                      &shell.cookie);
242     krb5_crypto_destroy(r->context, crypto);
243     if (ret)
244         goto out;
245
246     krb5_data_free(&data);
247
248     shell.version = "H5L1";
249
250     ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length,
251                        &shell, &size, ret);
252     free_EncryptedData(&shell.cookie);
253     if (ret)
254         goto out;
255     heim_assert(size == data.length, "internal asn.1 encoder error");
256
257     ret = krb5_padata_add(r->context, method_data,
258                           KRB5_PADATA_FX_COOKIE,
259                           data.data, data.length);
260     if (ret == 0)
261         krb5_data_zero(&data);
262
263  out:
264     krb5_data_free(&data);
265     return ret;
266 }
267
268 krb5_error_code
269 _kdc_fast_mk_response(krb5_context context,
270                       krb5_crypto armor_crypto,
271                       METHOD_DATA *pa_data,
272                       krb5_keyblock *strengthen_key,
273                       KrbFastFinished *finished,
274                       krb5uint32 nonce,
275                       krb5_data *data)
276 {
277     PA_FX_FAST_REPLY fxfastrep;
278     KrbFastResponse fastrep;
279     krb5_error_code ret;
280     krb5_data buf;
281     size_t size;
282
283     memset(&fxfastrep, 0, sizeof(fxfastrep));
284     memset(&fastrep, 0, sizeof(fastrep));
285     krb5_data_zero(data);
286
287     if (pa_data) {
288         fastrep.padata.val = pa_data->val;
289         fastrep.padata.len = pa_data->len;
290     }
291     fastrep.strengthen_key = strengthen_key;
292     fastrep.finished = finished;
293     fastrep.nonce = nonce;
294
295     ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length,
296                        &fastrep, &size, ret);
297     if (ret)
298         return ret;
299     heim_assert(size == buf.length, "internal asn.1 encoder error");
300
301     fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data;
302
303     ret = krb5_encrypt_EncryptedData(context,
304                                      armor_crypto,
305                                      KRB5_KU_FAST_REP,
306                                      buf.data,
307                                      buf.length,
308                                      0,
309                                      &fxfastrep.u.armored_data.enc_fast_rep);
310     krb5_data_free(&buf);
311     if (ret)
312         return ret;
313
314     ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length,
315                        &fxfastrep, &size, ret);
316     free_PA_FX_FAST_REPLY(&fxfastrep);
317     if (ret)
318         return ret;
319     heim_assert(size == data->length, "internal asn.1 encoder error");
320
321     return 0;
322 }
323
324
325 krb5_error_code
326 _kdc_fast_mk_error(astgs_request_t r,
327                    METHOD_DATA *error_method,
328                    krb5_crypto armor_crypto,
329                    const KDC_REQ_BODY *req_body,
330                    krb5_error_code outer_error,
331                    krb5_principal error_client,
332                    krb5_principal error_server,
333                    time_t *csec, int *cusec,
334                    krb5_data *error_msg)
335 {
336     krb5_error_code ret;
337     krb5_data e_data;
338     size_t size;
339
340     krb5_data_zero(&e_data);
341
342     heim_assert(r != NULL, "invalid request in _kdc_fast_mk_error");
343
344     /*
345      * FX-COOKIE can be used outside of FAST, e.g. SRP or GSS.
346      */
347     if (armor_crypto || r->fast.fast_state.len) {
348         kdc_log(r->context, r->config, 5, "Adding FAST cookie for KRB-ERROR");
349         ret = fast_add_cookie(r, error_client, error_method);
350         if (ret) {
351             kdc_log(r->context, r->config, 1,
352                     "Failed to add FAST cookie: %d", ret);
353             free_METHOD_DATA(error_method);
354             return ret;
355         }
356     }
357
358     if (armor_crypto) {
359         PA_FX_FAST_REPLY fxfastrep;
360         KrbFastResponse fastrep;
361
362         memset(&fxfastrep, 0, sizeof(fxfastrep));
363         memset(&fastrep, 0, sizeof(fastrep));
364
365         kdc_log(r->context, r->config, 5, "Making FAST inner KRB-ERROR");
366
367         /* first add the KRB-ERROR to the fast errors */
368
369         ret = krb5_mk_error(r->context,
370                             outer_error,
371                             r->e_text,
372                             NULL,
373                             error_client,
374                             error_server,
375                             NULL,
376                             NULL,
377                             &e_data);
378         if (ret) {
379             kdc_log(r->context, r->config, 1,
380                     "Failed to make inner KRB-ERROR: %d", ret);
381             return ret;
382         }
383
384         ret = krb5_padata_add(r->context, error_method,
385                               KRB5_PADATA_FX_ERROR,
386                               e_data.data, e_data.length);
387         if (ret) {
388             kdc_log(r->context, r->config, 1,
389                     "Failed to make add FAST PADATA to inner KRB-ERROR: %d", ret);
390             krb5_data_free(&e_data);
391             return ret;
392         }
393
394         r->e_text = NULL;
395         if (r->fast.flags.requested_hidden_names) {
396             error_client = NULL;
397             error_server = NULL;
398         }
399         csec = 0;
400         cusec = 0;
401
402         ret = _kdc_fast_mk_response(r->context, armor_crypto,
403                                     error_method, NULL, NULL,
404                                     req_body->nonce, &e_data);
405         free_METHOD_DATA(error_method);
406         if (ret) {
407             kdc_log(r->context, r->config, 1,
408                     "Failed to make outer KRB-ERROR: %d", ret);
409             return ret;
410         }
411
412         ret = krb5_padata_add(r->context, error_method,
413                               KRB5_PADATA_FX_FAST,
414                               e_data.data, e_data.length);
415         if (ret) {
416             kdc_log(r->context, r->config, 1,
417                     "Failed to make add FAST PADATA to outer KRB-ERROR: %d", ret);
418             return ret;
419         }
420     } else
421         kdc_log(r->context, r->config, 5, "Making non-FAST KRB-ERROR");
422
423     if (error_method && error_method->len) {
424         ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length,
425                            error_method, &size, ret);
426         if (ret) {
427             kdc_log(r->context, r->config, 1,
428                     "Failed to make encode METHOD-DATA: %d", ret);
429             return ret;
430         }
431         heim_assert(size == e_data.length, "internal asn.1 encoder error");
432     }
433
434     ret = krb5_mk_error(r->context,
435                         outer_error,
436                         r->e_text,
437                         (e_data.length ? &e_data : NULL),
438                         error_client,
439                         error_server,
440                         csec,
441                         cusec,
442                         error_msg);
443     krb5_data_free(&e_data);
444
445     if (ret)
446         kdc_log(r->context, r->config, 1,
447                 "Failed to make encode KRB-ERROR: %d", ret);
448
449     return ret;
450 }
451
452 static krb5_error_code
453 fast_unwrap_request(astgs_request_t r,
454                     krb5_ticket *tgs_ticket,
455                     krb5_auth_context tgs_ac)
456 {
457     krb5_principal armor_server_principal = NULL;
458     char *armor_client_principal_name = NULL;
459     char *armor_server_principal_name = NULL;
460     PA_FX_FAST_REQUEST fxreq = {0};
461     krb5_auth_context ac = NULL;
462     krb5_ticket *ticket = NULL;
463     krb5_flags ap_req_options;
464     krb5_keyblock armorkey;
465     krb5_keyblock explicit_armorkey;
466     krb5_boolean explicit_armor;
467     krb5_error_code ret;
468     krb5_ap_req ap_req;
469     KrbFastReq fastreq = {0};
470     const PA_DATA *pa;
471     krb5_data data;
472     size_t len;
473     int i = 0;
474
475     pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST);
476     if (pa == NULL) {
477         if (tgs_ac && r->fast_asserted) {
478             kdc_log(r->context, r->config, 1,
479                     "Client asserted FAST but did not include FX-FAST pa-data");
480             ret = KRB5KRB_AP_ERR_MODIFIED;
481             goto out;
482         }
483
484         kdc_log(r->context, r->config, 10, "Not a FAST request");
485         return 0;
486     }
487
488     ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data,
489                                     pa->padata_value.length,
490                                     &fxreq,
491                                     &len);
492     if (ret) {
493         kdc_log(r->context, r->config, 4,
494                 "Failed to decode PA-FX-FAST-REQUEST: %d", ret);
495         goto out;
496     }
497
498     if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) {
499         kdc_log(r->context, r->config, 4,
500                 "PA-FX-FAST-REQUEST contains unknown type: %d",
501                 (int)fxreq.element);
502         ret = KRB5KDC_ERR_PREAUTH_FAILED;
503         goto out;
504     }
505
506     /*
507      * If check for armor data or it's not a TGS-REQ with implicit
508      * armor.
509      */
510     if (fxreq.u.armored_data.armor == NULL && tgs_ac == NULL) {
511         kdc_log(r->context, r->config, 4,
512                 "AS-REQ armor missing");
513         ret = KRB5KDC_ERR_PREAUTH_FAILED;
514         goto out;
515     }
516
517     explicit_armor = fxreq.u.armored_data.armor != NULL && tgs_ac != NULL;
518
519     /*
520      *
521      */
522     if (fxreq.u.armored_data.armor != NULL) {
523         if (fxreq.u.armored_data.armor->armor_type != 1) {
524             kdc_log(r->context, r->config, 4,
525                     "Incorrect AS-REQ armor type");
526             ret = KRB5KDC_ERR_PREAUTH_FAILED;
527             goto out;
528         }
529
530         ret = krb5_decode_ap_req(r->context,
531                                  &fxreq.u.armored_data.armor->armor_value,
532                                  &ap_req);
533         if(ret) {
534             kdc_log(r->context, r->config, 4, "Failed to decode AP-REQ");
535             goto out;
536         }
537
538         /* Save that principal that was in the request */
539         ret = _krb5_principalname2krb5_principal(r->context,
540                                                  &armor_server_principal,
541                                                  ap_req.ticket.sname,
542                                                  ap_req.ticket.realm);
543         if (ret) {
544             free_AP_REQ(&ap_req);
545             goto out;
546         }
547
548         ret = _kdc_db_fetch(r->context, r->config, armor_server_principal,
549                             HDB_F_GET_KRBTGT | HDB_F_DELAY_NEW_KEYS,
550                             (krb5uint32 *)ap_req.ticket.enc_part.kvno,
551                             NULL, &r->armor_server);
552         if(ret == HDB_ERR_NOT_FOUND_HERE) {
553             free_AP_REQ(&ap_req);
554             kdc_log(r->context, r->config, 5,
555                     "Armor key does not have secrets at this KDC, "
556                     "need to proxy");
557             goto out;
558         } else if (ret) {
559             free_AP_REQ(&ap_req);
560             ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
561             goto out;
562         }
563
564         ret = hdb_enctype2key(r->context, &r->armor_server->entry, NULL,
565                               ap_req.ticket.enc_part.etype,
566                               &r->armor_key);
567         if (ret) {
568             free_AP_REQ(&ap_req);
569             goto out;
570         }
571
572         ret = krb5_verify_ap_req2(r->context, &ac,
573                                   &ap_req,
574                                   armor_server_principal,
575                                   &r->armor_key->key,
576                                   0,
577                                   &ap_req_options,
578                                   &r->armor_ticket,
579                                   KRB5_KU_AP_REQ_AUTH);
580         free_AP_REQ(&ap_req);
581         if (ret)
582             goto out;
583
584         ret = krb5_unparse_name(r->context, armor_server_principal,
585                                 &armor_server_principal_name);
586         if (ret)
587             goto out;
588
589         /* FIXME krb5_verify_ap_req2() also checks this */
590         ret = _kdc_verify_flags(r->context, r->config,
591                                 &r->armor_ticket->ticket,
592                                 armor_server_principal_name);
593         if (ret) {
594             _kdc_audit_addreason((kdc_request_t)r,
595                                  "Armor TGT expired or invalid");
596             goto out;
597         }
598         ticket = r->armor_ticket;
599     } else {
600         heim_assert(tgs_ticket != NULL, "TGS authentication context without ticket");
601         ac = tgs_ac;
602         ticket = tgs_ticket;
603     }
604
605     krb5_unparse_name(r->context, ticket->client, &armor_client_principal_name);
606     _kdc_audit_addkv((kdc_request_t)r, 0, "armor_client_name", "%s",
607                       armor_client_principal_name ? armor_client_principal_name : "<unknown>");
608
609     if (ac->remote_subkey == NULL) {
610         krb5_auth_con_free(r->context, ac);
611         kdc_log(r->context, r->config, 2,
612                 "FAST AP-REQ remote subkey missing");
613         ret = KRB5KDC_ERR_PREAUTH_FAILED;
614         goto out;
615     }
616
617     r->fast.flags.kdc_verified =
618         !_kdc_is_anonymous_pkinit(r->context, ticket->client);
619
620     ret = _krb5_fast_armor_key(r->context,
621                                ac->remote_subkey,
622                                &ticket->ticket.key,
623                                &armorkey,
624                                explicit_armor ? NULL : &r->armor_crypto);
625     if (ret)
626         goto out;
627
628     if (explicit_armor) {
629         ret = _krb5_fast_explicit_armor_key(r->context,
630                                             &armorkey,
631                                             tgs_ac->remote_subkey,
632                                             &explicit_armorkey,
633                                             &r->armor_crypto);
634         if (ret)
635             goto out;
636
637         krb5_free_keyblock_contents(r->context, &explicit_armorkey);
638     }
639
640     krb5_free_keyblock_contents(r->context, &armorkey);
641
642     ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto,
643                                      KRB5_KU_FAST_ENC,
644                                      &fxreq.u.armored_data.enc_fast_req,
645                                      &data);
646     if (ret) {
647         kdc_log(r->context, r->config, 2,
648                 "Failed to decrypt FAST request");
649         goto out;
650     }
651
652     ret = decode_KrbFastReq(data.data, data.length, &fastreq, NULL);
653     krb5_data_free(&data);
654     if (ret)
655         goto out;
656
657     /*
658      * verify req-checksum of the outer body
659      */
660     if (tgs_ac) {
661         /*
662          * -- For TGS, contains the checksum performed over the type
663          * -- AP-REQ in the PA-TGS-REQ padata.
664          */
665         i = 0;
666         pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_TGS_REQ);
667         if (pa == NULL) {
668             kdc_log(r->context, r->config, 4,
669                     "FAST TGS request missing TGS-REQ padata");
670             ret = KRB5KRB_ERR_GENERIC;
671             goto out;
672         }
673
674         ret = _kdc_verify_checksum(r->context, r->armor_crypto,
675                                    KRB5_KU_FAST_REQ_CHKSUM,
676                                    &pa->padata_value,
677                                    &fxreq.u.armored_data.req_checksum);
678         if (ret) {
679             kdc_log(r->context, r->config, 2,
680                     "Bad checksum in FAST TGS request");
681             goto out;
682         }
683     } else {
684         /*
685          * -- For AS, contains the checksum performed over the type
686          * -- KDC-REQ-BODY for the req-body field of the KDC-REQ
687          * -- structure;
688          */
689         ret = _kdc_verify_checksum(r->context, r->armor_crypto,
690                                    KRB5_KU_FAST_REQ_CHKSUM,
691                                    &r->req.req_body._save,
692                                    &fxreq.u.armored_data.req_checksum);
693         if (ret) {
694             kdc_log(r->context, r->config, 2,
695                     "Bad checksum in FAST AS request");
696             goto out;
697         }
698     }
699
700     /*
701      * check for unsupported mandatory options
702      */
703     if (FastOptions2int(fastreq.fast_options) & 0xfffc) {
704         kdc_log(r->context, r->config, 2,
705                 "FAST unsupported mandatory option set");
706         ret = KRB5_KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS;
707         goto out;
708     }
709
710     r->fast.flags.requested_hidden_names = fastreq.fast_options.hide_client_names;
711
712     /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */
713     if (r->req.padata)
714         free_METHOD_DATA(r->req.padata);
715     else
716         ALLOC(r->req.padata);
717
718     ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata);
719     if (ret)
720         goto out;
721
722     free_KDC_REQ_BODY(&r->req.req_body);
723     ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body);
724     if (ret)
725         goto out;
726
727     kdc_log(r->context, r->config, 5, "Client selected FAST");
728
729  out:
730     if (ac && ac != tgs_ac)
731         krb5_auth_con_free(r->context, ac);
732
733     krb5_free_principal(r->context, armor_server_principal);
734     krb5_xfree(armor_client_principal_name);
735     krb5_xfree(armor_server_principal_name);
736
737     free_KrbFastReq(&fastreq);
738     free_PA_FX_FAST_REQUEST(&fxreq);
739
740     return ret;
741 }
742
743 /*
744  *
745  */
746 krb5_error_code
747 _kdc_fast_unwrap_request(astgs_request_t r,
748                          krb5_ticket *tgs_ticket,
749                          krb5_auth_context tgs_ac)
750 {
751     krb5_error_code ret;
752     const PA_DATA *pa;
753     int i = 0;
754
755     ret = fast_unwrap_request(r, tgs_ticket, tgs_ac);
756     if (ret)
757         return ret;
758
759     /*
760      * FX-COOKIE can be used outside of FAST, e.g. SRP or GSS.
761      */
762     pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
763     if (pa) {
764         krb5_const_principal ticket_client = NULL;
765
766         if (tgs_ticket)
767             ticket_client = tgs_ticket->client;
768
769         ret = fast_parse_cookie(r, ticket_client, pa);
770     }
771
772     return ret;
773 }
774
775 /*
776  * Strengthen reply key by mixing with a random key that is
777  * protected by FAST.
778  */
779 krb5_error_code
780 _kdc_fast_strengthen_reply_key(astgs_request_t r)
781 {
782     if (r->armor_crypto) {
783         krb5_keyblock new_reply_key;
784         krb5_error_code ret;
785
786         kdc_log(r->context, r->config, 5,
787                 "FAST strengthen reply key with strengthen-key");
788
789         heim_assert(r->reply_key.keytype != KRB5_ENCTYPE_NULL, "NULL reply key enctype");
790
791         ret = krb5_generate_random_keyblock(r->context, r->reply_key.keytype,
792                                             &r->strengthen_key);
793         if (ret)
794             krb5_abortx(r->context, "random generator fail");
795
796         ret = _krb5_fast_cf2(r->context,
797                              &r->strengthen_key, "strengthenkey",
798                              &r->reply_key, "replykey",
799                              &new_reply_key, NULL);
800         if (ret)
801             return ret;
802
803         krb5_free_keyblock_contents(r->context, &r->reply_key);
804         r->reply_key = new_reply_key;
805     }
806
807     return 0;
808 }
809
810 /*
811  * Zero and free KDCFastState
812  */
813 void
814 _kdc_free_fast_state(KDCFastState *state)
815 {
816     size_t i;
817
818     for (i = 0; i < state->fast_state.len; i++) {
819         PA_DATA *pa = &state->fast_state.val[i];
820
821         if (pa->padata_value.data)
822             memset_s(pa->padata_value.data, 0,
823                      pa->padata_value.length, pa->padata_value.length);
824     }
825     free_KDCFastState(state);
826 }
827
828 krb5_error_code
829 _kdc_fast_check_armor_pac(astgs_request_t r)
830 {
831     krb5_error_code ret;
832     int flags;
833     krb5_boolean ad_kdc_issued = FALSE;
834     krb5_pac mspac = NULL;
835     krb5_principal armor_client_principal = NULL;
836     hdb_entry_ex *armor_client = NULL;
837     char *armor_client_principal_name = NULL;
838
839     flags = HDB_F_FOR_TGS_REQ;
840     if (_kdc_synthetic_princ_used_p(r->context, r->armor_ticket))
841         flags |= HDB_F_SYNTHETIC_OK;
842     if (r->req.req_body.kdc_options.canonicalize)
843         flags |= HDB_F_CANON;
844
845     ret = _krb5_principalname2krb5_principal(r->context,
846                                              &armor_client_principal,
847                                              r->armor_ticket->ticket.cname,
848                                              r->armor_ticket->ticket.crealm);
849     if (ret)
850         goto out;
851
852     ret = krb5_unparse_name(r->context, armor_client_principal,
853                             &armor_client_principal_name);
854     if (ret)
855         goto out;
856
857     ret = _kdc_db_fetch_client(r->context, r->config, flags,
858                                armor_client_principal, armor_client_principal_name,
859                                r->req.req_body.realm, NULL, &armor_client);
860     if (ret)
861         goto out;
862
863     ret = kdc_check_flags(r, FALSE, armor_client, NULL);
864     if (ret)
865         goto out;
866
867     ret = _kdc_check_pac(r->context, r->config, armor_client_principal, NULL,
868                          armor_client, r->armor_server,
869                          r->armor_server, r->armor_server,
870                          &r->armor_key->key, &r->armor_key->key,
871                          &r->armor_ticket->ticket, &ad_kdc_issued, &mspac, NULL, NULL);
872     if (ret) {
873         const char *msg = krb5_get_error_message(r->context, ret);
874
875         kdc_log(r->context, r->config, 4,
876                 "Verify armor PAC (%s) failed for %s (%s) from %s with %s (%s)",
877                 armor_client_principal_name, r->cname, r->sname,
878                 r->from, msg, mspac ? "Ticket unsigned" : "No PAC");
879
880         krb5_free_error_message(r->context, msg);
881
882         goto out;
883     }
884
885 out:
886     krb5_xfree(armor_client_principal_name);
887     if (armor_client)
888         _kdc_free_ent(r->context, armor_client);
889     krb5_free_principal(r->context, armor_client_principal);
890     krb5_pac_free(r->context, mspac);
891
892     return ret;
893 }