Imported Upstream version 1.10.2
[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 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
33 /*
34  * This function will find the fast and cookie padata and if fast is
35  * successfully processed, will throw away (and free) the outer
36  * request and update the pointer to point to the inner request.  The
37  * checksummed_data points to the data that is in the
38  * armored_fast_request checksum; either the pa-tgs-req or the
39  * kdc-req-body.
40  */
41
42 static krb5_error_code armor_ap_request
43 (struct kdc_request_state *state, krb5_fast_armor *armor)
44 {
45     krb5_error_code retval = 0;
46     krb5_auth_context authcontext = NULL;
47     krb5_ticket *ticket = NULL;
48     krb5_keyblock *subkey = NULL;
49
50     assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST);
51     krb5_clear_error_message(kdc_context);
52     retval = krb5_auth_con_init(kdc_context, &authcontext);
53     if (retval == 0)
54         retval = krb5_auth_con_setflags(kdc_context,
55                                         authcontext, 0); /*disable replay cache*/
56     retval = krb5_rd_req(kdc_context, &authcontext,
57                          &armor->armor_value, NULL /*server*/,
58                          kdc_active_realm->realm_keytab,  NULL, &ticket);
59     if (retval != 0) {
60         const char * errmsg = krb5_get_error_message(kdc_context, retval);
61         krb5_set_error_message(kdc_context, retval,
62                                _("%s while handling ap-request armor"),
63                                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 "
72                                      "than the local TGS"));
73             retval = KRB5KDC_ERR_SERVER_NOMATCH;
74         }
75     }
76     if (retval == 0) {
77         retval = krb5_auth_con_getrecvsubkey(kdc_context, authcontext, &subkey);
78         if (retval != 0 || subkey == NULL) {
79             krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY,
80                                    _("ap-request armor without subkey"));
81             retval = KRB5KDC_ERR_POLICY;
82         }
83     }
84     if (retval == 0)
85         retval = krb5_c_fx_cf2_simple(kdc_context,
86                                       subkey, "subkeyarmor",
87                                       ticket->enc_part2->session, "ticketarmor",
88                                       &state->armor_key);
89     if (ticket)
90         krb5_free_ticket(kdc_context, ticket);
91     if (subkey)
92         krb5_free_keyblock(kdc_context, subkey);
93     if (authcontext)
94         krb5_auth_con_free(kdc_context, authcontext);
95     return retval;
96 }
97
98 static krb5_error_code
99 encrypt_fast_reply(struct kdc_request_state *state,
100                    const krb5_fast_response *response,
101                    krb5_data **fx_fast_reply)
102 {
103     krb5_error_code retval = 0;
104     krb5_enc_data encrypted_reply;
105     krb5_data *encoded_response = NULL;
106     assert(state->armor_key);
107     retval = encode_krb5_fast_response(response, &encoded_response);
108     if (retval== 0)
109         retval = krb5_encrypt_helper(kdc_context, state->armor_key,
110                                      KRB5_KEYUSAGE_FAST_REP,
111                                      encoded_response, &encrypted_reply);
112     if (encoded_response)
113         krb5_free_data(kdc_context, encoded_response);
114     encoded_response = NULL;
115     if (retval == 0) {
116         retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply,
117                                               fx_fast_reply);
118         krb5_free_data_contents(kdc_context, &encrypted_reply.ciphertext);
119     }
120     return retval;
121 }
122
123
124 krb5_error_code
125 kdc_find_fast(krb5_kdc_req **requestptr,
126               krb5_data *checksummed_data,
127               krb5_keyblock *tgs_subkey,
128               krb5_keyblock *tgs_session,
129               struct kdc_request_state *state,
130               krb5_data **inner_body_out)
131 {
132     krb5_error_code retval = 0;
133     krb5_pa_data *fast_padata, *cookie_padata = NULL;
134     krb5_data scratch, *inner_body = NULL;
135     krb5_fast_req * fast_req = NULL;
136     krb5_kdc_req *request = *requestptr;
137     krb5_fast_armored_req *fast_armored_req = NULL;
138     krb5_checksum *cksum;
139     krb5_boolean cksum_valid;
140     krb5_keyblock empty_keyblock;
141
142     if (inner_body_out != NULL)
143         *inner_body_out = NULL;
144     scratch.data = NULL;
145     krb5_clear_error_message(kdc_context);
146     memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
147     fast_padata = find_pa_data(request->padata,
148                                KRB5_PADATA_FX_FAST);
149     if (fast_padata !=  NULL){
150         scratch.length = fast_padata->length;
151         scratch.data = (char *) fast_padata->contents;
152         retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
153         if (retval == 0 &&fast_armored_req->armor) {
154             switch (fast_armored_req->armor->armor_type) {
155             case KRB5_FAST_ARMOR_AP_REQUEST:
156                 if (tgs_subkey) {
157                     retval = KRB5KDC_ERR_PREAUTH_FAILED;
158                     krb5_set_error_message(kdc_context, retval,
159                                            _("Ap-request armor not permitted "
160                                              "with TGS"));
161                     break;
162                 }
163                 retval = armor_ap_request(state, fast_armored_req->armor);
164                 break;
165             default:
166                 krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
167                                        _("Unknown FAST armor type %d"),
168                                        fast_armored_req->armor->armor_type);
169                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
170             }
171         }
172         if (retval == 0 && !state->armor_key) {
173             if (tgs_subkey)
174                 retval = krb5_c_fx_cf2_simple(kdc_context,
175                                               tgs_subkey, "subkeyarmor",
176                                               tgs_session, "ticketarmor",
177                                               &state->armor_key);
178             else {
179                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
180                 krb5_set_error_message(kdc_context, retval,
181                                        _("No armor key but FAST armored "
182                                          "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             krb5_set_error_message(kdc_context, retval,
218                                    _("FAST req_checksum invalid; request "
219                                      "modified"));
220         }
221         if (retval == 0) {
222             if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) {
223                 retval = KRB5KDC_ERR_POLICY;
224                 krb5_set_error_message(kdc_context, retval,
225                                        _("Unkeyed checksum used in fast_req"));
226             }
227         }
228         if (retval == 0) {
229             if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
230                 retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
231         }
232         if (retval == 0)
233             cookie_padata = find_pa_data(fast_req->req_body->padata,
234                                          KRB5_PADATA_FX_COOKIE);
235         if (retval == 0) {
236             state->fast_options = fast_req->fast_options;
237             krb5_free_kdc_req( kdc_context, request);
238             *requestptr = fast_req->req_body;
239             fast_req->req_body = NULL;
240         }
241     }
242     else cookie_padata = find_pa_data(request->padata, KRB5_PADATA_FX_COOKIE);
243     if (retval == 0 && cookie_padata != NULL) {
244         krb5_pa_data *new_padata = malloc(sizeof (krb5_pa_data));
245         if (new_padata == NULL) {
246             retval = ENOMEM;
247         } else {
248             new_padata->pa_type = KRB5_PADATA_FX_COOKIE;
249             new_padata->length = cookie_padata->length;
250             new_padata->contents = malloc(new_padata->length);
251             if (new_padata->contents == NULL) {
252                 retval = ENOMEM;
253                 free(new_padata);
254             } else {
255                 memcpy(new_padata->contents, cookie_padata->contents,
256                        new_padata->length);
257                 state->cookie = new_padata;
258             }
259         }
260     }
261     if (retval == 0 && inner_body_out != NULL) {
262         *inner_body_out = inner_body;
263         inner_body = NULL;
264     }
265     krb5_free_data(kdc_context, inner_body);
266     if (fast_req)
267         krb5_free_fast_req( kdc_context, fast_req);
268     if (fast_armored_req)
269         krb5_free_fast_armored_req(kdc_context, fast_armored_req);
270     return retval;
271 }
272
273
274 krb5_error_code
275 kdc_make_rstate(struct kdc_request_state **out)
276 {
277     struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state));
278     if (state == NULL)
279         return ENOMEM;
280     memset( state, 0, sizeof(struct kdc_request_state));
281     *out = state;
282     return 0;
283 }
284
285 void
286 kdc_free_rstate (struct kdc_request_state *s)
287 {
288     if (s == NULL)
289         return;
290     if (s->armor_key)
291         krb5_free_keyblock(kdc_context, s->armor_key);
292     if (s->strengthen_key)
293         krb5_free_keyblock(kdc_context, s->strengthen_key);
294     if (s->cookie) {
295         free(s->cookie->contents);
296         free(s->cookie);
297     }
298     free(s);
299 }
300
301 krb5_error_code
302 kdc_fast_response_handle_padata(struct kdc_request_state *state,
303                                 krb5_kdc_req *request,
304                                 krb5_kdc_rep *rep, krb5_enctype enctype)
305 {
306     krb5_error_code retval = 0;
307     krb5_fast_finished finish;
308     krb5_fast_response fast_response;
309     krb5_data *encoded_ticket = NULL;
310     krb5_data *encrypted_reply = NULL;
311     krb5_pa_data *pa = NULL, **pa_array = NULL;
312     krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5;
313     krb5_pa_data *empty_padata[] = {NULL};
314     krb5_keyblock *strengthen_key = NULL;
315
316     if (!state->armor_key)
317         return 0;
318     memset(&finish, 0, sizeof(finish));
319     retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key);
320     if (retval == 0)
321         retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key);
322     if (retval == 0) {
323         state->strengthen_key = strengthen_key;
324         strengthen_key = NULL;
325     }
326
327     fast_response.padata = rep->padata;
328     if (fast_response.padata == NULL)
329         fast_response.padata = &empty_padata[0];
330     fast_response.strengthen_key = state->strengthen_key;
331     fast_response.nonce = request->nonce;
332     fast_response.finished = &finish;
333     finish.client = rep->client;
334     pa_array = calloc(3, sizeof(*pa_array));
335     if (pa_array == NULL)
336         retval = ENOMEM;
337     pa = calloc(1, sizeof(krb5_pa_data));
338     if (retval == 0 && pa == NULL)
339         retval = ENOMEM;
340     if (retval == 0)
341         retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec);
342     if (retval == 0)
343         retval = encode_krb5_ticket(rep->ticket, &encoded_ticket);
344     if (retval == 0)
345         retval = krb5int_c_mandatory_cksumtype(kdc_context,
346                                                state->armor_key->enctype,
347                                                &cksumtype);
348     if (retval == 0)
349         retval = krb5_c_make_checksum(kdc_context, cksumtype,
350                                       state->armor_key,
351                                       KRB5_KEYUSAGE_FAST_FINISHED,
352                                       encoded_ticket, &finish.ticket_checksum);
353     if (retval == 0)
354         retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply);
355     if (retval == 0) {
356         pa[0].pa_type = KRB5_PADATA_FX_FAST;
357         pa[0].length = encrypted_reply->length;
358         pa[0].contents = (unsigned char *)  encrypted_reply->data;
359         pa_array[0] = &pa[0];
360         krb5_free_pa_data(kdc_context, rep->padata);
361         rep->padata = pa_array;
362         pa_array = NULL;
363         free(encrypted_reply);
364         encrypted_reply = NULL;
365         pa = NULL;
366     }
367     if (pa)
368         free(pa);
369     if (pa_array)
370         free(pa_array);
371     if (encrypted_reply)
372         krb5_free_data(kdc_context, encrypted_reply);
373     if (encoded_ticket)
374         krb5_free_data(kdc_context, encoded_ticket);
375     if (strengthen_key != NULL)
376         krb5_free_keyblock(kdc_context, strengthen_key);
377     if (finish.ticket_checksum.contents)
378         krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum);
379     return retval;
380 }
381
382
383 /*
384  * We assume the caller is responsible for passing us an in_padata
385  * sufficient to include in a FAST error.  In the FAST case we will
386  * set *fast_edata_out to the edata to be included in the error; in
387  * the non-FAST case we will set it to NULL.
388  */
389 krb5_error_code
390 kdc_fast_handle_error(krb5_context context,
391                       struct kdc_request_state *state,
392                       krb5_kdc_req *request,
393                       krb5_pa_data  **in_padata, krb5_error *err,
394                       krb5_data **fast_edata_out)
395 {
396     krb5_error_code retval = 0;
397     krb5_fast_response resp;
398     krb5_error fx_error;
399     krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL;
400     krb5_pa_data pa[1];
401     krb5_pa_data *outer_pa[3], *cookie = NULL;
402     krb5_pa_data **inner_pa = NULL;
403     size_t size = 0;
404
405     *fast_edata_out = NULL;
406     memset(outer_pa, 0, sizeof(outer_pa));
407     if (!state || !state->armor_key)
408         return 0;
409     fx_error = *err;
410     fx_error.e_data.data = NULL;
411     fx_error.e_data.length = 0;
412     for (size = 0; in_padata&&in_padata[size]; size++);
413     size +=3;
414     inner_pa = calloc(size, sizeof(krb5_pa_data *));
415     if (inner_pa == NULL)
416         retval = ENOMEM;
417     if (retval == 0)
418         for (size=0; in_padata&&in_padata[size]; size++)
419             inner_pa[size] = in_padata[size];
420     if (retval == 0)
421         retval = encode_krb5_error(&fx_error, &encoded_fx_error);
422     if (retval == 0) {
423         pa[0].pa_type = KRB5_PADATA_FX_ERROR;
424         pa[0].length = encoded_fx_error->length;
425         pa[0].contents = (unsigned char *) encoded_fx_error->data;
426         inner_pa[size++] = &pa[0];
427         if (find_pa_data(inner_pa, KRB5_PADATA_FX_COOKIE) == NULL)
428             retval = kdc_preauth_get_cookie(state, &cookie);
429     }
430     if (cookie != NULL)
431         inner_pa[size++] = cookie;
432     if (retval == 0) {
433         resp.padata = inner_pa;
434         resp.nonce = request->nonce;
435         resp.strengthen_key = NULL;
436         resp.finished = NULL;
437     }
438     if (retval == 0)
439         retval = encrypt_fast_reply(state, &resp, &encrypted_reply);
440     if (inner_pa)
441         free(inner_pa); /*contained storage from caller and our stack*/
442     if (cookie) {
443         free(cookie->contents);
444         free(cookie);
445         cookie = NULL;
446     }
447     if (retval == 0) {
448         pa[0].pa_type = KRB5_PADATA_FX_FAST;
449         pa[0].length = encrypted_reply->length;
450         pa[0].contents = (unsigned char *) encrypted_reply->data;
451         outer_pa[0] = &pa[0];
452     }
453     retval = encode_krb5_padata_sequence(outer_pa, fast_edata_out);
454     if (encrypted_reply)
455         krb5_free_data(kdc_context, encrypted_reply);
456     if (encoded_fx_error)
457         krb5_free_data(kdc_context, encoded_fx_error);
458     return retval;
459 }
460
461 krb5_error_code
462 kdc_fast_handle_reply_key(struct kdc_request_state *state,
463                           krb5_keyblock *existing_key,
464                           krb5_keyblock **out_key)
465 {
466     krb5_error_code retval = 0;
467     if (state->armor_key)
468         retval = krb5_c_fx_cf2_simple(kdc_context,
469                                       state->strengthen_key, "strengthenkey",
470                                       existing_key,
471                                       "replykey", out_key);
472     else
473         retval = krb5_copy_keyblock(kdc_context, existing_key, out_key);
474     return retval;
475 }
476
477
478 krb5_error_code
479 kdc_preauth_get_cookie(struct kdc_request_state *state,
480                        krb5_pa_data **cookie)
481 {
482     char *contents;
483     krb5_pa_data *pa = NULL;
484     /* In our current implementation, the only purpose served by
485      * returning a cookie is to indicate that a conversation should
486      * continue on error.  Thus, the cookie can have a constant
487      * string.  If cookies are used for real, versioning so that KDCs
488      * can be upgraded, keying, expiration and many other issues need
489      * to be considered.
490      */
491     contents = strdup("MIT");
492     if (contents == NULL)
493         return ENOMEM;
494     pa = calloc(1, sizeof(krb5_pa_data));
495     if (pa == NULL) {
496         free(contents);
497         return ENOMEM;
498     }
499     pa->pa_type = KRB5_PADATA_FX_COOKIE;
500     pa->length = strlen(contents);
501     pa->contents = (unsigned char *) contents;
502     *cookie = pa;
503     return 0;
504 }