preauth2.c revision 159d09a20817016f09b3ea28d1bdada4a336bb91
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 1995, 2003 by the Massachusetts Institute of Technology. All
* Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
*/
/*
* This file contains routines for establishing, verifying, and any other
* necessary functions, for utilizing the pre-authentication field of the
*/
#include "k5-int.h"
#include "osconf.h"
#include <preauth_plugin.h>
#include "int-proto.h"
#if !defined(_WIN32)
#include <unistd.h>
#endif
#if TARGET_OS_MAC
static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
#else
/* Solaris Kerberos */
#endif
void *prompter_data,
void *gak_data);
typedef struct _pa_types_t {
int flags;
} pa_types_t;
/* Create the per-krb5_context context. This means loading the modules
* if we haven't done that yet (applications which never obtain initial
* credentials should never hit this routine), breaking up the module's
* list of support pa_types so that we can iterate over the modules more
* easily, and copying over the relevant parts of the module's table. */
void KRB5_CALLCONV
{
void **tables;
struct krb5plugin_preauth_client_ftable_v1 *table;
void *plugin_context;
void **rcpp;
/* Only do this once for each krb5_context */
return;
/* load the plugins for the current context */
return;
}
}
/* pull out the module function tables for all of the modules */
"preauthentication_client_1",
&tables,
return;
}
return;
}
/* count how many modules we ended up loading, and how many preauth
* types we may claim to support as a result */
n_modules = 0;
for (n_tables = 0;
n_tables++) {
for (j = 0; table->pa_type_list[j] > 0; j++) {
n_modules++;
}
}
}
/* allocate the space we need */
return;
}
return;
}
/* fill in the structure */
k = 0;
for (i = 0; i < n_tables; i++) {
#ifdef DEBUG
#endif
continue;
}
for (j = 0; table->pa_type_list[j] > 0; j++) {
/* Only call client_fini once per plugin */
if (j == 0)
else
if (j == 0)
else
/*
* Only call request_init and request_fini once per plugin.
* Only the first module within each plugin will ever
* have request_context filled in. Every module within
* the plugin will have its request_context_pp pointing
* to that entry's request_context. That way all the
* modules within the plugin share the same request_context
*/
if (j == 0) {
} else {
}
#ifdef DEBUG
#endif
k++;
}
}
}
/* return the result */
}
/* Zero the use counts for the modules herein. Usually used before we
* start processing any data from the server, at which point every module
* will again be able to take a crack at whatever the server sent. */
void KRB5_CALLCONV
{
int i;
}
}
}
/*
* Give all the preauth plugins a look at the preauth option which
* has just been set
*/
const char *attr,
const char *value)
{
int i;
void *pctx;
"krb5_preauth_supply_preauth_data: "
"Unable to initialize preauth context");
return retval;
}
/*
* Go down the list of preauth modules, and supply them with the
*/
continue;
if (retval) {
break;
}
}
return retval;
}
/* Free the per-krb5_context preauth_context. This means clearing any
* plugin-specific context which may have been created, and then
* freeing the context itself. */
void KRB5_CALLCONV
{
int i;
void *pctx;
}
}
}
}
}
/* Initialize the per-AS-REQ context. This means calling the client_req_init
* function to give the plugin a chance to allocate a per-request context. */
void KRB5_CALLCONV
{
int i;
/* Limit this to only one attempt per context? */
}
}
}
}
/* Free the per-AS-REQ context. This means clearing any request-specific
* context which the plugin may have created. */
void KRB5_CALLCONV
{
int i;
}
}
}
}
}
/* Add the named encryption type to the existing list of ktypes. */
static void
{
int i;
for (i = 0; i < *out_nktypes; i++) {
if ((*out_ktypes)[i] == ktype)
return;
}
if (ktypes) {
for (i = 0; i < *out_nktypes; i++)
ktypes[i] = (*out_ktypes)[i];
ktypes[i] = 0;
free(*out_ktypes);
*out_ktypes = ktypes;
*out_nktypes = i;
}
}
/*
* Add the given list of pa_data items to the existing list of items.
* Factored out here to make reading the do_preauth logic easier to read.
*/
static int
{
int i, j;
return EINVAL;
}
if (*out_pa_list == NULL) {
/* Allocate room for the new additions and a NULL terminator. */
return ENOMEM;
for (i = 0; i < num_addition; i++)
*out_pa_list = pa_list;
} else {
/*
* Allocate room for the existing entries plus
* the new additions and a NULL terminator.
*/
* sizeof(krb5_pa_data *));
return ENOMEM;
for (i = 0; i < *out_pa_list_size; i++)
pa_list[i] = (*out_pa_list)[i];
for (j = 0; j < num_addition;)
free(*out_pa_list);
*out_pa_list = pa_list;
*out_pa_list_size = i;
}
return 0;
}
/*
* Retrieve a specific piece of information required by the plugin and
* return it in a new krb5_data item. There are separate request_types
* to obtain the data and free it.
*
* This may require massaging data into a contrived format, but it will
* hopefully keep us from having to reveal library-internal functions
* or data to the plugin modules.
*/
static krb5_error_code
{
char *data;
return EINVAL;
return EINVAL;
switch (request_type) {
{
return ENOENT;
return ENOMEM;
return ENOMEM;
}
return 0;
}
break;
return 0;
return 0;
break;
default:
return EINVAL;
}
}
/* Tweak the request body, for now adding any enctypes which the module claims
* to add support for to the list, but in the future perhaps doing more
* involved things. */
void KRB5_CALLCONV
{
int i, j;
return;
}
/* Add the module-specific enctype list to the request, but only if
* it's something we can safely modify. */
continue;
}
}
}
}
/* Find the first module which provides for the named preauth type which also
* hasn't had a chance to run yet (INFO modules don't count, because as a rule
* they don't generate preauth data), and run it. */
static krb5_error_code
void *prompter_data,
void *gak_data,
int *out_pa_list_size,
int *module_ret,
int *module_flags,
{
int i;
struct _krb5_preauth_context_module *module;
return ENOENT;
}
/* iterate over all loaded modules */
/* skip over those which don't match the preauth type */
continue;
/* skip over those which don't match the flags (INFO vs REAL, mainly) */
continue;
/* if it's a REAL module, try to call it only once per library call */
if (module_required_flags & PA_REAL) {
#ifdef DEBUG
#endif
continue;
}
}
/* run the module's callback function */
out_pa_data = NULL;
#ifdef DEBUG
#endif
&out_pa_data);
/* Make note of the module's flags and status. */
*module_ret = ret;
/* Save the new preauth data item. */
if (out_pa_data != NULL) {
int j;
for (j = 0; out_pa_data[j] != NULL; j++);
if (ret != 0)
return ret;
}
break;
}
return ENOENT;
}
return 0;
}
static
{
/* Solaris Kerberos - resync */
return(0);
}
/*ARGSUSED*/
static
void *prompter_data,
void *gak_data)
{
#ifdef DEBUG
/* Solaris Kerberos */
}
#endif
return(ret);
}
/* now get the time of day, and encrypt it accordingly */
return(ret);
return(ret);
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
if (ret) {
return(ret);
}
if (ret)
return(ret);
return(ENOMEM);
}
*out_padata = pa;
return(0);
}
static
{
char *label;
switch (sam_type) {
case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
label = "Challenge for Enigma Logic mechanism";
break;
case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
label = "Challenge for Digital Pathways mechanism";
break;
case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
label = "Challenge for Activcard mechanism";
break;
case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
break;
case PA_SAM_TYPE_SKEY: /* Traditional S/Key */
break;
case PA_SAM_TYPE_SECURID: /* Security Dynamics */
label = "Challenge for Security Dynamics mechanism";
break;
case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
label = "Challenge for Security Dynamics mechanism";
break;
default:
label = "Challenge from authentication server";
break;
}
return(label);
}
/* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
/* XXX Danger! This code is not in sync with the kerberos-password-02
draft. This draft cannot be implemented as written. This code is
compatible with earlier versions of mit krb5 and cygnus kerbnet. */
/*ARGSUSED*/
static
void *prompter_data,
void *gak_data)
{
/* these two get encrypted and stuffed in to sam_response */
krb5_pa_data * pa;
/* Solaris Kerberos */
return EIO;
return(ret);
return(KRB5_SAM_UNSUPPORTED);
}
/* If we need the password from the user (USE_SAD_AS_KEY not set), */
/* then get it here. Exception for "old" KDCs with CryptoCard */
/* support which uses the USE_SAD_AS_KEY flag, but still needs pwd */
/* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */
/* message from the KDC. If it is not set, pick an enctype that we */
/* think the KDC will have for us. */
return(ret);
}
sizeof(name) - 1));
sizeof(banner)-1));
/* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */
/* PROMPTER_INVOCATION */
return(ret);
}
if (sam_challenge->sam_nonce == 0) {
&enc_sam_response_enc.sam_usec))) {
return(ret);
}
}
/* XXX What if more than one flag is set? */
/* Most of this should be taken care of before we get here. We */
/* will need the user's password and as_key to encrypt the SAD */
/* and we want to preserve ordering of user prompts (first */
/* password, then SAM data) so that user's won't be confused. */
}
/* generate a salt using the requested principal */
&defsalt))) {
return(ret);
}
} else {
}
/* generate a key using the supplied password */
if (ret) {
return(ret);
}
/* encrypt the passcode with the key from above */
/* process the key as password */
}
#if 0
&defsalt)) {
return(ret);
}
} else {
}
#else
#endif
/* XXX As of the passwords-04 draft, no enctype is specified,
the server uses ENCTYPE_DES_CBC_MD5. In the future the
server should send a PA-SAM-ETYPE-INFO containing the enctype. */
if (ret) {
return(ret);
}
} else {
/* Eventually, combine SAD with long-term key to get
encryption key. */
return KRB5_PREAUTH_BAD_TYPE;
}
/* copy things from the challenge */
/* encode the encoded part of the response */
&scratch)))
return(ret);
/*
* Solaris Kerberos:
* Using new crypto interface now so we can get rid of the
* old modules.
*/
return(ret);
}
return(ENOMEM);
}
}
if (ret)
return(ret);
/* sam_enc_key is reserved for future use */
return(ENOMEM);
return(ret);
}
*out_padata = pa;
return(0);
}
static
void *prompter_data,
void *gak_data) {
krb5_boolean valid_cksum = 0;
return KRB5_LIBOS_CANTREADPWD;
return(retval);
if (retval)
return(retval);
return(KRB5_SAM_NO_CHECKSUM);
}
return(KRB5_SAM_UNSUPPORTED);
}
return(KRB5_SAM_INVALID_ETYPE);
}
/* All of the above error checks are KDC-specific, that is, they */
/* assume a failure in the KDC reply. By returning anything other */
/* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */
/* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */
/* most likely go on to try the AS_REQ against master KDC */
/* We will need the password to obtain the key used for */
/* the checksum, and encryption of the sam_response. */
/* Go ahead and get it now, preserving the ordering of */
/* prompts for the user. */
if (retval) {
return(retval);
}
}
sizeof(name) - 1));
sizeof(banner)-1));
return(retval);
}
/* Generate salt used by string_to_key() */
if ((retval =
return(retval);
}
} else {
}
/* Get encryption key to be used for checksum and sam_response */
/* as_key = string_to_key(password) */
}
/* generate a key using the supplied password */
if (retval) {
return(retval);
}
/* as_key = combine_key (as_key, string_to_key(SAD)) */
if (retval) {
return(retval);
}
/* This should be a call to the crypto library some day */
/* key types should already match the sam_etype */
if (retval) {
return(retval);
}
}
} else {
/* as_key = string_to_key(SAD) */
}
/* generate a key using the supplied password */
if (retval) {
return(retval);
}
}
/* Now we have a key, verify the checksum on the sam_challenge */
while (*cksum) {
/* Check this cksum */
*cksum, &valid_cksum);
if (retval) {
return(retval);
}
if (valid_cksum)
break;
cksum++;
}
if (!valid_cksum) {
/* If KRB5_SAM_SEND_ENCRYPTED_SAD is set, then password is only */
/* source for checksum key. Therefore, a bad checksum means a */
/* bad password. Don't give that direct feedback to someone */
/* trying to brute-force passwords. */
/*
* Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
* can interpret that as "password incorrect", which is probably
* the best error we can return in this situation.
*/
return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
}
/* fill in enc_sam_response_enc_2 */
} else {
}
/* encode and encrypt enc_sam_response_enc_2 with as_key */
&scratch);
if (retval) {
return(retval);
}
/* Fill in sam_response_2 */
/* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */
/* enc_sam_response_enc_2 from above */
&ciph_len);
if (retval) {
return(retval);
}
return(ENOMEM);
}
if (retval) {
return(retval);
}
/* Encode the sam_response_2 */
if (retval) {
return (retval);
}
/* Almost there, just need to make padata ! */
if (sam_padata == NULL) {
return(ENOMEM);
}
*out_padata = sam_padata;
return(0);
}
static const pa_types_t pa_types[] = {
{
},
{
},
{
},
{
},
{
},
{
-1,
NULL,
0,
},
};
/*
* If one of the modules can adjust its AS_REQ data using the contents of the
* err_reply, return 0. If it's the sort of correction which requires that we
* ask the user another question, we let the calling application deal with it.
*/
{
struct _krb5_preauth_context_module *module;
int i, j;
int out_pa_list_size = 0;
return KRB5KRB_ERR_GENERIC;
}
return KRB5KRB_ERR_GENERIC;
}
out_padata = NULL;
continue;
}
continue;
}
padata[i],
&out_padata) == 0) {
if (out_padata != NULL) {
int k;
for (k = 0; out_padata[k] != NULL; k++);
out_padata, k);
return 0;
}
}
}
}
return ret;
}
{
int h, i, j, out_pa_list_size;
int seen_etype_info2 = 0;
int realdone;
/* Solaris Kerberos */
*out_padata = NULL;
return(0);
}
#ifdef DEBUG
/* Solaris Kerberos */
for (i = 0; in_padata[i]; i++) {
}
}
#endif
out_pa_list = NULL;
out_pa_list_size = 0;
/* first do all the informational preauths, then the first real one */
realdone = 0;
int k, l, etype_found, valid_etype_found;
/*
* This is really gross, but is necessary to prevent
* lossage when talking to a 1.0.x KDC, which returns an
* erroneous PA-PW-SALT when it returns a KRB-ERROR
* requiring additional preauth.
*/
case KRB5_PADATA_ETYPE_INFO:
case KRB5_PADATA_ETYPE_INFO2:
{
if (etype_info) {
continue;
if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
etype_info = NULL;
}
}
if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
}
if (ret) {
ret = 0; /*Ignore error and etype_info element*/
if (etype_info)
etype_info = NULL;
continue;
}
if (etype_info[0] == NULL) {
etype_info = NULL;
break;
}
/*
* Select first etype in our request which is also in
* etype-info (preferring client request ktype order).
*/
for (etype_found = 0, valid_etype_found = 0, k = 0;
for (l = 0; etype_info[l]; l++) {
etype_found++;
break;
}
/* check if program has support for this etype for more
* precise error reporting.
*/
}
}
if (!etype_found) {
/* Solaris Kerberos */
"valid_etype_found = %d",
if (valid_etype_found) {
/* supported enctype but not requested */
goto cleanup;
}
else {
/* unsupported enctype */
goto cleanup;
}
}
else
goto cleanup;
&etype_info[l]->s2kparams,
s2kparams)) != 0)
goto cleanup;
#ifdef DEBUG
for (j = 0; etype_info[j]; j++) {
krb5_etype_info_entry *e = etype_info[j];
}
#endif
break;
}
case KRB5_PADATA_PW_SALT:
case KRB5_PADATA_AFS3_SALT:
if (etype_info)
continue;
break;
default:
;
}
/* Try the internally-provided preauth type list. */
#ifdef DEBUG
#endif
goto cleanup;
}
&out_pa, 1);
if (ret != 0) {
goto cleanup;
}
realdone = 1;
}
}
/* Try to use plugins now. */
if (!realdone) {
int module_ret, module_flags;
#ifdef DEBUG
#endif
paorder[h],
in_padata[i],
opte);
if (ret == 0) {
if (module_ret == 0) {
realdone = 1;
}
}
}
}
}
}
}
if (etype_info)
/* Solaris Kerberos */
return(0);
if (out_pa_list) {
}
if (etype_info)
/* Solaris Kerberos */
return (ret);
}