Fix autoconf 2.70 compatibility
[platform/upstream/krb5.git] / src / kdc / fast_util.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/fast_util.c */
3 /*
4  * Copyright (C) 2009, 2015 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include <k5-int.h>
28
29 #include "kdc_util.h"
30 #include "extern.h"
31
32 /* Let cookies be valid for ten minutes. */
33 #define COOKIE_LIFETIME 600
34
35 static krb5_error_code armor_ap_request
36 (struct kdc_request_state *state, krb5_fast_armor *armor)
37 {
38     krb5_error_code retval = 0;
39     krb5_auth_context authcontext = NULL;
40     krb5_ticket *ticket = NULL;
41     krb5_keyblock *subkey = NULL;
42     kdc_realm_t *kdc_active_realm = state->realm_data;
43
44     assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST);
45     krb5_clear_error_message(kdc_context);
46     retval = krb5_auth_con_init(kdc_context, &authcontext);
47     if (retval == 0)
48         retval = krb5_auth_con_setflags(kdc_context,
49                                         authcontext, 0); /*disable replay cache*/
50     retval = krb5_rd_req(kdc_context, &authcontext,
51                          &armor->armor_value, NULL /*server*/,
52                          kdc_active_realm->realm_keytab,  NULL, &ticket);
53     if (retval != 0) {
54         const char * errmsg = krb5_get_error_message(kdc_context, retval);
55         k5_setmsg(kdc_context, retval, _("%s while handling ap-request armor"),
56                   errmsg);
57         krb5_free_error_message(kdc_context, errmsg);
58     }
59     if (retval == 0) {
60         if (!krb5_principal_compare_any_realm(kdc_context,
61                                               tgs_server,
62                                               ticket->server)) {
63             k5_setmsg(kdc_context, KRB5KDC_ERR_SERVER_NOMATCH,
64                       _("ap-request armor for something other than the local "
65                         "TGS"));
66             retval = KRB5KDC_ERR_SERVER_NOMATCH;
67         }
68     }
69     if (retval == 0) {
70         retval = krb5_auth_con_getrecvsubkey(kdc_context, authcontext, &subkey);
71         if (retval != 0 || subkey == NULL) {
72             k5_setmsg(kdc_context, KRB5KDC_ERR_POLICY,
73                       _("ap-request armor without subkey"));
74             retval = KRB5KDC_ERR_POLICY;
75         }
76     }
77     if (retval == 0)
78         retval = krb5_c_fx_cf2_simple(kdc_context,
79                                       subkey, "subkeyarmor",
80                                       ticket->enc_part2->session, "ticketarmor",
81                                       &state->armor_key);
82     if (ticket)
83         krb5_free_ticket(kdc_context, ticket);
84     if (subkey)
85         krb5_free_keyblock(kdc_context, subkey);
86     if (authcontext)
87         krb5_auth_con_free(kdc_context, authcontext);
88     return retval;
89 }
90
91 static krb5_error_code
92 encrypt_fast_reply(struct kdc_request_state *state,
93                    const krb5_fast_response *response,
94                    krb5_data **fx_fast_reply)
95 {
96     krb5_error_code retval = 0;
97     krb5_enc_data encrypted_reply;
98     krb5_data *encoded_response = NULL;
99     kdc_realm_t *kdc_active_realm = state->realm_data;
100
101     assert(state->armor_key);
102     retval = encode_krb5_fast_response(response, &encoded_response);
103     if (retval== 0)
104         retval = krb5_encrypt_helper(kdc_context, state->armor_key,
105                                      KRB5_KEYUSAGE_FAST_REP,
106                                      encoded_response, &encrypted_reply);
107     if (encoded_response)
108         krb5_free_data(kdc_context, encoded_response);
109     encoded_response = NULL;
110     if (retval == 0) {
111         retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply,
112                                               fx_fast_reply);
113         krb5_free_data_contents(kdc_context, &encrypted_reply.ciphertext);
114     }
115     return retval;
116 }
117
118
119 /*
120  * This function will find the FAST padata and, if FAST is successfully
121  * processed, will free the outer request and update the pointer to point to
122  * the inner request.  checksummed_data points to the data that is in the
123  * armored_fast_request checksum; either the pa-tgs-req or the kdc-req-body.
124  */
125 krb5_error_code
126 kdc_find_fast(krb5_kdc_req **requestptr,
127               krb5_data *checksummed_data,
128               krb5_keyblock *tgs_subkey,
129               krb5_keyblock *tgs_session,
130               struct kdc_request_state *state,
131               krb5_data **inner_body_out)
132 {
133     krb5_error_code retval = 0;
134     krb5_pa_data *fast_padata;
135     krb5_data scratch, *inner_body = NULL;
136     krb5_fast_req * fast_req = NULL;
137     krb5_kdc_req *request = *requestptr;
138     krb5_fast_armored_req *fast_armored_req = NULL;
139     krb5_checksum *cksum;
140     krb5_boolean cksum_valid;
141     krb5_keyblock empty_keyblock;
142     kdc_realm_t *kdc_active_realm = state->realm_data;
143
144     if (inner_body_out != NULL)
145         *inner_body_out = NULL;
146     scratch.data = NULL;
147     krb5_clear_error_message(kdc_context);
148     memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
149     fast_padata = krb5int_find_pa_data(kdc_context,
150                                        request->padata, KRB5_PADATA_FX_FAST);
151     if (fast_padata !=  NULL){
152         scratch.length = fast_padata->length;
153         scratch.data = (char *) fast_padata->contents;
154         retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
155         if (retval == 0 &&fast_armored_req->armor) {
156             switch (fast_armored_req->armor->armor_type) {
157             case KRB5_FAST_ARMOR_AP_REQUEST:
158                 if (tgs_subkey) {
159                     retval = KRB5KDC_ERR_PREAUTH_FAILED;
160                     k5_setmsg(kdc_context, retval,
161                               _("Ap-request armor not permitted with TGS"));
162                     break;
163                 }
164                 retval = armor_ap_request(state, fast_armored_req->armor);
165                 break;
166             default:
167                 k5_setmsg(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
168                           _("Unknown FAST armor type %d"),
169                           fast_armored_req->armor->armor_type);
170                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
171             }
172         }
173         if (retval == 0 && !state->armor_key) {
174             if (tgs_subkey)
175                 retval = krb5_c_fx_cf2_simple(kdc_context,
176                                               tgs_subkey, "subkeyarmor",
177                                               tgs_session, "ticketarmor",
178                                               &state->armor_key);
179             else {
180                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
181                 k5_setmsg(kdc_context, retval,
182                           _("No armor key but FAST armored request present"));
183             }
184         }
185         if (retval == 0) {
186             krb5_data plaintext;
187             plaintext.length = fast_armored_req->enc_part.ciphertext.length;
188             plaintext.data = malloc(plaintext.length);
189             if (plaintext.data == NULL)
190                 retval = ENOMEM;
191             retval = krb5_c_decrypt(kdc_context,
192                                     state->armor_key,
193                                     KRB5_KEYUSAGE_FAST_ENC, NULL,
194                                     &fast_armored_req->enc_part,
195                                     &plaintext);
196             if (retval == 0)
197                 retval = decode_krb5_fast_req(&plaintext, &fast_req);
198             if (retval == 0 && inner_body_out != NULL) {
199                 retval = fetch_asn1_field((unsigned char *)plaintext.data,
200                                           1, 2, &scratch);
201                 if (retval == 0) {
202                     retval = krb5_copy_data(kdc_context, &scratch,
203                                             &inner_body);
204                 }
205             }
206             if (plaintext.data)
207                 free(plaintext.data);
208         }
209         cksum = &fast_armored_req->req_checksum;
210         if (retval == 0)
211             retval = krb5_c_verify_checksum(kdc_context, state->armor_key,
212                                             KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
213                                             checksummed_data, cksum,
214                                             &cksum_valid);
215         if (retval == 0 && !cksum_valid) {
216             retval = KRB5KRB_AP_ERR_MODIFIED;
217             k5_setmsg(kdc_context, retval,
218                       _("FAST req_checksum invalid; request modified"));
219         }
220         if (retval == 0) {
221             if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) {
222                 retval = KRB5KDC_ERR_POLICY;
223                 k5_setmsg(kdc_context, retval,
224                           _("Unkeyed checksum used in fast_req"));
225             }
226         }
227         if (retval == 0) {
228             if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
229                 retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
230         }
231         if (retval == 0) {
232             state->fast_options = fast_req->fast_options;
233             fast_req->req_body->msg_type = request->msg_type;
234             krb5_free_kdc_req( kdc_context, request);
235             *requestptr = fast_req->req_body;
236             fast_req->req_body = NULL;
237         }
238     }
239     if (retval == 0 && inner_body_out != NULL) {
240         *inner_body_out = inner_body;
241         inner_body = NULL;
242     }
243     krb5_free_data(kdc_context, inner_body);
244     if (fast_req)
245         krb5_free_fast_req( kdc_context, fast_req);
246     if (fast_armored_req)
247         krb5_free_fast_armored_req(kdc_context, fast_armored_req);
248     return retval;
249 }
250
251
252 krb5_error_code
253 kdc_make_rstate(kdc_realm_t *active_realm, struct kdc_request_state **out)
254 {
255     struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state));
256     if (state == NULL)
257         return ENOMEM;
258     memset( state, 0, sizeof(struct kdc_request_state));
259     state->realm_data = active_realm;
260     *out = state;
261     return 0;
262 }
263
264 void
265 kdc_free_rstate (struct kdc_request_state *s)
266 {
267     kdc_realm_t *kdc_active_realm = s->realm_data;
268
269     if (s->armor_key)
270         krb5_free_keyblock(kdc_context, s->armor_key);
271     if (s->strengthen_key)
272         krb5_free_keyblock(kdc_context, s->strengthen_key);
273     k5_zapfree_pa_data(s->in_cookie_padata);
274     k5_zapfree_pa_data(s->out_cookie_padata);
275     free(s);
276 }
277
278 krb5_error_code
279 kdc_fast_response_handle_padata(struct kdc_request_state *state,
280                                 krb5_kdc_req *request,
281                                 krb5_kdc_rep *rep, krb5_enctype enctype)
282 {
283     krb5_error_code retval = 0;
284     krb5_fast_finished finish;
285     krb5_fast_response fast_response;
286     krb5_data *encoded_ticket = NULL;
287     krb5_data *encrypted_reply = NULL;
288     krb5_pa_data *pa = NULL, **pa_array = NULL;
289     krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5;
290     krb5_pa_data *empty_padata[] = {NULL};
291     krb5_keyblock *strengthen_key = NULL;
292     kdc_realm_t *kdc_active_realm = state->realm_data;
293
294     if (!state->armor_key)
295         return 0;
296     memset(&finish, 0, sizeof(finish));
297     retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key);
298     if (retval == 0)
299         retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key);
300     if (retval == 0) {
301         state->strengthen_key = strengthen_key;
302         strengthen_key = NULL;
303     }
304
305     fast_response.padata = rep->padata;
306     if (fast_response.padata == NULL)
307         fast_response.padata = &empty_padata[0];
308     fast_response.strengthen_key = state->strengthen_key;
309     fast_response.nonce = request->nonce;
310     fast_response.finished = &finish;
311     finish.client = rep->client;
312     pa_array = calloc(3, sizeof(*pa_array));
313     if (pa_array == NULL)
314         retval = ENOMEM;
315     pa = calloc(1, sizeof(krb5_pa_data));
316     if (retval == 0 && pa == NULL)
317         retval = ENOMEM;
318     if (retval == 0)
319         retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec);
320     if (retval == 0)
321         retval = encode_krb5_ticket(rep->ticket, &encoded_ticket);
322     if (retval == 0)
323         retval = krb5int_c_mandatory_cksumtype(kdc_context,
324                                                state->armor_key->enctype,
325                                                &cksumtype);
326     if (retval == 0)
327         retval = krb5_c_make_checksum(kdc_context, cksumtype,
328                                       state->armor_key,
329                                       KRB5_KEYUSAGE_FAST_FINISHED,
330                                       encoded_ticket, &finish.ticket_checksum);
331     if (retval == 0)
332         retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply);
333     if (retval == 0) {
334         pa[0].pa_type = KRB5_PADATA_FX_FAST;
335         pa[0].length = encrypted_reply->length;
336         pa[0].contents = (unsigned char *)  encrypted_reply->data;
337         pa_array[0] = &pa[0];
338         krb5_free_pa_data(kdc_context, rep->padata);
339         rep->padata = pa_array;
340         pa_array = NULL;
341         free(encrypted_reply);
342         encrypted_reply = NULL;
343         pa = NULL;
344     }
345     if (pa)
346         free(pa);
347     if (pa_array)
348         free(pa_array);
349     if (encrypted_reply)
350         krb5_free_data(kdc_context, encrypted_reply);
351     if (encoded_ticket)
352         krb5_free_data(kdc_context, encoded_ticket);
353     if (strengthen_key != NULL)
354         krb5_free_keyblock(kdc_context, strengthen_key);
355     if (finish.ticket_checksum.contents)
356         krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum);
357     return retval;
358 }
359
360
361 /*
362  * We assume the caller is responsible for passing us an in_padata
363  * sufficient to include in a FAST error.  In the FAST case we will
364  * set *fast_edata_out to the edata to be included in the error; in
365  * the non-FAST case we will set it to NULL.
366  */
367 krb5_error_code
368 kdc_fast_handle_error(krb5_context context,
369                       struct kdc_request_state *state,
370                       krb5_kdc_req *request,
371                       krb5_pa_data  **in_padata, krb5_error *err,
372                       krb5_data **fast_edata_out)
373 {
374     krb5_error_code retval = 0;
375     krb5_fast_response resp;
376     krb5_error fx_error;
377     krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL;
378     krb5_pa_data pa[1];
379     krb5_pa_data *outer_pa[3];
380     krb5_pa_data **inner_pa = NULL;
381     size_t size = 0;
382     kdc_realm_t *kdc_active_realm = state->realm_data;
383
384     *fast_edata_out = NULL;
385     memset(outer_pa, 0, sizeof(outer_pa));
386     if (state->armor_key == NULL)
387         return 0;
388     fx_error = *err;
389     fx_error.e_data.data = NULL;
390     fx_error.e_data.length = 0;
391     for (size = 0; in_padata&&in_padata[size]; size++);
392     inner_pa = calloc(size + 2, sizeof(krb5_pa_data *));
393     if (inner_pa == NULL)
394         retval = ENOMEM;
395     if (retval == 0)
396         for (size=0; in_padata&&in_padata[size]; size++)
397             inner_pa[size] = in_padata[size];
398     if (retval == 0)
399         retval = encode_krb5_error(&fx_error, &encoded_fx_error);
400     if (retval == 0) {
401         pa[0].pa_type = KRB5_PADATA_FX_ERROR;
402         pa[0].length = encoded_fx_error->length;
403         pa[0].contents = (unsigned char *) encoded_fx_error->data;
404         inner_pa[size++] = &pa[0];
405     }
406     if (retval == 0) {
407         resp.padata = inner_pa;
408         resp.nonce = request->nonce;
409         resp.strengthen_key = NULL;
410         resp.finished = NULL;
411     }
412     if (retval == 0)
413         retval = encrypt_fast_reply(state, &resp, &encrypted_reply);
414     if (inner_pa)
415         free(inner_pa); /*contained storage from caller and our stack*/
416     if (retval == 0) {
417         pa[0].pa_type = KRB5_PADATA_FX_FAST;
418         pa[0].length = encrypted_reply->length;
419         pa[0].contents = (unsigned char *) encrypted_reply->data;
420         outer_pa[0] = &pa[0];
421     }
422     retval = encode_krb5_padata_sequence(outer_pa, fast_edata_out);
423     if (encrypted_reply)
424         krb5_free_data(kdc_context, encrypted_reply);
425     if (encoded_fx_error)
426         krb5_free_data(kdc_context, encoded_fx_error);
427     return retval;
428 }
429
430 krb5_error_code
431 kdc_fast_handle_reply_key(struct kdc_request_state *state,
432                           krb5_keyblock *existing_key,
433                           krb5_keyblock **out_key)
434 {
435     krb5_error_code retval = 0;
436     kdc_realm_t *kdc_active_realm = state->realm_data;
437
438     if (state->armor_key)
439         retval = krb5_c_fx_cf2_simple(kdc_context,
440                                       state->strengthen_key, "strengthenkey",
441                                       existing_key,
442                                       "replykey", out_key);
443     else
444         retval = krb5_copy_keyblock(kdc_context, existing_key, out_key);
445     return retval;
446 }
447
448 krb5_boolean
449 kdc_fast_hide_client(struct kdc_request_state *state)
450 {
451     return (state->fast_options & KRB5_FAST_OPTION_HIDE_CLIENT_NAMES) != 0;
452 }
453
454 /* Create a pa-data entry with the specified type and contents. */
455 static krb5_error_code
456 make_padata(krb5_preauthtype pa_type, const void *contents, size_t len,
457             krb5_pa_data **out)
458 {
459     if (alloc_pa_data(pa_type, len, out) != 0)
460         return ENOMEM;
461     memcpy((*out)->contents, contents, len);
462     return 0;
463 }
464
465 /*
466  * Construct the secure cookie encryption key for the given local-realm TGT
467  * entry, kvno, and client principal.  The cookie key is derived from the first
468  * TGT key for the given kvno, using the concatenation of "COOKIE" and the
469  * unparsed client principal name as input.  If kvno is 0, the highest current
470  * kvno of the TGT is used.  If kvno_out is not null, *kvno_out is set to the
471  * kvno used.
472  */
473 static krb5_error_code
474 get_cookie_key(krb5_context context, krb5_db_entry *tgt, krb5_kvno kvno,
475                krb5_const_principal client_princ, krb5_keyblock **key_out,
476                krb5_kvno *kvno_out)
477 {
478     krb5_error_code ret;
479     krb5_key_data *kd;
480     krb5_keyblock kb;
481     krb5_data d;
482     krb5_int32 start = 0;
483     char *princstr = NULL, *derive_input = NULL;
484
485     *key_out = NULL;
486     memset(&kb, 0, sizeof(kb));
487
488     /* Find the first krbtgt key with the specified kvno. */
489     ret = krb5_dbe_search_enctype(context, tgt, &start, -1, -1, kvno, &kd);
490     if (ret)
491         goto cleanup;
492
493     /* Decrypt the key. */
494     ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL);
495     if (ret)
496         goto cleanup;
497
498     /* Construct the input string and derive the cookie key. */
499     ret = krb5_unparse_name(context, client_princ, &princstr);
500     if (ret)
501         goto cleanup;
502     if (asprintf(&derive_input, "COOKIE%s", princstr) < 0) {
503         ret = ENOMEM;
504         goto cleanup;
505     }
506     d = string2data(derive_input);
507     ret = krb5_c_derive_prfplus(context, &kb, &d, ENCTYPE_NULL, key_out);
508
509     if (kvno_out != NULL)
510         *kvno_out = kd->key_data_kvno;
511
512 cleanup:
513     krb5_free_keyblock_contents(context, &kb);
514     krb5_free_unparsed_name(context, princstr);
515     free(derive_input);
516     return ret;
517 }
518
519 /* Return true if there is any overlap between padata types in cpadata
520  * (from the cookie) and rpadata (from the request). */
521 static krb5_boolean
522 is_relevant(krb5_pa_data *const *cpadata, krb5_pa_data *const *rpadata)
523 {
524     krb5_pa_data *const *p;
525
526     for (p = cpadata; p != NULL && *p != NULL; p++) {
527         if (krb5int_find_pa_data(NULL, rpadata, (*p)->pa_type) != NULL)
528             return TRUE;
529     }
530     return FALSE;
531 }
532
533 /*
534  * Locate and decode the FAST cookie in req, storing its contents in state for
535  * later access by preauth modules.  If the cookie is expired, return
536  * KRB5KDC_ERR_PREAUTH_EXPIRED if its contents are relevant to req, and ignore
537  * it if they aren't.
538  */
539 krb5_error_code
540 kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
541                      krb5_kdc_req *req, krb5_db_entry *local_tgt)
542 {
543     krb5_error_code ret;
544     krb5_secure_cookie *cookie = NULL;
545     krb5_timestamp now;
546     krb5_keyblock *key = NULL;
547     krb5_enc_data enc;
548     krb5_pa_data *pa;
549     krb5_kvno kvno;
550     krb5_data plain = empty_data();
551
552     pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_FX_COOKIE);
553     if (pa == NULL)
554         return 0;
555
556     /* If it's not an MIT version 1 cookie, ignore it.  It may be an empty
557      * "MIT" cookie or a cookie generated by a different KDC implementation. */
558     if (pa->length <= 8 || memcmp(pa->contents, "MIT1", 4) != 0)
559         return 0;
560
561     /* Extract the kvno and generate the corresponding cookie key. */
562     kvno = load_32_be(pa->contents + 4);
563     ret = get_cookie_key(context, local_tgt, kvno, req->client, &key, NULL);
564     if (ret)
565         goto cleanup;
566
567     /* Decrypt and decode the cookie. */
568     memset(&enc, 0, sizeof(enc));
569     enc.enctype = key->enctype;
570     enc.ciphertext = make_data(pa->contents + 8, pa->length - 8);
571     ret = alloc_data(&plain, pa->length - 8);
572     if (ret)
573         goto cleanup;
574     ret = krb5_c_decrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, &enc,
575                          &plain);
576     if (ret)
577         goto cleanup;
578     ret = decode_krb5_secure_cookie(&plain, &cookie);
579     if (ret)
580         goto cleanup;
581
582     /* Check if the cookie is expired. */
583     ret = krb5_timeofday(context, &now);
584     if (ret)
585         goto cleanup;
586     if (ts2tt(now) > cookie->time + COOKIE_LIFETIME) {
587         /* Don't accept the cookie contents.  Only return an error if the
588          * cookie is relevant to the request. */
589         if (is_relevant(cookie->data, req->padata))
590             ret = KRB5KDC_ERR_PREAUTH_EXPIRED;
591         goto cleanup;
592     }
593
594     /* Steal the pa-data list pointer from the cookie and store it in state. */
595     state->in_cookie_padata = cookie->data;
596     cookie->data = NULL;
597
598 cleanup:
599     zapfree(plain.data, plain.length);
600     krb5_free_keyblock(context, key);
601     k5_free_secure_cookie(context, cookie);
602     return 0;
603 }
604
605 /* If state contains a cookie value for pa_type, set *out to the corresponding
606  * data and return true.  Otherwise set *out to empty and return false. */
607 krb5_boolean
608 kdc_fast_search_cookie(struct kdc_request_state *state,
609                        krb5_preauthtype pa_type, krb5_data *out)
610 {
611     krb5_pa_data *pa;
612
613     pa = krb5int_find_pa_data(NULL, state->in_cookie_padata, pa_type);
614     if (pa == NULL) {
615         *out = empty_data();
616         return FALSE;
617     } else {
618         *out = make_data(pa->contents, pa->length);
619         return TRUE;
620     }
621 }
622
623 /* Set a cookie value in state for data, to be included in the outgoing
624  * cookie.  Duplicate values are ignored. */
625 krb5_error_code
626 kdc_fast_set_cookie(struct kdc_request_state *state, krb5_preauthtype pa_type,
627                     const krb5_data *data)
628 {
629     krb5_pa_data **list = state->out_cookie_padata;
630     size_t count;
631
632     for (count = 0; list != NULL && list[count] != NULL; count++) {
633         if (list[count]->pa_type == pa_type)
634             return 0;
635     }
636
637     list = realloc(list, (count + 2) * sizeof(*list));
638     if (list == NULL)
639         return ENOMEM;
640     state->out_cookie_padata = list;
641     list[count] = list[count + 1] = NULL;
642     return make_padata(pa_type, data->data, data->length, &list[count]);
643 }
644
645 /* Construct a cookie pa-data item using the cookie values from state, or a
646  * trivial "MIT" cookie if no values are set. */
647 krb5_error_code
648 kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
649                      krb5_db_entry *local_tgt,
650                      krb5_const_principal client_princ,
651                      krb5_pa_data **cookie_out)
652 {
653     krb5_error_code ret;
654     krb5_secure_cookie cookie;
655     krb5_pa_data **contents = state->out_cookie_padata, *pa;
656     krb5_keyblock *key = NULL;
657     krb5_timestamp now;
658     krb5_enc_data enc;
659     krb5_data *der_cookie = NULL;
660     krb5_kvno kvno;
661     size_t ctlen;
662
663     *cookie_out = NULL;
664     memset(&enc, 0, sizeof(enc));
665
666     /* Make a trivial cookie if there are no contents to marshal or we don't
667      * have a TGT entry to encrypt them. */
668     if (contents == NULL || *contents == NULL || local_tgt == NULL)
669         return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out);
670
671     ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno);
672     if (ret)
673         goto cleanup;
674
675     /* Encode the cookie. */
676     ret = krb5_timeofday(context, &now);
677     if (ret)
678         goto cleanup;
679     cookie.time = ts2tt(now);
680     cookie.data = contents;
681     ret = encode_krb5_secure_cookie(&cookie, &der_cookie);
682     if (ret)
683         goto cleanup;
684
685     /* Encrypt the cookie in key. */
686     ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length,
687                                 &ctlen);
688     if (ret)
689         goto cleanup;
690     ret = alloc_data(&enc.ciphertext, ctlen);
691     if (ret)
692         goto cleanup;
693     ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL,
694                          der_cookie, &enc);
695     if (ret)
696         goto cleanup;
697
698     /* Construct the cookie pa-data entry. */
699     ret = alloc_pa_data(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa);
700     memcpy(pa->contents, "MIT1", 4);
701     store_32_be(kvno, pa->contents + 4);
702     memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length);
703     *cookie_out = pa;
704
705 cleanup:
706     krb5_free_keyblock(context, key);
707     if (der_cookie != NULL) {
708         zapfree(der_cookie->data, der_cookie->length);
709         free(der_cookie);
710     }
711     krb5_free_data_contents(context, &enc.ciphertext);
712     return ret;
713 }