8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / krb5 / krb5kdc / kdc_preauth.c
blob929e1467756f90d178f1eac32196d3947b10ba18
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 if (enc_tkt_part->authorization_data != NULL)
908 free(enc_tkt_part->authorization_data);
910 /* Install our new list */
911 enc_tkt_part->authorization_data = newad;
913 return 0;
917 * This routine is called to verify the preauthentication information
918 * for a V5 request.
920 * Returns 0 if the pre-authentication is valid, non-zero to indicate
921 * an error code of some sort.
924 krb5_error_code
925 check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
926 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
927 void **padata_context, krb5_data *e_data)
929 krb5_error_code retval = 0;
930 krb5_pa_data **padata;
931 krb5_preauth_systems *pa_sys;
932 void **pa_context;
933 krb5_data *pa_e_data = NULL, *tmp_e_data = NULL;
934 int pa_ok = 0, pa_found = 0;
935 krb5_error_code saved_retval = 0;
936 int use_saved_retval = 0;
937 const char *emsg;
938 krb5_authdata **tmp_authz_data = NULL;
940 if (request->padata == 0)
941 return 0;
943 if (make_padata_context(context, padata_context) != 0) {
944 return KRB5KRB_ERR_GENERIC;
947 #ifdef DEBUG
948 krb5_klog_syslog (LOG_DEBUG, "checking padata");
949 #endif
950 for (padata = request->padata; *padata; padata++) {
951 #ifdef DEBUG
952 krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type);
953 #endif
954 if (find_pa_system((*padata)->pa_type, &pa_sys))
955 continue;
956 if (find_pa_context(pa_sys, *padata_context, &pa_context))
957 continue;
958 #ifdef DEBUG
959 krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
960 #endif
961 if (pa_sys->verify_padata == 0)
962 continue;
963 pa_found++;
964 retval = pa_sys->verify_padata(context, client, req_pkt, request,
965 enc_tkt_reply, *padata,
966 get_entry_data, pa_sys->plugin_context,
967 pa_context, &tmp_e_data, &tmp_authz_data);
968 if (retval) {
969 emsg = krb5_get_error_message (context, retval);
970 krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
971 pa_sys->name, emsg);
972 krb5_free_error_message (context, emsg);
973 /* Ignore authorization data returned from modules that fail */
974 if (tmp_authz_data != NULL) {
975 krb5_free_authdata(context, tmp_authz_data);
976 tmp_authz_data = NULL;
978 if (pa_sys->flags & PA_REQUIRED) {
979 /* free up any previous edata we might have been saving */
980 if (pa_e_data != NULL)
981 krb5_free_data(context, pa_e_data);
982 pa_e_data = tmp_e_data;
983 tmp_e_data = NULL;
984 use_saved_retval = 0; /* Make sure we use the current retval */
985 pa_ok = 0;
986 break;
989 * We'll return edata from either the first PA_REQUIRED module
990 * that fails, or the first non-PA_REQUIRED module that fails.
991 * Hang on to edata from the first non-PA_REQUIRED module.
992 * If we've already got one saved, simply discard this one.
994 if (tmp_e_data != NULL) {
995 if (pa_e_data == NULL) {
996 /* save the first error code and e-data */
997 pa_e_data = tmp_e_data;
998 tmp_e_data = NULL;
999 saved_retval = retval;
1000 use_saved_retval = 1;
1001 } else {
1002 /* discard this extra e-data from non-PA_REQUIRED module */
1003 krb5_free_data(context, tmp_e_data);
1004 tmp_e_data = NULL;
1007 } else {
1008 #ifdef DEBUG
1009 krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
1010 #endif
1011 /* Ignore any edata returned on success */
1012 if (tmp_e_data != NULL) {
1013 krb5_free_data(context, tmp_e_data);
1014 tmp_e_data = NULL;
1016 /* Add any authorization data to the ticket */
1017 if (tmp_authz_data != NULL) {
1018 add_authorization_data(enc_tkt_reply, tmp_authz_data);
1019 free(tmp_authz_data);
1020 tmp_authz_data = NULL;
1022 pa_ok = 1;
1023 if (pa_sys->flags & PA_SUFFICIENT)
1024 break;
1028 /* Don't bother copying and returning e-data on success */
1029 if (pa_ok && pa_e_data != NULL) {
1030 krb5_free_data(context, pa_e_data);
1031 pa_e_data = NULL;
1033 /* Return any e-data from the preauth that caused us to exit the loop */
1034 if (pa_e_data != NULL) {
1035 e_data->data = malloc(pa_e_data->length);
1036 if (e_data->data == NULL) {
1037 krb5_free_data(context, pa_e_data);
1038 /* Solaris Kerberos */
1039 return ENOMEM;
1041 memcpy(e_data->data, pa_e_data->data, pa_e_data->length);
1042 e_data->length = pa_e_data->length;
1043 krb5_free_data(context, pa_e_data);
1044 pa_e_data = NULL;
1045 if (use_saved_retval != 0)
1046 retval = saved_retval;
1049 if (pa_ok)
1050 return 0;
1052 /* pa system was not found, but principal doesn't require preauth */
1053 if (!pa_found &&
1054 !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1055 !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
1056 return 0;
1058 if (!pa_found) {
1059 emsg = krb5_get_error_message(context, retval);
1060 krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
1061 krb5_free_error_message(context, emsg);
1063 /* The following switch statement allows us
1064 * to return some preauth system errors back to the client.
1066 switch(retval) {
1067 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
1068 case KRB5KRB_AP_ERR_SKEW:
1069 case KRB5KDC_ERR_ETYPE_NOSUPP:
1070 /* rfc 4556 */
1071 case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
1072 case KRB5KDC_ERR_INVALID_SIG:
1073 case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
1074 case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
1075 case KRB5KDC_ERR_INVALID_CERTIFICATE:
1076 case KRB5KDC_ERR_REVOKED_CERTIFICATE:
1077 case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
1078 case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
1079 case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
1080 case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
1081 case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
1082 case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
1083 case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
1084 /* earlier drafts of what became rfc 4556 */
1085 case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
1086 case KRB5KDC_ERR_KDC_NOT_TRUSTED:
1087 case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
1088 /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
1089 /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
1090 return retval;
1091 default:
1092 return KRB5KDC_ERR_PREAUTH_FAILED;
1097 * return_padata creates any necessary preauthentication
1098 * structures which should be returned by the KDC to the client
1100 krb5_error_code
1101 return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
1102 krb5_kdc_req *request, krb5_kdc_rep *reply,
1103 krb5_key_data *client_key, krb5_keyblock *encrypting_key,
1104 void **padata_context)
1106 krb5_error_code retval;
1107 krb5_pa_data ** padata;
1108 krb5_pa_data ** send_pa_list;
1109 krb5_pa_data ** send_pa;
1110 krb5_pa_data * pa = 0;
1111 krb5_preauth_systems * ap;
1112 int * pa_order;
1113 int * pa_type;
1114 int size = 0;
1115 void ** pa_context;
1116 krb5_boolean key_modified;
1117 krb5_keyblock original_key;
1118 if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) {
1119 return KRB5KRB_ERR_GENERIC;
1122 for (ap = preauth_systems; ap->type != -1; ap++) {
1123 if (ap->return_padata)
1124 size++;
1127 if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
1128 return ENOMEM;
1129 if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
1130 free(send_pa_list);
1131 return ENOMEM;
1133 sort_pa_order(context, request, pa_order);
1135 retval = krb5_copy_keyblock_contents(context, encrypting_key,
1136 &original_key);
1137 if (retval) {
1138 free(send_pa_list);
1139 free(pa_order);
1140 return retval;
1142 key_modified = FALSE;
1144 send_pa = send_pa_list;
1145 *send_pa = 0;
1147 for (pa_type = pa_order; *pa_type != -1; pa_type++) {
1148 ap = &preauth_systems[*pa_type];
1149 if (!key_modified)
1150 if (original_key.enctype != encrypting_key->enctype)
1151 key_modified = TRUE;
1152 if (!key_modified)
1153 if (original_key.length != encrypting_key->length)
1154 key_modified = TRUE;
1155 if (!key_modified)
1156 if (memcmp(original_key.contents, encrypting_key->contents,
1157 original_key.length) != 0)
1158 key_modified = TRUE;
1159 if (key_modified && (ap->flags & PA_REPLACES_KEY))
1160 continue;
1161 if (ap->return_padata == 0)
1162 continue;
1163 if (find_pa_context(ap, *padata_context, &pa_context))
1164 continue;
1165 pa = 0;
1166 if (request->padata) {
1167 for (padata = request->padata; *padata; padata++) {
1168 if ((*padata)->pa_type == ap->type) {
1169 pa = *padata;
1170 break;
1174 if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
1175 client_key, encrypting_key, send_pa,
1176 get_entry_data, ap->plugin_context,
1177 pa_context))) {
1178 goto cleanup;
1181 if (*send_pa)
1182 send_pa++;
1183 *send_pa = 0;
1186 retval = 0;
1188 if (send_pa_list[0]) {
1189 reply->padata = send_pa_list;
1190 send_pa_list = 0;
1193 cleanup:
1194 if (send_pa_list)
1195 krb5_free_pa_data(context, send_pa_list);
1197 /* Solaris Kerberos */
1198 krb5_free_keyblock_contents(context, &original_key);
1199 free(pa_order);
1201 return (retval);
1204 static krb5_boolean
1205 enctype_requires_etype_info_2(krb5_enctype enctype)
1207 switch(enctype) {
1208 case ENCTYPE_DES_CBC_CRC:
1209 case ENCTYPE_DES_CBC_MD4:
1210 case ENCTYPE_DES_CBC_MD5:
1211 case ENCTYPE_DES3_CBC_SHA1:
1212 case ENCTYPE_DES3_CBC_RAW:
1213 case ENCTYPE_ARCFOUR_HMAC:
1214 case ENCTYPE_ARCFOUR_HMAC_EXP :
1215 return 0;
1216 default:
1217 if (krb5_c_valid_enctype(enctype))
1218 return 1;
1219 else return 0;
1223 static krb5_boolean
1224 request_contains_enctype (krb5_context context, const krb5_kdc_req *request,
1225 krb5_enctype enctype)
1227 int i;
1228 for (i =0; i < request->nktypes; i++)
1229 if (request->ktype[i] == enctype)
1230 return 1;
1231 return 0;
1235 static krb5_error_code
1236 verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
1237 krb5_data *req_pkt,
1238 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
1239 krb5_pa_data *pa,
1240 preauth_get_entry_data_proc ets_get_entry_data,
1241 void *pa_system_context,
1242 void **pa_request_context,
1243 krb5_data **e_data,
1244 krb5_authdata ***authz_data)
1246 krb5_pa_enc_ts * pa_enc = 0;
1247 krb5_error_code retval;
1248 krb5_data scratch;
1249 krb5_data enc_ts_data;
1250 krb5_enc_data *enc_data = 0;
1251 krb5_keyblock key;
1252 krb5_key_data * client_key;
1253 krb5_int32 start;
1254 krb5_timestamp timenow;
1255 krb5_error_code decrypt_err = 0;
1257 (void) memset(&key, 0, sizeof(krb5_keyblock));
1258 scratch.data = (char *) pa->contents;
1259 scratch.length = pa->length;
1261 enc_ts_data.data = 0;
1263 if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
1264 goto cleanup;
1266 enc_ts_data.length = enc_data->ciphertext.length;
1267 if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
1268 goto cleanup;
1270 start = 0;
1271 decrypt_err = 0;
1272 while (1) {
1273 if ((retval = krb5_dbe_search_enctype(context, client,
1274 &start, enc_data->enctype,
1275 -1, 0, &client_key)))
1276 goto cleanup;
1278 if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
1279 client_key, &key, NULL)))
1280 goto cleanup;
1282 key.enctype = enc_data->enctype;
1284 retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
1285 0, enc_data, &enc_ts_data);
1286 krb5_free_keyblock_contents(context, &key);
1287 if (retval == 0)
1288 break;
1289 else
1290 decrypt_err = retval;
1293 if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
1294 goto cleanup;
1296 if ((retval = krb5_timeofday(context, &timenow)) != 0)
1297 goto cleanup;
1299 if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
1300 retval = KRB5KRB_AP_ERR_SKEW;
1301 goto cleanup;
1304 setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
1306 retval = 0;
1308 cleanup:
1309 if (enc_data) {
1310 krb5_free_data_contents(context, &enc_data->ciphertext);
1311 free(enc_data);
1313 krb5_free_data_contents(context, &enc_ts_data);
1314 if (pa_enc)
1315 free(pa_enc);
1317 * If we get NO_MATCHING_KEY and decryption previously failed, and
1318 * we failed to find any other keys of the correct enctype after
1319 * that failed decryption, it probably means that the password was
1320 * incorrect.
1322 if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
1323 retval = decrypt_err;
1324 return retval;
1327 static krb5_error_code
1328 _make_etype_info_entry(krb5_context context,
1329 krb5_kdc_req *request, krb5_key_data *client_key,
1330 krb5_enctype etype, krb5_etype_info_entry **entry,
1331 int etype_info2)
1333 krb5_data salt;
1334 krb5_etype_info_entry * tmp_entry;
1335 krb5_error_code retval;
1337 if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL)
1338 return ENOMEM;
1340 salt.data = 0;
1342 tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY;
1343 tmp_entry->etype = etype;
1344 tmp_entry->length = KRB5_ETYPE_NO_SALT;
1345 tmp_entry->salt = 0;
1346 tmp_entry->s2kparams.data = NULL;
1347 tmp_entry->s2kparams.length = 0;
1348 retval = get_salt_from_key(context, request->client,
1349 client_key, &salt);
1350 if (retval)
1351 goto fail;
1352 if (etype_info2 && client_key->key_data_ver > 1 &&
1353 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) {
1354 switch (etype) {
1355 case ENCTYPE_DES_CBC_CRC:
1356 case ENCTYPE_DES_CBC_MD4:
1357 case ENCTYPE_DES_CBC_MD5:
1358 tmp_entry->s2kparams.data = malloc(1);
1359 if (tmp_entry->s2kparams.data == NULL) {
1360 retval = ENOMEM;
1361 goto fail;
1363 tmp_entry->s2kparams.length = 1;
1364 tmp_entry->s2kparams.data[0] = 1;
1365 break;
1366 default:
1367 break;
1371 if (salt.length >= 0) {
1372 tmp_entry->length = salt.length;
1373 tmp_entry->salt = (unsigned char *) salt.data;
1374 salt.data = 0;
1376 *entry = tmp_entry;
1377 return 0;
1379 fail:
1380 if (tmp_entry) {
1381 if (tmp_entry->s2kparams.data)
1382 free(tmp_entry->s2kparams.data);
1383 free(tmp_entry);
1385 if (salt.data)
1386 free(salt.data);
1387 return retval;
1390 * This function returns the etype information for a particular
1391 * client, to be passed back in the preauth list in the KRB_ERROR
1392 * message. It supports generating both etype_info and etype_info2
1393 * as most of the work is the same.
1395 static krb5_error_code
1396 etype_info_helper(krb5_context context, krb5_kdc_req *request,
1397 krb5_db_entry *client, krb5_db_entry *server,
1398 krb5_pa_data *pa_data, int etype_info2)
1400 krb5_etype_info_entry ** entry = 0;
1401 krb5_key_data *client_key;
1402 krb5_error_code retval;
1403 krb5_data * scratch;
1404 krb5_enctype db_etype;
1405 int i = 0;
1406 int start = 0;
1407 int seen_des = 0;
1409 entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
1410 if (entry == NULL)
1411 return ENOMEM;
1412 entry[0] = NULL;
1414 while (1) {
1415 retval = krb5_dbe_search_enctype(context, client, &start, -1,
1416 -1, 0, &client_key);
1417 if (retval == KRB5_KDB_NO_MATCHING_KEY)
1418 break;
1419 if (retval)
1420 goto cleanup;
1421 db_etype = client_key->key_data_type[0];
1422 if (db_etype == ENCTYPE_DES_CBC_MD4)
1423 db_etype = ENCTYPE_DES_CBC_MD5;
1425 if (request_contains_enctype(context, request, db_etype)) {
1426 assert(etype_info2 ||
1427 !enctype_requires_etype_info_2(db_etype));
1428 if ((retval = _make_etype_info_entry(context, request, client_key,
1429 db_etype, &entry[i], etype_info2)) != 0) {
1430 goto cleanup;
1432 entry[i+1] = 0;
1433 i++;
1437 * If there is a des key in the kdb, try the "similar" enctypes,
1438 * avoid duplicate entries.
1440 if (!seen_des) {
1441 switch (db_etype) {
1442 case ENCTYPE_DES_CBC_MD5:
1443 db_etype = ENCTYPE_DES_CBC_CRC;
1444 break;
1445 case ENCTYPE_DES_CBC_CRC:
1446 db_etype = ENCTYPE_DES_CBC_MD5;
1447 break;
1448 default:
1449 continue;
1452 if (request_contains_enctype(context, request, db_etype)) {
1453 if ((retval = _make_etype_info_entry(context, request,
1454 client_key, db_etype, &entry[i], etype_info2)) != 0) {
1455 goto cleanup;
1457 entry[i+1] = 0;
1458 i++;
1460 seen_des++;
1463 if (etype_info2)
1464 retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry,
1465 &scratch);
1466 else retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry,
1467 &scratch);
1468 if (retval)
1469 goto cleanup;
1470 pa_data->contents = (unsigned char *)scratch->data;
1471 pa_data->length = scratch->length;
1472 free(scratch);
1474 retval = 0;
1476 cleanup:
1477 if (entry)
1478 krb5_free_etype_info(context, entry);
1479 return retval;
1482 static krb5_error_code
1483 get_etype_info(krb5_context context, krb5_kdc_req *request,
1484 krb5_db_entry *client, krb5_db_entry *server,
1485 preauth_get_entry_data_proc etype_get_entry_data,
1486 void *pa_system_context,
1487 krb5_pa_data *pa_data)
1489 int i;
1490 for (i=0; i < request->nktypes; i++) {
1491 if (enctype_requires_etype_info_2(request->ktype[i]))
1492 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
1493 * skip this
1494 * type*/
1496 return etype_info_helper(context, request, client, server, pa_data, 0);
1499 static krb5_error_code
1500 get_etype_info2(krb5_context context, krb5_kdc_req *request,
1501 krb5_db_entry *client, krb5_db_entry *server,
1502 preauth_get_entry_data_proc etype_get_entry_data,
1503 void *pa_system_context,
1504 krb5_pa_data *pa_data)
1506 return etype_info_helper( context, request, client, server, pa_data, 1);
1509 static krb5_error_code
1510 etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
1511 krb5_db_entry *client,
1512 krb5_kdc_req *request, krb5_kdc_rep *reply,
1513 krb5_key_data *client_key,
1514 krb5_keyblock *encrypting_key,
1515 krb5_pa_data **send_pa,
1516 int etype_info2)
1518 int i;
1519 krb5_error_code retval;
1520 krb5_pa_data *tmp_padata;
1521 krb5_etype_info_entry **entry = NULL;
1522 krb5_data *scratch = NULL;
1525 * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
1526 * enctypes.
1528 if (!etype_info2) {
1529 for (i = 0; i < request->nktypes; i++) {
1530 if (enctype_requires_etype_info_2(request->ktype[i])) {
1531 *send_pa = NULL;
1532 return 0;
1537 tmp_padata = malloc( sizeof(krb5_pa_data));
1538 if (tmp_padata == NULL)
1539 return ENOMEM;
1540 if (etype_info2)
1541 tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
1542 else
1543 tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO;
1545 entry = malloc(2 * sizeof(krb5_etype_info_entry *));
1546 if (entry == NULL) {
1547 retval = ENOMEM;
1548 goto cleanup;
1550 entry[0] = NULL;
1551 entry[1] = NULL;
1552 retval = _make_etype_info_entry(context, request,
1553 client_key, encrypting_key->enctype,
1554 entry, etype_info2);
1555 if (retval)
1556 goto cleanup;
1558 if (etype_info2)
1559 retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry, &scratch);
1560 else
1561 retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry, &scratch);
1563 if (retval)
1564 goto cleanup;
1565 tmp_padata->contents = (uchar_t *)scratch->data;
1566 tmp_padata->length = scratch->length;
1567 *send_pa = tmp_padata;
1569 /* For cleanup - we no longer own the contents of the krb5_data
1570 * only to pointer to the krb5_data
1572 scratch->data = 0;
1574 cleanup:
1575 if (entry)
1576 krb5_free_etype_info(context, entry);
1577 if (retval) {
1578 if (tmp_padata)
1579 free(tmp_padata);
1581 if (scratch)
1582 krb5_free_data(context, scratch);
1583 return retval;
1586 static krb5_error_code
1587 return_etype_info2(krb5_context context, krb5_pa_data * padata,
1588 krb5_db_entry *client,
1589 krb5_data *req_pkt,
1590 krb5_kdc_req *request, krb5_kdc_rep *reply,
1591 krb5_key_data *client_key,
1592 krb5_keyblock *encrypting_key,
1593 krb5_pa_data **send_pa,
1594 preauth_get_entry_data_proc etype_get_entry_data,
1595 void *pa_system_context,
1596 void **pa_request_context)
1598 return etype_info_as_rep_helper(context, padata, client, request, reply,
1599 client_key, encrypting_key, send_pa, 1);
1603 static krb5_error_code
1604 return_etype_info(krb5_context context, krb5_pa_data * padata,
1605 krb5_db_entry *client,
1606 krb5_data *req_pkt,
1607 krb5_kdc_req *request, krb5_kdc_rep *reply,
1608 krb5_key_data *client_key,
1609 krb5_keyblock *encrypting_key,
1610 krb5_pa_data **send_pa,
1611 preauth_get_entry_data_proc etypeget_entry_data,
1612 void *pa_system_context,
1613 void **pa_request_context)
1615 return etype_info_as_rep_helper(context, padata, client, request, reply,
1616 client_key, encrypting_key, send_pa, 0);
1619 static krb5_error_code
1620 return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
1621 krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
1622 krb5_kdc_rep *reply, krb5_key_data *client_key,
1623 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1624 preauth_get_entry_data_proc etype_get_entry_data,
1625 void *pa_system_context,
1626 void **pa_request_context)
1628 krb5_error_code retval;
1629 krb5_pa_data * padata;
1630 krb5_data * scratch;
1631 krb5_data salt_data;
1632 int i;
1634 for (i = 0; i < request->nktypes; i++) {
1635 if (enctype_requires_etype_info_2(request->ktype[i]))
1636 return 0;
1638 if (client_key->key_data_ver == 1 ||
1639 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
1640 return 0;
1642 if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
1643 return ENOMEM;
1644 padata->magic = KV5M_PA_DATA;
1645 padata->pa_type = KRB5_PADATA_PW_SALT;
1647 switch (client_key->key_data_type[1]) {
1648 case KRB5_KDB_SALTTYPE_V4:
1649 /* send an empty (V4) salt */
1650 padata->contents = 0;
1651 padata->length = 0;
1652 break;
1653 case KRB5_KDB_SALTTYPE_NOREALM:
1654 if ((retval = krb5_principal2salt_norealm(kdc_context,
1655 request->client,
1656 &salt_data)))
1657 goto cleanup;
1658 padata->contents = (krb5_octet *)salt_data.data;
1659 padata->length = salt_data.length;
1660 break;
1661 case KRB5_KDB_SALTTYPE_AFS3:
1662 /* send an AFS style realm-based salt */
1663 /* for now, just pass the realm back and let the client
1664 do the work. In the future, add a kdc configuration
1665 variable that specifies the old cell name. */
1666 padata->pa_type = KRB5_PADATA_AFS3_SALT;
1667 /* it would be just like ONLYREALM, but we need to pass the 0 */
1668 scratch = krb5_princ_realm(kdc_context, request->client);
1669 if ((padata->contents = malloc(scratch->length+1)) == NULL) {
1670 retval = ENOMEM;
1671 goto cleanup;
1673 memcpy(padata->contents, scratch->data, scratch->length);
1674 padata->length = scratch->length+1;
1675 padata->contents[scratch->length] = 0;
1676 break;
1677 case KRB5_KDB_SALTTYPE_ONLYREALM:
1678 scratch = krb5_princ_realm(kdc_context, request->client);
1679 if ((padata->contents = malloc(scratch->length)) == NULL) {
1680 retval = ENOMEM;
1681 goto cleanup;
1683 memcpy(padata->contents, scratch->data, scratch->length);
1684 padata->length = scratch->length;
1685 break;
1686 case KRB5_KDB_SALTTYPE_SPECIAL:
1687 if ((padata->contents = malloc(client_key->key_data_length[1]))
1688 == NULL) {
1689 retval = ENOMEM;
1690 goto cleanup;
1692 memcpy(padata->contents, client_key->key_data_contents[1],
1693 client_key->key_data_length[1]);
1694 padata->length = client_key->key_data_length[1];
1695 break;
1696 default:
1697 free(padata);
1698 return 0;
1701 *send_pa = padata;
1702 return 0;
1704 cleanup:
1705 free(padata);
1706 return retval;
1709 static krb5_error_code
1710 return_sam_data(krb5_context context, krb5_pa_data *in_padata,
1711 krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
1712 krb5_kdc_rep *reply, krb5_key_data *client_key,
1713 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1714 preauth_get_entry_data_proc sam_get_entry_data,
1715 void *pa_system_context,
1716 void **pa_request_context)
1718 krb5_error_code retval;
1719 krb5_data scratch;
1720 int i;
1722 krb5_sam_response *sr = 0;
1723 krb5_predicted_sam_response *psr = 0;
1725 if (in_padata == 0)
1726 return 0;
1729 * We start by doing the same thing verify_sam_response() does:
1730 * extract the psr from the padata (which is an sr). Nothing
1731 * here should generate errors! We've already successfully done
1732 * all this once.
1735 scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
1736 scratch.length = in_padata->length;
1738 if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
1739 com_err("krb5kdc", retval,
1740 gettext("return_sam_data(): decode_krb5_sam_response failed"));
1741 goto cleanup;
1745 krb5_enc_data tmpdata;
1747 tmpdata.enctype = ENCTYPE_UNKNOWN;
1748 tmpdata.ciphertext = sr->sam_track_id;
1750 scratch.length = tmpdata.ciphertext.length;
1751 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
1752 retval = ENOMEM;
1753 goto cleanup;
1756 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
1757 &tmpdata, &scratch))) {
1758 com_err("krb5kdc", retval,
1759 gettext("return_sam_data(): decrypt track_id failed"));
1760 free(scratch.data);
1761 goto cleanup;
1765 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
1766 com_err("krb5kdc", retval,
1767 gettext(
1768 "return_sam_data(): decode_krb5_predicted_sam_response failed"));
1769 free(scratch.data);
1770 goto cleanup;
1773 /* We could use sr->sam_flags, but it may be absent or altered. */
1774 if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
1775 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1776 gettext("Unsupported SAM flag must-pk-encrypt-sad"));
1777 goto cleanup;
1779 if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
1780 /* No key munging */
1781 goto cleanup;
1783 if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
1784 /* Use sam_key instead of client key */
1785 krb5_free_keyblock_contents(context, encrypting_key);
1786 krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
1787 /* XXX Attach a useful pa_data */
1788 goto cleanup;
1791 /* Otherwise (no flags set), we XOR the keys */
1792 /* XXX The passwords-04 draft is underspecified here wrt different
1793 key types. We will do what I hope to get into the -05 draft. */
1795 krb5_octet *p = encrypting_key->contents;
1796 krb5_octet *q = psr->sam_key.contents;
1797 int length = ((encrypting_key->length < psr->sam_key.length)
1798 ? encrypting_key->length
1799 : psr->sam_key.length);
1801 for (i = 0; i < length; i++)
1802 p[i] ^= q[i];
1805 /* Post-mixing key correction */
1806 switch (encrypting_key->enctype) {
1807 case ENCTYPE_DES_CBC_CRC:
1808 case ENCTYPE_DES_CBC_MD4:
1809 case ENCTYPE_DES_CBC_MD5:
1810 case ENCTYPE_DES_CBC_RAW:
1811 mit_des_fixup_key_parity(encrypting_key->contents);
1812 if (mit_des_is_weak_key(encrypting_key->contents))
1813 ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
1814 break;
1816 /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
1817 case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
1818 case ENCTYPE_DES3_CBC_RAW:
1819 case ENCTYPE_DES3_CBC_SHA1:
1820 for (i = 0; i < 3; i++) {
1821 mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
1822 if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
1823 ((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
1825 break;
1827 default:
1828 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1829 gettext("Unimplemented keytype for SAM key mixing"));
1830 goto cleanup;
1833 /* XXX Attach a useful pa_data */
1834 cleanup:
1835 if (sr)
1836 krb5_free_sam_response(context, sr);
1837 if (psr)
1838 krb5_free_predicted_sam_response(context, psr);
1840 return retval;
1843 static struct {
1844 char* name;
1845 int sam_type;
1846 } *sam_ptr, sam_inst_map[] = {
1847 #if 0 /* SUNWresync121 - unsupported hardware and kdc.log annoyance */
1848 { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
1849 { "SECURID", PA_SAM_TYPE_SECURID, },
1850 { "GRAIL", PA_SAM_TYPE_GRAIL, },
1851 #endif
1852 { 0, 0 },
1855 static krb5_error_code
1856 get_sam_edata(krb5_context context, krb5_kdc_req *request,
1857 krb5_db_entry *client, krb5_db_entry *server,
1858 preauth_get_entry_data_proc sam_get_entry_data,
1859 void *pa_system_context, krb5_pa_data *pa_data)
1861 krb5_error_code retval;
1862 krb5_sam_challenge sc;
1863 krb5_predicted_sam_response psr;
1864 krb5_data * scratch;
1865 krb5_keyblock encrypting_key;
1866 char response[9];
1867 char inputblock[8];
1868 krb5_data predict_response;
1870 (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
1871 (void) memset(&sc, 0, sizeof(sc));
1872 (void) memset(&psr, 0, sizeof(psr));
1874 /* Given the client name we can figure out what type of preauth
1875 they need. The spec is currently for querying the database for
1876 names that match the types of preauth used. Later we should
1877 make this mapping show up in kdc.conf. In the meantime, we
1878 hardcode the following:
1879 /SNK4 -- Digital Pathways SNK/4 preauth.
1880 /GRAIL -- experimental preauth
1881 The first one found is used. See sam_inst_map above.
1883 For SNK4 in particular, the key in the database is the key for
1884 the device; kadmin needs a special interface for it.
1888 int npr = 1;
1889 krb5_boolean more;
1890 krb5_db_entry assoc;
1891 krb5_key_data *assoc_key;
1892 krb5_principal newp;
1893 int probeslot;
1895 sc.sam_type = 0;
1897 retval = krb5_copy_principal(kdc_context, request->client, &newp);
1898 if (retval) {
1899 com_err(gettext("krb5kdc"),
1900 retval,
1901 gettext("copying client name for preauth probe"));
1902 return retval;
1905 probeslot = krb5_princ_size(context, newp)++;
1906 krb5_princ_name(kdc_context, newp) =
1907 realloc(krb5_princ_name(kdc_context, newp),
1908 krb5_princ_size(context, newp) * sizeof(krb5_data));
1910 for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
1911 krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
1912 krb5_princ_component(kdc_context,newp,probeslot)->length =
1913 strlen(sam_ptr->name);
1914 npr = 1;
1915 retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
1916 if(!retval && npr) {
1917 sc.sam_type = sam_ptr->sam_type;
1918 break;
1922 krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
1923 krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
1924 krb5_princ_size(context, newp)--;
1926 krb5_free_principal(kdc_context, newp);
1928 /* if sc.sam_type is set, it worked */
1929 if (sc.sam_type) {
1930 /* so use assoc to get the key out! */
1932 /* here's what do_tgs_req does */
1933 retval = krb5_dbe_find_enctype(kdc_context, &assoc,
1934 ENCTYPE_DES_CBC_RAW,
1935 KRB5_KDB_SALTTYPE_NORMAL,
1936 0, /* Get highest kvno */
1937 &assoc_key);
1938 if (retval) {
1939 char *sname;
1940 krb5_unparse_name(kdc_context, request->client, &sname);
1941 com_err(gettext("krb5kdc"),
1942 retval,
1943 gettext("snk4 finding the enctype and key <%s>"),
1944 sname);
1945 free(sname);
1946 return retval;
1948 /* convert server.key into a real key */
1949 retval = krb5_dbekd_decrypt_key_data(kdc_context,
1950 &master_keyblock,
1951 assoc_key, &encrypting_key,
1952 NULL);
1953 if (retval) {
1954 com_err(gettext("krb5kdc"),
1955 retval,
1956 gettext("snk4 pulling out key entry"));
1957 return retval;
1959 /* now we can use encrypting_key... */
1961 } else {
1962 /* SAM is not an option - so don't return as hint */
1963 return KRB5_PREAUTH_BAD_TYPE;
1966 sc.magic = KV5M_SAM_CHALLENGE;
1967 psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
1969 /* Replay prevention */
1970 if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
1971 return retval;
1972 #ifdef USE_RCACHE
1973 if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
1974 return retval;
1975 #endif /* USE_RCACHE */
1977 switch (sc.sam_type) {
1978 case PA_SAM_TYPE_GRAIL:
1979 sc.sam_type_name.data = "Experimental System";
1980 sc.sam_type_name.length = strlen(sc.sam_type_name.data);
1981 sc.sam_challenge_label.data = "experimental challenge label";
1982 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1983 sc.sam_challenge.data = "12345";
1984 sc.sam_challenge.length = strlen(sc.sam_challenge.data);
1986 #if 0 /* Enable this to test "normal" (no flags set) mode. */
1987 psr.sam_flags = sc.sam_flags = 0;
1988 #endif
1990 psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
1991 /* string2key on sc.sam_challenge goes in here */
1992 /* eblock is just to set the enctype */
1994 const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
1996 if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
1997 0 /* salt */, &psr.sam_key)))
1998 goto cleanup;
2000 if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
2001 goto cleanup;
2004 size_t enclen;
2005 krb5_enc_data tmpdata;
2007 if ((retval = krb5_c_encrypt_length(context,
2008 psr_key.enctype,
2009 scratch->length, &enclen)))
2010 goto cleanup;
2012 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
2013 retval = ENOMEM;
2014 goto cleanup;
2016 tmpdata.ciphertext.length = enclen;
2018 if ((retval = krb5_c_encrypt(context, &psr_key,
2019 /* XXX */ 0, 0, scratch, &tmpdata)))
2020 goto cleanup;
2022 sc.sam_track_id = tmpdata.ciphertext;
2026 sc.sam_response_prompt.data = "response prompt";
2027 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
2028 sc.sam_pk_for_sad.length = 0;
2029 sc.sam_nonce = 0;
2030 /* Generate checksum */
2031 /*krb5_checksum_size(context, ctype)*/
2032 /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
2033 seed_length,outcksum) */
2034 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
2035 seed_length) */
2036 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
2037 sc.sam_cksum.contents = (krb5_octet *)
2038 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
2039 if (sc.sam_cksum.contents == NULL) return(ENOMEM);
2041 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
2042 sc.sam_challenge.data,
2043 sc.sam_challenge.length,
2044 psr.sam_key.contents, /* key */
2045 psr.sam_key.length, /* key length */
2046 &sc.sam_cksum);
2047 if (retval) { free(sc.sam_cksum.contents); return(retval); }
2048 #endif /* 0 */
2050 retval = encode_krb5_sam_challenge(&sc, &scratch);
2051 if (retval) goto cleanup;
2052 pa_data->magic = KV5M_PA_DATA;
2053 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
2054 pa_data->contents = (unsigned char *) scratch->data;
2055 pa_data->length = scratch->length;
2057 retval = 0;
2058 break;
2059 case PA_SAM_TYPE_DIGI_PATH:
2060 sc.sam_type_name.data = "Digital Pathways";
2061 sc.sam_type_name.length = strlen(sc.sam_type_name.data);
2062 #if 1
2063 sc.sam_challenge_label.data = "Enter the following on your keypad";
2064 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
2065 #endif
2066 /* generate digit string, take it mod 1000000 (six digits.) */
2068 int j;
2069 krb5_keyblock session_key;
2070 char outputblock[8];
2071 int i;
2073 (void) memset(&session_key, 0, sizeof(krb5_keyblock));
2075 (void) memset(inputblock, 0, 8);
2077 retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
2078 &session_key);
2080 if (retval) {
2081 /* random key failed */
2082 com_err(gettext("krb5kdc"),
2083 retval,
2084 gettext("generating random challenge for preauth"));
2085 return retval;
2087 /* now session_key has a key which we can pick bits out of */
2088 /* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
2089 if (session_key.length != 8) {
2090 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
2091 com_err(gettext("krb5kdc"),
2092 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
2093 gettext("keytype didn't match code expectations"));
2094 return retval;
2096 for(i = 0; i<6; i++) {
2097 inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
2099 if (session_key.contents)
2100 krb5_free_keyblock_contents(kdc_context, &session_key);
2102 /* retval = krb5_finish_key(kdc_context, &eblock); */
2103 /* now we have inputblock containing the 8 byte input to DES... */
2104 sc.sam_challenge.data = inputblock;
2105 sc.sam_challenge.length = 6;
2107 encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
2109 if (retval) {
2110 com_err(gettext("krb5kdc"),
2111 retval,
2112 gettext("snk4 processing key"));
2116 krb5_data plain;
2117 krb5_enc_data cipher;
2119 plain.length = 8;
2120 plain.data = inputblock;
2122 /* XXX I know this is enough because of the fixed raw enctype.
2123 if it's not, the underlying code will return a reasonable
2124 error, which should never happen */
2125 cipher.ciphertext.length = 8;
2126 cipher.ciphertext.data = outputblock;
2128 if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
2129 /* XXX */ 0, 0, &plain, &cipher))) {
2130 com_err(gettext("krb5kdc"),
2131 retval,
2132 gettext("snk4 response generation failed"));
2133 return retval;
2137 /* now output block is the raw bits of the response; convert it
2138 to display form */
2139 for (j=0; j<4; j++) {
2140 char n[2];
2141 int k;
2142 n[0] = outputblock[j] & 0xf;
2143 n[1] = (outputblock[j]>>4) & 0xf;
2144 for (k=0; k<2; k++) {
2145 if(n[k] > 9) n[k] = ((n[k]-1)>>2);
2146 /* This is equivalent to:
2147 if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
2148 if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
2151 /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
2152 /* for v5, we just generate a string */
2153 response[2*j+0] = '0' + n[1];
2154 response[2*j+1] = '0' + n[0];
2155 /* and now, response has what we work with. */
2157 response[8] = 0;
2158 predict_response.data = response;
2159 predict_response.length = 8;
2160 #if 0 /* for debugging, hack the output too! */
2161 sc.sam_challenge_label.data = response;
2162 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
2163 #endif
2166 psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
2167 /* string2key on sc.sam_challenge goes in here */
2168 /* eblock is just to set the enctype */
2170 retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
2171 &predict_response, 0 /* salt */,
2172 &psr.sam_key);
2173 if (retval) goto cleanup;
2175 retval = encode_krb5_predicted_sam_response(&psr, &scratch);
2176 if (retval) goto cleanup;
2179 size_t enclen;
2180 krb5_enc_data tmpdata;
2182 if ((retval = krb5_c_encrypt_length(context,
2183 psr_key.enctype,
2184 scratch->length, &enclen)))
2185 goto cleanup;
2187 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
2188 retval = ENOMEM;
2189 goto cleanup;
2191 tmpdata.ciphertext.length = enclen;
2193 if ((retval = krb5_c_encrypt(context, &psr_key,
2194 /* XXX */ 0, 0, scratch, &tmpdata)))
2195 goto cleanup;
2197 sc.sam_track_id = tmpdata.ciphertext;
2199 if (retval) goto cleanup;
2202 sc.sam_response_prompt.data = "Enter the displayed response";
2203 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
2204 sc.sam_pk_for_sad.length = 0;
2205 sc.sam_nonce = 0;
2206 /* Generate checksum */
2207 /*krb5_checksum_size(context, ctype)*/
2208 /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
2209 seed_length,outcksum) */
2210 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
2211 seed_length) */
2212 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
2213 sc.sam_cksum.contents = (krb5_octet *)
2214 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
2215 if (sc.sam_cksum.contents == NULL) return(ENOMEM);
2217 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
2218 sc.sam_challenge.data,
2219 sc.sam_challenge.length,
2220 psr.sam_key.contents, /* key */
2221 psr.sam_key.length, /* key length */
2222 &sc.sam_cksum);
2223 if (retval) { free(sc.sam_cksum.contents); return(retval); }
2224 #endif /* 0 */
2226 retval = encode_krb5_sam_challenge(&sc, &scratch);
2227 if (retval) goto cleanup;
2228 pa_data->magic = KV5M_PA_DATA;
2229 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
2230 pa_data->contents = (unsigned char *) scratch->data;
2231 pa_data->length = scratch->length;
2233 retval = 0;
2234 break;
2237 cleanup:
2238 krb5_free_keyblock_contents(context, &encrypting_key);
2239 return retval;
2242 static krb5_error_code
2243 verify_sam_response(krb5_context context, krb5_db_entry *client,
2244 krb5_data *req_pkt,
2245 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
2246 krb5_pa_data *pa,
2247 preauth_get_entry_data_proc sam_get_entry_data,
2248 void *pa_system_context,
2249 void **pa_request_context,
2250 krb5_data **e_data,
2251 krb5_authdata ***authz_data)
2253 krb5_error_code retval;
2254 krb5_data scratch;
2255 krb5_sam_response *sr = 0;
2256 krb5_predicted_sam_response *psr = 0;
2257 krb5_enc_sam_response_enc *esre = 0;
2258 krb5_timestamp timenow;
2259 char *princ_req = 0, *princ_psr = 0;
2261 scratch.data = (char *) pa->contents;
2262 scratch.length = pa->length;
2264 if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
2265 scratch.data = 0;
2266 com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
2267 goto cleanup;
2270 /* XXX We can only handle the challenge/response model of SAM.
2271 See passwords-04, par 4.1, 4.2 */
2273 krb5_enc_data tmpdata;
2275 tmpdata.enctype = ENCTYPE_UNKNOWN;
2276 tmpdata.ciphertext = sr->sam_track_id;
2278 scratch.length = tmpdata.ciphertext.length;
2279 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
2280 retval = ENOMEM;
2281 goto cleanup;
2284 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
2285 &tmpdata, &scratch))) {
2286 com_err(gettext("krb5kdc"),
2287 retval,
2288 gettext("decrypt track_id failed"));
2289 goto cleanup;
2293 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
2294 com_err(gettext("krb5kdc"), retval,
2295 gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
2296 goto cleanup;
2299 /* Replay detection */
2300 if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
2301 goto cleanup;
2302 if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
2303 goto cleanup;
2304 if (strcmp(princ_req, princ_psr) != 0) {
2305 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
2306 gettext("Principal mismatch in SAM psr! -- replay attack?"));
2307 goto cleanup;
2310 if ((retval = krb5_timeofday(context, &timenow)))
2311 goto cleanup;
2313 #ifdef USE_RCACHE
2315 krb5_donot_replay rep;
2316 extern krb5_deltat rc_lifetime;
2318 * Verify this response came back in a timely manner.
2319 * We do this b/c otherwise very old (expunged from the rcache)
2320 * psr's would be able to be replayed.
2322 if (timenow - psr->stime > rc_lifetime) {
2323 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
2324 gettext("SAM psr came back too late! -- replay attack?"));
2325 goto cleanup;
2328 /* Now check the replay cache. */
2329 rep.client = princ_psr;
2330 rep.server = "SAM/rc"; /* Should not match any principal name. */
2331 rep.ctime = psr->stime;
2332 rep.cusec = psr->susec;
2333 retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
2334 if (retval) {
2335 com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
2336 goto cleanup;
2339 #endif /* USE_RCACHE */
2343 free(scratch.data);
2344 scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
2345 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
2346 retval = ENOMEM;
2347 goto cleanup;
2350 if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
2351 0, &sr->sam_enc_nonce_or_ts, &scratch))) {
2352 com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
2353 goto cleanup;
2357 if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
2358 com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
2359 goto cleanup;
2362 if (esre->sam_timestamp != sr->sam_patimestamp) {
2363 retval = KRB5KDC_ERR_PREAUTH_FAILED;
2364 goto cleanup;
2367 if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
2368 retval = KRB5KRB_AP_ERR_SKEW;
2369 goto cleanup;
2372 setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
2374 cleanup:
2375 if (retval) com_err(gettext("krb5kdc"),
2376 retval,
2377 gettext("sam verify failure"));
2378 if (scratch.data) free(scratch.data);
2379 if (sr) free(sr);
2380 if (psr) free(psr);
2381 if (esre) free(esre);
2382 if (princ_psr) free(princ_psr);
2383 if (princ_req) free(princ_req);
2385 return retval;