dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / krb5 / krb5kdc / kdc_preauth.c
blob38c447dbaa7aa5dd48cfd8552b8d3c34618e24f5
1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
7 /*
8 * kdc/kdc_preauth.c
10 * Copyright 1995, 2003 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
32 * Preauthentication routines for the KDC.
36 * Copyright (C) 1998 by the FundsXpress, INC.
38 * All rights reserved.
40 * Export of this software from the United States of America may require
41 * a specific license from the United States Government. It is the
42 * responsibility of any person or organization contemplating export to
43 * obtain such a license before exporting.
45 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
46 * distribute this software and its documentation for any purpose and
47 * without fee is hereby granted, provided that the above copyright
48 * notice appear in all copies and that both that copyright notice and
49 * this permission notice appear in supporting documentation, and that
50 * the name of FundsXpress. not be used in advertising or publicity pertaining
51 * to distribution of the software without specific, written prior
52 * permission. FundsXpress makes no representations about the suitability of
53 * this software for any purpose. It is provided "as is" without express
54 * or implied warranty.
56 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
57 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
61 #include "k5-int.h"
62 #include "kdc_util.h"
63 #include "extern.h"
64 #include "com_err.h"
65 #include <assert.h>
66 #include <stdio.h>
67 #include "adm_proto.h"
68 #include <libintl.h>
69 #include <syslog.h>
71 #include <assert.h>
72 #include "preauth_plugin.h"
74 #if TARGET_OS_MAC
75 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
76 #else
77 static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
78 #endif
80 /* XXX This is ugly and should be in a header file somewhere */
81 #ifndef KRB5INT_DES_TYPES_DEFINED
82 #define KRB5INT_DES_TYPES_DEFINED
83 typedef unsigned char des_cblock[8]; /* crypto-block size */
84 #endif
85 typedef des_cblock mit_des_cblock;
86 extern void mit_des_fixup_key_parity (mit_des_cblock );
87 extern int mit_des_is_weak_key (mit_des_cblock );
89 typedef struct _krb5_preauth_systems {
90 const char *name;
91 int type;
92 int flags;
93 void *plugin_context;
94 preauth_server_init_proc init;
95 preauth_server_fini_proc fini;
96 preauth_server_edata_proc get_edata;
97 preauth_server_verify_proc verify_padata;
98 preauth_server_return_proc return_padata;
99 preauth_server_free_reqcontext_proc free_pa_reqctx;
100 } krb5_preauth_systems;
102 static krb5_error_code verify_enc_timestamp
103 (krb5_context, krb5_db_entry *client,
104 krb5_data *req_pkt,
105 krb5_kdc_req *request,
106 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
107 preauth_get_entry_data_proc get_entry_data,
108 void *pa_system_context,
109 void **pa_request_context,
110 krb5_data **e_data,
111 krb5_authdata ***authz_data);
113 static krb5_error_code get_etype_info
114 (krb5_context, krb5_kdc_req *request,
115 krb5_db_entry *client, krb5_db_entry *server,
116 preauth_get_entry_data_proc get_entry_data,
117 void *pa_system_context,
118 krb5_pa_data *data);
119 static krb5_error_code
120 get_etype_info2(krb5_context context, krb5_kdc_req *request,
121 krb5_db_entry *client, krb5_db_entry *server,
122 preauth_get_entry_data_proc get_entry_data,
123 void *pa_system_context,
124 krb5_pa_data *pa_data);
125 static krb5_error_code
126 etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
127 krb5_db_entry *client,
128 krb5_kdc_req *request, krb5_kdc_rep *reply,
129 krb5_key_data *client_key,
130 krb5_keyblock *encrypting_key,
131 krb5_pa_data **send_pa,
132 int etype_info2);
134 static krb5_error_code
135 return_etype_info(krb5_context, krb5_pa_data * padata,
136 krb5_db_entry *client,
137 krb5_data *req_pkt,
138 krb5_kdc_req *request, krb5_kdc_rep *reply,
139 krb5_key_data *client_key,
140 krb5_keyblock *encrypting_key,
141 krb5_pa_data **send_pa,
142 preauth_get_entry_data_proc get_entry_data,
143 void *pa_system_context,
144 void **pa_request_context);
146 static krb5_error_code
147 return_etype_info2(krb5_context, krb5_pa_data * padata,
148 krb5_db_entry *client,
149 krb5_data *req_pkt,
150 krb5_kdc_req *request, krb5_kdc_rep *reply,
151 krb5_key_data *client_key,
152 krb5_keyblock *encrypting_key,
153 krb5_pa_data **send_pa,
154 preauth_get_entry_data_proc get_entry_data,
155 void *pa_system_context,
156 void **pa_request_context);
158 static krb5_error_code return_pw_salt
159 (krb5_context, krb5_pa_data * padata,
160 krb5_db_entry *client,
161 krb5_data *req_pkt,
162 krb5_kdc_req *request, krb5_kdc_rep *reply,
163 krb5_key_data *client_key,
164 krb5_keyblock *encrypting_key,
165 krb5_pa_data **send_pa,
166 preauth_get_entry_data_proc get_entry_data,
167 void *pa_system_context,
168 void **pa_request_context);
170 /* SAM preauth support */
171 static krb5_error_code verify_sam_response
172 (krb5_context, krb5_db_entry *client,
173 krb5_data *req_pkt,
174 krb5_kdc_req *request,
175 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
176 preauth_get_entry_data_proc get_entry_data,
177 void *pa_module_context,
178 void **pa_request_context,
179 krb5_data **e_data,
180 krb5_authdata ***authz_data);
182 static krb5_error_code get_sam_edata
183 (krb5_context, krb5_kdc_req *request,
184 krb5_db_entry *client, krb5_db_entry *server,
185 preauth_get_entry_data_proc get_entry_data,
186 void *pa_module_context,
187 krb5_pa_data *data);
188 static krb5_error_code return_sam_data
189 (krb5_context, krb5_pa_data * padata,
190 krb5_db_entry *client,
191 krb5_data *req_pkt,
192 krb5_kdc_req *request, krb5_kdc_rep *reply,
193 krb5_key_data *client_key,
194 krb5_keyblock *encrypting_key,
195 krb5_pa_data **send_pa,
196 preauth_get_entry_data_proc get_entry_data,
197 void *pa_module_context,
198 void **pa_request_context);
200 static krb5_preauth_systems static_preauth_systems[] = {
202 "timestamp",
203 KRB5_PADATA_ENC_TIMESTAMP,
205 NULL,
206 NULL,
207 NULL,
209 verify_enc_timestamp,
213 "etype-info",
214 KRB5_PADATA_ETYPE_INFO,
216 NULL,
217 NULL,
218 NULL,
219 get_etype_info,
221 return_etype_info
224 "etype-info2",
225 KRB5_PADATA_ETYPE_INFO2,
227 NULL,
228 NULL,
229 NULL,
230 get_etype_info2,
232 return_etype_info2
235 "pw-salt",
236 KRB5_PADATA_PW_SALT,
237 PA_PSEUDO, /* Don't include this in the error list */
238 NULL,
239 NULL,
240 NULL,
243 return_pw_salt
246 "sam-response",
247 KRB5_PADATA_SAM_RESPONSE,
249 NULL,
250 NULL,
251 NULL,
253 verify_sam_response,
254 return_sam_data
257 "sam-challenge",
258 KRB5_PADATA_SAM_CHALLENGE,
259 PA_HARDWARE, /* causes get_preauth_hint_list to use this */
260 NULL,
261 NULL,
262 NULL,
263 get_sam_edata,
267 { "[end]", -1,}
270 static krb5_preauth_systems *preauth_systems;
271 static int n_preauth_systems;
272 static struct plugin_dir_handle preauth_plugins;
274 krb5_error_code
275 load_preauth_plugins(krb5_context context)
277 struct errinfo err;
278 void **preauth_plugins_ftables;
279 struct krb5plugin_preauth_server_ftable_v1 *ftable;
280 int module_count, i, j, k;
281 void *plugin_context;
282 preauth_server_init_proc server_init_proc = NULL;
283 char **kdc_realm_names = NULL;
285 memset(&err, 0, sizeof(err));
287 /* Attempt to load all of the preauth plugins we can find. */
288 PLUGIN_DIR_INIT(&preauth_plugins);
289 if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) {
290 if (krb5int_open_plugin_dirs(objdirs, NULL,
291 &preauth_plugins, &err) != 0) {
292 return KRB5_PLUGIN_NO_HANDLE;
296 /* Get the method tables provided by the loaded plugins. */
297 preauth_plugins_ftables = NULL;
298 if (krb5int_get_plugin_dir_data(&preauth_plugins,
299 "preauthentication_server_1",
300 &preauth_plugins_ftables, &err) != 0) {
301 return KRB5_PLUGIN_NO_HANDLE;
304 /* Count the valid modules. */
305 module_count = sizeof(static_preauth_systems)
306 / sizeof(static_preauth_systems[0]);
307 if (preauth_plugins_ftables != NULL) {
308 for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
309 ftable = preauth_plugins_ftables[i];
310 if ((ftable->flags_proc == NULL) &&
311 (ftable->edata_proc == NULL) &&
312 (ftable->verify_proc == NULL) &&
313 (ftable->return_proc == NULL)) {
314 continue;
316 for (j = 0;
317 ftable->pa_type_list != NULL &&
318 ftable->pa_type_list[j] > 0;
319 j++) {
320 module_count++;
325 /* Build the complete list of supported preauthentication options, and
326 * leave room for a terminator entry. */
327 preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1));
328 if (preauth_systems == NULL) {
329 krb5int_free_plugin_dir_data(preauth_plugins_ftables);
330 return ENOMEM;
333 /* Build a list of the names of the supported realms for this KDC.
334 * The list of names is terminated with a NULL. */
335 kdc_realm_names = malloc(sizeof(char *) * (kdc_numrealms + 1));
336 if (kdc_realm_names == NULL) {
337 krb5int_free_plugin_dir_data(preauth_plugins_ftables);
338 return ENOMEM;
340 for (i = 0; i < kdc_numrealms; i++) {
341 kdc_realm_names[i] = kdc_realmlist[i]->realm_name;
343 kdc_realm_names[i] = NULL;
345 /* Add the locally-supplied mechanisms to the dynamic list first. */
346 for (i = 0, k = 0;
347 i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
348 i++) {
349 if (static_preauth_systems[i].type == -1)
350 break;
351 preauth_systems[k] = static_preauth_systems[i];
352 /* Try to initialize the preauth system. If it fails, we'll remove it
353 * from the list of systems we'll be using. */
354 plugin_context = NULL;
355 server_init_proc = static_preauth_systems[i].init;
356 if ((server_init_proc != NULL) &&
357 ((*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names) != 0)) {
358 memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
359 continue;
361 preauth_systems[k].plugin_context = plugin_context;
362 k++;
365 /* Now add the dynamically-loaded mechanisms to the list. */
366 if (preauth_plugins_ftables != NULL) {
367 for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
368 ftable = preauth_plugins_ftables[i];
369 if ((ftable->flags_proc == NULL) &&
370 (ftable->edata_proc == NULL) &&
371 (ftable->verify_proc == NULL) &&
372 (ftable->return_proc == NULL)) {
373 continue;
375 plugin_context = NULL;
376 for (j = 0;
377 ftable->pa_type_list != NULL &&
378 ftable->pa_type_list[j] > 0;
379 j++) {
380 /* Try to initialize the plugin. If it fails, we'll remove it
381 * from the list of modules we'll be using. */
382 if (j == 0) {
383 server_init_proc = ftable->init_proc;
384 if (server_init_proc != NULL) {
385 krb5_error_code initerr;
386 initerr = (*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names);
387 if (initerr) {
388 const char *emsg;
389 emsg = krb5_get_error_message(context, initerr);
390 if (emsg) {
391 krb5_klog_syslog(LOG_ERR,
392 "preauth %s failed to initialize: %s",
393 ftable->name, emsg);
394 krb5_free_error_message(context, emsg);
396 memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
398 break; /* skip all modules in this plugin */
402 preauth_systems[k].name = ftable->name;
403 preauth_systems[k].type = ftable->pa_type_list[j];
404 if (ftable->flags_proc != NULL)
405 preauth_systems[k].flags = ftable->flags_proc(context, preauth_systems[k].type);
406 else
407 preauth_systems[k].flags = 0;
408 preauth_systems[k].plugin_context = plugin_context;
409 preauth_systems[k].init = server_init_proc;
410 /* Only call fini once for each plugin */
411 if (j == 0)
412 preauth_systems[k].fini = ftable->fini_proc;
413 else
414 preauth_systems[k].fini = NULL;
415 preauth_systems[k].get_edata = ftable->edata_proc;
416 preauth_systems[k].verify_padata = ftable->verify_proc;
417 preauth_systems[k].return_padata = ftable->return_proc;
418 preauth_systems[k].free_pa_reqctx =
419 ftable->freepa_reqcontext_proc;
420 k++;
423 krb5int_free_plugin_dir_data(preauth_plugins_ftables);
425 free(kdc_realm_names);
426 n_preauth_systems = k;
427 /* Add the end-of-list marker. */
428 preauth_systems[k].name = "[end]";
429 preauth_systems[k].type = -1;
430 return 0;
433 krb5_error_code
434 unload_preauth_plugins(krb5_context context)
436 int i;
437 if (preauth_systems != NULL) {
438 for (i = 0; i < n_preauth_systems; i++) {
439 if (preauth_systems[i].fini != NULL) {
440 (*preauth_systems[i].fini)(context,
441 preauth_systems[i].plugin_context);
443 memset(&preauth_systems[i], 0, sizeof(preauth_systems[i]));
445 free(preauth_systems);
446 preauth_systems = NULL;
447 n_preauth_systems = 0;
448 krb5int_close_plugin_dirs(&preauth_plugins);
450 return 0;
454 * The make_padata_context() function creates a space for storing any context
455 * information which will be needed by return_padata() later. Each preauth
456 * type gets a context storage location of its own.
458 struct request_pa_context {
459 int n_contexts;
460 struct {
461 krb5_preauth_systems *pa_system;
462 void *pa_context;
463 } *contexts;
466 static krb5_error_code
467 make_padata_context(krb5_context context, void **padata_context)
469 int i;
470 struct request_pa_context *ret;
472 ret = malloc(sizeof(*ret));
473 if (ret == NULL) {
474 return ENOMEM;
477 ret->n_contexts = n_preauth_systems;
478 ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
479 if (ret->contexts == NULL) {
480 free(ret);
481 return ENOMEM;
484 memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
486 for (i = 0; i < ret->n_contexts; i++) {
487 ret->contexts[i].pa_system = &preauth_systems[i];
488 ret->contexts[i].pa_context = NULL;
491 *padata_context = ret;
493 return 0;
497 * The free_padata_context function frees any context information pointers
498 * which the check_padata() function created but which weren't already cleaned
499 * up by return_padata().
501 krb5_error_code
502 free_padata_context(krb5_context kcontext, void **padata_context)
504 struct request_pa_context *context;
505 krb5_preauth_systems *preauth_system;
506 void **pctx, *mctx;
507 int i;
509 if (padata_context == NULL)
510 return 0;
512 context = *padata_context;
514 for (i = 0; i < context->n_contexts; i++) {
515 if (context->contexts[i].pa_context != NULL) {
516 preauth_system = context->contexts[i].pa_system;
517 mctx = preauth_system->plugin_context;
518 if (preauth_system->free_pa_reqctx != NULL) {
519 pctx = &context->contexts[i].pa_context;
520 (*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx);
522 context->contexts[i].pa_context = NULL;
526 free(context->contexts);
527 free(context);
529 return 0;
532 /* Retrieve a specified tl_data item from the given entry, and return its
533 * contents in a new krb5_data, which must be freed by the caller. */
534 static krb5_error_code
535 get_entry_tl_data(krb5_context context, krb5_db_entry *entry,
536 krb5_int16 tl_data_type, krb5_data **result)
538 krb5_tl_data *tl;
539 for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
540 if (tl->tl_data_type == tl_data_type) {
541 *result = malloc(sizeof(krb5_data));
542 if (*result == NULL) {
543 return ENOMEM;
545 (*result)->magic = KV5M_DATA;
546 (*result)->data = malloc(tl->tl_data_length);
547 if ((*result)->data == NULL) {
548 free(*result);
549 *result = NULL;
550 return ENOMEM;
552 memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length);
553 return 0;
556 return ENOENT;
560 * Retrieve a specific piece of information pertaining to the entry or the
561 * request and return it in a new krb5_data item which the caller must free.
563 * This may require massaging data into a contrived format, but it will
564 * hopefully keep us from having to reveal library-internal functions to
565 * modules.
567 static krb5_error_code
568 get_entry_data(krb5_context context,
569 krb5_kdc_req *request, krb5_db_entry *entry,
570 krb5_int32 type,
571 krb5_data **result)
573 int i, k;
574 krb5_data *ret;
575 krb5_deltat *delta;
576 krb5_keyblock *keys;
577 krb5_key_data *entry_key;
579 switch (type) {
580 case krb5plugin_preauth_entry_request_certificate:
581 return get_entry_tl_data(context, entry,
582 KRB5_TL_USER_CERTIFICATE, result);
583 break;
584 case krb5plugin_preauth_entry_max_time_skew:
585 ret = malloc(sizeof(krb5_data));
586 if (ret == NULL)
587 return ENOMEM;
588 delta = malloc(sizeof(krb5_deltat));
589 if (delta == NULL) {
590 free(ret);
591 return ENOMEM;
593 *delta = context->clockskew;
594 ret->data = (char *) delta;
595 ret->length = sizeof(*delta);
596 *result = ret;
597 return 0;
598 break;
599 case krb5plugin_preauth_keys:
600 ret = malloc(sizeof(krb5_data));
601 if (ret == NULL)
602 return ENOMEM;
603 keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1));
604 if (keys == NULL) {
605 free(ret);
606 return ENOMEM;
608 ret->data = (char *) keys;
609 ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
610 memset(ret->data, 0, ret->length);
611 k = 0;
612 for (i = 0; i < request->nktypes; i++) {
613 entry_key = NULL;
614 if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
615 -1, 0, &entry_key) != 0)
616 continue;
617 if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
618 entry_key, &keys[k], NULL) != 0) {
619 if (keys[k].contents != NULL)
620 krb5_free_keyblock_contents(context, &keys[k]);
621 memset(&keys[k], 0, sizeof(keys[k]));
622 continue;
624 k++;
626 if (k > 0) {
627 *result = ret;
628 return 0;
629 } else {
630 free(keys);
631 free(ret);
633 break;
634 case krb5plugin_preauth_request_body:
635 ret = NULL;
636 encode_krb5_kdc_req_body(request, &ret);
637 if (ret != NULL) {
638 *result = ret;
639 return 0;
641 return ASN1_PARSE_ERROR;
642 break;
643 default:
644 break;
646 return ENOENT;
649 static krb5_error_code
650 find_pa_system(int type, krb5_preauth_systems **preauth)
652 krb5_preauth_systems *ap;
654 ap = preauth_systems ? preauth_systems : static_preauth_systems;
655 while ((ap->type != -1) && (ap->type != type))
656 ap++;
657 if (ap->type == -1)
658 return(KRB5_PREAUTH_BAD_TYPE);
659 *preauth = ap;
660 return 0;
663 static krb5_error_code
664 find_pa_context(krb5_preauth_systems *pa_sys,
665 struct request_pa_context *context,
666 void ***pa_context)
668 int i;
670 *pa_context = 0;
672 if (context == NULL)
673 return KRB5KRB_ERR_GENERIC;
675 for (i = 0; i < context->n_contexts; i++) {
676 if (context->contexts[i].pa_system == pa_sys) {
677 *pa_context = &context->contexts[i].pa_context;
678 return 0;
682 return KRB5KRB_ERR_GENERIC;
686 * Create a list of indices into the preauth_systems array, sorted by order of
687 * preference.
689 static krb5_boolean
690 pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
692 while (*pa_data != NULL) {
693 if ((*pa_data)->pa_type == pa_type)
694 return TRUE;
695 pa_data++;
697 return FALSE;
699 static void
700 sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
702 int i, j, k, n_repliers, n_key_replacers;
704 /* First, set up the default order. */
705 i = 0;
706 for (j = 0; j < n_preauth_systems; j++) {
707 if (preauth_systems[j].return_padata != NULL)
708 pa_order[i++] = j;
710 n_repliers = i;
711 pa_order[n_repliers] = -1;
713 /* Reorder so that PA_REPLACES_KEY modules are listed first. */
714 for (i = 0; i < n_repliers; i++) {
715 /* If this module replaces the key, then it's okay to leave it where it
716 * is in the order. */
717 if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
718 continue;
719 /* If not, search for a module which does, and swap in the first one we
720 * find. */
721 for (j = i + 1; j < n_repliers; j++) {
722 if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
723 k = pa_order[j];
724 pa_order[j] = pa_order[i];
725 pa_order[i] = k;
726 break;
731 if (request->padata != NULL) {
732 /* Now reorder the subset of modules which replace the key,
733 * bubbling those which handle pa_data types provided by the
734 * client ahead of the others. */
735 for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
736 continue;
738 n_key_replacers = i;
739 for (i = 0; i < n_key_replacers; i++) {
740 if (pa_list_includes(request->padata,
741 preauth_systems[pa_order[i]].type))
742 continue;
743 for (j = i + 1; j < n_key_replacers; j++) {
744 if (pa_list_includes(request->padata,
745 preauth_systems[pa_order[j]].type)) {
746 k = pa_order[j];
747 pa_order[j] = pa_order[i];
748 pa_order[i] = k;
749 break;
754 #ifdef DEBUG
755 krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
756 for (i = 0; i < n_preauth_systems; i++) {
757 if (preauth_systems[i].return_padata != NULL)
758 krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
759 preauth_systems[i].type);
761 krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
762 for (i = 0; pa_order[i] != -1; i++) {
763 krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
764 preauth_systems[pa_order[i]].name,
765 preauth_systems[pa_order[i]].type);
767 #endif
770 const char *missing_required_preauth(krb5_db_entry *client,
771 krb5_db_entry *server,
772 krb5_enc_tkt_part *enc_tkt_reply)
774 #if 0
776 * If this is the pwchange service, and the pre-auth bit is set,
777 * allow it even if the HW preauth would normally be required.
779 * Sandia national labs wanted this for some strange reason... we
780 * leave it disabled normally.
782 if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) &&
783 isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
784 return 0;
785 #endif
787 #ifdef DEBUG
788 krb5_klog_syslog (LOG_DEBUG,
789 "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
790 isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
791 isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
792 isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
793 isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
794 #endif
796 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
797 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
798 return "NEEDED_PREAUTH";
800 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
801 !isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
802 return "NEEDED_HW_PREAUTH";
804 return 0;
807 void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client,
808 krb5_db_entry *server, krb5_data *e_data)
810 int hw_only;
811 krb5_preauth_systems *ap;
812 krb5_pa_data **pa_data, **pa;
813 krb5_data *edat;
814 krb5_error_code retval;
816 /* Zero these out in case we need to abort */
817 e_data->length = 0;
818 e_data->data = 0;
820 hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
821 pa_data = malloc(sizeof(krb5_pa_data *) * (n_preauth_systems+1));
822 if (pa_data == 0)
823 return;
824 memset(pa_data, 0, sizeof(krb5_pa_data *) * (n_preauth_systems+1));
825 pa = pa_data;
827 for (ap = preauth_systems; ap->type != -1; ap++) {
828 if (hw_only && !(ap->flags & PA_HARDWARE))
829 continue;
830 if (ap->flags & PA_PSEUDO)
831 continue;
832 *pa = malloc(sizeof(krb5_pa_data));
833 if (*pa == 0)
834 goto errout;
835 memset(*pa, 0, sizeof(krb5_pa_data));
836 (*pa)->magic = KV5M_PA_DATA;
837 (*pa)->pa_type = ap->type;
838 if (ap->get_edata) {
839 retval = (ap->get_edata)(kdc_context, request, client, server,
840 get_entry_data, ap->plugin_context, *pa);
841 if (retval) {
842 /* just failed on this type, continue */
843 free(*pa);
844 *pa = 0;
845 continue;
848 pa++;
850 if (pa_data[0] == 0) {
851 krb5_klog_syslog (LOG_INFO,
852 "%spreauth required but hint list is empty",
853 hw_only ? "hw" : "");
855 retval = encode_krb5_padata_sequence((krb5_pa_data * const *) pa_data,
856 &edat);
857 if (retval)
858 goto errout;
859 *e_data = *edat;
860 free(edat);
862 errout:
863 krb5_free_pa_data(kdc_context, pa_data);
864 return;
868 * Add authorization data returned from preauth modules to the ticket
869 * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
871 static krb5_error_code
872 add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
874 krb5_authdata **newad;
875 int oldones, newones;
876 int i;
878 if (enc_tkt_part == NULL || ad == NULL)
879 return EINVAL;
881 for (newones = 0; ad[newones] != NULL; newones++);
882 if (newones == 0)
883 return 0; /* nothing to add */
885 if (enc_tkt_part->authorization_data == NULL)
886 oldones = 0;
887 else
888 for (oldones = 0;
889 enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
891 newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
892 if (newad == NULL)
893 return ENOMEM;
895 /* Copy any existing pointers */
896 for (i = 0; i < oldones; i++)
897 newad[i] = enc_tkt_part->authorization_data[i];
899 /* Add the new ones */
900 for (i = 0; i < newones; i++)
901 newad[oldones+i] = ad[i];
903 /* Terminate the new list */
904 newad[oldones+i] = NULL;
906 /* Free any existing list */
907 free(enc_tkt_part->authorization_data);
909 /* Install our new list */
910 enc_tkt_part->authorization_data = newad;
912 return 0;
916 * This routine is called to verify the preauthentication information
917 * for a V5 request.
919 * Returns 0 if the pre-authentication is valid, non-zero to indicate
920 * an error code of some sort.
923 krb5_error_code
924 check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
925 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
926 void **padata_context, krb5_data *e_data)
928 krb5_error_code retval = 0;
929 krb5_pa_data **padata;
930 krb5_preauth_systems *pa_sys;
931 void **pa_context;
932 krb5_data *pa_e_data = NULL, *tmp_e_data = NULL;
933 int pa_ok = 0, pa_found = 0;
934 krb5_error_code saved_retval = 0;
935 int use_saved_retval = 0;
936 const char *emsg;
937 krb5_authdata **tmp_authz_data = NULL;
939 if (request->padata == 0)
940 return 0;
942 if (make_padata_context(context, padata_context) != 0) {
943 return KRB5KRB_ERR_GENERIC;
946 #ifdef DEBUG
947 krb5_klog_syslog (LOG_DEBUG, "checking padata");
948 #endif
949 for (padata = request->padata; *padata; padata++) {
950 #ifdef DEBUG
951 krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type);
952 #endif
953 if (find_pa_system((*padata)->pa_type, &pa_sys))
954 continue;
955 if (find_pa_context(pa_sys, *padata_context, &pa_context))
956 continue;
957 #ifdef DEBUG
958 krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
959 #endif
960 if (pa_sys->verify_padata == 0)
961 continue;
962 pa_found++;
963 retval = pa_sys->verify_padata(context, client, req_pkt, request,
964 enc_tkt_reply, *padata,
965 get_entry_data, pa_sys->plugin_context,
966 pa_context, &tmp_e_data, &tmp_authz_data);
967 if (retval) {
968 emsg = krb5_get_error_message (context, retval);
969 krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
970 pa_sys->name, emsg);
971 krb5_free_error_message (context, emsg);
972 /* Ignore authorization data returned from modules that fail */
973 if (tmp_authz_data != NULL) {
974 krb5_free_authdata(context, tmp_authz_data);
975 tmp_authz_data = NULL;
977 if (pa_sys->flags & PA_REQUIRED) {
978 /* free up any previous edata we might have been saving */
979 if (pa_e_data != NULL)
980 krb5_free_data(context, pa_e_data);
981 pa_e_data = tmp_e_data;
982 tmp_e_data = NULL;
983 use_saved_retval = 0; /* Make sure we use the current retval */
984 pa_ok = 0;
985 break;
988 * We'll return edata from either the first PA_REQUIRED module
989 * that fails, or the first non-PA_REQUIRED module that fails.
990 * Hang on to edata from the first non-PA_REQUIRED module.
991 * If we've already got one saved, simply discard this one.
993 if (tmp_e_data != NULL) {
994 if (pa_e_data == NULL) {
995 /* save the first error code and e-data */
996 pa_e_data = tmp_e_data;
997 tmp_e_data = NULL;
998 saved_retval = retval;
999 use_saved_retval = 1;
1000 } else {
1001 /* discard this extra e-data from non-PA_REQUIRED module */
1002 krb5_free_data(context, tmp_e_data);
1003 tmp_e_data = NULL;
1006 } else {
1007 #ifdef DEBUG
1008 krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
1009 #endif
1010 /* Ignore any edata returned on success */
1011 if (tmp_e_data != NULL) {
1012 krb5_free_data(context, tmp_e_data);
1013 tmp_e_data = NULL;
1015 /* Add any authorization data to the ticket */
1016 if (tmp_authz_data != NULL) {
1017 add_authorization_data(enc_tkt_reply, tmp_authz_data);
1018 free(tmp_authz_data);
1019 tmp_authz_data = NULL;
1021 pa_ok = 1;
1022 if (pa_sys->flags & PA_SUFFICIENT)
1023 break;
1027 /* Don't bother copying and returning e-data on success */
1028 if (pa_ok && pa_e_data != NULL) {
1029 krb5_free_data(context, pa_e_data);
1030 pa_e_data = NULL;
1032 /* Return any e-data from the preauth that caused us to exit the loop */
1033 if (pa_e_data != NULL) {
1034 e_data->data = malloc(pa_e_data->length);
1035 if (e_data->data == NULL) {
1036 krb5_free_data(context, pa_e_data);
1037 /* Solaris Kerberos */
1038 return ENOMEM;
1040 memcpy(e_data->data, pa_e_data->data, pa_e_data->length);
1041 e_data->length = pa_e_data->length;
1042 krb5_free_data(context, pa_e_data);
1043 pa_e_data = NULL;
1044 if (use_saved_retval != 0)
1045 retval = saved_retval;
1048 if (pa_ok)
1049 return 0;
1051 /* pa system was not found, but principal doesn't require preauth */
1052 if (!pa_found &&
1053 !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1054 !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
1055 return 0;
1057 if (!pa_found) {
1058 emsg = krb5_get_error_message(context, retval);
1059 krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
1060 krb5_free_error_message(context, emsg);
1062 /* The following switch statement allows us
1063 * to return some preauth system errors back to the client.
1065 switch(retval) {
1066 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
1067 case KRB5KRB_AP_ERR_SKEW:
1068 case KRB5KDC_ERR_ETYPE_NOSUPP:
1069 /* rfc 4556 */
1070 case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
1071 case KRB5KDC_ERR_INVALID_SIG:
1072 case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
1073 case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
1074 case KRB5KDC_ERR_INVALID_CERTIFICATE:
1075 case KRB5KDC_ERR_REVOKED_CERTIFICATE:
1076 case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
1077 case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
1078 case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
1079 case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
1080 case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
1081 case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
1082 case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
1083 /* earlier drafts of what became rfc 4556 */
1084 case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
1085 case KRB5KDC_ERR_KDC_NOT_TRUSTED:
1086 case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
1087 /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
1088 /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
1089 return retval;
1090 default:
1091 return KRB5KDC_ERR_PREAUTH_FAILED;
1096 * return_padata creates any necessary preauthentication
1097 * structures which should be returned by the KDC to the client
1099 krb5_error_code
1100 return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
1101 krb5_kdc_req *request, krb5_kdc_rep *reply,
1102 krb5_key_data *client_key, krb5_keyblock *encrypting_key,
1103 void **padata_context)
1105 krb5_error_code retval;
1106 krb5_pa_data ** padata;
1107 krb5_pa_data ** send_pa_list;
1108 krb5_pa_data ** send_pa;
1109 krb5_pa_data * pa = 0;
1110 krb5_preauth_systems * ap;
1111 int * pa_order;
1112 int * pa_type;
1113 int size = 0;
1114 void ** pa_context;
1115 krb5_boolean key_modified;
1116 krb5_keyblock original_key;
1117 if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) {
1118 return KRB5KRB_ERR_GENERIC;
1121 for (ap = preauth_systems; ap->type != -1; ap++) {
1122 if (ap->return_padata)
1123 size++;
1126 if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
1127 return ENOMEM;
1128 if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
1129 free(send_pa_list);
1130 return ENOMEM;
1132 sort_pa_order(context, request, pa_order);
1134 retval = krb5_copy_keyblock_contents(context, encrypting_key,
1135 &original_key);
1136 if (retval) {
1137 free(send_pa_list);
1138 free(pa_order);
1139 return retval;
1141 key_modified = FALSE;
1143 send_pa = send_pa_list;
1144 *send_pa = 0;
1146 for (pa_type = pa_order; *pa_type != -1; pa_type++) {
1147 ap = &preauth_systems[*pa_type];
1148 if (!key_modified)
1149 if (original_key.enctype != encrypting_key->enctype)
1150 key_modified = TRUE;
1151 if (!key_modified)
1152 if (original_key.length != encrypting_key->length)
1153 key_modified = TRUE;
1154 if (!key_modified)
1155 if (memcmp(original_key.contents, encrypting_key->contents,
1156 original_key.length) != 0)
1157 key_modified = TRUE;
1158 if (key_modified && (ap->flags & PA_REPLACES_KEY))
1159 continue;
1160 if (ap->return_padata == 0)
1161 continue;
1162 if (find_pa_context(ap, *padata_context, &pa_context))
1163 continue;
1164 pa = 0;
1165 if (request->padata) {
1166 for (padata = request->padata; *padata; padata++) {
1167 if ((*padata)->pa_type == ap->type) {
1168 pa = *padata;
1169 break;
1173 if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
1174 client_key, encrypting_key, send_pa,
1175 get_entry_data, ap->plugin_context,
1176 pa_context))) {
1177 goto cleanup;
1180 if (*send_pa)
1181 send_pa++;
1182 *send_pa = 0;
1185 retval = 0;
1187 if (send_pa_list[0]) {
1188 reply->padata = send_pa_list;
1189 send_pa_list = 0;
1192 cleanup:
1193 if (send_pa_list)
1194 krb5_free_pa_data(context, send_pa_list);
1196 /* Solaris Kerberos */
1197 krb5_free_keyblock_contents(context, &original_key);
1198 free(pa_order);
1200 return (retval);
1203 static krb5_boolean
1204 enctype_requires_etype_info_2(krb5_enctype enctype)
1206 switch(enctype) {
1207 case ENCTYPE_DES_CBC_CRC:
1208 case ENCTYPE_DES_CBC_MD4:
1209 case ENCTYPE_DES_CBC_MD5:
1210 case ENCTYPE_DES3_CBC_SHA1:
1211 case ENCTYPE_DES3_CBC_RAW:
1212 case ENCTYPE_ARCFOUR_HMAC:
1213 case ENCTYPE_ARCFOUR_HMAC_EXP :
1214 return 0;
1215 default:
1216 if (krb5_c_valid_enctype(enctype))
1217 return 1;
1218 else return 0;
1222 static krb5_boolean
1223 request_contains_enctype (krb5_context context, const krb5_kdc_req *request,
1224 krb5_enctype enctype)
1226 int i;
1227 for (i =0; i < request->nktypes; i++)
1228 if (request->ktype[i] == enctype)
1229 return 1;
1230 return 0;
1234 static krb5_error_code
1235 verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
1236 krb5_data *req_pkt,
1237 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
1238 krb5_pa_data *pa,
1239 preauth_get_entry_data_proc ets_get_entry_data,
1240 void *pa_system_context,
1241 void **pa_request_context,
1242 krb5_data **e_data,
1243 krb5_authdata ***authz_data)
1245 krb5_pa_enc_ts * pa_enc = 0;
1246 krb5_error_code retval;
1247 krb5_data scratch;
1248 krb5_data enc_ts_data;
1249 krb5_enc_data *enc_data = 0;
1250 krb5_keyblock key;
1251 krb5_key_data * client_key;
1252 krb5_int32 start;
1253 krb5_timestamp timenow;
1254 krb5_error_code decrypt_err = 0;
1256 (void) memset(&key, 0, sizeof(krb5_keyblock));
1257 scratch.data = (char *) pa->contents;
1258 scratch.length = pa->length;
1260 enc_ts_data.data = 0;
1262 if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
1263 goto cleanup;
1265 enc_ts_data.length = enc_data->ciphertext.length;
1266 if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
1267 goto cleanup;
1269 start = 0;
1270 decrypt_err = 0;
1271 while (1) {
1272 if ((retval = krb5_dbe_search_enctype(context, client,
1273 &start, enc_data->enctype,
1274 -1, 0, &client_key)))
1275 goto cleanup;
1277 if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
1278 client_key, &key, NULL)))
1279 goto cleanup;
1281 key.enctype = enc_data->enctype;
1283 retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
1284 0, enc_data, &enc_ts_data);
1285 krb5_free_keyblock_contents(context, &key);
1286 if (retval == 0)
1287 break;
1288 else
1289 decrypt_err = retval;
1292 if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
1293 goto cleanup;
1295 if ((retval = krb5_timeofday(context, &timenow)) != 0)
1296 goto cleanup;
1298 if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
1299 retval = KRB5KRB_AP_ERR_SKEW;
1300 goto cleanup;
1303 setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
1305 retval = 0;
1307 cleanup:
1308 if (enc_data) {
1309 krb5_free_data_contents(context, &enc_data->ciphertext);
1310 free(enc_data);
1312 krb5_free_data_contents(context, &enc_ts_data);
1313 free(pa_enc);
1315 * If we get NO_MATCHING_KEY and decryption previously failed, and
1316 * we failed to find any other keys of the correct enctype after
1317 * that failed decryption, it probably means that the password was
1318 * incorrect.
1320 if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
1321 retval = decrypt_err;
1322 return retval;
1325 static krb5_error_code
1326 _make_etype_info_entry(krb5_context context,
1327 krb5_kdc_req *request, krb5_key_data *client_key,
1328 krb5_enctype etype, krb5_etype_info_entry **entry,
1329 int etype_info2)
1331 krb5_data salt;
1332 krb5_etype_info_entry * tmp_entry;
1333 krb5_error_code retval;
1335 if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL)
1336 return ENOMEM;
1338 salt.data = 0;
1340 tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY;
1341 tmp_entry->etype = etype;
1342 tmp_entry->length = KRB5_ETYPE_NO_SALT;
1343 tmp_entry->salt = 0;
1344 tmp_entry->s2kparams.data = NULL;
1345 tmp_entry->s2kparams.length = 0;
1346 retval = get_salt_from_key(context, request->client,
1347 client_key, &salt);
1348 if (retval)
1349 goto fail;
1350 if (etype_info2 && client_key->key_data_ver > 1 &&
1351 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) {
1352 switch (etype) {
1353 case ENCTYPE_DES_CBC_CRC:
1354 case ENCTYPE_DES_CBC_MD4:
1355 case ENCTYPE_DES_CBC_MD5:
1356 tmp_entry->s2kparams.data = malloc(1);
1357 if (tmp_entry->s2kparams.data == NULL) {
1358 retval = ENOMEM;
1359 goto fail;
1361 tmp_entry->s2kparams.length = 1;
1362 tmp_entry->s2kparams.data[0] = 1;
1363 break;
1364 default:
1365 break;
1369 if (salt.length >= 0) {
1370 tmp_entry->length = salt.length;
1371 tmp_entry->salt = (unsigned char *) salt.data;
1372 salt.data = 0;
1374 *entry = tmp_entry;
1375 return 0;
1377 fail:
1378 if (tmp_entry) {
1379 free(tmp_entry->s2kparams.data);
1380 free(tmp_entry);
1382 free(salt.data);
1383 return retval;
1386 * This function returns the etype information for a particular
1387 * client, to be passed back in the preauth list in the KRB_ERROR
1388 * message. It supports generating both etype_info and etype_info2
1389 * as most of the work is the same.
1391 static krb5_error_code
1392 etype_info_helper(krb5_context context, krb5_kdc_req *request,
1393 krb5_db_entry *client, krb5_db_entry *server,
1394 krb5_pa_data *pa_data, int etype_info2)
1396 krb5_etype_info_entry ** entry = 0;
1397 krb5_key_data *client_key;
1398 krb5_error_code retval;
1399 krb5_data * scratch;
1400 krb5_enctype db_etype;
1401 int i = 0;
1402 int start = 0;
1403 int seen_des = 0;
1405 entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
1406 if (entry == NULL)
1407 return ENOMEM;
1408 entry[0] = NULL;
1410 while (1) {
1411 retval = krb5_dbe_search_enctype(context, client, &start, -1,
1412 -1, 0, &client_key);
1413 if (retval == KRB5_KDB_NO_MATCHING_KEY)
1414 break;
1415 if (retval)
1416 goto cleanup;
1417 db_etype = client_key->key_data_type[0];
1418 if (db_etype == ENCTYPE_DES_CBC_MD4)
1419 db_etype = ENCTYPE_DES_CBC_MD5;
1421 if (request_contains_enctype(context, request, db_etype)) {
1422 assert(etype_info2 ||
1423 !enctype_requires_etype_info_2(db_etype));
1424 if ((retval = _make_etype_info_entry(context, request, client_key,
1425 db_etype, &entry[i], etype_info2)) != 0) {
1426 goto cleanup;
1428 entry[i+1] = 0;
1429 i++;
1433 * If there is a des key in the kdb, try the "similar" enctypes,
1434 * avoid duplicate entries.
1436 if (!seen_des) {
1437 switch (db_etype) {
1438 case ENCTYPE_DES_CBC_MD5:
1439 db_etype = ENCTYPE_DES_CBC_CRC;
1440 break;
1441 case ENCTYPE_DES_CBC_CRC:
1442 db_etype = ENCTYPE_DES_CBC_MD5;
1443 break;
1444 default:
1445 continue;
1448 if (request_contains_enctype(context, request, db_etype)) {
1449 if ((retval = _make_etype_info_entry(context, request,
1450 client_key, db_etype, &entry[i], etype_info2)) != 0) {
1451 goto cleanup;
1453 entry[i+1] = 0;
1454 i++;
1456 seen_des++;
1459 if (etype_info2)
1460 retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry,
1461 &scratch);
1462 else retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry,
1463 &scratch);
1464 if (retval)
1465 goto cleanup;
1466 pa_data->contents = (unsigned char *)scratch->data;
1467 pa_data->length = scratch->length;
1468 free(scratch);
1470 retval = 0;
1472 cleanup:
1473 if (entry)
1474 krb5_free_etype_info(context, entry);
1475 return retval;
1478 static krb5_error_code
1479 get_etype_info(krb5_context context, krb5_kdc_req *request,
1480 krb5_db_entry *client, krb5_db_entry *server,
1481 preauth_get_entry_data_proc etype_get_entry_data,
1482 void *pa_system_context,
1483 krb5_pa_data *pa_data)
1485 int i;
1486 for (i=0; i < request->nktypes; i++) {
1487 if (enctype_requires_etype_info_2(request->ktype[i]))
1488 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
1489 * skip this
1490 * type*/
1492 return etype_info_helper(context, request, client, server, pa_data, 0);
1495 static krb5_error_code
1496 get_etype_info2(krb5_context context, krb5_kdc_req *request,
1497 krb5_db_entry *client, krb5_db_entry *server,
1498 preauth_get_entry_data_proc etype_get_entry_data,
1499 void *pa_system_context,
1500 krb5_pa_data *pa_data)
1502 return etype_info_helper( context, request, client, server, pa_data, 1);
1505 static krb5_error_code
1506 etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
1507 krb5_db_entry *client,
1508 krb5_kdc_req *request, krb5_kdc_rep *reply,
1509 krb5_key_data *client_key,
1510 krb5_keyblock *encrypting_key,
1511 krb5_pa_data **send_pa,
1512 int etype_info2)
1514 int i;
1515 krb5_error_code retval;
1516 krb5_pa_data *tmp_padata;
1517 krb5_etype_info_entry **entry = NULL;
1518 krb5_data *scratch = NULL;
1521 * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
1522 * enctypes.
1524 if (!etype_info2) {
1525 for (i = 0; i < request->nktypes; i++) {
1526 if (enctype_requires_etype_info_2(request->ktype[i])) {
1527 *send_pa = NULL;
1528 return 0;
1533 tmp_padata = malloc( sizeof(krb5_pa_data));
1534 if (tmp_padata == NULL)
1535 return ENOMEM;
1536 if (etype_info2)
1537 tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
1538 else
1539 tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO;
1541 entry = malloc(2 * sizeof(krb5_etype_info_entry *));
1542 if (entry == NULL) {
1543 retval = ENOMEM;
1544 goto cleanup;
1546 entry[0] = NULL;
1547 entry[1] = NULL;
1548 retval = _make_etype_info_entry(context, request,
1549 client_key, encrypting_key->enctype,
1550 entry, etype_info2);
1551 if (retval)
1552 goto cleanup;
1554 if (etype_info2)
1555 retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry, &scratch);
1556 else
1557 retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry, &scratch);
1559 if (retval)
1560 goto cleanup;
1561 tmp_padata->contents = (uchar_t *)scratch->data;
1562 tmp_padata->length = scratch->length;
1563 *send_pa = tmp_padata;
1565 /* For cleanup - we no longer own the contents of the krb5_data
1566 * only to pointer to the krb5_data
1568 scratch->data = 0;
1570 cleanup:
1571 if (entry)
1572 krb5_free_etype_info(context, entry);
1573 if (retval) {
1574 free(tmp_padata);
1576 if (scratch)
1577 krb5_free_data(context, scratch);
1578 return retval;
1581 static krb5_error_code
1582 return_etype_info2(krb5_context context, krb5_pa_data * padata,
1583 krb5_db_entry *client,
1584 krb5_data *req_pkt,
1585 krb5_kdc_req *request, krb5_kdc_rep *reply,
1586 krb5_key_data *client_key,
1587 krb5_keyblock *encrypting_key,
1588 krb5_pa_data **send_pa,
1589 preauth_get_entry_data_proc etype_get_entry_data,
1590 void *pa_system_context,
1591 void **pa_request_context)
1593 return etype_info_as_rep_helper(context, padata, client, request, reply,
1594 client_key, encrypting_key, send_pa, 1);
1598 static krb5_error_code
1599 return_etype_info(krb5_context context, krb5_pa_data * padata,
1600 krb5_db_entry *client,
1601 krb5_data *req_pkt,
1602 krb5_kdc_req *request, krb5_kdc_rep *reply,
1603 krb5_key_data *client_key,
1604 krb5_keyblock *encrypting_key,
1605 krb5_pa_data **send_pa,
1606 preauth_get_entry_data_proc etypeget_entry_data,
1607 void *pa_system_context,
1608 void **pa_request_context)
1610 return etype_info_as_rep_helper(context, padata, client, request, reply,
1611 client_key, encrypting_key, send_pa, 0);
1614 static krb5_error_code
1615 return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
1616 krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
1617 krb5_kdc_rep *reply, krb5_key_data *client_key,
1618 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1619 preauth_get_entry_data_proc etype_get_entry_data,
1620 void *pa_system_context,
1621 void **pa_request_context)
1623 krb5_error_code retval;
1624 krb5_pa_data * padata;
1625 krb5_data * scratch;
1626 krb5_data salt_data;
1627 int i;
1629 for (i = 0; i < request->nktypes; i++) {
1630 if (enctype_requires_etype_info_2(request->ktype[i]))
1631 return 0;
1633 if (client_key->key_data_ver == 1 ||
1634 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
1635 return 0;
1637 if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
1638 return ENOMEM;
1639 padata->magic = KV5M_PA_DATA;
1640 padata->pa_type = KRB5_PADATA_PW_SALT;
1642 switch (client_key->key_data_type[1]) {
1643 case KRB5_KDB_SALTTYPE_V4:
1644 /* send an empty (V4) salt */
1645 padata->contents = 0;
1646 padata->length = 0;
1647 break;
1648 case KRB5_KDB_SALTTYPE_NOREALM:
1649 if ((retval = krb5_principal2salt_norealm(kdc_context,
1650 request->client,
1651 &salt_data)))
1652 goto cleanup;
1653 padata->contents = (krb5_octet *)salt_data.data;
1654 padata->length = salt_data.length;
1655 break;
1656 case KRB5_KDB_SALTTYPE_AFS3:
1657 /* send an AFS style realm-based salt */
1658 /* for now, just pass the realm back and let the client
1659 do the work. In the future, add a kdc configuration
1660 variable that specifies the old cell name. */
1661 padata->pa_type = KRB5_PADATA_AFS3_SALT;
1662 /* it would be just like ONLYREALM, but we need to pass the 0 */
1663 scratch = krb5_princ_realm(kdc_context, request->client);
1664 if ((padata->contents = malloc(scratch->length+1)) == NULL) {
1665 retval = ENOMEM;
1666 goto cleanup;
1668 memcpy(padata->contents, scratch->data, scratch->length);
1669 padata->length = scratch->length+1;
1670 padata->contents[scratch->length] = 0;
1671 break;
1672 case KRB5_KDB_SALTTYPE_ONLYREALM:
1673 scratch = krb5_princ_realm(kdc_context, request->client);
1674 if ((padata->contents = malloc(scratch->length)) == NULL) {
1675 retval = ENOMEM;
1676 goto cleanup;
1678 memcpy(padata->contents, scratch->data, scratch->length);
1679 padata->length = scratch->length;
1680 break;
1681 case KRB5_KDB_SALTTYPE_SPECIAL:
1682 if ((padata->contents = malloc(client_key->key_data_length[1]))
1683 == NULL) {
1684 retval = ENOMEM;
1685 goto cleanup;
1687 memcpy(padata->contents, client_key->key_data_contents[1],
1688 client_key->key_data_length[1]);
1689 padata->length = client_key->key_data_length[1];
1690 break;
1691 default:
1692 free(padata);
1693 return 0;
1696 *send_pa = padata;
1697 return 0;
1699 cleanup:
1700 free(padata);
1701 return retval;
1704 static krb5_error_code
1705 return_sam_data(krb5_context context, krb5_pa_data *in_padata,
1706 krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
1707 krb5_kdc_rep *reply, krb5_key_data *client_key,
1708 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1709 preauth_get_entry_data_proc sam_get_entry_data,
1710 void *pa_system_context,
1711 void **pa_request_context)
1713 krb5_error_code retval;
1714 krb5_data scratch;
1715 int i;
1717 krb5_sam_response *sr = 0;
1718 krb5_predicted_sam_response *psr = 0;
1720 if (in_padata == 0)
1721 return 0;
1724 * We start by doing the same thing verify_sam_response() does:
1725 * extract the psr from the padata (which is an sr). Nothing
1726 * here should generate errors! We've already successfully done
1727 * all this once.
1730 scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
1731 scratch.length = in_padata->length;
1733 if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
1734 com_err("krb5kdc", retval,
1735 gettext("return_sam_data(): decode_krb5_sam_response failed"));
1736 goto cleanup;
1740 krb5_enc_data tmpdata;
1742 tmpdata.enctype = ENCTYPE_UNKNOWN;
1743 tmpdata.ciphertext = sr->sam_track_id;
1745 scratch.length = tmpdata.ciphertext.length;
1746 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
1747 retval = ENOMEM;
1748 goto cleanup;
1751 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
1752 &tmpdata, &scratch))) {
1753 com_err("krb5kdc", retval,
1754 gettext("return_sam_data(): decrypt track_id failed"));
1755 free(scratch.data);
1756 goto cleanup;
1760 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
1761 com_err("krb5kdc", retval,
1762 gettext(
1763 "return_sam_data(): decode_krb5_predicted_sam_response failed"));
1764 free(scratch.data);
1765 goto cleanup;
1768 /* We could use sr->sam_flags, but it may be absent or altered. */
1769 if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
1770 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1771 gettext("Unsupported SAM flag must-pk-encrypt-sad"));
1772 goto cleanup;
1774 if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
1775 /* No key munging */
1776 goto cleanup;
1778 if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
1779 /* Use sam_key instead of client key */
1780 krb5_free_keyblock_contents(context, encrypting_key);
1781 krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
1782 /* XXX Attach a useful pa_data */
1783 goto cleanup;
1786 /* Otherwise (no flags set), we XOR the keys */
1787 /* XXX The passwords-04 draft is underspecified here wrt different
1788 key types. We will do what I hope to get into the -05 draft. */
1790 krb5_octet *p = encrypting_key->contents;
1791 krb5_octet *q = psr->sam_key.contents;
1792 int length = ((encrypting_key->length < psr->sam_key.length)
1793 ? encrypting_key->length
1794 : psr->sam_key.length);
1796 for (i = 0; i < length; i++)
1797 p[i] ^= q[i];
1800 /* Post-mixing key correction */
1801 switch (encrypting_key->enctype) {
1802 case ENCTYPE_DES_CBC_CRC:
1803 case ENCTYPE_DES_CBC_MD4:
1804 case ENCTYPE_DES_CBC_MD5:
1805 case ENCTYPE_DES_CBC_RAW:
1806 mit_des_fixup_key_parity(encrypting_key->contents);
1807 if (mit_des_is_weak_key(encrypting_key->contents))
1808 ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
1809 break;
1811 /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
1812 case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
1813 case ENCTYPE_DES3_CBC_RAW:
1814 case ENCTYPE_DES3_CBC_SHA1:
1815 for (i = 0; i < 3; i++) {
1816 mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
1817 if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
1818 ((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
1820 break;
1822 default:
1823 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1824 gettext("Unimplemented keytype for SAM key mixing"));
1825 goto cleanup;
1828 /* XXX Attach a useful pa_data */
1829 cleanup:
1830 if (sr)
1831 krb5_free_sam_response(context, sr);
1832 if (psr)
1833 krb5_free_predicted_sam_response(context, psr);
1835 return retval;
1838 static struct {
1839 char* name;
1840 int sam_type;
1841 } *sam_ptr, sam_inst_map[] = {
1842 #if 0 /* SUNWresync121 - unsupported hardware and kdc.log annoyance */
1843 { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
1844 { "SECURID", PA_SAM_TYPE_SECURID, },
1845 { "GRAIL", PA_SAM_TYPE_GRAIL, },
1846 #endif
1847 { 0, 0 },
1850 static krb5_error_code
1851 get_sam_edata(krb5_context context, krb5_kdc_req *request,
1852 krb5_db_entry *client, krb5_db_entry *server,
1853 preauth_get_entry_data_proc sam_get_entry_data,
1854 void *pa_system_context, krb5_pa_data *pa_data)
1856 krb5_error_code retval;
1857 krb5_sam_challenge sc;
1858 krb5_predicted_sam_response psr;
1859 krb5_data * scratch;
1860 krb5_keyblock encrypting_key;
1861 char response[9];
1862 char inputblock[8];
1863 krb5_data predict_response;
1865 (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
1866 (void) memset(&sc, 0, sizeof(sc));
1867 (void) memset(&psr, 0, sizeof(psr));
1869 /* Given the client name we can figure out what type of preauth
1870 they need. The spec is currently for querying the database for
1871 names that match the types of preauth used. Later we should
1872 make this mapping show up in kdc.conf. In the meantime, we
1873 hardcode the following:
1874 /SNK4 -- Digital Pathways SNK/4 preauth.
1875 /GRAIL -- experimental preauth
1876 The first one found is used. See sam_inst_map above.
1878 For SNK4 in particular, the key in the database is the key for
1879 the device; kadmin needs a special interface for it.
1883 int npr = 1;
1884 krb5_boolean more;
1885 krb5_db_entry assoc;
1886 krb5_key_data *assoc_key;
1887 krb5_principal newp;
1888 int probeslot;
1890 sc.sam_type = 0;
1892 retval = krb5_copy_principal(kdc_context, request->client, &newp);
1893 if (retval) {
1894 com_err(gettext("krb5kdc"),
1895 retval,
1896 gettext("copying client name for preauth probe"));
1897 return retval;
1900 probeslot = krb5_princ_size(context, newp)++;
1901 krb5_princ_name(kdc_context, newp) =
1902 reallocarray(krb5_princ_name(kdc_context, newp),
1903 krb5_princ_size(context, newp), sizeof(krb5_data));
1905 for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
1906 krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
1907 krb5_princ_component(kdc_context,newp,probeslot)->length =
1908 strlen(sam_ptr->name);
1909 npr = 1;
1910 retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
1911 if(!retval && npr) {
1912 sc.sam_type = sam_ptr->sam_type;
1913 break;
1917 krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
1918 krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
1919 krb5_princ_size(context, newp)--;
1921 krb5_free_principal(kdc_context, newp);
1923 /* if sc.sam_type is set, it worked */
1924 if (sc.sam_type) {
1925 /* so use assoc to get the key out! */
1927 /* here's what do_tgs_req does */
1928 retval = krb5_dbe_find_enctype(kdc_context, &assoc,
1929 ENCTYPE_DES_CBC_RAW,
1930 KRB5_KDB_SALTTYPE_NORMAL,
1931 0, /* Get highest kvno */
1932 &assoc_key);
1933 if (retval) {
1934 char *sname;
1935 krb5_unparse_name(kdc_context, request->client, &sname);
1936 com_err(gettext("krb5kdc"),
1937 retval,
1938 gettext("snk4 finding the enctype and key <%s>"),
1939 sname);
1940 free(sname);
1941 return retval;
1943 /* convert server.key into a real key */
1944 retval = krb5_dbekd_decrypt_key_data(kdc_context,
1945 &master_keyblock,
1946 assoc_key, &encrypting_key,
1947 NULL);
1948 if (retval) {
1949 com_err(gettext("krb5kdc"),
1950 retval,
1951 gettext("snk4 pulling out key entry"));
1952 return retval;
1954 /* now we can use encrypting_key... */
1956 } else {
1957 /* SAM is not an option - so don't return as hint */
1958 return KRB5_PREAUTH_BAD_TYPE;
1961 sc.magic = KV5M_SAM_CHALLENGE;
1962 psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
1964 /* Replay prevention */
1965 if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
1966 return retval;
1967 #ifdef USE_RCACHE
1968 if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
1969 return retval;
1970 #endif /* USE_RCACHE */
1972 switch (sc.sam_type) {
1973 case PA_SAM_TYPE_GRAIL:
1974 sc.sam_type_name.data = "Experimental System";
1975 sc.sam_type_name.length = strlen(sc.sam_type_name.data);
1976 sc.sam_challenge_label.data = "experimental challenge label";
1977 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1978 sc.sam_challenge.data = "12345";
1979 sc.sam_challenge.length = strlen(sc.sam_challenge.data);
1981 #if 0 /* Enable this to test "normal" (no flags set) mode. */
1982 psr.sam_flags = sc.sam_flags = 0;
1983 #endif
1985 psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
1986 /* string2key on sc.sam_challenge goes in here */
1987 /* eblock is just to set the enctype */
1989 const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
1991 if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
1992 0 /* salt */, &psr.sam_key)))
1993 goto cleanup;
1995 if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
1996 goto cleanup;
1999 size_t enclen;
2000 krb5_enc_data tmpdata;
2002 if ((retval = krb5_c_encrypt_length(context,
2003 psr_key.enctype,
2004 scratch->length, &enclen)))
2005 goto cleanup;
2007 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
2008 retval = ENOMEM;
2009 goto cleanup;
2011 tmpdata.ciphertext.length = enclen;
2013 if ((retval = krb5_c_encrypt(context, &psr_key,
2014 /* XXX */ 0, 0, scratch, &tmpdata)))
2015 goto cleanup;
2017 sc.sam_track_id = tmpdata.ciphertext;
2021 sc.sam_response_prompt.data = "response prompt";
2022 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
2023 sc.sam_pk_for_sad.length = 0;
2024 sc.sam_nonce = 0;
2025 /* Generate checksum */
2026 /*krb5_checksum_size(context, ctype)*/
2027 /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
2028 seed_length,outcksum) */
2029 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
2030 seed_length) */
2031 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
2032 sc.sam_cksum.contents = (krb5_octet *)
2033 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
2034 if (sc.sam_cksum.contents == NULL) return(ENOMEM);
2036 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
2037 sc.sam_challenge.data,
2038 sc.sam_challenge.length,
2039 psr.sam_key.contents, /* key */
2040 psr.sam_key.length, /* key length */
2041 &sc.sam_cksum);
2042 if (retval) { free(sc.sam_cksum.contents); return(retval); }
2043 #endif /* 0 */
2045 retval = encode_krb5_sam_challenge(&sc, &scratch);
2046 if (retval) goto cleanup;
2047 pa_data->magic = KV5M_PA_DATA;
2048 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
2049 pa_data->contents = (unsigned char *) scratch->data;
2050 pa_data->length = scratch->length;
2052 retval = 0;
2053 break;
2054 case PA_SAM_TYPE_DIGI_PATH:
2055 sc.sam_type_name.data = "Digital Pathways";
2056 sc.sam_type_name.length = strlen(sc.sam_type_name.data);
2057 #if 1
2058 sc.sam_challenge_label.data = "Enter the following on your keypad";
2059 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
2060 #endif
2061 /* generate digit string, take it mod 1000000 (six digits.) */
2063 int j;
2064 krb5_keyblock session_key;
2065 char outputblock[8];
2066 int i;
2068 (void) memset(&session_key, 0, sizeof(krb5_keyblock));
2070 (void) memset(inputblock, 0, 8);
2072 retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
2073 &session_key);
2075 if (retval) {
2076 /* random key failed */
2077 com_err(gettext("krb5kdc"),
2078 retval,
2079 gettext("generating random challenge for preauth"));
2080 return retval;
2082 /* now session_key has a key which we can pick bits out of */
2083 /* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
2084 if (session_key.length != 8) {
2085 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
2086 com_err(gettext("krb5kdc"),
2087 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
2088 gettext("keytype didn't match code expectations"));
2089 return retval;
2091 for(i = 0; i<6; i++) {
2092 inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
2094 if (session_key.contents)
2095 krb5_free_keyblock_contents(kdc_context, &session_key);
2097 /* retval = krb5_finish_key(kdc_context, &eblock); */
2098 /* now we have inputblock containing the 8 byte input to DES... */
2099 sc.sam_challenge.data = inputblock;
2100 sc.sam_challenge.length = 6;
2102 encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
2104 if (retval) {
2105 com_err(gettext("krb5kdc"),
2106 retval,
2107 gettext("snk4 processing key"));
2111 krb5_data plain;
2112 krb5_enc_data cipher;
2114 plain.length = 8;
2115 plain.data = inputblock;
2117 /* XXX I know this is enough because of the fixed raw enctype.
2118 if it's not, the underlying code will return a reasonable
2119 error, which should never happen */
2120 cipher.ciphertext.length = 8;
2121 cipher.ciphertext.data = outputblock;
2123 if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
2124 /* XXX */ 0, 0, &plain, &cipher))) {
2125 com_err(gettext("krb5kdc"),
2126 retval,
2127 gettext("snk4 response generation failed"));
2128 return retval;
2132 /* now output block is the raw bits of the response; convert it
2133 to display form */
2134 for (j=0; j<4; j++) {
2135 char n[2];
2136 int k;
2137 n[0] = outputblock[j] & 0xf;
2138 n[1] = (outputblock[j]>>4) & 0xf;
2139 for (k=0; k<2; k++) {
2140 if(n[k] > 9) n[k] = ((n[k]-1)>>2);
2141 /* This is equivalent to:
2142 if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
2143 if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
2146 /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
2147 /* for v5, we just generate a string */
2148 response[2*j+0] = '0' + n[1];
2149 response[2*j+1] = '0' + n[0];
2150 /* and now, response has what we work with. */
2152 response[8] = 0;
2153 predict_response.data = response;
2154 predict_response.length = 8;
2155 #if 0 /* for debugging, hack the output too! */
2156 sc.sam_challenge_label.data = response;
2157 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
2158 #endif
2161 psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
2162 /* string2key on sc.sam_challenge goes in here */
2163 /* eblock is just to set the enctype */
2165 retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
2166 &predict_response, 0 /* salt */,
2167 &psr.sam_key);
2168 if (retval) goto cleanup;
2170 retval = encode_krb5_predicted_sam_response(&psr, &scratch);
2171 if (retval) goto cleanup;
2174 size_t enclen;
2175 krb5_enc_data tmpdata;
2177 if ((retval = krb5_c_encrypt_length(context,
2178 psr_key.enctype,
2179 scratch->length, &enclen)))
2180 goto cleanup;
2182 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
2183 retval = ENOMEM;
2184 goto cleanup;
2186 tmpdata.ciphertext.length = enclen;
2188 if ((retval = krb5_c_encrypt(context, &psr_key,
2189 /* XXX */ 0, 0, scratch, &tmpdata)))
2190 goto cleanup;
2192 sc.sam_track_id = tmpdata.ciphertext;
2194 if (retval) goto cleanup;
2197 sc.sam_response_prompt.data = "Enter the displayed response";
2198 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
2199 sc.sam_pk_for_sad.length = 0;
2200 sc.sam_nonce = 0;
2201 /* Generate checksum */
2202 /*krb5_checksum_size(context, ctype)*/
2203 /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
2204 seed_length,outcksum) */
2205 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
2206 seed_length) */
2207 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
2208 sc.sam_cksum.contents = (krb5_octet *)
2209 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
2210 if (sc.sam_cksum.contents == NULL) return(ENOMEM);
2212 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
2213 sc.sam_challenge.data,
2214 sc.sam_challenge.length,
2215 psr.sam_key.contents, /* key */
2216 psr.sam_key.length, /* key length */
2217 &sc.sam_cksum);
2218 if (retval) { free(sc.sam_cksum.contents); return(retval); }
2219 #endif /* 0 */
2221 retval = encode_krb5_sam_challenge(&sc, &scratch);
2222 if (retval) goto cleanup;
2223 pa_data->magic = KV5M_PA_DATA;
2224 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
2225 pa_data->contents = (unsigned char *) scratch->data;
2226 pa_data->length = scratch->length;
2228 retval = 0;
2229 break;
2232 cleanup:
2233 krb5_free_keyblock_contents(context, &encrypting_key);
2234 return retval;
2237 static krb5_error_code
2238 verify_sam_response(krb5_context context, krb5_db_entry *client,
2239 krb5_data *req_pkt,
2240 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
2241 krb5_pa_data *pa,
2242 preauth_get_entry_data_proc sam_get_entry_data,
2243 void *pa_system_context,
2244 void **pa_request_context,
2245 krb5_data **e_data,
2246 krb5_authdata ***authz_data)
2248 krb5_error_code retval;
2249 krb5_data scratch;
2250 krb5_sam_response *sr = 0;
2251 krb5_predicted_sam_response *psr = 0;
2252 krb5_enc_sam_response_enc *esre = 0;
2253 krb5_timestamp timenow;
2254 char *princ_req = 0, *princ_psr = 0;
2256 scratch.data = (char *) pa->contents;
2257 scratch.length = pa->length;
2259 if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
2260 scratch.data = 0;
2261 com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
2262 goto cleanup;
2265 /* XXX We can only handle the challenge/response model of SAM.
2266 See passwords-04, par 4.1, 4.2 */
2268 krb5_enc_data tmpdata;
2270 tmpdata.enctype = ENCTYPE_UNKNOWN;
2271 tmpdata.ciphertext = sr->sam_track_id;
2273 scratch.length = tmpdata.ciphertext.length;
2274 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
2275 retval = ENOMEM;
2276 goto cleanup;
2279 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
2280 &tmpdata, &scratch))) {
2281 com_err(gettext("krb5kdc"),
2282 retval,
2283 gettext("decrypt track_id failed"));
2284 goto cleanup;
2288 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
2289 com_err(gettext("krb5kdc"), retval,
2290 gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
2291 goto cleanup;
2294 /* Replay detection */
2295 if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
2296 goto cleanup;
2297 if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
2298 goto cleanup;
2299 if (strcmp(princ_req, princ_psr) != 0) {
2300 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
2301 gettext("Principal mismatch in SAM psr! -- replay attack?"));
2302 goto cleanup;
2305 if ((retval = krb5_timeofday(context, &timenow)))
2306 goto cleanup;
2308 #ifdef USE_RCACHE
2310 krb5_donot_replay rep;
2311 extern krb5_deltat rc_lifetime;
2313 * Verify this response came back in a timely manner.
2314 * We do this b/c otherwise very old (expunged from the rcache)
2315 * psr's would be able to be replayed.
2317 if (timenow - psr->stime > rc_lifetime) {
2318 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
2319 gettext("SAM psr came back too late! -- replay attack?"));
2320 goto cleanup;
2323 /* Now check the replay cache. */
2324 rep.client = princ_psr;
2325 rep.server = "SAM/rc"; /* Should not match any principal name. */
2326 rep.ctime = psr->stime;
2327 rep.cusec = psr->susec;
2328 retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
2329 if (retval) {
2330 com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
2331 goto cleanup;
2334 #endif /* USE_RCACHE */
2338 free(scratch.data);
2339 scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
2340 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
2341 retval = ENOMEM;
2342 goto cleanup;
2345 if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
2346 0, &sr->sam_enc_nonce_or_ts, &scratch))) {
2347 com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
2348 goto cleanup;
2352 if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
2353 com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
2354 goto cleanup;
2357 if (esre->sam_timestamp != sr->sam_patimestamp) {
2358 retval = KRB5KDC_ERR_PREAUTH_FAILED;
2359 goto cleanup;
2362 if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
2363 retval = KRB5KRB_AP_ERR_SKEW;
2364 goto cleanup;
2367 setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
2369 cleanup:
2370 if (retval) com_err(gettext("krb5kdc"),
2371 retval,
2372 gettext("sam verify failure"));
2373 free(scratch.data);
2374 free(sr);
2375 free(psr);
2376 free(esre);
2377 free(princ_psr);
2378 free(princ_req);
2380 return retval;