xref: /freebsd/crypto/krb5/src/kdc/kdc_preauth.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_preauth.c - Preauthentication routines for the KDC */
3 /*
4  * Copyright 1995, 2003, 2007, 2009 by the Massachusetts Institute of
5  * Technology.  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  * Copyright (C) 1998 by the FundsXpress, INC.
28  *
29  * All rights reserved.
30  *
31  * Export of this software from the United States of America may require
32  * a specific license from the United States Government.  It is the
33  * responsibility of any person or organization contemplating export to
34  * obtain such a license before exporting.
35  *
36  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37  * distribute this software and its documentation for any purpose and
38  * without fee is hereby granted, provided that the above copyright
39  * notice appear in all copies and that both that copyright notice and
40  * this permission notice appear in supporting documentation, and that
41  * the name of FundsXpress. not be used in advertising or publicity pertaining
42  * to distribution of the software without specific, written prior
43  * permission.  FundsXpress makes no representations about the suitability of
44  * this software for any purpose.  It is provided "as is" without express
45  * or implied warranty.
46  *
47  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50  */
51 /*
52  * Copyright (c) 2006-2008, Novell, Inc.
53  * All rights reserved.
54  *
55  * Redistribution and use in source and binary forms, with or without
56  * modification, are permitted provided that the following conditions are met:
57  *
58  *   * Redistributions of source code must retain the above copyright notice,
59  *       this list of conditions and the following disclaimer.
60  *   * Redistributions in binary form must reproduce the above copyright
61  *       notice, this list of conditions and the following disclaimer in the
62  *       documentation and/or other materials provided with the distribution.
63  *   * The copyright holder's name is not used to endorse or promote products
64  *       derived from this software without specific prior written permission.
65  *
66  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
67  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
68  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
69  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
70  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
71  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
72  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
73  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
74  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
75  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
76  * POSSIBILITY OF SUCH DAMAGE.
77  */
78 
79 #include "k5-int.h"
80 #include "kdc_util.h"
81 #include "extern.h"
82 #include <stdio.h>
83 #include "adm_proto.h"
84 
85 #include <syslog.h>
86 
87 #include <assert.h>
88 #include <krb5/kdcpreauth_plugin.h>
89 
90 /* Let freshness tokens be valid for ten minutes. */
91 #define FRESHNESS_LIFETIME 600
92 
93 typedef struct preauth_system_st {
94     const char *name;
95     int type;
96     int flags;
97     krb5_kdcpreauth_moddata moddata;
98     krb5_kdcpreauth_init_fn init;
99     krb5_kdcpreauth_fini_fn fini;
100     krb5_kdcpreauth_edata_fn get_edata;
101     krb5_kdcpreauth_verify_fn verify_padata;
102     krb5_kdcpreauth_return_fn return_padata;
103     krb5_kdcpreauth_free_modreq_fn free_modreq;
104     krb5_kdcpreauth_loop_fn loop;
105 } preauth_system;
106 
107 static preauth_system *preauth_systems;
108 static size_t n_preauth_systems;
109 
110 static krb5_error_code
111 make_etype_info(krb5_context context, krb5_boolean etype_info2,
112                 krb5_principal client, krb5_key_data *client_key,
113                 krb5_enctype enctype, krb5_data **der_out);
114 
115 /* Get all available kdcpreauth vtables and a count of preauth types they
116  * support.  Return an empty list on failure. */
117 static void
get_plugin_vtables(krb5_context context,struct krb5_kdcpreauth_vtable_st ** vtables_out,size_t * n_tables_out,size_t * n_systems_out)118 get_plugin_vtables(krb5_context context,
119                    struct krb5_kdcpreauth_vtable_st **vtables_out,
120                    size_t *n_tables_out, size_t *n_systems_out)
121 {
122     krb5_plugin_initvt_fn *plugins = NULL, *pl;
123     struct krb5_kdcpreauth_vtable_st *vtables;
124     size_t count, n_tables, n_systems, i;
125 
126     *vtables_out = NULL;
127     *n_tables_out = *n_systems_out = 0;
128 
129     /* Auto-register encrypted challenge and (if possible) pkinit. */
130     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit",
131                            "preauth");
132     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp",
133                            "preauth");
134     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "spake",
135                            "preauth");
136     k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
137                        "encrypted_challenge",
138                        kdcpreauth_encrypted_challenge_initvt);
139     k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
140                        "encrypted_timestamp",
141                        kdcpreauth_encrypted_timestamp_initvt);
142 
143     if (k5_plugin_load_all(context, PLUGIN_INTERFACE_KDCPREAUTH, &plugins))
144         return;
145     for (count = 0; plugins[count]; count++);
146     vtables = calloc(count + 1, sizeof(*vtables));
147     if (vtables == NULL)
148         goto cleanup;
149     for (pl = plugins, n_tables = 0; *pl != NULL; pl++) {
150         if ((*pl)(context, 1, 2, (krb5_plugin_vtable)&vtables[n_tables]) == 0)
151             n_tables++;
152     }
153     for (i = 0, n_systems = 0; i < n_tables; i++) {
154         for (count = 0; vtables[i].pa_type_list[count] != 0; count++);
155         n_systems += count;
156     }
157     *vtables_out = vtables;
158     *n_tables_out = n_tables;
159     *n_systems_out = n_systems;
160 
161 cleanup:
162     k5_plugin_free_modules(context, plugins);
163 }
164 
165 /* Make a list of realm names.  The caller should free the list container but
166  * not the list elements (which are aliases into kdc_realmlist). */
167 static krb5_error_code
get_realm_names(struct server_handle * handle,const char *** list_out)168 get_realm_names(struct server_handle *handle, const char ***list_out)
169 {
170     const char **list;
171     int i;
172 
173     list = calloc(handle->kdc_numrealms + 1, sizeof(*list));
174     if (list == NULL)
175         return ENOMEM;
176     for (i = 0; i < handle->kdc_numrealms; i++)
177         list[i] = handle->kdc_realmlist[i]->realm_name;
178     list[i] = NULL;
179     *list_out = list;
180     return 0;
181 }
182 
183 void
load_preauth_plugins(struct server_handle * handle,krb5_context context,verto_ctx * ctx)184 load_preauth_plugins(struct server_handle *handle, krb5_context context,
185                      verto_ctx *ctx)
186 {
187     krb5_error_code ret;
188     struct krb5_kdcpreauth_vtable_st *vtables = NULL, *vt;
189     size_t n_systems, n_tables, i, j;
190     krb5_kdcpreauth_moddata moddata;
191     const char **realm_names = NULL, *emsg;
192     preauth_system *sys;
193 
194     /* Get all available kdcpreauth vtables. */
195     get_plugin_vtables(context, &vtables, &n_tables, &n_systems);
196 
197     /* Allocate the list of static and plugin preauth systems. */
198     preauth_systems = calloc(n_systems + 1, sizeof(preauth_system));
199     if (preauth_systems == NULL)
200         goto cleanup;
201 
202     if (get_realm_names(handle, &realm_names))
203         goto cleanup;
204 
205     /* Add the dynamically-loaded mechanisms to the list. */
206     n_systems = 0;
207     for (i = 0; i < n_tables; i++) {
208         /* Try to initialize this module. */
209         vt = &vtables[i];
210         moddata = NULL;
211         if (vt->init) {
212             ret = vt->init(context, &moddata, realm_names);
213             if (ret) {
214                 emsg = krb5_get_error_message(context, ret);
215                 krb5_klog_syslog(LOG_ERR, _("preauth %s failed to "
216                                             "initialize: %s"), vt->name, emsg);
217                 krb5_free_error_message(context, emsg);
218                 continue;
219             }
220         }
221 
222         if (vt->loop) {
223             ret = vt->loop(context, moddata, ctx);
224             if (ret) {
225                 emsg = krb5_get_error_message(context, ret);
226                 krb5_klog_syslog(LOG_ERR, _("preauth %s failed to setup "
227                                             "loop: %s"), vt->name, emsg);
228                 krb5_free_error_message(context, emsg);
229                 if (vt->fini)
230                     vt->fini(context, moddata);
231                 continue;
232             }
233         }
234 
235         /* Add this module to the systems list once for each pa type. */
236         for (j = 0; vt->pa_type_list[j] != 0; j++) {
237             sys = &preauth_systems[n_systems];
238             sys->name = vt->name;
239             sys->type = vt->pa_type_list[j];
240             sys->flags = (vt->flags) ? vt->flags(context, sys->type) : 0;
241             sys->moddata = moddata;
242             sys->init = vt->init;
243             /* Only call fini once for each plugin. */
244             sys->fini = (j == 0) ? vt->fini : NULL;
245             sys->get_edata = vt->edata;
246             sys->verify_padata = vt->verify;
247             sys->return_padata = vt->return_padata;
248             sys->free_modreq = vt->free_modreq;
249             sys->loop = vt->loop;
250             n_systems++;
251         }
252     }
253     n_preauth_systems = n_systems;
254     /* Add the end-of-list marker. */
255     preauth_systems[n_systems].name = "[end]";
256     preauth_systems[n_systems].type = -1;
257 
258 cleanup:
259     free(vtables);
260     free(realm_names);
261 }
262 
263 void
unload_preauth_plugins(krb5_context context)264 unload_preauth_plugins(krb5_context context)
265 {
266     size_t i;
267 
268     for (i = 0; i < n_preauth_systems; i++) {
269         if (preauth_systems[i].fini)
270             preauth_systems[i].fini(context, preauth_systems[i].moddata);
271     }
272     free(preauth_systems);
273     preauth_systems = NULL;
274     n_preauth_systems = 0;
275 }
276 
277 /*
278  * The make_padata_context() function creates a space for storing any
279  * request-specific module data which will be needed by return_padata() later.
280  * Each preauth type gets a storage location of its own.
281  */
282 struct request_pa_context {
283     int n_contexts;
284     struct {
285         preauth_system *pa_system;
286         krb5_kdcpreauth_modreq modreq;
287     } *contexts;
288 };
289 
290 static krb5_error_code
make_padata_context(krb5_context context,void ** padata_context)291 make_padata_context(krb5_context context, void **padata_context)
292 {
293     int i;
294     struct request_pa_context *ret;
295 
296     ret = malloc(sizeof(*ret));
297     if (ret == NULL) {
298         return ENOMEM;
299     }
300 
301     ret->n_contexts = n_preauth_systems;
302     ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
303     if (ret->contexts == NULL) {
304         free(ret);
305         return ENOMEM;
306     }
307 
308     memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
309 
310     for (i = 0; i < ret->n_contexts; i++) {
311         ret->contexts[i].pa_system = &preauth_systems[i];
312         ret->contexts[i].modreq = NULL;
313     }
314 
315     *padata_context = ret;
316 
317     return 0;
318 }
319 
320 /*
321  * The free_padata_context function frees any context information pointers
322  * which the check_padata() function created but which weren't already cleaned
323  * up by return_padata().
324  */
325 void
free_padata_context(krb5_context kcontext,void * padata_context)326 free_padata_context(krb5_context kcontext, void *padata_context)
327 {
328     struct request_pa_context *context = padata_context;
329     preauth_system *sys;
330     int i;
331 
332     if (context == NULL)
333         return;
334     for (i = 0; i < context->n_contexts; i++) {
335         sys = context->contexts[i].pa_system;
336         if (!sys->free_modreq || !context->contexts[i].modreq)
337             continue;
338         sys->free_modreq(kcontext, sys->moddata, context->contexts[i].modreq);
339         context->contexts[i].modreq = NULL;
340     }
341 
342     free(context->contexts);
343     free(context);
344 }
345 
346 static krb5_deltat
max_time_skew(krb5_context context,krb5_kdcpreauth_rock rock)347 max_time_skew(krb5_context context, krb5_kdcpreauth_rock rock)
348 {
349     return context->clockskew;
350 }
351 
352 static krb5_error_code
client_keys(krb5_context context,krb5_kdcpreauth_rock rock,krb5_keyblock ** keys_out)353 client_keys(krb5_context context, krb5_kdcpreauth_rock rock,
354             krb5_keyblock **keys_out)
355 {
356     krb5_kdc_req *request = rock->request;
357     krb5_db_entry *client = rock->client;
358     krb5_keyblock *keys, key;
359     krb5_key_data *entry_key;
360     int i, k;
361 
362     keys = calloc(request->nktypes + 1, sizeof(krb5_keyblock));
363     if (keys == NULL)
364         return ENOMEM;
365 
366     k = 0;
367     for (i = 0; i < request->nktypes; i++) {
368         entry_key = NULL;
369         if (krb5_dbe_find_enctype(context, client, request->ktype[i],
370                                   -1, 0, &entry_key) != 0)
371             continue;
372         if (krb5_dbe_decrypt_key_data(context, NULL, entry_key,
373                                       &key, NULL) != 0)
374             continue;
375         keys[k++] = key;
376     }
377     if (k == 0) {
378         free(keys);
379         return ENOENT;
380     }
381     *keys_out = keys;
382     return 0;
383 }
384 
free_keys(krb5_context context,krb5_kdcpreauth_rock rock,krb5_keyblock * keys)385 static void free_keys(krb5_context context, krb5_kdcpreauth_rock rock,
386                       krb5_keyblock *keys)
387 {
388     krb5_keyblock *k;
389 
390     if (keys == NULL)
391         return;
392     for (k = keys; k->enctype != 0; k++)
393         krb5_free_keyblock_contents(context, k);
394     free(keys);
395 }
396 
397 static krb5_data *
request_body(krb5_context context,krb5_kdcpreauth_rock rock)398 request_body(krb5_context context, krb5_kdcpreauth_rock rock)
399 {
400     return rock->inner_body;
401 }
402 
403 static krb5_keyblock *
fast_armor(krb5_context context,krb5_kdcpreauth_rock rock)404 fast_armor(krb5_context context, krb5_kdcpreauth_rock rock)
405 {
406     return rock->rstate->armor_key;
407 }
408 
409 static krb5_error_code
get_string(krb5_context context,krb5_kdcpreauth_rock rock,const char * key,char ** value_out)410 get_string(krb5_context context, krb5_kdcpreauth_rock rock, const char *key,
411            char **value_out)
412 {
413     return krb5_dbe_get_string(context, rock->client, key, value_out);
414 }
415 
416 static void
free_string(krb5_context context,krb5_kdcpreauth_rock rock,char * string)417 free_string(krb5_context context, krb5_kdcpreauth_rock rock, char *string)
418 {
419     krb5_dbe_free_string(context, string);
420 }
421 
422 static void *
client_entry(krb5_context context,krb5_kdcpreauth_rock rock)423 client_entry(krb5_context context, krb5_kdcpreauth_rock rock)
424 {
425     return rock->client;
426 }
427 
428 static verto_ctx *
event_context(krb5_context context,krb5_kdcpreauth_rock rock)429 event_context(krb5_context context, krb5_kdcpreauth_rock rock)
430 {
431     return rock->vctx;
432 }
433 
434 static krb5_boolean
have_client_keys(krb5_context context,krb5_kdcpreauth_rock rock)435 have_client_keys(krb5_context context, krb5_kdcpreauth_rock rock)
436 {
437     krb5_kdc_req *request = rock->request;
438     krb5_key_data *kd;
439     int i;
440 
441     for (i = 0; i < request->nktypes; i++) {
442         if (krb5_dbe_find_enctype(context, rock->client, request->ktype[i],
443                                   -1, 0, &kd) == 0)
444             return TRUE;
445     }
446     return FALSE;
447 }
448 
449 static const krb5_keyblock *
client_keyblock(krb5_context context,krb5_kdcpreauth_rock rock)450 client_keyblock(krb5_context context, krb5_kdcpreauth_rock rock)
451 {
452     if (rock->client_keyblock->enctype == ENCTYPE_NULL)
453         return NULL;
454     return rock->client_keyblock;
455 }
456 
457 static krb5_error_code
add_auth_indicator(krb5_context context,krb5_kdcpreauth_rock rock,const char * indicator)458 add_auth_indicator(krb5_context context, krb5_kdcpreauth_rock rock,
459                    const char *indicator)
460 {
461     return authind_add(context, indicator, rock->auth_indicators);
462 }
463 
464 static krb5_boolean
get_cookie(krb5_context context,krb5_kdcpreauth_rock rock,krb5_preauthtype pa_type,krb5_data * out)465 get_cookie(krb5_context context, krb5_kdcpreauth_rock rock,
466            krb5_preauthtype pa_type, krb5_data *out)
467 {
468     return kdc_fast_search_cookie(rock->rstate, pa_type, out);
469 }
470 
471 static krb5_error_code
set_cookie(krb5_context context,krb5_kdcpreauth_rock rock,krb5_preauthtype pa_type,const krb5_data * data)472 set_cookie(krb5_context context, krb5_kdcpreauth_rock rock,
473            krb5_preauthtype pa_type, const krb5_data *data)
474 {
475     return kdc_fast_set_cookie(rock->rstate, pa_type, data);
476 }
477 
478 static krb5_boolean
match_client(krb5_context context,krb5_kdcpreauth_rock rock,krb5_principal princ)479 match_client(krb5_context context, krb5_kdcpreauth_rock rock,
480              krb5_principal princ)
481 {
482     krb5_db_entry *ent;
483     krb5_boolean match = FALSE;
484     krb5_principal req_client = rock->request->client;
485     krb5_principal client = rock->client->princ;
486 
487     /* Check for a direct match against the request principal or
488      * the post-canon client principal. */
489     if (krb5_principal_compare_flags(context, princ, req_client,
490                                      KRB5_PRINCIPAL_COMPARE_ENTERPRISE) ||
491         krb5_principal_compare(context, princ, client))
492         return TRUE;
493 
494     if (krb5_db_get_principal(context, princ, KRB5_KDB_FLAG_CLIENT, &ent))
495         return FALSE;
496     match = krb5_principal_compare(context, ent->princ, client);
497     krb5_db_free_principal(context, ent);
498     return match;
499 }
500 
501 static krb5_principal
client_name(krb5_context context,krb5_kdcpreauth_rock rock)502 client_name(krb5_context context, krb5_kdcpreauth_rock rock)
503 {
504     return rock->client->princ;
505 }
506 
507 static void
send_freshness_token(krb5_context context,krb5_kdcpreauth_rock rock)508 send_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock)
509 {
510     rock->send_freshness_token = TRUE;
511 }
512 
513 static krb5_error_code
check_freshness_token(krb5_context context,krb5_kdcpreauth_rock rock,const krb5_data * token)514 check_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
515                       const krb5_data *token)
516 {
517     krb5_timestamp token_ts, now;
518     krb5_key_data *kd;
519     krb5_keyblock kb;
520     krb5_kvno token_kvno;
521     krb5_checksum cksum;
522     krb5_data d;
523     uint8_t *token_cksum;
524     size_t token_cksum_len;
525     krb5_boolean valid = FALSE;
526     char ckbuf[4];
527 
528     memset(&kb, 0, sizeof(kb));
529 
530     if (krb5_timeofday(context, &now) != 0)
531         goto cleanup;
532 
533     if (token->length <= 8)
534         goto cleanup;
535     token_ts = load_32_be(token->data);
536     token_kvno = load_32_be(token->data + 4);
537     token_cksum = (uint8_t *)token->data + 8;
538     token_cksum_len = token->length - 8;
539 
540     /* Check if the token timestamp is too old. */
541     if (ts_after(now, ts_incr(token_ts, FRESHNESS_LIFETIME)))
542         goto cleanup;
543 
544     /* Fetch and decrypt the local krbtgt key of the token's kvno. */
545     if (krb5_dbe_find_enctype(context, rock->local_tgt, -1, -1, token_kvno,
546                               &kd) != 0)
547         goto cleanup;
548     if (krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL) != 0)
549         goto cleanup;
550 
551     /* Verify the token checksum against the current KDC time.  The checksum
552      * must use the mandatory checksum type of the krbtgt key's enctype. */
553     store_32_be(token_ts, ckbuf);
554     d = make_data(ckbuf, sizeof(ckbuf));
555     cksum.magic = KV5M_CHECKSUM;
556     cksum.checksum_type = 0;
557     cksum.length = token_cksum_len;
558     cksum.contents = token_cksum;
559     (void)krb5_c_verify_checksum(context, &kb, KRB5_KEYUSAGE_PA_AS_FRESHNESS,
560                                  &d, &cksum, &valid);
561 
562 cleanup:
563     krb5_free_keyblock_contents(context, &kb);
564     return valid ? 0 : KRB5KDC_ERR_PREAUTH_EXPIRED;
565 }
566 
567 static krb5_error_code
replace_reply_key(krb5_context context,krb5_kdcpreauth_rock rock,const krb5_keyblock * key,krb5_boolean is_strengthen)568 replace_reply_key(krb5_context context, krb5_kdcpreauth_rock rock,
569                   const krb5_keyblock *key, krb5_boolean is_strengthen)
570 {
571     krb5_keyblock copy;
572 
573     if (krb5_copy_keyblock_contents(context, key, &copy) != 0)
574         return ENOMEM;
575     krb5_free_keyblock_contents(context, rock->client_keyblock);
576     *rock->client_keyblock = copy;
577     if (!is_strengthen)
578         rock->replaced_reply_key = TRUE;
579     return 0;
580 }
581 
582 static struct krb5_kdcpreauth_callbacks_st callbacks = {
583     6,
584     max_time_skew,
585     client_keys,
586     free_keys,
587     request_body,
588     fast_armor,
589     get_string,
590     free_string,
591     client_entry,
592     event_context,
593     have_client_keys,
594     client_keyblock,
595     add_auth_indicator,
596     get_cookie,
597     set_cookie,
598     match_client,
599     client_name,
600     send_freshness_token,
601     check_freshness_token,
602     replace_reply_key
603 };
604 
605 static krb5_error_code
find_pa_system(int type,preauth_system ** preauth)606 find_pa_system(int type, preauth_system **preauth)
607 {
608     preauth_system *ap;
609 
610     if (preauth_systems == NULL)
611         return KRB5_PREAUTH_BAD_TYPE;
612     ap = preauth_systems;
613     while ((ap->type != -1) && (ap->type != type))
614         ap++;
615     if (ap->type == -1)
616         return(KRB5_PREAUTH_BAD_TYPE);
617     *preauth = ap;
618     return 0;
619 }
620 
621 /* Find a pointer to the request-specific module data for pa_sys. */
622 static krb5_error_code
find_modreq(preauth_system * pa_sys,struct request_pa_context * context,krb5_kdcpreauth_modreq ** modreq_out)623 find_modreq(preauth_system *pa_sys, struct request_pa_context *context,
624             krb5_kdcpreauth_modreq **modreq_out)
625 {
626     int i;
627 
628     *modreq_out = NULL;
629     if (context == NULL)
630         return KRB5KRB_ERR_GENERIC;
631 
632     for (i = 0; i < context->n_contexts; i++) {
633         if (context->contexts[i].pa_system == pa_sys) {
634             *modreq_out = &context->contexts[i].modreq;
635             return 0;
636         }
637     }
638 
639     return KRB5KRB_ERR_GENERIC;
640 }
641 
642 /*
643  * Create a list of indices into the preauth_systems array, sorted by order of
644  * preference.
645  */
646 static krb5_boolean
pa_list_includes(krb5_pa_data ** pa_data,krb5_preauthtype pa_type)647 pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
648 {
649     while (*pa_data != NULL) {
650         if ((*pa_data)->pa_type == pa_type)
651             return TRUE;
652         pa_data++;
653     }
654     return FALSE;
655 }
656 static void
sort_pa_order(krb5_context context,krb5_kdc_req * request,int * pa_order)657 sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
658 {
659     size_t i, j, k, n_repliers, n_key_replacers;
660 
661     /* First, set up the default order. */
662     i = 0;
663     for (j = 0; j < n_preauth_systems; j++) {
664         if (preauth_systems[j].return_padata != NULL)
665             pa_order[i++] = j;
666     }
667     n_repliers = i;
668     pa_order[n_repliers] = -1;
669 
670     /* Reorder so that PA_REPLACES_KEY modules are listed first. */
671     for (i = 0; i < n_repliers; i++) {
672         /* If this module replaces the key, then it's okay to leave it where it
673          * is in the order. */
674         if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
675             continue;
676         /* If not, search for a module which does, and swap in the first one we
677          * find. */
678         for (j = i + 1; j < n_repliers; j++) {
679             if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
680                 k = pa_order[j];
681                 pa_order[j] = pa_order[i];
682                 pa_order[i] = k;
683                 break;
684             }
685         }
686         /* If we didn't find one, we have moved all of the key-replacing
687          * modules, and i is the count of those modules. */
688         if (j == n_repliers)
689             break;
690     }
691     n_key_replacers = i;
692 
693     if (request->padata != NULL) {
694         /* Now reorder the subset of modules which replace the key,
695          * bubbling those which handle pa_data types provided by the
696          * client ahead of the others.
697          */
698         for (i = 0; i < n_key_replacers; i++) {
699             if (pa_list_includes(request->padata,
700                                  preauth_systems[pa_order[i]].type))
701                 continue;
702             for (j = i + 1; j < n_key_replacers; j++) {
703                 if (pa_list_includes(request->padata,
704                                      preauth_systems[pa_order[j]].type)) {
705                     k = pa_order[j];
706                     pa_order[j] = pa_order[i];
707                     pa_order[i] = k;
708                     break;
709                 }
710             }
711         }
712     }
713 #ifdef DEBUG
714     krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
715     for (i = 0; i < n_preauth_systems; i++) {
716         if (preauth_systems[i].return_padata != NULL)
717             krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
718                              preauth_systems[i].type);
719     }
720     krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
721     for (i = 0; pa_order[i] != -1; i++) {
722         krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
723                          preauth_systems[pa_order[i]].name,
724                          preauth_systems[pa_order[i]].type);
725     }
726 #endif
727 }
728 
missing_required_preauth(krb5_db_entry * client,krb5_db_entry * server,krb5_enc_tkt_part * enc_tkt_reply)729 const char *missing_required_preauth(krb5_db_entry *client,
730                                      krb5_db_entry *server,
731                                      krb5_enc_tkt_part *enc_tkt_reply)
732 {
733 #ifdef DEBUG
734     krb5_klog_syslog (
735         LOG_DEBUG,
736         "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
737         isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
738         isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
739         isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
740         isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
741 #endif
742 
743     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
744         !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
745         return "NEEDED_PREAUTH";
746 
747     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
748         !isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
749         return "NEEDED_HW_PREAUTH";
750 
751     return 0;
752 }
753 
754 /* Return true if request's enctypes indicate support for etype-info2. */
755 static krb5_boolean
requires_info2(const krb5_kdc_req * request)756 requires_info2(const krb5_kdc_req *request)
757 {
758     int i;
759 
760     for (i = 0; i < request->nktypes; i++) {
761         if (enctype_requires_etype_info_2(request->ktype[i]))
762             return TRUE;
763     }
764     return FALSE;
765 }
766 
767 /* Add PA-ETYPE-INFO2 and possibly PA-ETYPE-INFO entries to pa_list as
768  * appropriate for the request and client principal. */
769 static krb5_error_code
add_etype_info(krb5_context context,krb5_kdcpreauth_rock rock,krb5_pa_data *** pa_list)770 add_etype_info(krb5_context context, krb5_kdcpreauth_rock rock,
771                krb5_pa_data ***pa_list)
772 {
773     krb5_error_code ret;
774     krb5_data *der;
775 
776     if (rock->client_key == NULL)
777         return 0;
778 
779     if (!requires_info2(rock->request)) {
780         /* Include PA-ETYPE-INFO only for old clients. */
781         ret = make_etype_info(context, FALSE, rock->client->princ,
782                               rock->client_key, rock->client_keyblock->enctype,
783                               &der);
784         if (ret)
785             return ret;
786         ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_ETYPE_INFO, der);
787         krb5_free_data(context, der);
788         if (ret)
789             return ret;
790     }
791 
792     /* Always include PA-ETYPE-INFO2. */
793     ret = make_etype_info(context, TRUE, rock->client->princ, rock->client_key,
794                           rock->client_keyblock->enctype, &der);
795     if (ret)
796         return ret;
797     ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_ETYPE_INFO2, der);
798     krb5_free_data(context, der);
799     return ret;
800 }
801 
802 /* Add PW-SALT entries to pa_list as appropriate for the request and client
803  * principal. */
804 static krb5_error_code
add_pw_salt(krb5_context context,krb5_kdcpreauth_rock rock,krb5_pa_data *** pa_list)805 add_pw_salt(krb5_context context, krb5_kdcpreauth_rock rock,
806             krb5_pa_data ***pa_list)
807 {
808     krb5_error_code ret;
809     krb5_data *salt = NULL;
810     krb5_int16 salttype;
811 
812     /* Only include this pa-data for old clients. */
813     if (rock->client_key == NULL || requires_info2(rock->request))
814         return 0;
815 
816     ret = krb5_dbe_compute_salt(context, rock->client_key,
817                                 rock->request->client, &salttype, &salt);
818     if (ret)
819         return 0;
820 
821     ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_PW_SALT, salt);
822     krb5_free_data(context, salt);
823     return ret;
824 }
825 
826 static krb5_error_code
add_freshness_token(krb5_context context,krb5_kdcpreauth_rock rock,krb5_pa_data *** pa_list)827 add_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
828                     krb5_pa_data ***pa_list)
829 {
830     krb5_error_code ret;
831     krb5_timestamp now;
832     krb5_keyblock kb;
833     krb5_checksum cksum;
834     krb5_data d;
835     krb5_pa_data *pa = NULL;
836     char ckbuf[4];
837 
838     memset(&cksum, 0, sizeof(cksum));
839     memset(&kb, 0, sizeof(kb));
840 
841     if (!rock->send_freshness_token)
842         return 0;
843     if (krb5int_find_pa_data(context, rock->request->padata,
844                              KRB5_PADATA_AS_FRESHNESS) == NULL)
845         return 0;
846 
847     /* Compute a checksum over the current KDC time. */
848     ret = krb5_timeofday(context, &now);
849     if (ret)
850         goto cleanup;
851     store_32_be(now, ckbuf);
852     d = make_data(ckbuf, sizeof(ckbuf));
853     ret = krb5_c_make_checksum(context, 0, rock->local_tgt_key,
854                                KRB5_KEYUSAGE_PA_AS_FRESHNESS, &d, &cksum);
855 
856     /* Compose a freshness token from the time, krbtgt kvno, and checksum. */
857     ret = k5_alloc_pa_data(KRB5_PADATA_AS_FRESHNESS, 8 + cksum.length, &pa);
858     if (ret)
859         goto cleanup;
860     store_32_be(now, pa->contents);
861     store_32_be(current_kvno(rock->local_tgt), pa->contents + 4);
862     memcpy(pa->contents + 8, cksum.contents, cksum.length);
863 
864     ret = k5_add_pa_data_element(pa_list, &pa);
865 
866 cleanup:
867     krb5_free_keyblock_contents(context, &kb);
868     krb5_free_checksum_contents(context, &cksum);
869     k5_free_pa_data_element(pa);
870     return ret;
871 }
872 
873 struct hint_state {
874     kdc_hint_respond_fn respond;
875     void *arg;
876     krb5_context context;
877 
878     krb5_kdcpreauth_rock rock;
879     krb5_kdc_req *request;
880     krb5_pa_data ***e_data_out;
881 
882     int hw_only;
883     preauth_system *ap;
884     krb5_pa_data **pa_data;
885     krb5_preauthtype pa_type;
886 };
887 
888 static void
hint_list_finish(struct hint_state * state,krb5_error_code code)889 hint_list_finish(struct hint_state *state, krb5_error_code code)
890 {
891     krb5_context context = state->context;
892     kdc_hint_respond_fn oldrespond = state->respond;
893     void *oldarg = state->arg;
894 
895     /* Add a freshness token if a preauth module requested it and the client
896      * request indicates support for it. */
897     if (!code)
898         code = add_freshness_token(context, state->rock, &state->pa_data);
899 
900     if (!code) {
901         if (state->pa_data == NULL) {
902             krb5_klog_syslog(LOG_INFO,
903                              _("%spreauth required but hint list is empty"),
904                              state->hw_only ? "hw" : "");
905         }
906 
907         *state->e_data_out = state->pa_data;
908         state->pa_data = NULL;
909     }
910 
911     krb5_free_pa_data(context, state->pa_data);
912     free(state);
913     (*oldrespond)(oldarg);
914 }
915 
916 static void
917 hint_list_next(struct hint_state *arg);
918 
919 static void
finish_get_edata(void * arg,krb5_error_code code,krb5_pa_data * pa)920 finish_get_edata(void *arg, krb5_error_code code, krb5_pa_data *pa)
921 {
922     krb5_error_code ret;
923     struct hint_state *state = arg;
924 
925     if (code == 0) {
926         if (pa == NULL) {
927             ret = k5_alloc_pa_data(state->pa_type, 0, &pa);
928             if (ret)
929                 goto error;
930         }
931         ret = k5_add_pa_data_element(&state->pa_data, &pa);
932         k5_free_pa_data_element(pa);
933         if (ret)
934             goto error;
935     }
936 
937     state->ap++;
938     hint_list_next(state);
939     return;
940 
941 error:
942     hint_list_finish(state, ret);
943 }
944 
945 static void
hint_list_next(struct hint_state * state)946 hint_list_next(struct hint_state *state)
947 {
948     krb5_context context = state->context;
949     preauth_system *ap = state->ap;
950 
951     if (ap->type == -1) {
952         hint_list_finish(state, 0);
953         return;
954     }
955 
956     if (state->hw_only && !(ap->flags & PA_HARDWARE))
957         goto next;
958     if (ap->flags & PA_PSEUDO)
959         goto next;
960 
961     state->pa_type = ap->type;
962     if (ap->get_edata) {
963         ap->get_edata(context, state->request, &callbacks, state->rock,
964                       ap->moddata, ap->type, finish_get_edata, state);
965     } else
966         finish_get_edata(state, 0, NULL);
967     return;
968 
969 next:
970     state->ap++;
971     hint_list_next(state);
972 }
973 
974 void
get_preauth_hint_list(krb5_kdc_req * request,krb5_kdcpreauth_rock rock,krb5_pa_data *** e_data_out,kdc_hint_respond_fn respond,void * arg)975 get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock,
976                       krb5_pa_data ***e_data_out, kdc_hint_respond_fn respond,
977                       void *arg)
978 {
979     krb5_context context = rock->rstate->realm_data->realm_context;
980     struct hint_state *state;
981 
982     *e_data_out = NULL;
983 
984     /* Allocate our state. */
985     state = calloc(1, sizeof(*state));
986     if (state == NULL)
987         goto error;
988     state->hw_only = isflagset(rock->client->attributes,
989                                KRB5_KDB_REQUIRES_HW_AUTH);
990     state->respond = respond;
991     state->arg = arg;
992     state->request = request;
993     state->rock = rock;
994     state->context = context;
995     state->e_data_out = e_data_out;
996     state->pa_data = NULL;
997     state->ap = preauth_systems;
998 
999     /* Add an empty PA-FX-FAST element to advertise FAST support. */
1000     if (k5_add_empty_pa_data(&state->pa_data, KRB5_PADATA_FX_FAST) != 0)
1001         goto error;
1002 
1003     if (add_etype_info(context, rock, &state->pa_data) != 0)
1004         goto error;
1005 
1006     hint_list_next(state);
1007     return;
1008 
1009 error:
1010     if (state != NULL)
1011         krb5_free_pa_data(context, state->pa_data);
1012     free(state);
1013     (*respond)(arg);
1014 }
1015 
1016 /*
1017  * Add authorization data returned from preauth modules to the ticket
1018  * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
1019  */
1020 static krb5_error_code
add_authorization_data(krb5_enc_tkt_part * enc_tkt_part,krb5_authdata ** ad)1021 add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
1022 {
1023     krb5_authdata **newad;
1024     int oldones, newones;
1025     int i;
1026 
1027     if (enc_tkt_part == NULL || ad == NULL)
1028         return EINVAL;
1029 
1030     for (newones = 0; ad[newones] != NULL; newones++);
1031     if (newones == 0)
1032         return 0;   /* nothing to add */
1033 
1034     if (enc_tkt_part->authorization_data == NULL)
1035         oldones = 0;
1036     else
1037         for (oldones = 0;
1038              enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
1039 
1040     newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
1041     if (newad == NULL)
1042         return ENOMEM;
1043 
1044     /* Copy any existing pointers */
1045     for (i = 0; i < oldones; i++)
1046         newad[i] = enc_tkt_part->authorization_data[i];
1047 
1048     /* Add the new ones */
1049     for (i = 0; i < newones; i++)
1050         newad[oldones+i] = ad[i];
1051 
1052     /* Terminate the new list */
1053     newad[oldones+i] = NULL;
1054 
1055     /* Free any existing list */
1056     if (enc_tkt_part->authorization_data != NULL)
1057         free(enc_tkt_part->authorization_data);
1058 
1059     /* Install our new list */
1060     enc_tkt_part->authorization_data = newad;
1061 
1062     return 0;
1063 }
1064 
1065 struct padata_state {
1066     kdc_preauth_respond_fn respond;
1067     void *arg;
1068     kdc_realm_t *realm;
1069 
1070     krb5_kdcpreauth_modreq *modreq_ptr;
1071     krb5_pa_data **padata;
1072     int pa_found;
1073     krb5_context context;
1074     krb5_kdcpreauth_rock rock;
1075     krb5_data *req_pkt;
1076     krb5_kdc_req *request;
1077     krb5_enc_tkt_part *enc_tkt_reply;
1078     void **padata_context;
1079 
1080     preauth_system *pa_sys;
1081     krb5_pa_data **pa_e_data;
1082     krb5_boolean typed_e_data_flag;
1083     int pa_ok;
1084     krb5_error_code saved_code;
1085 
1086     krb5_pa_data ***e_data_out;
1087     krb5_boolean *typed_e_data_out;
1088 };
1089 
1090 /* Return code if it is 0 or one of the codes we pass through to the client.
1091  * Otherwise return KRB5KDC_ERR_PREAUTH_FAILED. */
1092 static krb5_error_code
filter_preauth_error(krb5_error_code code)1093 filter_preauth_error(krb5_error_code code)
1094 {
1095     /* The following switch statement allows us
1096      * to return some preauth system errors back to the client.
1097      */
1098     switch(code) {
1099     case 0:
1100     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
1101     case KRB5KRB_AP_ERR_SKEW:
1102     case KRB5KDC_ERR_PREAUTH_REQUIRED:
1103     case KRB5KDC_ERR_ETYPE_NOSUPP:
1104         /* rfc 4556 */
1105     case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
1106     case KRB5KDC_ERR_INVALID_SIG:
1107     case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
1108     case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
1109     case KRB5KDC_ERR_INVALID_CERTIFICATE:
1110     case KRB5KDC_ERR_REVOKED_CERTIFICATE:
1111     case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
1112     case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
1113     case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
1114     case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
1115     case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
1116     case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
1117     case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
1118         /* earlier drafts of what became rfc 4556 */
1119     case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
1120     case KRB5KDC_ERR_KDC_NOT_TRUSTED:
1121     case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
1122         /* This value is shared with
1123          *     KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
1124         /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
1125     case KRB5KDC_ERR_DISCARD:
1126         /* pkinit alg-agility */
1127     case KRB5KDC_ERR_NO_ACCEPTABLE_KDF:
1128         /* rfc 6113 */
1129     case KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED:
1130         return code;
1131     default:
1132         return KRB5KDC_ERR_PREAUTH_FAILED;
1133     }
1134 }
1135 
1136 /*
1137  * If the client performed optimistic pre-authentication for a multi-round-trip
1138  * mechanism, it may need key information to complete the exchange, so send it
1139  * a PA-ETYPE-INFO2 element in addition to the pa-data from the module.
1140  */
1141 static krb5_error_code
maybe_add_etype_info2(struct padata_state * state,krb5_error_code code)1142 maybe_add_etype_info2(struct padata_state *state, krb5_error_code code)
1143 {
1144     krb5_error_code ret;
1145     krb5_context context = state->context;
1146     krb5_kdcpreauth_rock rock = state->rock;
1147     krb5_data *der;
1148 
1149     /* Only add key information when requesting another preauth round trip. */
1150     if (code != KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
1151         return 0;
1152 
1153     /* Don't try to add key information when there is no key. */
1154     if (rock->client_key == NULL)
1155         return 0;
1156 
1157     /* If the client sent a cookie, it has already seen a KDC response with key
1158      * information. */
1159     if (krb5int_find_pa_data(context, state->request->padata,
1160                              KRB5_PADATA_FX_COOKIE) != NULL)
1161         return 0;
1162 
1163     ret = make_etype_info(context, TRUE, rock->client->princ, rock->client_key,
1164                           rock->client_keyblock->enctype, &der);
1165     if (ret)
1166         return ret;
1167     ret = k5_add_pa_data_from_data(&state->pa_e_data, KRB5_PADATA_ETYPE_INFO2,
1168                                    der);
1169     krb5_free_data(context, der);
1170     return ret;
1171 }
1172 
1173 /* Release state and respond to the AS-REQ processing code with the result of
1174  * checking pre-authentication data. */
1175 static void
finish_check_padata(struct padata_state * state,krb5_error_code code)1176 finish_check_padata(struct padata_state *state, krb5_error_code code)
1177 {
1178     kdc_preauth_respond_fn respond;
1179     void *arg;
1180 
1181     if (state->pa_ok || !state->pa_found) {
1182         /* Return successfully.  If we didn't match a preauth system, we may
1183          * return PREAUTH_REQUIRED later, but we didn't fail to verify. */
1184         code = 0;
1185         goto cleanup;
1186     }
1187 
1188     /* Add key information to the saved error pa-data if required. */
1189     if (maybe_add_etype_info2(state, code) != 0) {
1190         code = KRB5KDC_ERR_PREAUTH_FAILED;
1191         goto cleanup;
1192     }
1193 
1194     /* Return any saved error pa-data, stealing the pointer from state. */
1195     *state->e_data_out = state->pa_e_data;
1196     *state->typed_e_data_out = state->typed_e_data_flag;
1197     state->pa_e_data = NULL;
1198 
1199 cleanup:
1200     /* Discard saved error pa-data if we aren't returning it, free state, and
1201      * respond to the AS-REQ processing code. */
1202     respond = state->respond;
1203     arg = state->arg;
1204     krb5_free_pa_data(state->context, state->pa_e_data);
1205     free(state);
1206     (*respond)(arg, filter_preauth_error(code));
1207 }
1208 
1209 static void
1210 next_padata(struct padata_state *state);
1211 
1212 static void
finish_verify_padata(void * arg,krb5_error_code code,krb5_kdcpreauth_modreq modreq,krb5_pa_data ** e_data,krb5_authdata ** authz_data)1213 finish_verify_padata(void *arg, krb5_error_code code,
1214                      krb5_kdcpreauth_modreq modreq, krb5_pa_data **e_data,
1215                      krb5_authdata **authz_data)
1216 {
1217     struct padata_state *state = arg;
1218     const char *emsg;
1219     krb5_boolean typed_e_data_flag;
1220 
1221     assert(state);
1222     *state->modreq_ptr = modreq;
1223 
1224     if (code) {
1225         emsg = krb5_get_error_message(state->context, code);
1226         krb5_klog_syslog(LOG_INFO, "preauth (%s) verify failure: %s",
1227                          state->pa_sys->name, emsg);
1228         krb5_free_error_message(state->context, emsg);
1229 
1230         /* Ignore authorization data returned from modules that fail */
1231         if (authz_data != NULL) {
1232             krb5_free_authdata(state->context, authz_data);
1233             authz_data = NULL;
1234         }
1235 
1236         typed_e_data_flag = ((state->pa_sys->flags & PA_TYPED_E_DATA) != 0);
1237 
1238         /*
1239          * We'll return edata from either the first PA_REQUIRED module
1240          * that fails, or the first non-PA_REQUIRED module that fails.
1241          * Hang on to edata from the first non-PA_REQUIRED module.
1242          * If we've already got one saved, simply discard this one.
1243          */
1244         if (state->pa_sys->flags & PA_REQUIRED) {
1245             /* free up any previous edata we might have been saving */
1246             if (state->pa_e_data != NULL)
1247                 krb5_free_pa_data(state->context, state->pa_e_data);
1248             state->pa_e_data = e_data;
1249             state->typed_e_data_flag = typed_e_data_flag;
1250 
1251             /* Make sure we use the current retval */
1252             state->pa_ok = 0;
1253             finish_check_padata(state, code);
1254             return;
1255         } else if (state->pa_e_data == NULL) {
1256             /* save the first error code and e-data */
1257             state->pa_e_data = e_data;
1258             state->typed_e_data_flag = typed_e_data_flag;
1259             state->saved_code = code;
1260         } else if (e_data != NULL) {
1261             /* discard this extra e-data from non-PA_REQUIRED module */
1262             krb5_free_pa_data(state->context, e_data);
1263         }
1264     } else {
1265 #ifdef DEBUG
1266         krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
1267 #endif
1268 
1269         /* Ignore any edata returned on success */
1270         if (e_data != NULL)
1271             krb5_free_pa_data(state->context, e_data);
1272 
1273         /* Add any authorization data to the ticket */
1274         if (authz_data != NULL) {
1275             add_authorization_data(state->enc_tkt_reply, authz_data);
1276             free(authz_data);
1277         }
1278 
1279         state->pa_ok = 1;
1280         if (state->pa_sys->flags & PA_SUFFICIENT) {
1281             finish_check_padata(state, state->saved_code);
1282             return;
1283         }
1284     }
1285 
1286     next_padata(state);
1287 }
1288 
1289 static void
next_padata(struct padata_state * state)1290 next_padata(struct padata_state *state)
1291 {
1292     assert(state);
1293     if (!state->padata)
1294         state->padata = state->request->padata;
1295     else
1296         state->padata++;
1297 
1298     if (!*state->padata) {
1299         finish_check_padata(state, state->saved_code);
1300         return;
1301     }
1302 
1303 #ifdef DEBUG
1304     krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*state->padata)->pa_type);
1305 #endif
1306     if (find_pa_system((*state->padata)->pa_type, &state->pa_sys))
1307         goto next;
1308     if (find_modreq(state->pa_sys, *state->padata_context, &state->modreq_ptr))
1309         goto next;
1310 #ifdef DEBUG
1311     krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", state->pa_sys->name);
1312 #endif
1313     if (state->pa_sys->verify_padata == 0)
1314         goto next;
1315 
1316     state->pa_found++;
1317     state->pa_sys->verify_padata(state->context, state->req_pkt,
1318                                  state->request, state->enc_tkt_reply,
1319                                  *state->padata, &callbacks, state->rock,
1320                                  state->pa_sys->moddata, finish_verify_padata,
1321                                  state);
1322     return;
1323 
1324 next:
1325     next_padata(state);
1326 }
1327 
1328 /*
1329  * This routine is called to verify the preauthentication information
1330  * for a V5 request.
1331  *
1332  * Returns 0 if the pre-authentication is valid, non-zero to indicate
1333  * an error code of some sort.
1334  */
1335 
1336 void
check_padata(krb5_context context,krb5_kdcpreauth_rock rock,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,void ** padata_context,krb5_pa_data *** e_data,krb5_boolean * typed_e_data,kdc_preauth_respond_fn respond,void * arg)1337 check_padata(krb5_context context, krb5_kdcpreauth_rock rock,
1338              krb5_data *req_pkt, krb5_kdc_req *request,
1339              krb5_enc_tkt_part *enc_tkt_reply, void **padata_context,
1340              krb5_pa_data ***e_data, krb5_boolean *typed_e_data,
1341              kdc_preauth_respond_fn respond, void *arg)
1342 {
1343     struct padata_state *state;
1344 
1345     if (request->padata == 0) {
1346         (*respond)(arg, 0);
1347         return;
1348     }
1349 
1350     if (make_padata_context(context, padata_context) != 0) {
1351         (*respond)(arg, KRB5KRB_ERR_GENERIC);
1352         return;
1353     }
1354 
1355     state = calloc(1, sizeof(*state));
1356     if (state == NULL) {
1357         (*respond)(arg, ENOMEM);
1358         return;
1359     }
1360     state->respond = respond;
1361     state->arg = arg;
1362     state->context = context;
1363     state->rock = rock;
1364     state->req_pkt = req_pkt;
1365     state->request = request;
1366     state->enc_tkt_reply = enc_tkt_reply;
1367     state->padata_context = padata_context;
1368     state->e_data_out = e_data;
1369     state->typed_e_data_out = typed_e_data;
1370     state->realm = rock->rstate->realm_data;
1371 
1372 #ifdef DEBUG
1373     krb5_klog_syslog (LOG_DEBUG, "checking padata");
1374 #endif
1375 
1376     next_padata(state);
1377 }
1378 
1379 /* Return true if k1 and k2 have the same type and contents. */
1380 static krb5_boolean
keyblock_equal(const krb5_keyblock * k1,const krb5_keyblock * k2)1381 keyblock_equal(const krb5_keyblock *k1, const krb5_keyblock *k2)
1382 {
1383     if (k1->enctype != k2->enctype)
1384         return FALSE;
1385     if (k1->length != k2->length)
1386         return FALSE;
1387     return memcmp(k1->contents, k2->contents, k1->length) == 0;
1388 }
1389 
1390 /*
1391  * return_padata creates any necessary preauthentication
1392  * structures which should be returned by the KDC to the client
1393  */
1394 krb5_error_code
return_padata(krb5_context context,krb5_kdcpreauth_rock rock,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_keyblock * encrypting_key,void ** padata_context)1395 return_padata(krb5_context context, krb5_kdcpreauth_rock rock,
1396               krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply,
1397               krb5_keyblock *encrypting_key, void **padata_context)
1398 {
1399     krb5_error_code             retval;
1400     krb5_pa_data **             padata;
1401     krb5_pa_data **             send_pa_list = NULL;
1402     krb5_pa_data *              send_pa;
1403     krb5_pa_data *              pa = 0;
1404     krb5_pa_data null_item;
1405     preauth_system *            ap;
1406     int *                       pa_order = NULL;
1407     int *                       pa_type;
1408     int                         size = 0;
1409     krb5_kdcpreauth_modreq      *modreq_ptr;
1410     krb5_boolean                key_modified;
1411     krb5_keyblock               original_key;
1412 
1413     memset(&original_key, 0, sizeof(original_key));
1414 
1415     if ((!*padata_context) &&
1416         (make_padata_context(context, padata_context) != 0)) {
1417         return KRB5KRB_ERR_GENERIC;
1418     }
1419 
1420     for (ap = preauth_systems; ap->type != -1; ap++) {
1421         if (ap->return_padata)
1422             size++;
1423     }
1424 
1425     pa_order = k5calloc(size + 1, sizeof(int), &retval);
1426     if (pa_order == NULL)
1427         goto cleanup;
1428     sort_pa_order(context, request, pa_order);
1429 
1430     retval = krb5_copy_keyblock_contents(context, encrypting_key,
1431                                          &original_key);
1432     if (retval)
1433         goto cleanup;
1434     key_modified = FALSE;
1435     null_item.contents = NULL;
1436     null_item.length = 0;
1437 
1438     for (pa_type = pa_order; *pa_type != -1; pa_type++) {
1439         ap = &preauth_systems[*pa_type];
1440         if (key_modified && (ap->flags & PA_REPLACES_KEY))
1441             continue;
1442         if (ap->return_padata == 0)
1443             continue;
1444         if (find_modreq(ap, *padata_context, &modreq_ptr))
1445             continue;
1446         pa = &null_item;
1447         null_item.pa_type = ap->type;
1448         if (request->padata) {
1449             for (padata = request->padata; *padata; padata++) {
1450                 if ((*padata)->pa_type == ap->type) {
1451                     pa = *padata;
1452                     break;
1453                 }
1454             }
1455         }
1456         send_pa = NULL;
1457         retval = ap->return_padata(context, pa, req_pkt, request, reply,
1458                                    encrypting_key, &send_pa, &callbacks, rock,
1459                                    ap->moddata, *modreq_ptr);
1460         if (retval)
1461             goto cleanup;
1462 
1463         if (send_pa != NULL) {
1464             retval = k5_add_pa_data_element(&send_pa_list, &send_pa);
1465             k5_free_pa_data_element(send_pa);
1466             if (retval)
1467                 goto cleanup;
1468         }
1469 
1470         if (!key_modified && !keyblock_equal(&original_key, encrypting_key))
1471             key_modified = TRUE;
1472     }
1473 
1474     /*
1475      * Add etype-info and pw-salt pa-data as needed.  If we replaced the reply
1476      * key, we can't send consistent etype-info; the salt from the client key
1477      * data doesn't correspond to the replaced reply key, and RFC 4120 section
1478      * 5.2.7.5 forbids us from sending etype-info describing the initial reply
1479      * key in an AS-REP if it doesn't have the same enctype as the replaced
1480      * reply key.  For all current and foreseeable preauth mechs, we can assume
1481      * the client received etype-info2 in an earlier step and already computed
1482      * the initial reply key if it needed it.  The client can determine the
1483      * enctype of the replaced reply key from the etype field of the enc-part
1484      * field of the AS-REP.
1485      */
1486     if (!key_modified) {
1487         retval = add_etype_info(context, rock, &send_pa_list);
1488         if (retval)
1489             goto cleanup;
1490         retval = add_pw_salt(context, rock, &send_pa_list);
1491         if (retval)
1492             goto cleanup;
1493     }
1494 
1495     if (send_pa_list != NULL) {
1496         reply->padata = send_pa_list;
1497         send_pa_list = 0;
1498     }
1499 
1500 cleanup:
1501     krb5_free_keyblock_contents(context, &original_key);
1502     free(pa_order);
1503     krb5_free_pa_data(context, send_pa_list);
1504 
1505     return (retval);
1506 }
1507 
1508 static krb5_error_code
_make_etype_info_entry(krb5_context context,krb5_principal client_princ,krb5_key_data * client_key,krb5_enctype etype,krb5_etype_info_entry ** entry_out,int etype_info2)1509 _make_etype_info_entry(krb5_context context,
1510                        krb5_principal client_princ, krb5_key_data *client_key,
1511                        krb5_enctype etype, krb5_etype_info_entry **entry_out,
1512                        int etype_info2)
1513 {
1514     krb5_error_code retval;
1515     krb5_int16 salttype;
1516     krb5_data *salt = NULL;
1517     krb5_etype_info_entry *entry = NULL;
1518 
1519     *entry_out = NULL;
1520     entry = malloc(sizeof(*entry));
1521     if (entry == NULL)
1522         return ENOMEM;
1523 
1524     entry->magic = KV5M_ETYPE_INFO_ENTRY;
1525     entry->etype = etype;
1526     entry->length = KRB5_ETYPE_NO_SALT;
1527     entry->salt = NULL;
1528     entry->s2kparams = empty_data();
1529     retval = krb5_dbe_compute_salt(context, client_key, client_princ,
1530                                    &salttype, &salt);
1531     if (retval)
1532         goto cleanup;
1533 
1534     entry->length = salt->length;
1535     entry->salt = (unsigned char *)salt->data;
1536     salt->data = NULL;
1537     *entry_out = entry;
1538     entry = NULL;
1539 
1540 cleanup:
1541     if (entry != NULL)
1542         krb5_free_data_contents(context, &entry->s2kparams);
1543     free(entry);
1544     krb5_free_data(context, salt);
1545     return retval;
1546 }
1547 
1548 /* Encode an etype-info or etype-info2 message for client_key with the given
1549  * enctype, using client to compute the salt if necessary. */
1550 static krb5_error_code
make_etype_info(krb5_context context,krb5_boolean etype_info2,krb5_principal client,krb5_key_data * client_key,krb5_enctype enctype,krb5_data ** der_out)1551 make_etype_info(krb5_context context, krb5_boolean etype_info2,
1552                 krb5_principal client, krb5_key_data *client_key,
1553                 krb5_enctype enctype, krb5_data **der_out)
1554 {
1555     krb5_error_code retval;
1556     krb5_etype_info_entry **entry = NULL;
1557 
1558     *der_out = NULL;
1559 
1560     entry = k5calloc(2, sizeof(*entry), &retval);
1561     if (entry == NULL)
1562         goto cleanup;
1563     retval = _make_etype_info_entry(context, client, client_key, enctype,
1564                                     &entry[0], etype_info2);
1565     if (retval != 0)
1566         goto cleanup;
1567 
1568     if (etype_info2)
1569         retval = encode_krb5_etype_info2(entry, der_out);
1570     else
1571         retval = encode_krb5_etype_info(entry, der_out);
1572 
1573 cleanup:
1574     krb5_free_etype_info(context, entry);
1575     return retval;
1576 }
1577 
1578 /*
1579  * Returns TRUE if the PAC should be included
1580  */
1581 krb5_boolean
include_pac_p(krb5_context context,krb5_kdc_req * request)1582 include_pac_p(krb5_context context, krb5_kdc_req *request)
1583 {
1584     krb5_error_code             code;
1585     krb5_pa_data                **padata;
1586     krb5_boolean                retval = TRUE; /* default is to return PAC */
1587     krb5_data                   data;
1588     krb5_pa_pac_req             *req = NULL;
1589 
1590     if (request->padata == NULL) {
1591         return retval;
1592     }
1593 
1594     for (padata = request->padata; *padata != NULL; padata++) {
1595         if ((*padata)->pa_type == KRB5_PADATA_PAC_REQUEST) {
1596             data.data = (char *)(*padata)->contents;
1597             data.length = (*padata)->length;
1598 
1599             code = decode_krb5_pa_pac_req(&data, &req);
1600             if (code == 0) {
1601                 retval = req->include_pac;
1602                 krb5_free_pa_pac_req(context, req);
1603                 req = NULL;
1604             }
1605             break;
1606         }
1607     }
1608 
1609     return retval;
1610 }
1611 
1612 static krb5_error_code
return_referral_enc_padata(krb5_context context,krb5_enc_kdc_rep_part * reply,krb5_db_entry * server)1613 return_referral_enc_padata( krb5_context context,
1614                             krb5_enc_kdc_rep_part *reply,
1615                             krb5_db_entry *server)
1616 {
1617     krb5_error_code             code;
1618     krb5_tl_data                tl_data;
1619     krb5_pa_data                *pa;
1620 
1621     tl_data.tl_data_type = KRB5_TL_SVR_REFERRAL_DATA;
1622     code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
1623     if (code || tl_data.tl_data_length == 0)
1624         return 0;
1625 
1626     code = k5_alloc_pa_data(KRB5_PADATA_SVR_REFERRAL_INFO,
1627                             tl_data.tl_data_length, &pa);
1628     if (code)
1629         return code;
1630     memcpy(pa->contents, tl_data.tl_data_contents, tl_data.tl_data_length);
1631     code = k5_add_pa_data_element(&reply->enc_padata, &pa);
1632     k5_free_pa_data_element(pa);
1633     return code;
1634 }
1635 
1636 krb5_error_code
return_enc_padata(krb5_context context,krb5_data * req_pkt,krb5_kdc_req * request,krb5_keyblock * reply_key,krb5_db_entry * server,krb5_enc_kdc_rep_part * reply_encpart,krb5_boolean is_referral)1637 return_enc_padata(krb5_context context, krb5_data *req_pkt,
1638                   krb5_kdc_req *request, krb5_keyblock *reply_key,
1639                   krb5_db_entry *server, krb5_enc_kdc_rep_part *reply_encpart,
1640                   krb5_boolean is_referral)
1641 {
1642     krb5_error_code code = 0;
1643     /* This should be initialized and only used for Win2K compat and other
1644      * specific standardized uses such as FAST negotiation. */
1645     if (is_referral) {
1646         code = return_referral_enc_padata(context, reply_encpart, server);
1647         if (code)
1648             return code;
1649     }
1650     code = kdc_handle_protected_negotiation(context, req_pkt, request, reply_key,
1651                                             &reply_encpart->enc_padata);
1652     if (code)
1653         goto cleanup;
1654 
1655     code = kdc_add_pa_pac_options(context, request,
1656                                   &reply_encpart->enc_padata);
1657     if (code)
1658         goto cleanup;
1659 
1660     /*Add potentially other enc_padata providers*/
1661 cleanup:
1662     return code;
1663 }
1664