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