KDC MUST NOT accept ap-request armor in FAST TGS
[krb5.git] / src / kdc / fast_util.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * kdc/fast_util.c
4  *
5  * Copyright (C) 2009 by the Massachusetts Institute of Technology.
6  * All rights reserved.
7  *
8  * Export of this software from the United States of America may
9  *   require a specific license from the United States Government.
10  *   It is the responsibility of any person or organization contemplating
11  *   export to obtain such a license before exporting.
12  *
13  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14  * distribute this software and its documentation for any purpose and
15  * without fee is hereby granted, provided that the above copyright
16  * notice appear in all copies and that both that copyright notice and
17  * this permission notice appear in supporting documentation, and that
18  * the name of M.I.T. not be used in advertising or publicity pertaining
19  * to distribution of the software without specific, written prior
20  * permission.  Furthermore if you modify this software you must label
21  * your software as modified software and not distribute it in such a
22  * fashion that it might be confused with the original M.I.T. software.
23  * M.I.T. makes no representations about the suitability of
24  * this software for any purpose.  It is provided "as is" without express
25  * or implied warranty.
26  */
27
28 #include <k5-int.h>
29
30 #include "kdc_util.h"
31 #include "extern.h"
32
33
34 /*
35  * This function will find the fast and cookie padata and if fast is
36  * successfully processed, will throw away (and free) the outer
37  * request and update the pointer to point to the inner request.  The
38  * checksummed_data points to the data that is in the
39  * armored_fast_request checksum; either the pa-tgs-req or the
40  * kdc-req-body.
41  */
42
43 static krb5_error_code armor_ap_request
44 (struct kdc_request_state *state, krb5_fast_armor *armor)
45 {
46     krb5_error_code retval = 0;
47     krb5_auth_context authcontext = NULL;
48     krb5_ticket *ticket = NULL;
49     krb5_keyblock *subkey = NULL;
50
51     assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST);
52     krb5_clear_error_message(kdc_context);
53     retval = krb5_auth_con_init(kdc_context, &authcontext);
54     if (retval == 0)
55         retval = krb5_auth_con_setflags(kdc_context,
56                                         authcontext, 0); /*disable replay cache*/
57     retval = krb5_rd_req(kdc_context, &authcontext,
58                          &armor->armor_value, NULL /*server*/,
59                          kdc_active_realm->realm_keytab,  NULL, &ticket);
60     if (retval != 0) {
61         const char * errmsg = krb5_get_error_message(kdc_context, retval);
62         krb5_set_error_message(kdc_context, retval,
63                                "%s while handling ap-request armor", errmsg);
64         krb5_free_error_message(kdc_context, errmsg);
65     }
66     if (retval == 0) {
67         if (!krb5_principal_compare_any_realm(kdc_context,
68                                               tgs_server,
69                                               ticket->server)) {
70             krb5_set_error_message(kdc_context, KRB5KDC_ERR_SERVER_NOMATCH,
71                                    "ap-request armor for something other than  the local TGS");
72             retval = KRB5KDC_ERR_SERVER_NOMATCH;
73         }
74     }
75     if (retval == 0) {
76         retval = krb5_auth_con_getrecvsubkey(kdc_context, authcontext, &subkey);
77         if (retval != 0 || subkey == NULL) {
78             krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY,
79                                    "ap-request armor without subkey");
80             retval = KRB5KDC_ERR_POLICY;
81         }
82     }
83     if (retval == 0)
84         retval = krb5_c_fx_cf2_simple(kdc_context,
85                                       subkey, "subkeyarmor",
86                                       ticket->enc_part2->session, "ticketarmor",
87                                       &state->armor_key);
88     if (ticket)
89         krb5_free_ticket(kdc_context, ticket);
90     if (subkey)
91         krb5_free_keyblock(kdc_context, subkey);
92     if (authcontext)
93         krb5_auth_con_free(kdc_context, authcontext);
94     return retval;
95 }
96
97 static krb5_error_code
98 encrypt_fast_reply(struct kdc_request_state *state,
99                    const krb5_fast_response *response,
100                    krb5_data **fx_fast_reply)
101 {
102     krb5_error_code retval = 0;
103     krb5_enc_data encrypted_reply;
104     krb5_data *encoded_response = NULL;
105     assert(state->armor_key);
106     retval = encode_krb5_fast_response(response, &encoded_response);
107     if (retval== 0)
108         retval = krb5_encrypt_helper(kdc_context, state->armor_key,
109                                      KRB5_KEYUSAGE_FAST_REP,
110                                      encoded_response, &encrypted_reply);
111     if (encoded_response)
112         krb5_free_data(kdc_context, encoded_response);
113     encoded_response = NULL;
114     if (retval == 0) {
115         retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply,
116                                               fx_fast_reply);
117         krb5_free_data_contents(kdc_context, &encrypted_reply.ciphertext);
118     }
119     return retval;
120 }
121
122
123 krb5_error_code
124 kdc_find_fast(krb5_kdc_req **requestptr,
125               krb5_data *checksummed_data,
126               krb5_keyblock *tgs_subkey,
127               krb5_keyblock *tgs_session,
128               struct kdc_request_state *state)
129 {
130     krb5_error_code retval = 0;
131     krb5_pa_data *fast_padata, *cookie_padata;
132     krb5_data scratch;
133     krb5_fast_req * fast_req = NULL;
134     krb5_kdc_req *request = *requestptr;
135     krb5_fast_armored_req *fast_armored_req = NULL;
136     krb5_boolean cksum_valid;
137     krb5_keyblock empty_keyblock;
138
139     scratch.data = NULL;
140     krb5_clear_error_message(kdc_context);
141     memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
142     fast_padata = find_pa_data(request->padata,
143                                KRB5_PADATA_FX_FAST);
144     if (fast_padata !=  NULL){
145         scratch.length = fast_padata->length;
146         scratch.data = (char *) fast_padata->contents;
147         retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
148         if (retval == 0 &&fast_armored_req->armor) {
149             switch (fast_armored_req->armor->armor_type) {
150             case KRB5_FAST_ARMOR_AP_REQUEST:
151                 if (tgs_subkey) {
152                     krb5_set_error_message( kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
153                                             "Ap-request armor not permitted with TGS");
154                     return KRB5KDC_ERR_PREAUTH_FAILED;
155                 }
156                 retval = armor_ap_request(state, fast_armored_req->armor);
157                 break;
158             default:
159                 krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
160                                        "Unknow FAST armor type %d",
161                                        fast_armored_req->armor->armor_type);
162                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
163             }
164         }
165         if (retval == 0 && !state->armor_key) {
166             if (tgs_subkey)
167                 retval = krb5_c_fx_cf2_simple(kdc_context,
168                                               tgs_subkey, "subkeyarmor",
169                                               tgs_session, "ticketarmor",
170                                               &state->armor_key);
171             else {
172                 krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
173                                        "No armor key but FAST armored request present");
174                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
175             }
176         }
177         if (retval == 0) {
178             krb5_data plaintext;
179             plaintext.length = fast_armored_req->enc_part.ciphertext.length;
180             plaintext.data = malloc(plaintext.length);
181             if (plaintext.data == NULL)
182                 retval = ENOMEM;
183             retval = krb5_c_decrypt(kdc_context,
184                                     state->armor_key,
185                                     KRB5_KEYUSAGE_FAST_ENC, NULL,
186                                     &fast_armored_req->enc_part,
187                                     &plaintext);
188             if (retval == 0)
189                 retval = decode_krb5_fast_req(&plaintext, &fast_req);
190             if (plaintext.data)
191                 free(plaintext.data);
192         }
193         if (retval == 0)
194             retval = krb5_c_verify_checksum(kdc_context, state->armor_key,
195                                             KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
196                                             checksummed_data,
197                                             &fast_armored_req->req_checksum,
198                                             &cksum_valid);
199         if (retval == 0 && !cksum_valid) {
200             retval = KRB5KRB_AP_ERR_MODIFIED;
201             krb5_set_error_message(kdc_context, KRB5KRB_AP_ERR_MODIFIED,
202                                    "FAST req_checksum invalid; request modified");
203         }
204         if (retval == 0) {
205             krb5_error_code ret;
206             /*
207              * We need to confirm that a keyed checksum is used for the
208              * fast_req checksum.  In April 2009, the best way to do this is
209              * to try verifying the checksum with a keyblock with an zero
210              * length; if it succeeds, then an unkeyed checksum is used.
211              */
212             ret  = krb5_c_verify_checksum(kdc_context, &empty_keyblock,
213                                           KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
214                                           checksummed_data,
215                                           &fast_armored_req->req_checksum,
216                                           &cksum_valid);
217             if (ret == 0) {
218                 retval = KRB5KDC_ERR_POLICY;
219                 krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY,
220                                        "Unkeyed checksum used in fast_req");
221             }
222         }
223         if (retval == 0) {
224             if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
225                 retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
226         }
227         if (retval == 0)
228             cookie_padata = find_pa_data(fast_req->req_body->padata,
229                                          KRB5_PADATA_FX_COOKIE);
230         if (retval == 0) {
231             state->fast_options = fast_req->fast_options;
232             if (request->kdc_state == state)
233                 request->kdc_state = NULL;
234             krb5_free_kdc_req( kdc_context, request);
235             *requestptr = fast_req->req_body;
236             fast_req->req_body = NULL;
237         }
238     }
239     else cookie_padata = find_pa_data(request->padata, KRB5_PADATA_FX_COOKIE);
240     if (retval == 0 && cookie_padata != NULL) {
241         krb5_pa_data *new_padata = malloc(sizeof (krb5_pa_data));
242         if (new_padata == NULL) {
243             retval = ENOMEM;
244         } else {
245             new_padata->pa_type = KRB5_PADATA_FX_COOKIE;
246             new_padata->length = cookie_padata->length;
247             new_padata->contents = malloc(new_padata->length);
248             if (new_padata->contents == NULL) {
249                 retval = ENOMEM;
250                 free(new_padata);
251             } else {
252                 memcpy(new_padata->contents, cookie_padata->contents,
253                        new_padata->length);
254                 state->cookie = new_padata;
255             }
256         }
257     }
258     if (fast_req)
259         krb5_free_fast_req( kdc_context, fast_req);
260     if (fast_armored_req)
261         krb5_free_fast_armored_req(kdc_context, fast_armored_req);
262     return retval;
263 }
264
265
266 krb5_error_code
267 kdc_make_rstate(struct kdc_request_state **out)
268 {
269     struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state));
270     if (state == NULL)
271         return ENOMEM;
272     memset( state, 0, sizeof(struct kdc_request_state));
273     *out = state;
274     return 0;
275 }
276
277 void
278 kdc_free_rstate (struct kdc_request_state *s)
279 {
280     if (s == NULL)
281         return;
282     if (s->armor_key)
283         krb5_free_keyblock(kdc_context, s->armor_key);
284     if (s->strengthen_key)
285         krb5_free_keyblock(kdc_context, s->strengthen_key);
286     if (s->cookie) {
287         free(s->cookie->contents);
288         free(s->cookie);
289     }
290     free(s);
291 }
292
293 krb5_error_code
294 kdc_fast_response_handle_padata(struct kdc_request_state *state,
295                                 krb5_kdc_req *request,
296                                 krb5_kdc_rep *rep, krb5_enctype enctype)
297 {
298     krb5_error_code retval = 0;
299     krb5_fast_finished finish;
300     krb5_fast_response fast_response;
301     krb5_data *encoded_ticket = NULL;
302     krb5_data *encrypted_reply = NULL;
303     krb5_pa_data *pa = NULL, **pa_array = NULL;
304     krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5;
305     krb5_pa_data *empty_padata[] = {NULL};
306     krb5_keyblock *strengthen_key = NULL;
307
308     if (!state->armor_key)
309         return 0;
310     memset(&finish, 0, sizeof(finish));
311     retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key);
312     if (retval == 0)
313         retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key);
314     if (retval == 0) {
315         state->strengthen_key = strengthen_key;
316         strengthen_key = NULL;
317     }
318
319     fast_response.padata = rep->padata;
320     if (fast_response.padata == NULL)
321         fast_response.padata = &empty_padata[0];
322     fast_response.strengthen_key = state->strengthen_key;
323     fast_response.nonce = request->nonce;
324     fast_response.finished = &finish;
325     finish.client = rep->client;
326     pa_array = calloc(3, sizeof(*pa_array));
327     if (pa_array == NULL)
328         retval = ENOMEM;
329     pa = calloc(1, sizeof(krb5_pa_data));
330     if (retval == 0 && pa == NULL)
331         retval = ENOMEM;
332     if (retval == 0)
333         retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec);
334     if (retval == 0)
335         retval = encode_krb5_ticket(rep->ticket, &encoded_ticket);
336     if (retval == 0)
337         retval = krb5int_c_mandatory_cksumtype(kdc_context,
338                                                state->armor_key->enctype,
339                                                &cksumtype);
340     if (retval == 0)
341         retval = krb5_c_make_checksum(kdc_context, cksumtype,
342                                       state->armor_key,
343                                       KRB5_KEYUSAGE_FAST_FINISHED,
344                                       encoded_ticket, &finish.ticket_checksum);
345     if (retval == 0)
346         retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply);
347     if (retval == 0) {
348         pa[0].pa_type = KRB5_PADATA_FX_FAST;
349         pa[0].length = encrypted_reply->length;
350         pa[0].contents = (unsigned char *)  encrypted_reply->data;
351         pa_array[0] = &pa[0];
352         rep->padata = pa_array;
353         pa_array = NULL;
354         free(encrypted_reply);
355         encrypted_reply = NULL;
356         pa = NULL;
357     }
358     if (pa)
359         free(pa);
360     if (pa_array)
361         free(pa_array);
362     if (encrypted_reply)
363         krb5_free_data(kdc_context, encrypted_reply);
364     if (encoded_ticket)
365         krb5_free_data(kdc_context, encoded_ticket);
366     if (strengthen_key != NULL)
367         krb5_free_keyblock(kdc_context, strengthen_key);
368     if (finish.ticket_checksum.contents)
369         krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum);
370     return retval;
371 }
372
373
374 /*
375  * We assume the caller is responsible for passing us an in_padata
376  * sufficient to include in a FAST error.  In the FAST case we will
377  * throw away the e_data in the error (if any); in the non-FAST case
378  * we will not use the in_padata.
379  */
380 krb5_error_code
381 kdc_fast_handle_error(krb5_context context,
382                       struct kdc_request_state *state,
383                       krb5_kdc_req *request,
384                       krb5_pa_data  **in_padata, krb5_error *err)
385 {
386     krb5_error_code retval = 0;
387     krb5_fast_response resp;
388     krb5_error fx_error;
389     krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL;
390     krb5_pa_data pa[1];
391     krb5_pa_data *outer_pa[3], *cookie = NULL;
392     krb5_pa_data **inner_pa = NULL;
393     size_t size = 0;
394     krb5_data *encoded_e_data = NULL;
395
396     memset(outer_pa, 0, sizeof(outer_pa));
397     if (!state->armor_key)
398         return 0;
399     fx_error = *err;
400     fx_error.e_data.data = NULL;
401     fx_error.e_data.length = 0;
402     for (size = 0; in_padata&&in_padata[size]; size++);
403     size +=3;
404     inner_pa = calloc(size, sizeof(krb5_pa_data *));
405     if (inner_pa == NULL)
406         retval = ENOMEM;
407     if (retval == 0)
408         for (size=0; in_padata&&in_padata[size]; size++)
409             inner_pa[size] = in_padata[size];
410     if (retval == 0)
411         retval = encode_krb5_error(&fx_error, &encoded_fx_error);
412     if (retval == 0) {
413         pa[0].pa_type = KRB5_PADATA_FX_ERROR;
414         pa[0].length = encoded_fx_error->length;
415         pa[0].contents = (unsigned char *) encoded_fx_error->data;
416         inner_pa[size++] = &pa[0];
417         if (find_pa_data(inner_pa, KRB5_PADATA_FX_COOKIE) == NULL)
418             retval = kdc_preauth_get_cookie(state, &cookie);
419     }
420     if (cookie != NULL)
421         inner_pa[size++] = cookie;
422     if (retval == 0) {
423         resp.padata = inner_pa;
424         resp.nonce = request->nonce;
425         resp.strengthen_key = NULL;
426         resp.finished = NULL;
427     }
428     if (retval == 0)
429         retval = encrypt_fast_reply(state, &resp, &encrypted_reply);
430     if (inner_pa)
431         free(inner_pa); /*contained storage from caller and our stack*/
432     if (cookie) {
433         free(cookie->contents);
434         free(cookie);
435         cookie = NULL;
436     }
437     if (retval == 0) {
438         pa[0].pa_type = KRB5_PADATA_FX_FAST;
439         pa[0].length = encrypted_reply->length;
440         pa[0].contents = (unsigned char *) encrypted_reply->data;
441         outer_pa[0] = &pa[0];
442     }
443     retval = encode_krb5_padata_sequence(outer_pa, &encoded_e_data);
444     if (retval == 0) {
445         /*process_as holds onto a pointer to the original e_data and frees it*/
446         err->e_data = *encoded_e_data;
447         free(encoded_e_data); /*contents belong to err*/
448         encoded_e_data = NULL;
449     }
450     if (encoded_e_data)
451         krb5_free_data(kdc_context, encoded_e_data);
452     if (encrypted_reply)
453         krb5_free_data(kdc_context, encrypted_reply);
454     if (encoded_fx_error)
455         krb5_free_data(kdc_context, encoded_fx_error);
456     return retval;
457 }
458
459 krb5_error_code
460 kdc_fast_handle_reply_key(struct kdc_request_state *state,
461                           krb5_keyblock *existing_key,
462                           krb5_keyblock **out_key)
463 {
464     krb5_error_code retval = 0;
465     if (state->armor_key)
466         retval = krb5_c_fx_cf2_simple(kdc_context,
467                                       state->strengthen_key, "strengthenkey",
468                                       existing_key,
469                                       "replykey", out_key);
470     else
471         retval = krb5_copy_keyblock(kdc_context, existing_key, out_key);
472     return retval;
473 }
474
475
476 krb5_error_code
477 kdc_preauth_get_cookie(struct kdc_request_state *state,
478                        krb5_pa_data **cookie)
479 {
480     char *contents;
481     krb5_pa_data *pa = NULL;
482     /* In our current implementation, the only purpose served by
483      * returning a cookie is to indicate that a conversation should
484      * continue on error.  Thus, the cookie can have a constant
485      * string.  If cookies are used for real, versioning so that KDCs
486      * can be upgraded, keying, expiration and many other issues need
487      * to be considered.
488      */
489     contents = strdup("MIT");
490     if (contents == NULL)
491         return ENOMEM;
492     pa = calloc(1, sizeof(krb5_pa_data));
493     if (pa == NULL) {
494         free(contents);
495         return ENOMEM;
496     }
497     pa->pa_type = KRB5_PADATA_FX_COOKIE;
498     pa->length = strlen(contents);
499     pa->contents = (unsigned char *) contents;
500     *cookie = pa;
501     return 0;
502 }