xref: /onnv-gate/usr/src/cmd/krb5/krb5kdc/kdc_preauth.c (revision 7934:6aeeafc994de)
10Sstevel@tonic-gate /*
2*7934SMark.Phalan@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate 
70Sstevel@tonic-gate /*
80Sstevel@tonic-gate  * kdc/kdc_preauth.c
90Sstevel@tonic-gate  *
102881Smp153739  * Copyright 1995, 2003 by the Massachusetts Institute of Technology.
110Sstevel@tonic-gate  * All Rights Reserved.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * Export of this software from the United States of America may
140Sstevel@tonic-gate  *   require a specific license from the United States Government.
150Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
160Sstevel@tonic-gate  *   export to obtain such a license before exporting.
170Sstevel@tonic-gate  *
180Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
190Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
200Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
210Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
220Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
230Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
240Sstevel@tonic-gate  * to distribution of the software without specific, written prior
250Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
260Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
270Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
280Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
290Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
300Sstevel@tonic-gate  * or implied warranty.
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  * Preauthentication routines for the KDC.
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /*
360Sstevel@tonic-gate  * Copyright (C) 1998 by the FundsXpress, INC.
370Sstevel@tonic-gate  *
380Sstevel@tonic-gate  * All rights reserved.
390Sstevel@tonic-gate  *
400Sstevel@tonic-gate  * Export of this software from the United States of America may require
410Sstevel@tonic-gate  * a specific license from the United States Government.  It is the
420Sstevel@tonic-gate  * responsibility of any person or organization contemplating export to
430Sstevel@tonic-gate  * obtain such a license before exporting.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
460Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
470Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
480Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
490Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
500Sstevel@tonic-gate  * the name of FundsXpress. not be used in advertising or publicity pertaining
510Sstevel@tonic-gate  * to distribution of the software without specific, written prior
520Sstevel@tonic-gate  * permission.  FundsXpress makes no representations about the suitability of
530Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
540Sstevel@tonic-gate  * or implied warranty.
550Sstevel@tonic-gate  *
560Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
570Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
580Sstevel@tonic-gate  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
590Sstevel@tonic-gate  */
600Sstevel@tonic-gate 
610Sstevel@tonic-gate #include "k5-int.h"
620Sstevel@tonic-gate #include "kdc_util.h"
630Sstevel@tonic-gate #include "extern.h"
640Sstevel@tonic-gate #include "com_err.h"
650Sstevel@tonic-gate #include <assert.h>
660Sstevel@tonic-gate #include <stdio.h>
672881Smp153739 #include "adm_proto.h"
680Sstevel@tonic-gate #include <libintl.h>
690Sstevel@tonic-gate #include <syslog.h>
700Sstevel@tonic-gate 
712881Smp153739 #include <assert.h>
72*7934SMark.Phalan@Sun.COM #include "preauth_plugin.h"
73*7934SMark.Phalan@Sun.COM 
74*7934SMark.Phalan@Sun.COM #if TARGET_OS_MAC
75*7934SMark.Phalan@Sun.COM static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
76*7934SMark.Phalan@Sun.COM #else
77*7934SMark.Phalan@Sun.COM static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
78*7934SMark.Phalan@Sun.COM #endif
792881Smp153739 
802881Smp153739 /* XXX This is ugly and should be in a header file somewhere */
812881Smp153739 #ifndef KRB5INT_DES_TYPES_DEFINED
822881Smp153739 #define KRB5INT_DES_TYPES_DEFINED
832881Smp153739 typedef unsigned char des_cblock[8];	/* crypto-block size */
842881Smp153739 #endif
852881Smp153739 typedef des_cblock mit_des_cblock;
862881Smp153739 extern void mit_des_fixup_key_parity (mit_des_cblock );
872881Smp153739 extern int mit_des_is_weak_key (mit_des_cblock );
882881Smp153739 
890Sstevel@tonic-gate typedef struct _krb5_preauth_systems {
90*7934SMark.Phalan@Sun.COM     const char *name;
910Sstevel@tonic-gate     int		type;
920Sstevel@tonic-gate     int		flags;
93*7934SMark.Phalan@Sun.COM     void       *plugin_context;
94*7934SMark.Phalan@Sun.COM     preauth_server_init_proc	init;
95*7934SMark.Phalan@Sun.COM     preauth_server_fini_proc	fini;
96*7934SMark.Phalan@Sun.COM     preauth_server_edata_proc	get_edata;
97*7934SMark.Phalan@Sun.COM     preauth_server_verify_proc	verify_padata;
98*7934SMark.Phalan@Sun.COM     preauth_server_return_proc	return_padata;
99*7934SMark.Phalan@Sun.COM     preauth_server_free_reqcontext_proc	free_pa_reqctx;
1000Sstevel@tonic-gate } krb5_preauth_systems;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate static krb5_error_code verify_enc_timestamp
1030Sstevel@tonic-gate     (krb5_context, krb5_db_entry *client,
104*7934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
1052881Smp153739 		    krb5_kdc_req *request,
106*7934SMark.Phalan@Sun.COM 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
107*7934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
108*7934SMark.Phalan@Sun.COM 		    void *pa_system_context,
109*7934SMark.Phalan@Sun.COM 		    void **pa_request_context,
110*7934SMark.Phalan@Sun.COM 		    krb5_data **e_data,
111*7934SMark.Phalan@Sun.COM 		    krb5_authdata ***authz_data);
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate static krb5_error_code get_etype_info
1140Sstevel@tonic-gate     (krb5_context, krb5_kdc_req *request,
1152881Smp153739 		    krb5_db_entry *client, krb5_db_entry *server,
116*7934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
117*7934SMark.Phalan@Sun.COM 		    void *pa_system_context,
1182881Smp153739 		    krb5_pa_data *data);
1190Sstevel@tonic-gate static krb5_error_code
1200Sstevel@tonic-gate get_etype_info2(krb5_context context, krb5_kdc_req *request,
121*7934SMark.Phalan@Sun.COM 	        krb5_db_entry *client, krb5_db_entry *server,
122*7934SMark.Phalan@Sun.COM 		preauth_get_entry_data_proc get_entry_data,
123*7934SMark.Phalan@Sun.COM 		void *pa_system_context,
124*7934SMark.Phalan@Sun.COM 		krb5_pa_data *pa_data);
125*7934SMark.Phalan@Sun.COM static krb5_error_code
126*7934SMark.Phalan@Sun.COM etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
127*7934SMark.Phalan@Sun.COM 			 krb5_db_entry *client,
128*7934SMark.Phalan@Sun.COM 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
129*7934SMark.Phalan@Sun.COM 			 krb5_key_data *client_key,
130*7934SMark.Phalan@Sun.COM 			 krb5_keyblock *encrypting_key,
131*7934SMark.Phalan@Sun.COM 			 krb5_pa_data **send_pa,
132*7934SMark.Phalan@Sun.COM 			 int etype_info2);
133*7934SMark.Phalan@Sun.COM 
134*7934SMark.Phalan@Sun.COM static krb5_error_code
135*7934SMark.Phalan@Sun.COM return_etype_info(krb5_context, krb5_pa_data * padata,
136*7934SMark.Phalan@Sun.COM 		  krb5_db_entry *client,
137*7934SMark.Phalan@Sun.COM 		  krb5_data *req_pkt,
138*7934SMark.Phalan@Sun.COM 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
139*7934SMark.Phalan@Sun.COM 		  krb5_key_data *client_key,
140*7934SMark.Phalan@Sun.COM 		  krb5_keyblock *encrypting_key,
141*7934SMark.Phalan@Sun.COM 		  krb5_pa_data **send_pa,
142*7934SMark.Phalan@Sun.COM 		  preauth_get_entry_data_proc get_entry_data,
143*7934SMark.Phalan@Sun.COM 		  void *pa_system_context,
144*7934SMark.Phalan@Sun.COM 		  void **pa_request_context);
145*7934SMark.Phalan@Sun.COM 
1460Sstevel@tonic-gate static krb5_error_code
1472881Smp153739 return_etype_info2(krb5_context, krb5_pa_data * padata,
1482881Smp153739 		   krb5_db_entry *client,
149*7934SMark.Phalan@Sun.COM 		   krb5_data *req_pkt,
1502881Smp153739 		   krb5_kdc_req *request, krb5_kdc_rep *reply,
1512881Smp153739 		   krb5_key_data *client_key,
1522881Smp153739 		   krb5_keyblock *encrypting_key,
153*7934SMark.Phalan@Sun.COM 		   krb5_pa_data **send_pa,
154*7934SMark.Phalan@Sun.COM 		   preauth_get_entry_data_proc get_entry_data,
155*7934SMark.Phalan@Sun.COM 		   void *pa_system_context,
156*7934SMark.Phalan@Sun.COM 		   void **pa_request_context);
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate static krb5_error_code return_pw_salt
1590Sstevel@tonic-gate     (krb5_context, krb5_pa_data * padata,
1602881Smp153739 		    krb5_db_entry *client,
161*7934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
1622881Smp153739 		    krb5_kdc_req *request, krb5_kdc_rep *reply,
1632881Smp153739 		    krb5_key_data *client_key,
1642881Smp153739 		    krb5_keyblock *encrypting_key,
165*7934SMark.Phalan@Sun.COM 		    krb5_pa_data **send_pa,
166*7934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
167*7934SMark.Phalan@Sun.COM 		    void *pa_system_context,
168*7934SMark.Phalan@Sun.COM 		    void **pa_request_context);
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate /* SAM preauth support */
1710Sstevel@tonic-gate static krb5_error_code verify_sam_response
1722881Smp153739     (krb5_context, krb5_db_entry *client,
173*7934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
1742881Smp153739 		    krb5_kdc_req *request,
175*7934SMark.Phalan@Sun.COM 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
176*7934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
177*7934SMark.Phalan@Sun.COM 		    void *pa_module_context,
178*7934SMark.Phalan@Sun.COM 		    void **pa_request_context,
179*7934SMark.Phalan@Sun.COM 		    krb5_data **e_data,
180*7934SMark.Phalan@Sun.COM 		    krb5_authdata ***authz_data);
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate static krb5_error_code get_sam_edata
1830Sstevel@tonic-gate     (krb5_context, krb5_kdc_req *request,
1842881Smp153739 		    krb5_db_entry *client, krb5_db_entry *server,
185*7934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
186*7934SMark.Phalan@Sun.COM 		    void *pa_module_context,
1872881Smp153739 		    krb5_pa_data *data);
1880Sstevel@tonic-gate static krb5_error_code return_sam_data
1890Sstevel@tonic-gate     (krb5_context, krb5_pa_data * padata,
1902881Smp153739 		    krb5_db_entry *client,
191*7934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
1922881Smp153739 		    krb5_kdc_req *request, krb5_kdc_rep *reply,
1932881Smp153739 		    krb5_key_data *client_key,
1942881Smp153739 		    krb5_keyblock *encrypting_key,
195*7934SMark.Phalan@Sun.COM 		    krb5_pa_data **send_pa,
196*7934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
197*7934SMark.Phalan@Sun.COM 		    void *pa_module_context,
198*7934SMark.Phalan@Sun.COM 		    void **pa_request_context);
1990Sstevel@tonic-gate 
200*7934SMark.Phalan@Sun.COM static krb5_preauth_systems static_preauth_systems[] = {
2010Sstevel@tonic-gate     {
2020Sstevel@tonic-gate 	"timestamp",
2030Sstevel@tonic-gate         KRB5_PADATA_ENC_TIMESTAMP,
2040Sstevel@tonic-gate         0,
205*7934SMark.Phalan@Sun.COM 	NULL,
206*7934SMark.Phalan@Sun.COM 	NULL,
207*7934SMark.Phalan@Sun.COM 	NULL,
2080Sstevel@tonic-gate         0,
2090Sstevel@tonic-gate 	verify_enc_timestamp,
2100Sstevel@tonic-gate 	0
2110Sstevel@tonic-gate     },
2120Sstevel@tonic-gate     {
2130Sstevel@tonic-gate 	"etype-info",
2140Sstevel@tonic-gate 	KRB5_PADATA_ETYPE_INFO,
2150Sstevel@tonic-gate 	0,
216*7934SMark.Phalan@Sun.COM 	NULL,
217*7934SMark.Phalan@Sun.COM 	NULL,
218*7934SMark.Phalan@Sun.COM 	NULL,
2190Sstevel@tonic-gate 	get_etype_info,
2200Sstevel@tonic-gate 	0,
221*7934SMark.Phalan@Sun.COM 	return_etype_info
2220Sstevel@tonic-gate     },
2230Sstevel@tonic-gate     {
2242881Smp153739 	"etype-info2",
2250Sstevel@tonic-gate 	KRB5_PADATA_ETYPE_INFO2,
2260Sstevel@tonic-gate 	0,
227*7934SMark.Phalan@Sun.COM 	NULL,
228*7934SMark.Phalan@Sun.COM 	NULL,
229*7934SMark.Phalan@Sun.COM 	NULL,
2302881Smp153739 	get_etype_info2,
2310Sstevel@tonic-gate 	0,
2322881Smp153739 	return_etype_info2
2330Sstevel@tonic-gate     },
2340Sstevel@tonic-gate     {
2350Sstevel@tonic-gate 	"pw-salt",
2360Sstevel@tonic-gate 	KRB5_PADATA_PW_SALT,
2370Sstevel@tonic-gate 	PA_PSEUDO,		/* Don't include this in the error list */
238*7934SMark.Phalan@Sun.COM 	NULL,
239*7934SMark.Phalan@Sun.COM 	NULL,
240*7934SMark.Phalan@Sun.COM 	NULL,
2410Sstevel@tonic-gate 	0,
2420Sstevel@tonic-gate 	0,
2430Sstevel@tonic-gate 	return_pw_salt
2440Sstevel@tonic-gate     },
2450Sstevel@tonic-gate     {
2460Sstevel@tonic-gate 	"sam-response",
2470Sstevel@tonic-gate 	KRB5_PADATA_SAM_RESPONSE,
2480Sstevel@tonic-gate 	0,
249*7934SMark.Phalan@Sun.COM 	NULL,
250*7934SMark.Phalan@Sun.COM 	NULL,
251*7934SMark.Phalan@Sun.COM 	NULL,
2520Sstevel@tonic-gate 	0,
2530Sstevel@tonic-gate 	verify_sam_response,
2540Sstevel@tonic-gate 	return_sam_data
2550Sstevel@tonic-gate     },
2560Sstevel@tonic-gate     {
2570Sstevel@tonic-gate 	"sam-challenge",
2580Sstevel@tonic-gate 	KRB5_PADATA_SAM_CHALLENGE,
2590Sstevel@tonic-gate 	PA_HARDWARE,		/* causes get_preauth_hint_list to use this */
260*7934SMark.Phalan@Sun.COM 	NULL,
261*7934SMark.Phalan@Sun.COM 	NULL,
262*7934SMark.Phalan@Sun.COM 	NULL,
2630Sstevel@tonic-gate 	get_sam_edata,
2640Sstevel@tonic-gate 	0,
2650Sstevel@tonic-gate 	0
2660Sstevel@tonic-gate     },
2670Sstevel@tonic-gate     { "[end]", -1,}
2680Sstevel@tonic-gate };
2690Sstevel@tonic-gate 
270*7934SMark.Phalan@Sun.COM static krb5_preauth_systems *preauth_systems;
271*7934SMark.Phalan@Sun.COM static int n_preauth_systems;
272*7934SMark.Phalan@Sun.COM static struct plugin_dir_handle preauth_plugins;
273*7934SMark.Phalan@Sun.COM 
274*7934SMark.Phalan@Sun.COM krb5_error_code
275*7934SMark.Phalan@Sun.COM load_preauth_plugins(krb5_context context)
276*7934SMark.Phalan@Sun.COM {
277*7934SMark.Phalan@Sun.COM     struct errinfo err;
278*7934SMark.Phalan@Sun.COM     void **preauth_plugins_ftables;
279*7934SMark.Phalan@Sun.COM     struct krb5plugin_preauth_server_ftable_v1 *ftable;
280*7934SMark.Phalan@Sun.COM     int module_count, i, j, k;
281*7934SMark.Phalan@Sun.COM     void *plugin_context;
282*7934SMark.Phalan@Sun.COM     preauth_server_init_proc server_init_proc = NULL;
283*7934SMark.Phalan@Sun.COM     char **kdc_realm_names = NULL;
284*7934SMark.Phalan@Sun.COM 
285*7934SMark.Phalan@Sun.COM     memset(&err, 0, sizeof(err));
286*7934SMark.Phalan@Sun.COM 
287*7934SMark.Phalan@Sun.COM     /* Attempt to load all of the preauth plugins we can find. */
288*7934SMark.Phalan@Sun.COM     PLUGIN_DIR_INIT(&preauth_plugins);
289*7934SMark.Phalan@Sun.COM     if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) {
290*7934SMark.Phalan@Sun.COM 	if (krb5int_open_plugin_dirs(objdirs, NULL,
291*7934SMark.Phalan@Sun.COM 				     &preauth_plugins, &err) != 0) {
292*7934SMark.Phalan@Sun.COM 	    return KRB5_PLUGIN_NO_HANDLE;
293*7934SMark.Phalan@Sun.COM 	}
294*7934SMark.Phalan@Sun.COM     }
295*7934SMark.Phalan@Sun.COM 
296*7934SMark.Phalan@Sun.COM     /* Get the method tables provided by the loaded plugins. */
297*7934SMark.Phalan@Sun.COM     preauth_plugins_ftables = NULL;
298*7934SMark.Phalan@Sun.COM     if (krb5int_get_plugin_dir_data(&preauth_plugins,
299*7934SMark.Phalan@Sun.COM 				    "preauthentication_server_1",
300*7934SMark.Phalan@Sun.COM 				    &preauth_plugins_ftables, &err) != 0) {
301*7934SMark.Phalan@Sun.COM 	return KRB5_PLUGIN_NO_HANDLE;
302*7934SMark.Phalan@Sun.COM     }
303*7934SMark.Phalan@Sun.COM 
304*7934SMark.Phalan@Sun.COM     /* Count the valid modules. */
305*7934SMark.Phalan@Sun.COM     module_count = sizeof(static_preauth_systems)
306*7934SMark.Phalan@Sun.COM 		   / sizeof(static_preauth_systems[0]);
307*7934SMark.Phalan@Sun.COM     if (preauth_plugins_ftables != NULL) {
308*7934SMark.Phalan@Sun.COM 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
309*7934SMark.Phalan@Sun.COM 	    ftable = preauth_plugins_ftables[i];
310*7934SMark.Phalan@Sun.COM 	    if ((ftable->flags_proc == NULL) &&
311*7934SMark.Phalan@Sun.COM 		(ftable->edata_proc == NULL) &&
312*7934SMark.Phalan@Sun.COM 		(ftable->verify_proc == NULL) &&
313*7934SMark.Phalan@Sun.COM 		(ftable->return_proc == NULL)) {
314*7934SMark.Phalan@Sun.COM 		continue;
315*7934SMark.Phalan@Sun.COM 	    }
316*7934SMark.Phalan@Sun.COM 	    for (j = 0;
317*7934SMark.Phalan@Sun.COM 		 ftable->pa_type_list != NULL &&
318*7934SMark.Phalan@Sun.COM 		 ftable->pa_type_list[j] > 0;
319*7934SMark.Phalan@Sun.COM 		 j++) {
320*7934SMark.Phalan@Sun.COM 		module_count++;
321*7934SMark.Phalan@Sun.COM 	    }
322*7934SMark.Phalan@Sun.COM 	}
323*7934SMark.Phalan@Sun.COM     }
324*7934SMark.Phalan@Sun.COM 
325*7934SMark.Phalan@Sun.COM     /* Build the complete list of supported preauthentication options, and
326*7934SMark.Phalan@Sun.COM      * leave room for a terminator entry. */
327*7934SMark.Phalan@Sun.COM     preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1));
328*7934SMark.Phalan@Sun.COM     if (preauth_systems == NULL) {
329*7934SMark.Phalan@Sun.COM 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
330*7934SMark.Phalan@Sun.COM 	return ENOMEM;
331*7934SMark.Phalan@Sun.COM     }
332*7934SMark.Phalan@Sun.COM 
333*7934SMark.Phalan@Sun.COM     /* Build a list of the names of the supported realms for this KDC.
334*7934SMark.Phalan@Sun.COM      * The list of names is terminated with a NULL. */
335*7934SMark.Phalan@Sun.COM     kdc_realm_names = malloc(sizeof(char *) * (kdc_numrealms + 1));
336*7934SMark.Phalan@Sun.COM     if (kdc_realm_names == NULL) {
337*7934SMark.Phalan@Sun.COM 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
338*7934SMark.Phalan@Sun.COM 	return ENOMEM;
339*7934SMark.Phalan@Sun.COM     }
340*7934SMark.Phalan@Sun.COM     for (i = 0; i < kdc_numrealms; i++) {
341*7934SMark.Phalan@Sun.COM 	kdc_realm_names[i] = kdc_realmlist[i]->realm_name;
342*7934SMark.Phalan@Sun.COM     }
343*7934SMark.Phalan@Sun.COM     kdc_realm_names[i] = NULL;
344*7934SMark.Phalan@Sun.COM 
345*7934SMark.Phalan@Sun.COM     /* Add the locally-supplied mechanisms to the dynamic list first. */
346*7934SMark.Phalan@Sun.COM     for (i = 0, k = 0;
347*7934SMark.Phalan@Sun.COM 	 i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
348*7934SMark.Phalan@Sun.COM 	 i++) {
349*7934SMark.Phalan@Sun.COM 	if (static_preauth_systems[i].type == -1)
350*7934SMark.Phalan@Sun.COM 	    break;
351*7934SMark.Phalan@Sun.COM 	preauth_systems[k] = static_preauth_systems[i];
352*7934SMark.Phalan@Sun.COM 	/* Try to initialize the preauth system.  If it fails, we'll remove it
353*7934SMark.Phalan@Sun.COM 	 * from the list of systems we'll be using. */
354*7934SMark.Phalan@Sun.COM 	plugin_context = NULL;
355*7934SMark.Phalan@Sun.COM 	server_init_proc = static_preauth_systems[i].init;
356*7934SMark.Phalan@Sun.COM 	if ((server_init_proc != NULL) &&
357*7934SMark.Phalan@Sun.COM 	    ((*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names) != 0)) {
358*7934SMark.Phalan@Sun.COM 	    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
359*7934SMark.Phalan@Sun.COM 	    continue;
360*7934SMark.Phalan@Sun.COM 	}
361*7934SMark.Phalan@Sun.COM 	preauth_systems[k].plugin_context = plugin_context;
362*7934SMark.Phalan@Sun.COM 	k++;
363*7934SMark.Phalan@Sun.COM     }
364*7934SMark.Phalan@Sun.COM 
365*7934SMark.Phalan@Sun.COM     /* Now add the dynamically-loaded mechanisms to the list. */
366*7934SMark.Phalan@Sun.COM     if (preauth_plugins_ftables != NULL) {
367*7934SMark.Phalan@Sun.COM 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
368*7934SMark.Phalan@Sun.COM 	    ftable = preauth_plugins_ftables[i];
369*7934SMark.Phalan@Sun.COM 	    if ((ftable->flags_proc == NULL) &&
370*7934SMark.Phalan@Sun.COM 		(ftable->edata_proc == NULL) &&
371*7934SMark.Phalan@Sun.COM 		(ftable->verify_proc == NULL) &&
372*7934SMark.Phalan@Sun.COM 		(ftable->return_proc == NULL)) {
373*7934SMark.Phalan@Sun.COM 		continue;
374*7934SMark.Phalan@Sun.COM 	    }
375*7934SMark.Phalan@Sun.COM 	    plugin_context = NULL;
376*7934SMark.Phalan@Sun.COM 	    for (j = 0;
377*7934SMark.Phalan@Sun.COM 		 ftable->pa_type_list != NULL &&
378*7934SMark.Phalan@Sun.COM 		 ftable->pa_type_list[j] > 0;
379*7934SMark.Phalan@Sun.COM 		 j++) {
380*7934SMark.Phalan@Sun.COM 		/* Try to initialize the plugin.  If it fails, we'll remove it
381*7934SMark.Phalan@Sun.COM 		 * from the list of modules we'll be using. */
382*7934SMark.Phalan@Sun.COM 		if (j == 0) {
383*7934SMark.Phalan@Sun.COM 		    server_init_proc = ftable->init_proc;
384*7934SMark.Phalan@Sun.COM 		    if (server_init_proc != NULL) {
385*7934SMark.Phalan@Sun.COM 			krb5_error_code initerr;
386*7934SMark.Phalan@Sun.COM 			initerr = (*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names);
387*7934SMark.Phalan@Sun.COM 			if (initerr) {
388*7934SMark.Phalan@Sun.COM 			    const char *emsg;
389*7934SMark.Phalan@Sun.COM 			    emsg = krb5_get_error_message(context, initerr);
390*7934SMark.Phalan@Sun.COM 			    if (emsg) {
391*7934SMark.Phalan@Sun.COM 				krb5_klog_syslog(LOG_ERR,
392*7934SMark.Phalan@Sun.COM 					"preauth %s failed to initialize: %s",
393*7934SMark.Phalan@Sun.COM 					ftable->name, emsg);
394*7934SMark.Phalan@Sun.COM 				krb5_free_error_message(context, emsg);
395*7934SMark.Phalan@Sun.COM 			    }
396*7934SMark.Phalan@Sun.COM 			    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
397*7934SMark.Phalan@Sun.COM 
398*7934SMark.Phalan@Sun.COM 			    break;	/* skip all modules in this plugin */
399*7934SMark.Phalan@Sun.COM 			}
400*7934SMark.Phalan@Sun.COM 		    }
401*7934SMark.Phalan@Sun.COM 		}
402*7934SMark.Phalan@Sun.COM 		preauth_systems[k].name = ftable->name;
403*7934SMark.Phalan@Sun.COM 		preauth_systems[k].type = ftable->pa_type_list[j];
404*7934SMark.Phalan@Sun.COM 		if (ftable->flags_proc != NULL)
405*7934SMark.Phalan@Sun.COM 		    preauth_systems[k].flags = ftable->flags_proc(context, preauth_systems[k].type);
406*7934SMark.Phalan@Sun.COM 		else
407*7934SMark.Phalan@Sun.COM 		    preauth_systems[k].flags = 0;
408*7934SMark.Phalan@Sun.COM 		preauth_systems[k].plugin_context = plugin_context;
409*7934SMark.Phalan@Sun.COM 		preauth_systems[k].init = server_init_proc;
410*7934SMark.Phalan@Sun.COM 		/* Only call fini once for each plugin */
411*7934SMark.Phalan@Sun.COM 		if (j == 0)
412*7934SMark.Phalan@Sun.COM 		    preauth_systems[k].fini = ftable->fini_proc;
413*7934SMark.Phalan@Sun.COM 		else
414*7934SMark.Phalan@Sun.COM 		    preauth_systems[k].fini = NULL;
415*7934SMark.Phalan@Sun.COM 		preauth_systems[k].get_edata = ftable->edata_proc;
416*7934SMark.Phalan@Sun.COM 		preauth_systems[k].verify_padata = ftable->verify_proc;
417*7934SMark.Phalan@Sun.COM 		preauth_systems[k].return_padata = ftable->return_proc;
418*7934SMark.Phalan@Sun.COM 		preauth_systems[k].free_pa_reqctx =
419*7934SMark.Phalan@Sun.COM 		    ftable->freepa_reqcontext_proc;
420*7934SMark.Phalan@Sun.COM 		k++;
421*7934SMark.Phalan@Sun.COM 	    }
422*7934SMark.Phalan@Sun.COM 	}
423*7934SMark.Phalan@Sun.COM 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
424*7934SMark.Phalan@Sun.COM     }
425*7934SMark.Phalan@Sun.COM     free(kdc_realm_names);
426*7934SMark.Phalan@Sun.COM     n_preauth_systems = k;
427*7934SMark.Phalan@Sun.COM     /* Add the end-of-list marker. */
428*7934SMark.Phalan@Sun.COM     preauth_systems[k].name = "[end]";
429*7934SMark.Phalan@Sun.COM     preauth_systems[k].type = -1;
430*7934SMark.Phalan@Sun.COM     return 0;
431*7934SMark.Phalan@Sun.COM }
432*7934SMark.Phalan@Sun.COM 
433*7934SMark.Phalan@Sun.COM krb5_error_code
434*7934SMark.Phalan@Sun.COM unload_preauth_plugins(krb5_context context)
435*7934SMark.Phalan@Sun.COM {
436*7934SMark.Phalan@Sun.COM     int i;
437*7934SMark.Phalan@Sun.COM     if (preauth_systems != NULL) {
438*7934SMark.Phalan@Sun.COM 	for (i = 0; i < n_preauth_systems; i++) {
439*7934SMark.Phalan@Sun.COM 	    if (preauth_systems[i].fini != NULL) {
440*7934SMark.Phalan@Sun.COM 	        (*preauth_systems[i].fini)(context,
441*7934SMark.Phalan@Sun.COM 					   preauth_systems[i].plugin_context);
442*7934SMark.Phalan@Sun.COM 	    }
443*7934SMark.Phalan@Sun.COM 	    memset(&preauth_systems[i], 0, sizeof(preauth_systems[i]));
444*7934SMark.Phalan@Sun.COM 	}
445*7934SMark.Phalan@Sun.COM 	free(preauth_systems);
446*7934SMark.Phalan@Sun.COM 	preauth_systems = NULL;
447*7934SMark.Phalan@Sun.COM 	n_preauth_systems = 0;
448*7934SMark.Phalan@Sun.COM 	krb5int_close_plugin_dirs(&preauth_plugins);
449*7934SMark.Phalan@Sun.COM     }
450*7934SMark.Phalan@Sun.COM     return 0;
451*7934SMark.Phalan@Sun.COM }
452*7934SMark.Phalan@Sun.COM 
453*7934SMark.Phalan@Sun.COM /*
454*7934SMark.Phalan@Sun.COM  * The make_padata_context() function creates a space for storing any context
455*7934SMark.Phalan@Sun.COM  * information which will be needed by return_padata() later.  Each preauth
456*7934SMark.Phalan@Sun.COM  * type gets a context storage location of its own.
457*7934SMark.Phalan@Sun.COM  */
458*7934SMark.Phalan@Sun.COM struct request_pa_context {
459*7934SMark.Phalan@Sun.COM     int n_contexts;
460*7934SMark.Phalan@Sun.COM     struct {
461*7934SMark.Phalan@Sun.COM 	krb5_preauth_systems *pa_system;
462*7934SMark.Phalan@Sun.COM 	void *pa_context;
463*7934SMark.Phalan@Sun.COM     } *contexts;
464*7934SMark.Phalan@Sun.COM };
465*7934SMark.Phalan@Sun.COM 
466*7934SMark.Phalan@Sun.COM static krb5_error_code
467*7934SMark.Phalan@Sun.COM make_padata_context(krb5_context context, void **padata_context)
468*7934SMark.Phalan@Sun.COM {
469*7934SMark.Phalan@Sun.COM     int i;
470*7934SMark.Phalan@Sun.COM     struct request_pa_context *ret;
471*7934SMark.Phalan@Sun.COM 
472*7934SMark.Phalan@Sun.COM     ret = malloc(sizeof(*ret));
473*7934SMark.Phalan@Sun.COM     if (ret == NULL) {
474*7934SMark.Phalan@Sun.COM 	return ENOMEM;
475*7934SMark.Phalan@Sun.COM     }
476*7934SMark.Phalan@Sun.COM 
477*7934SMark.Phalan@Sun.COM     ret->n_contexts = n_preauth_systems;
478*7934SMark.Phalan@Sun.COM     ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
479*7934SMark.Phalan@Sun.COM     if (ret->contexts == NULL) {
480*7934SMark.Phalan@Sun.COM 	free(ret);
481*7934SMark.Phalan@Sun.COM 	return ENOMEM;
482*7934SMark.Phalan@Sun.COM     }
483*7934SMark.Phalan@Sun.COM 
484*7934SMark.Phalan@Sun.COM     memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
485*7934SMark.Phalan@Sun.COM 
486*7934SMark.Phalan@Sun.COM     for (i = 0; i < ret->n_contexts; i++) {
487*7934SMark.Phalan@Sun.COM 	ret->contexts[i].pa_system = &preauth_systems[i];
488*7934SMark.Phalan@Sun.COM 	ret->contexts[i].pa_context = NULL;
489*7934SMark.Phalan@Sun.COM     }
490*7934SMark.Phalan@Sun.COM 
491*7934SMark.Phalan@Sun.COM     *padata_context = ret;
492*7934SMark.Phalan@Sun.COM 
493*7934SMark.Phalan@Sun.COM     return 0;
494*7934SMark.Phalan@Sun.COM }
495*7934SMark.Phalan@Sun.COM 
496*7934SMark.Phalan@Sun.COM /*
497*7934SMark.Phalan@Sun.COM  * The free_padata_context function frees any context information pointers
498*7934SMark.Phalan@Sun.COM  * which the check_padata() function created but which weren't already cleaned
499*7934SMark.Phalan@Sun.COM  * up by return_padata().
500*7934SMark.Phalan@Sun.COM  */
501*7934SMark.Phalan@Sun.COM krb5_error_code
502*7934SMark.Phalan@Sun.COM free_padata_context(krb5_context kcontext, void **padata_context)
503*7934SMark.Phalan@Sun.COM {
504*7934SMark.Phalan@Sun.COM     struct request_pa_context *context;
505*7934SMark.Phalan@Sun.COM     krb5_preauth_systems *preauth_system;
506*7934SMark.Phalan@Sun.COM     void **pctx, *mctx;
507*7934SMark.Phalan@Sun.COM     int i;
508*7934SMark.Phalan@Sun.COM 
509*7934SMark.Phalan@Sun.COM     if (padata_context == NULL)
510*7934SMark.Phalan@Sun.COM 	return 0;
511*7934SMark.Phalan@Sun.COM 
512*7934SMark.Phalan@Sun.COM     context = *padata_context;
513*7934SMark.Phalan@Sun.COM 
514*7934SMark.Phalan@Sun.COM     for (i = 0; i < context->n_contexts; i++) {
515*7934SMark.Phalan@Sun.COM 	if (context->contexts[i].pa_context != NULL) {
516*7934SMark.Phalan@Sun.COM 	    preauth_system = context->contexts[i].pa_system;
517*7934SMark.Phalan@Sun.COM 	    mctx = preauth_system->plugin_context;
518*7934SMark.Phalan@Sun.COM 	    if (preauth_system->free_pa_reqctx != NULL) {
519*7934SMark.Phalan@Sun.COM 		pctx = &context->contexts[i].pa_context;
520*7934SMark.Phalan@Sun.COM 		(*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx);
521*7934SMark.Phalan@Sun.COM 	    }
522*7934SMark.Phalan@Sun.COM 	    context->contexts[i].pa_context = NULL;
523*7934SMark.Phalan@Sun.COM 	}
524*7934SMark.Phalan@Sun.COM     }
525*7934SMark.Phalan@Sun.COM 
526*7934SMark.Phalan@Sun.COM     free(context->contexts);
527*7934SMark.Phalan@Sun.COM     free(context);
528*7934SMark.Phalan@Sun.COM 
529*7934SMark.Phalan@Sun.COM     return 0;
530*7934SMark.Phalan@Sun.COM }
531*7934SMark.Phalan@Sun.COM 
532*7934SMark.Phalan@Sun.COM /* Retrieve a specified tl_data item from the given entry, and return its
533*7934SMark.Phalan@Sun.COM  * contents in a new krb5_data, which must be freed by the caller. */
534*7934SMark.Phalan@Sun.COM static krb5_error_code
535*7934SMark.Phalan@Sun.COM get_entry_tl_data(krb5_context context, krb5_db_entry *entry,
536*7934SMark.Phalan@Sun.COM 		  krb5_int16 tl_data_type, krb5_data **result)
537*7934SMark.Phalan@Sun.COM {
538*7934SMark.Phalan@Sun.COM     krb5_tl_data *tl;
539*7934SMark.Phalan@Sun.COM     for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
540*7934SMark.Phalan@Sun.COM 	if (tl->tl_data_type == tl_data_type) {
541*7934SMark.Phalan@Sun.COM 	    *result = malloc(sizeof(krb5_data));
542*7934SMark.Phalan@Sun.COM 	    if (*result == NULL) {
543*7934SMark.Phalan@Sun.COM 		return ENOMEM;
544*7934SMark.Phalan@Sun.COM 	    }
545*7934SMark.Phalan@Sun.COM 	    (*result)->magic = KV5M_DATA;
546*7934SMark.Phalan@Sun.COM 	    (*result)->data = malloc(tl->tl_data_length);
547*7934SMark.Phalan@Sun.COM 	    if ((*result)->data == NULL) {
548*7934SMark.Phalan@Sun.COM 		free(*result);
549*7934SMark.Phalan@Sun.COM 		*result = NULL;
550*7934SMark.Phalan@Sun.COM 		return ENOMEM;
551*7934SMark.Phalan@Sun.COM 	    }
552*7934SMark.Phalan@Sun.COM 	    memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length);
553*7934SMark.Phalan@Sun.COM 	    return 0;
554*7934SMark.Phalan@Sun.COM 	}
555*7934SMark.Phalan@Sun.COM     }
556*7934SMark.Phalan@Sun.COM     return ENOENT;
557*7934SMark.Phalan@Sun.COM }
558*7934SMark.Phalan@Sun.COM 
559*7934SMark.Phalan@Sun.COM /*
560*7934SMark.Phalan@Sun.COM  * Retrieve a specific piece of information pertaining to the entry or the
561*7934SMark.Phalan@Sun.COM  * request and return it in a new krb5_data item which the caller must free.
562*7934SMark.Phalan@Sun.COM  *
563*7934SMark.Phalan@Sun.COM  * This may require massaging data into a contrived format, but it will
564*7934SMark.Phalan@Sun.COM  * hopefully keep us from having to reveal library-internal functions to
565*7934SMark.Phalan@Sun.COM  * modules.
566*7934SMark.Phalan@Sun.COM  */
567*7934SMark.Phalan@Sun.COM static krb5_error_code
568*7934SMark.Phalan@Sun.COM get_entry_data(krb5_context context,
569*7934SMark.Phalan@Sun.COM 	       krb5_kdc_req *request, krb5_db_entry *entry,
570*7934SMark.Phalan@Sun.COM 	       krb5_int32  type,
571*7934SMark.Phalan@Sun.COM 	       krb5_data **result)
572*7934SMark.Phalan@Sun.COM {
573*7934SMark.Phalan@Sun.COM     int i, k;
574*7934SMark.Phalan@Sun.COM     krb5_data *ret;
575*7934SMark.Phalan@Sun.COM     krb5_deltat *delta;
576*7934SMark.Phalan@Sun.COM     krb5_keyblock *keys;
577*7934SMark.Phalan@Sun.COM     krb5_key_data *entry_key;
578*7934SMark.Phalan@Sun.COM 
579*7934SMark.Phalan@Sun.COM     switch (type) {
580*7934SMark.Phalan@Sun.COM     case krb5plugin_preauth_entry_request_certificate:
581*7934SMark.Phalan@Sun.COM 	return get_entry_tl_data(context, entry,
582*7934SMark.Phalan@Sun.COM 				 KRB5_TL_USER_CERTIFICATE, result);
583*7934SMark.Phalan@Sun.COM 	break;
584*7934SMark.Phalan@Sun.COM     case krb5plugin_preauth_entry_max_time_skew:
585*7934SMark.Phalan@Sun.COM 	ret = malloc(sizeof(krb5_data));
586*7934SMark.Phalan@Sun.COM 	if (ret == NULL)
587*7934SMark.Phalan@Sun.COM 	    return ENOMEM;
588*7934SMark.Phalan@Sun.COM 	delta = malloc(sizeof(krb5_deltat));
589*7934SMark.Phalan@Sun.COM 	if (delta == NULL) {
590*7934SMark.Phalan@Sun.COM 	    free(ret);
591*7934SMark.Phalan@Sun.COM 	    return ENOMEM;
592*7934SMark.Phalan@Sun.COM 	}
593*7934SMark.Phalan@Sun.COM 	*delta = context->clockskew;
594*7934SMark.Phalan@Sun.COM 	ret->data = (char *) delta;
595*7934SMark.Phalan@Sun.COM 	ret->length = sizeof(*delta);
596*7934SMark.Phalan@Sun.COM 	*result = ret;
597*7934SMark.Phalan@Sun.COM 	return 0;
598*7934SMark.Phalan@Sun.COM 	break;
599*7934SMark.Phalan@Sun.COM     case krb5plugin_preauth_keys:
600*7934SMark.Phalan@Sun.COM 	ret = malloc(sizeof(krb5_data));
601*7934SMark.Phalan@Sun.COM 	if (ret == NULL)
602*7934SMark.Phalan@Sun.COM 	    return ENOMEM;
603*7934SMark.Phalan@Sun.COM 	keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1));
604*7934SMark.Phalan@Sun.COM 	if (keys == NULL) {
605*7934SMark.Phalan@Sun.COM 	    free(ret);
606*7934SMark.Phalan@Sun.COM 	    return ENOMEM;
607*7934SMark.Phalan@Sun.COM 	}
608*7934SMark.Phalan@Sun.COM 	ret->data = (char *) keys;
609*7934SMark.Phalan@Sun.COM 	ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
610*7934SMark.Phalan@Sun.COM 	memset(ret->data, 0, ret->length);
611*7934SMark.Phalan@Sun.COM 	k = 0;
612*7934SMark.Phalan@Sun.COM 	for (i = 0; i < request->nktypes; i++) {
613*7934SMark.Phalan@Sun.COM 	    entry_key = NULL;
614*7934SMark.Phalan@Sun.COM 	    if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
615*7934SMark.Phalan@Sun.COM 				      -1, 0, &entry_key) != 0)
616*7934SMark.Phalan@Sun.COM 		continue;
617*7934SMark.Phalan@Sun.COM 	    if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
618*7934SMark.Phalan@Sun.COM 					    entry_key, &keys[k], NULL) != 0) {
619*7934SMark.Phalan@Sun.COM 		if (keys[k].contents != NULL)
620*7934SMark.Phalan@Sun.COM 		    krb5_free_keyblock_contents(context, &keys[k]);
621*7934SMark.Phalan@Sun.COM 		memset(&keys[k], 0, sizeof(keys[k]));
622*7934SMark.Phalan@Sun.COM 		continue;
623*7934SMark.Phalan@Sun.COM 	    }
624*7934SMark.Phalan@Sun.COM 	    k++;
625*7934SMark.Phalan@Sun.COM 	}
626*7934SMark.Phalan@Sun.COM 	if (k > 0) {
627*7934SMark.Phalan@Sun.COM 	    *result = ret;
628*7934SMark.Phalan@Sun.COM 	    return 0;
629*7934SMark.Phalan@Sun.COM 	} else {
630*7934SMark.Phalan@Sun.COM 	    free(keys);
631*7934SMark.Phalan@Sun.COM 	    free(ret);
632*7934SMark.Phalan@Sun.COM 	}
633*7934SMark.Phalan@Sun.COM 	break;
634*7934SMark.Phalan@Sun.COM     case krb5plugin_preauth_request_body:
635*7934SMark.Phalan@Sun.COM 	ret = NULL;
636*7934SMark.Phalan@Sun.COM 	encode_krb5_kdc_req_body(request, &ret);
637*7934SMark.Phalan@Sun.COM 	if (ret != NULL) {
638*7934SMark.Phalan@Sun.COM 	    *result = ret;
639*7934SMark.Phalan@Sun.COM 	    return 0;
640*7934SMark.Phalan@Sun.COM 	}
641*7934SMark.Phalan@Sun.COM 	return ASN1_PARSE_ERROR;
642*7934SMark.Phalan@Sun.COM 	break;
643*7934SMark.Phalan@Sun.COM     default:
644*7934SMark.Phalan@Sun.COM 	break;
645*7934SMark.Phalan@Sun.COM     }
646*7934SMark.Phalan@Sun.COM     return ENOENT;
647*7934SMark.Phalan@Sun.COM }
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate static krb5_error_code
6500Sstevel@tonic-gate find_pa_system(int type, krb5_preauth_systems **preauth)
6510Sstevel@tonic-gate {
652*7934SMark.Phalan@Sun.COM     krb5_preauth_systems *ap;
653*7934SMark.Phalan@Sun.COM 
654*7934SMark.Phalan@Sun.COM     ap = preauth_systems ? preauth_systems : static_preauth_systems;
6550Sstevel@tonic-gate     while ((ap->type != -1) && (ap->type != type))
6560Sstevel@tonic-gate 	ap++;
6570Sstevel@tonic-gate     if (ap->type == -1)
6580Sstevel@tonic-gate 	return(KRB5_PREAUTH_BAD_TYPE);
6590Sstevel@tonic-gate     *preauth = ap;
6600Sstevel@tonic-gate     return 0;
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate 
663*7934SMark.Phalan@Sun.COM static krb5_error_code
664*7934SMark.Phalan@Sun.COM find_pa_context(krb5_preauth_systems *pa_sys,
665*7934SMark.Phalan@Sun.COM 		struct request_pa_context *context,
666*7934SMark.Phalan@Sun.COM 		void ***pa_context)
667*7934SMark.Phalan@Sun.COM {
668*7934SMark.Phalan@Sun.COM     int i;
669*7934SMark.Phalan@Sun.COM 
670*7934SMark.Phalan@Sun.COM     *pa_context = 0;
671*7934SMark.Phalan@Sun.COM 
672*7934SMark.Phalan@Sun.COM     if (context == NULL)
673*7934SMark.Phalan@Sun.COM 	return KRB5KRB_ERR_GENERIC;
674*7934SMark.Phalan@Sun.COM 
675*7934SMark.Phalan@Sun.COM     for (i = 0; i < context->n_contexts; i++) {
676*7934SMark.Phalan@Sun.COM 	if (context->contexts[i].pa_system == pa_sys) {
677*7934SMark.Phalan@Sun.COM 	    *pa_context = &context->contexts[i].pa_context;
678*7934SMark.Phalan@Sun.COM 	    return 0;
679*7934SMark.Phalan@Sun.COM 	}
680*7934SMark.Phalan@Sun.COM     }
681*7934SMark.Phalan@Sun.COM 
682*7934SMark.Phalan@Sun.COM     return KRB5KRB_ERR_GENERIC;
683*7934SMark.Phalan@Sun.COM }
684*7934SMark.Phalan@Sun.COM 
685*7934SMark.Phalan@Sun.COM /*
686*7934SMark.Phalan@Sun.COM  * Create a list of indices into the preauth_systems array, sorted by order of
687*7934SMark.Phalan@Sun.COM  * preference.
688*7934SMark.Phalan@Sun.COM  */
689*7934SMark.Phalan@Sun.COM static krb5_boolean
690*7934SMark.Phalan@Sun.COM pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
691*7934SMark.Phalan@Sun.COM {
692*7934SMark.Phalan@Sun.COM     while (*pa_data != NULL) {
693*7934SMark.Phalan@Sun.COM 	if ((*pa_data)->pa_type == pa_type)
694*7934SMark.Phalan@Sun.COM 	    return TRUE;
695*7934SMark.Phalan@Sun.COM 	pa_data++;
696*7934SMark.Phalan@Sun.COM     }
697*7934SMark.Phalan@Sun.COM     return FALSE;
698*7934SMark.Phalan@Sun.COM }
699*7934SMark.Phalan@Sun.COM static void
700*7934SMark.Phalan@Sun.COM sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
701*7934SMark.Phalan@Sun.COM {
702*7934SMark.Phalan@Sun.COM     int i, j, k, n_repliers, n_key_replacers;
703*7934SMark.Phalan@Sun.COM 
704*7934SMark.Phalan@Sun.COM     /* First, set up the default order. */
705*7934SMark.Phalan@Sun.COM     i = 0;
706*7934SMark.Phalan@Sun.COM     for (j = 0; j < n_preauth_systems; j++) {
707*7934SMark.Phalan@Sun.COM         if (preauth_systems[j].return_padata != NULL)
708*7934SMark.Phalan@Sun.COM 	    pa_order[i++] = j;
709*7934SMark.Phalan@Sun.COM     }
710*7934SMark.Phalan@Sun.COM     n_repliers = i;
711*7934SMark.Phalan@Sun.COM     pa_order[n_repliers] = -1;
712*7934SMark.Phalan@Sun.COM 
713*7934SMark.Phalan@Sun.COM     /* Reorder so that PA_REPLACES_KEY modules are listed first. */
714*7934SMark.Phalan@Sun.COM     for (i = 0; i < n_repliers; i++) {
715*7934SMark.Phalan@Sun.COM 	/* If this module replaces the key, then it's okay to leave it where it
716*7934SMark.Phalan@Sun.COM 	 * is in the order. */
717*7934SMark.Phalan@Sun.COM 	if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
718*7934SMark.Phalan@Sun.COM 	    continue;
719*7934SMark.Phalan@Sun.COM 	/* If not, search for a module which does, and swap in the first one we
720*7934SMark.Phalan@Sun.COM 	 * find. */
721*7934SMark.Phalan@Sun.COM         for (j = i + 1; j < n_repliers; j++) {
722*7934SMark.Phalan@Sun.COM 	    if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
723*7934SMark.Phalan@Sun.COM                 k = pa_order[j];
724*7934SMark.Phalan@Sun.COM 		pa_order[j] = pa_order[i];
725*7934SMark.Phalan@Sun.COM 		pa_order[i] = k;
726*7934SMark.Phalan@Sun.COM 		break;
727*7934SMark.Phalan@Sun.COM 	    }
728*7934SMark.Phalan@Sun.COM         }
729*7934SMark.Phalan@Sun.COM     }
730*7934SMark.Phalan@Sun.COM 
731*7934SMark.Phalan@Sun.COM     if (request->padata != NULL) {
732*7934SMark.Phalan@Sun.COM 	/* Now reorder the subset of modules which replace the key,
733*7934SMark.Phalan@Sun.COM 	 * bubbling those which handle pa_data types provided by the
734*7934SMark.Phalan@Sun.COM 	 * client ahead of the others. */
735*7934SMark.Phalan@Sun.COM 	for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
736*7934SMark.Phalan@Sun.COM 	    continue;
737*7934SMark.Phalan@Sun.COM 	}
738*7934SMark.Phalan@Sun.COM 	n_key_replacers = i;
739*7934SMark.Phalan@Sun.COM 	for (i = 0; i < n_key_replacers; i++) {
740*7934SMark.Phalan@Sun.COM 	    if (pa_list_includes(request->padata,
741*7934SMark.Phalan@Sun.COM 				preauth_systems[pa_order[i]].type))
742*7934SMark.Phalan@Sun.COM 		continue;
743*7934SMark.Phalan@Sun.COM 	    for (j = i + 1; j < n_key_replacers; j++) {
744*7934SMark.Phalan@Sun.COM 		if (pa_list_includes(request->padata,
745*7934SMark.Phalan@Sun.COM 				    preauth_systems[pa_order[j]].type)) {
746*7934SMark.Phalan@Sun.COM 		    k = pa_order[j];
747*7934SMark.Phalan@Sun.COM 		    pa_order[j] = pa_order[i];
748*7934SMark.Phalan@Sun.COM 		    pa_order[i] = k;
749*7934SMark.Phalan@Sun.COM 		    break;
750*7934SMark.Phalan@Sun.COM 		}
751*7934SMark.Phalan@Sun.COM 	    }
752*7934SMark.Phalan@Sun.COM 	}
753*7934SMark.Phalan@Sun.COM     }
754*7934SMark.Phalan@Sun.COM #ifdef DEBUG
755*7934SMark.Phalan@Sun.COM     krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
756*7934SMark.Phalan@Sun.COM     for (i = 0; i < n_preauth_systems; i++) {
757*7934SMark.Phalan@Sun.COM 	if (preauth_systems[i].return_padata != NULL)
758*7934SMark.Phalan@Sun.COM             krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
759*7934SMark.Phalan@Sun.COM 			     preauth_systems[i].type);
760*7934SMark.Phalan@Sun.COM     }
761*7934SMark.Phalan@Sun.COM     krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
762*7934SMark.Phalan@Sun.COM     for (i = 0; pa_order[i] != -1; i++) {
763*7934SMark.Phalan@Sun.COM         krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
764*7934SMark.Phalan@Sun.COM 			 preauth_systems[pa_order[i]].name,
765*7934SMark.Phalan@Sun.COM 			 preauth_systems[pa_order[i]].type);
766*7934SMark.Phalan@Sun.COM     }
767*7934SMark.Phalan@Sun.COM #endif
768*7934SMark.Phalan@Sun.COM }
769*7934SMark.Phalan@Sun.COM 
7702881Smp153739 const char *missing_required_preauth(krb5_db_entry *client,
7712881Smp153739 				     krb5_db_entry *server,
7722881Smp153739 				     krb5_enc_tkt_part *enc_tkt_reply)
7730Sstevel@tonic-gate {
7740Sstevel@tonic-gate #if 0
7750Sstevel@tonic-gate     /*
7760Sstevel@tonic-gate      * If this is the pwchange service, and the pre-auth bit is set,
7770Sstevel@tonic-gate      * allow it even if the HW preauth would normally be required.
7780Sstevel@tonic-gate      *
7790Sstevel@tonic-gate      * Sandia national labs wanted this for some strange reason... we
7800Sstevel@tonic-gate      * leave it disabled normally.
7810Sstevel@tonic-gate      */
7820Sstevel@tonic-gate     if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) &&
7830Sstevel@tonic-gate 	isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
7840Sstevel@tonic-gate 	return 0;
7850Sstevel@tonic-gate #endif
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate #ifdef DEBUG
7880Sstevel@tonic-gate     krb5_klog_syslog (LOG_DEBUG,
7890Sstevel@tonic-gate 		      "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
7900Sstevel@tonic-gate 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
7910Sstevel@tonic-gate 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
7920Sstevel@tonic-gate 		      isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
7930Sstevel@tonic-gate 		      isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
7940Sstevel@tonic-gate #endif
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
7970Sstevel@tonic-gate 	 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
7980Sstevel@tonic-gate 	return "NEEDED_PREAUTH";
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
8010Sstevel@tonic-gate 	!isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
8020Sstevel@tonic-gate 	return "NEEDED_HW_PREAUTH";
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate     return 0;
8050Sstevel@tonic-gate }
8060Sstevel@tonic-gate 
8072881Smp153739 void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client,
8082881Smp153739 			   krb5_db_entry *server, krb5_data *e_data)
8090Sstevel@tonic-gate {
8100Sstevel@tonic-gate     int hw_only;
8110Sstevel@tonic-gate     krb5_preauth_systems *ap;
8120Sstevel@tonic-gate     krb5_pa_data **pa_data, **pa;
8130Sstevel@tonic-gate     krb5_data *edat;
8140Sstevel@tonic-gate     krb5_error_code retval;
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate     /* Zero these out in case we need to abort */
8170Sstevel@tonic-gate     e_data->length = 0;
8180Sstevel@tonic-gate     e_data->data = 0;
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate     hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
821*7934SMark.Phalan@Sun.COM     pa_data = malloc(sizeof(krb5_pa_data *) * (n_preauth_systems+1));
8220Sstevel@tonic-gate     if (pa_data == 0)
8230Sstevel@tonic-gate 	return;
824*7934SMark.Phalan@Sun.COM     memset(pa_data, 0, sizeof(krb5_pa_data *) * (n_preauth_systems+1));
8250Sstevel@tonic-gate     pa = pa_data;
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate     for (ap = preauth_systems; ap->type != -1; ap++) {
8280Sstevel@tonic-gate 	if (hw_only && !(ap->flags & PA_HARDWARE))
8290Sstevel@tonic-gate 	    continue;
8300Sstevel@tonic-gate 	if (ap->flags & PA_PSEUDO)
8310Sstevel@tonic-gate 	    continue;
8320Sstevel@tonic-gate 	*pa = malloc(sizeof(krb5_pa_data));
8330Sstevel@tonic-gate 	if (*pa == 0)
8340Sstevel@tonic-gate 	    goto errout;
8350Sstevel@tonic-gate 	memset(*pa, 0, sizeof(krb5_pa_data));
8360Sstevel@tonic-gate 	(*pa)->magic = KV5M_PA_DATA;
8370Sstevel@tonic-gate 	(*pa)->pa_type = ap->type;
8380Sstevel@tonic-gate 	if (ap->get_edata) {
839*7934SMark.Phalan@Sun.COM 	  retval = (ap->get_edata)(kdc_context, request, client, server,
840*7934SMark.Phalan@Sun.COM 				   get_entry_data, ap->plugin_context, *pa);
8410Sstevel@tonic-gate 	  if (retval) {
8420Sstevel@tonic-gate 	    /* just failed on this type, continue */
8430Sstevel@tonic-gate 	    free(*pa);
8440Sstevel@tonic-gate 	    *pa = 0;
8450Sstevel@tonic-gate 	    continue;
8460Sstevel@tonic-gate 	  }
8470Sstevel@tonic-gate 	}
8480Sstevel@tonic-gate 	pa++;
8490Sstevel@tonic-gate     }
8500Sstevel@tonic-gate     if (pa_data[0] == 0) {
8510Sstevel@tonic-gate 	krb5_klog_syslog (LOG_INFO,
8520Sstevel@tonic-gate 			  "%spreauth required but hint list is empty",
8530Sstevel@tonic-gate 			  hw_only ? "hw" : "");
8540Sstevel@tonic-gate     }
8550Sstevel@tonic-gate     retval = encode_krb5_padata_sequence((const krb5_pa_data **) pa_data,
8560Sstevel@tonic-gate 					 &edat);
8570Sstevel@tonic-gate     if (retval)
8580Sstevel@tonic-gate 	goto errout;
8590Sstevel@tonic-gate     *e_data = *edat;
8600Sstevel@tonic-gate     free(edat);
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate errout:
8630Sstevel@tonic-gate     krb5_free_pa_data(kdc_context, pa_data);
8640Sstevel@tonic-gate     return;
8650Sstevel@tonic-gate }
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate /*
868*7934SMark.Phalan@Sun.COM  * Add authorization data returned from preauth modules to the ticket
869*7934SMark.Phalan@Sun.COM  * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
870*7934SMark.Phalan@Sun.COM  */
871*7934SMark.Phalan@Sun.COM static krb5_error_code
872*7934SMark.Phalan@Sun.COM add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
873*7934SMark.Phalan@Sun.COM {
874*7934SMark.Phalan@Sun.COM     krb5_authdata **newad;
875*7934SMark.Phalan@Sun.COM     int oldones, newones;
876*7934SMark.Phalan@Sun.COM     int i;
877*7934SMark.Phalan@Sun.COM 
878*7934SMark.Phalan@Sun.COM     if (enc_tkt_part == NULL || ad == NULL)
879*7934SMark.Phalan@Sun.COM 	return EINVAL;
880*7934SMark.Phalan@Sun.COM 
881*7934SMark.Phalan@Sun.COM     for (newones = 0; ad[newones] != NULL; newones++);
882*7934SMark.Phalan@Sun.COM     if (newones == 0)
883*7934SMark.Phalan@Sun.COM 	return 0;   /* nothing to add */
884*7934SMark.Phalan@Sun.COM 
885*7934SMark.Phalan@Sun.COM     if (enc_tkt_part->authorization_data == NULL)
886*7934SMark.Phalan@Sun.COM 	oldones = 0;
887*7934SMark.Phalan@Sun.COM     else
888*7934SMark.Phalan@Sun.COM 	for (oldones = 0;
889*7934SMark.Phalan@Sun.COM 	     enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
890*7934SMark.Phalan@Sun.COM 
891*7934SMark.Phalan@Sun.COM     newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
892*7934SMark.Phalan@Sun.COM     if (newad == NULL)
893*7934SMark.Phalan@Sun.COM 	return ENOMEM;
894*7934SMark.Phalan@Sun.COM 
895*7934SMark.Phalan@Sun.COM     /* Copy any existing pointers */
896*7934SMark.Phalan@Sun.COM     for (i = 0; i < oldones; i++)
897*7934SMark.Phalan@Sun.COM 	newad[i] = enc_tkt_part->authorization_data[i];
898*7934SMark.Phalan@Sun.COM 
899*7934SMark.Phalan@Sun.COM     /* Add the new ones */
900*7934SMark.Phalan@Sun.COM     for (i = 0; i < newones; i++)
901*7934SMark.Phalan@Sun.COM 	newad[oldones+i] = ad[i];
902*7934SMark.Phalan@Sun.COM 
903*7934SMark.Phalan@Sun.COM     /* Terminate the new list */
904*7934SMark.Phalan@Sun.COM     newad[oldones+i] = NULL;
905*7934SMark.Phalan@Sun.COM 
906*7934SMark.Phalan@Sun.COM     /* Free any existing list */
907*7934SMark.Phalan@Sun.COM     if (enc_tkt_part->authorization_data != NULL)
908*7934SMark.Phalan@Sun.COM 	free(enc_tkt_part->authorization_data);
909*7934SMark.Phalan@Sun.COM 
910*7934SMark.Phalan@Sun.COM     /* Install our new list */
911*7934SMark.Phalan@Sun.COM     enc_tkt_part->authorization_data = newad;
912*7934SMark.Phalan@Sun.COM 
913*7934SMark.Phalan@Sun.COM     return 0;
914*7934SMark.Phalan@Sun.COM }
915*7934SMark.Phalan@Sun.COM 
916*7934SMark.Phalan@Sun.COM /*
9170Sstevel@tonic-gate  * This routine is called to verify the preauthentication information
9180Sstevel@tonic-gate  * for a V5 request.
919*7934SMark.Phalan@Sun.COM  *
9200Sstevel@tonic-gate  * Returns 0 if the pre-authentication is valid, non-zero to indicate
9210Sstevel@tonic-gate  * an error code of some sort.
9220Sstevel@tonic-gate  */
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate krb5_error_code
925*7934SMark.Phalan@Sun.COM check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
926*7934SMark.Phalan@Sun.COM 	      krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
927*7934SMark.Phalan@Sun.COM 	      void **padata_context, krb5_data *e_data)
9280Sstevel@tonic-gate {
9290Sstevel@tonic-gate     krb5_error_code retval = 0;
9300Sstevel@tonic-gate     krb5_pa_data **padata;
9310Sstevel@tonic-gate     krb5_preauth_systems *pa_sys;
932*7934SMark.Phalan@Sun.COM     void **pa_context;
933*7934SMark.Phalan@Sun.COM     krb5_data *pa_e_data = NULL, *tmp_e_data = NULL;
934*7934SMark.Phalan@Sun.COM     int	pa_ok = 0, pa_found = 0;
935*7934SMark.Phalan@Sun.COM     krb5_error_code saved_retval = 0;
936*7934SMark.Phalan@Sun.COM     int use_saved_retval = 0;
937*7934SMark.Phalan@Sun.COM     const char *emsg;
938*7934SMark.Phalan@Sun.COM     krb5_authdata **tmp_authz_data = NULL;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate     if (request->padata == 0)
9410Sstevel@tonic-gate 	return 0;
9420Sstevel@tonic-gate 
943*7934SMark.Phalan@Sun.COM     if (make_padata_context(context, padata_context) != 0) {
944*7934SMark.Phalan@Sun.COM 	return KRB5KRB_ERR_GENERIC;
945*7934SMark.Phalan@Sun.COM     }
946*7934SMark.Phalan@Sun.COM 
9470Sstevel@tonic-gate #ifdef DEBUG
9480Sstevel@tonic-gate     krb5_klog_syslog (LOG_DEBUG, "checking padata");
9490Sstevel@tonic-gate #endif
9500Sstevel@tonic-gate     for (padata = request->padata; *padata; padata++) {
9510Sstevel@tonic-gate #ifdef DEBUG
9520Sstevel@tonic-gate 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type);
9530Sstevel@tonic-gate #endif
9540Sstevel@tonic-gate 	if (find_pa_system((*padata)->pa_type, &pa_sys))
9550Sstevel@tonic-gate 	    continue;
956*7934SMark.Phalan@Sun.COM 	if (find_pa_context(pa_sys, *padata_context, &pa_context))
957*7934SMark.Phalan@Sun.COM 	    continue;
9580Sstevel@tonic-gate #ifdef DEBUG
9590Sstevel@tonic-gate 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
9600Sstevel@tonic-gate #endif
9610Sstevel@tonic-gate 	if (pa_sys->verify_padata == 0)
9620Sstevel@tonic-gate 	    continue;
9630Sstevel@tonic-gate 	pa_found++;
964*7934SMark.Phalan@Sun.COM 	retval = pa_sys->verify_padata(context, client, req_pkt, request,
965*7934SMark.Phalan@Sun.COM 				       enc_tkt_reply, *padata,
966*7934SMark.Phalan@Sun.COM 				       get_entry_data, pa_sys->plugin_context,
967*7934SMark.Phalan@Sun.COM 				       pa_context, &tmp_e_data, &tmp_authz_data);
9680Sstevel@tonic-gate 	if (retval) {
969*7934SMark.Phalan@Sun.COM 	    emsg = krb5_get_error_message (context, retval);
9700Sstevel@tonic-gate 	    krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
971*7934SMark.Phalan@Sun.COM 			      pa_sys->name, emsg);
972*7934SMark.Phalan@Sun.COM 	    krb5_free_error_message (context, emsg);
973*7934SMark.Phalan@Sun.COM 	    /* Ignore authorization data returned from modules that fail */
974*7934SMark.Phalan@Sun.COM 	    if (tmp_authz_data != NULL) {
975*7934SMark.Phalan@Sun.COM 		krb5_free_authdata(context, tmp_authz_data);
976*7934SMark.Phalan@Sun.COM 		tmp_authz_data = NULL;
977*7934SMark.Phalan@Sun.COM 	    }
9780Sstevel@tonic-gate 	    if (pa_sys->flags & PA_REQUIRED) {
979*7934SMark.Phalan@Sun.COM 		/* free up any previous edata we might have been saving */
980*7934SMark.Phalan@Sun.COM 		if (pa_e_data != NULL)
981*7934SMark.Phalan@Sun.COM 		    krb5_free_data(context, pa_e_data);
982*7934SMark.Phalan@Sun.COM 		pa_e_data = tmp_e_data;
983*7934SMark.Phalan@Sun.COM 		tmp_e_data = NULL;
984*7934SMark.Phalan@Sun.COM 		use_saved_retval = 0; /* Make sure we use the current retval */
9850Sstevel@tonic-gate 		pa_ok = 0;
9860Sstevel@tonic-gate 		break;
9870Sstevel@tonic-gate 	    }
988*7934SMark.Phalan@Sun.COM 	    /*
989*7934SMark.Phalan@Sun.COM 	     * We'll return edata from either the first PA_REQUIRED module
990*7934SMark.Phalan@Sun.COM 	     * that fails, or the first non-PA_REQUIRED module that fails.
991*7934SMark.Phalan@Sun.COM 	     * Hang on to edata from the first non-PA_REQUIRED module.
992*7934SMark.Phalan@Sun.COM 	     * If we've already got one saved, simply discard this one.
993*7934SMark.Phalan@Sun.COM 	     */
994*7934SMark.Phalan@Sun.COM 	    if (tmp_e_data != NULL) {
995*7934SMark.Phalan@Sun.COM 		if (pa_e_data == NULL) {
996*7934SMark.Phalan@Sun.COM 		    /* save the first error code and e-data */
997*7934SMark.Phalan@Sun.COM 		    pa_e_data = tmp_e_data;
998*7934SMark.Phalan@Sun.COM 		    tmp_e_data = NULL;
999*7934SMark.Phalan@Sun.COM 		    saved_retval = retval;
1000*7934SMark.Phalan@Sun.COM 		    use_saved_retval = 1;
1001*7934SMark.Phalan@Sun.COM 		} else {
1002*7934SMark.Phalan@Sun.COM 		    /* discard this extra e-data from non-PA_REQUIRED module */
1003*7934SMark.Phalan@Sun.COM 		    krb5_free_data(context, tmp_e_data);
1004*7934SMark.Phalan@Sun.COM 		    tmp_e_data = NULL;
1005*7934SMark.Phalan@Sun.COM 		}
1006*7934SMark.Phalan@Sun.COM 	    }
10070Sstevel@tonic-gate 	} else {
10080Sstevel@tonic-gate #ifdef DEBUG
10090Sstevel@tonic-gate 	    krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
10100Sstevel@tonic-gate #endif
1011*7934SMark.Phalan@Sun.COM 	    /* Ignore any edata returned on success */
1012*7934SMark.Phalan@Sun.COM 	    if (tmp_e_data != NULL) {
1013*7934SMark.Phalan@Sun.COM 	        krb5_free_data(context, tmp_e_data);
1014*7934SMark.Phalan@Sun.COM 		tmp_e_data = NULL;
1015*7934SMark.Phalan@Sun.COM 	    }
1016*7934SMark.Phalan@Sun.COM 	    /* Add any authorization data to the ticket */
1017*7934SMark.Phalan@Sun.COM 	    if (tmp_authz_data != NULL) {
1018*7934SMark.Phalan@Sun.COM 		add_authorization_data(enc_tkt_reply, tmp_authz_data);
1019*7934SMark.Phalan@Sun.COM 		free(tmp_authz_data);
1020*7934SMark.Phalan@Sun.COM 		tmp_authz_data = NULL;
1021*7934SMark.Phalan@Sun.COM 	    }
10220Sstevel@tonic-gate 	    pa_ok = 1;
1023*7934SMark.Phalan@Sun.COM 	    if (pa_sys->flags & PA_SUFFICIENT)
10240Sstevel@tonic-gate 		break;
10250Sstevel@tonic-gate 	}
10260Sstevel@tonic-gate     }
1027*7934SMark.Phalan@Sun.COM 
1028*7934SMark.Phalan@Sun.COM     /* Don't bother copying and returning e-data on success */
1029*7934SMark.Phalan@Sun.COM     if (pa_ok && pa_e_data != NULL) {
1030*7934SMark.Phalan@Sun.COM 	krb5_free_data(context, pa_e_data);
1031*7934SMark.Phalan@Sun.COM 	pa_e_data = NULL;
1032*7934SMark.Phalan@Sun.COM     }
1033*7934SMark.Phalan@Sun.COM     /* Return any e-data from the preauth that caused us to exit the loop */
1034*7934SMark.Phalan@Sun.COM     if (pa_e_data != NULL) {
1035*7934SMark.Phalan@Sun.COM 	e_data->data = malloc(pa_e_data->length);
1036*7934SMark.Phalan@Sun.COM 	if (e_data->data == NULL) {
1037*7934SMark.Phalan@Sun.COM 	    krb5_free_data(context, pa_e_data);
1038*7934SMark.Phalan@Sun.COM 	    /* Solaris Kerberos */
1039*7934SMark.Phalan@Sun.COM 	    return ENOMEM;
1040*7934SMark.Phalan@Sun.COM 	}
1041*7934SMark.Phalan@Sun.COM 	memcpy(e_data->data, pa_e_data->data, pa_e_data->length);
1042*7934SMark.Phalan@Sun.COM 	e_data->length = pa_e_data->length;
1043*7934SMark.Phalan@Sun.COM 	krb5_free_data(context, pa_e_data);
1044*7934SMark.Phalan@Sun.COM 	pa_e_data = NULL;
1045*7934SMark.Phalan@Sun.COM 	if (use_saved_retval != 0)
1046*7934SMark.Phalan@Sun.COM 	    retval = saved_retval;
1047*7934SMark.Phalan@Sun.COM     }
1048*7934SMark.Phalan@Sun.COM 
10490Sstevel@tonic-gate     if (pa_ok)
10500Sstevel@tonic-gate 	return 0;
10510Sstevel@tonic-gate 
10520Sstevel@tonic-gate     /* pa system was not found, but principal doesn't require preauth */
10530Sstevel@tonic-gate     if (!pa_found &&
1054*7934SMark.Phalan@Sun.COM 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1055*7934SMark.Phalan@Sun.COM 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
10560Sstevel@tonic-gate        return 0;
10570Sstevel@tonic-gate 
1058*7934SMark.Phalan@Sun.COM     if (!pa_found) {
1059*7934SMark.Phalan@Sun.COM 	emsg = krb5_get_error_message(context, retval);
1060*7934SMark.Phalan@Sun.COM 	krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
1061*7934SMark.Phalan@Sun.COM 	krb5_free_error_message(context, emsg);
1062*7934SMark.Phalan@Sun.COM     }
1063*7934SMark.Phalan@Sun.COM     /* The following switch statement allows us
1064*7934SMark.Phalan@Sun.COM      * to return some preauth system errors back to the client.
1065*7934SMark.Phalan@Sun.COM      */
1066*7934SMark.Phalan@Sun.COM     switch(retval) {
1067*7934SMark.Phalan@Sun.COM     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
10682881Smp153739     case KRB5KRB_AP_ERR_SKEW:
1069*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_ETYPE_NOSUPP:
1070*7934SMark.Phalan@Sun.COM     /* rfc 4556 */
1071*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
1072*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_INVALID_SIG:
1073*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
1074*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
1075*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_INVALID_CERTIFICATE:
1076*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_REVOKED_CERTIFICATE:
1077*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
1078*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
1079*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
1080*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
1081*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
1082*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
1083*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
1084*7934SMark.Phalan@Sun.COM     /* earlier drafts of what became rfc 4556 */
1085*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
1086*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_KDC_NOT_TRUSTED:
1087*7934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
1088*7934SMark.Phalan@Sun.COM     /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
1089*7934SMark.Phalan@Sun.COM     /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
10902881Smp153739 	return retval;
10912881Smp153739     default:
10922881Smp153739 	return KRB5KDC_ERR_PREAUTH_FAILED;
10930Sstevel@tonic-gate     }
10940Sstevel@tonic-gate }
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate /*
10970Sstevel@tonic-gate  * return_padata creates any necessary preauthentication
10980Sstevel@tonic-gate  * structures which should be returned by the KDC to the client
10990Sstevel@tonic-gate  */
11000Sstevel@tonic-gate krb5_error_code
1101*7934SMark.Phalan@Sun.COM return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
11022881Smp153739 	      krb5_kdc_req *request, krb5_kdc_rep *reply,
1103*7934SMark.Phalan@Sun.COM 	      krb5_key_data *client_key, krb5_keyblock *encrypting_key,
1104*7934SMark.Phalan@Sun.COM 	      void **padata_context)
11050Sstevel@tonic-gate {
11060Sstevel@tonic-gate     krb5_error_code		retval;
11070Sstevel@tonic-gate     krb5_pa_data **		padata;
11080Sstevel@tonic-gate     krb5_pa_data **		send_pa_list;
11090Sstevel@tonic-gate     krb5_pa_data **		send_pa;
11100Sstevel@tonic-gate     krb5_pa_data *		pa = 0;
11110Sstevel@tonic-gate     krb5_preauth_systems *	ap;
1112*7934SMark.Phalan@Sun.COM     int *			pa_order;
1113*7934SMark.Phalan@Sun.COM     int *			pa_type;
11140Sstevel@tonic-gate     int 			size = 0;
1115*7934SMark.Phalan@Sun.COM     void **			pa_context;
1116*7934SMark.Phalan@Sun.COM     krb5_boolean		key_modified;
1117*7934SMark.Phalan@Sun.COM     krb5_keyblock		original_key;
1118*7934SMark.Phalan@Sun.COM     if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) {
1119*7934SMark.Phalan@Sun.COM 	return KRB5KRB_ERR_GENERIC;
1120*7934SMark.Phalan@Sun.COM     }
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate     for (ap = preauth_systems; ap->type != -1; ap++) {
11230Sstevel@tonic-gate 	if (ap->return_padata)
11240Sstevel@tonic-gate 	    size++;
11250Sstevel@tonic-gate     }
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate     if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
11280Sstevel@tonic-gate 	return ENOMEM;
1129*7934SMark.Phalan@Sun.COM     if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
1130*7934SMark.Phalan@Sun.COM 	free(send_pa_list);
1131*7934SMark.Phalan@Sun.COM 	return ENOMEM;
1132*7934SMark.Phalan@Sun.COM     }
1133*7934SMark.Phalan@Sun.COM     sort_pa_order(context, request, pa_order);
1134*7934SMark.Phalan@Sun.COM 
1135*7934SMark.Phalan@Sun.COM     retval = krb5_copy_keyblock_contents(context, encrypting_key,
1136*7934SMark.Phalan@Sun.COM 					 &original_key);
1137*7934SMark.Phalan@Sun.COM     if (retval) {
1138*7934SMark.Phalan@Sun.COM 	free(send_pa_list);
1139*7934SMark.Phalan@Sun.COM 	free(pa_order);
1140*7934SMark.Phalan@Sun.COM 	return retval;
1141*7934SMark.Phalan@Sun.COM     }
1142*7934SMark.Phalan@Sun.COM     key_modified = FALSE;
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate     send_pa = send_pa_list;
11450Sstevel@tonic-gate     *send_pa = 0;
1146*7934SMark.Phalan@Sun.COM 
1147*7934SMark.Phalan@Sun.COM     for (pa_type = pa_order; *pa_type != -1; pa_type++) {
1148*7934SMark.Phalan@Sun.COM 	ap = &preauth_systems[*pa_type];
1149*7934SMark.Phalan@Sun.COM         if (!key_modified)
1150*7934SMark.Phalan@Sun.COM 	    if (original_key.enctype != encrypting_key->enctype)
1151*7934SMark.Phalan@Sun.COM                 key_modified = TRUE;
1152*7934SMark.Phalan@Sun.COM         if (!key_modified)
1153*7934SMark.Phalan@Sun.COM 	    if (original_key.length != encrypting_key->length)
1154*7934SMark.Phalan@Sun.COM                 key_modified = TRUE;
1155*7934SMark.Phalan@Sun.COM         if (!key_modified)
1156*7934SMark.Phalan@Sun.COM 	    if (memcmp(original_key.contents, encrypting_key->contents,
1157*7934SMark.Phalan@Sun.COM 		       original_key.length) != 0)
1158*7934SMark.Phalan@Sun.COM                 key_modified = TRUE;
1159*7934SMark.Phalan@Sun.COM 	if (key_modified && (ap->flags & PA_REPLACES_KEY))
1160*7934SMark.Phalan@Sun.COM 	    continue;
11610Sstevel@tonic-gate 	if (ap->return_padata == 0)
11620Sstevel@tonic-gate 	    continue;
1163*7934SMark.Phalan@Sun.COM 	if (find_pa_context(ap, *padata_context, &pa_context))
1164*7934SMark.Phalan@Sun.COM 	    continue;
11650Sstevel@tonic-gate 	pa = 0;
11660Sstevel@tonic-gate 	if (request->padata) {
11670Sstevel@tonic-gate 	    for (padata = request->padata; *padata; padata++) {
11680Sstevel@tonic-gate 		if ((*padata)->pa_type == ap->type) {
11690Sstevel@tonic-gate 		    pa = *padata;
11700Sstevel@tonic-gate 		    break;
11710Sstevel@tonic-gate 		}
11720Sstevel@tonic-gate 	    }
11730Sstevel@tonic-gate 	}
1174*7934SMark.Phalan@Sun.COM 	if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
1175*7934SMark.Phalan@Sun.COM 					client_key, encrypting_key, send_pa,
1176*7934SMark.Phalan@Sun.COM 					get_entry_data, ap->plugin_context,
1177*7934SMark.Phalan@Sun.COM 					pa_context))) {
11780Sstevel@tonic-gate 	    goto cleanup;
1179*7934SMark.Phalan@Sun.COM 	}
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 	if (*send_pa)
11820Sstevel@tonic-gate 	    send_pa++;
11830Sstevel@tonic-gate 	*send_pa = 0;
11840Sstevel@tonic-gate     }
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate     retval = 0;
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate     if (send_pa_list[0]) {
11890Sstevel@tonic-gate 	reply->padata = send_pa_list;
11900Sstevel@tonic-gate 	send_pa_list = 0;
11910Sstevel@tonic-gate     }
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate cleanup:
11940Sstevel@tonic-gate     if (send_pa_list)
11950Sstevel@tonic-gate 	krb5_free_pa_data(context, send_pa_list);
1196*7934SMark.Phalan@Sun.COM 
1197*7934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
1198*7934SMark.Phalan@Sun.COM     krb5_free_keyblock_contents(context, &original_key);
1199*7934SMark.Phalan@Sun.COM     free(pa_order);
1200*7934SMark.Phalan@Sun.COM 
12010Sstevel@tonic-gate     return (retval);
12020Sstevel@tonic-gate }
12032881Smp153739 
12040Sstevel@tonic-gate static krb5_boolean
12050Sstevel@tonic-gate enctype_requires_etype_info_2(krb5_enctype enctype)
12060Sstevel@tonic-gate {
12070Sstevel@tonic-gate     switch(enctype) {
12080Sstevel@tonic-gate     case ENCTYPE_DES_CBC_CRC:
12090Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD4:
12100Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD5:
12110Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA1:
12120Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_RAW:
12130Sstevel@tonic-gate     case ENCTYPE_ARCFOUR_HMAC:
12140Sstevel@tonic-gate     case ENCTYPE_ARCFOUR_HMAC_EXP :
12150Sstevel@tonic-gate 	return 0;
12160Sstevel@tonic-gate     default:
12170Sstevel@tonic-gate 	if (krb5_c_valid_enctype(enctype))
12182881Smp153739 	    return 1;
12190Sstevel@tonic-gate 	else return 0;
12200Sstevel@tonic-gate     }
12210Sstevel@tonic-gate }
12220Sstevel@tonic-gate 
12230Sstevel@tonic-gate static krb5_boolean
12240Sstevel@tonic-gate request_contains_enctype (krb5_context context,  const krb5_kdc_req *request,
12250Sstevel@tonic-gate 			  krb5_enctype enctype)
12260Sstevel@tonic-gate {
12270Sstevel@tonic-gate     int i;
12280Sstevel@tonic-gate     for (i =0; i < request->nktypes; i++)
12290Sstevel@tonic-gate 	if (request->ktype[i] == enctype)
12300Sstevel@tonic-gate 	    return 1;
12310Sstevel@tonic-gate     return 0;
12320Sstevel@tonic-gate }
12330Sstevel@tonic-gate 
12342881Smp153739 
12350Sstevel@tonic-gate static krb5_error_code
12362881Smp153739 verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
1237*7934SMark.Phalan@Sun.COM 		     krb5_data *req_pkt,
12382881Smp153739 		     krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
1239*7934SMark.Phalan@Sun.COM 		     krb5_pa_data *pa,
1240*7934SMark.Phalan@Sun.COM 		     preauth_get_entry_data_proc ets_get_entry_data,
1241*7934SMark.Phalan@Sun.COM 		     void *pa_system_context,
1242*7934SMark.Phalan@Sun.COM 		     void **pa_request_context,
1243*7934SMark.Phalan@Sun.COM 		     krb5_data **e_data,
1244*7934SMark.Phalan@Sun.COM 		     krb5_authdata ***authz_data)
12450Sstevel@tonic-gate {
12460Sstevel@tonic-gate     krb5_pa_enc_ts *		pa_enc = 0;
12470Sstevel@tonic-gate     krb5_error_code		retval;
12480Sstevel@tonic-gate     krb5_data			scratch;
12490Sstevel@tonic-gate     krb5_data			enc_ts_data;
12500Sstevel@tonic-gate     krb5_enc_data 		*enc_data = 0;
12510Sstevel@tonic-gate     krb5_keyblock		key;
12520Sstevel@tonic-gate     krb5_key_data *		client_key;
12530Sstevel@tonic-gate     krb5_int32			start;
12540Sstevel@tonic-gate     krb5_timestamp		timenow;
1255*7934SMark.Phalan@Sun.COM     krb5_error_code		decrypt_err = 0;
12562881Smp153739 
12570Sstevel@tonic-gate     (void) memset(&key, 0, sizeof(krb5_keyblock));
12580Sstevel@tonic-gate     scratch.data = (char *) pa->contents;
12590Sstevel@tonic-gate     scratch.length = pa->length;
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate     enc_ts_data.data = 0;
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate     if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
12640Sstevel@tonic-gate 	goto cleanup;
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate     enc_ts_data.length = enc_data->ciphertext.length;
12670Sstevel@tonic-gate     if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
12680Sstevel@tonic-gate 	goto cleanup;
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate     start = 0;
12710Sstevel@tonic-gate     decrypt_err = 0;
12720Sstevel@tonic-gate     while (1) {
12730Sstevel@tonic-gate 	if ((retval = krb5_dbe_search_enctype(context, client,
12740Sstevel@tonic-gate 					      &start, enc_data->enctype,
12750Sstevel@tonic-gate 					      -1, 0, &client_key)))
12760Sstevel@tonic-gate 	    goto cleanup;
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 	if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
12790Sstevel@tonic-gate 						  client_key, &key, NULL)))
12800Sstevel@tonic-gate 	    goto cleanup;
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 	key.enctype = enc_data->enctype;
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 	retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
12850Sstevel@tonic-gate 				0, enc_data, &enc_ts_data);
12860Sstevel@tonic-gate 	krb5_free_keyblock_contents(context, &key);
12870Sstevel@tonic-gate 	if (retval == 0)
12880Sstevel@tonic-gate 	    break;
12890Sstevel@tonic-gate 	else
12900Sstevel@tonic-gate 	    decrypt_err = retval;
12910Sstevel@tonic-gate     }
12920Sstevel@tonic-gate 
12930Sstevel@tonic-gate     if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
12940Sstevel@tonic-gate 	goto cleanup;
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate     if ((retval = krb5_timeofday(context, &timenow)) != 0)
12970Sstevel@tonic-gate 	goto cleanup;
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate     if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
13000Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_SKEW;
13010Sstevel@tonic-gate 	goto cleanup;
13020Sstevel@tonic-gate     }
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate     setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate     retval = 0;
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate cleanup:
13090Sstevel@tonic-gate     if (enc_data) {
13100Sstevel@tonic-gate 	krb5_free_data_contents(context, &enc_data->ciphertext);
13110Sstevel@tonic-gate 	free(enc_data);
13120Sstevel@tonic-gate     }
13130Sstevel@tonic-gate     krb5_free_data_contents(context, &enc_ts_data);
13140Sstevel@tonic-gate     if (pa_enc)
13150Sstevel@tonic-gate 	free(pa_enc);
13160Sstevel@tonic-gate     /*
13170Sstevel@tonic-gate      * If we get NO_MATCHING_KEY and decryption previously failed, and
13180Sstevel@tonic-gate      * we failed to find any other keys of the correct enctype after
13190Sstevel@tonic-gate      * that failed decryption, it probably means that the password was
13200Sstevel@tonic-gate      * incorrect.
13210Sstevel@tonic-gate      */
13220Sstevel@tonic-gate     if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
13230Sstevel@tonic-gate 	retval = decrypt_err;
13240Sstevel@tonic-gate     return retval;
13250Sstevel@tonic-gate }
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate static krb5_error_code
13280Sstevel@tonic-gate _make_etype_info_entry(krb5_context context,
13292881Smp153739 		       krb5_kdc_req *request, krb5_key_data *client_key,
13302881Smp153739 		       krb5_enctype etype, krb5_etype_info_entry **entry,
13312881Smp153739 		       int etype_info2)
13320Sstevel@tonic-gate {
13330Sstevel@tonic-gate     krb5_data			salt;
13340Sstevel@tonic-gate     krb5_etype_info_entry *	tmp_entry;
13350Sstevel@tonic-gate     krb5_error_code		retval;
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate     if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL)
13380Sstevel@tonic-gate        return ENOMEM;
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate     salt.data = 0;
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate     tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY;
13430Sstevel@tonic-gate     tmp_entry->etype = etype;
13440Sstevel@tonic-gate     tmp_entry->length = KRB5_ETYPE_NO_SALT;
13450Sstevel@tonic-gate     tmp_entry->salt = 0;
13460Sstevel@tonic-gate     tmp_entry->s2kparams.data = NULL;
13470Sstevel@tonic-gate     tmp_entry->s2kparams.length = 0;
13480Sstevel@tonic-gate     retval = get_salt_from_key(context, request->client,
13490Sstevel@tonic-gate 			       client_key, &salt);
13500Sstevel@tonic-gate     if (retval)
13510Sstevel@tonic-gate 	goto fail;
13520Sstevel@tonic-gate     if (etype_info2 && client_key->key_data_ver > 1 &&
13530Sstevel@tonic-gate 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) {
13540Sstevel@tonic-gate 	switch (etype) {
13550Sstevel@tonic-gate 	case ENCTYPE_DES_CBC_CRC:
13560Sstevel@tonic-gate 	case ENCTYPE_DES_CBC_MD4:
13570Sstevel@tonic-gate 	case ENCTYPE_DES_CBC_MD5:
13582881Smp153739 	    tmp_entry->s2kparams.data = malloc(1);
13592881Smp153739 	    if (tmp_entry->s2kparams.data == NULL) {
13600Sstevel@tonic-gate 		retval = ENOMEM;
13610Sstevel@tonic-gate 		goto fail;
13622881Smp153739 	    }
13632881Smp153739 	    tmp_entry->s2kparams.length = 1;
13642881Smp153739 	    tmp_entry->s2kparams.data[0] = 1;
13652881Smp153739 	    break;
13660Sstevel@tonic-gate 	default:
13672881Smp153739 	    break;
13680Sstevel@tonic-gate 	}
13690Sstevel@tonic-gate     }
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate     if (salt.length >= 0) {
13720Sstevel@tonic-gate 	tmp_entry->length = salt.length;
13730Sstevel@tonic-gate 	tmp_entry->salt = (unsigned char *) salt.data;
13740Sstevel@tonic-gate 	salt.data = 0;
13750Sstevel@tonic-gate     }
13760Sstevel@tonic-gate     *entry = tmp_entry;
13770Sstevel@tonic-gate     return 0;
13780Sstevel@tonic-gate 
13790Sstevel@tonic-gate fail:
13800Sstevel@tonic-gate     if (tmp_entry) {
13810Sstevel@tonic-gate 	if (tmp_entry->s2kparams.data)
13822881Smp153739 	    free(tmp_entry->s2kparams.data);
13830Sstevel@tonic-gate 	free(tmp_entry);
13840Sstevel@tonic-gate     }
13850Sstevel@tonic-gate     if (salt.data)
13860Sstevel@tonic-gate 	free(salt.data);
13870Sstevel@tonic-gate     return retval;
13880Sstevel@tonic-gate }
13890Sstevel@tonic-gate /*
13900Sstevel@tonic-gate  * This function returns the etype information for a particular
13910Sstevel@tonic-gate  * client, to be passed back in the preauth list in the KRB_ERROR
13920Sstevel@tonic-gate  * message.  It supports generating both etype_info  and etype_info2
13932881Smp153739  *  as most of the work is the same.
13940Sstevel@tonic-gate  */
13950Sstevel@tonic-gate static krb5_error_code
13960Sstevel@tonic-gate etype_info_helper(krb5_context context, krb5_kdc_req *request,
13972881Smp153739 	       krb5_db_entry *client, krb5_db_entry *server,
13982881Smp153739 	       krb5_pa_data *pa_data, int etype_info2)
13990Sstevel@tonic-gate {
14000Sstevel@tonic-gate     krb5_etype_info_entry **	entry = 0;
14010Sstevel@tonic-gate     krb5_key_data		*client_key;
14020Sstevel@tonic-gate     krb5_error_code		retval;
14030Sstevel@tonic-gate     krb5_data *			scratch;
14040Sstevel@tonic-gate     krb5_enctype		db_etype;
14052881Smp153739     int 			i = 0;
14062881Smp153739     int 			start = 0;
14070Sstevel@tonic-gate     int				seen_des = 0;
14080Sstevel@tonic-gate 
14092881Smp153739     entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
14100Sstevel@tonic-gate     if (entry == NULL)
14110Sstevel@tonic-gate 	return ENOMEM;
14120Sstevel@tonic-gate     entry[0] = NULL;
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate     while (1) {
14150Sstevel@tonic-gate 	retval = krb5_dbe_search_enctype(context, client, &start, -1,
14162881Smp153739 					 -1, 0, &client_key);
14170Sstevel@tonic-gate 	if (retval == KRB5_KDB_NO_MATCHING_KEY)
14182881Smp153739 	    break;
14190Sstevel@tonic-gate 	if (retval)
14202881Smp153739 	    goto cleanup;
14210Sstevel@tonic-gate 	db_etype = client_key->key_data_type[0];
14220Sstevel@tonic-gate 	if (db_etype == ENCTYPE_DES_CBC_MD4)
14232881Smp153739 	    db_etype = ENCTYPE_DES_CBC_MD5;
14242881Smp153739 
14250Sstevel@tonic-gate 	if (request_contains_enctype(context, request, db_etype)) {
14262881Smp153739 	    assert(etype_info2 ||
14272881Smp153739 		   !enctype_requires_etype_info_2(db_etype));
14282881Smp153739 	    if ((retval = _make_etype_info_entry(context, request, client_key,
14292881Smp153739 			    db_etype, &entry[i], etype_info2)) != 0) {
14300Sstevel@tonic-gate 		goto cleanup;
14312881Smp153739 	    }
14322881Smp153739 	    entry[i+1] = 0;
14332881Smp153739 	    i++;
14340Sstevel@tonic-gate 	}
14350Sstevel@tonic-gate 
14362881Smp153739 	/*
14372881Smp153739 	 * If there is a des key in the kdb, try the "similar" enctypes,
14382881Smp153739 	 * avoid duplicate entries.
14390Sstevel@tonic-gate 	 */
14400Sstevel@tonic-gate 	if (!seen_des) {
14412881Smp153739 	    switch (db_etype) {
14422881Smp153739 	    case ENCTYPE_DES_CBC_MD5:
14430Sstevel@tonic-gate 		db_etype = ENCTYPE_DES_CBC_CRC;
14440Sstevel@tonic-gate 		break;
14452881Smp153739 	    case ENCTYPE_DES_CBC_CRC:
14460Sstevel@tonic-gate 		db_etype = ENCTYPE_DES_CBC_MD5;
14470Sstevel@tonic-gate 		break;
14482881Smp153739 	    default:
14490Sstevel@tonic-gate 		continue;
14500Sstevel@tonic-gate 
14512881Smp153739 	    }
14522881Smp153739 	    if (request_contains_enctype(context, request, db_etype)) {
14530Sstevel@tonic-gate 		if ((retval = _make_etype_info_entry(context, request,
14542881Smp153739 				client_key, db_etype, &entry[i], etype_info2)) != 0) {
14552881Smp153739 		    goto cleanup;
14560Sstevel@tonic-gate 		}
14572881Smp153739 		entry[i+1] = 0;
14580Sstevel@tonic-gate 		i++;
14592881Smp153739 	    }
14602881Smp153739 	    seen_des++;
14610Sstevel@tonic-gate 	}
14620Sstevel@tonic-gate     }
14630Sstevel@tonic-gate     if (etype_info2)
14640Sstevel@tonic-gate 	retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry,
14652881Smp153739 				    &scratch);
14662881Smp153739     else 	retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry,
14672881Smp153739 				    &scratch);
14680Sstevel@tonic-gate     if (retval)
14690Sstevel@tonic-gate 	goto cleanup;
14700Sstevel@tonic-gate     pa_data->contents = (unsigned char *)scratch->data;
14710Sstevel@tonic-gate     pa_data->length = scratch->length;
14720Sstevel@tonic-gate     free(scratch);
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate     retval = 0;
14750Sstevel@tonic-gate 
14760Sstevel@tonic-gate cleanup:
14770Sstevel@tonic-gate     if (entry)
14780Sstevel@tonic-gate 	krb5_free_etype_info(context, entry);
14790Sstevel@tonic-gate     return retval;
14800Sstevel@tonic-gate }
14810Sstevel@tonic-gate 
14820Sstevel@tonic-gate static krb5_error_code
14830Sstevel@tonic-gate get_etype_info(krb5_context context, krb5_kdc_req *request,
14842881Smp153739 	       krb5_db_entry *client, krb5_db_entry *server,
1485*7934SMark.Phalan@Sun.COM 	       preauth_get_entry_data_proc etype_get_entry_data,
1486*7934SMark.Phalan@Sun.COM 	       void *pa_system_context,
14872881Smp153739 	       krb5_pa_data *pa_data)
14880Sstevel@tonic-gate {
14890Sstevel@tonic-gate   int i;
14900Sstevel@tonic-gate     for (i=0;  i < request->nktypes; i++) {
14912881Smp153739 	if (enctype_requires_etype_info_2(request->ktype[i]))
14922881Smp153739 	    return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
14932881Smp153739 							* skip this
14942881Smp153739 							* type*/
14950Sstevel@tonic-gate     }
14960Sstevel@tonic-gate     return etype_info_helper(context, request, client, server, pa_data, 0);
14970Sstevel@tonic-gate }
14980Sstevel@tonic-gate 
14990Sstevel@tonic-gate static krb5_error_code
15000Sstevel@tonic-gate get_etype_info2(krb5_context context, krb5_kdc_req *request,
15012881Smp153739 	       krb5_db_entry *client, krb5_db_entry *server,
1502*7934SMark.Phalan@Sun.COM 	       preauth_get_entry_data_proc etype_get_entry_data,
1503*7934SMark.Phalan@Sun.COM 	       void *pa_system_context,
15042881Smp153739 	       krb5_pa_data *pa_data)
15050Sstevel@tonic-gate {
15060Sstevel@tonic-gate     return etype_info_helper( context, request, client, server, pa_data, 1);
15070Sstevel@tonic-gate }
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate static krb5_error_code
1510*7934SMark.Phalan@Sun.COM etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
1511*7934SMark.Phalan@Sun.COM 			 krb5_db_entry *client,
1512*7934SMark.Phalan@Sun.COM 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
1513*7934SMark.Phalan@Sun.COM 			 krb5_key_data *client_key,
1514*7934SMark.Phalan@Sun.COM 			 krb5_keyblock *encrypting_key,
1515*7934SMark.Phalan@Sun.COM 			 krb5_pa_data **send_pa,
1516*7934SMark.Phalan@Sun.COM 			 int etype_info2)
15170Sstevel@tonic-gate {
1518*7934SMark.Phalan@Sun.COM     int i;
15190Sstevel@tonic-gate     krb5_error_code retval;
15200Sstevel@tonic-gate     krb5_pa_data *tmp_padata;
15210Sstevel@tonic-gate     krb5_etype_info_entry **entry = NULL;
15220Sstevel@tonic-gate     krb5_data *scratch = NULL;
1523*7934SMark.Phalan@Sun.COM 
1524*7934SMark.Phalan@Sun.COM     /*
1525*7934SMark.Phalan@Sun.COM      * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
1526*7934SMark.Phalan@Sun.COM      * enctypes.
1527*7934SMark.Phalan@Sun.COM      */
1528*7934SMark.Phalan@Sun.COM     if (!etype_info2) {
1529*7934SMark.Phalan@Sun.COM 	for (i = 0; i < request->nktypes; i++) {
1530*7934SMark.Phalan@Sun.COM 	    if (enctype_requires_etype_info_2(request->ktype[i])) {
1531*7934SMark.Phalan@Sun.COM 		*send_pa = NULL;
1532*7934SMark.Phalan@Sun.COM 		return 0;
1533*7934SMark.Phalan@Sun.COM 	    }
1534*7934SMark.Phalan@Sun.COM 	}
1535*7934SMark.Phalan@Sun.COM     }
1536*7934SMark.Phalan@Sun.COM 
15370Sstevel@tonic-gate     tmp_padata = malloc( sizeof(krb5_pa_data));
15380Sstevel@tonic-gate     if (tmp_padata == NULL)
15390Sstevel@tonic-gate 	return ENOMEM;
1540*7934SMark.Phalan@Sun.COM     if (etype_info2)
1541*7934SMark.Phalan@Sun.COM 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
1542*7934SMark.Phalan@Sun.COM     else
1543*7934SMark.Phalan@Sun.COM 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO;
1544*7934SMark.Phalan@Sun.COM 
15450Sstevel@tonic-gate     entry = malloc(2 * sizeof(krb5_etype_info_entry *));
15460Sstevel@tonic-gate     if (entry == NULL) {
15470Sstevel@tonic-gate 	retval = ENOMEM;
15480Sstevel@tonic-gate 	goto cleanup;
15490Sstevel@tonic-gate     }
15500Sstevel@tonic-gate     entry[0] = NULL;
15510Sstevel@tonic-gate     entry[1] = NULL;
1552*7934SMark.Phalan@Sun.COM     retval = _make_etype_info_entry(context, request,
1553*7934SMark.Phalan@Sun.COM 				    client_key, encrypting_key->enctype,
1554*7934SMark.Phalan@Sun.COM 				    entry, etype_info2);
15550Sstevel@tonic-gate     if (retval)
15560Sstevel@tonic-gate 	goto cleanup;
1557*7934SMark.Phalan@Sun.COM 
1558*7934SMark.Phalan@Sun.COM     if (etype_info2)
1559*7934SMark.Phalan@Sun.COM 	retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry, &scratch);
1560*7934SMark.Phalan@Sun.COM     else
1561*7934SMark.Phalan@Sun.COM 	retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry, &scratch);
1562*7934SMark.Phalan@Sun.COM 
15630Sstevel@tonic-gate     if (retval)
15640Sstevel@tonic-gate 	goto cleanup;
15650Sstevel@tonic-gate     tmp_padata->contents = (uchar_t *)scratch->data;
15660Sstevel@tonic-gate     tmp_padata->length = scratch->length;
15670Sstevel@tonic-gate     *send_pa = tmp_padata;
15680Sstevel@tonic-gate 
15692881Smp153739     /* For cleanup - we no longer own the contents of the krb5_data
15700Sstevel@tonic-gate      * only to pointer to the krb5_data
15710Sstevel@tonic-gate      */
15722881Smp153739     scratch->data = 0;
15730Sstevel@tonic-gate 
15740Sstevel@tonic-gate  cleanup:
15750Sstevel@tonic-gate     if (entry)
15760Sstevel@tonic-gate 	krb5_free_etype_info(context, entry);
15770Sstevel@tonic-gate     if (retval) {
15780Sstevel@tonic-gate 	if (tmp_padata)
15792881Smp153739 	    free(tmp_padata);
15800Sstevel@tonic-gate     }
15810Sstevel@tonic-gate     if (scratch)
15822881Smp153739 	    krb5_free_data(context, scratch);
15830Sstevel@tonic-gate     return retval;
15840Sstevel@tonic-gate }
15850Sstevel@tonic-gate 
1586*7934SMark.Phalan@Sun.COM static krb5_error_code
1587*7934SMark.Phalan@Sun.COM return_etype_info2(krb5_context context, krb5_pa_data * padata,
1588*7934SMark.Phalan@Sun.COM 		   krb5_db_entry *client,
1589*7934SMark.Phalan@Sun.COM 		   krb5_data *req_pkt,
1590*7934SMark.Phalan@Sun.COM 		   krb5_kdc_req *request, krb5_kdc_rep *reply,
1591*7934SMark.Phalan@Sun.COM 		   krb5_key_data *client_key,
1592*7934SMark.Phalan@Sun.COM 		   krb5_keyblock *encrypting_key,
1593*7934SMark.Phalan@Sun.COM 		   krb5_pa_data **send_pa,
1594*7934SMark.Phalan@Sun.COM 		   preauth_get_entry_data_proc etype_get_entry_data,
1595*7934SMark.Phalan@Sun.COM 	           void *pa_system_context,
1596*7934SMark.Phalan@Sun.COM 		   void **pa_request_context)
1597*7934SMark.Phalan@Sun.COM {
1598*7934SMark.Phalan@Sun.COM     return etype_info_as_rep_helper(context, padata, client, request, reply,
1599*7934SMark.Phalan@Sun.COM 				    client_key, encrypting_key, send_pa, 1);
1600*7934SMark.Phalan@Sun.COM }
1601*7934SMark.Phalan@Sun.COM 
1602*7934SMark.Phalan@Sun.COM 
1603*7934SMark.Phalan@Sun.COM static krb5_error_code
1604*7934SMark.Phalan@Sun.COM return_etype_info(krb5_context context, krb5_pa_data * padata,
1605*7934SMark.Phalan@Sun.COM 		  krb5_db_entry *client,
1606*7934SMark.Phalan@Sun.COM 		  krb5_data *req_pkt,
1607*7934SMark.Phalan@Sun.COM 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
1608*7934SMark.Phalan@Sun.COM 		  krb5_key_data *client_key,
1609*7934SMark.Phalan@Sun.COM 		  krb5_keyblock *encrypting_key,
1610*7934SMark.Phalan@Sun.COM 		  krb5_pa_data **send_pa,
1611*7934SMark.Phalan@Sun.COM 		  preauth_get_entry_data_proc etypeget_entry_data,
1612*7934SMark.Phalan@Sun.COM 	          void *pa_system_context,
1613*7934SMark.Phalan@Sun.COM 		  void **pa_request_context)
1614*7934SMark.Phalan@Sun.COM {
1615*7934SMark.Phalan@Sun.COM     return etype_info_as_rep_helper(context, padata, client, request, reply,
1616*7934SMark.Phalan@Sun.COM 				    client_key, encrypting_key, send_pa, 0);
1617*7934SMark.Phalan@Sun.COM }
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate static krb5_error_code
16202881Smp153739 return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
1621*7934SMark.Phalan@Sun.COM 	       krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
16222881Smp153739 	       krb5_kdc_rep *reply, krb5_key_data *client_key,
1623*7934SMark.Phalan@Sun.COM 	       krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1624*7934SMark.Phalan@Sun.COM 	       preauth_get_entry_data_proc etype_get_entry_data,
1625*7934SMark.Phalan@Sun.COM 	       void *pa_system_context,
1626*7934SMark.Phalan@Sun.COM 	       void **pa_request_context)
16270Sstevel@tonic-gate {
16280Sstevel@tonic-gate     krb5_error_code	retval;
16290Sstevel@tonic-gate     krb5_pa_data *	padata;
16300Sstevel@tonic-gate     krb5_data *		scratch;
16310Sstevel@tonic-gate     krb5_data		salt_data;
16320Sstevel@tonic-gate     int i;
16332881Smp153739 
16340Sstevel@tonic-gate     for (i = 0; i < request->nktypes; i++) {
16350Sstevel@tonic-gate 	if (enctype_requires_etype_info_2(request->ktype[i]))
16362881Smp153739 	    return 0;
16370Sstevel@tonic-gate     }
16380Sstevel@tonic-gate     if (client_key->key_data_ver == 1 ||
16390Sstevel@tonic-gate 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
16400Sstevel@tonic-gate 	return 0;
16410Sstevel@tonic-gate 
16420Sstevel@tonic-gate     if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
16430Sstevel@tonic-gate 	return ENOMEM;
16440Sstevel@tonic-gate     padata->magic = KV5M_PA_DATA;
16450Sstevel@tonic-gate     padata->pa_type = KRB5_PADATA_PW_SALT;
16460Sstevel@tonic-gate 
16470Sstevel@tonic-gate     switch (client_key->key_data_type[1]) {
16480Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_V4:
16490Sstevel@tonic-gate 	/* send an empty (V4) salt */
16500Sstevel@tonic-gate 	padata->contents = 0;
16510Sstevel@tonic-gate 	padata->length = 0;
16520Sstevel@tonic-gate 	break;
16530Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_NOREALM:
16540Sstevel@tonic-gate 	if ((retval = krb5_principal2salt_norealm(kdc_context,
16550Sstevel@tonic-gate 						   request->client,
16560Sstevel@tonic-gate 						   &salt_data)))
16570Sstevel@tonic-gate 	    goto cleanup;
16580Sstevel@tonic-gate 	padata->contents = (krb5_octet *)salt_data.data;
16590Sstevel@tonic-gate 	padata->length = salt_data.length;
16600Sstevel@tonic-gate 	break;
16610Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_AFS3:
16620Sstevel@tonic-gate 	/* send an AFS style realm-based salt */
16630Sstevel@tonic-gate 	/* for now, just pass the realm back and let the client
16640Sstevel@tonic-gate 	   do the work. In the future, add a kdc configuration
16650Sstevel@tonic-gate 	   variable that specifies the old cell name. */
16660Sstevel@tonic-gate 	padata->pa_type = KRB5_PADATA_AFS3_SALT;
16670Sstevel@tonic-gate 	/* it would be just like ONLYREALM, but we need to pass the 0 */
16680Sstevel@tonic-gate 	scratch = krb5_princ_realm(kdc_context, request->client);
16690Sstevel@tonic-gate 	if ((padata->contents = malloc(scratch->length+1)) == NULL) {
16700Sstevel@tonic-gate 	    retval = ENOMEM;
16710Sstevel@tonic-gate 	    goto cleanup;
16720Sstevel@tonic-gate 	}
16730Sstevel@tonic-gate 	memcpy(padata->contents, scratch->data, scratch->length);
16740Sstevel@tonic-gate 	padata->length = scratch->length+1;
16750Sstevel@tonic-gate 	padata->contents[scratch->length] = 0;
16760Sstevel@tonic-gate 	break;
16770Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_ONLYREALM:
16780Sstevel@tonic-gate 	scratch = krb5_princ_realm(kdc_context, request->client);
16790Sstevel@tonic-gate 	if ((padata->contents = malloc(scratch->length)) == NULL) {
16800Sstevel@tonic-gate 	    retval = ENOMEM;
16810Sstevel@tonic-gate 	    goto cleanup;
16820Sstevel@tonic-gate 	}
16830Sstevel@tonic-gate 	memcpy(padata->contents, scratch->data, scratch->length);
16840Sstevel@tonic-gate 	padata->length = scratch->length;
16850Sstevel@tonic-gate 	break;
16860Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_SPECIAL:
16870Sstevel@tonic-gate 	if ((padata->contents = malloc(client_key->key_data_length[1]))
16880Sstevel@tonic-gate 	    == NULL) {
16890Sstevel@tonic-gate 	    retval = ENOMEM;
16900Sstevel@tonic-gate 	    goto cleanup;
16910Sstevel@tonic-gate 	}
16920Sstevel@tonic-gate 	memcpy(padata->contents, client_key->key_data_contents[1],
16930Sstevel@tonic-gate 	       client_key->key_data_length[1]);
16940Sstevel@tonic-gate 	padata->length = client_key->key_data_length[1];
16950Sstevel@tonic-gate 	break;
16960Sstevel@tonic-gate     default:
16970Sstevel@tonic-gate 	free(padata);
16980Sstevel@tonic-gate 	return 0;
16990Sstevel@tonic-gate     }
17000Sstevel@tonic-gate 
17010Sstevel@tonic-gate     *send_pa = padata;
17020Sstevel@tonic-gate     return 0;
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate cleanup:
17050Sstevel@tonic-gate     free(padata);
17060Sstevel@tonic-gate     return retval;
17070Sstevel@tonic-gate }
17080Sstevel@tonic-gate 
17090Sstevel@tonic-gate static krb5_error_code
17102881Smp153739 return_sam_data(krb5_context context, krb5_pa_data *in_padata,
1711*7934SMark.Phalan@Sun.COM 		krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
17122881Smp153739 		krb5_kdc_rep *reply, krb5_key_data *client_key,
1713*7934SMark.Phalan@Sun.COM 		krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1714*7934SMark.Phalan@Sun.COM 		preauth_get_entry_data_proc sam_get_entry_data,
1715*7934SMark.Phalan@Sun.COM 		void *pa_system_context,
1716*7934SMark.Phalan@Sun.COM 		void **pa_request_context)
17170Sstevel@tonic-gate {
17180Sstevel@tonic-gate     krb5_error_code	retval;
17190Sstevel@tonic-gate     krb5_data		scratch;
17200Sstevel@tonic-gate     int			i;
17210Sstevel@tonic-gate 
17220Sstevel@tonic-gate     krb5_sam_response		*sr = 0;
17230Sstevel@tonic-gate     krb5_predicted_sam_response	*psr = 0;
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate     if (in_padata == 0)
17260Sstevel@tonic-gate 	return 0;
17270Sstevel@tonic-gate 
17280Sstevel@tonic-gate     /*
17290Sstevel@tonic-gate      * We start by doing the same thing verify_sam_response() does:
17300Sstevel@tonic-gate      * extract the psr from the padata (which is an sr). Nothing
17310Sstevel@tonic-gate      * here should generate errors! We've already successfully done
17320Sstevel@tonic-gate      * all this once.
17330Sstevel@tonic-gate      */
17340Sstevel@tonic-gate 
17350Sstevel@tonic-gate     scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
17360Sstevel@tonic-gate     scratch.length = in_padata->length;
17370Sstevel@tonic-gate 
17380Sstevel@tonic-gate     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
17390Sstevel@tonic-gate 	com_err("krb5kdc", retval,
17400Sstevel@tonic-gate 		gettext("return_sam_data(): decode_krb5_sam_response failed"));
17410Sstevel@tonic-gate 	goto cleanup;
17420Sstevel@tonic-gate     }
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate     {
17450Sstevel@tonic-gate 	krb5_enc_data tmpdata;
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	tmpdata.enctype = ENCTYPE_UNKNOWN;
17480Sstevel@tonic-gate 	tmpdata.ciphertext = sr->sam_track_id;
17490Sstevel@tonic-gate 
17500Sstevel@tonic-gate 	scratch.length = tmpdata.ciphertext.length;
17510Sstevel@tonic-gate 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
17520Sstevel@tonic-gate 	    retval = ENOMEM;
17530Sstevel@tonic-gate 	    goto cleanup;
17540Sstevel@tonic-gate 	}
17550Sstevel@tonic-gate 
17560Sstevel@tonic-gate 	if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
17570Sstevel@tonic-gate 				     &tmpdata, &scratch))) {
17580Sstevel@tonic-gate 	    com_err("krb5kdc", retval,
17590Sstevel@tonic-gate 		    gettext("return_sam_data(): decrypt track_id failed"));
17600Sstevel@tonic-gate 	    free(scratch.data);
17610Sstevel@tonic-gate 	    goto cleanup;
17620Sstevel@tonic-gate 	}
17630Sstevel@tonic-gate     }
17640Sstevel@tonic-gate 
17650Sstevel@tonic-gate     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
17660Sstevel@tonic-gate 	com_err("krb5kdc", retval,
17670Sstevel@tonic-gate 		gettext(
17680Sstevel@tonic-gate 		"return_sam_data(): decode_krb5_predicted_sam_response failed"));
17690Sstevel@tonic-gate 	free(scratch.data);
17700Sstevel@tonic-gate 	goto cleanup;
17710Sstevel@tonic-gate     }
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate     /* We could use sr->sam_flags, but it may be absent or altered. */
17740Sstevel@tonic-gate     if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
17750Sstevel@tonic-gate 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
17760Sstevel@tonic-gate 		gettext("Unsupported SAM flag must-pk-encrypt-sad"));
17770Sstevel@tonic-gate 	goto cleanup;
17780Sstevel@tonic-gate     }
17790Sstevel@tonic-gate     if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
17800Sstevel@tonic-gate 	/* No key munging */
17810Sstevel@tonic-gate 	goto cleanup;
17820Sstevel@tonic-gate     }
17830Sstevel@tonic-gate     if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
17840Sstevel@tonic-gate 	/* Use sam_key instead of client key */
17850Sstevel@tonic-gate 	krb5_free_keyblock_contents(context, encrypting_key);
17860Sstevel@tonic-gate 	krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
17870Sstevel@tonic-gate 	/* XXX Attach a useful pa_data */
17880Sstevel@tonic-gate 	goto cleanup;
17890Sstevel@tonic-gate     }
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate     /* Otherwise (no flags set), we XOR the keys */
17920Sstevel@tonic-gate     /* XXX The passwords-04 draft is underspecified here wrt different
17930Sstevel@tonic-gate 	   key types. We will do what I hope to get into the -05 draft. */
17940Sstevel@tonic-gate     {
17950Sstevel@tonic-gate 	krb5_octet *p = encrypting_key->contents;
17960Sstevel@tonic-gate 	krb5_octet *q = psr->sam_key.contents;
17970Sstevel@tonic-gate 	int length = ((encrypting_key->length < psr->sam_key.length)
17980Sstevel@tonic-gate 		      ? encrypting_key->length
17990Sstevel@tonic-gate 		      : psr->sam_key.length);
18000Sstevel@tonic-gate 
18010Sstevel@tonic-gate 	for (i = 0; i < length; i++)
18020Sstevel@tonic-gate 	    p[i] ^= q[i];
18030Sstevel@tonic-gate     }
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate     /* Post-mixing key correction */
18060Sstevel@tonic-gate     switch (encrypting_key->enctype) {
18070Sstevel@tonic-gate     case ENCTYPE_DES_CBC_CRC:
18080Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD4:
18090Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD5:
18100Sstevel@tonic-gate     case ENCTYPE_DES_CBC_RAW:
18110Sstevel@tonic-gate 	mit_des_fixup_key_parity(encrypting_key->contents);
18120Sstevel@tonic-gate 	if (mit_des_is_weak_key(encrypting_key->contents))
18130Sstevel@tonic-gate 	    ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
18140Sstevel@tonic-gate 	break;
18150Sstevel@tonic-gate 
18160Sstevel@tonic-gate     /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
18170Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
18180Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_RAW:
18190Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA1:
18200Sstevel@tonic-gate 	for (i = 0; i < 3; i++) {
18210Sstevel@tonic-gate 	    mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
18220Sstevel@tonic-gate 	    if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
18230Sstevel@tonic-gate 		((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
18240Sstevel@tonic-gate 	}
18250Sstevel@tonic-gate 	break;
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate     default:
18280Sstevel@tonic-gate 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
18290Sstevel@tonic-gate 		gettext("Unimplemented keytype for SAM key mixing"));
18300Sstevel@tonic-gate 	goto cleanup;
18310Sstevel@tonic-gate     }
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate     /* XXX Attach a useful pa_data */
18340Sstevel@tonic-gate cleanup:
18350Sstevel@tonic-gate     if (sr)
18360Sstevel@tonic-gate 	krb5_free_sam_response(context, sr);
18370Sstevel@tonic-gate     if (psr)
18380Sstevel@tonic-gate 	krb5_free_predicted_sam_response(context, psr);
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate     return retval;
18410Sstevel@tonic-gate }
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate static struct {
18440Sstevel@tonic-gate   char* name;
18450Sstevel@tonic-gate   int   sam_type;
18460Sstevel@tonic-gate } *sam_ptr, sam_inst_map[] = {
18470Sstevel@tonic-gate #if 0	/* SUNWresync121 - unsupported hardware and kdc.log annoyance */
18480Sstevel@tonic-gate   { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
18490Sstevel@tonic-gate   { "SECURID", PA_SAM_TYPE_SECURID, },
18500Sstevel@tonic-gate   { "GRAIL", PA_SAM_TYPE_GRAIL, },
18510Sstevel@tonic-gate #endif
18520Sstevel@tonic-gate   { 0, 0 },
18530Sstevel@tonic-gate };
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate static krb5_error_code
18562881Smp153739 get_sam_edata(krb5_context context, krb5_kdc_req *request,
18572881Smp153739 	      krb5_db_entry *client, krb5_db_entry *server,
1858*7934SMark.Phalan@Sun.COM 	      preauth_get_entry_data_proc sam_get_entry_data,
1859*7934SMark.Phalan@Sun.COM 	      void *pa_system_context, krb5_pa_data *pa_data)
18600Sstevel@tonic-gate {
18610Sstevel@tonic-gate     krb5_error_code		retval;
18620Sstevel@tonic-gate     krb5_sam_challenge		sc;
18630Sstevel@tonic-gate     krb5_predicted_sam_response	psr;
18640Sstevel@tonic-gate     krb5_data *			scratch;
18650Sstevel@tonic-gate     krb5_keyblock encrypting_key;
18660Sstevel@tonic-gate     char response[9];
18670Sstevel@tonic-gate     char inputblock[8];
18680Sstevel@tonic-gate     krb5_data predict_response;
18690Sstevel@tonic-gate 
18700Sstevel@tonic-gate     (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
18710Sstevel@tonic-gate     (void) memset(&sc, 0, sizeof(sc));
18720Sstevel@tonic-gate     (void) memset(&psr, 0, sizeof(psr));
18730Sstevel@tonic-gate 
18740Sstevel@tonic-gate     /* Given the client name we can figure out what type of preauth
18750Sstevel@tonic-gate        they need. The spec is currently for querying the database for
18760Sstevel@tonic-gate        names that match the types of preauth used. Later we should
18770Sstevel@tonic-gate        make this mapping show up in kdc.conf. In the meantime, we
18780Sstevel@tonic-gate        hardcode the following:
18790Sstevel@tonic-gate 		/SNK4 -- Digital Pathways SNK/4 preauth.
18800Sstevel@tonic-gate 		/GRAIL -- experimental preauth
18810Sstevel@tonic-gate        The first one found is used. See sam_inst_map above.
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate        For SNK4 in particular, the key in the database is the key for
18840Sstevel@tonic-gate        the device; kadmin needs a special interface for it.
18850Sstevel@tonic-gate      */
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate     {
18882881Smp153739       int npr = 1;
18892881Smp153739       krb5_boolean more;
18900Sstevel@tonic-gate       krb5_db_entry assoc;
18910Sstevel@tonic-gate       krb5_key_data  *assoc_key;
18920Sstevel@tonic-gate       krb5_principal newp;
18930Sstevel@tonic-gate       int probeslot;
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate       sc.sam_type = 0;
18960Sstevel@tonic-gate 
18970Sstevel@tonic-gate       retval = krb5_copy_principal(kdc_context, request->client, &newp);
18980Sstevel@tonic-gate       if (retval) {
18990Sstevel@tonic-gate 	com_err(gettext("krb5kdc"),
19000Sstevel@tonic-gate 		retval,
19010Sstevel@tonic-gate 		gettext("copying client name for preauth probe"));
19020Sstevel@tonic-gate 	return retval;
19030Sstevel@tonic-gate       }
19040Sstevel@tonic-gate 
19050Sstevel@tonic-gate       probeslot = krb5_princ_size(context, newp)++;
19060Sstevel@tonic-gate       krb5_princ_name(kdc_context, newp) =
19070Sstevel@tonic-gate 	realloc(krb5_princ_name(kdc_context, newp),
19080Sstevel@tonic-gate 		krb5_princ_size(context, newp) * sizeof(krb5_data));
19090Sstevel@tonic-gate 
19100Sstevel@tonic-gate       for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
19110Sstevel@tonic-gate 	krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
19120Sstevel@tonic-gate 	krb5_princ_component(kdc_context,newp,probeslot)->length =
19130Sstevel@tonic-gate 	  strlen(sam_ptr->name);
19140Sstevel@tonic-gate 	npr = 1;
19150Sstevel@tonic-gate 	retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
19162881Smp153739 	if(!retval && npr) {
19170Sstevel@tonic-gate 	  sc.sam_type = sam_ptr->sam_type;
19180Sstevel@tonic-gate 	  break;
19190Sstevel@tonic-gate 	}
19200Sstevel@tonic-gate       }
19210Sstevel@tonic-gate 
19220Sstevel@tonic-gate       krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
19230Sstevel@tonic-gate       krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
19240Sstevel@tonic-gate       krb5_princ_size(context, newp)--;
19250Sstevel@tonic-gate 
19260Sstevel@tonic-gate       krb5_free_principal(kdc_context, newp);
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate       /* if sc.sam_type is set, it worked */
19290Sstevel@tonic-gate       if (sc.sam_type) {
19300Sstevel@tonic-gate 	/* so use assoc to get the key out! */
19310Sstevel@tonic-gate 	{
19320Sstevel@tonic-gate 	  /* here's what do_tgs_req does */
19330Sstevel@tonic-gate 	  retval = krb5_dbe_find_enctype(kdc_context, &assoc,
19340Sstevel@tonic-gate 					 ENCTYPE_DES_CBC_RAW,
19350Sstevel@tonic-gate 					 KRB5_KDB_SALTTYPE_NORMAL,
19360Sstevel@tonic-gate 					 0,		/* Get highest kvno */
19370Sstevel@tonic-gate 					 &assoc_key);
19380Sstevel@tonic-gate 	  if (retval) {
19390Sstevel@tonic-gate 	    char *sname;
19400Sstevel@tonic-gate 	    krb5_unparse_name(kdc_context, request->client, &sname);
19410Sstevel@tonic-gate 	    com_err(gettext("krb5kdc"),
19420Sstevel@tonic-gate 		    retval,
19430Sstevel@tonic-gate 		    gettext("snk4 finding the enctype and key <%s>"),
19440Sstevel@tonic-gate 		    sname);
19450Sstevel@tonic-gate 	    free(sname);
19460Sstevel@tonic-gate 	    return retval;
19470Sstevel@tonic-gate 	  }
19480Sstevel@tonic-gate 	  /* convert server.key into a real key */
19490Sstevel@tonic-gate 	  retval = krb5_dbekd_decrypt_key_data(kdc_context,
19500Sstevel@tonic-gate 					       &master_keyblock,
19510Sstevel@tonic-gate 					       assoc_key, &encrypting_key,
19520Sstevel@tonic-gate 					       NULL);
19530Sstevel@tonic-gate 	  if (retval) {
19540Sstevel@tonic-gate 	    com_err(gettext("krb5kdc"),
19550Sstevel@tonic-gate 		    retval,
19560Sstevel@tonic-gate 		   gettext("snk4 pulling out key entry"));
19570Sstevel@tonic-gate 	    return retval;
19580Sstevel@tonic-gate 	  }
19590Sstevel@tonic-gate 	  /* now we can use encrypting_key... */
19600Sstevel@tonic-gate 	}
19610Sstevel@tonic-gate       } else {
19620Sstevel@tonic-gate 	/* SAM is not an option - so don't return as hint */
19630Sstevel@tonic-gate 	return KRB5_PREAUTH_BAD_TYPE;
19640Sstevel@tonic-gate       }
19650Sstevel@tonic-gate     }
19660Sstevel@tonic-gate     sc.magic = KV5M_SAM_CHALLENGE;
19670Sstevel@tonic-gate     psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
19680Sstevel@tonic-gate 
19690Sstevel@tonic-gate     /* Replay prevention */
19700Sstevel@tonic-gate     if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
19710Sstevel@tonic-gate 	return retval;
19720Sstevel@tonic-gate #ifdef USE_RCACHE
19730Sstevel@tonic-gate     if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
19740Sstevel@tonic-gate 	return retval;
19750Sstevel@tonic-gate #endif /* USE_RCACHE */
19760Sstevel@tonic-gate 
19770Sstevel@tonic-gate     switch (sc.sam_type) {
19780Sstevel@tonic-gate     case PA_SAM_TYPE_GRAIL:
19790Sstevel@tonic-gate       sc.sam_type_name.data = "Experimental System";
19800Sstevel@tonic-gate       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
19810Sstevel@tonic-gate       sc.sam_challenge_label.data = "experimental challenge label";
19820Sstevel@tonic-gate       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
19830Sstevel@tonic-gate       sc.sam_challenge.data = "12345";
19840Sstevel@tonic-gate       sc.sam_challenge.length = strlen(sc.sam_challenge.data);
19850Sstevel@tonic-gate 
19860Sstevel@tonic-gate #if 0 /* Enable this to test "normal" (no flags set) mode.  */
19870Sstevel@tonic-gate       psr.sam_flags = sc.sam_flags = 0;
19880Sstevel@tonic-gate #endif
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
19910Sstevel@tonic-gate       /* string2key on sc.sam_challenge goes in here */
19920Sstevel@tonic-gate       /* eblock is just to set the enctype */
19930Sstevel@tonic-gate       {
19940Sstevel@tonic-gate 	const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
19950Sstevel@tonic-gate 
19960Sstevel@tonic-gate 	if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
19970Sstevel@tonic-gate 					   0 /* salt */, &psr.sam_key)))
19980Sstevel@tonic-gate 	    goto cleanup;
19990Sstevel@tonic-gate 
20000Sstevel@tonic-gate 	if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
20010Sstevel@tonic-gate 	    goto cleanup;
20020Sstevel@tonic-gate 
20030Sstevel@tonic-gate 	{
20040Sstevel@tonic-gate 	    size_t enclen;
20050Sstevel@tonic-gate 	    krb5_enc_data tmpdata;
20060Sstevel@tonic-gate 
20070Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt_length(context,
20080Sstevel@tonic-gate 						psr_key.enctype,
20090Sstevel@tonic-gate 						scratch->length, &enclen)))
20100Sstevel@tonic-gate 		goto cleanup;
20110Sstevel@tonic-gate 
20120Sstevel@tonic-gate 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
20130Sstevel@tonic-gate 		retval = ENOMEM;
20140Sstevel@tonic-gate 		goto cleanup;
20150Sstevel@tonic-gate 	    }
20160Sstevel@tonic-gate 	    tmpdata.ciphertext.length = enclen;
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt(context, &psr_key,
20190Sstevel@tonic-gate 					 /* XXX */ 0, 0, scratch, &tmpdata)))
20200Sstevel@tonic-gate 		goto cleanup;
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate 	    sc.sam_track_id = tmpdata.ciphertext;
20230Sstevel@tonic-gate 	}
20240Sstevel@tonic-gate       }
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate       sc.sam_response_prompt.data = "response prompt";
20270Sstevel@tonic-gate       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
20280Sstevel@tonic-gate       sc.sam_pk_for_sad.length = 0;
20290Sstevel@tonic-gate       sc.sam_nonce = 0;
20300Sstevel@tonic-gate       /* Generate checksum */
20310Sstevel@tonic-gate       /*krb5_checksum_size(context, ctype)*/
20320Sstevel@tonic-gate       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
20330Sstevel@tonic-gate 	seed_length,outcksum) */
20340Sstevel@tonic-gate       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
20350Sstevel@tonic-gate 	seed_length) */
20360Sstevel@tonic-gate #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
20370Sstevel@tonic-gate       sc.sam_cksum.contents = (krb5_octet *)
20380Sstevel@tonic-gate 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
20390Sstevel@tonic-gate       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
20400Sstevel@tonic-gate 
20410Sstevel@tonic-gate       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
20420Sstevel@tonic-gate 				       sc.sam_challenge.data,
20430Sstevel@tonic-gate 				       sc.sam_challenge.length,
20440Sstevel@tonic-gate 				       psr.sam_key.contents, /* key */
20450Sstevel@tonic-gate 				       psr.sam_key.length, /* key length */
20460Sstevel@tonic-gate 				       &sc.sam_cksum);
20470Sstevel@tonic-gate       if (retval) { free(sc.sam_cksum.contents); return(retval); }
20480Sstevel@tonic-gate #endif /* 0 */
20490Sstevel@tonic-gate 
20500Sstevel@tonic-gate       retval = encode_krb5_sam_challenge(&sc, &scratch);
20510Sstevel@tonic-gate       if (retval) goto cleanup;
20520Sstevel@tonic-gate       pa_data->magic = KV5M_PA_DATA;
20530Sstevel@tonic-gate       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
20540Sstevel@tonic-gate       pa_data->contents = (unsigned char *) scratch->data;
20550Sstevel@tonic-gate       pa_data->length = scratch->length;
20560Sstevel@tonic-gate 
20570Sstevel@tonic-gate       retval = 0;
20580Sstevel@tonic-gate       break;
20590Sstevel@tonic-gate     case PA_SAM_TYPE_DIGI_PATH:
20600Sstevel@tonic-gate       sc.sam_type_name.data = "Digital Pathways";
20610Sstevel@tonic-gate       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
20620Sstevel@tonic-gate #if 1
20630Sstevel@tonic-gate       sc.sam_challenge_label.data = "Enter the following on your keypad";
20640Sstevel@tonic-gate       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
20650Sstevel@tonic-gate #endif
20660Sstevel@tonic-gate       /* generate digit string, take it mod 1000000 (six digits.) */
20670Sstevel@tonic-gate       {
20680Sstevel@tonic-gate 	int j;
20690Sstevel@tonic-gate 	krb5_keyblock session_key;
20700Sstevel@tonic-gate 	char outputblock[8];
20710Sstevel@tonic-gate 	int i;
20720Sstevel@tonic-gate 
20730Sstevel@tonic-gate 	(void) memset(&session_key, 0, sizeof(krb5_keyblock));
20742881Smp153739 
20750Sstevel@tonic-gate 	(void) memset(inputblock, 0, 8);
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 	retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
20780Sstevel@tonic-gate 					&session_key);
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	if (retval) {
20810Sstevel@tonic-gate 	  /* random key failed */
20820Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
20830Sstevel@tonic-gate 		 retval,
20840Sstevel@tonic-gate 		gettext("generating random challenge for preauth"));
20850Sstevel@tonic-gate 	  return retval;
20860Sstevel@tonic-gate 	}
20870Sstevel@tonic-gate 	/* now session_key has a key which we can pick bits out of */
20880Sstevel@tonic-gate 	/* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
20890Sstevel@tonic-gate 	if (session_key.length != 8) {
20900Sstevel@tonic-gate 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
20910Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
20920Sstevel@tonic-gate 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
20930Sstevel@tonic-gate 		 gettext("keytype didn't match code expectations"));
20940Sstevel@tonic-gate 	  return retval;
20950Sstevel@tonic-gate 	}
20960Sstevel@tonic-gate 	for(i = 0; i<6; i++) {
20970Sstevel@tonic-gate 	  inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
20980Sstevel@tonic-gate 	}
20990Sstevel@tonic-gate 	if (session_key.contents)
21000Sstevel@tonic-gate 	  krb5_free_keyblock_contents(kdc_context, &session_key);
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 	/* retval = krb5_finish_key(kdc_context, &eblock); */
21030Sstevel@tonic-gate 	/* now we have inputblock containing the 8 byte input to DES... */
21040Sstevel@tonic-gate 	sc.sam_challenge.data = inputblock;
21050Sstevel@tonic-gate 	sc.sam_challenge.length = 6;
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate 	encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate 	if (retval) {
21100Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
21110Sstevel@tonic-gate 		 retval,
21120Sstevel@tonic-gate 		gettext("snk4 processing key"));
21130Sstevel@tonic-gate 	}
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate 	{
21160Sstevel@tonic-gate 	    krb5_data plain;
21170Sstevel@tonic-gate 	    krb5_enc_data cipher;
21180Sstevel@tonic-gate 
21190Sstevel@tonic-gate 	    plain.length = 8;
21200Sstevel@tonic-gate 	    plain.data = inputblock;
21210Sstevel@tonic-gate 
21220Sstevel@tonic-gate 	    /* XXX I know this is enough because of the fixed raw enctype.
21230Sstevel@tonic-gate 	       if it's not, the underlying code will return a reasonable
21240Sstevel@tonic-gate 	       error, which should never happen */
21250Sstevel@tonic-gate 	    cipher.ciphertext.length = 8;
21260Sstevel@tonic-gate 	    cipher.ciphertext.data = outputblock;
21270Sstevel@tonic-gate 
21280Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
21290Sstevel@tonic-gate 					 /* XXX */ 0, 0, &plain, &cipher))) {
21300Sstevel@tonic-gate 		com_err(gettext("krb5kdc"),
21310Sstevel@tonic-gate 		retval,
21320Sstevel@tonic-gate 		gettext("snk4 response generation failed"));
21330Sstevel@tonic-gate 		return retval;
21340Sstevel@tonic-gate 	    }
21350Sstevel@tonic-gate 	}
21360Sstevel@tonic-gate 
21370Sstevel@tonic-gate 	/* now output block is the raw bits of the response; convert it
21380Sstevel@tonic-gate 	   to display form */
21390Sstevel@tonic-gate 	for (j=0; j<4; j++) {
21400Sstevel@tonic-gate 	  char n[2];
21410Sstevel@tonic-gate 	  int k;
21420Sstevel@tonic-gate 	  n[0] = outputblock[j] & 0xf;
21430Sstevel@tonic-gate 	  n[1] = (outputblock[j]>>4) & 0xf;
21440Sstevel@tonic-gate 	  for (k=0; k<2; k++) {
21450Sstevel@tonic-gate 	    if(n[k] > 9) n[k] = ((n[k]-1)>>2);
21460Sstevel@tonic-gate 	    /* This is equivalent to:
21470Sstevel@tonic-gate 	       if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
21480Sstevel@tonic-gate 	       if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
21490Sstevel@tonic-gate 	       */
21500Sstevel@tonic-gate 	  }
21510Sstevel@tonic-gate 	  /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
21520Sstevel@tonic-gate 	  /* for v5, we just generate a string */
21530Sstevel@tonic-gate 	  response[2*j+0] = '0' + n[1];
21540Sstevel@tonic-gate 	  response[2*j+1] = '0' + n[0];
21550Sstevel@tonic-gate 	  /* and now, response has what we work with. */
21560Sstevel@tonic-gate 	}
21570Sstevel@tonic-gate 	response[8] = 0;
21580Sstevel@tonic-gate 	predict_response.data = response;
21590Sstevel@tonic-gate 	predict_response.length = 8;
21600Sstevel@tonic-gate #if 0				/* for debugging, hack the output too! */
21610Sstevel@tonic-gate sc.sam_challenge_label.data = response;
21620Sstevel@tonic-gate sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
21630Sstevel@tonic-gate #endif
21640Sstevel@tonic-gate       }
21650Sstevel@tonic-gate 
21660Sstevel@tonic-gate       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
21670Sstevel@tonic-gate       /* string2key on sc.sam_challenge goes in here */
21680Sstevel@tonic-gate       /* eblock is just to set the enctype */
21690Sstevel@tonic-gate       {
21700Sstevel@tonic-gate 	retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
21710Sstevel@tonic-gate 				      &predict_response, 0 /* salt */,
21720Sstevel@tonic-gate 				      &psr.sam_key);
21730Sstevel@tonic-gate 	if (retval) goto cleanup;
21740Sstevel@tonic-gate 
21750Sstevel@tonic-gate 	retval = encode_krb5_predicted_sam_response(&psr, &scratch);
21760Sstevel@tonic-gate 	if (retval) goto cleanup;
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate 	{
21790Sstevel@tonic-gate 	    size_t enclen;
21800Sstevel@tonic-gate 	    krb5_enc_data tmpdata;
21810Sstevel@tonic-gate 
21820Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt_length(context,
21830Sstevel@tonic-gate 						psr_key.enctype,
21840Sstevel@tonic-gate 						scratch->length, &enclen)))
21850Sstevel@tonic-gate 		goto cleanup;
21860Sstevel@tonic-gate 
21870Sstevel@tonic-gate 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
21880Sstevel@tonic-gate 		retval = ENOMEM;
21890Sstevel@tonic-gate 		goto cleanup;
21900Sstevel@tonic-gate 	    }
21910Sstevel@tonic-gate 	    tmpdata.ciphertext.length = enclen;
21920Sstevel@tonic-gate 
21930Sstevel@tonic-gate 	    if ((retval = krb5_c_encrypt(context, &psr_key,
21940Sstevel@tonic-gate 					 /* XXX */ 0, 0, scratch, &tmpdata)))
21950Sstevel@tonic-gate 		goto cleanup;
21960Sstevel@tonic-gate 
21970Sstevel@tonic-gate 	    sc.sam_track_id = tmpdata.ciphertext;
21980Sstevel@tonic-gate 	}
21990Sstevel@tonic-gate 	if (retval) goto cleanup;
22000Sstevel@tonic-gate       }
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate       sc.sam_response_prompt.data = "Enter the displayed response";
22030Sstevel@tonic-gate       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
22040Sstevel@tonic-gate       sc.sam_pk_for_sad.length = 0;
22050Sstevel@tonic-gate       sc.sam_nonce = 0;
22060Sstevel@tonic-gate       /* Generate checksum */
22070Sstevel@tonic-gate       /*krb5_checksum_size(context, ctype)*/
22080Sstevel@tonic-gate       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
22090Sstevel@tonic-gate 	seed_length,outcksum) */
22100Sstevel@tonic-gate       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
22110Sstevel@tonic-gate 	seed_length) */
22120Sstevel@tonic-gate #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
22130Sstevel@tonic-gate       sc.sam_cksum.contents = (krb5_octet *)
22140Sstevel@tonic-gate 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
22150Sstevel@tonic-gate       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
22160Sstevel@tonic-gate 
22170Sstevel@tonic-gate       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
22180Sstevel@tonic-gate 				       sc.sam_challenge.data,
22190Sstevel@tonic-gate 				       sc.sam_challenge.length,
22200Sstevel@tonic-gate 				       psr.sam_key.contents, /* key */
22210Sstevel@tonic-gate 				       psr.sam_key.length, /* key length */
22220Sstevel@tonic-gate 				       &sc.sam_cksum);
22230Sstevel@tonic-gate       if (retval) { free(sc.sam_cksum.contents); return(retval); }
22240Sstevel@tonic-gate #endif /* 0 */
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate       retval = encode_krb5_sam_challenge(&sc, &scratch);
22270Sstevel@tonic-gate       if (retval) goto cleanup;
22280Sstevel@tonic-gate       pa_data->magic = KV5M_PA_DATA;
22290Sstevel@tonic-gate       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
22300Sstevel@tonic-gate       pa_data->contents = (unsigned char *) scratch->data;
22310Sstevel@tonic-gate       pa_data->length = scratch->length;
22320Sstevel@tonic-gate 
22330Sstevel@tonic-gate       retval = 0;
22340Sstevel@tonic-gate       break;
22350Sstevel@tonic-gate     }
22360Sstevel@tonic-gate 
22370Sstevel@tonic-gate cleanup:
22380Sstevel@tonic-gate     krb5_free_keyblock_contents(context, &encrypting_key);
22390Sstevel@tonic-gate     return retval;
22400Sstevel@tonic-gate }
22410Sstevel@tonic-gate 
22420Sstevel@tonic-gate static krb5_error_code
22432881Smp153739 verify_sam_response(krb5_context context, krb5_db_entry *client,
2244*7934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
22452881Smp153739 		    krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
2246*7934SMark.Phalan@Sun.COM 		    krb5_pa_data *pa,
2247*7934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc sam_get_entry_data,
2248*7934SMark.Phalan@Sun.COM 		    void *pa_system_context,
2249*7934SMark.Phalan@Sun.COM 		    void **pa_request_context,
2250*7934SMark.Phalan@Sun.COM 		    krb5_data **e_data,
2251*7934SMark.Phalan@Sun.COM 		    krb5_authdata ***authz_data)
22520Sstevel@tonic-gate {
22530Sstevel@tonic-gate     krb5_error_code		retval;
22540Sstevel@tonic-gate     krb5_data			scratch;
22550Sstevel@tonic-gate     krb5_sam_response		*sr = 0;
22560Sstevel@tonic-gate     krb5_predicted_sam_response	*psr = 0;
22570Sstevel@tonic-gate     krb5_enc_sam_response_enc	*esre = 0;
22580Sstevel@tonic-gate     krb5_timestamp		timenow;
22590Sstevel@tonic-gate     char			*princ_req = 0, *princ_psr = 0;
22600Sstevel@tonic-gate 
22610Sstevel@tonic-gate     scratch.data = (char *) pa->contents;
22620Sstevel@tonic-gate     scratch.length = pa->length;
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
22650Sstevel@tonic-gate 	scratch.data = 0;
22660Sstevel@tonic-gate 	com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
22670Sstevel@tonic-gate 	goto cleanup;
22680Sstevel@tonic-gate     }
22690Sstevel@tonic-gate 
22700Sstevel@tonic-gate     /* XXX We can only handle the challenge/response model of SAM.
22710Sstevel@tonic-gate 	   See passwords-04, par 4.1, 4.2 */
22720Sstevel@tonic-gate     {
22730Sstevel@tonic-gate       krb5_enc_data tmpdata;
22740Sstevel@tonic-gate 
22750Sstevel@tonic-gate       tmpdata.enctype = ENCTYPE_UNKNOWN;
22760Sstevel@tonic-gate       tmpdata.ciphertext = sr->sam_track_id;
22770Sstevel@tonic-gate 
22780Sstevel@tonic-gate       scratch.length = tmpdata.ciphertext.length;
22790Sstevel@tonic-gate       if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
22800Sstevel@tonic-gate 	  retval = ENOMEM;
22810Sstevel@tonic-gate 	  goto cleanup;
22820Sstevel@tonic-gate       }
22830Sstevel@tonic-gate 
22840Sstevel@tonic-gate       if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
22850Sstevel@tonic-gate 				   &tmpdata, &scratch))) {
22860Sstevel@tonic-gate 	  com_err(gettext("krb5kdc"),
22870Sstevel@tonic-gate 		retval,
22880Sstevel@tonic-gate 		gettext("decrypt track_id failed"));
22890Sstevel@tonic-gate 	  goto cleanup;
22900Sstevel@tonic-gate       }
22910Sstevel@tonic-gate     }
22920Sstevel@tonic-gate 
22930Sstevel@tonic-gate     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
22940Sstevel@tonic-gate 	com_err(gettext("krb5kdc"), retval,
22950Sstevel@tonic-gate 		gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
22960Sstevel@tonic-gate 	goto cleanup;
22970Sstevel@tonic-gate     }
22980Sstevel@tonic-gate 
22990Sstevel@tonic-gate     /* Replay detection */
23000Sstevel@tonic-gate     if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
23010Sstevel@tonic-gate 	goto cleanup;
23020Sstevel@tonic-gate     if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
23030Sstevel@tonic-gate 	goto cleanup;
23040Sstevel@tonic-gate     if (strcmp(princ_req, princ_psr) != 0) {
23050Sstevel@tonic-gate 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
23060Sstevel@tonic-gate 		gettext("Principal mismatch in SAM psr! -- replay attack?"));
23070Sstevel@tonic-gate 	goto cleanup;
23080Sstevel@tonic-gate     }
23090Sstevel@tonic-gate 
23100Sstevel@tonic-gate     if ((retval = krb5_timeofday(context, &timenow)))
23110Sstevel@tonic-gate 	goto cleanup;
23120Sstevel@tonic-gate 
23130Sstevel@tonic-gate #ifdef USE_RCACHE
23140Sstevel@tonic-gate     {
23150Sstevel@tonic-gate 	krb5_donot_replay rep;
23160Sstevel@tonic-gate 	extern krb5_deltat rc_lifetime;
23170Sstevel@tonic-gate 	/*
23180Sstevel@tonic-gate 	 * Verify this response came back in a timely manner.
23190Sstevel@tonic-gate 	 * We do this b/c otherwise very old (expunged from the rcache)
23200Sstevel@tonic-gate 	 * psr's would be able to be replayed.
23210Sstevel@tonic-gate 	 */
23220Sstevel@tonic-gate 	if (timenow - psr->stime > rc_lifetime) {
23230Sstevel@tonic-gate 	    com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
23240Sstevel@tonic-gate 	    gettext("SAM psr came back too late! -- replay attack?"));
23250Sstevel@tonic-gate 	    goto cleanup;
23260Sstevel@tonic-gate 	}
23270Sstevel@tonic-gate 
23280Sstevel@tonic-gate 	/* Now check the replay cache. */
23290Sstevel@tonic-gate 	rep.client = princ_psr;
23300Sstevel@tonic-gate 	rep.server = "SAM/rc";  /* Should not match any principal name. */
23310Sstevel@tonic-gate 	rep.ctime = psr->stime;
23320Sstevel@tonic-gate 	rep.cusec = psr->susec;
23332881Smp153739 	retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
23342881Smp153739 	if (retval) {
23350Sstevel@tonic-gate 	    com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
23360Sstevel@tonic-gate 	    goto cleanup;
23370Sstevel@tonic-gate 	}
23380Sstevel@tonic-gate     }
23390Sstevel@tonic-gate #endif /* USE_RCACHE */
23400Sstevel@tonic-gate 
23410Sstevel@tonic-gate 
23420Sstevel@tonic-gate     {
23430Sstevel@tonic-gate 	free(scratch.data);
23440Sstevel@tonic-gate 	scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
23450Sstevel@tonic-gate 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
23460Sstevel@tonic-gate 	    retval = ENOMEM;
23470Sstevel@tonic-gate 	    goto cleanup;
23480Sstevel@tonic-gate 	}
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate 	if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
23510Sstevel@tonic-gate 				     0, &sr->sam_enc_nonce_or_ts, &scratch))) {
23520Sstevel@tonic-gate 	    com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
23530Sstevel@tonic-gate 	    goto cleanup;
23540Sstevel@tonic-gate 	}
23550Sstevel@tonic-gate     }
23560Sstevel@tonic-gate 
23570Sstevel@tonic-gate     if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
23580Sstevel@tonic-gate 	com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
23590Sstevel@tonic-gate 	goto cleanup;
23600Sstevel@tonic-gate     }
23610Sstevel@tonic-gate 
23620Sstevel@tonic-gate     if (esre->sam_timestamp != sr->sam_patimestamp) {
23630Sstevel@tonic-gate       retval = KRB5KDC_ERR_PREAUTH_FAILED;
23640Sstevel@tonic-gate       goto cleanup;
23650Sstevel@tonic-gate     }
23660Sstevel@tonic-gate 
23670Sstevel@tonic-gate     if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
23680Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_SKEW;
23690Sstevel@tonic-gate 	goto cleanup;
23700Sstevel@tonic-gate     }
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate     setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
23730Sstevel@tonic-gate 
23740Sstevel@tonic-gate   cleanup:
23750Sstevel@tonic-gate     if (retval) com_err(gettext("krb5kdc"),
23760Sstevel@tonic-gate 			retval,
23770Sstevel@tonic-gate 			gettext("sam verify failure"));
23780Sstevel@tonic-gate     if (scratch.data) free(scratch.data);
23790Sstevel@tonic-gate     if (sr) free(sr);
23800Sstevel@tonic-gate     if (psr) free(psr);
23810Sstevel@tonic-gate     if (esre) free(esre);
23822881Smp153739     if (princ_psr) free(princ_psr);
23832881Smp153739     if (princ_req) free(princ_req);
23840Sstevel@tonic-gate 
23850Sstevel@tonic-gate     return retval;
23860Sstevel@tonic-gate }
2387