xref: /onnv-gate/usr/src/cmd/krb5/krb5kdc/kdc_preauth.c (revision 10598:6f30db2c2cd0)
10Sstevel@tonic-gate /*
2*10598SGlenn.Barry@Sun.COM  * Copyright 2009 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>
727934SMark.Phalan@Sun.COM #include "preauth_plugin.h"
737934SMark.Phalan@Sun.COM 
747934SMark.Phalan@Sun.COM #if TARGET_OS_MAC
757934SMark.Phalan@Sun.COM static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
767934SMark.Phalan@Sun.COM #else
777934SMark.Phalan@Sun.COM static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
787934SMark.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 {
907934SMark.Phalan@Sun.COM     const char *name;
910Sstevel@tonic-gate     int		type;
920Sstevel@tonic-gate     int		flags;
937934SMark.Phalan@Sun.COM     void       *plugin_context;
947934SMark.Phalan@Sun.COM     preauth_server_init_proc	init;
957934SMark.Phalan@Sun.COM     preauth_server_fini_proc	fini;
967934SMark.Phalan@Sun.COM     preauth_server_edata_proc	get_edata;
977934SMark.Phalan@Sun.COM     preauth_server_verify_proc	verify_padata;
987934SMark.Phalan@Sun.COM     preauth_server_return_proc	return_padata;
997934SMark.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,
1047934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
1052881Smp153739 		    krb5_kdc_req *request,
1067934SMark.Phalan@Sun.COM 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
1077934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
1087934SMark.Phalan@Sun.COM 		    void *pa_system_context,
1097934SMark.Phalan@Sun.COM 		    void **pa_request_context,
1107934SMark.Phalan@Sun.COM 		    krb5_data **e_data,
1117934SMark.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,
1167934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
1177934SMark.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,
1217934SMark.Phalan@Sun.COM 	        krb5_db_entry *client, krb5_db_entry *server,
1227934SMark.Phalan@Sun.COM 		preauth_get_entry_data_proc get_entry_data,
1237934SMark.Phalan@Sun.COM 		void *pa_system_context,
1247934SMark.Phalan@Sun.COM 		krb5_pa_data *pa_data);
1257934SMark.Phalan@Sun.COM static krb5_error_code
1267934SMark.Phalan@Sun.COM etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
1277934SMark.Phalan@Sun.COM 			 krb5_db_entry *client,
1287934SMark.Phalan@Sun.COM 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
1297934SMark.Phalan@Sun.COM 			 krb5_key_data *client_key,
1307934SMark.Phalan@Sun.COM 			 krb5_keyblock *encrypting_key,
1317934SMark.Phalan@Sun.COM 			 krb5_pa_data **send_pa,
1327934SMark.Phalan@Sun.COM 			 int etype_info2);
1337934SMark.Phalan@Sun.COM 
1347934SMark.Phalan@Sun.COM static krb5_error_code
1357934SMark.Phalan@Sun.COM return_etype_info(krb5_context, krb5_pa_data * padata,
1367934SMark.Phalan@Sun.COM 		  krb5_db_entry *client,
1377934SMark.Phalan@Sun.COM 		  krb5_data *req_pkt,
1387934SMark.Phalan@Sun.COM 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
1397934SMark.Phalan@Sun.COM 		  krb5_key_data *client_key,
1407934SMark.Phalan@Sun.COM 		  krb5_keyblock *encrypting_key,
1417934SMark.Phalan@Sun.COM 		  krb5_pa_data **send_pa,
1427934SMark.Phalan@Sun.COM 		  preauth_get_entry_data_proc get_entry_data,
1437934SMark.Phalan@Sun.COM 		  void *pa_system_context,
1447934SMark.Phalan@Sun.COM 		  void **pa_request_context);
1457934SMark.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,
1497934SMark.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,
1537934SMark.Phalan@Sun.COM 		   krb5_pa_data **send_pa,
1547934SMark.Phalan@Sun.COM 		   preauth_get_entry_data_proc get_entry_data,
1557934SMark.Phalan@Sun.COM 		   void *pa_system_context,
1567934SMark.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,
1617934SMark.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,
1657934SMark.Phalan@Sun.COM 		    krb5_pa_data **send_pa,
1667934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
1677934SMark.Phalan@Sun.COM 		    void *pa_system_context,
1687934SMark.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,
1737934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
1742881Smp153739 		    krb5_kdc_req *request,
1757934SMark.Phalan@Sun.COM 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
1767934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
1777934SMark.Phalan@Sun.COM 		    void *pa_module_context,
1787934SMark.Phalan@Sun.COM 		    void **pa_request_context,
1797934SMark.Phalan@Sun.COM 		    krb5_data **e_data,
1807934SMark.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,
1857934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
1867934SMark.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,
1917934SMark.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,
1957934SMark.Phalan@Sun.COM 		    krb5_pa_data **send_pa,
1967934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc get_entry_data,
1977934SMark.Phalan@Sun.COM 		    void *pa_module_context,
1987934SMark.Phalan@Sun.COM 		    void **pa_request_context);
1990Sstevel@tonic-gate 
2007934SMark.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,
2057934SMark.Phalan@Sun.COM 	NULL,
2067934SMark.Phalan@Sun.COM 	NULL,
2077934SMark.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,
2167934SMark.Phalan@Sun.COM 	NULL,
2177934SMark.Phalan@Sun.COM 	NULL,
2187934SMark.Phalan@Sun.COM 	NULL,
2190Sstevel@tonic-gate 	get_etype_info,
2200Sstevel@tonic-gate 	0,
2217934SMark.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,
2277934SMark.Phalan@Sun.COM 	NULL,
2287934SMark.Phalan@Sun.COM 	NULL,
2297934SMark.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 */
2387934SMark.Phalan@Sun.COM 	NULL,
2397934SMark.Phalan@Sun.COM 	NULL,
2407934SMark.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,
2497934SMark.Phalan@Sun.COM 	NULL,
2507934SMark.Phalan@Sun.COM 	NULL,
2517934SMark.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 */
2607934SMark.Phalan@Sun.COM 	NULL,
2617934SMark.Phalan@Sun.COM 	NULL,
2627934SMark.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 
2707934SMark.Phalan@Sun.COM static krb5_preauth_systems *preauth_systems;
2717934SMark.Phalan@Sun.COM static int n_preauth_systems;
2727934SMark.Phalan@Sun.COM static struct plugin_dir_handle preauth_plugins;
2737934SMark.Phalan@Sun.COM 
2747934SMark.Phalan@Sun.COM krb5_error_code
load_preauth_plugins(krb5_context context)2757934SMark.Phalan@Sun.COM load_preauth_plugins(krb5_context context)
2767934SMark.Phalan@Sun.COM {
2777934SMark.Phalan@Sun.COM     struct errinfo err;
2787934SMark.Phalan@Sun.COM     void **preauth_plugins_ftables;
2797934SMark.Phalan@Sun.COM     struct krb5plugin_preauth_server_ftable_v1 *ftable;
2807934SMark.Phalan@Sun.COM     int module_count, i, j, k;
2817934SMark.Phalan@Sun.COM     void *plugin_context;
2827934SMark.Phalan@Sun.COM     preauth_server_init_proc server_init_proc = NULL;
2837934SMark.Phalan@Sun.COM     char **kdc_realm_names = NULL;
2847934SMark.Phalan@Sun.COM 
2857934SMark.Phalan@Sun.COM     memset(&err, 0, sizeof(err));
2867934SMark.Phalan@Sun.COM 
2877934SMark.Phalan@Sun.COM     /* Attempt to load all of the preauth plugins we can find. */
2887934SMark.Phalan@Sun.COM     PLUGIN_DIR_INIT(&preauth_plugins);
2897934SMark.Phalan@Sun.COM     if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) {
2907934SMark.Phalan@Sun.COM 	if (krb5int_open_plugin_dirs(objdirs, NULL,
2917934SMark.Phalan@Sun.COM 				     &preauth_plugins, &err) != 0) {
2927934SMark.Phalan@Sun.COM 	    return KRB5_PLUGIN_NO_HANDLE;
2937934SMark.Phalan@Sun.COM 	}
2947934SMark.Phalan@Sun.COM     }
2957934SMark.Phalan@Sun.COM 
2967934SMark.Phalan@Sun.COM     /* Get the method tables provided by the loaded plugins. */
2977934SMark.Phalan@Sun.COM     preauth_plugins_ftables = NULL;
2987934SMark.Phalan@Sun.COM     if (krb5int_get_plugin_dir_data(&preauth_plugins,
2997934SMark.Phalan@Sun.COM 				    "preauthentication_server_1",
3007934SMark.Phalan@Sun.COM 				    &preauth_plugins_ftables, &err) != 0) {
3017934SMark.Phalan@Sun.COM 	return KRB5_PLUGIN_NO_HANDLE;
3027934SMark.Phalan@Sun.COM     }
3037934SMark.Phalan@Sun.COM 
3047934SMark.Phalan@Sun.COM     /* Count the valid modules. */
3057934SMark.Phalan@Sun.COM     module_count = sizeof(static_preauth_systems)
3067934SMark.Phalan@Sun.COM 		   / sizeof(static_preauth_systems[0]);
3077934SMark.Phalan@Sun.COM     if (preauth_plugins_ftables != NULL) {
3087934SMark.Phalan@Sun.COM 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
3097934SMark.Phalan@Sun.COM 	    ftable = preauth_plugins_ftables[i];
3107934SMark.Phalan@Sun.COM 	    if ((ftable->flags_proc == NULL) &&
3117934SMark.Phalan@Sun.COM 		(ftable->edata_proc == NULL) &&
3127934SMark.Phalan@Sun.COM 		(ftable->verify_proc == NULL) &&
3137934SMark.Phalan@Sun.COM 		(ftable->return_proc == NULL)) {
3147934SMark.Phalan@Sun.COM 		continue;
3157934SMark.Phalan@Sun.COM 	    }
3167934SMark.Phalan@Sun.COM 	    for (j = 0;
3177934SMark.Phalan@Sun.COM 		 ftable->pa_type_list != NULL &&
3187934SMark.Phalan@Sun.COM 		 ftable->pa_type_list[j] > 0;
3197934SMark.Phalan@Sun.COM 		 j++) {
3207934SMark.Phalan@Sun.COM 		module_count++;
3217934SMark.Phalan@Sun.COM 	    }
3227934SMark.Phalan@Sun.COM 	}
3237934SMark.Phalan@Sun.COM     }
3247934SMark.Phalan@Sun.COM 
3257934SMark.Phalan@Sun.COM     /* Build the complete list of supported preauthentication options, and
3267934SMark.Phalan@Sun.COM      * leave room for a terminator entry. */
3277934SMark.Phalan@Sun.COM     preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1));
3287934SMark.Phalan@Sun.COM     if (preauth_systems == NULL) {
3297934SMark.Phalan@Sun.COM 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
3307934SMark.Phalan@Sun.COM 	return ENOMEM;
3317934SMark.Phalan@Sun.COM     }
3327934SMark.Phalan@Sun.COM 
3337934SMark.Phalan@Sun.COM     /* Build a list of the names of the supported realms for this KDC.
3347934SMark.Phalan@Sun.COM      * The list of names is terminated with a NULL. */
3357934SMark.Phalan@Sun.COM     kdc_realm_names = malloc(sizeof(char *) * (kdc_numrealms + 1));
3367934SMark.Phalan@Sun.COM     if (kdc_realm_names == NULL) {
3377934SMark.Phalan@Sun.COM 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
3387934SMark.Phalan@Sun.COM 	return ENOMEM;
3397934SMark.Phalan@Sun.COM     }
3407934SMark.Phalan@Sun.COM     for (i = 0; i < kdc_numrealms; i++) {
3417934SMark.Phalan@Sun.COM 	kdc_realm_names[i] = kdc_realmlist[i]->realm_name;
3427934SMark.Phalan@Sun.COM     }
3437934SMark.Phalan@Sun.COM     kdc_realm_names[i] = NULL;
3447934SMark.Phalan@Sun.COM 
3457934SMark.Phalan@Sun.COM     /* Add the locally-supplied mechanisms to the dynamic list first. */
3467934SMark.Phalan@Sun.COM     for (i = 0, k = 0;
3477934SMark.Phalan@Sun.COM 	 i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
3487934SMark.Phalan@Sun.COM 	 i++) {
3497934SMark.Phalan@Sun.COM 	if (static_preauth_systems[i].type == -1)
3507934SMark.Phalan@Sun.COM 	    break;
3517934SMark.Phalan@Sun.COM 	preauth_systems[k] = static_preauth_systems[i];
3527934SMark.Phalan@Sun.COM 	/* Try to initialize the preauth system.  If it fails, we'll remove it
3537934SMark.Phalan@Sun.COM 	 * from the list of systems we'll be using. */
3547934SMark.Phalan@Sun.COM 	plugin_context = NULL;
3557934SMark.Phalan@Sun.COM 	server_init_proc = static_preauth_systems[i].init;
3567934SMark.Phalan@Sun.COM 	if ((server_init_proc != NULL) &&
3577934SMark.Phalan@Sun.COM 	    ((*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names) != 0)) {
3587934SMark.Phalan@Sun.COM 	    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
3597934SMark.Phalan@Sun.COM 	    continue;
3607934SMark.Phalan@Sun.COM 	}
3617934SMark.Phalan@Sun.COM 	preauth_systems[k].plugin_context = plugin_context;
3627934SMark.Phalan@Sun.COM 	k++;
3637934SMark.Phalan@Sun.COM     }
3647934SMark.Phalan@Sun.COM 
3657934SMark.Phalan@Sun.COM     /* Now add the dynamically-loaded mechanisms to the list. */
3667934SMark.Phalan@Sun.COM     if (preauth_plugins_ftables != NULL) {
3677934SMark.Phalan@Sun.COM 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
3687934SMark.Phalan@Sun.COM 	    ftable = preauth_plugins_ftables[i];
3697934SMark.Phalan@Sun.COM 	    if ((ftable->flags_proc == NULL) &&
3707934SMark.Phalan@Sun.COM 		(ftable->edata_proc == NULL) &&
3717934SMark.Phalan@Sun.COM 		(ftable->verify_proc == NULL) &&
3727934SMark.Phalan@Sun.COM 		(ftable->return_proc == NULL)) {
3737934SMark.Phalan@Sun.COM 		continue;
3747934SMark.Phalan@Sun.COM 	    }
3757934SMark.Phalan@Sun.COM 	    plugin_context = NULL;
3767934SMark.Phalan@Sun.COM 	    for (j = 0;
3777934SMark.Phalan@Sun.COM 		 ftable->pa_type_list != NULL &&
3787934SMark.Phalan@Sun.COM 		 ftable->pa_type_list[j] > 0;
3797934SMark.Phalan@Sun.COM 		 j++) {
3807934SMark.Phalan@Sun.COM 		/* Try to initialize the plugin.  If it fails, we'll remove it
3817934SMark.Phalan@Sun.COM 		 * from the list of modules we'll be using. */
3827934SMark.Phalan@Sun.COM 		if (j == 0) {
3837934SMark.Phalan@Sun.COM 		    server_init_proc = ftable->init_proc;
3847934SMark.Phalan@Sun.COM 		    if (server_init_proc != NULL) {
3857934SMark.Phalan@Sun.COM 			krb5_error_code initerr;
3867934SMark.Phalan@Sun.COM 			initerr = (*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names);
3877934SMark.Phalan@Sun.COM 			if (initerr) {
3887934SMark.Phalan@Sun.COM 			    const char *emsg;
3897934SMark.Phalan@Sun.COM 			    emsg = krb5_get_error_message(context, initerr);
3907934SMark.Phalan@Sun.COM 			    if (emsg) {
3917934SMark.Phalan@Sun.COM 				krb5_klog_syslog(LOG_ERR,
3927934SMark.Phalan@Sun.COM 					"preauth %s failed to initialize: %s",
3937934SMark.Phalan@Sun.COM 					ftable->name, emsg);
3947934SMark.Phalan@Sun.COM 				krb5_free_error_message(context, emsg);
3957934SMark.Phalan@Sun.COM 			    }
3967934SMark.Phalan@Sun.COM 			    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
3977934SMark.Phalan@Sun.COM 
3987934SMark.Phalan@Sun.COM 			    break;	/* skip all modules in this plugin */
3997934SMark.Phalan@Sun.COM 			}
4007934SMark.Phalan@Sun.COM 		    }
4017934SMark.Phalan@Sun.COM 		}
4027934SMark.Phalan@Sun.COM 		preauth_systems[k].name = ftable->name;
4037934SMark.Phalan@Sun.COM 		preauth_systems[k].type = ftable->pa_type_list[j];
4047934SMark.Phalan@Sun.COM 		if (ftable->flags_proc != NULL)
4057934SMark.Phalan@Sun.COM 		    preauth_systems[k].flags = ftable->flags_proc(context, preauth_systems[k].type);
4067934SMark.Phalan@Sun.COM 		else
4077934SMark.Phalan@Sun.COM 		    preauth_systems[k].flags = 0;
4087934SMark.Phalan@Sun.COM 		preauth_systems[k].plugin_context = plugin_context;
4097934SMark.Phalan@Sun.COM 		preauth_systems[k].init = server_init_proc;
4107934SMark.Phalan@Sun.COM 		/* Only call fini once for each plugin */
4117934SMark.Phalan@Sun.COM 		if (j == 0)
4127934SMark.Phalan@Sun.COM 		    preauth_systems[k].fini = ftable->fini_proc;
4137934SMark.Phalan@Sun.COM 		else
4147934SMark.Phalan@Sun.COM 		    preauth_systems[k].fini = NULL;
4157934SMark.Phalan@Sun.COM 		preauth_systems[k].get_edata = ftable->edata_proc;
4167934SMark.Phalan@Sun.COM 		preauth_systems[k].verify_padata = ftable->verify_proc;
4177934SMark.Phalan@Sun.COM 		preauth_systems[k].return_padata = ftable->return_proc;
4187934SMark.Phalan@Sun.COM 		preauth_systems[k].free_pa_reqctx =
4197934SMark.Phalan@Sun.COM 		    ftable->freepa_reqcontext_proc;
4207934SMark.Phalan@Sun.COM 		k++;
4217934SMark.Phalan@Sun.COM 	    }
4227934SMark.Phalan@Sun.COM 	}
4237934SMark.Phalan@Sun.COM 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
4247934SMark.Phalan@Sun.COM     }
4257934SMark.Phalan@Sun.COM     free(kdc_realm_names);
4267934SMark.Phalan@Sun.COM     n_preauth_systems = k;
4277934SMark.Phalan@Sun.COM     /* Add the end-of-list marker. */
4287934SMark.Phalan@Sun.COM     preauth_systems[k].name = "[end]";
4297934SMark.Phalan@Sun.COM     preauth_systems[k].type = -1;
4307934SMark.Phalan@Sun.COM     return 0;
4317934SMark.Phalan@Sun.COM }
4327934SMark.Phalan@Sun.COM 
4337934SMark.Phalan@Sun.COM krb5_error_code
unload_preauth_plugins(krb5_context context)4347934SMark.Phalan@Sun.COM unload_preauth_plugins(krb5_context context)
4357934SMark.Phalan@Sun.COM {
4367934SMark.Phalan@Sun.COM     int i;
4377934SMark.Phalan@Sun.COM     if (preauth_systems != NULL) {
4387934SMark.Phalan@Sun.COM 	for (i = 0; i < n_preauth_systems; i++) {
4397934SMark.Phalan@Sun.COM 	    if (preauth_systems[i].fini != NULL) {
4407934SMark.Phalan@Sun.COM 	        (*preauth_systems[i].fini)(context,
4417934SMark.Phalan@Sun.COM 					   preauth_systems[i].plugin_context);
4427934SMark.Phalan@Sun.COM 	    }
4437934SMark.Phalan@Sun.COM 	    memset(&preauth_systems[i], 0, sizeof(preauth_systems[i]));
4447934SMark.Phalan@Sun.COM 	}
4457934SMark.Phalan@Sun.COM 	free(preauth_systems);
4467934SMark.Phalan@Sun.COM 	preauth_systems = NULL;
4477934SMark.Phalan@Sun.COM 	n_preauth_systems = 0;
4487934SMark.Phalan@Sun.COM 	krb5int_close_plugin_dirs(&preauth_plugins);
4497934SMark.Phalan@Sun.COM     }
4507934SMark.Phalan@Sun.COM     return 0;
4517934SMark.Phalan@Sun.COM }
4527934SMark.Phalan@Sun.COM 
4537934SMark.Phalan@Sun.COM /*
4547934SMark.Phalan@Sun.COM  * The make_padata_context() function creates a space for storing any context
4557934SMark.Phalan@Sun.COM  * information which will be needed by return_padata() later.  Each preauth
4567934SMark.Phalan@Sun.COM  * type gets a context storage location of its own.
4577934SMark.Phalan@Sun.COM  */
4587934SMark.Phalan@Sun.COM struct request_pa_context {
4597934SMark.Phalan@Sun.COM     int n_contexts;
4607934SMark.Phalan@Sun.COM     struct {
4617934SMark.Phalan@Sun.COM 	krb5_preauth_systems *pa_system;
4627934SMark.Phalan@Sun.COM 	void *pa_context;
4637934SMark.Phalan@Sun.COM     } *contexts;
4647934SMark.Phalan@Sun.COM };
4657934SMark.Phalan@Sun.COM 
4667934SMark.Phalan@Sun.COM static krb5_error_code
make_padata_context(krb5_context context,void ** padata_context)4677934SMark.Phalan@Sun.COM make_padata_context(krb5_context context, void **padata_context)
4687934SMark.Phalan@Sun.COM {
4697934SMark.Phalan@Sun.COM     int i;
4707934SMark.Phalan@Sun.COM     struct request_pa_context *ret;
4717934SMark.Phalan@Sun.COM 
4727934SMark.Phalan@Sun.COM     ret = malloc(sizeof(*ret));
4737934SMark.Phalan@Sun.COM     if (ret == NULL) {
4747934SMark.Phalan@Sun.COM 	return ENOMEM;
4757934SMark.Phalan@Sun.COM     }
4767934SMark.Phalan@Sun.COM 
4777934SMark.Phalan@Sun.COM     ret->n_contexts = n_preauth_systems;
4787934SMark.Phalan@Sun.COM     ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
4797934SMark.Phalan@Sun.COM     if (ret->contexts == NULL) {
4807934SMark.Phalan@Sun.COM 	free(ret);
4817934SMark.Phalan@Sun.COM 	return ENOMEM;
4827934SMark.Phalan@Sun.COM     }
4837934SMark.Phalan@Sun.COM 
4847934SMark.Phalan@Sun.COM     memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
4857934SMark.Phalan@Sun.COM 
4867934SMark.Phalan@Sun.COM     for (i = 0; i < ret->n_contexts; i++) {
4877934SMark.Phalan@Sun.COM 	ret->contexts[i].pa_system = &preauth_systems[i];
4887934SMark.Phalan@Sun.COM 	ret->contexts[i].pa_context = NULL;
4897934SMark.Phalan@Sun.COM     }
4907934SMark.Phalan@Sun.COM 
4917934SMark.Phalan@Sun.COM     *padata_context = ret;
4927934SMark.Phalan@Sun.COM 
4937934SMark.Phalan@Sun.COM     return 0;
4947934SMark.Phalan@Sun.COM }
4957934SMark.Phalan@Sun.COM 
4967934SMark.Phalan@Sun.COM /*
4977934SMark.Phalan@Sun.COM  * The free_padata_context function frees any context information pointers
4987934SMark.Phalan@Sun.COM  * which the check_padata() function created but which weren't already cleaned
4997934SMark.Phalan@Sun.COM  * up by return_padata().
5007934SMark.Phalan@Sun.COM  */
5017934SMark.Phalan@Sun.COM krb5_error_code
free_padata_context(krb5_context kcontext,void ** padata_context)5027934SMark.Phalan@Sun.COM free_padata_context(krb5_context kcontext, void **padata_context)
5037934SMark.Phalan@Sun.COM {
5047934SMark.Phalan@Sun.COM     struct request_pa_context *context;
5057934SMark.Phalan@Sun.COM     krb5_preauth_systems *preauth_system;
5067934SMark.Phalan@Sun.COM     void **pctx, *mctx;
5077934SMark.Phalan@Sun.COM     int i;
5087934SMark.Phalan@Sun.COM 
5097934SMark.Phalan@Sun.COM     if (padata_context == NULL)
5107934SMark.Phalan@Sun.COM 	return 0;
5117934SMark.Phalan@Sun.COM 
5127934SMark.Phalan@Sun.COM     context = *padata_context;
5137934SMark.Phalan@Sun.COM 
5147934SMark.Phalan@Sun.COM     for (i = 0; i < context->n_contexts; i++) {
5157934SMark.Phalan@Sun.COM 	if (context->contexts[i].pa_context != NULL) {
5167934SMark.Phalan@Sun.COM 	    preauth_system = context->contexts[i].pa_system;
5177934SMark.Phalan@Sun.COM 	    mctx = preauth_system->plugin_context;
5187934SMark.Phalan@Sun.COM 	    if (preauth_system->free_pa_reqctx != NULL) {
5197934SMark.Phalan@Sun.COM 		pctx = &context->contexts[i].pa_context;
5207934SMark.Phalan@Sun.COM 		(*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx);
5217934SMark.Phalan@Sun.COM 	    }
5227934SMark.Phalan@Sun.COM 	    context->contexts[i].pa_context = NULL;
5237934SMark.Phalan@Sun.COM 	}
5247934SMark.Phalan@Sun.COM     }
5257934SMark.Phalan@Sun.COM 
5267934SMark.Phalan@Sun.COM     free(context->contexts);
5277934SMark.Phalan@Sun.COM     free(context);
5287934SMark.Phalan@Sun.COM 
5297934SMark.Phalan@Sun.COM     return 0;
5307934SMark.Phalan@Sun.COM }
5317934SMark.Phalan@Sun.COM 
5327934SMark.Phalan@Sun.COM /* Retrieve a specified tl_data item from the given entry, and return its
5337934SMark.Phalan@Sun.COM  * contents in a new krb5_data, which must be freed by the caller. */
5347934SMark.Phalan@Sun.COM static krb5_error_code
get_entry_tl_data(krb5_context context,krb5_db_entry * entry,krb5_int16 tl_data_type,krb5_data ** result)5357934SMark.Phalan@Sun.COM get_entry_tl_data(krb5_context context, krb5_db_entry *entry,
5367934SMark.Phalan@Sun.COM 		  krb5_int16 tl_data_type, krb5_data **result)
5377934SMark.Phalan@Sun.COM {
5387934SMark.Phalan@Sun.COM     krb5_tl_data *tl;
5397934SMark.Phalan@Sun.COM     for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
5407934SMark.Phalan@Sun.COM 	if (tl->tl_data_type == tl_data_type) {
5417934SMark.Phalan@Sun.COM 	    *result = malloc(sizeof(krb5_data));
5427934SMark.Phalan@Sun.COM 	    if (*result == NULL) {
5437934SMark.Phalan@Sun.COM 		return ENOMEM;
5447934SMark.Phalan@Sun.COM 	    }
5457934SMark.Phalan@Sun.COM 	    (*result)->magic = KV5M_DATA;
5467934SMark.Phalan@Sun.COM 	    (*result)->data = malloc(tl->tl_data_length);
5477934SMark.Phalan@Sun.COM 	    if ((*result)->data == NULL) {
5487934SMark.Phalan@Sun.COM 		free(*result);
5497934SMark.Phalan@Sun.COM 		*result = NULL;
5507934SMark.Phalan@Sun.COM 		return ENOMEM;
5517934SMark.Phalan@Sun.COM 	    }
5527934SMark.Phalan@Sun.COM 	    memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length);
5537934SMark.Phalan@Sun.COM 	    return 0;
5547934SMark.Phalan@Sun.COM 	}
5557934SMark.Phalan@Sun.COM     }
5567934SMark.Phalan@Sun.COM     return ENOENT;
5577934SMark.Phalan@Sun.COM }
5587934SMark.Phalan@Sun.COM 
5597934SMark.Phalan@Sun.COM /*
5607934SMark.Phalan@Sun.COM  * Retrieve a specific piece of information pertaining to the entry or the
5617934SMark.Phalan@Sun.COM  * request and return it in a new krb5_data item which the caller must free.
5627934SMark.Phalan@Sun.COM  *
5637934SMark.Phalan@Sun.COM  * This may require massaging data into a contrived format, but it will
5647934SMark.Phalan@Sun.COM  * hopefully keep us from having to reveal library-internal functions to
5657934SMark.Phalan@Sun.COM  * modules.
5667934SMark.Phalan@Sun.COM  */
5677934SMark.Phalan@Sun.COM static krb5_error_code
get_entry_data(krb5_context context,krb5_kdc_req * request,krb5_db_entry * entry,krb5_int32 type,krb5_data ** result)5687934SMark.Phalan@Sun.COM get_entry_data(krb5_context context,
5697934SMark.Phalan@Sun.COM 	       krb5_kdc_req *request, krb5_db_entry *entry,
5707934SMark.Phalan@Sun.COM 	       krb5_int32  type,
5717934SMark.Phalan@Sun.COM 	       krb5_data **result)
5727934SMark.Phalan@Sun.COM {
5737934SMark.Phalan@Sun.COM     int i, k;
5747934SMark.Phalan@Sun.COM     krb5_data *ret;
5757934SMark.Phalan@Sun.COM     krb5_deltat *delta;
5767934SMark.Phalan@Sun.COM     krb5_keyblock *keys;
5777934SMark.Phalan@Sun.COM     krb5_key_data *entry_key;
5787934SMark.Phalan@Sun.COM 
5797934SMark.Phalan@Sun.COM     switch (type) {
5807934SMark.Phalan@Sun.COM     case krb5plugin_preauth_entry_request_certificate:
5817934SMark.Phalan@Sun.COM 	return get_entry_tl_data(context, entry,
5827934SMark.Phalan@Sun.COM 				 KRB5_TL_USER_CERTIFICATE, result);
5837934SMark.Phalan@Sun.COM 	break;
5847934SMark.Phalan@Sun.COM     case krb5plugin_preauth_entry_max_time_skew:
5857934SMark.Phalan@Sun.COM 	ret = malloc(sizeof(krb5_data));
5867934SMark.Phalan@Sun.COM 	if (ret == NULL)
5877934SMark.Phalan@Sun.COM 	    return ENOMEM;
5887934SMark.Phalan@Sun.COM 	delta = malloc(sizeof(krb5_deltat));
5897934SMark.Phalan@Sun.COM 	if (delta == NULL) {
5907934SMark.Phalan@Sun.COM 	    free(ret);
5917934SMark.Phalan@Sun.COM 	    return ENOMEM;
5927934SMark.Phalan@Sun.COM 	}
5937934SMark.Phalan@Sun.COM 	*delta = context->clockskew;
5947934SMark.Phalan@Sun.COM 	ret->data = (char *) delta;
5957934SMark.Phalan@Sun.COM 	ret->length = sizeof(*delta);
5967934SMark.Phalan@Sun.COM 	*result = ret;
5977934SMark.Phalan@Sun.COM 	return 0;
5987934SMark.Phalan@Sun.COM 	break;
5997934SMark.Phalan@Sun.COM     case krb5plugin_preauth_keys:
6007934SMark.Phalan@Sun.COM 	ret = malloc(sizeof(krb5_data));
6017934SMark.Phalan@Sun.COM 	if (ret == NULL)
6027934SMark.Phalan@Sun.COM 	    return ENOMEM;
6037934SMark.Phalan@Sun.COM 	keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1));
6047934SMark.Phalan@Sun.COM 	if (keys == NULL) {
6057934SMark.Phalan@Sun.COM 	    free(ret);
6067934SMark.Phalan@Sun.COM 	    return ENOMEM;
6077934SMark.Phalan@Sun.COM 	}
6087934SMark.Phalan@Sun.COM 	ret->data = (char *) keys;
6097934SMark.Phalan@Sun.COM 	ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
6107934SMark.Phalan@Sun.COM 	memset(ret->data, 0, ret->length);
6117934SMark.Phalan@Sun.COM 	k = 0;
6127934SMark.Phalan@Sun.COM 	for (i = 0; i < request->nktypes; i++) {
6137934SMark.Phalan@Sun.COM 	    entry_key = NULL;
6147934SMark.Phalan@Sun.COM 	    if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
6157934SMark.Phalan@Sun.COM 				      -1, 0, &entry_key) != 0)
6167934SMark.Phalan@Sun.COM 		continue;
6177934SMark.Phalan@Sun.COM 	    if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
6187934SMark.Phalan@Sun.COM 					    entry_key, &keys[k], NULL) != 0) {
6197934SMark.Phalan@Sun.COM 		if (keys[k].contents != NULL)
6207934SMark.Phalan@Sun.COM 		    krb5_free_keyblock_contents(context, &keys[k]);
6217934SMark.Phalan@Sun.COM 		memset(&keys[k], 0, sizeof(keys[k]));
6227934SMark.Phalan@Sun.COM 		continue;
6237934SMark.Phalan@Sun.COM 	    }
6247934SMark.Phalan@Sun.COM 	    k++;
6257934SMark.Phalan@Sun.COM 	}
6267934SMark.Phalan@Sun.COM 	if (k > 0) {
6277934SMark.Phalan@Sun.COM 	    *result = ret;
6287934SMark.Phalan@Sun.COM 	    return 0;
6297934SMark.Phalan@Sun.COM 	} else {
6307934SMark.Phalan@Sun.COM 	    free(keys);
6317934SMark.Phalan@Sun.COM 	    free(ret);
6327934SMark.Phalan@Sun.COM 	}
6337934SMark.Phalan@Sun.COM 	break;
6347934SMark.Phalan@Sun.COM     case krb5plugin_preauth_request_body:
6357934SMark.Phalan@Sun.COM 	ret = NULL;
6367934SMark.Phalan@Sun.COM 	encode_krb5_kdc_req_body(request, &ret);
6377934SMark.Phalan@Sun.COM 	if (ret != NULL) {
6387934SMark.Phalan@Sun.COM 	    *result = ret;
6397934SMark.Phalan@Sun.COM 	    return 0;
6407934SMark.Phalan@Sun.COM 	}
6417934SMark.Phalan@Sun.COM 	return ASN1_PARSE_ERROR;
6427934SMark.Phalan@Sun.COM 	break;
6437934SMark.Phalan@Sun.COM     default:
6447934SMark.Phalan@Sun.COM 	break;
6457934SMark.Phalan@Sun.COM     }
6467934SMark.Phalan@Sun.COM     return ENOENT;
6477934SMark.Phalan@Sun.COM }
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate static krb5_error_code
find_pa_system(int type,krb5_preauth_systems ** preauth)6500Sstevel@tonic-gate find_pa_system(int type, krb5_preauth_systems **preauth)
6510Sstevel@tonic-gate {
6527934SMark.Phalan@Sun.COM     krb5_preauth_systems *ap;
6537934SMark.Phalan@Sun.COM 
6547934SMark.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 
6637934SMark.Phalan@Sun.COM static krb5_error_code
find_pa_context(krb5_preauth_systems * pa_sys,struct request_pa_context * context,void *** pa_context)6647934SMark.Phalan@Sun.COM find_pa_context(krb5_preauth_systems *pa_sys,
6657934SMark.Phalan@Sun.COM 		struct request_pa_context *context,
6667934SMark.Phalan@Sun.COM 		void ***pa_context)
6677934SMark.Phalan@Sun.COM {
6687934SMark.Phalan@Sun.COM     int i;
6697934SMark.Phalan@Sun.COM 
6707934SMark.Phalan@Sun.COM     *pa_context = 0;
6717934SMark.Phalan@Sun.COM 
6727934SMark.Phalan@Sun.COM     if (context == NULL)
6737934SMark.Phalan@Sun.COM 	return KRB5KRB_ERR_GENERIC;
6747934SMark.Phalan@Sun.COM 
6757934SMark.Phalan@Sun.COM     for (i = 0; i < context->n_contexts; i++) {
6767934SMark.Phalan@Sun.COM 	if (context->contexts[i].pa_system == pa_sys) {
6777934SMark.Phalan@Sun.COM 	    *pa_context = &context->contexts[i].pa_context;
6787934SMark.Phalan@Sun.COM 	    return 0;
6797934SMark.Phalan@Sun.COM 	}
6807934SMark.Phalan@Sun.COM     }
6817934SMark.Phalan@Sun.COM 
6827934SMark.Phalan@Sun.COM     return KRB5KRB_ERR_GENERIC;
6837934SMark.Phalan@Sun.COM }
6847934SMark.Phalan@Sun.COM 
6857934SMark.Phalan@Sun.COM /*
6867934SMark.Phalan@Sun.COM  * Create a list of indices into the preauth_systems array, sorted by order of
6877934SMark.Phalan@Sun.COM  * preference.
6887934SMark.Phalan@Sun.COM  */
6897934SMark.Phalan@Sun.COM static krb5_boolean
pa_list_includes(krb5_pa_data ** pa_data,krb5_preauthtype pa_type)6907934SMark.Phalan@Sun.COM pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
6917934SMark.Phalan@Sun.COM {
6927934SMark.Phalan@Sun.COM     while (*pa_data != NULL) {
6937934SMark.Phalan@Sun.COM 	if ((*pa_data)->pa_type == pa_type)
6947934SMark.Phalan@Sun.COM 	    return TRUE;
6957934SMark.Phalan@Sun.COM 	pa_data++;
6967934SMark.Phalan@Sun.COM     }
6977934SMark.Phalan@Sun.COM     return FALSE;
6987934SMark.Phalan@Sun.COM }
6997934SMark.Phalan@Sun.COM static void
sort_pa_order(krb5_context context,krb5_kdc_req * request,int * pa_order)7007934SMark.Phalan@Sun.COM sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
7017934SMark.Phalan@Sun.COM {
7027934SMark.Phalan@Sun.COM     int i, j, k, n_repliers, n_key_replacers;
7037934SMark.Phalan@Sun.COM 
7047934SMark.Phalan@Sun.COM     /* First, set up the default order. */
7057934SMark.Phalan@Sun.COM     i = 0;
7067934SMark.Phalan@Sun.COM     for (j = 0; j < n_preauth_systems; j++) {
7077934SMark.Phalan@Sun.COM         if (preauth_systems[j].return_padata != NULL)
7087934SMark.Phalan@Sun.COM 	    pa_order[i++] = j;
7097934SMark.Phalan@Sun.COM     }
7107934SMark.Phalan@Sun.COM     n_repliers = i;
7117934SMark.Phalan@Sun.COM     pa_order[n_repliers] = -1;
7127934SMark.Phalan@Sun.COM 
7137934SMark.Phalan@Sun.COM     /* Reorder so that PA_REPLACES_KEY modules are listed first. */
7147934SMark.Phalan@Sun.COM     for (i = 0; i < n_repliers; i++) {
7157934SMark.Phalan@Sun.COM 	/* If this module replaces the key, then it's okay to leave it where it
7167934SMark.Phalan@Sun.COM 	 * is in the order. */
7177934SMark.Phalan@Sun.COM 	if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
7187934SMark.Phalan@Sun.COM 	    continue;
7197934SMark.Phalan@Sun.COM 	/* If not, search for a module which does, and swap in the first one we
7207934SMark.Phalan@Sun.COM 	 * find. */
7217934SMark.Phalan@Sun.COM         for (j = i + 1; j < n_repliers; j++) {
7227934SMark.Phalan@Sun.COM 	    if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
7237934SMark.Phalan@Sun.COM                 k = pa_order[j];
7247934SMark.Phalan@Sun.COM 		pa_order[j] = pa_order[i];
7257934SMark.Phalan@Sun.COM 		pa_order[i] = k;
7267934SMark.Phalan@Sun.COM 		break;
7277934SMark.Phalan@Sun.COM 	    }
7287934SMark.Phalan@Sun.COM         }
7297934SMark.Phalan@Sun.COM     }
7307934SMark.Phalan@Sun.COM 
7317934SMark.Phalan@Sun.COM     if (request->padata != NULL) {
7327934SMark.Phalan@Sun.COM 	/* Now reorder the subset of modules which replace the key,
7337934SMark.Phalan@Sun.COM 	 * bubbling those which handle pa_data types provided by the
7347934SMark.Phalan@Sun.COM 	 * client ahead of the others. */
7357934SMark.Phalan@Sun.COM 	for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
7367934SMark.Phalan@Sun.COM 	    continue;
7377934SMark.Phalan@Sun.COM 	}
7387934SMark.Phalan@Sun.COM 	n_key_replacers = i;
7397934SMark.Phalan@Sun.COM 	for (i = 0; i < n_key_replacers; i++) {
7407934SMark.Phalan@Sun.COM 	    if (pa_list_includes(request->padata,
7417934SMark.Phalan@Sun.COM 				preauth_systems[pa_order[i]].type))
7427934SMark.Phalan@Sun.COM 		continue;
7437934SMark.Phalan@Sun.COM 	    for (j = i + 1; j < n_key_replacers; j++) {
7447934SMark.Phalan@Sun.COM 		if (pa_list_includes(request->padata,
7457934SMark.Phalan@Sun.COM 				    preauth_systems[pa_order[j]].type)) {
7467934SMark.Phalan@Sun.COM 		    k = pa_order[j];
7477934SMark.Phalan@Sun.COM 		    pa_order[j] = pa_order[i];
7487934SMark.Phalan@Sun.COM 		    pa_order[i] = k;
7497934SMark.Phalan@Sun.COM 		    break;
7507934SMark.Phalan@Sun.COM 		}
7517934SMark.Phalan@Sun.COM 	    }
7527934SMark.Phalan@Sun.COM 	}
7537934SMark.Phalan@Sun.COM     }
7547934SMark.Phalan@Sun.COM #ifdef DEBUG
7557934SMark.Phalan@Sun.COM     krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
7567934SMark.Phalan@Sun.COM     for (i = 0; i < n_preauth_systems; i++) {
7577934SMark.Phalan@Sun.COM 	if (preauth_systems[i].return_padata != NULL)
7587934SMark.Phalan@Sun.COM             krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
7597934SMark.Phalan@Sun.COM 			     preauth_systems[i].type);
7607934SMark.Phalan@Sun.COM     }
7617934SMark.Phalan@Sun.COM     krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
7627934SMark.Phalan@Sun.COM     for (i = 0; pa_order[i] != -1; i++) {
7637934SMark.Phalan@Sun.COM         krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
7647934SMark.Phalan@Sun.COM 			 preauth_systems[pa_order[i]].name,
7657934SMark.Phalan@Sun.COM 			 preauth_systems[pa_order[i]].type);
7667934SMark.Phalan@Sun.COM     }
7677934SMark.Phalan@Sun.COM #endif
7687934SMark.Phalan@Sun.COM }
7697934SMark.Phalan@Sun.COM 
missing_required_preauth(krb5_db_entry * client,krb5_db_entry * server,krb5_enc_tkt_part * enc_tkt_reply)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 
get_preauth_hint_list(krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_data * e_data)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);
8217934SMark.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;
8247934SMark.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) {
8397934SMark.Phalan@Sun.COM 	  retval = (ap->get_edata)(kdc_context, request, client, server,
8407934SMark.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     }
855*10598SGlenn.Barry@Sun.COM     retval = encode_krb5_padata_sequence((krb5_pa_data * const *) 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 /*
8687934SMark.Phalan@Sun.COM  * Add authorization data returned from preauth modules to the ticket
8697934SMark.Phalan@Sun.COM  * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
8707934SMark.Phalan@Sun.COM  */
8717934SMark.Phalan@Sun.COM static krb5_error_code
add_authorization_data(krb5_enc_tkt_part * enc_tkt_part,krb5_authdata ** ad)8727934SMark.Phalan@Sun.COM add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
8737934SMark.Phalan@Sun.COM {
8747934SMark.Phalan@Sun.COM     krb5_authdata **newad;
8757934SMark.Phalan@Sun.COM     int oldones, newones;
8767934SMark.Phalan@Sun.COM     int i;
8777934SMark.Phalan@Sun.COM 
8787934SMark.Phalan@Sun.COM     if (enc_tkt_part == NULL || ad == NULL)
8797934SMark.Phalan@Sun.COM 	return EINVAL;
8807934SMark.Phalan@Sun.COM 
8817934SMark.Phalan@Sun.COM     for (newones = 0; ad[newones] != NULL; newones++);
8827934SMark.Phalan@Sun.COM     if (newones == 0)
8837934SMark.Phalan@Sun.COM 	return 0;   /* nothing to add */
8847934SMark.Phalan@Sun.COM 
8857934SMark.Phalan@Sun.COM     if (enc_tkt_part->authorization_data == NULL)
8867934SMark.Phalan@Sun.COM 	oldones = 0;
8877934SMark.Phalan@Sun.COM     else
8887934SMark.Phalan@Sun.COM 	for (oldones = 0;
8897934SMark.Phalan@Sun.COM 	     enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
8907934SMark.Phalan@Sun.COM 
8917934SMark.Phalan@Sun.COM     newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
8927934SMark.Phalan@Sun.COM     if (newad == NULL)
8937934SMark.Phalan@Sun.COM 	return ENOMEM;
8947934SMark.Phalan@Sun.COM 
8957934SMark.Phalan@Sun.COM     /* Copy any existing pointers */
8967934SMark.Phalan@Sun.COM     for (i = 0; i < oldones; i++)
8977934SMark.Phalan@Sun.COM 	newad[i] = enc_tkt_part->authorization_data[i];
8987934SMark.Phalan@Sun.COM 
8997934SMark.Phalan@Sun.COM     /* Add the new ones */
9007934SMark.Phalan@Sun.COM     for (i = 0; i < newones; i++)
9017934SMark.Phalan@Sun.COM 	newad[oldones+i] = ad[i];
9027934SMark.Phalan@Sun.COM 
9037934SMark.Phalan@Sun.COM     /* Terminate the new list */
9047934SMark.Phalan@Sun.COM     newad[oldones+i] = NULL;
9057934SMark.Phalan@Sun.COM 
9067934SMark.Phalan@Sun.COM     /* Free any existing list */
9077934SMark.Phalan@Sun.COM     if (enc_tkt_part->authorization_data != NULL)
9087934SMark.Phalan@Sun.COM 	free(enc_tkt_part->authorization_data);
9097934SMark.Phalan@Sun.COM 
9107934SMark.Phalan@Sun.COM     /* Install our new list */
9117934SMark.Phalan@Sun.COM     enc_tkt_part->authorization_data = newad;
9127934SMark.Phalan@Sun.COM 
9137934SMark.Phalan@Sun.COM     return 0;
9147934SMark.Phalan@Sun.COM }
9157934SMark.Phalan@Sun.COM 
9167934SMark.Phalan@Sun.COM /*
9170Sstevel@tonic-gate  * This routine is called to verify the preauthentication information
9180Sstevel@tonic-gate  * for a V5 request.
9197934SMark.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
check_padata(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,void ** padata_context,krb5_data * e_data)9257934SMark.Phalan@Sun.COM check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
9267934SMark.Phalan@Sun.COM 	      krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
9277934SMark.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;
9327934SMark.Phalan@Sun.COM     void **pa_context;
9337934SMark.Phalan@Sun.COM     krb5_data *pa_e_data = NULL, *tmp_e_data = NULL;
9347934SMark.Phalan@Sun.COM     int	pa_ok = 0, pa_found = 0;
9357934SMark.Phalan@Sun.COM     krb5_error_code saved_retval = 0;
9367934SMark.Phalan@Sun.COM     int use_saved_retval = 0;
9377934SMark.Phalan@Sun.COM     const char *emsg;
9387934SMark.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 
9437934SMark.Phalan@Sun.COM     if (make_padata_context(context, padata_context) != 0) {
9447934SMark.Phalan@Sun.COM 	return KRB5KRB_ERR_GENERIC;
9457934SMark.Phalan@Sun.COM     }
9467934SMark.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;
9567934SMark.Phalan@Sun.COM 	if (find_pa_context(pa_sys, *padata_context, &pa_context))
9577934SMark.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++;
9647934SMark.Phalan@Sun.COM 	retval = pa_sys->verify_padata(context, client, req_pkt, request,
9657934SMark.Phalan@Sun.COM 				       enc_tkt_reply, *padata,
9667934SMark.Phalan@Sun.COM 				       get_entry_data, pa_sys->plugin_context,
9677934SMark.Phalan@Sun.COM 				       pa_context, &tmp_e_data, &tmp_authz_data);
9680Sstevel@tonic-gate 	if (retval) {
9697934SMark.Phalan@Sun.COM 	    emsg = krb5_get_error_message (context, retval);
9700Sstevel@tonic-gate 	    krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
9717934SMark.Phalan@Sun.COM 			      pa_sys->name, emsg);
9727934SMark.Phalan@Sun.COM 	    krb5_free_error_message (context, emsg);
9737934SMark.Phalan@Sun.COM 	    /* Ignore authorization data returned from modules that fail */
9747934SMark.Phalan@Sun.COM 	    if (tmp_authz_data != NULL) {
9757934SMark.Phalan@Sun.COM 		krb5_free_authdata(context, tmp_authz_data);
9767934SMark.Phalan@Sun.COM 		tmp_authz_data = NULL;
9777934SMark.Phalan@Sun.COM 	    }
9780Sstevel@tonic-gate 	    if (pa_sys->flags & PA_REQUIRED) {
9797934SMark.Phalan@Sun.COM 		/* free up any previous edata we might have been saving */
9807934SMark.Phalan@Sun.COM 		if (pa_e_data != NULL)
9817934SMark.Phalan@Sun.COM 		    krb5_free_data(context, pa_e_data);
9827934SMark.Phalan@Sun.COM 		pa_e_data = tmp_e_data;
9837934SMark.Phalan@Sun.COM 		tmp_e_data = NULL;
9847934SMark.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 	    }
9887934SMark.Phalan@Sun.COM 	    /*
9897934SMark.Phalan@Sun.COM 	     * We'll return edata from either the first PA_REQUIRED module
9907934SMark.Phalan@Sun.COM 	     * that fails, or the first non-PA_REQUIRED module that fails.
9917934SMark.Phalan@Sun.COM 	     * Hang on to edata from the first non-PA_REQUIRED module.
9927934SMark.Phalan@Sun.COM 	     * If we've already got one saved, simply discard this one.
9937934SMark.Phalan@Sun.COM 	     */
9947934SMark.Phalan@Sun.COM 	    if (tmp_e_data != NULL) {
9957934SMark.Phalan@Sun.COM 		if (pa_e_data == NULL) {
9967934SMark.Phalan@Sun.COM 		    /* save the first error code and e-data */
9977934SMark.Phalan@Sun.COM 		    pa_e_data = tmp_e_data;
9987934SMark.Phalan@Sun.COM 		    tmp_e_data = NULL;
9997934SMark.Phalan@Sun.COM 		    saved_retval = retval;
10007934SMark.Phalan@Sun.COM 		    use_saved_retval = 1;
10017934SMark.Phalan@Sun.COM 		} else {
10027934SMark.Phalan@Sun.COM 		    /* discard this extra e-data from non-PA_REQUIRED module */
10037934SMark.Phalan@Sun.COM 		    krb5_free_data(context, tmp_e_data);
10047934SMark.Phalan@Sun.COM 		    tmp_e_data = NULL;
10057934SMark.Phalan@Sun.COM 		}
10067934SMark.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
10117934SMark.Phalan@Sun.COM 	    /* Ignore any edata returned on success */
10127934SMark.Phalan@Sun.COM 	    if (tmp_e_data != NULL) {
10137934SMark.Phalan@Sun.COM 	        krb5_free_data(context, tmp_e_data);
10147934SMark.Phalan@Sun.COM 		tmp_e_data = NULL;
10157934SMark.Phalan@Sun.COM 	    }
10167934SMark.Phalan@Sun.COM 	    /* Add any authorization data to the ticket */
10177934SMark.Phalan@Sun.COM 	    if (tmp_authz_data != NULL) {
10187934SMark.Phalan@Sun.COM 		add_authorization_data(enc_tkt_reply, tmp_authz_data);
10197934SMark.Phalan@Sun.COM 		free(tmp_authz_data);
10207934SMark.Phalan@Sun.COM 		tmp_authz_data = NULL;
10217934SMark.Phalan@Sun.COM 	    }
10220Sstevel@tonic-gate 	    pa_ok = 1;
10237934SMark.Phalan@Sun.COM 	    if (pa_sys->flags & PA_SUFFICIENT)
10240Sstevel@tonic-gate 		break;
10250Sstevel@tonic-gate 	}
10260Sstevel@tonic-gate     }
10277934SMark.Phalan@Sun.COM 
10287934SMark.Phalan@Sun.COM     /* Don't bother copying and returning e-data on success */
10297934SMark.Phalan@Sun.COM     if (pa_ok && pa_e_data != NULL) {
10307934SMark.Phalan@Sun.COM 	krb5_free_data(context, pa_e_data);
10317934SMark.Phalan@Sun.COM 	pa_e_data = NULL;
10327934SMark.Phalan@Sun.COM     }
10337934SMark.Phalan@Sun.COM     /* Return any e-data from the preauth that caused us to exit the loop */
10347934SMark.Phalan@Sun.COM     if (pa_e_data != NULL) {
10357934SMark.Phalan@Sun.COM 	e_data->data = malloc(pa_e_data->length);
10367934SMark.Phalan@Sun.COM 	if (e_data->data == NULL) {
10377934SMark.Phalan@Sun.COM 	    krb5_free_data(context, pa_e_data);
10387934SMark.Phalan@Sun.COM 	    /* Solaris Kerberos */
10397934SMark.Phalan@Sun.COM 	    return ENOMEM;
10407934SMark.Phalan@Sun.COM 	}
10417934SMark.Phalan@Sun.COM 	memcpy(e_data->data, pa_e_data->data, pa_e_data->length);
10427934SMark.Phalan@Sun.COM 	e_data->length = pa_e_data->length;
10437934SMark.Phalan@Sun.COM 	krb5_free_data(context, pa_e_data);
10447934SMark.Phalan@Sun.COM 	pa_e_data = NULL;
10457934SMark.Phalan@Sun.COM 	if (use_saved_retval != 0)
10467934SMark.Phalan@Sun.COM 	    retval = saved_retval;
10477934SMark.Phalan@Sun.COM     }
10487934SMark.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 &&
10547934SMark.Phalan@Sun.COM 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
10557934SMark.Phalan@Sun.COM 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
10560Sstevel@tonic-gate        return 0;
10570Sstevel@tonic-gate 
10587934SMark.Phalan@Sun.COM     if (!pa_found) {
10597934SMark.Phalan@Sun.COM 	emsg = krb5_get_error_message(context, retval);
10607934SMark.Phalan@Sun.COM 	krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
10617934SMark.Phalan@Sun.COM 	krb5_free_error_message(context, emsg);
10627934SMark.Phalan@Sun.COM     }
10637934SMark.Phalan@Sun.COM     /* The following switch statement allows us
10647934SMark.Phalan@Sun.COM      * to return some preauth system errors back to the client.
10657934SMark.Phalan@Sun.COM      */
10667934SMark.Phalan@Sun.COM     switch(retval) {
10677934SMark.Phalan@Sun.COM     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
10682881Smp153739     case KRB5KRB_AP_ERR_SKEW:
10697934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_ETYPE_NOSUPP:
10707934SMark.Phalan@Sun.COM     /* rfc 4556 */
10717934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
10727934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_INVALID_SIG:
10737934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
10747934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
10757934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_INVALID_CERTIFICATE:
10767934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_REVOKED_CERTIFICATE:
10777934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
10787934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
10797934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
10807934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
10817934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
10827934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
10837934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
10847934SMark.Phalan@Sun.COM     /* earlier drafts of what became rfc 4556 */
10857934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
10867934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_KDC_NOT_TRUSTED:
10877934SMark.Phalan@Sun.COM     case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
10887934SMark.Phalan@Sun.COM     /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
10897934SMark.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
return_padata(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,void ** padata_context)11017934SMark.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,
11037934SMark.Phalan@Sun.COM 	      krb5_key_data *client_key, krb5_keyblock *encrypting_key,
11047934SMark.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;
11127934SMark.Phalan@Sun.COM     int *			pa_order;
11137934SMark.Phalan@Sun.COM     int *			pa_type;
11140Sstevel@tonic-gate     int 			size = 0;
11157934SMark.Phalan@Sun.COM     void **			pa_context;
11167934SMark.Phalan@Sun.COM     krb5_boolean		key_modified;
11177934SMark.Phalan@Sun.COM     krb5_keyblock		original_key;
11187934SMark.Phalan@Sun.COM     if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) {
11197934SMark.Phalan@Sun.COM 	return KRB5KRB_ERR_GENERIC;
11207934SMark.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;
11297934SMark.Phalan@Sun.COM     if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
11307934SMark.Phalan@Sun.COM 	free(send_pa_list);
11317934SMark.Phalan@Sun.COM 	return ENOMEM;
11327934SMark.Phalan@Sun.COM     }
11337934SMark.Phalan@Sun.COM     sort_pa_order(context, request, pa_order);
11347934SMark.Phalan@Sun.COM 
11357934SMark.Phalan@Sun.COM     retval = krb5_copy_keyblock_contents(context, encrypting_key,
11367934SMark.Phalan@Sun.COM 					 &original_key);
11377934SMark.Phalan@Sun.COM     if (retval) {
11387934SMark.Phalan@Sun.COM 	free(send_pa_list);
11397934SMark.Phalan@Sun.COM 	free(pa_order);
11407934SMark.Phalan@Sun.COM 	return retval;
11417934SMark.Phalan@Sun.COM     }
11427934SMark.Phalan@Sun.COM     key_modified = FALSE;
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate     send_pa = send_pa_list;
11450Sstevel@tonic-gate     *send_pa = 0;
11467934SMark.Phalan@Sun.COM 
11477934SMark.Phalan@Sun.COM     for (pa_type = pa_order; *pa_type != -1; pa_type++) {
11487934SMark.Phalan@Sun.COM 	ap = &preauth_systems[*pa_type];
11497934SMark.Phalan@Sun.COM         if (!key_modified)
11507934SMark.Phalan@Sun.COM 	    if (original_key.enctype != encrypting_key->enctype)
11517934SMark.Phalan@Sun.COM                 key_modified = TRUE;
11527934SMark.Phalan@Sun.COM         if (!key_modified)
11537934SMark.Phalan@Sun.COM 	    if (original_key.length != encrypting_key->length)
11547934SMark.Phalan@Sun.COM                 key_modified = TRUE;
11557934SMark.Phalan@Sun.COM         if (!key_modified)
11567934SMark.Phalan@Sun.COM 	    if (memcmp(original_key.contents, encrypting_key->contents,
11577934SMark.Phalan@Sun.COM 		       original_key.length) != 0)
11587934SMark.Phalan@Sun.COM                 key_modified = TRUE;
11597934SMark.Phalan@Sun.COM 	if (key_modified && (ap->flags & PA_REPLACES_KEY))
11607934SMark.Phalan@Sun.COM 	    continue;
11610Sstevel@tonic-gate 	if (ap->return_padata == 0)
11620Sstevel@tonic-gate 	    continue;
11637934SMark.Phalan@Sun.COM 	if (find_pa_context(ap, *padata_context, &pa_context))
11647934SMark.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 	}
11747934SMark.Phalan@Sun.COM 	if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
11757934SMark.Phalan@Sun.COM 					client_key, encrypting_key, send_pa,
11767934SMark.Phalan@Sun.COM 					get_entry_data, ap->plugin_context,
11777934SMark.Phalan@Sun.COM 					pa_context))) {
11780Sstevel@tonic-gate 	    goto cleanup;
11797934SMark.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);
11967934SMark.Phalan@Sun.COM 
11977934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
11987934SMark.Phalan@Sun.COM     krb5_free_keyblock_contents(context, &original_key);
11997934SMark.Phalan@Sun.COM     free(pa_order);
12007934SMark.Phalan@Sun.COM 
12010Sstevel@tonic-gate     return (retval);
12020Sstevel@tonic-gate }
12032881Smp153739 
12040Sstevel@tonic-gate static krb5_boolean
enctype_requires_etype_info_2(krb5_enctype enctype)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
request_contains_enctype(krb5_context context,const krb5_kdc_req * request,krb5_enctype enctype)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
verify_enc_timestamp(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * pa,preauth_get_entry_data_proc ets_get_entry_data,void * pa_system_context,void ** pa_request_context,krb5_data ** e_data,krb5_authdata *** authz_data)12362881Smp153739 verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
12377934SMark.Phalan@Sun.COM 		     krb5_data *req_pkt,
12382881Smp153739 		     krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
12397934SMark.Phalan@Sun.COM 		     krb5_pa_data *pa,
12407934SMark.Phalan@Sun.COM 		     preauth_get_entry_data_proc ets_get_entry_data,
12417934SMark.Phalan@Sun.COM 		     void *pa_system_context,
12427934SMark.Phalan@Sun.COM 		     void **pa_request_context,
12437934SMark.Phalan@Sun.COM 		     krb5_data **e_data,
12447934SMark.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;
12557934SMark.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
_make_etype_info_entry(krb5_context context,krb5_kdc_req * request,krb5_key_data * client_key,krb5_enctype etype,krb5_etype_info_entry ** entry,int etype_info2)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
etype_info_helper(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_pa_data * pa_data,int etype_info2)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)
1464*10598SGlenn.Barry@Sun.COM 	retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry,
14652881Smp153739 				    &scratch);
1466*10598SGlenn.Barry@Sun.COM     else 	retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) 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
get_etype_info(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,krb5_pa_data * pa_data)14830Sstevel@tonic-gate get_etype_info(krb5_context context, krb5_kdc_req *request,
14842881Smp153739 	       krb5_db_entry *client, krb5_db_entry *server,
14857934SMark.Phalan@Sun.COM 	       preauth_get_entry_data_proc etype_get_entry_data,
14867934SMark.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
get_etype_info2(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,krb5_pa_data * pa_data)15000Sstevel@tonic-gate get_etype_info2(krb5_context context, krb5_kdc_req *request,
15012881Smp153739 	       krb5_db_entry *client, krb5_db_entry *server,
15027934SMark.Phalan@Sun.COM 	       preauth_get_entry_data_proc etype_get_entry_data,
15037934SMark.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
etype_info_as_rep_helper(krb5_context context,krb5_pa_data * padata,krb5_db_entry * client,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,int etype_info2)15107934SMark.Phalan@Sun.COM etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
15117934SMark.Phalan@Sun.COM 			 krb5_db_entry *client,
15127934SMark.Phalan@Sun.COM 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
15137934SMark.Phalan@Sun.COM 			 krb5_key_data *client_key,
15147934SMark.Phalan@Sun.COM 			 krb5_keyblock *encrypting_key,
15157934SMark.Phalan@Sun.COM 			 krb5_pa_data **send_pa,
15167934SMark.Phalan@Sun.COM 			 int etype_info2)
15170Sstevel@tonic-gate {
15187934SMark.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;
15237934SMark.Phalan@Sun.COM 
15247934SMark.Phalan@Sun.COM     /*
15257934SMark.Phalan@Sun.COM      * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
15267934SMark.Phalan@Sun.COM      * enctypes.
15277934SMark.Phalan@Sun.COM      */
15287934SMark.Phalan@Sun.COM     if (!etype_info2) {
15297934SMark.Phalan@Sun.COM 	for (i = 0; i < request->nktypes; i++) {
15307934SMark.Phalan@Sun.COM 	    if (enctype_requires_etype_info_2(request->ktype[i])) {
15317934SMark.Phalan@Sun.COM 		*send_pa = NULL;
15327934SMark.Phalan@Sun.COM 		return 0;
15337934SMark.Phalan@Sun.COM 	    }
15347934SMark.Phalan@Sun.COM 	}
15357934SMark.Phalan@Sun.COM     }
15367934SMark.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;
15407934SMark.Phalan@Sun.COM     if (etype_info2)
15417934SMark.Phalan@Sun.COM 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
15427934SMark.Phalan@Sun.COM     else
15437934SMark.Phalan@Sun.COM 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO;
15447934SMark.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;
15527934SMark.Phalan@Sun.COM     retval = _make_etype_info_entry(context, request,
15537934SMark.Phalan@Sun.COM 				    client_key, encrypting_key->enctype,
15547934SMark.Phalan@Sun.COM 				    entry, etype_info2);
15550Sstevel@tonic-gate     if (retval)
15560Sstevel@tonic-gate 	goto cleanup;
15577934SMark.Phalan@Sun.COM 
15587934SMark.Phalan@Sun.COM     if (etype_info2)
1559*10598SGlenn.Barry@Sun.COM 	retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry, &scratch);
15607934SMark.Phalan@Sun.COM     else
1561*10598SGlenn.Barry@Sun.COM 	retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry, &scratch);
15627934SMark.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 
15867934SMark.Phalan@Sun.COM static krb5_error_code
return_etype_info2(krb5_context context,krb5_pa_data * padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,void ** pa_request_context)15877934SMark.Phalan@Sun.COM return_etype_info2(krb5_context context, krb5_pa_data * padata,
15887934SMark.Phalan@Sun.COM 		   krb5_db_entry *client,
15897934SMark.Phalan@Sun.COM 		   krb5_data *req_pkt,
15907934SMark.Phalan@Sun.COM 		   krb5_kdc_req *request, krb5_kdc_rep *reply,
15917934SMark.Phalan@Sun.COM 		   krb5_key_data *client_key,
15927934SMark.Phalan@Sun.COM 		   krb5_keyblock *encrypting_key,
15937934SMark.Phalan@Sun.COM 		   krb5_pa_data **send_pa,
15947934SMark.Phalan@Sun.COM 		   preauth_get_entry_data_proc etype_get_entry_data,
15957934SMark.Phalan@Sun.COM 	           void *pa_system_context,
15967934SMark.Phalan@Sun.COM 		   void **pa_request_context)
15977934SMark.Phalan@Sun.COM {
15987934SMark.Phalan@Sun.COM     return etype_info_as_rep_helper(context, padata, client, request, reply,
15997934SMark.Phalan@Sun.COM 				    client_key, encrypting_key, send_pa, 1);
16007934SMark.Phalan@Sun.COM }
16017934SMark.Phalan@Sun.COM 
16027934SMark.Phalan@Sun.COM 
16037934SMark.Phalan@Sun.COM static krb5_error_code
return_etype_info(krb5_context context,krb5_pa_data * padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc etypeget_entry_data,void * pa_system_context,void ** pa_request_context)16047934SMark.Phalan@Sun.COM return_etype_info(krb5_context context, krb5_pa_data * padata,
16057934SMark.Phalan@Sun.COM 		  krb5_db_entry *client,
16067934SMark.Phalan@Sun.COM 		  krb5_data *req_pkt,
16077934SMark.Phalan@Sun.COM 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
16087934SMark.Phalan@Sun.COM 		  krb5_key_data *client_key,
16097934SMark.Phalan@Sun.COM 		  krb5_keyblock *encrypting_key,
16107934SMark.Phalan@Sun.COM 		  krb5_pa_data **send_pa,
16117934SMark.Phalan@Sun.COM 		  preauth_get_entry_data_proc etypeget_entry_data,
16127934SMark.Phalan@Sun.COM 	          void *pa_system_context,
16137934SMark.Phalan@Sun.COM 		  void **pa_request_context)
16147934SMark.Phalan@Sun.COM {
16157934SMark.Phalan@Sun.COM     return etype_info_as_rep_helper(context, padata, client, request, reply,
16167934SMark.Phalan@Sun.COM 				    client_key, encrypting_key, send_pa, 0);
16177934SMark.Phalan@Sun.COM }
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate static krb5_error_code
return_pw_salt(krb5_context context,krb5_pa_data * in_padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc etype_get_entry_data,void * pa_system_context,void ** pa_request_context)16202881Smp153739 return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
16217934SMark.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,
16237934SMark.Phalan@Sun.COM 	       krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
16247934SMark.Phalan@Sun.COM 	       preauth_get_entry_data_proc etype_get_entry_data,
16257934SMark.Phalan@Sun.COM 	       void *pa_system_context,
16267934SMark.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
return_sam_data(krb5_context context,krb5_pa_data * in_padata,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc sam_get_entry_data,void * pa_system_context,void ** pa_request_context)17102881Smp153739 return_sam_data(krb5_context context, krb5_pa_data *in_padata,
17117934SMark.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,
17137934SMark.Phalan@Sun.COM 		krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
17147934SMark.Phalan@Sun.COM 		preauth_get_entry_data_proc sam_get_entry_data,
17157934SMark.Phalan@Sun.COM 		void *pa_system_context,
17167934SMark.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
get_sam_edata(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,preauth_get_entry_data_proc sam_get_entry_data,void * pa_system_context,krb5_pa_data * pa_data)18562881Smp153739 get_sam_edata(krb5_context context, krb5_kdc_req *request,
18572881Smp153739 	      krb5_db_entry *client, krb5_db_entry *server,
18587934SMark.Phalan@Sun.COM 	      preauth_get_entry_data_proc sam_get_entry_data,
18597934SMark.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
verify_sam_response(krb5_context context,krb5_db_entry * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * pa,preauth_get_entry_data_proc sam_get_entry_data,void * pa_system_context,void ** pa_request_context,krb5_data ** e_data,krb5_authdata *** authz_data)22432881Smp153739 verify_sam_response(krb5_context context, krb5_db_entry *client,
22447934SMark.Phalan@Sun.COM 		    krb5_data *req_pkt,
22452881Smp153739 		    krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
22467934SMark.Phalan@Sun.COM 		    krb5_pa_data *pa,
22477934SMark.Phalan@Sun.COM 		    preauth_get_entry_data_proc sam_get_entry_data,
22487934SMark.Phalan@Sun.COM 		    void *pa_system_context,
22497934SMark.Phalan@Sun.COM 		    void **pa_request_context,
22507934SMark.Phalan@Sun.COM 		    krb5_data **e_data,
22517934SMark.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