17c478bd9Sstevel@tonic-gate /*
2ba7b222eSGlenn Barry  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate 
77c478bd9Sstevel@tonic-gate /*
87c478bd9Sstevel@tonic-gate  * kdc/kdc_preauth.c
97c478bd9Sstevel@tonic-gate  *
1056a424ccSmp  * Copyright 1995, 2003 by the Massachusetts Institute of Technology.
117c478bd9Sstevel@tonic-gate  * All Rights Reserved.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
147c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
157c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
167c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
17*55fea89dSDan Cross  *
187c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
197c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
207c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
217c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
227c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
237c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
247c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
257c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
267c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
277c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
287c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
297c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
307c478bd9Sstevel@tonic-gate  * or implied warranty.
31*55fea89dSDan Cross  *
327c478bd9Sstevel@tonic-gate  * Preauthentication routines for the KDC.
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate /*
367c478bd9Sstevel@tonic-gate  * Copyright (C) 1998 by the FundsXpress, INC.
37*55fea89dSDan Cross  *
387c478bd9Sstevel@tonic-gate  * All rights reserved.
39*55fea89dSDan Cross  *
407c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may require
417c478bd9Sstevel@tonic-gate  * a specific license from the United States Government.  It is the
427c478bd9Sstevel@tonic-gate  * responsibility of any person or organization contemplating export to
437c478bd9Sstevel@tonic-gate  * obtain such a license before exporting.
44*55fea89dSDan Cross  *
457c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
467c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
477c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
487c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
497c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
507c478bd9Sstevel@tonic-gate  * the name of FundsXpress. not be used in advertising or publicity pertaining
517c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
527c478bd9Sstevel@tonic-gate  * permission.  FundsXpress makes no representations about the suitability of
537c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
547c478bd9Sstevel@tonic-gate  * or implied warranty.
55*55fea89dSDan Cross  *
567c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
577c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
587c478bd9Sstevel@tonic-gate  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #include "k5-int.h"
627c478bd9Sstevel@tonic-gate #include "kdc_util.h"
637c478bd9Sstevel@tonic-gate #include "extern.h"
647c478bd9Sstevel@tonic-gate #include "com_err.h"
657c478bd9Sstevel@tonic-gate #include <assert.h>
667c478bd9Sstevel@tonic-gate #include <stdio.h>
6756a424ccSmp #include "adm_proto.h"
687c478bd9Sstevel@tonic-gate #include <libintl.h>
697c478bd9Sstevel@tonic-gate #include <syslog.h>
707c478bd9Sstevel@tonic-gate 
7156a424ccSmp #include <assert.h>
72159d09a2SMark Phalan #include "preauth_plugin.h"
73159d09a2SMark Phalan 
74159d09a2SMark Phalan #if TARGET_OS_MAC
75159d09a2SMark Phalan static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
76159d09a2SMark Phalan #else
77159d09a2SMark Phalan static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
78159d09a2SMark Phalan #endif
7956a424ccSmp 
8056a424ccSmp /* XXX This is ugly and should be in a header file somewhere */
8156a424ccSmp #ifndef KRB5INT_DES_TYPES_DEFINED
8256a424ccSmp #define KRB5INT_DES_TYPES_DEFINED
8356a424ccSmp typedef unsigned char des_cblock[8];	/* crypto-block size */
8456a424ccSmp #endif
8556a424ccSmp typedef des_cblock mit_des_cblock;
8656a424ccSmp extern void mit_des_fixup_key_parity (mit_des_cblock );
8756a424ccSmp extern int mit_des_is_weak_key (mit_des_cblock );
8856a424ccSmp 
897c478bd9Sstevel@tonic-gate typedef struct _krb5_preauth_systems {
90159d09a2SMark Phalan     const char *name;
917c478bd9Sstevel@tonic-gate     int		type;
927c478bd9Sstevel@tonic-gate     int		flags;
93159d09a2SMark Phalan     void       *plugin_context;
94159d09a2SMark Phalan     preauth_server_init_proc	init;
95159d09a2SMark Phalan     preauth_server_fini_proc	fini;
96159d09a2SMark Phalan     preauth_server_edata_proc	get_edata;
97159d09a2SMark Phalan     preauth_server_verify_proc	verify_padata;
98159d09a2SMark Phalan     preauth_server_return_proc	return_padata;
99159d09a2SMark Phalan     preauth_server_free_reqcontext_proc	free_pa_reqctx;
1007c478bd9Sstevel@tonic-gate } krb5_preauth_systems;
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate static krb5_error_code verify_enc_timestamp
1037c478bd9Sstevel@tonic-gate     (krb5_context, krb5_db_entry *client,
104159d09a2SMark Phalan 		    krb5_data *req_pkt,
10556a424ccSmp 		    krb5_kdc_req *request,
106159d09a2SMark Phalan 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
107159d09a2SMark Phalan 		    preauth_get_entry_data_proc get_entry_data,
108159d09a2SMark Phalan 		    void *pa_system_context,
109159d09a2SMark Phalan 		    void **pa_request_context,
110159d09a2SMark Phalan 		    krb5_data **e_data,
111159d09a2SMark Phalan 		    krb5_authdata ***authz_data);
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate static krb5_error_code get_etype_info
1147c478bd9Sstevel@tonic-gate     (krb5_context, krb5_kdc_req *request,
11556a424ccSmp 		    krb5_db_entry *client, krb5_db_entry *server,
116159d09a2SMark Phalan 		    preauth_get_entry_data_proc get_entry_data,
117159d09a2SMark Phalan 		    void *pa_system_context,
11856a424ccSmp 		    krb5_pa_data *data);
1197c478bd9Sstevel@tonic-gate static krb5_error_code
1207c478bd9Sstevel@tonic-gate get_etype_info2(krb5_context context, krb5_kdc_req *request,
121159d09a2SMark Phalan 	        krb5_db_entry *client, krb5_db_entry *server,
122159d09a2SMark Phalan 		preauth_get_entry_data_proc get_entry_data,
123159d09a2SMark Phalan 		void *pa_system_context,
124159d09a2SMark Phalan 		krb5_pa_data *pa_data);
125159d09a2SMark Phalan static krb5_error_code
126*55fea89dSDan Cross etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
127159d09a2SMark Phalan 			 krb5_db_entry *client,
128159d09a2SMark Phalan 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
129159d09a2SMark Phalan 			 krb5_key_data *client_key,
130159d09a2SMark Phalan 			 krb5_keyblock *encrypting_key,
131159d09a2SMark Phalan 			 krb5_pa_data **send_pa,
132159d09a2SMark Phalan 			 int etype_info2);
133159d09a2SMark Phalan 
134159d09a2SMark Phalan static krb5_error_code
135*55fea89dSDan Cross return_etype_info(krb5_context, krb5_pa_data * padata,
136159d09a2SMark Phalan 		  krb5_db_entry *client,
137159d09a2SMark Phalan 		  krb5_data *req_pkt,
138159d09a2SMark Phalan 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
139159d09a2SMark Phalan 		  krb5_key_data *client_key,
140159d09a2SMark Phalan 		  krb5_keyblock *encrypting_key,
141159d09a2SMark Phalan 		  krb5_pa_data **send_pa,
142159d09a2SMark Phalan 		  preauth_get_entry_data_proc get_entry_data,
143159d09a2SMark Phalan 		  void *pa_system_context,
144159d09a2SMark Phalan 		  void **pa_request_context);
145159d09a2SMark Phalan 
1467c478bd9Sstevel@tonic-gate static krb5_error_code
147*55fea89dSDan Cross return_etype_info2(krb5_context, krb5_pa_data * padata,
14856a424ccSmp 		   krb5_db_entry *client,
149159d09a2SMark Phalan 		   krb5_data *req_pkt,
15056a424ccSmp 		   krb5_kdc_req *request, krb5_kdc_rep *reply,
15156a424ccSmp 		   krb5_key_data *client_key,
15256a424ccSmp 		   krb5_keyblock *encrypting_key,
153159d09a2SMark Phalan 		   krb5_pa_data **send_pa,
154159d09a2SMark Phalan 		   preauth_get_entry_data_proc get_entry_data,
155159d09a2SMark Phalan 		   void *pa_system_context,
156159d09a2SMark Phalan 		   void **pa_request_context);
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate static krb5_error_code return_pw_salt
159*55fea89dSDan Cross     (krb5_context, krb5_pa_data * padata,
16056a424ccSmp 		    krb5_db_entry *client,
161159d09a2SMark Phalan 		    krb5_data *req_pkt,
16256a424ccSmp 		    krb5_kdc_req *request, krb5_kdc_rep *reply,
16356a424ccSmp 		    krb5_key_data *client_key,
16456a424ccSmp 		    krb5_keyblock *encrypting_key,
165159d09a2SMark Phalan 		    krb5_pa_data **send_pa,
166159d09a2SMark Phalan 		    preauth_get_entry_data_proc get_entry_data,
167159d09a2SMark Phalan 		    void *pa_system_context,
168159d09a2SMark Phalan 		    void **pa_request_context);
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate /* SAM preauth support */
1717c478bd9Sstevel@tonic-gate static krb5_error_code verify_sam_response
17256a424ccSmp     (krb5_context, krb5_db_entry *client,
173159d09a2SMark Phalan 		    krb5_data *req_pkt,
17456a424ccSmp 		    krb5_kdc_req *request,
175159d09a2SMark Phalan 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
176159d09a2SMark Phalan 		    preauth_get_entry_data_proc get_entry_data,
177159d09a2SMark Phalan 		    void *pa_module_context,
178159d09a2SMark Phalan 		    void **pa_request_context,
179159d09a2SMark Phalan 		    krb5_data **e_data,
180159d09a2SMark Phalan 		    krb5_authdata ***authz_data);
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate static krb5_error_code get_sam_edata
1837c478bd9Sstevel@tonic-gate     (krb5_context, krb5_kdc_req *request,
18456a424ccSmp 		    krb5_db_entry *client, krb5_db_entry *server,
185159d09a2SMark Phalan 		    preauth_get_entry_data_proc get_entry_data,
186159d09a2SMark Phalan 		    void *pa_module_context,
18756a424ccSmp 		    krb5_pa_data *data);
1887c478bd9Sstevel@tonic-gate static krb5_error_code return_sam_data
189*55fea89dSDan Cross     (krb5_context, krb5_pa_data * padata,
19056a424ccSmp 		    krb5_db_entry *client,
191159d09a2SMark Phalan 		    krb5_data *req_pkt,
19256a424ccSmp 		    krb5_kdc_req *request, krb5_kdc_rep *reply,
19356a424ccSmp 		    krb5_key_data *client_key,
19456a424ccSmp 		    krb5_keyblock *encrypting_key,
195159d09a2SMark Phalan 		    krb5_pa_data **send_pa,
196159d09a2SMark Phalan 		    preauth_get_entry_data_proc get_entry_data,
197159d09a2SMark Phalan 		    void *pa_module_context,
198159d09a2SMark Phalan 		    void **pa_request_context);
1997c478bd9Sstevel@tonic-gate 
200159d09a2SMark Phalan static krb5_preauth_systems static_preauth_systems[] = {
2017c478bd9Sstevel@tonic-gate     {
2027c478bd9Sstevel@tonic-gate 	"timestamp",
2037c478bd9Sstevel@tonic-gate         KRB5_PADATA_ENC_TIMESTAMP,
2047c478bd9Sstevel@tonic-gate         0,
205159d09a2SMark Phalan 	NULL,
206159d09a2SMark Phalan 	NULL,
207159d09a2SMark Phalan 	NULL,
2087c478bd9Sstevel@tonic-gate         0,
2097c478bd9Sstevel@tonic-gate 	verify_enc_timestamp,
2107c478bd9Sstevel@tonic-gate 	0
2117c478bd9Sstevel@tonic-gate     },
2127c478bd9Sstevel@tonic-gate     {
2137c478bd9Sstevel@tonic-gate 	"etype-info",
2147c478bd9Sstevel@tonic-gate 	KRB5_PADATA_ETYPE_INFO,
2157c478bd9Sstevel@tonic-gate 	0,
216159d09a2SMark Phalan 	NULL,
217159d09a2SMark Phalan 	NULL,
218159d09a2SMark Phalan 	NULL,
2197c478bd9Sstevel@tonic-gate 	get_etype_info,
2207c478bd9Sstevel@tonic-gate 	0,
221159d09a2SMark Phalan 	return_etype_info
2227c478bd9Sstevel@tonic-gate     },
2237c478bd9Sstevel@tonic-gate     {
22456a424ccSmp 	"etype-info2",
2257c478bd9Sstevel@tonic-gate 	KRB5_PADATA_ETYPE_INFO2,
2267c478bd9Sstevel@tonic-gate 	0,
227159d09a2SMark Phalan 	NULL,
228159d09a2SMark Phalan 	NULL,
229159d09a2SMark Phalan 	NULL,
23056a424ccSmp 	get_etype_info2,
2317c478bd9Sstevel@tonic-gate 	0,
23256a424ccSmp 	return_etype_info2
2337c478bd9Sstevel@tonic-gate     },
2347c478bd9Sstevel@tonic-gate     {
2357c478bd9Sstevel@tonic-gate 	"pw-salt",
2367c478bd9Sstevel@tonic-gate 	KRB5_PADATA_PW_SALT,
2377c478bd9Sstevel@tonic-gate 	PA_PSEUDO,		/* Don't include this in the error list */
238159d09a2SMark Phalan 	NULL,
239159d09a2SMark Phalan 	NULL,
240159d09a2SMark Phalan 	NULL,
241*55fea89dSDan Cross 	0,
2427c478bd9Sstevel@tonic-gate 	0,
2437c478bd9Sstevel@tonic-gate 	return_pw_salt
2447c478bd9Sstevel@tonic-gate     },
2457c478bd9Sstevel@tonic-gate     {
2467c478bd9Sstevel@tonic-gate 	"sam-response",
2477c478bd9Sstevel@tonic-gate 	KRB5_PADATA_SAM_RESPONSE,
2487c478bd9Sstevel@tonic-gate 	0,
249159d09a2SMark Phalan 	NULL,
250159d09a2SMark Phalan 	NULL,
251159d09a2SMark Phalan 	NULL,
2527c478bd9Sstevel@tonic-gate 	0,
2537c478bd9Sstevel@tonic-gate 	verify_sam_response,
2547c478bd9Sstevel@tonic-gate 	return_sam_data
2557c478bd9Sstevel@tonic-gate     },
2567c478bd9Sstevel@tonic-gate     {
2577c478bd9Sstevel@tonic-gate 	"sam-challenge",
2587c478bd9Sstevel@tonic-gate 	KRB5_PADATA_SAM_CHALLENGE,
2597c478bd9Sstevel@tonic-gate 	PA_HARDWARE,		/* causes get_preauth_hint_list to use this */
260159d09a2SMark Phalan 	NULL,
261159d09a2SMark Phalan 	NULL,
262159d09a2SMark Phalan 	NULL,
2637c478bd9Sstevel@tonic-gate 	get_sam_edata,
2647c478bd9Sstevel@tonic-gate 	0,
2657c478bd9Sstevel@tonic-gate 	0
2667c478bd9Sstevel@tonic-gate     },
2677c478bd9Sstevel@tonic-gate     { "[end]", -1,}
2687c478bd9Sstevel@tonic-gate };
2697c478bd9Sstevel@tonic-gate 
270159d09a2SMark Phalan static krb5_preauth_systems *preauth_systems;
271159d09a2SMark Phalan static int n_preauth_systems;
272159d09a2SMark Phalan static struct plugin_dir_handle preauth_plugins;
273159d09a2SMark Phalan 
274159d09a2SMark Phalan krb5_error_code
load_preauth_plugins(krb5_context context)275159d09a2SMark Phalan load_preauth_plugins(krb5_context context)
276159d09a2SMark Phalan {
277159d09a2SMark Phalan     struct errinfo err;
278159d09a2SMark Phalan     void **preauth_plugins_ftables;
279159d09a2SMark Phalan     struct krb5plugin_preauth_server_ftable_v1 *ftable;
280159d09a2SMark Phalan     int module_count, i, j, k;
281159d09a2SMark Phalan     void *plugin_context;
282159d09a2SMark Phalan     preauth_server_init_proc server_init_proc = NULL;
283159d09a2SMark Phalan     char **kdc_realm_names = NULL;
284159d09a2SMark Phalan 
285159d09a2SMark Phalan     memset(&err, 0, sizeof(err));
286159d09a2SMark Phalan 
287159d09a2SMark Phalan     /* Attempt to load all of the preauth plugins we can find. */
288159d09a2SMark Phalan     PLUGIN_DIR_INIT(&preauth_plugins);
289159d09a2SMark Phalan     if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) {
290159d09a2SMark Phalan 	if (krb5int_open_plugin_dirs(objdirs, NULL,
291159d09a2SMark Phalan 				     &preauth_plugins, &err) != 0) {
292159d09a2SMark Phalan 	    return KRB5_PLUGIN_NO_HANDLE;
293159d09a2SMark Phalan 	}
294159d09a2SMark Phalan     }
295159d09a2SMark Phalan 
296159d09a2SMark Phalan     /* Get the method tables provided by the loaded plugins. */
297159d09a2SMark Phalan     preauth_plugins_ftables = NULL;
298159d09a2SMark Phalan     if (krb5int_get_plugin_dir_data(&preauth_plugins,
299159d09a2SMark Phalan 				    "preauthentication_server_1",
300159d09a2SMark Phalan 				    &preauth_plugins_ftables, &err) != 0) {
301159d09a2SMark Phalan 	return KRB5_PLUGIN_NO_HANDLE;
302159d09a2SMark Phalan     }
303159d09a2SMark Phalan 
304159d09a2SMark Phalan     /* Count the valid modules. */
305159d09a2SMark Phalan     module_count = sizeof(static_preauth_systems)
306159d09a2SMark Phalan 		   / sizeof(static_preauth_systems[0]);
307159d09a2SMark Phalan     if (preauth_plugins_ftables != NULL) {
308159d09a2SMark Phalan 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
309159d09a2SMark Phalan 	    ftable = preauth_plugins_ftables[i];
310159d09a2SMark Phalan 	    if ((ftable->flags_proc == NULL) &&
311159d09a2SMark Phalan 		(ftable->edata_proc == NULL) &&
312159d09a2SMark Phalan 		(ftable->verify_proc == NULL) &&
313159d09a2SMark Phalan 		(ftable->return_proc == NULL)) {
314159d09a2SMark Phalan 		continue;
315159d09a2SMark Phalan 	    }
316159d09a2SMark Phalan 	    for (j = 0;
317159d09a2SMark Phalan 		 ftable->pa_type_list != NULL &&
318159d09a2SMark Phalan 		 ftable->pa_type_list[j] > 0;
319159d09a2SMark Phalan 		 j++) {
320159d09a2SMark Phalan 		module_count++;
321159d09a2SMark Phalan 	    }
322159d09a2SMark Phalan 	}
323159d09a2SMark Phalan     }
324159d09a2SMark Phalan 
325159d09a2SMark Phalan     /* Build the complete list of supported preauthentication options, and
326159d09a2SMark Phalan      * leave room for a terminator entry. */
327159d09a2SMark Phalan     preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1));
328159d09a2SMark Phalan     if (preauth_systems == NULL) {
329159d09a2SMark Phalan 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
330159d09a2SMark Phalan 	return ENOMEM;
331159d09a2SMark Phalan     }
332159d09a2SMark Phalan 
333159d09a2SMark Phalan     /* Build a list of the names of the supported realms for this KDC.
334159d09a2SMark Phalan      * The list of names is terminated with a NULL. */
335159d09a2SMark Phalan     kdc_realm_names = malloc(sizeof(char *) * (kdc_numrealms + 1));
336159d09a2SMark Phalan     if (kdc_realm_names == NULL) {
337159d09a2SMark Phalan 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
338159d09a2SMark Phalan 	return ENOMEM;
339159d09a2SMark Phalan     }
340159d09a2SMark Phalan     for (i = 0; i < kdc_numrealms; i++) {
341159d09a2SMark Phalan 	kdc_realm_names[i] = kdc_realmlist[i]->realm_name;
342159d09a2SMark Phalan     }
343159d09a2SMark Phalan     kdc_realm_names[i] = NULL;
344159d09a2SMark Phalan 
345159d09a2SMark Phalan     /* Add the locally-supplied mechanisms to the dynamic list first. */
346159d09a2SMark Phalan     for (i = 0, k = 0;
347159d09a2SMark Phalan 	 i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
348159d09a2SMark Phalan 	 i++) {
349159d09a2SMark Phalan 	if (static_preauth_systems[i].type == -1)
350159d09a2SMark Phalan 	    break;
351159d09a2SMark Phalan 	preauth_systems[k] = static_preauth_systems[i];
352159d09a2SMark Phalan 	/* Try to initialize the preauth system.  If it fails, we'll remove it
353159d09a2SMark Phalan 	 * from the list of systems we'll be using. */
354159d09a2SMark Phalan 	plugin_context = NULL;
355159d09a2SMark Phalan 	server_init_proc = static_preauth_systems[i].init;
356159d09a2SMark Phalan 	if ((server_init_proc != NULL) &&
357159d09a2SMark Phalan 	    ((*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names) != 0)) {
358159d09a2SMark Phalan 	    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
359159d09a2SMark Phalan 	    continue;
360159d09a2SMark Phalan 	}
361159d09a2SMark Phalan 	preauth_systems[k].plugin_context = plugin_context;
362159d09a2SMark Phalan 	k++;
363159d09a2SMark Phalan     }
364159d09a2SMark Phalan 
365159d09a2SMark Phalan     /* Now add the dynamically-loaded mechanisms to the list. */
366159d09a2SMark Phalan     if (preauth_plugins_ftables != NULL) {
367159d09a2SMark Phalan 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
368159d09a2SMark Phalan 	    ftable = preauth_plugins_ftables[i];
369159d09a2SMark Phalan 	    if ((ftable->flags_proc == NULL) &&
370159d09a2SMark Phalan 		(ftable->edata_proc == NULL) &&
371159d09a2SMark Phalan 		(ftable->verify_proc == NULL) &&
372159d09a2SMark Phalan 		(ftable->return_proc == NULL)) {
373159d09a2SMark Phalan 		continue;
374159d09a2SMark Phalan 	    }
375159d09a2SMark Phalan 	    plugin_context = NULL;
376159d09a2SMark Phalan 	    for (j = 0;
377159d09a2SMark Phalan 		 ftable->pa_type_list != NULL &&
378159d09a2SMark Phalan 		 ftable->pa_type_list[j] > 0;
379159d09a2SMark Phalan 		 j++) {
380159d09a2SMark Phalan 		/* Try to initialize the plugin.  If it fails, we'll remove it
381159d09a2SMark Phalan 		 * from the list of modules we'll be using. */
382159d09a2SMark Phalan 		if (j == 0) {
383159d09a2SMark Phalan 		    server_init_proc = ftable->init_proc;
384159d09a2SMark Phalan 		    if (server_init_proc != NULL) {
385159d09a2SMark Phalan 			krb5_error_code initerr;
386159d09a2SMark Phalan 			initerr = (*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names);
387159d09a2SMark Phalan 			if (initerr) {
388159d09a2SMark Phalan 			    const char *emsg;
389159d09a2SMark Phalan 			    emsg = krb5_get_error_message(context, initerr);
390159d09a2SMark Phalan 			    if (emsg) {
391159d09a2SMark Phalan 				krb5_klog_syslog(LOG_ERR,
392159d09a2SMark Phalan 					"preauth %s failed to initialize: %s",
393159d09a2SMark Phalan 					ftable->name, emsg);
394159d09a2SMark Phalan 				krb5_free_error_message(context, emsg);
395159d09a2SMark Phalan 			    }
396159d09a2SMark Phalan 			    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
397159d09a2SMark Phalan 
398159d09a2SMark Phalan 			    break;	/* skip all modules in this plugin */
399159d09a2SMark Phalan 			}
400159d09a2SMark Phalan 		    }
401159d09a2SMark Phalan 		}
402159d09a2SMark Phalan 		preauth_systems[k].name = ftable->name;
403159d09a2SMark Phalan 		preauth_systems[k].type = ftable->pa_type_list[j];
404159d09a2SMark Phalan 		if (ftable->flags_proc != NULL)
405159d09a2SMark Phalan 		    preauth_systems[k].flags = ftable->flags_proc(context, preauth_systems[k].type);
406159d09a2SMark Phalan 		else
407159d09a2SMark Phalan 		    preauth_systems[k].flags = 0;
408159d09a2SMark Phalan 		preauth_systems[k].plugin_context = plugin_context;
409159d09a2SMark Phalan 		preauth_systems[k].init = server_init_proc;
410159d09a2SMark Phalan 		/* Only call fini once for each plugin */
411159d09a2SMark Phalan 		if (j == 0)
412159d09a2SMark Phalan 		    preauth_systems[k].fini = ftable->fini_proc;
413159d09a2SMark Phalan 		else
414159d09a2SMark Phalan 		    preauth_systems[k].fini = NULL;
415159d09a2SMark Phalan 		preauth_systems[k].get_edata = ftable->edata_proc;
416159d09a2SMark Phalan 		preauth_systems[k].verify_padata = ftable->verify_proc;
417159d09a2SMark Phalan 		preauth_systems[k].return_padata = ftable->return_proc;
418159d09a2SMark Phalan 		preauth_systems[k].free_pa_reqctx =
419159d09a2SMark Phalan 		    ftable->freepa_reqcontext_proc;
420159d09a2SMark Phalan 		k++;
421159d09a2SMark Phalan 	    }
422159d09a2SMark Phalan 	}
423159d09a2SMark Phalan 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
424159d09a2SMark Phalan     }
425159d09a2SMark Phalan     free(kdc_realm_names);
426159d09a2SMark Phalan     n_preauth_systems = k;
427159d09a2SMark Phalan     /* Add the end-of-list marker. */
428159d09a2SMark Phalan     preauth_systems[k].name = "[end]";
429159d09a2SMark Phalan     preauth_systems[k].type = -1;
430159d09a2SMark Phalan     return 0;
431159d09a2SMark Phalan }
432159d09a2SMark Phalan 
433159d09a2SMark Phalan krb5_error_code
unload_preauth_plugins(krb5_context context)434159d09a2SMark Phalan unload_preauth_plugins(krb5_context context)
435159d09a2SMark Phalan {
436159d09a2SMark Phalan     int i;
437159d09a2SMark Phalan     if (preauth_systems != NULL) {
438159d09a2SMark Phalan 	for (i = 0; i < n_preauth_systems; i++) {
439159d09a2SMark Phalan 	    if (preauth_systems[i].fini != NULL) {
440159d09a2SMark Phalan 	        (*preauth_systems[i].fini)(context,
441159d09a2SMark Phalan 					   preauth_systems[i].plugin_context);
442159d09a2SMark Phalan 	    }
443159d09a2SMark Phalan 	    memset(&preauth_systems[i], 0, sizeof(preauth_systems[i]));
444159d09a2SMark Phalan 	}
445159d09a2SMark Phalan 	free(preauth_systems);
446159d09a2SMark Phalan 	preauth_systems = NULL;
447159d09a2SMark Phalan 	n_preauth_systems = 0;
448159d09a2SMark Phalan 	krb5int_close_plugin_dirs(&preauth_plugins);
449159d09a2SMark Phalan     }
450159d09a2SMark Phalan     return 0;
451159d09a2SMark Phalan }
452159d09a2SMark Phalan 
453159d09a2SMark Phalan /*
454159d09a2SMark Phalan  * The make_padata_context() function creates a space for storing any context
455159d09a2SMark Phalan  * information which will be needed by return_padata() later.  Each preauth
456159d09a2SMark Phalan  * type gets a context storage location of its own.
457159d09a2SMark Phalan  */
458159d09a2SMark Phalan struct request_pa_context {
459159d09a2SMark Phalan     int n_contexts;
460159d09a2SMark Phalan     struct {
461159d09a2SMark Phalan 	krb5_preauth_systems *pa_system;
462159d09a2SMark Phalan 	void *pa_context;
463159d09a2SMark Phalan     } *contexts;
464159d09a2SMark Phalan };
465159d09a2SMark Phalan 
466159d09a2SMark Phalan static krb5_error_code
make_padata_context(krb5_context context,void ** padata_context)467159d09a2SMark Phalan make_padata_context(krb5_context context, void **padata_context)
468159d09a2SMark Phalan {
469159d09a2SMark Phalan     int i;
470159d09a2SMark Phalan     struct request_pa_context *ret;
471159d09a2SMark Phalan 
472159d09a2SMark Phalan     ret = malloc(sizeof(*ret));
473159d09a2SMark Phalan     if (ret == NULL) {
474159d09a2SMark Phalan 	return ENOMEM;
475159d09a2SMark Phalan     }
476159d09a2SMark Phalan 
477159d09a2SMark Phalan     ret->n_contexts = n_preauth_systems;
478159d09a2SMark Phalan     ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
479159d09a2SMark Phalan     if (ret->contexts == NULL) {
480159d09a2SMark Phalan 	free(ret);
481159d09a2SMark Phalan 	return ENOMEM;
482159d09a2SMark Phalan     }
483159d09a2SMark Phalan 
484159d09a2SMark Phalan     memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
485159d09a2SMark Phalan 
486159d09a2SMark Phalan     for (i = 0; i < ret->n_contexts; i++) {
487159d09a2SMark Phalan 	ret->contexts[i].pa_system = &preauth_systems[i];
488159d09a2SMark Phalan 	ret->contexts[i].pa_context = NULL;
489159d09a2SMark Phalan     }
490159d09a2SMark Phalan 
491159d09a2SMark Phalan     *padata_context = ret;
492159d09a2SMark Phalan 
493159d09a2SMark Phalan     return 0;
494159d09a2SMark Phalan }
495159d09a2SMark Phalan 
496159d09a2SMark Phalan /*
497159d09a2SMark Phalan  * The free_padata_context function frees any context information pointers
498159d09a2SMark Phalan  * which the check_padata() function created but which weren't already cleaned
499159d09a2SMark Phalan  * up by return_padata().
500159d09a2SMark Phalan  */
501159d09a2SMark Phalan krb5_error_code
free_padata_context(krb5_context kcontext,void ** padata_context)502159d09a2SMark Phalan free_padata_context(krb5_context kcontext, void **padata_context)
503159d09a2SMark Phalan {
504159d09a2SMark Phalan     struct request_pa_context *context;
505159d09a2SMark Phalan     krb5_preauth_systems *preauth_system;
506159d09a2SMark Phalan     void **pctx, *mctx;
507159d09a2SMark Phalan     int i;
508159d09a2SMark Phalan 
509159d09a2SMark Phalan     if (padata_context == NULL)
510159d09a2SMark Phalan 	return 0;
511159d09a2SMark Phalan 
512159d09a2SMark Phalan     context = *padata_context;
513159d09a2SMark Phalan 
514159d09a2SMark Phalan     for (i = 0; i < context->n_contexts; i++) {
515159d09a2SMark Phalan 	if (context->contexts[i].pa_context != NULL) {
516159d09a2SMark Phalan 	    preauth_system = context->contexts[i].pa_system;
517159d09a2SMark Phalan 	    mctx = preauth_system->plugin_context;
518159d09a2SMark Phalan 	    if (preauth_system->free_pa_reqctx != NULL) {
519159d09a2SMark Phalan 		pctx = &context->contexts[i].pa_context;
520159d09a2SMark Phalan 		(*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx);
521159d09a2SMark Phalan 	    }
522159d09a2SMark Phalan 	    context->contexts[i].pa_context = NULL;
523159d09a2SMark Phalan 	}
524159d09a2SMark Phalan     }
525159d09a2SMark Phalan 
526159d09a2SMark Phalan     free(context->contexts);
527159d09a2SMark Phalan     free(context);
528159d09a2SMark Phalan 
529159d09a2SMark Phalan     return 0;
530159d09a2SMark Phalan }
531159d09a2SMark Phalan 
532159d09a2SMark Phalan /* Retrieve a specified tl_data item from the given entry, and return its
533159d09a2SMark Phalan  * contents in a new krb5_data, which must be freed by the caller. */
534159d09a2SMark Phalan static krb5_error_code
get_entry_tl_data(krb5_context context,krb5_db_entry * entry,krb5_int16 tl_data_type,krb5_data ** result)535159d09a2SMark Phalan get_entry_tl_data(krb5_context context, krb5_db_entry *entry,
536159d09a2SMark Phalan 		  krb5_int16 tl_data_type, krb5_data **result)
537159d09a2SMark Phalan {
538159d09a2SMark Phalan     krb5_tl_data *tl;
539159d09a2SMark Phalan     for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
540159d09a2SMark Phalan 	if (tl->tl_data_type == tl_data_type) {
541159d09a2SMark Phalan 	    *result = malloc(sizeof(krb5_data));
542159d09a2SMark Phalan 	    if (*result == NULL) {
543159d09a2SMark Phalan 		return ENOMEM;
544159d09a2SMark Phalan 	    }
545159d09a2SMark Phalan 	    (*result)->magic = KV5M_DATA;
546159d09a2SMark Phalan 	    (*result)->data = malloc(tl->tl_data_length);
547159d09a2SMark Phalan 	    if ((*result)->data == NULL) {
548159d09a2SMark Phalan 		free(*result);
549159d09a2SMark Phalan 		*result = NULL;
550159d09a2SMark Phalan 		return ENOMEM;
551159d09a2SMark Phalan 	    }
552159d09a2SMark Phalan 	    memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length);
553159d09a2SMark Phalan 	    return 0;
554159d09a2SMark Phalan 	}
555159d09a2SMark Phalan     }
556159d09a2SMark Phalan     return ENOENT;
557159d09a2SMark Phalan }
558159d09a2SMark Phalan 
559159d09a2SMark Phalan /*
560159d09a2SMark Phalan  * Retrieve a specific piece of information pertaining to the entry or the
561159d09a2SMark Phalan  * request and return it in a new krb5_data item which the caller must free.
562159d09a2SMark Phalan  *
563159d09a2SMark Phalan  * This may require massaging data into a contrived format, but it will
564159d09a2SMark Phalan  * hopefully keep us from having to reveal library-internal functions to
565159d09a2SMark Phalan  * modules.
566159d09a2SMark Phalan  */
567159d09a2SMark Phalan static krb5_error_code
get_entry_data(krb5_context context,krb5_kdc_req * request,krb5_db_entry * entry,krb5_int32 type,krb5_data ** result)568159d09a2SMark Phalan get_entry_data(krb5_context context,
569159d09a2SMark Phalan 	       krb5_kdc_req *request, krb5_db_entry *entry,
570159d09a2SMark Phalan 	       krb5_int32  type,
571159d09a2SMark Phalan 	       krb5_data **result)
572159d09a2SMark Phalan {
573159d09a2SMark Phalan     int i, k;
574159d09a2SMark Phalan     krb5_data *ret;
575159d09a2SMark Phalan     krb5_deltat *delta;
576159d09a2SMark Phalan     krb5_keyblock *keys;
577159d09a2SMark Phalan     krb5_key_data *entry_key;
578159d09a2SMark Phalan 
579159d09a2SMark Phalan     switch (type) {
580159d09a2SMark Phalan     case krb5plugin_preauth_entry_request_certificate:
581159d09a2SMark Phalan 	return get_entry_tl_data(context, entry,
582159d09a2SMark Phalan 				 KRB5_TL_USER_CERTIFICATE, result);
583159d09a2SMark Phalan 	break;
584159d09a2SMark Phalan     case krb5plugin_preauth_entry_max_time_skew:
585159d09a2SMark Phalan 	ret = malloc(sizeof(krb5_data));
586159d09a2SMark Phalan 	if (ret == NULL)
587159d09a2SMark Phalan 	    return ENOMEM;
588159d09a2SMark Phalan 	delta = malloc(sizeof(krb5_deltat));
589159d09a2SMark Phalan 	if (delta == NULL) {
590159d09a2SMark Phalan 	    free(ret);
591159d09a2SMark Phalan 	    return ENOMEM;
592159d09a2SMark Phalan 	}
593159d09a2SMark Phalan 	*delta = context->clockskew;
594159d09a2SMark Phalan 	ret->data = (char *) delta;
595159d09a2SMark Phalan 	ret->length = sizeof(*delta);
596159d09a2SMark Phalan 	*result = ret;
597159d09a2SMark Phalan 	return 0;
598159d09a2SMark Phalan 	break;
599159d09a2SMark Phalan     case krb5plugin_preauth_keys:
600159d09a2SMark Phalan 	ret = malloc(sizeof(krb5_data));
601159d09a2SMark Phalan 	if (ret == NULL)
602159d09a2SMark Phalan 	    return ENOMEM;
603159d09a2SMark Phalan 	keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1));
604159d09a2SMark Phalan 	if (keys == NULL) {
605159d09a2SMark Phalan 	    free(ret);
606159d09a2SMark Phalan 	    return ENOMEM;
607159d09a2SMark Phalan 	}
608159d09a2SMark Phalan 	ret->data = (char *) keys;
609159d09a2SMark Phalan 	ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
610159d09a2SMark Phalan 	memset(ret->data, 0, ret->length);
611159d09a2SMark Phalan 	k = 0;
612159d09a2SMark Phalan 	for (i = 0; i < request->nktypes; i++) {
613159d09a2SMark Phalan 	    entry_key = NULL;
614159d09a2SMark Phalan 	    if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
615159d09a2SMark Phalan 				      -1, 0, &entry_key) != 0)
616159d09a2SMark Phalan 		continue;
617159d09a2SMark Phalan 	    if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
618159d09a2SMark Phalan 					    entry_key, &keys[k], NULL) != 0) {
619159d09a2SMark Phalan 		if (keys[k].contents != NULL)
620159d09a2SMark Phalan 		    krb5_free_keyblock_contents(context, &keys[k]);
621159d09a2SMark Phalan 		memset(&keys[k], 0, sizeof(keys[k]));
622159d09a2SMark Phalan 		continue;
623159d09a2SMark Phalan 	    }
624159d09a2SMark Phalan 	    k++;
625159d09a2SMark Phalan 	}
626159d09a2SMark Phalan 	if (k > 0) {
627159d09a2SMark Phalan 	    *result = ret;
628159d09a2SMark Phalan 	    return 0;
629159d09a2SMark Phalan 	} else {
630159d09a2SMark Phalan 	    free(keys);
631159d09a2SMark Phalan 	    free(ret);
632159d09a2SMark Phalan 	}
633159d09a2SMark Phalan 	break;
634159d09a2SMark Phalan     case krb5plugin_preauth_request_body:
635159d09a2SMark Phalan 	ret = NULL;
636159d09a2SMark Phalan 	encode_krb5_kdc_req_body(request, &ret);
637159d09a2SMark Phalan 	if (ret != NULL) {
638159d09a2SMark Phalan 	    *result = ret;
639159d09a2SMark Phalan 	    return 0;
640159d09a2SMark Phalan 	}
641159d09a2SMark Phalan 	return ASN1_PARSE_ERROR;
642159d09a2SMark Phalan 	break;
643159d09a2SMark Phalan     default:
644159d09a2SMark Phalan 	break;
645159d09a2SMark Phalan     }
646159d09a2SMark Phalan     return ENOENT;
647159d09a2SMark Phalan }
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate static krb5_error_code
find_pa_system(int type,krb5_preauth_systems ** preauth)6507c478bd9Sstevel@tonic-gate find_pa_system(int type, krb5_preauth_systems **preauth)
6517c478bd9Sstevel@tonic-gate {
652159d09a2SMark Phalan     krb5_preauth_systems *ap;
653159d09a2SMark Phalan 
654159d09a2SMark Phalan     ap = preauth_systems ? preauth_systems : static_preauth_systems;
6557c478bd9Sstevel@tonic-gate     while ((ap->type != -1) && (ap->type != type))
6567c478bd9Sstevel@tonic-gate 	ap++;
6577c478bd9Sstevel@tonic-gate     if (ap->type == -1)
6587c478bd9Sstevel@tonic-gate 	return(KRB5_PREAUTH_BAD_TYPE);
6597c478bd9Sstevel@tonic-gate     *preauth = ap;
6607c478bd9Sstevel@tonic-gate     return 0;
661*55fea89dSDan Cross }
6627c478bd9Sstevel@tonic-gate 
663159d09a2SMark Phalan static krb5_error_code
find_pa_context(krb5_preauth_systems * pa_sys,struct request_pa_context * context,void *** pa_context)664159d09a2SMark Phalan find_pa_context(krb5_preauth_systems *pa_sys,
665159d09a2SMark Phalan 		struct request_pa_context *context,
666159d09a2SMark Phalan 		void ***pa_context)
667159d09a2SMark Phalan {
668159d09a2SMark Phalan     int i;
669159d09a2SMark Phalan 
670159d09a2SMark Phalan     *pa_context = 0;
671159d09a2SMark Phalan 
672159d09a2SMark Phalan     if (context == NULL)
673159d09a2SMark Phalan 	return KRB5KRB_ERR_GENERIC;
674159d09a2SMark Phalan 
675159d09a2SMark Phalan     for (i = 0; i < context->n_contexts; i++) {
676159d09a2SMark Phalan 	if (context->contexts[i].pa_system == pa_sys) {
677159d09a2SMark Phalan 	    *pa_context = &context->contexts[i].pa_context;
678159d09a2SMark Phalan 	    return 0;
679159d09a2SMark Phalan 	}
680159d09a2SMark Phalan     }
681159d09a2SMark Phalan 
682159d09a2SMark Phalan     return KRB5KRB_ERR_GENERIC;
683159d09a2SMark Phalan }
684159d09a2SMark Phalan 
685159d09a2SMark Phalan /*
686159d09a2SMark Phalan  * Create a list of indices into the preauth_systems array, sorted by order of
687159d09a2SMark Phalan  * preference.
688159d09a2SMark Phalan  */
689159d09a2SMark Phalan static krb5_boolean
pa_list_includes(krb5_pa_data ** pa_data,krb5_preauthtype pa_type)690159d09a2SMark Phalan pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
691159d09a2SMark Phalan {
692159d09a2SMark Phalan     while (*pa_data != NULL) {
693159d09a2SMark Phalan 	if ((*pa_data)->pa_type == pa_type)
694159d09a2SMark Phalan 	    return TRUE;
695159d09a2SMark Phalan 	pa_data++;
696159d09a2SMark Phalan     }
697159d09a2SMark Phalan     return FALSE;
698159d09a2SMark Phalan }
699159d09a2SMark Phalan static void
sort_pa_order(krb5_context context,krb5_kdc_req * request,int * pa_order)700159d09a2SMark Phalan sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
701159d09a2SMark Phalan {
702159d09a2SMark Phalan     int i, j, k, n_repliers, n_key_replacers;
703159d09a2SMark Phalan 
704159d09a2SMark Phalan     /* First, set up the default order. */
705159d09a2SMark Phalan     i = 0;
706159d09a2SMark Phalan     for (j = 0; j < n_preauth_systems; j++) {
707159d09a2SMark Phalan         if (preauth_systems[j].return_padata != NULL)
708159d09a2SMark Phalan 	    pa_order[i++] = j;
709159d09a2SMark Phalan     }
710159d09a2SMark Phalan     n_repliers = i;
711159d09a2SMark Phalan     pa_order[n_repliers] = -1;
712159d09a2SMark Phalan 
713159d09a2SMark Phalan     /* Reorder so that PA_REPLACES_KEY modules are listed first. */
714159d09a2SMark Phalan     for (i = 0; i < n_repliers; i++) {
715159d09a2SMark Phalan 	/* If this module replaces the key, then it's okay to leave it where it
716159d09a2SMark Phalan 	 * is in the order. */
717159d09a2SMark Phalan 	if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
718159d09a2SMark Phalan 	    continue;
719159d09a2SMark Phalan 	/* If not, search for a module which does, and swap in the first one we
720159d09a2SMark Phalan 	 * find. */
721159d09a2SMark Phalan         for (j = i + 1; j < n_repliers; j++) {
722159d09a2SMark Phalan 	    if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
723159d09a2SMark Phalan                 k = pa_order[j];
724159d09a2SMark Phalan 		pa_order[j] = pa_order[i];
725159d09a2SMark Phalan 		pa_order[i] = k;
726159d09a2SMark Phalan 		break;
727159d09a2SMark Phalan 	    }
728159d09a2SMark Phalan         }
729159d09a2SMark Phalan     }
730159d09a2SMark Phalan 
731159d09a2SMark Phalan     if (request->padata != NULL) {
732159d09a2SMark Phalan 	/* Now reorder the subset of modules which replace the key,
733159d09a2SMark Phalan 	 * bubbling those which handle pa_data types provided by the
734159d09a2SMark Phalan 	 * client ahead of the others. */
735159d09a2SMark Phalan 	for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
736159d09a2SMark Phalan 	    continue;
737159d09a2SMark Phalan 	}
738159d09a2SMark Phalan 	n_key_replacers = i;
739159d09a2SMark Phalan 	for (i = 0; i < n_key_replacers; i++) {
740159d09a2SMark Phalan 	    if (pa_list_includes(request->padata,
741159d09a2SMark Phalan 				preauth_systems[pa_order[i]].type))
742159d09a2SMark Phalan 		continue;
743159d09a2SMark Phalan 	    for (j = i + 1; j < n_key_replacers; j++) {
744159d09a2SMark Phalan 		if (pa_list_includes(request->padata,
745159d09a2SMark Phalan 				    preauth_systems[pa_order[j]].type)) {
746159d09a2SMark Phalan 		    k = pa_order[j];
747159d09a2SMark Phalan 		    pa_order[j] = pa_order[i];
748159d09a2SMark Phalan 		    pa_order[i] = k;
749159d09a2SMark Phalan 		    break;
750159d09a2SMark Phalan 		}
751159d09a2SMark Phalan 	    }
752159d09a2SMark Phalan 	}
753159d09a2SMark Phalan     }
754159d09a2SMark Phalan #ifdef DEBUG
755159d09a2SMark Phalan     krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
756159d09a2SMark Phalan     for (i = 0; i < n_preauth_systems; i++) {
757159d09a2SMark Phalan 	if (preauth_systems[i].return_padata != NULL)
758159d09a2SMark Phalan             krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
759159d09a2SMark Phalan 			     preauth_systems[i].type);
760159d09a2SMark Phalan     }
761159d09a2SMark Phalan     krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
762159d09a2SMark Phalan     for (i = 0; pa_order[i] != -1; i++) {
763159d09a2SMark Phalan         krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
764159d09a2SMark Phalan 			 preauth_systems[pa_order[i]].name,
765159d09a2SMark Phalan 			 preauth_systems[pa_order[i]].type);
766159d09a2SMark Phalan     }
767159d09a2SMark Phalan #endif
768159d09a2SMark Phalan }
769159d09a2SMark Phalan 
missing_required_preauth(krb5_db_entry * client,krb5_db_entry * server,krb5_enc_tkt_part * enc_tkt_reply)77056a424ccSmp const char *missing_required_preauth(krb5_db_entry *client,
77156a424ccSmp 				     krb5_db_entry *server,
77256a424ccSmp 				     krb5_enc_tkt_part *enc_tkt_reply)
7737c478bd9Sstevel@tonic-gate {
7747c478bd9Sstevel@tonic-gate #if 0
7757c478bd9Sstevel@tonic-gate     /*
7767c478bd9Sstevel@tonic-gate      * If this is the pwchange service, and the pre-auth bit is set,
7777c478bd9Sstevel@tonic-gate      * allow it even if the HW preauth would normally be required.
778*55fea89dSDan Cross      *
7797c478bd9Sstevel@tonic-gate      * Sandia national labs wanted this for some strange reason... we
7807c478bd9Sstevel@tonic-gate      * leave it disabled normally.
7817c478bd9Sstevel@tonic-gate      */
7827c478bd9Sstevel@tonic-gate     if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) &&
7837c478bd9Sstevel@tonic-gate 	isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
7847c478bd9Sstevel@tonic-gate 	return 0;
7857c478bd9Sstevel@tonic-gate #endif
786*55fea89dSDan Cross 
7877c478bd9Sstevel@tonic-gate #ifdef DEBUG
7887c478bd9Sstevel@tonic-gate     krb5_klog_syslog (LOG_DEBUG,
7897c478bd9Sstevel@tonic-gate 		      "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
7907c478bd9Sstevel@tonic-gate 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
7917c478bd9Sstevel@tonic-gate 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
7927c478bd9Sstevel@tonic-gate 		      isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
7937c478bd9Sstevel@tonic-gate 		      isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
7947c478bd9Sstevel@tonic-gate #endif
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
7977c478bd9Sstevel@tonic-gate 	 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
7987c478bd9Sstevel@tonic-gate 	return "NEEDED_PREAUTH";
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
8017c478bd9Sstevel@tonic-gate 	!isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
8027c478bd9Sstevel@tonic-gate 	return "NEEDED_HW_PREAUTH";
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate     return 0;
8057c478bd9Sstevel@tonic-gate }
8067c478bd9Sstevel@tonic-gate 
get_preauth_hint_list(krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_data * e_data)80756a424ccSmp void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client,
80856a424ccSmp 			   krb5_db_entry *server, krb5_data *e_data)
8097c478bd9Sstevel@tonic-gate {
8107c478bd9Sstevel@tonic-gate     int hw_only;
8117c478bd9Sstevel@tonic-gate     krb5_preauth_systems *ap;
8127c478bd9Sstevel@tonic-gate     krb5_pa_data **pa_data, **pa;
8137c478bd9Sstevel@tonic-gate     krb5_data *edat;
8147c478bd9Sstevel@tonic-gate     krb5_error_code retval;
815*55fea89dSDan Cross 
8167c478bd9Sstevel@tonic-gate     /* Zero these out in case we need to abort */
8177c478bd9Sstevel@tonic-gate     e_data->length = 0;
8187c478bd9Sstevel@tonic-gate     e_data->data = 0;
819*55fea89dSDan Cross 
8207c478bd9Sstevel@tonic-gate     hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
821159d09a2SMark Phalan     pa_data = malloc(sizeof(krb5_pa_data *) * (n_preauth_systems+1));
8227c478bd9Sstevel@tonic-gate     if (pa_data == 0)
8237c478bd9Sstevel@tonic-gate 	return;
824159d09a2SMark Phalan     memset(pa_data, 0, sizeof(krb5_pa_data *) * (n_preauth_systems+1));
8257c478bd9Sstevel@tonic-gate     pa = pa_data;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate     for (ap = preauth_systems; ap->type != -1; ap++) {
8287c478bd9Sstevel@tonic-gate 	if (hw_only && !(ap->flags & PA_HARDWARE))
8297c478bd9Sstevel@tonic-gate 	    continue;
8307c478bd9Sstevel@tonic-gate 	if (ap->flags & PA_PSEUDO)
8317c478bd9Sstevel@tonic-gate 	    continue;
8327c478bd9Sstevel@tonic-gate 	*pa = malloc(sizeof(krb5_pa_data));
8337c478bd9Sstevel@tonic-gate 	if (*pa == 0)
8347c478bd9Sstevel@tonic-gate 	    goto errout;
8357c478bd9Sstevel@tonic-gate 	memset(*pa, 0, sizeof(krb5_pa_data));
8367c478bd9Sstevel@tonic-gate 	(*pa)->magic = KV5M_PA_DATA;
8377c478bd9Sstevel@tonic-gate 	(*pa)->pa_type = ap->type;
8387c478bd9Sstevel@tonic-gate 	if (ap->get_edata) {
839159d09a2SMark Phalan 	  retval = (ap->get_edata)(kdc_context, request, client, server,
840159d09a2SMark Phalan 				   get_entry_data, ap->plugin_context, *pa);
8417c478bd9Sstevel@tonic-gate 	  if (retval) {
8427c478bd9Sstevel@tonic-gate 	    /* just failed on this type, continue */
8437c478bd9Sstevel@tonic-gate 	    free(*pa);
8447c478bd9Sstevel@tonic-gate 	    *pa = 0;
8457c478bd9Sstevel@tonic-gate 	    continue;
8467c478bd9Sstevel@tonic-gate 	  }
8477c478bd9Sstevel@tonic-gate 	}
8487c478bd9Sstevel@tonic-gate 	pa++;
8497c478bd9Sstevel@tonic-gate     }
8507c478bd9Sstevel@tonic-gate     if (pa_data[0] == 0) {
8517c478bd9Sstevel@tonic-gate 	krb5_klog_syslog (LOG_INFO,
8527c478bd9Sstevel@tonic-gate 			  "%spreauth required but hint list is empty",
8537c478bd9Sstevel@tonic-gate 			  hw_only ? "hw" : "");
8547c478bd9Sstevel@tonic-gate     }
855ba7b222eSGlenn Barry     retval = encode_krb5_padata_sequence((krb5_pa_data * const *) pa_data,
8567c478bd9Sstevel@tonic-gate 					 &edat);
8577c478bd9Sstevel@tonic-gate     if (retval)
8587c478bd9Sstevel@tonic-gate 	goto errout;
8597c478bd9Sstevel@tonic-gate     *e_data = *edat;
8607c478bd9Sstevel@tonic-gate     free(edat);
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate errout:
8637c478bd9Sstevel@tonic-gate     krb5_free_pa_data(kdc_context, pa_data);
8647c478bd9Sstevel@tonic-gate     return;
8657c478bd9Sstevel@tonic-gate }
8667c478bd9Sstevel@tonic-gate 
867159d09a2SMark Phalan /*
868159d09a2SMark Phalan  * Add authorization data returned from preauth modules to the ticket
869159d09a2SMark Phalan  * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
870159d09a2SMark Phalan  */
871159d09a2SMark Phalan static krb5_error_code
add_authorization_data(krb5_enc_tkt_part * enc_tkt_part,krb5_authdata ** ad)872159d09a2SMark Phalan add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
873159d09a2SMark Phalan {
874159d09a2SMark Phalan     krb5_authdata **newad;
875159d09a2SMark Phalan     int oldones, newones;
876159d09a2SMark Phalan     int i;
877159d09a2SMark Phalan 
878159d09a2SMark Phalan     if (enc_tkt_part == NULL || ad == NULL)
879159d09a2SMark Phalan 	return EINVAL;
880159d09a2SMark Phalan 
881159d09a2SMark Phalan     for (newones = 0; ad[newones] != NULL; newones++);
882159d09a2SMark Phalan     if (newones == 0)
883159d09a2SMark Phalan 	return 0;   /* nothing to add */
884159d09a2SMark Phalan 
885159d09a2SMark Phalan     if (enc_tkt_part->authorization_data == NULL)
886159d09a2SMark Phalan 	oldones = 0;
887159d09a2SMark Phalan     else
888159d09a2SMark Phalan 	for (oldones = 0;
889159d09a2SMark Phalan 	     enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
890159d09a2SMark Phalan 
891159d09a2SMark Phalan     newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
892159d09a2SMark Phalan     if (newad == NULL)
893159d09a2SMark Phalan 	return ENOMEM;
894159d09a2SMark Phalan 
895159d09a2SMark Phalan     /* Copy any existing pointers */
896159d09a2SMark Phalan     for (i = 0; i < oldones; i++)
897159d09a2SMark Phalan 	newad[i] = enc_tkt_part->authorization_data[i];
898159d09a2SMark Phalan 
899159d09a2SMark Phalan     /* Add the new ones */
900159d09a2SMark Phalan     for (i = 0; i < newones; i++)
901159d09a2SMark Phalan 	newad[oldones+i] = ad[i];
902159d09a2SMark Phalan 
903159d09a2SMark Phalan     /* Terminate the new list */
904159d09a2SMark Phalan     newad[oldones+i] = NULL;
905159d09a2SMark Phalan 
906159d09a2SMark Phalan     /* Free any existing list */
907159d09a2SMark Phalan     if (enc_tkt_part->authorization_data != NULL)
908159d09a2SMark Phalan 	free(enc_tkt_part->authorization_data);
909159d09a2SMark Phalan 
910159d09a2SMark Phalan     /* Install our new list */
911159d09a2SMark Phalan     enc_tkt_part->authorization_data = newad;
912159d09a2SMark Phalan 
913159d09a2SMark Phalan     return 0;
914159d09a2SMark Phalan }
915159d09a2SMark Phalan 
9167c478bd9Sstevel@tonic-gate /*
9177c478bd9Sstevel@tonic-gate  * This routine is called to verify the preauthentication information
9187c478bd9Sstevel@tonic-gate  * for a V5 request.
919159d09a2SMark Phalan  *
9207c478bd9Sstevel@tonic-gate  * Returns 0 if the pre-authentication is valid, non-zero to indicate
9217c478bd9Sstevel@tonic-gate  * an error code of some sort.
9227c478bd9Sstevel@tonic-gate  */
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate krb5_error_code
check_padata(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,void ** padata_context,krb5_data * e_data)925159d09a2SMark Phalan check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
926159d09a2SMark Phalan 	      krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
927159d09a2SMark Phalan 	      void **padata_context, krb5_data *e_data)
9287c478bd9Sstevel@tonic-gate {
9297c478bd9Sstevel@tonic-gate     krb5_error_code retval = 0;
9307c478bd9Sstevel@tonic-gate     krb5_pa_data **padata;
9317c478bd9Sstevel@tonic-gate     krb5_preauth_systems *pa_sys;
932159d09a2SMark Phalan     void **pa_context;
933159d09a2SMark Phalan     krb5_data *pa_e_data = NULL, *tmp_e_data = NULL;
934159d09a2SMark Phalan     int	pa_ok = 0, pa_found = 0;
935159d09a2SMark Phalan     krb5_error_code saved_retval = 0;
936159d09a2SMark Phalan     int use_saved_retval = 0;
937159d09a2SMark Phalan     const char *emsg;
938159d09a2SMark Phalan     krb5_authdata **tmp_authz_data = NULL;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate     if (request->padata == 0)
9417c478bd9Sstevel@tonic-gate 	return 0;
9427c478bd9Sstevel@tonic-gate 
943159d09a2SMark Phalan     if (make_padata_context(context, padata_context) != 0) {
944159d09a2SMark Phalan 	return KRB5KRB_ERR_GENERIC;
945159d09a2SMark Phalan     }
946159d09a2SMark Phalan 
9477c478bd9Sstevel@tonic-gate #ifdef DEBUG
9487c478bd9Sstevel@tonic-gate     krb5_klog_syslog (LOG_DEBUG, "checking padata");
9497c478bd9Sstevel@tonic-gate #endif
9507c478bd9Sstevel@tonic-gate     for (padata = request->padata; *padata; padata++) {
9517c478bd9Sstevel@tonic-gate #ifdef DEBUG
9527c478bd9Sstevel@tonic-gate 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type);
9537c478bd9Sstevel@tonic-gate #endif
9547c478bd9Sstevel@tonic-gate 	if (find_pa_system((*padata)->pa_type, &pa_sys))
9557c478bd9Sstevel@tonic-gate 	    continue;
956159d09a2SMark Phalan 	if (find_pa_context(pa_sys, *padata_context, &pa_context))
957159d09a2SMark Phalan 	    continue;
9587c478bd9Sstevel@tonic-gate #ifdef DEBUG
9597c478bd9Sstevel@tonic-gate 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
9607c478bd9Sstevel@tonic-gate #endif
9617c478bd9Sstevel@tonic-gate 	if (pa_sys->verify_padata == 0)
9627c478bd9Sstevel@tonic-gate 	    continue;
9637c478bd9Sstevel@tonic-gate 	pa_found++;
964159d09a2SMark Phalan 	retval = pa_sys->verify_padata(context, client, req_pkt, request,
965159d09a2SMark Phalan 				       enc_tkt_reply, *padata,
966159d09a2SMark Phalan 				       get_entry_data, pa_sys->plugin_context,
967159d09a2SMark Phalan 				       pa_context, &tmp_e_data, &tmp_authz_data);
9687c478bd9Sstevel@tonic-gate 	if (retval) {
969159d09a2SMark Phalan 	    emsg = krb5_get_error_message (context, retval);
9707c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
971159d09a2SMark Phalan 			      pa_sys->name, emsg);
972159d09a2SMark Phalan 	    krb5_free_error_message (context, emsg);
973159d09a2SMark Phalan 	    /* Ignore authorization data returned from modules that fail */
974159d09a2SMark Phalan 	    if (tmp_authz_data != NULL) {
975159d09a2SMark Phalan 		krb5_free_authdata(context, tmp_authz_data);
976159d09a2SMark Phalan 		tmp_authz_data = NULL;
977159d09a2SMark Phalan 	    }
9787c478bd9Sstevel@tonic-gate 	    if (pa_sys->flags & PA_REQUIRED) {
979159d09a2SMark Phalan 		/* free up any previous edata we might have been saving */
980159d09a2SMark Phalan 		if (pa_e_data != NULL)
981159d09a2SMark Phalan 		    krb5_free_data(context, pa_e_data);
982159d09a2SMark Phalan 		pa_e_data = tmp_e_data;
983159d09a2SMark Phalan 		tmp_e_data = NULL;
984159d09a2SMark Phalan 		use_saved_retval = 0; /* Make sure we use the current retval */
9857c478bd9Sstevel@tonic-gate 		pa_ok = 0;
9867c478bd9Sstevel@tonic-gate 		break;
9877c478bd9Sstevel@tonic-gate 	    }
988159d09a2SMark Phalan 	    /*
989159d09a2SMark Phalan 	     * We'll return edata from either the first PA_REQUIRED module
990159d09a2SMark Phalan 	     * that fails, or the first non-PA_REQUIRED module that fails.
991159d09a2SMark Phalan 	     * Hang on to edata from the first non-PA_REQUIRED module.
992159d09a2SMark Phalan 	     * If we've already got one saved, simply discard this one.
993159d09a2SMark Phalan 	     */
994159d09a2SMark Phalan 	    if (tmp_e_data != NULL) {
995159d09a2SMark Phalan 		if (pa_e_data == NULL) {
996159d09a2SMark Phalan 		    /* save the first error code and e-data */
997159d09a2SMark Phalan 		    pa_e_data = tmp_e_data;
998159d09a2SMark Phalan 		    tmp_e_data = NULL;
999159d09a2SMark Phalan 		    saved_retval = retval;
1000159d09a2SMark Phalan 		    use_saved_retval = 1;
1001159d09a2SMark Phalan 		} else {
1002159d09a2SMark Phalan 		    /* discard this extra e-data from non-PA_REQUIRED module */
1003159d09a2SMark Phalan 		    krb5_free_data(context, tmp_e_data);
1004159d09a2SMark Phalan 		    tmp_e_data = NULL;
1005159d09a2SMark Phalan 		}
1006159d09a2SMark Phalan 	    }
10077c478bd9Sstevel@tonic-gate 	} else {
10087c478bd9Sstevel@tonic-gate #ifdef DEBUG
10097c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
10107c478bd9Sstevel@tonic-gate #endif
1011159d09a2SMark Phalan 	    /* Ignore any edata returned on success */
1012159d09a2SMark Phalan 	    if (tmp_e_data != NULL) {
1013159d09a2SMark Phalan 	        krb5_free_data(context, tmp_e_data);
1014159d09a2SMark Phalan 		tmp_e_data = NULL;
1015159d09a2SMark Phalan 	    }
1016159d09a2SMark Phalan 	    /* Add any authorization data to the ticket */
1017159d09a2SMark Phalan 	    if (tmp_authz_data != NULL) {
1018159d09a2SMark Phalan 		add_authorization_data(enc_tkt_reply, tmp_authz_data);
1019159d09a2SMark Phalan 		free(tmp_authz_data);
1020159d09a2SMark Phalan 		tmp_authz_data = NULL;
1021159d09a2SMark Phalan 	    }
10227c478bd9Sstevel@tonic-gate 	    pa_ok = 1;
1023159d09a2SMark Phalan 	    if (pa_sys->flags & PA_SUFFICIENT)
10247c478bd9Sstevel@tonic-gate 		break;
10257c478bd9Sstevel@tonic-gate 	}
10267c478bd9Sstevel@tonic-gate     }
1027159d09a2SMark Phalan 
1028159d09a2SMark Phalan     /* Don't bother copying and returning e-data on success */
1029159d09a2SMark Phalan     if (pa_ok && pa_e_data != NULL) {
1030159d09a2SMark Phalan 	krb5_free_data(context, pa_e_data);
1031159d09a2SMark Phalan 	pa_e_data = NULL;
1032159d09a2SMark Phalan     }
1033159d09a2SMark Phalan     /* Return any e-data from the preauth that caused us to exit the loop */
1034159d09a2SMark Phalan     if (pa_e_data != NULL) {
1035159d09a2SMark Phalan 	e_data->data = malloc(pa_e_data->length);
1036159d09a2SMark Phalan 	if (e_data->data == NULL) {
1037159d09a2SMark Phalan 	    krb5_free_data(context, pa_e_data);
1038159d09a2SMark Phalan 	    /* Solaris Kerberos */
1039159d09a2SMark Phalan 	    return ENOMEM;
1040159d09a2SMark Phalan 	}
1041159d09a2SMark Phalan 	memcpy(e_data->data, pa_e_data->data, pa_e_data->length);
1042159d09a2SMark Phalan 	e_data->length = pa_e_data->length;
1043159d09a2SMark Phalan 	krb5_free_data(context, pa_e_data);
1044159d09a2SMark Phalan 	pa_e_data = NULL;
1045159d09a2SMark Phalan 	if (use_saved_retval != 0)
1046159d09a2SMark Phalan 	    retval = saved_retval;
1047159d09a2SMark Phalan     }
1048159d09a2SMark Phalan 
10497c478bd9Sstevel@tonic-gate     if (pa_ok)
10507c478bd9Sstevel@tonic-gate 	return 0;
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate     /* pa system was not found, but principal doesn't require preauth */
10537c478bd9Sstevel@tonic-gate     if (!pa_found &&
1054159d09a2SMark Phalan 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1055159d09a2SMark Phalan 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
10567c478bd9Sstevel@tonic-gate        return 0;
10577c478bd9Sstevel@tonic-gate 
1058159d09a2SMark Phalan     if (!pa_found) {
1059159d09a2SMark Phalan 	emsg = krb5_get_error_message(context, retval);
1060159d09a2SMark Phalan 	krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
1061159d09a2SMark Phalan 	krb5_free_error_message(context, emsg);
1062159d09a2SMark Phalan     }
1063159d09a2SMark Phalan     /* The following switch statement allows us
1064159d09a2SMark Phalan      * to return some preauth system errors back to the client.
1065159d09a2SMark Phalan      */
1066159d09a2SMark Phalan     switch(retval) {
1067159d09a2SMark Phalan     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
106856a424ccSmp     case KRB5KRB_AP_ERR_SKEW:
1069159d09a2SMark Phalan     case KRB5KDC_ERR_ETYPE_NOSUPP:
1070159d09a2SMark Phalan     /* rfc 4556 */
1071159d09a2SMark Phalan     case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
1072159d09a2SMark Phalan     case KRB5KDC_ERR_INVALID_SIG:
1073159d09a2SMark Phalan     case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
1074159d09a2SMark Phalan     case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
1075159d09a2SMark Phalan     case KRB5KDC_ERR_INVALID_CERTIFICATE:
1076159d09a2SMark Phalan     case KRB5KDC_ERR_REVOKED_CERTIFICATE:
1077159d09a2SMark Phalan     case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
1078159d09a2SMark Phalan     case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
1079159d09a2SMark Phalan     case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
1080159d09a2SMark Phalan     case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
1081159d09a2SMark Phalan     case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
1082159d09a2SMark Phalan     case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
1083159d09a2SMark Phalan     case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
1084159d09a2SMark Phalan     /* earlier drafts of what became rfc 4556 */
1085159d09a2SMark Phalan     case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
1086159d09a2SMark Phalan     case KRB5KDC_ERR_KDC_NOT_TRUSTED:
1087159d09a2SMark Phalan     case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
1088159d09a2SMark Phalan     /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
1089159d09a2SMark Phalan     /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
109056a424ccSmp 	return retval;
109156a424ccSmp     default:
109256a424ccSmp 	return KRB5KDC_ERR_PREAUTH_FAILED;
10937c478bd9Sstevel@tonic-gate     }
10947c478bd9Sstevel@tonic-gate }
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate /*
10977c478bd9Sstevel@tonic-gate  * return_padata creates any necessary preauthentication
10987c478bd9Sstevel@tonic-gate  * structures which should be returned by the KDC to the client
10997c478bd9Sstevel@tonic-gate  */
11007c478bd9Sstevel@tonic-gate krb5_error_code
return_padata(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,void ** padata_context)1101159d09a2SMark Phalan return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
110256a424ccSmp 	      krb5_kdc_req *request, krb5_kdc_rep *reply,
1103159d09a2SMark Phalan 	      krb5_key_data *client_key, krb5_keyblock *encrypting_key,
1104159d09a2SMark Phalan 	      void **padata_context)
11057c478bd9Sstevel@tonic-gate {
11067c478bd9Sstevel@tonic-gate     krb5_error_code		retval;
11077c478bd9Sstevel@tonic-gate     krb5_pa_data **		padata;
11087c478bd9Sstevel@tonic-gate     krb5_pa_data **		send_pa_list;
11097c478bd9Sstevel@tonic-gate     krb5_pa_data **		send_pa;
11107c478bd9Sstevel@tonic-gate     krb5_pa_data *		pa = 0;
11117c478bd9Sstevel@tonic-gate     krb5_preauth_systems *	ap;
1112159d09a2SMark Phalan     int *			pa_order;
1113159d09a2SMark Phalan     int *			pa_type;
11147c478bd9Sstevel@tonic-gate     int 			size = 0;
1115159d09a2SMark Phalan     void **			pa_context;
1116159d09a2SMark Phalan     krb5_boolean		key_modified;
1117159d09a2SMark Phalan     krb5_keyblock		original_key;
1118159d09a2SMark Phalan     if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) {
1119159d09a2SMark Phalan 	return KRB5KRB_ERR_GENERIC;
1120159d09a2SMark Phalan     }
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate     for (ap = preauth_systems; ap->type != -1; ap++) {
11237c478bd9Sstevel@tonic-gate 	if (ap->return_padata)
11247c478bd9Sstevel@tonic-gate 	    size++;
11257c478bd9Sstevel@tonic-gate     }
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate     if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
11287c478bd9Sstevel@tonic-gate 	return ENOMEM;
1129159d09a2SMark Phalan     if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
1130159d09a2SMark Phalan 	free(send_pa_list);
1131159d09a2SMark Phalan 	return ENOMEM;
1132159d09a2SMark Phalan     }
1133159d09a2SMark Phalan     sort_pa_order(context, request, pa_order);
1134159d09a2SMark Phalan 
1135159d09a2SMark Phalan     retval = krb5_copy_keyblock_contents(context, encrypting_key,
1136159d09a2SMark Phalan 					 &original_key);
1137159d09a2SMark Phalan     if (retval) {
1138159d09a2SMark Phalan 	free(send_pa_list);
1139159d09a2SMark Phalan 	free(pa_order);
1140159d09a2SMark Phalan 	return retval;
1141159d09a2SMark Phalan     }
1142159d09a2SMark Phalan     key_modified = FALSE;
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate     send_pa = send_pa_list;
11457c478bd9Sstevel@tonic-gate     *send_pa = 0;
1146159d09a2SMark Phalan 
1147159d09a2SMark Phalan     for (pa_type = pa_order; *pa_type != -1; pa_type++) {
1148159d09a2SMark Phalan 	ap = &preauth_systems[*pa_type];
1149159d09a2SMark Phalan         if (!key_modified)
1150159d09a2SMark Phalan 	    if (original_key.enctype != encrypting_key->enctype)
1151159d09a2SMark Phalan                 key_modified = TRUE;
1152159d09a2SMark Phalan         if (!key_modified)
1153159d09a2SMark Phalan 	    if (original_key.length != encrypting_key->length)
1154159d09a2SMark Phalan                 key_modified = TRUE;
1155159d09a2SMark Phalan         if (!key_modified)
1156159d09a2SMark Phalan 	    if (memcmp(original_key.contents, encrypting_key->contents,
1157159d09a2SMark Phalan 		       original_key.length) != 0)
1158159d09a2SMark Phalan                 key_modified = TRUE;
1159159d09a2SMark Phalan 	if (key_modified && (ap->flags & PA_REPLACES_KEY))
1160159d09a2SMark Phalan 	    continue;
11617c478bd9Sstevel@tonic-gate 	if (ap->return_padata == 0)
11627c478bd9Sstevel@tonic-gate 	    continue;
1163159d09a2SMark Phalan 	if (find_pa_context(ap, *padata_context, &pa_context))
1164159d09a2SMark Phalan 	    continue;
11657c478bd9Sstevel@tonic-gate 	pa = 0;
11667c478bd9Sstevel@tonic-gate 	if (request->padata) {
11677c478bd9Sstevel@tonic-gate 	    for (padata = request->padata; *padata; padata++) {
11687c478bd9Sstevel@tonic-gate 		if ((*padata)->pa_type == ap->type) {
11697c478bd9Sstevel@tonic-gate 		    pa = *padata;
11707c478bd9Sstevel@tonic-gate 		    break;
11717c478bd9Sstevel@tonic-gate 		}
11727c478bd9Sstevel@tonic-gate 	    }
11737c478bd9Sstevel@tonic-gate 	}
1174159d09a2SMark Phalan 	if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
1175159d09a2SMark Phalan 					client_key, encrypting_key, send_pa,
1176159d09a2SMark Phalan 					get_entry_data, ap->plugin_context,
1177159d09a2SMark Phalan 					pa_context))) {
11787c478bd9Sstevel@tonic-gate 	    goto cleanup;
1179159d09a2SMark Phalan 	}
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	if (*send_pa)
11827c478bd9Sstevel@tonic-gate 	    send_pa++;
11837c478bd9Sstevel@tonic-gate 	*send_pa = 0;
11847c478bd9Sstevel@tonic-gate     }
1185*55fea89dSDan Cross 
11867c478bd9Sstevel@tonic-gate     retval = 0;
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate     if (send_pa_list[0]) {
11897c478bd9Sstevel@tonic-gate 	reply->padata = send_pa_list;
11907c478bd9Sstevel@tonic-gate 	send_pa_list = 0;
11917c478bd9Sstevel@tonic-gate     }
1192*55fea89dSDan Cross 
11937c478bd9Sstevel@tonic-gate cleanup:
11947c478bd9Sstevel@tonic-gate     if (send_pa_list)
11957c478bd9Sstevel@tonic-gate 	krb5_free_pa_data(context, send_pa_list);
1196159d09a2SMark Phalan 
1197159d09a2SMark Phalan     /* Solaris Kerberos */
1198159d09a2SMark Phalan     krb5_free_keyblock_contents(context, &original_key);
1199159d09a2SMark Phalan     free(pa_order);
1200159d09a2SMark Phalan 
12017c478bd9Sstevel@tonic-gate     return (retval);
12027c478bd9Sstevel@tonic-gate }
120356a424ccSmp 
12047c478bd9Sstevel@tonic-gate static krb5_boolean
enctype_requires_etype_info_2(krb5_enctype enctype)12057c478bd9Sstevel@tonic-gate enctype_requires_etype_info_2(krb5_enctype enctype)
12067c478bd9Sstevel@tonic-gate {
12077c478bd9Sstevel@tonic-gate     switch(enctype) {
12087c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_CRC:
12097c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD4:
12107c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD5:
12117c478bd9Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA1:
12127c478bd9Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_RAW:
12137c478bd9Sstevel@tonic-gate     case ENCTYPE_ARCFOUR_HMAC:
12147c478bd9Sstevel@tonic-gate     case ENCTYPE_ARCFOUR_HMAC_EXP :
12157c478bd9Sstevel@tonic-gate 	return 0;
12167c478bd9Sstevel@tonic-gate     default:
12177c478bd9Sstevel@tonic-gate 	if (krb5_c_valid_enctype(enctype))
121856a424ccSmp 	    return 1;
12197c478bd9Sstevel@tonic-gate 	else return 0;
12207c478bd9Sstevel@tonic-gate     }
12217c478bd9Sstevel@tonic-gate }
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate static krb5_boolean
request_contains_enctype(krb5_context context,const krb5_kdc_req * request,krb5_enctype enctype)12247c478bd9Sstevel@tonic-gate request_contains_enctype (krb5_context context,  const krb5_kdc_req *request,
12257c478bd9Sstevel@tonic-gate 			  krb5_enctype enctype)
12267c478bd9Sstevel@tonic-gate {
12277c478bd9Sstevel@tonic-gate     int i;
12287c478bd9Sstevel@tonic-gate     for (i =0; i < request->nktypes; i++)
12297c478bd9Sstevel@tonic-gate 	if (request->ktype[i] == enctype)
12307c478bd9Sstevel@tonic-gate 	    return 1;
12317c478bd9Sstevel@tonic-gate     return 0;
12327c478bd9Sstevel@tonic-gate }
12337c478bd9Sstevel@tonic-gate 
123456a424ccSmp 
12357c478bd9Sstevel@tonic-gate static krb5_error_code
verify_enc_timestamp(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * pa,preauth_get_entry_data_proc ets_get_entry_data,void * pa_system_context,void ** pa_request_context,krb5_data ** e_data,krb5_authdata *** authz_data)123656a424ccSmp verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
1237159d09a2SMark Phalan 		     krb5_data *req_pkt,
123856a424ccSmp 		     krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
1239159d09a2SMark Phalan 		     krb5_pa_data *pa,
1240159d09a2SMark Phalan 		     preauth_get_entry_data_proc ets_get_entry_data,
1241159d09a2SMark Phalan 		     void *pa_system_context,
1242159d09a2SMark Phalan 		     void **pa_request_context,
1243159d09a2SMark Phalan 		     krb5_data **e_data,
1244159d09a2SMark Phalan 		     krb5_authdata ***authz_data)
12457c478bd9Sstevel@tonic-gate {
12467c478bd9Sstevel@tonic-gate     krb5_pa_enc_ts *		pa_enc = 0;
12477c478bd9Sstevel@tonic-gate     krb5_error_code		retval;
12487c478bd9Sstevel@tonic-gate     krb5_data			scratch;
12497c478bd9Sstevel@tonic-gate     krb5_data			enc_ts_data;
12507c478bd9Sstevel@tonic-gate     krb5_enc_data 		*enc_data = 0;
12517c478bd9Sstevel@tonic-gate     krb5_keyblock		key;
12527c478bd9Sstevel@tonic-gate     krb5_key_data *		client_key;
12537c478bd9Sstevel@tonic-gate     krb5_int32			start;
12547c478bd9Sstevel@tonic-gate     krb5_timestamp		timenow;
1255159d09a2SMark Phalan     krb5_error_code		decrypt_err = 0;
125656a424ccSmp 
12577c478bd9Sstevel@tonic-gate     (void) memset(&key, 0, sizeof(krb5_keyblock));
12587c478bd9Sstevel@tonic-gate     scratch.data = (char *) pa->contents;
12597c478bd9Sstevel@tonic-gate     scratch.length = pa->length;
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate     enc_ts_data.data = 0;
1262*55fea89dSDan Cross 
12637c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
12647c478bd9Sstevel@tonic-gate 	goto cleanup;
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate     enc_ts_data.length = enc_data->ciphertext.length;
12677c478bd9Sstevel@tonic-gate     if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
12687c478bd9Sstevel@tonic-gate 	goto cleanup;
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate     start = 0;
12717c478bd9Sstevel@tonic-gate     decrypt_err = 0;
12727c478bd9Sstevel@tonic-gate     while (1) {
12737c478bd9Sstevel@tonic-gate 	if ((retval = krb5_dbe_search_enctype(context, client,
12747c478bd9Sstevel@tonic-gate 					      &start, enc_data->enctype,
12757c478bd9Sstevel@tonic-gate 					      -1, 0, &client_key)))
12767c478bd9Sstevel@tonic-gate 	    goto cleanup;
12777c478bd9Sstevel@tonic-gate 
1278*55fea89dSDan Cross 	if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
12797c478bd9Sstevel@tonic-gate 						  client_key, &key, NULL)))
12807c478bd9Sstevel@tonic-gate 	    goto cleanup;
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 	key.enctype = enc_data->enctype;
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 	retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
12857c478bd9Sstevel@tonic-gate 				0, enc_data, &enc_ts_data);
12867c478bd9Sstevel@tonic-gate 	krb5_free_keyblock_contents(context, &key);
12877c478bd9Sstevel@tonic-gate 	if (retval == 0)
12887c478bd9Sstevel@tonic-gate 	    break;
12897c478bd9Sstevel@tonic-gate 	else
12907c478bd9Sstevel@tonic-gate 	    decrypt_err = retval;
12917c478bd9Sstevel@tonic-gate     }
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
12947c478bd9Sstevel@tonic-gate 	goto cleanup;
12957c478bd9Sstevel@tonic-gate 
12967c478bd9Sstevel@tonic-gate     if ((retval = krb5_timeofday(context, &timenow)) != 0)
12977c478bd9Sstevel@tonic-gate 	goto cleanup;
1298*55fea89dSDan Cross 
12997c478bd9Sstevel@tonic-gate     if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
13007c478bd9Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_SKEW;
13017c478bd9Sstevel@tonic-gate 	goto cleanup;
13027c478bd9Sstevel@tonic-gate     }
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate     setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate     retval = 0;
1307*55fea89dSDan Cross 
13087c478bd9Sstevel@tonic-gate cleanup:
13097c478bd9Sstevel@tonic-gate     if (enc_data) {
13107c478bd9Sstevel@tonic-gate 	krb5_free_data_contents(context, &enc_data->ciphertext);
13117c478bd9Sstevel@tonic-gate 	free(enc_data);
13127c478bd9Sstevel@tonic-gate     }
13137c478bd9Sstevel@tonic-gate     krb5_free_data_contents(context, &enc_ts_data);
13147c478bd9Sstevel@tonic-gate     if (pa_enc)
13157c478bd9Sstevel@tonic-gate 	free(pa_enc);
13167c478bd9Sstevel@tonic-gate     /*
13177c478bd9Sstevel@tonic-gate      * If we get NO_MATCHING_KEY and decryption previously failed, and
13187c478bd9Sstevel@tonic-gate      * we failed to find any other keys of the correct enctype after
13197c478bd9Sstevel@tonic-gate      * that failed decryption, it probably means that the password was
13207c478bd9Sstevel@tonic-gate      * incorrect.
13217c478bd9Sstevel@tonic-gate      */
13227c478bd9Sstevel@tonic-gate     if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
13237c478bd9Sstevel@tonic-gate 	retval = decrypt_err;
13247c478bd9Sstevel@tonic-gate     return retval;
13257c478bd9Sstevel@tonic-gate }
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate static krb5_error_code
_make_etype_info_entry(krb5_context context,krb5_kdc_req * request,krb5_key_data * client_key,krb5_enctype etype,krb5_etype_info_entry ** entry,int etype_info2)13287c478bd9Sstevel@tonic-gate _make_etype_info_entry(krb5_context context,
132956a424ccSmp 		       krb5_kdc_req *request, krb5_key_data *client_key,
133056a424ccSmp 		       krb5_enctype etype, krb5_etype_info_entry **entry,
133156a424ccSmp 		       int etype_info2)
13327c478bd9Sstevel@tonic-gate {
13337c478bd9Sstevel@tonic-gate     krb5_data			salt;
1334*55fea89dSDan Cross     krb5_etype_info_entry *	tmp_entry;
13357c478bd9Sstevel@tonic-gate     krb5_error_code		retval;
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate     if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL)
13387c478bd9Sstevel@tonic-gate        return ENOMEM;
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate     salt.data = 0;
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate     tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY;
13437c478bd9Sstevel@tonic-gate     tmp_entry->etype = etype;
13447c478bd9Sstevel@tonic-gate     tmp_entry->length = KRB5_ETYPE_NO_SALT;
13457c478bd9Sstevel@tonic-gate     tmp_entry->salt = 0;
13467c478bd9Sstevel@tonic-gate     tmp_entry->s2kparams.data = NULL;
13477c478bd9Sstevel@tonic-gate     tmp_entry->s2kparams.length = 0;
13487c478bd9Sstevel@tonic-gate     retval = get_salt_from_key(context, request->client,
13497c478bd9Sstevel@tonic-gate 			       client_key, &salt);
13507c478bd9Sstevel@tonic-gate     if (retval)
13517c478bd9Sstevel@tonic-gate 	goto fail;
13527c478bd9Sstevel@tonic-gate     if (etype_info2 && client_key->key_data_ver > 1 &&
13537c478bd9Sstevel@tonic-gate 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) {
13547c478bd9Sstevel@tonic-gate 	switch (etype) {
13557c478bd9Sstevel@tonic-gate 	case ENCTYPE_DES_CBC_CRC:
13567c478bd9Sstevel@tonic-gate 	case ENCTYPE_DES_CBC_MD4:
13577c478bd9Sstevel@tonic-gate 	case ENCTYPE_DES_CBC_MD5:
135856a424ccSmp 	    tmp_entry->s2kparams.data = malloc(1);
135956a424ccSmp 	    if (tmp_entry->s2kparams.data == NULL) {
13607c478bd9Sstevel@tonic-gate 		retval = ENOMEM;
13617c478bd9Sstevel@tonic-gate 		goto fail;
136256a424ccSmp 	    }
136356a424ccSmp 	    tmp_entry->s2kparams.length = 1;
136456a424ccSmp 	    tmp_entry->s2kparams.data[0] = 1;
136556a424ccSmp 	    break;
13667c478bd9Sstevel@tonic-gate 	default:
136756a424ccSmp 	    break;
13687c478bd9Sstevel@tonic-gate 	}
13697c478bd9Sstevel@tonic-gate     }
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate     if (salt.length >= 0) {
13727c478bd9Sstevel@tonic-gate 	tmp_entry->length = salt.length;
13737c478bd9Sstevel@tonic-gate 	tmp_entry->salt = (unsigned char *) salt.data;
13747c478bd9Sstevel@tonic-gate 	salt.data = 0;
13757c478bd9Sstevel@tonic-gate     }
13767c478bd9Sstevel@tonic-gate     *entry = tmp_entry;
13777c478bd9Sstevel@tonic-gate     return 0;
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate fail:
13807c478bd9Sstevel@tonic-gate     if (tmp_entry) {
13817c478bd9Sstevel@tonic-gate 	if (tmp_entry->s2kparams.data)
138256a424ccSmp 	    free(tmp_entry->s2kparams.data);
13837c478bd9Sstevel@tonic-gate 	free(tmp_entry);
13847c478bd9Sstevel@tonic-gate     }
13857c478bd9Sstevel@tonic-gate     if (salt.data)
13867c478bd9Sstevel@tonic-gate 	free(salt.data);
13877c478bd9Sstevel@tonic-gate     return retval;
13887c478bd9Sstevel@tonic-gate }
13897c478bd9Sstevel@tonic-gate /*
13907c478bd9Sstevel@tonic-gate  * This function returns the etype information for a particular
13917c478bd9Sstevel@tonic-gate  * client, to be passed back in the preauth list in the KRB_ERROR
13927c478bd9Sstevel@tonic-gate  * message.  It supports generating both etype_info  and etype_info2
1393*55fea89dSDan Cross  *  as most of the work is the same.
13947c478bd9Sstevel@tonic-gate  */
13957c478bd9Sstevel@tonic-gate static krb5_error_code
etype_info_helper(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_pa_data * pa_data,int etype_info2)13967c478bd9Sstevel@tonic-gate etype_info_helper(krb5_context context, krb5_kdc_req *request,
139756a424ccSmp 	       krb5_db_entry *client, krb5_db_entry *server,
139856a424ccSmp 	       krb5_pa_data *pa_data, int etype_info2)
13997c478bd9Sstevel@tonic-gate {
14007c478bd9Sstevel@tonic-gate     krb5_etype_info_entry **	entry = 0;
14017c478bd9Sstevel@tonic-gate     krb5_key_data		*client_key;
14027c478bd9Sstevel@tonic-gate     krb5_error_code		retval;
14037c478bd9Sstevel@tonic-gate     krb5_data *			scratch;
14047c478bd9Sstevel@tonic-gate     krb5_enctype		db_etype;
140556a424ccSmp     int 			i = 0;
140656a424ccSmp     int 			start = 0;
14077c478bd9Sstevel@tonic-gate     int				seen_des = 0;
14087c478bd9Sstevel@tonic-gate 
140956a424ccSmp     entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
14107c478bd9Sstevel@tonic-gate     if (entry == NULL)
14117c478bd9Sstevel@tonic-gate 	return ENOMEM;
14127c478bd9Sstevel@tonic-gate     entry[0] = NULL;
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate     while (1) {
14157c478bd9Sstevel@tonic-gate 	retval = krb5_dbe_search_enctype(context, client, &start, -1,
141656a424ccSmp 					 -1, 0, &client_key);
14177c478bd9Sstevel@tonic-gate 	if (retval == KRB5_KDB_NO_MATCHING_KEY)
141856a424ccSmp 	    break;
14197c478bd9Sstevel@tonic-gate 	if (retval)
142056a424ccSmp 	    goto cleanup;
14217c478bd9Sstevel@tonic-gate 	db_etype = client_key->key_data_type[0];
14227c478bd9Sstevel@tonic-gate 	if (db_etype == ENCTYPE_DES_CBC_MD4)
142356a424ccSmp 	    db_etype = ENCTYPE_DES_CBC_MD5;
1424*55fea89dSDan Cross 
14257c478bd9Sstevel@tonic-gate 	if (request_contains_enctype(context, request, db_etype)) {
142656a424ccSmp 	    assert(etype_info2 ||
142756a424ccSmp 		   !enctype_requires_etype_info_2(db_etype));
142856a424ccSmp 	    if ((retval = _make_etype_info_entry(context, request, client_key,
142956a424ccSmp 			    db_etype, &entry[i], etype_info2)) != 0) {
14307c478bd9Sstevel@tonic-gate 		goto cleanup;
143156a424ccSmp 	    }
143256a424ccSmp 	    entry[i+1] = 0;
143356a424ccSmp 	    i++;
14347c478bd9Sstevel@tonic-gate 	}
14357c478bd9Sstevel@tonic-gate 
1436*55fea89dSDan Cross 	/*
143756a424ccSmp 	 * If there is a des key in the kdb, try the "similar" enctypes,
1438*55fea89dSDan Cross 	 * avoid duplicate entries.
14397c478bd9Sstevel@tonic-gate 	 */
14407c478bd9Sstevel@tonic-gate 	if (!seen_des) {
144156a424ccSmp 	    switch (db_etype) {
144256a424ccSmp 	    case ENCTYPE_DES_CBC_MD5:
14437c478bd9Sstevel@tonic-gate 		db_etype = ENCTYPE_DES_CBC_CRC;
14447c478bd9Sstevel@tonic-gate 		break;
144556a424ccSmp 	    case ENCTYPE_DES_CBC_CRC:
14467c478bd9Sstevel@tonic-gate 		db_etype = ENCTYPE_DES_CBC_MD5;
14477c478bd9Sstevel@tonic-gate 		break;
144856a424ccSmp 	    default:
14497c478bd9Sstevel@tonic-gate 		continue;
14507c478bd9Sstevel@tonic-gate 
145156a424ccSmp 	    }
145256a424ccSmp 	    if (request_contains_enctype(context, request, db_etype)) {
14537c478bd9Sstevel@tonic-gate 		if ((retval = _make_etype_info_entry(context, request,
145456a424ccSmp 				client_key, db_etype, &entry[i], etype_info2)) != 0) {
145556a424ccSmp 		    goto cleanup;
14567c478bd9Sstevel@tonic-gate 		}
145756a424ccSmp 		entry[i+1] = 0;
14587c478bd9Sstevel@tonic-gate 		i++;
145956a424ccSmp 	    }
146056a424ccSmp 	    seen_des++;
14617c478bd9Sstevel@tonic-gate 	}
14627c478bd9Sstevel@tonic-gate     }
14637c478bd9Sstevel@tonic-gate     if (etype_info2)
1464ba7b222eSGlenn Barry 	retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry,
146556a424ccSmp 				    &scratch);
1466ba7b222eSGlenn Barry     else 	retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry,
146756a424ccSmp 				    &scratch);
14687c478bd9Sstevel@tonic-gate     if (retval)
14697c478bd9Sstevel@tonic-gate 	goto cleanup;
14707c478bd9Sstevel@tonic-gate     pa_data->contents = (unsigned char *)scratch->data;
14717c478bd9Sstevel@tonic-gate     pa_data->length = scratch->length;
14727c478bd9Sstevel@tonic-gate     free(scratch);
14737c478bd9Sstevel@tonic-gate 
14747c478bd9Sstevel@tonic-gate     retval = 0;
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate cleanup:
14777c478bd9Sstevel@tonic-gate     if (entry)
14787c478bd9Sstevel@tonic-gate 	krb5_free_etype_info(context, entry);
14797c478bd9Sstevel@tonic-gate     return retval;
14807c478bd9Sstevel@tonic-gate }
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate static krb5_error_code
get_etype_info(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,krb5_pa_data * pa_data)14837c478bd9Sstevel@tonic-gate get_etype_info(krb5_context context, krb5_kdc_req *request,
148456a424ccSmp 	       krb5_db_entry *client, krb5_db_entry *server,
1485159d09a2SMark Phalan 	       preauth_get_entry_data_proc etype_get_entry_data,
1486159d09a2SMark Phalan 	       void *pa_system_context,
148756a424ccSmp 	       krb5_pa_data *pa_data)
14887c478bd9Sstevel@tonic-gate {
14897c478bd9Sstevel@tonic-gate   int i;
14907c478bd9Sstevel@tonic-gate     for (i=0;  i < request->nktypes; i++) {
1491*55fea89dSDan Cross 	if (enctype_requires_etype_info_2(request->ktype[i]))
149256a424ccSmp 	    return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
149356a424ccSmp 							* skip this
149456a424ccSmp 							* type*/
14957c478bd9Sstevel@tonic-gate     }
14967c478bd9Sstevel@tonic-gate     return etype_info_helper(context, request, client, server, pa_data, 0);
14977c478bd9Sstevel@tonic-gate }
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate static krb5_error_code
get_etype_info2(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,krb5_pa_data * pa_data)15007c478bd9Sstevel@tonic-gate get_etype_info2(krb5_context context, krb5_kdc_req *request,
150156a424ccSmp 	       krb5_db_entry *client, krb5_db_entry *server,
1502159d09a2SMark Phalan 	       preauth_get_entry_data_proc etype_get_entry_data,
1503159d09a2SMark Phalan 	       void *pa_system_context,
150456a424ccSmp 	       krb5_pa_data *pa_data)
15057c478bd9Sstevel@tonic-gate {
15067c478bd9Sstevel@tonic-gate     return etype_info_helper( context, request, client, server, pa_data, 1);
15077c478bd9Sstevel@tonic-gate }
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate static krb5_error_code
etype_info_as_rep_helper(krb5_context context,krb5_pa_data * padata,krb5_db_entry * client,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,int etype_info2)1510*55fea89dSDan Cross etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
1511159d09a2SMark Phalan 			 krb5_db_entry *client,
1512159d09a2SMark Phalan 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
1513159d09a2SMark Phalan 			 krb5_key_data *client_key,
1514159d09a2SMark Phalan 			 krb5_keyblock *encrypting_key,
1515159d09a2SMark Phalan 			 krb5_pa_data **send_pa,
1516159d09a2SMark Phalan 			 int etype_info2)
15177c478bd9Sstevel@tonic-gate {
1518159d09a2SMark Phalan     int i;
15197c478bd9Sstevel@tonic-gate     krb5_error_code retval;
15207c478bd9Sstevel@tonic-gate     krb5_pa_data *tmp_padata;
15217c478bd9Sstevel@tonic-gate     krb5_etype_info_entry **entry = NULL;
15227c478bd9Sstevel@tonic-gate     krb5_data *scratch = NULL;
1523159d09a2SMark Phalan 
1524159d09a2SMark Phalan     /*
1525159d09a2SMark Phalan      * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
1526159d09a2SMark Phalan      * enctypes.
1527159d09a2SMark Phalan      */
1528159d09a2SMark Phalan     if (!etype_info2) {
1529159d09a2SMark Phalan 	for (i = 0; i < request->nktypes; i++) {
1530159d09a2SMark Phalan 	    if (enctype_requires_etype_info_2(request->ktype[i])) {
1531159d09a2SMark Phalan 		*send_pa = NULL;
1532159d09a2SMark Phalan 		return 0;
1533159d09a2SMark Phalan 	    }
1534159d09a2SMark Phalan 	}
1535159d09a2SMark Phalan     }
1536159d09a2SMark Phalan 
15377c478bd9Sstevel@tonic-gate     tmp_padata = malloc( sizeof(krb5_pa_data));
15387c478bd9Sstevel@tonic-gate     if (tmp_padata == NULL)
15397c478bd9Sstevel@tonic-gate 	return ENOMEM;
1540159d09a2SMark Phalan     if (etype_info2)
1541159d09a2SMark Phalan 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
1542159d09a2SMark Phalan     else
1543159d09a2SMark Phalan 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO;
1544159d09a2SMark Phalan 
15457c478bd9Sstevel@tonic-gate     entry = malloc(2 * sizeof(krb5_etype_info_entry *));
15467c478bd9Sstevel@tonic-gate     if (entry == NULL) {
15477c478bd9Sstevel@tonic-gate 	retval = ENOMEM;
15487c478bd9Sstevel@tonic-gate 	goto cleanup;
15497c478bd9Sstevel@tonic-gate     }
15507c478bd9Sstevel@tonic-gate     entry[0] = NULL;
15517c478bd9Sstevel@tonic-gate     entry[1] = NULL;
1552159d09a2SMark Phalan     retval = _make_etype_info_entry(context, request,
1553159d09a2SMark Phalan 				    client_key, encrypting_key->enctype,
1554159d09a2SMark Phalan 				    entry, etype_info2);
15557c478bd9Sstevel@tonic-gate     if (retval)
15567c478bd9Sstevel@tonic-gate 	goto cleanup;
1557159d09a2SMark Phalan 
1558159d09a2SMark Phalan     if (etype_info2)
1559ba7b222eSGlenn Barry 	retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry, &scratch);
1560159d09a2SMark Phalan     else
1561ba7b222eSGlenn Barry 	retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry, &scratch);
1562159d09a2SMark Phalan 
15637c478bd9Sstevel@tonic-gate     if (retval)
15647c478bd9Sstevel@tonic-gate 	goto cleanup;
15657c478bd9Sstevel@tonic-gate     tmp_padata->contents = (uchar_t *)scratch->data;
15667c478bd9Sstevel@tonic-gate     tmp_padata->length = scratch->length;
15677c478bd9Sstevel@tonic-gate     *send_pa = tmp_padata;
15687c478bd9Sstevel@tonic-gate 
1569*55fea89dSDan Cross     /* For cleanup - we no longer own the contents of the krb5_data
15707c478bd9Sstevel@tonic-gate      * only to pointer to the krb5_data
15717c478bd9Sstevel@tonic-gate      */
157256a424ccSmp     scratch->data = 0;
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate  cleanup:
15757c478bd9Sstevel@tonic-gate     if (entry)
15767c478bd9Sstevel@tonic-gate 	krb5_free_etype_info(context, entry);
15777c478bd9Sstevel@tonic-gate     if (retval) {
15787c478bd9Sstevel@tonic-gate 	if (tmp_padata)
157956a424ccSmp 	    free(tmp_padata);
15807c478bd9Sstevel@tonic-gate     }
15817c478bd9Sstevel@tonic-gate     if (scratch)
158256a424ccSmp 	    krb5_free_data(context, scratch);
15837c478bd9Sstevel@tonic-gate     return retval;
15847c478bd9Sstevel@tonic-gate }
15857c478bd9Sstevel@tonic-gate 
1586159d09a2SMark Phalan static krb5_error_code
return_etype_info2(krb5_context context,krb5_pa_data * padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,void ** pa_request_context)1587*55fea89dSDan Cross return_etype_info2(krb5_context context, krb5_pa_data * padata,
1588159d09a2SMark Phalan 		   krb5_db_entry *client,
1589159d09a2SMark Phalan 		   krb5_data *req_pkt,
1590159d09a2SMark Phalan 		   krb5_kdc_req *request, krb5_kdc_rep *reply,
1591159d09a2SMark Phalan 		   krb5_key_data *client_key,
1592159d09a2SMark Phalan 		   krb5_keyblock *encrypting_key,
1593159d09a2SMark Phalan 		   krb5_pa_data **send_pa,
1594159d09a2SMark Phalan 		   preauth_get_entry_data_proc etype_get_entry_data,
1595159d09a2SMark Phalan 	           void *pa_system_context,
1596159d09a2SMark Phalan 		   void **pa_request_context)
1597159d09a2SMark Phalan {
1598159d09a2SMark Phalan     return etype_info_as_rep_helper(context, padata, client, request, reply,
1599159d09a2SMark Phalan 				    client_key, encrypting_key, send_pa, 1);
1600159d09a2SMark Phalan }
1601159d09a2SMark Phalan 
1602159d09a2SMark Phalan 
1603159d09a2SMark Phalan static krb5_error_code
return_etype_info(krb5_context context,krb5_pa_data * padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc etypeget_entry_data,void * pa_system_context,void ** pa_request_context)1604*55fea89dSDan Cross return_etype_info(krb5_context context, krb5_pa_data * padata,
1605159d09a2SMark Phalan 		  krb5_db_entry *client,
1606159d09a2SMark Phalan 		  krb5_data *req_pkt,
1607159d09a2SMark Phalan 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
1608159d09a2SMark Phalan 		  krb5_key_data *client_key,
1609159d09a2SMark Phalan 		  krb5_keyblock *encrypting_key,
1610159d09a2SMark Phalan 		  krb5_pa_data **send_pa,
1611159d09a2SMark Phalan 		  preauth_get_entry_data_proc etypeget_entry_data,
1612159d09a2SMark Phalan 	          void *pa_system_context,
1613159d09a2SMark Phalan 		  void **pa_request_context)
1614159d09a2SMark Phalan {
1615159d09a2SMark Phalan     return etype_info_as_rep_helper(context, padata, client, request, reply,
1616159d09a2SMark Phalan 				    client_key, encrypting_key, send_pa, 0);
1617159d09a2SMark Phalan }
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate static krb5_error_code
return_pw_salt(krb5_context context,krb5_pa_data * in_padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,void ** pa_request_context)162056a424ccSmp return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
1621159d09a2SMark Phalan 	       krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
162256a424ccSmp 	       krb5_kdc_rep *reply, krb5_key_data *client_key,
1623159d09a2SMark Phalan 	       krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1624159d09a2SMark Phalan 	       preauth_get_entry_data_proc etype_get_entry_data,
1625159d09a2SMark Phalan 	       void *pa_system_context,
1626159d09a2SMark Phalan 	       void **pa_request_context)
16277c478bd9Sstevel@tonic-gate {
16287c478bd9Sstevel@tonic-gate     krb5_error_code	retval;
16297c478bd9Sstevel@tonic-gate     krb5_pa_data *	padata;
16307c478bd9Sstevel@tonic-gate     krb5_data *		scratch;
16317c478bd9Sstevel@tonic-gate     krb5_data		salt_data;
16327c478bd9Sstevel@tonic-gate     int i;
1633*55fea89dSDan Cross 
16347c478bd9Sstevel@tonic-gate     for (i = 0; i < request->nktypes; i++) {
16357c478bd9Sstevel@tonic-gate 	if (enctype_requires_etype_info_2(request->ktype[i]))
163656a424ccSmp 	    return 0;
16377c478bd9Sstevel@tonic-gate     }
16387c478bd9Sstevel@tonic-gate     if (client_key->key_data_ver == 1 ||
16397c478bd9Sstevel@tonic-gate 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
16407c478bd9Sstevel@tonic-gate 	return 0;
16417c478bd9Sstevel@tonic-gate 
16427c478bd9Sstevel@tonic-gate     if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
16437c478bd9Sstevel@tonic-gate 	return ENOMEM;
16447c478bd9Sstevel@tonic-gate     padata->magic = KV5M_PA_DATA;
16457c478bd9Sstevel@tonic-gate     padata->pa_type = KRB5_PADATA_PW_SALT;
1646*55fea89dSDan Cross 
16477c478bd9Sstevel@tonic-gate     switch (client_key->key_data_type[1]) {
16487c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_V4:
16497c478bd9Sstevel@tonic-gate 	/* send an empty (V4) salt */
16507c478bd9Sstevel@tonic-gate 	padata->contents = 0;
16517c478bd9Sstevel@tonic-gate 	padata->length = 0;
16527c478bd9Sstevel@tonic-gate 	break;
16537c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_NOREALM:
1654*55fea89dSDan Cross 	if ((retval = krb5_principal2salt_norealm(kdc_context,
16557c478bd9Sstevel@tonic-gate 						   request->client,
16567c478bd9Sstevel@tonic-gate 						   &salt_data)))
16577c478bd9Sstevel@tonic-gate 	    goto cleanup;
16587c478bd9Sstevel@tonic-gate 	padata->contents = (krb5_octet *)salt_data.data;
16597c478bd9Sstevel@tonic-gate 	padata->length = salt_data.length;
16607c478bd9Sstevel@tonic-gate 	break;
16617c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_AFS3:
16627c478bd9Sstevel@tonic-gate 	/* send an AFS style realm-based salt */
16637c478bd9Sstevel@tonic-gate 	/* for now, just pass the realm back and let the client
16647c478bd9Sstevel@tonic-gate 	   do the work. In the future, add a kdc configuration
16657c478bd9Sstevel@tonic-gate 	   variable that specifies the old cell name. */
16667c478bd9Sstevel@tonic-gate 	padata->pa_type = KRB5_PADATA_AFS3_SALT;
16677c478bd9Sstevel@tonic-gate 	/* it would be just like ONLYREALM, but we need to pass the 0 */
16687c478bd9Sstevel@tonic-gate 	scratch = krb5_princ_realm(kdc_context, request->client);
16697c478bd9Sstevel@tonic-gate 	if ((padata->contents = malloc(scratch->length+1)) == NULL) {
16707c478bd9Sstevel@tonic-gate 	    retval = ENOMEM;
16717c478bd9Sstevel@tonic-gate 	    goto cleanup;
16727c478bd9Sstevel@tonic-gate 	}
16737c478bd9Sstevel@tonic-gate 	memcpy(padata->contents, scratch->data, scratch->length);
16747c478bd9Sstevel@tonic-gate 	padata->length = scratch->length+1;
16757c478bd9Sstevel@tonic-gate 	padata->contents[scratch->length] = 0;
16767c478bd9Sstevel@tonic-gate 	break;
16777c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_ONLYREALM:
16787c478bd9Sstevel@tonic-gate 	scratch = krb5_princ_realm(kdc_context, request->client);
16797c478bd9Sstevel@tonic-gate 	if ((padata->contents = malloc(scratch->length)) == NULL) {
16807c478bd9Sstevel@tonic-gate 	    retval = ENOMEM;
16817c478bd9Sstevel@tonic-gate 	    goto cleanup;
16827c478bd9Sstevel@tonic-gate 	}
16837c478bd9Sstevel@tonic-gate 	memcpy(padata->contents, scratch->data, scratch->length);
16847c478bd9Sstevel@tonic-gate 	padata->length = scratch->length;
16857c478bd9Sstevel@tonic-gate 	break;
16867c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_SPECIAL:
16877c478bd9Sstevel@tonic-gate 	if ((padata->contents = malloc(client_key->key_data_length[1]))
16887c478bd9Sstevel@tonic-gate 	    == NULL) {
16897c478bd9Sstevel@tonic-gate 	    retval = ENOMEM;
16907c478bd9Sstevel@tonic-gate 	    goto cleanup;
16917c478bd9Sstevel@tonic-gate 	}
16927c478bd9Sstevel@tonic-gate 	memcpy(padata->contents, client_key->key_data_contents[1],
16937c478bd9Sstevel@tonic-gate 	       client_key->key_data_length[1]);
16947c478bd9Sstevel@tonic-gate 	padata->length = client_key->key_data_length[1];
16957c478bd9Sstevel@tonic-gate 	break;
16967c478bd9Sstevel@tonic-gate     default:
16977c478bd9Sstevel@tonic-gate 	free(padata);
16987c478bd9Sstevel@tonic-gate 	return 0;
16997c478bd9Sstevel@tonic-gate     }
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate     *send_pa = padata;
17027c478bd9Sstevel@tonic-gate     return 0;
1703*55fea89dSDan Cross 
17047c478bd9Sstevel@tonic-gate cleanup:
17057c478bd9Sstevel@tonic-gate     free(padata);
17067c478bd9Sstevel@tonic-gate     return retval;
17077c478bd9Sstevel@tonic-gate }
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate static krb5_error_code
return_sam_data(krb5_context context,krb5_pa_data * in_padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc sam_get_entry_data,void * pa_system_context,void ** pa_request_context)171056a424ccSmp return_sam_data(krb5_context context, krb5_pa_data *in_padata,
1711159d09a2SMark Phalan 		krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
171256a424ccSmp 		krb5_kdc_rep *reply, krb5_key_data *client_key,
1713159d09a2SMark Phalan 		krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1714159d09a2SMark Phalan 		preauth_get_entry_data_proc sam_get_entry_data,
1715159d09a2SMark Phalan 		void *pa_system_context,
1716159d09a2SMark Phalan 		void **pa_request_context)
17177c478bd9Sstevel@tonic-gate {
17187c478bd9Sstevel@tonic-gate     krb5_error_code	retval;
17197c478bd9Sstevel@tonic-gate     krb5_data		scratch;
17207c478bd9Sstevel@tonic-gate     int			i;
17217c478bd9Sstevel@tonic-gate 
17227c478bd9Sstevel@tonic-gate     krb5_sam_response		*sr = 0;
17237c478bd9Sstevel@tonic-gate     krb5_predicted_sam_response	*psr = 0;
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate     if (in_padata == 0)
17267c478bd9Sstevel@tonic-gate 	return 0;
17277c478bd9Sstevel@tonic-gate 
17287c478bd9Sstevel@tonic-gate     /*
17297c478bd9Sstevel@tonic-gate      * We start by doing the same thing verify_sam_response() does:
17307c478bd9Sstevel@tonic-gate      * extract the psr from the padata (which is an sr). Nothing
17317c478bd9Sstevel@tonic-gate      * here should generate errors! We've already successfully done
17327c478bd9Sstevel@tonic-gate      * all this once.
17337c478bd9Sstevel@tonic-gate      */
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate     scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
17367c478bd9Sstevel@tonic-gate     scratch.length = in_padata->length;
1737*55fea89dSDan Cross 
17387c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
17397c478bd9Sstevel@tonic-gate 	com_err("krb5kdc", retval,
17407c478bd9Sstevel@tonic-gate 		gettext("return_sam_data(): decode_krb5_sam_response failed"));
17417c478bd9Sstevel@tonic-gate 	goto cleanup;
17427c478bd9Sstevel@tonic-gate     }
17437c478bd9Sstevel@tonic-gate 
17447c478bd9Sstevel@tonic-gate     {
17457c478bd9Sstevel@tonic-gate 	krb5_enc_data tmpdata;
17467c478bd9Sstevel@tonic-gate 
17477c478bd9Sstevel@tonic-gate 	tmpdata.enctype = ENCTYPE_UNKNOWN;
17487c478bd9Sstevel@tonic-gate 	tmpdata.ciphertext = sr->sam_track_id;
17497c478bd9Sstevel@tonic-gate 
17507c478bd9Sstevel@tonic-gate 	scratch.length = tmpdata.ciphertext.length;
17517c478bd9Sstevel@tonic-gate 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
17527c478bd9Sstevel@tonic-gate 	    retval = ENOMEM;
17537c478bd9Sstevel@tonic-gate 	    goto cleanup;
17547c478bd9Sstevel@tonic-gate 	}
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate 	if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
17577c478bd9Sstevel@tonic-gate 				     &tmpdata, &scratch))) {
17587c478bd9Sstevel@tonic-gate 	    com_err("krb5kdc", retval,
17597c478bd9Sstevel@tonic-gate 		    gettext("return_sam_data(): decrypt track_id failed"));
17607c478bd9Sstevel@tonic-gate 	    free(scratch.data);
17617c478bd9Sstevel@tonic-gate 	    goto cleanup;
17627c478bd9Sstevel@tonic-gate 	}
17637c478bd9Sstevel@tonic-gate     }
17647c478bd9Sstevel@tonic-gate 
17657c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
17667c478bd9Sstevel@tonic-gate 	com_err("krb5kdc", retval,
17677c478bd9Sstevel@tonic-gate 		gettext(
17687c478bd9Sstevel@tonic-gate 		"return_sam_data(): decode_krb5_predicted_sam_response failed"));
17697c478bd9Sstevel@tonic-gate 	free(scratch.data);
17707c478bd9Sstevel@tonic-gate 	goto cleanup;
17717c478bd9Sstevel@tonic-gate     }
17727c478bd9Sstevel@tonic-gate 
17737c478bd9Sstevel@tonic-gate     /* We could use sr->sam_flags, but it may be absent or altered. */
17747c478bd9Sstevel@tonic-gate     if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
17757c478bd9Sstevel@tonic-gate 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
17767c478bd9Sstevel@tonic-gate 		gettext("Unsupported SAM flag must-pk-encrypt-sad"));
17777c478bd9Sstevel@tonic-gate 	goto cleanup;
17787c478bd9Sstevel@tonic-gate     }
17797c478bd9Sstevel@tonic-gate     if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
17807c478bd9Sstevel@tonic-gate 	/* No key munging */
17817c478bd9Sstevel@tonic-gate 	goto cleanup;
17827c478bd9Sstevel@tonic-gate     }
17837c478bd9Sstevel@tonic-gate     if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
17847c478bd9Sstevel@tonic-gate 	/* Use sam_key instead of client key */
17857c478bd9Sstevel@tonic-gate 	krb5_free_keyblock_contents(context, encrypting_key);
17867c478bd9Sstevel@tonic-gate 	krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
17877c478bd9Sstevel@tonic-gate 	/* XXX Attach a useful pa_data */
17887c478bd9Sstevel@tonic-gate 	goto cleanup;
17897c478bd9Sstevel@tonic-gate     }
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate     /* Otherwise (no flags set), we XOR the keys */
17927c478bd9Sstevel@tonic-gate     /* XXX The passwords-04 draft is underspecified here wrt different
17937c478bd9Sstevel@tonic-gate 	   key types. We will do what I hope to get into the -05 draft. */
17947c478bd9Sstevel@tonic-gate     {
17957c478bd9Sstevel@tonic-gate 	krb5_octet *p = encrypting_key->contents;
17967c478bd9Sstevel@tonic-gate 	krb5_octet *q = psr->sam_key.contents;
17977c478bd9Sstevel@tonic-gate 	int length = ((encrypting_key->length < psr->sam_key.length)
17987c478bd9Sstevel@tonic-gate 		      ? encrypting_key->length
17997c478bd9Sstevel@tonic-gate 		      : psr->sam_key.length);
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 	for (i = 0; i < length; i++)
18027c478bd9Sstevel@tonic-gate 	    p[i] ^= q[i];
18037c478bd9Sstevel@tonic-gate     }
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate     /* Post-mixing key correction */
18067c478bd9Sstevel@tonic-gate     switch (encrypting_key->enctype) {
18077c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_CRC:
18087c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD4:
18097c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD5:
18107c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_RAW:
18117c478bd9Sstevel@tonic-gate 	mit_des_fixup_key_parity(encrypting_key->contents);
18127c478bd9Sstevel@tonic-gate 	if (mit_des_is_weak_key(encrypting_key->contents))
18137c478bd9Sstevel@tonic-gate 	    ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
18147c478bd9Sstevel@tonic-gate 	break;
18157c478bd9Sstevel@tonic-gate 
18167c478bd9Sstevel@tonic-gate     /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
18177c478bd9Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
18187c478bd9Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_RAW:
18197c478bd9Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA1:
18207c478bd9Sstevel@tonic-gate 	for (i = 0; i < 3; i++) {
18217c478bd9Sstevel@tonic-gate 	    mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
18227c478bd9Sstevel@tonic-gate 	    if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
18237c478bd9Sstevel@tonic-gate 		((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
18247c478bd9Sstevel@tonic-gate 	}
18257c478bd9Sstevel@tonic-gate 	break;
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate     default:
18287c478bd9Sstevel@tonic-gate 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
18297c478bd9Sstevel@tonic-gate 		gettext("Unimplemented keytype for SAM key mixing"));
18307c478bd9Sstevel@tonic-gate 	goto cleanup;
18317c478bd9Sstevel@tonic-gate     }
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate     /* XXX Attach a useful pa_data */
18347c478bd9Sstevel@tonic-gate cleanup:
18357c478bd9Sstevel@tonic-gate     if (sr)
18367c478bd9Sstevel@tonic-gate 	krb5_free_sam_response(context, sr);
18377c478bd9Sstevel@tonic-gate     if (psr)
18387c478bd9Sstevel@tonic-gate 	krb5_free_predicted_sam_response(context, psr);
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate     return retval;
18417c478bd9Sstevel@tonic-gate }
1842*55fea89dSDan Cross 
18437c478bd9Sstevel@tonic-gate static struct {
18447c478bd9Sstevel@tonic-gate   char* name;
18457c478bd9Sstevel@tonic-gate   int   sam_type;
18467c478bd9Sstevel@tonic-gate } *sam_ptr, sam_inst_map[] = {
18477c478bd9Sstevel@tonic-gate #if 0	/* SUNWresync121 - unsupported hardware and kdc.log annoyance */
18487c478bd9Sstevel@tonic-gate   { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
18497c478bd9Sstevel@tonic-gate   { "SECURID", PA_SAM_TYPE_SECURID, },
18507c478bd9Sstevel@tonic-gate   { "GRAIL", PA_SAM_TYPE_GRAIL, },
18517c478bd9Sstevel@tonic-gate #endif
18527c478bd9Sstevel@tonic-gate   { 0, 0 },
18537c478bd9Sstevel@tonic-gate };
18547c478bd9Sstevel@tonic-gate 
18557c478bd9Sstevel@tonic-gate static krb5_error_code
get_sam_edata(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,preauth_get_entry_data_proc sam_get_entry_data,void * pa_system_context,krb5_pa_data * pa_data)185656a424ccSmp get_sam_edata(krb5_context context, krb5_kdc_req *request,
185756a424ccSmp 	      krb5_db_entry *client, krb5_db_entry *server,
1858159d09a2SMark Phalan 	      preauth_get_entry_data_proc sam_get_entry_data,
1859159d09a2SMark Phalan 	      void *pa_system_context, krb5_pa_data *pa_data)
18607c478bd9Sstevel@tonic-gate {
18617c478bd9Sstevel@tonic-gate     krb5_error_code		retval;
18627c478bd9Sstevel@tonic-gate     krb5_sam_challenge		sc;
18637c478bd9Sstevel@tonic-gate     krb5_predicted_sam_response	psr;
18647c478bd9Sstevel@tonic-gate     krb5_data *			scratch;
18657c478bd9Sstevel@tonic-gate     krb5_keyblock encrypting_key;
18667c478bd9Sstevel@tonic-gate     char response[9];
18677c478bd9Sstevel@tonic-gate     char inputblock[8];
18687c478bd9Sstevel@tonic-gate     krb5_data predict_response;
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate     (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
18717c478bd9Sstevel@tonic-gate     (void) memset(&sc, 0, sizeof(sc));
18727c478bd9Sstevel@tonic-gate     (void) memset(&psr, 0, sizeof(psr));
18737c478bd9Sstevel@tonic-gate 
18747c478bd9Sstevel@tonic-gate     /* Given the client name we can figure out what type of preauth
18757c478bd9Sstevel@tonic-gate        they need. The spec is currently for querying the database for
18767c478bd9Sstevel@tonic-gate        names that match the types of preauth used. Later we should
18777c478bd9Sstevel@tonic-gate        make this mapping show up in kdc.conf. In the meantime, we
18787c478bd9Sstevel@tonic-gate        hardcode the following:
18797c478bd9Sstevel@tonic-gate 		/SNK4 -- Digital Pathways SNK/4 preauth.
18807c478bd9Sstevel@tonic-gate 		/GRAIL -- experimental preauth
18817c478bd9Sstevel@tonic-gate        The first one found is used. See sam_inst_map above.
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate        For SNK4 in particular, the key in the database is the key for
18847c478bd9Sstevel@tonic-gate        the device; kadmin needs a special interface for it.
18857c478bd9Sstevel@tonic-gate      */
18867c478bd9Sstevel@tonic-gate 
18877c478bd9Sstevel@tonic-gate     {
188856a424ccSmp       int npr = 1;
188956a424ccSmp       krb5_boolean more;
18907c478bd9Sstevel@tonic-gate       krb5_db_entry assoc;
18917c478bd9Sstevel@tonic-gate       krb5_key_data  *assoc_key;
18927c478bd9Sstevel@tonic-gate       krb5_principal newp;
18937c478bd9Sstevel@tonic-gate       int probeslot;
18947c478bd9Sstevel@tonic-gate 
18957c478bd9Sstevel@tonic-gate       sc.sam_type = 0;
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate       retval = krb5_copy_principal(kdc_context, request->client, &newp);
18987c478bd9Sstevel@tonic-gate       if (retval) {
18997c478bd9Sstevel@tonic-gate 	com_err(gettext("krb5kdc"),
19007c478bd9Sstevel@tonic-gate 		retval,
19017c478bd9Sstevel@tonic-gate 		gettext("copying client name for preauth probe"));
19027c478bd9Sstevel@tonic-gate 	return retval;
19037c478bd9Sstevel@tonic-gate       }
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate       probeslot = krb5_princ_size(context, newp)++;
1906*55fea89dSDan Cross       krb5_princ_name(kdc_context, newp) =
19077c478bd9Sstevel@tonic-gate 	realloc(krb5_princ_name(kdc_context, newp),
19087c478bd9Sstevel@tonic-gate 		krb5_princ_size(context, newp) * sizeof(krb5_data));
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate       for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
19117c478bd9Sstevel@tonic-gate 	krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
1912*55fea89dSDan Cross 	krb5_princ_component(kdc_context,newp,probeslot)->length =
19137c478bd9Sstevel@tonic-gate 	  strlen(sam_ptr->name);
19147c478bd9Sstevel@tonic-gate 	npr = 1;
19157c478bd9Sstevel@tonic-gate 	retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
191656a424ccSmp 	if(!retval && npr) {
19177c478bd9Sstevel@tonic-gate 	  sc.sam_type = sam_ptr->sam_type;
19187c478bd9Sstevel@tonic-gate 	  break;
19197c478bd9Sstevel@tonic-gate 	}
19207c478bd9Sstevel@tonic-gate       }
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate       krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
19237c478bd9Sstevel@tonic-gate       krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
19247c478bd9Sstevel@tonic-gate       krb5_princ_size(context, newp)--;
19257c478bd9Sstevel@tonic-gate 
19267c478bd9Sstevel@tonic-gate       krb5_free_principal(kdc_context, newp);
19277c478bd9Sstevel@tonic-gate 
19287c478bd9Sstevel@tonic-gate       /* if sc.sam_type is set, it worked */
19297c478bd9Sstevel@tonic-gate       if (sc.sam_type) {
19307c478bd9Sstevel@tonic-gate 	/* so use assoc to get the key out! */
19317c478bd9Sstevel@tonic-gate 	{
19327c478bd9Sstevel@tonic-gate 	  /* here's what do_tgs_req does */
19337c478bd9Sstevel@tonic-gate 	  retval = krb5_dbe_find_enctype(kdc_context, &assoc,
19347c478bd9Sstevel@tonic-gate 					 ENCTYPE_DES_CBC_RAW,
19357c478bd9Sstevel@tonic-gate 					 KRB5_KDB_SALTTYPE_NORMAL,
19367c478bd9Sstevel@tonic-gate 					 0,		/* Get highest kvno */
19377c478bd9Sstevel@tonic-gate 					 &assoc_key);
19387c478bd9Sstevel@tonic-gate 	  if (retval) {
19397c478bd9Sstevel@tonic-gate 	    char *sname;
19407c478bd9Sstevel@tonic-gate 	    krb5_unparse_name(kdc_context, request->client, &sname);
1941*55fea89dSDan Cross 	    com_err(gettext("krb5kdc"),
1942*55fea89dSDan Cross 		    retval,
19437c478bd9Sstevel@tonic-gate 		    gettext("snk4 finding the enctype and key <%s>"),
19447c478bd9Sstevel@tonic-gate 		    sname);
19457c478bd9Sstevel@tonic-gate 	    free(sname);
19467c478bd9Sstevel@tonic-gate 	    return retval;
19477c478bd9Sstevel@tonic-gate 	  }
19487c478bd9Sstevel@tonic-gate 	  /* convert server.key into a real key */
19497c478bd9Sstevel@tonic-gate 	  retval = krb5_dbekd_decrypt_key_data(kdc_context,
1950*55fea89dSDan Cross 					       &master_keyblock,
19517c478bd9Sstevel@tonic-gate 					       assoc_key, &encrypting_key,
19527c478bd9Sstevel@tonic-gate 					       NULL);
19537c478bd9Sstevel@tonic-gate 	  if (retval) {
19547c478bd9Sstevel@tonic-gate 	    com_err(gettext("krb5kdc"),
1955*55fea89dSDan Cross 		    retval,
19567c478bd9Sstevel@tonic-gate 		   gettext("snk4 pulling out key entry"));
19577c478bd9Sstevel@tonic-gate 	    return retval;
19587c478bd9Sstevel@tonic-gate 	  }
19597c478bd9Sstevel@tonic-gate 	  /* now we can use encrypting_key... */
19607c478bd9Sstevel@tonic-gate 	}
19617c478bd9Sstevel@tonic-gate       } else {
19627c478bd9Sstevel@tonic-gate 	/* SAM is not an option - so don't return as hint */
19637c478bd9Sstevel@tonic-gate 	return KRB5_PREAUTH_BAD_TYPE;
19647c478bd9Sstevel@tonic-gate       }
19657c478bd9Sstevel@tonic-gate     }
19667c478bd9Sstevel@tonic-gate     sc.magic = KV5M_SAM_CHALLENGE;
19677c478bd9Sstevel@tonic-gate     psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
19687c478bd9Sstevel@tonic-gate 
19697c478bd9Sstevel@tonic-gate     /* Replay prevention */
19707c478bd9Sstevel@tonic-gate     if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
19717c478bd9Sstevel@tonic-gate 	return retval;
19727c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE
19737c478bd9Sstevel@tonic-gate     if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
19747c478bd9Sstevel@tonic-gate 	return retval;
19757c478bd9Sstevel@tonic-gate #endif /* USE_RCACHE */
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate     switch (sc.sam_type) {
19787c478bd9Sstevel@tonic-gate     case PA_SAM_TYPE_GRAIL:
19797c478bd9Sstevel@tonic-gate       sc.sam_type_name.data = "Experimental System";
19807c478bd9Sstevel@tonic-gate       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
19817c478bd9Sstevel@tonic-gate       sc.sam_challenge_label.data = "experimental challenge label";
19827c478bd9Sstevel@tonic-gate       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
19837c478bd9Sstevel@tonic-gate       sc.sam_challenge.data = "12345";
19847c478bd9Sstevel@tonic-gate       sc.sam_challenge.length = strlen(sc.sam_challenge.data);
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate #if 0 /* Enable this to test "normal" (no flags set) mode.  */
19877c478bd9Sstevel@tonic-gate       psr.sam_flags = sc.sam_flags = 0;
19887c478bd9Sstevel@tonic-gate #endif
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
19917c478bd9Sstevel@tonic-gate       /* string2key on sc.sam_challenge goes in here */
19927c478bd9Sstevel@tonic-gate       /* eblock is just to set the enctype */
19937c478bd9Sstevel@tonic-gate       {
19947c478bd9Sstevel@tonic-gate 	const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
19957c478bd9Sstevel@tonic-gate 
19967c478bd9Sstevel@tonic-gate 	if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
19977c478bd9Sstevel@tonic-gate 					   0 /* salt */, &psr.sam_key)))
19987c478bd9Sstevel@tonic-gate 	    goto cleanup;
19997c478bd9Sstevel@tonic-gate 
20007c478bd9Sstevel@tonic-gate 	if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
20017c478bd9Sstevel@tonic-gate 	    goto cleanup;
2002*55fea89dSDan Cross 
20037c478bd9Sstevel@tonic-gate 	{
20047c478bd9Sstevel@tonic-gate 	    size_t enclen;
20057c478bd9Sstevel@tonic-gate 	    krb5_enc_data tmpdata;
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt_length(context,
20087c478bd9Sstevel@tonic-gate 						psr_key.enctype,
20097c478bd9Sstevel@tonic-gate 						scratch->length, &enclen)))
20107c478bd9Sstevel@tonic-gate 		goto cleanup;
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
20137c478bd9Sstevel@tonic-gate 		retval = ENOMEM;
20147c478bd9Sstevel@tonic-gate 		goto cleanup;
20157c478bd9Sstevel@tonic-gate 	    }
20167c478bd9Sstevel@tonic-gate 	    tmpdata.ciphertext.length = enclen;
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt(context, &psr_key,
20197c478bd9Sstevel@tonic-gate 					 /* XXX */ 0, 0, scratch, &tmpdata)))
20207c478bd9Sstevel@tonic-gate 		goto cleanup;
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 	    sc.sam_track_id = tmpdata.ciphertext;
20237c478bd9Sstevel@tonic-gate 	}
20247c478bd9Sstevel@tonic-gate       }
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate       sc.sam_response_prompt.data = "response prompt";
20277c478bd9Sstevel@tonic-gate       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
20287c478bd9Sstevel@tonic-gate       sc.sam_pk_for_sad.length = 0;
20297c478bd9Sstevel@tonic-gate       sc.sam_nonce = 0;
20307c478bd9Sstevel@tonic-gate       /* Generate checksum */
20317c478bd9Sstevel@tonic-gate       /*krb5_checksum_size(context, ctype)*/
20327c478bd9Sstevel@tonic-gate       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
20337c478bd9Sstevel@tonic-gate 	seed_length,outcksum) */
20347c478bd9Sstevel@tonic-gate       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
20357c478bd9Sstevel@tonic-gate 	seed_length) */
20367c478bd9Sstevel@tonic-gate #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
20377c478bd9Sstevel@tonic-gate       sc.sam_cksum.contents = (krb5_octet *)
20387c478bd9Sstevel@tonic-gate 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
20397c478bd9Sstevel@tonic-gate       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
20427c478bd9Sstevel@tonic-gate 				       sc.sam_challenge.data,
20437c478bd9Sstevel@tonic-gate 				       sc.sam_challenge.length,
20447c478bd9Sstevel@tonic-gate 				       psr.sam_key.contents, /* key */
20457c478bd9Sstevel@tonic-gate 				       psr.sam_key.length, /* key length */
20467c478bd9Sstevel@tonic-gate 				       &sc.sam_cksum);
20477c478bd9Sstevel@tonic-gate       if (retval) { free(sc.sam_cksum.contents); return(retval); }
20487c478bd9Sstevel@tonic-gate #endif /* 0 */
2049*55fea89dSDan Cross 
20507c478bd9Sstevel@tonic-gate       retval = encode_krb5_sam_challenge(&sc, &scratch);
20517c478bd9Sstevel@tonic-gate       if (retval) goto cleanup;
20527c478bd9Sstevel@tonic-gate       pa_data->magic = KV5M_PA_DATA;
20537c478bd9Sstevel@tonic-gate       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
20547c478bd9Sstevel@tonic-gate       pa_data->contents = (unsigned char *) scratch->data;
20557c478bd9Sstevel@tonic-gate       pa_data->length = scratch->length;
2056*55fea89dSDan Cross 
20577c478bd9Sstevel@tonic-gate       retval = 0;
20587c478bd9Sstevel@tonic-gate       break;
20597c478bd9Sstevel@tonic-gate     case PA_SAM_TYPE_DIGI_PATH:
20607c478bd9Sstevel@tonic-gate       sc.sam_type_name.data = "Digital Pathways";
20617c478bd9Sstevel@tonic-gate       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
20627c478bd9Sstevel@tonic-gate #if 1
20637c478bd9Sstevel@tonic-gate       sc.sam_challenge_label.data = "Enter the following on your keypad";
20647c478bd9Sstevel@tonic-gate       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
20657c478bd9Sstevel@tonic-gate #endif
20667c478bd9Sstevel@tonic-gate       /* generate digit string, take it mod 1000000 (six digits.) */
20677c478bd9Sstevel@tonic-gate       {
20687c478bd9Sstevel@tonic-gate 	int j;
20697c478bd9Sstevel@tonic-gate 	krb5_keyblock session_key;
20707c478bd9Sstevel@tonic-gate 	char outputblock[8];
20717c478bd9Sstevel@tonic-gate 	int i;
20727c478bd9Sstevel@tonic-gate 
20737c478bd9Sstevel@tonic-gate 	(void) memset(&session_key, 0, sizeof(krb5_keyblock));
207456a424ccSmp 
20757c478bd9Sstevel@tonic-gate 	(void) memset(inputblock, 0, 8);
20767c478bd9Sstevel@tonic-gate 
20777c478bd9Sstevel@tonic-gate 	retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
20787c478bd9Sstevel@tonic-gate 					&session_key);
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 	if (retval) {
20817c478bd9Sstevel@tonic-gate 	  /* random key failed */
20827c478bd9Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
20837c478bd9Sstevel@tonic-gate 		 retval,
20847c478bd9Sstevel@tonic-gate 		gettext("generating random challenge for preauth"));
20857c478bd9Sstevel@tonic-gate 	  return retval;
20867c478bd9Sstevel@tonic-gate 	}
20877c478bd9Sstevel@tonic-gate 	/* now session_key has a key which we can pick bits out of */
20887c478bd9Sstevel@tonic-gate 	/* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
20897c478bd9Sstevel@tonic-gate 	if (session_key.length != 8) {
20907c478bd9Sstevel@tonic-gate 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
20917c478bd9Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
20927c478bd9Sstevel@tonic-gate 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
20937c478bd9Sstevel@tonic-gate 		 gettext("keytype didn't match code expectations"));
20947c478bd9Sstevel@tonic-gate 	  return retval;
20957c478bd9Sstevel@tonic-gate 	}
20967c478bd9Sstevel@tonic-gate 	for(i = 0; i<6; i++) {
20977c478bd9Sstevel@tonic-gate 	  inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
20987c478bd9Sstevel@tonic-gate 	}
20997c478bd9Sstevel@tonic-gate 	if (session_key.contents)
21007c478bd9Sstevel@tonic-gate 	  krb5_free_keyblock_contents(kdc_context, &session_key);
21017c478bd9Sstevel@tonic-gate 
21027c478bd9Sstevel@tonic-gate 	/* retval = krb5_finish_key(kdc_context, &eblock); */
21037c478bd9Sstevel@tonic-gate 	/* now we have inputblock containing the 8 byte input to DES... */
21047c478bd9Sstevel@tonic-gate 	sc.sam_challenge.data = inputblock;
21057c478bd9Sstevel@tonic-gate 	sc.sam_challenge.length = 6;
21067c478bd9Sstevel@tonic-gate 
21077c478bd9Sstevel@tonic-gate 	encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
21087c478bd9Sstevel@tonic-gate 
21097c478bd9Sstevel@tonic-gate 	if (retval) {
21107c478bd9Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
21117c478bd9Sstevel@tonic-gate 		 retval,
21127c478bd9Sstevel@tonic-gate 		gettext("snk4 processing key"));
21137c478bd9Sstevel@tonic-gate 	}
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 	{
21167c478bd9Sstevel@tonic-gate 	    krb5_data plain;
21177c478bd9Sstevel@tonic-gate 	    krb5_enc_data cipher;
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 	    plain.length = 8;
21207c478bd9Sstevel@tonic-gate 	    plain.data = inputblock;
21217c478bd9Sstevel@tonic-gate 
21227c478bd9Sstevel@tonic-gate 	    /* XXX I know this is enough because of the fixed raw enctype.
21237c478bd9Sstevel@tonic-gate 	       if it's not, the underlying code will return a reasonable
21247c478bd9Sstevel@tonic-gate 	       error, which should never happen */
21257c478bd9Sstevel@tonic-gate 	    cipher.ciphertext.length = 8;
21267c478bd9Sstevel@tonic-gate 	    cipher.ciphertext.data = outputblock;
21277c478bd9Sstevel@tonic-gate 
21287c478bd9Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
21297c478bd9Sstevel@tonic-gate 					 /* XXX */ 0, 0, &plain, &cipher))) {
2130*55fea89dSDan Cross 		com_err(gettext("krb5kdc"),
2131*55fea89dSDan Cross 		retval,
21327c478bd9Sstevel@tonic-gate 		gettext("snk4 response generation failed"));
21337c478bd9Sstevel@tonic-gate 		return retval;
21347c478bd9Sstevel@tonic-gate 	    }
21357c478bd9Sstevel@tonic-gate 	}
21367c478bd9Sstevel@tonic-gate 
21377c478bd9Sstevel@tonic-gate 	/* now output block is the raw bits of the response; convert it
21387c478bd9Sstevel@tonic-gate 	   to display form */
21397c478bd9Sstevel@tonic-gate 	for (j=0; j<4; j++) {
21407c478bd9Sstevel@tonic-gate 	  char n[2];
21417c478bd9Sstevel@tonic-gate 	  int k;
21427c478bd9Sstevel@tonic-gate 	  n[0] = outputblock[j] & 0xf;
21437c478bd9Sstevel@tonic-gate 	  n[1] = (outputblock[j]>>4) & 0xf;
21447c478bd9Sstevel@tonic-gate 	  for (k=0; k<2; k++) {
21457c478bd9Sstevel@tonic-gate 	    if(n[k] > 9) n[k] = ((n[k]-1)>>2);
21467c478bd9Sstevel@tonic-gate 	    /* This is equivalent to:
21477c478bd9Sstevel@tonic-gate 	       if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
21487c478bd9Sstevel@tonic-gate 	       if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
21497c478bd9Sstevel@tonic-gate 	       */
21507c478bd9Sstevel@tonic-gate 	  }
21517c478bd9Sstevel@tonic-gate 	  /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
21527c478bd9Sstevel@tonic-gate 	  /* for v5, we just generate a string */
21537c478bd9Sstevel@tonic-gate 	  response[2*j+0] = '0' + n[1];
21547c478bd9Sstevel@tonic-gate 	  response[2*j+1] = '0' + n[0];
21557c478bd9Sstevel@tonic-gate 	  /* and now, response has what we work with. */
21567c478bd9Sstevel@tonic-gate 	}
21577c478bd9Sstevel@tonic-gate 	response[8] = 0;
21587c478bd9Sstevel@tonic-gate 	predict_response.data = response;
21597c478bd9Sstevel@tonic-gate 	predict_response.length = 8;
21607c478bd9Sstevel@tonic-gate #if 0				/* for debugging, hack the output too! */
21617c478bd9Sstevel@tonic-gate sc.sam_challenge_label.data = response;
21627c478bd9Sstevel@tonic-gate sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
21637c478bd9Sstevel@tonic-gate #endif
21647c478bd9Sstevel@tonic-gate       }
21657c478bd9Sstevel@tonic-gate 
21667c478bd9Sstevel@tonic-gate       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
21677c478bd9Sstevel@tonic-gate       /* string2key on sc.sam_challenge goes in here */
21687c478bd9Sstevel@tonic-gate       /* eblock is just to set the enctype */
21697c478bd9Sstevel@tonic-gate       {
21707c478bd9Sstevel@tonic-gate 	retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
21717c478bd9Sstevel@tonic-gate 				      &predict_response, 0 /* salt */,
21727c478bd9Sstevel@tonic-gate 				      &psr.sam_key);
21737c478bd9Sstevel@tonic-gate 	if (retval) goto cleanup;
21747c478bd9Sstevel@tonic-gate 
21757c478bd9Sstevel@tonic-gate 	retval = encode_krb5_predicted_sam_response(&psr, &scratch);
21767c478bd9Sstevel@tonic-gate 	if (retval) goto cleanup;
2177*55fea89dSDan Cross 
21787c478bd9Sstevel@tonic-gate 	{
21797c478bd9Sstevel@tonic-gate 	    size_t enclen;
21807c478bd9Sstevel@tonic-gate 	    krb5_enc_data tmpdata;
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt_length(context,
21837c478bd9Sstevel@tonic-gate 						psr_key.enctype,
21847c478bd9Sstevel@tonic-gate 						scratch->length, &enclen)))
21857c478bd9Sstevel@tonic-gate 		goto cleanup;
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
21887c478bd9Sstevel@tonic-gate 		retval = ENOMEM;
21897c478bd9Sstevel@tonic-gate 		goto cleanup;
21907c478bd9Sstevel@tonic-gate 	    }
21917c478bd9Sstevel@tonic-gate 	    tmpdata.ciphertext.length = enclen;
21927c478bd9Sstevel@tonic-gate 
21937c478bd9Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt(context, &psr_key,
21947c478bd9Sstevel@tonic-gate 					 /* XXX */ 0, 0, scratch, &tmpdata)))
21957c478bd9Sstevel@tonic-gate 		goto cleanup;
21967c478bd9Sstevel@tonic-gate 
21977c478bd9Sstevel@tonic-gate 	    sc.sam_track_id = tmpdata.ciphertext;
21987c478bd9Sstevel@tonic-gate 	}
21997c478bd9Sstevel@tonic-gate 	if (retval) goto cleanup;
22007c478bd9Sstevel@tonic-gate       }
22017c478bd9Sstevel@tonic-gate 
22027c478bd9Sstevel@tonic-gate       sc.sam_response_prompt.data = "Enter the displayed response";
22037c478bd9Sstevel@tonic-gate       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
22047c478bd9Sstevel@tonic-gate       sc.sam_pk_for_sad.length = 0;
22057c478bd9Sstevel@tonic-gate       sc.sam_nonce = 0;
22067c478bd9Sstevel@tonic-gate       /* Generate checksum */
22077c478bd9Sstevel@tonic-gate       /*krb5_checksum_size(context, ctype)*/
22087c478bd9Sstevel@tonic-gate       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
22097c478bd9Sstevel@tonic-gate 	seed_length,outcksum) */
22107c478bd9Sstevel@tonic-gate       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
22117c478bd9Sstevel@tonic-gate 	seed_length) */
22127c478bd9Sstevel@tonic-gate #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
22137c478bd9Sstevel@tonic-gate       sc.sam_cksum.contents = (krb5_octet *)
22147c478bd9Sstevel@tonic-gate 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
22157c478bd9Sstevel@tonic-gate       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
22187c478bd9Sstevel@tonic-gate 				       sc.sam_challenge.data,
22197c478bd9Sstevel@tonic-gate 				       sc.sam_challenge.length,
22207c478bd9Sstevel@tonic-gate 				       psr.sam_key.contents, /* key */
22217c478bd9Sstevel@tonic-gate 				       psr.sam_key.length, /* key length */
22227c478bd9Sstevel@tonic-gate 				       &sc.sam_cksum);
22237c478bd9Sstevel@tonic-gate       if (retval) { free(sc.sam_cksum.contents); return(retval); }
22247c478bd9Sstevel@tonic-gate #endif /* 0 */
2225*55fea89dSDan Cross 
22267c478bd9Sstevel@tonic-gate       retval = encode_krb5_sam_challenge(&sc, &scratch);
22277c478bd9Sstevel@tonic-gate       if (retval) goto cleanup;
22287c478bd9Sstevel@tonic-gate       pa_data->magic = KV5M_PA_DATA;
22297c478bd9Sstevel@tonic-gate       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
22307c478bd9Sstevel@tonic-gate       pa_data->contents = (unsigned char *) scratch->data;
22317c478bd9Sstevel@tonic-gate       pa_data->length = scratch->length;
2232*55fea89dSDan Cross 
22337c478bd9Sstevel@tonic-gate       retval = 0;
22347c478bd9Sstevel@tonic-gate       break;
22357c478bd9Sstevel@tonic-gate     }
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate cleanup:
22387c478bd9Sstevel@tonic-gate     krb5_free_keyblock_contents(context, &encrypting_key);
22397c478bd9Sstevel@tonic-gate     return retval;
22407c478bd9Sstevel@tonic-gate }
22417c478bd9Sstevel@tonic-gate 
22427c478bd9Sstevel@tonic-gate static krb5_error_code
verify_sam_response(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * pa,preauth_get_entry_data_proc sam_get_entry_data,void * pa_system_context,void ** pa_request_context,krb5_data ** e_data,krb5_authdata *** authz_data)224356a424ccSmp verify_sam_response(krb5_context context, krb5_db_entry *client,
2244159d09a2SMark Phalan 		    krb5_data *req_pkt,
224556a424ccSmp 		    krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
2246159d09a2SMark Phalan 		    krb5_pa_data *pa,
2247159d09a2SMark Phalan 		    preauth_get_entry_data_proc sam_get_entry_data,
2248159d09a2SMark Phalan 		    void *pa_system_context,
2249159d09a2SMark Phalan 		    void **pa_request_context,
2250159d09a2SMark Phalan 		    krb5_data **e_data,
2251159d09a2SMark Phalan 		    krb5_authdata ***authz_data)
22527c478bd9Sstevel@tonic-gate {
22537c478bd9Sstevel@tonic-gate     krb5_error_code		retval;
22547c478bd9Sstevel@tonic-gate     krb5_data			scratch;
22557c478bd9Sstevel@tonic-gate     krb5_sam_response		*sr = 0;
22567c478bd9Sstevel@tonic-gate     krb5_predicted_sam_response	*psr = 0;
22577c478bd9Sstevel@tonic-gate     krb5_enc_sam_response_enc	*esre = 0;
22587c478bd9Sstevel@tonic-gate     krb5_timestamp		timenow;
22597c478bd9Sstevel@tonic-gate     char			*princ_req = 0, *princ_psr = 0;
22607c478bd9Sstevel@tonic-gate 
22617c478bd9Sstevel@tonic-gate     scratch.data = (char *) pa->contents;
22627c478bd9Sstevel@tonic-gate     scratch.length = pa->length;
2263*55fea89dSDan Cross 
22647c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
22657c478bd9Sstevel@tonic-gate 	scratch.data = 0;
22667c478bd9Sstevel@tonic-gate 	com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
22677c478bd9Sstevel@tonic-gate 	goto cleanup;
22687c478bd9Sstevel@tonic-gate     }
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate     /* XXX We can only handle the challenge/response model of SAM.
22717c478bd9Sstevel@tonic-gate 	   See passwords-04, par 4.1, 4.2 */
22727c478bd9Sstevel@tonic-gate     {
22737c478bd9Sstevel@tonic-gate       krb5_enc_data tmpdata;
22747c478bd9Sstevel@tonic-gate 
22757c478bd9Sstevel@tonic-gate       tmpdata.enctype = ENCTYPE_UNKNOWN;
22767c478bd9Sstevel@tonic-gate       tmpdata.ciphertext = sr->sam_track_id;
22777c478bd9Sstevel@tonic-gate 
22787c478bd9Sstevel@tonic-gate       scratch.length = tmpdata.ciphertext.length;
22797c478bd9Sstevel@tonic-gate       if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
22807c478bd9Sstevel@tonic-gate 	  retval = ENOMEM;
22817c478bd9Sstevel@tonic-gate 	  goto cleanup;
22827c478bd9Sstevel@tonic-gate       }
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate       if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
22857c478bd9Sstevel@tonic-gate 				   &tmpdata, &scratch))) {
22867c478bd9Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
22877c478bd9Sstevel@tonic-gate 		retval,
22887c478bd9Sstevel@tonic-gate 		gettext("decrypt track_id failed"));
22897c478bd9Sstevel@tonic-gate 	  goto cleanup;
22907c478bd9Sstevel@tonic-gate       }
22917c478bd9Sstevel@tonic-gate     }
22927c478bd9Sstevel@tonic-gate 
22937c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
22947c478bd9Sstevel@tonic-gate 	com_err(gettext("krb5kdc"), retval,
22957c478bd9Sstevel@tonic-gate 		gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
22967c478bd9Sstevel@tonic-gate 	goto cleanup;
22977c478bd9Sstevel@tonic-gate     }
22987c478bd9Sstevel@tonic-gate 
22997c478bd9Sstevel@tonic-gate     /* Replay detection */
23007c478bd9Sstevel@tonic-gate     if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
23017c478bd9Sstevel@tonic-gate 	goto cleanup;
23027c478bd9Sstevel@tonic-gate     if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
23037c478bd9Sstevel@tonic-gate 	goto cleanup;
23047c478bd9Sstevel@tonic-gate     if (strcmp(princ_req, princ_psr) != 0) {
23057c478bd9Sstevel@tonic-gate 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
23067c478bd9Sstevel@tonic-gate 		gettext("Principal mismatch in SAM psr! -- replay attack?"));
23077c478bd9Sstevel@tonic-gate 	goto cleanup;
23087c478bd9Sstevel@tonic-gate     }
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate     if ((retval = krb5_timeofday(context, &timenow)))
23117c478bd9Sstevel@tonic-gate 	goto cleanup;
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE
23147c478bd9Sstevel@tonic-gate     {
23157c478bd9Sstevel@tonic-gate 	krb5_donot_replay rep;
23167c478bd9Sstevel@tonic-gate 	extern krb5_deltat rc_lifetime;
23177c478bd9Sstevel@tonic-gate 	/*
23187c478bd9Sstevel@tonic-gate 	 * Verify this response came back in a timely manner.
23197c478bd9Sstevel@tonic-gate 	 * We do this b/c otherwise very old (expunged from the rcache)
23207c478bd9Sstevel@tonic-gate 	 * psr's would be able to be replayed.
23217c478bd9Sstevel@tonic-gate 	 */
23227c478bd9Sstevel@tonic-gate 	if (timenow - psr->stime > rc_lifetime) {
23237c478bd9Sstevel@tonic-gate 	    com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
23247c478bd9Sstevel@tonic-gate 	    gettext("SAM psr came back too late! -- replay attack?"));
23257c478bd9Sstevel@tonic-gate 	    goto cleanup;
23267c478bd9Sstevel@tonic-gate 	}
23277c478bd9Sstevel@tonic-gate 
23287c478bd9Sstevel@tonic-gate 	/* Now check the replay cache. */
23297c478bd9Sstevel@tonic-gate 	rep.client = princ_psr;
23307c478bd9Sstevel@tonic-gate 	rep.server = "SAM/rc";  /* Should not match any principal name. */
23317c478bd9Sstevel@tonic-gate 	rep.ctime = psr->stime;
23327c478bd9Sstevel@tonic-gate 	rep.cusec = psr->susec;
233356a424ccSmp 	retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
233456a424ccSmp 	if (retval) {
23357c478bd9Sstevel@tonic-gate 	    com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
23367c478bd9Sstevel@tonic-gate 	    goto cleanup;
23377c478bd9Sstevel@tonic-gate 	}
23387c478bd9Sstevel@tonic-gate     }
23397c478bd9Sstevel@tonic-gate #endif /* USE_RCACHE */
23407c478bd9Sstevel@tonic-gate 
23417c478bd9Sstevel@tonic-gate 
23427c478bd9Sstevel@tonic-gate     {
23437c478bd9Sstevel@tonic-gate 	free(scratch.data);
23447c478bd9Sstevel@tonic-gate 	scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
23457c478bd9Sstevel@tonic-gate 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
23467c478bd9Sstevel@tonic-gate 	    retval = ENOMEM;
23477c478bd9Sstevel@tonic-gate 	    goto cleanup;
23487c478bd9Sstevel@tonic-gate 	}
23497c478bd9Sstevel@tonic-gate 
23507c478bd9Sstevel@tonic-gate 	if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
23517c478bd9Sstevel@tonic-gate 				     0, &sr->sam_enc_nonce_or_ts, &scratch))) {
23527c478bd9Sstevel@tonic-gate 	    com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
23537c478bd9Sstevel@tonic-gate 	    goto cleanup;
23547c478bd9Sstevel@tonic-gate 	}
23557c478bd9Sstevel@tonic-gate     }
23567c478bd9Sstevel@tonic-gate 
23577c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
23587c478bd9Sstevel@tonic-gate 	com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
23597c478bd9Sstevel@tonic-gate 	goto cleanup;
23607c478bd9Sstevel@tonic-gate     }
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate     if (esre->sam_timestamp != sr->sam_patimestamp) {
23637c478bd9Sstevel@tonic-gate       retval = KRB5KDC_ERR_PREAUTH_FAILED;
23647c478bd9Sstevel@tonic-gate       goto cleanup;
23657c478bd9Sstevel@tonic-gate     }
2366*55fea89dSDan Cross 
23677c478bd9Sstevel@tonic-gate     if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
23687c478bd9Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_SKEW;
23697c478bd9Sstevel@tonic-gate 	goto cleanup;
23707c478bd9Sstevel@tonic-gate     }
23717c478bd9Sstevel@tonic-gate 
23727c478bd9Sstevel@tonic-gate     setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate   cleanup:
2375*55fea89dSDan Cross     if (retval) com_err(gettext("krb5kdc"),
23767c478bd9Sstevel@tonic-gate 			retval,
23777c478bd9Sstevel@tonic-gate 			gettext("sam verify failure"));
23787c478bd9Sstevel@tonic-gate     if (scratch.data) free(scratch.data);
23797c478bd9Sstevel@tonic-gate     if (sr) free(sr);
23807c478bd9Sstevel@tonic-gate     if (psr) free(psr);
23817c478bd9Sstevel@tonic-gate     if (esre) free(esre);
238256a424ccSmp     if (princ_psr) free(princ_psr);
238356a424ccSmp     if (princ_req) free(princ_req);
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate     return retval;
23867c478bd9Sstevel@tonic-gate }
2387