In lib/krb5/krb, ensure all function definition headers are in ANSI
[krb5.git] / src / lib / krb5 / krb / fast.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * lib/krb5/krb/fast.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  *
29  */
30
31 #include <k5-int.h>
32
33 /*
34  * It is possible to support sending a request that includes both a
35  * FAST and normal version.  This would complicate the
36  * pre-authentication logic significantly.  You would need to maintain
37  * two contexts, one for FAST and one for normal use.  In adition, you
38  * would need to manage the security issues surrounding downgrades.
39  * However trying FAST at all requires an armor key.  Generally in
40  * obtaining the armor key, the client learns enough to know that FAST
41  * is supported.  If not, the client can see FAST in the
42  * preauth_required error's padata and retry with FAST.  So, this
43  * implementation does not support FAST+normal.
44  *
45  * We store the outer version of the request to use .  The caller
46  * stores the inner version.  We handle the encoding of the request
47  * body (and request) and provide encoded request bodies for the
48  * caller to use as these may be used for checksums.  In the AS case
49  * we also evaluate whether to continue a conversation as one of the
50  * important questions there is the presence of a cookie.
51  */
52 #include "fast.h"
53 #include "int-proto.h"
54
55
56 static krb5_error_code
57 fast_armor_ap_request(krb5_context context,
58                       struct krb5int_fast_request_state *state,
59                       krb5_ccache ccache, krb5_data *target_realm)
60 {
61     krb5_error_code retval = 0;
62     krb5_creds creds, *out_creds = NULL;
63     krb5_auth_context authcontext = NULL;
64     krb5_data encoded_authenticator;
65     krb5_fast_armor *armor = NULL;
66     krb5_keyblock *subkey = NULL, *armor_key = NULL;
67     encoded_authenticator.data = NULL;
68     memset(&creds, 0, sizeof(creds));
69     retval = krb5_tgtname(context, target_realm, target_realm, &creds.server);
70     if (retval ==0)
71         retval = krb5_cc_get_principal(context, ccache, &creds.client);
72     if (retval == 0)
73         retval = krb5_get_credentials(context, 0, ccache,  &creds, &out_creds);
74     if (retval == 0)
75         retval = krb5_mk_req_extended(context, &authcontext, AP_OPTS_USE_SUBKEY, NULL /*data*/,
76                                       out_creds, &encoded_authenticator);
77     if (retval == 0)
78         retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
79     if (retval == 0)
80         retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
81                                       &out_creds->keyblock, "ticketarmor", &armor_key);
82     if (retval == 0) {
83         armor = calloc(1, sizeof(krb5_fast_armor));
84         if (armor == NULL)
85             retval = ENOMEM;
86     }
87     if (retval == 0) {
88         armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST;
89         armor->armor_value = encoded_authenticator;
90         encoded_authenticator.data = NULL;
91         encoded_authenticator.length = 0;
92         state->armor = armor;
93         armor = NULL;
94         state->armor_key = armor_key;
95         armor_key = NULL;
96     }
97     krb5_free_keyblock(context, armor_key);
98     krb5_free_keyblock(context, subkey);
99     if (out_creds)
100         krb5_free_creds(context, out_creds);
101     krb5_free_cred_contents(context, &creds);
102     if (encoded_authenticator.data)
103         krb5_free_data_contents(context, &encoded_authenticator);
104     krb5_auth_con_free(context, authcontext);
105     return retval;
106 }
107
108 krb5_error_code
109 krb5int_fast_prep_req_body(krb5_context context, struct krb5int_fast_request_state *state,
110                            krb5_kdc_req *request, krb5_data **encoded_request_body)
111 {
112     krb5_error_code retval = 0;
113     krb5_data *local_encoded_request_body = NULL;
114     assert(state != NULL);
115     *encoded_request_body = NULL;
116     if (state->armor_key == NULL) {
117         return   encode_krb5_kdc_req_body(request, encoded_request_body);
118     }
119     state->fast_outer_request = *request;
120     state->fast_outer_request.padata = NULL;
121     if (retval == 0)
122         retval = encode_krb5_kdc_req_body(&state->fast_outer_request,
123                                           &local_encoded_request_body);
124     if (retval == 0) {
125         *encoded_request_body = local_encoded_request_body;
126         local_encoded_request_body = NULL;
127     }
128     if (local_encoded_request_body != NULL)
129         krb5_free_data(context, local_encoded_request_body);
130     return retval;
131 }
132
133 krb5_error_code
134 krb5int_fast_as_armor(krb5_context context,
135                       struct krb5int_fast_request_state *state,
136                       krb5_gic_opt_ext *opte,
137                       krb5_kdc_req *request)
138 {
139     krb5_error_code retval = 0;
140     krb5_ccache ccache = NULL;
141     krb5_clear_error_message(context);
142     if (opte->opt_private->fast_ccache_name) {
143         retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
144                                  &ccache);
145         if (retval==0)
146             retval = fast_armor_ap_request(context, state, ccache,
147                                            krb5_princ_realm(context, request->server));
148         if (retval != 0) {
149             const char * errmsg;
150             errmsg = krb5_get_error_message(context, retval);
151             if (errmsg) {
152                 krb5_set_error_message(context, retval, "%s constructing AP-REQ armor", errmsg);
153                 krb5_free_error_message(context, errmsg);
154             }
155         }
156     }
157     if (ccache)
158         krb5_cc_close(context, ccache);
159     return retval;
160 }
161
162
163 krb5_error_code
164 krb5int_fast_prep_req(krb5_context context,
165                       struct krb5int_fast_request_state *state,
166                       krb5_kdc_req *request,
167                       const krb5_data *to_be_checksummed,
168                       kdc_req_encoder_proc encoder,
169                       krb5_data **encoded_request)
170 {
171     krb5_error_code retval = 0;
172     krb5_pa_data *pa_array[2];
173     krb5_pa_data pa[2];
174     krb5_fast_req fast_req;
175     krb5_fast_armored_req *armored_req = NULL;
176     krb5_data *encoded_fast_req = NULL;
177     krb5_data *encoded_armored_req = NULL;
178     krb5_data *local_encoded_result = NULL;
179     krb5_cksumtype cksumtype;
180     krb5_data random_data;
181     char random_buf[4];
182
183
184     assert(state != NULL);
185     assert(state->fast_outer_request.padata == NULL);
186     memset(pa_array, 0, sizeof pa_array);
187     if (state->armor_key == NULL) {
188         return encoder(request, encoded_request);
189     }
190 /* Fill in a fresh random nonce for each inner request*/
191     random_data.length = 4;
192     random_data.data = (char *)random_buf;
193     retval = krb5_c_random_make_octets(context, &random_data);
194     if (retval == 0) {
195         request->nonce = 0x7fffffff & load_32_n(random_buf);
196         state->nonce = request->nonce;
197     }
198     fast_req.req_body =  request;
199     if (fast_req.req_body->padata == NULL) {
200         fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
201         if (fast_req.req_body->padata == NULL)
202             retval = ENOMEM;
203     }
204     fast_req.fast_options = state->fast_options;
205     if (retval == 0)
206         retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req);
207     if (retval == 0) {
208         armored_req = calloc(1, sizeof(krb5_fast_armored_req));
209         if (armored_req == NULL)
210             retval = ENOMEM;
211     }
212     if (retval == 0)
213         armored_req->armor = state->armor;
214     if (retval == 0)
215         retval = krb5int_c_mandatory_cksumtype(context, state->armor_key->enctype,
216                                                &cksumtype);
217     if (retval ==0)
218         retval = krb5_c_make_checksum(context, cksumtype, state->armor_key,
219                                       KRB5_KEYUSAGE_FAST_REQ_CHKSUM, to_be_checksummed,
220                                       &armored_req->req_checksum);
221     if (retval == 0)
222         retval = krb5_encrypt_helper(context, state->armor_key,
223                                      KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req,
224                                      &armored_req->enc_part);
225     if (retval == 0)
226         retval = encode_krb5_pa_fx_fast_request(armored_req, &encoded_armored_req);
227     if (retval==0) {
228         pa[0].pa_type = KRB5_PADATA_FX_FAST;
229         pa[0].contents = (unsigned char *) encoded_armored_req->data;
230         pa[0].length = encoded_armored_req->length;
231         pa_array[0] = &pa[0];
232     }
233     state->fast_outer_request.padata = pa_array;
234     if(retval == 0)
235         retval = encoder(&state->fast_outer_request, &local_encoded_result);
236     if (retval == 0) {
237         *encoded_request = local_encoded_result;
238         local_encoded_result = NULL;
239     }
240     if (encoded_armored_req)
241         krb5_free_data(context, encoded_armored_req);
242     if (armored_req) {
243         armored_req->armor = NULL; /*owned by state*/
244         krb5_free_fast_armored_req(context, armored_req);
245     }
246     if (encoded_fast_req)
247         krb5_free_data(context, encoded_fast_req);
248     if (local_encoded_result)
249         krb5_free_data(context, local_encoded_result);
250     state->fast_outer_request.padata = NULL;
251     return retval;
252 }
253
254 static krb5_error_code
255 decrypt_fast_reply(krb5_context context,
256                    struct krb5int_fast_request_state *state,
257                    krb5_pa_data **in_padata,
258                    krb5_fast_response **response)
259 {
260     krb5_error_code retval = 0;
261     krb5_data scratch;
262     krb5_enc_data *encrypted_response = NULL;
263     krb5_pa_data *fx_reply = NULL;
264     krb5_fast_response *local_resp = NULL;
265     assert(state != NULL);
266     assert(state->armor_key);
267     fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
268     if (fx_reply == NULL)
269         retval = KRB5_ERR_FAST_REQUIRED;
270     if (retval == 0) {
271         scratch.data = (char *) fx_reply->contents;
272         scratch.length = fx_reply->length;
273         retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
274     }
275     scratch.data = NULL;
276     if (retval == 0) {
277         scratch.data = malloc(encrypted_response->ciphertext.length);
278         if (scratch.data == NULL)
279             retval = ENOMEM;
280         scratch.length = encrypted_response->ciphertext.length;
281     }
282     if (retval == 0)
283         retval = krb5_c_decrypt(context, state->armor_key,
284                                 KRB5_KEYUSAGE_FAST_REP, NULL,
285                                 encrypted_response, &scratch);
286     if (retval != 0) {
287         const char * errmsg;
288         errmsg = krb5_get_error_message(context, retval);
289         krb5_set_error_message(context, retval, "%s while decrypting FAST reply", errmsg);
290         krb5_free_error_message(context, errmsg);
291     }
292     if (retval == 0)
293         retval = decode_krb5_fast_response(&scratch, &local_resp);
294     if (retval == 0) {
295         if (local_resp->nonce != state->nonce) {
296             retval = KRB5_KDCREP_MODIFIED;
297             krb5_set_error_message(context, retval, "nonce modified in FAST response: KDC response modified");
298         }
299     }
300     if (retval == 0) {
301         *response = local_resp;
302         local_resp = NULL;
303     }
304     if (scratch.data)
305         free(scratch.data);
306     if (encrypted_response)
307         krb5_free_enc_data(context, encrypted_response);
308     if (local_resp)
309         krb5_free_fast_response(context, local_resp);
310     return retval;
311 }
312
313 /*
314  * FAST separates two concepts: the set of padata we're using to
315  * decide what pre-auth mechanisms to use and the set of padata we're
316  * making available to mechanisms in order for them to respond to an
317  * error.  The plugin interface in March 2009 does not permit
318  * separating these concepts for the plugins.  This function makes
319  * both available for future revisions to the plugin interface.  It
320  * also re-encodes the padata from the current error as a encoded
321  * typed-data and puts that in the e_data field.  That will allow
322  * existing plugins with the old interface to find the error data.
323  * The output parameter out_padata contains the padata from the error
324  * whenever padata  is available (all the time with fast).
325  */
326 krb5_error_code
327 krb5int_fast_process_error(krb5_context context,
328                            struct krb5int_fast_request_state *state,
329                            krb5_error **err_replyptr,
330                            krb5_pa_data ***out_padata,
331                            krb5_boolean *retry)
332 {
333     krb5_error_code retval = 0;
334     krb5_error *err_reply = *err_replyptr;
335     *out_padata = NULL;
336     *retry = 0;
337     if (state->armor_key) {
338         krb5_pa_data *fx_error_pa;
339         krb5_pa_data **result = NULL;
340         krb5_data scratch, *encoded_td = NULL;
341         krb5_error *fx_error = NULL;
342         krb5_fast_response *fast_response = NULL;
343         retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
344         if (retval == 0)
345             retval = decrypt_fast_reply(context, state, result, &fast_response);
346         if (retval) {
347             /*This can happen if the KDC does not understand FAST. We
348              * don't expect that, but treating it as the fatal error
349              * indicated by the KDC seems reasonable.
350              */
351             *retry = 0;
352             krb5_free_pa_data(context, result);
353             return 0;
354         }
355         krb5_free_pa_data(context, result);
356         result = NULL;
357         if (retval == 0) {
358             fx_error_pa = krb5int_find_pa_data(context, fast_response->padata, KRB5_PADATA_FX_ERROR);
359             if (fx_error_pa == NULL) {
360                 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, "Expecting FX_ERROR pa-data inside FAST container");
361                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
362             }
363         }
364         if (retval == 0) {
365             scratch.data = (char *) fx_error_pa->contents;
366             scratch.length = fx_error_pa->length;
367             retval = decode_krb5_error(&scratch, &fx_error);
368         }
369         /*
370          * krb5_pa_data and krb5_typed_data are safe to cast between:
371          * they have the same type fields in the same order.
372          * (krb5_preauthtype is a krb5_int32).  If krb5_typed_data is
373          * ever changed then this will need to be a copy not a cast.
374          */
375         if (retval == 0)
376             retval = encode_krb5_typed_data( (krb5_typed_data **) fast_response->padata,
377                                              &encoded_td);
378         if (retval == 0) {
379             fx_error->e_data = *encoded_td;
380             free(encoded_td); /*contents owned by fx_error*/
381             encoded_td = NULL;
382             krb5_free_error(context, err_reply);
383             *err_replyptr = fx_error;
384             fx_error = NULL;
385             *out_padata = fast_response->padata;
386             fast_response->padata = NULL;
387             /*
388              * If there is more than the fx_error padata, then we want
389              * to retry the error if a cookie is present
390              */
391             *retry = (*out_padata)[1] != NULL;
392             if (krb5int_find_pa_data(context, *out_padata, KRB5_PADATA_FX_COOKIE) == NULL)
393                 *retry = 0;
394         }
395         if (fx_error)
396             krb5_free_error(context, fx_error);
397         krb5_free_fast_response(context, fast_response);
398     } else { /*not FAST*/
399         *retry = (err_reply->e_data.length > 0);
400         if ((err_reply->error == KDC_ERR_PREAUTH_REQUIRED
401              ||err_reply->error == KDC_ERR_PREAUTH_FAILED) && err_reply->e_data.length) {
402             krb5_pa_data **result = NULL;
403             retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
404             if (retval == 0)
405                 if (retval == 0) {
406                     *out_padata = result;
407
408                     return 0;
409                 }
410             krb5_free_pa_data(context, result);
411             krb5_set_error_message(context, retval,
412                                    "Error decoding padata in error reply");
413             return retval;
414         }
415     }
416     return retval;
417 }
418
419
420 krb5_error_code
421 krb5int_fast_process_response(krb5_context context,
422                               struct krb5int_fast_request_state *state,
423                               krb5_kdc_rep *resp,
424                               krb5_keyblock **strengthen_key)
425 {
426     krb5_error_code retval = 0;
427     krb5_fast_response *fast_response = NULL;
428     krb5_data *encoded_ticket = NULL;
429     krb5_boolean cksum_valid;
430     krb5_clear_error_message(context);
431     *strengthen_key = NULL;
432     if (state->armor_key == 0)
433         return 0;
434     retval = decrypt_fast_reply(context, state, resp->padata,
435                                 &fast_response);
436     if (retval == 0) {
437         if (fast_response->finished == 0) {
438             retval = KRB5_KDCREP_MODIFIED;
439             krb5_set_error_message(context, retval, "FAST response missing finish message in KDC reply");
440         }
441     }
442     if (retval == 0)
443         retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
444     if (retval == 0)
445         retval = krb5_c_verify_checksum(context, state->armor_key,
446                                         KRB5_KEYUSAGE_FAST_FINISHED,
447                                         encoded_ticket,
448                                         &fast_response->finished->ticket_checksum,
449                                         &cksum_valid);
450     if (retval == 0 && cksum_valid == 0) {
451         retval = KRB5_KDCREP_MODIFIED;
452         krb5_set_error_message(context, retval, "ticket modified in KDC reply");
453     }
454     if (retval == 0) {
455         krb5_free_principal(context, resp->client);
456         resp->client = fast_response->finished->client;
457         fast_response->finished->client = NULL;
458         *strengthen_key = fast_response->strengthen_key;
459         fast_response->strengthen_key = NULL;
460         krb5_free_pa_data(context, resp->padata);
461         resp->padata = fast_response->padata;
462         fast_response->padata = NULL;
463     }
464     if (fast_response)
465         krb5_free_fast_response(context, fast_response);
466     if (encoded_ticket)
467         krb5_free_data(context, encoded_ticket);
468     return retval;
469 }
470
471 krb5_error_code
472 krb5int_fast_reply_key(krb5_context context,
473                        krb5_keyblock *strengthen_key,
474                        krb5_keyblock *existing_key,
475                        krb5_keyblock *out_key)
476 {
477     krb5_keyblock *key = NULL;
478     krb5_error_code retval = 0;
479     krb5_free_keyblock_contents(context, out_key);
480     if (strengthen_key) {
481         retval = krb5_c_fx_cf2_simple(context, strengthen_key,
482                                       "strengthenkey", existing_key, "replykey", &key);
483         if (retval == 0) {
484             *out_key = *key;
485             free(key);
486         }
487     } else {
488         retval = krb5_copy_keyblock_contents(context, existing_key, out_key);
489     }
490     return retval;
491 }
492
493
494 krb5_error_code
495 krb5int_fast_make_state( krb5_context context, struct krb5int_fast_request_state **state)
496 {
497     struct krb5int_fast_request_state *local_state ;
498     local_state = malloc(sizeof *local_state);
499     if (local_state == NULL)
500         return ENOMEM;
501     memset(local_state, 0, sizeof(*local_state));
502     *state = local_state;
503     return 0;
504 }
505
506 void
507 krb5int_fast_free_state( krb5_context context, struct krb5int_fast_request_state *state)
508 {
509     /*We are responsible for none of the store in the fast_outer_req*/
510     krb5_free_keyblock(context, state->armor_key);
511     krb5_free_fast_armor(context, state->armor);
512     free(state);
513 }
514
515 krb5_pa_data *
516 krb5int_find_pa_data(krb5_context context, krb5_pa_data *const *padata,
517                      krb5_preauthtype pa_type)
518 {
519     krb5_pa_data * const *tmppa;
520
521     if (padata == NULL)
522         return NULL;
523
524     for (tmppa = padata; *tmppa != NULL; tmppa++) {
525         if ((*tmppa)->pa_type == pa_type)
526             break;
527     }
528
529     return *tmppa;
530 }