Pass prompter data to the prompter function, implement a UI prompter

function wrapping the kerberos prompter function so that the the
OpenSSL ENGINE can ask for a password when loading the private key.

From: Douglas E. Engert


git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@15040 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
Love Hörnquist Åstrand
2005-04-30 16:12:18 +00:00
parent 48870fb07a
commit a4f747ea8e

View File

@@ -44,6 +44,7 @@ RCSID("$Id$");
#include <openssl/dh.h> #include <openssl/dh.h>
#include <openssl/bn.h> #include <openssl/bn.h>
#include <openssl/engine.h> #include <openssl/engine.h>
#include <openssl/ui.h>
#ifdef HAVE_DIRENT_H #ifdef HAVE_DIRENT_H
#include <dirent.h> #include <dirent.h>
@@ -84,6 +85,16 @@ enum {
} \ } \
} }
/* ENGING_load_private_key requires a UI_METHOD and data
* if to be usable from PAM
*/
struct krb5_ui_data {
krb5_context context;
krb5_prompter_fct prompter;
void * prompter_data;
};
struct krb5_pk_identity { struct krb5_pk_identity {
EVP_PKEY *private_key; EVP_PKEY *private_key;
STACK_OF(X509) *cert; STACK_OF(X509) *cert;
@@ -124,6 +135,76 @@ BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
return 0; return 0;
} }
/*
* UI ex_data has the callback_data as passed to Engine. This is far
* from being complete, we will only process one prompt
*/
static int
krb5_ui_method_read_string(UI *ui, UI_STRING *uis)
{
char *buffer;
size_t length;
krb5_error_code ret;
krb5_prompt prompt;
krb5_data password_data;
struct krb5_ui_data *ui_data;
ui_data = (struct krb5_ui_data *)UI_get_app_data(ui);
switch (UI_get_string_type(uis)) {
case UIT_INFO:
case UIT_ERROR:
/* looks like the RedHat pam_prompter might handle
* INFO and ERROR, Will see what happens */
case UIT_VERIFY:
case UIT_PROMPT:
length = UI_get_result_maxsize(uis);
buffer = malloc(password_data.length);
if (buffer == NULL) {
krb5_set_error_string(ui_data->context, "malloc: out of memory");
return 0;
}
password_data.data = buffer;
password_data.length = length;
prompt.prompt = UI_get0_output_string(uis);
prompt.hidden = !(UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO);
prompt.reply = &password_data;
prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
ret = (*ui_data->prompter)(ui_data->context,
ui_data->prompter_data,
NULL, NULL, 1, &prompt);
if (ret == 0) {
buffer[length - 1] = '\0';
UI_set_result(ui, uis, password_data.data);
/*
* RedHat pam_krb5 pam_prompter does a strdup but others
* may copy into buffer. XXX should we just leak the
* memory instead ?
*/
if (buffer != password_data.data)
free(password_data.data);
memset (buffer, 0, length);
free(buffer);
return 1;
}
memset (buffer, 0, length);
free(buffer);
break;
case UIT_NONE:
case UIT_BOOLEAN:
/* XXX for now do not handle */
break;
}
return 0;
}
static krb5_error_code static krb5_error_code
set_digest_alg(DigestAlgorithmIdentifier *id, set_digest_alg(DigestAlgorithmIdentifier *id,
const heim_oid *oid, const heim_oid *oid,
@@ -1789,6 +1870,7 @@ static krb5_error_code
load_openssl_file(krb5_context context, load_openssl_file(krb5_context context,
char *password, char *password,
krb5_prompter_fct prompter, krb5_prompter_fct prompter,
void *prompter_data,
const char *user_id, const char *user_id,
struct krb5_pk_identity *id) struct krb5_pk_identity *id)
{ {
@@ -1969,6 +2051,7 @@ static krb5_error_code
load_openssl_engine(krb5_context context, load_openssl_engine(krb5_context context,
char *password, char *password,
krb5_prompter_fct prompter, krb5_prompter_fct prompter,
void *prompter_data,
const char *string, const char *string,
struct krb5_pk_identity *id) struct krb5_pk_identity *id)
{ {
@@ -2045,23 +2128,70 @@ load_openssl_engine(krb5_context context,
"PKINIT: openssl engine init %s failed: %s", "PKINIT: openssl engine init %s failed: %s",
ctx.engine_name, ctx.engine_name,
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
ENGINE_free(e);
goto out; goto out;
} }
ENGINE_free(e);
ret = eval_pairs(context, e, ctx.engine_name, "post", ret = eval_pairs(context, e, ctx.engine_name, "post",
ctx.post_cmds, ctx.num_post); ctx.post_cmds, ctx.num_post);
if (ret) if (ret)
goto out; goto out;
/*
* If the engine supports a LOAD_CERT_CTRL function, lets try
* it. OpenSC support this function. Eventially this should be
* a ENGINE_load_cert function if it failes, treat it like a
* non fatal error.
*/
{
struct {
const char * cert_id;
X509 * cert;
} parms;
parms.cert_id = ctx.cert_file;
parms.cert = NULL;
ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1);
if (parms.cert) {
id->cert = sk_X509_new_null();
sk_X509_insert(id->cert, parms.cert, 0);
}
}
if (id->cert == NULL) {
ret = load_openssl_cert(context, ctx.cert_file, &id->cert); ret = load_openssl_cert(context, ctx.cert_file, &id->cert);
if (ret) if (ret)
goto out; goto out;
}
{
UI_METHOD * krb5_ui_method = NULL;
struct krb5_ui_data ui_data;
krb5_ui_method = UI_create_method("Krb5 ui method");
if (krb5_ui_method == NULL) {
krb5_set_error_string(context,
"PKINIT: failed to setup prompter "
"function: %s",
ERR_error_string(ERR_get_error(), NULL));
ret = HEIM_PKINIT_NO_PRIVATE_KEY;
goto out;
}
UI_method_set_reader(krb5_ui_method, krb5_ui_method_read_string);
ui_data.context = context;
ui_data.prompter = prompter;
if (prompter == NULL)
ui_data.prompter = krb5_prompter_posix;
ui_data.prompter_data = prompter_data;
id->private_key = ENGINE_load_private_key(e, id->private_key = ENGINE_load_private_key(e,
ctx.key_id, ctx.key_id,
NULL, krb5_ui_method,
NULL); (void*) &ui_data);
UI_destroy_method(krb5_ui_method);
}
if (id->private_key == NULL) { if (id->private_key == NULL) {
krb5_set_error_string(context, krb5_set_error_string(context,
"PKINIT: failed to load private key: %s", "PKINIT: failed to load private key: %s",
@@ -2093,8 +2223,10 @@ load_openssl_engine(krb5_context context,
free(user_conf); free(user_conf);
if (file_conf) if (file_conf)
free(file_conf); free(file_conf);
if (e) if (e) {
ENGINE_finish(e); /* make sure all shared libs are unloaded */
ENGINE_free(e); ENGINE_free(e);
}
return ret; return ret;
} }
@@ -2105,6 +2237,7 @@ _krb5_pk_load_openssl_id(krb5_context context,
const char *user_id, const char *user_id,
const char *x509_anchors, const char *x509_anchors,
krb5_prompter_fct prompter, krb5_prompter_fct prompter,
void *prompter_data,
char *password) char *password)
{ {
STACK_OF(X509) *trusted_certs = NULL; STACK_OF(X509) *trusted_certs = NULL;
@@ -2117,6 +2250,7 @@ _krb5_pk_load_openssl_id(krb5_context context,
krb5_error_code (*load_pair)(krb5_context, krb5_error_code (*load_pair)(krb5_context,
char *, char *,
krb5_prompter_fct prompter, krb5_prompter_fct prompter,
void * prompter_data,
const char *, const char *,
struct krb5_pk_identity *) = NULL; struct krb5_pk_identity *) = NULL;
@@ -2166,7 +2300,7 @@ _krb5_pk_load_openssl_id(krb5_context context,
ERR_load_crypto_strings(); ERR_load_crypto_strings();
ret = (*load_pair)(context, password, prompter, user_id, id); ret = (*load_pair)(context, password, prompter, prompter_data, user_id, id);
if (ret) if (ret)
goto out; goto out;
@@ -2275,6 +2409,7 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
ctx = opt->private->pk_init_ctx; ctx = opt->private->pk_init_ctx;
if (ctx->dh) if (ctx->dh)
DH_free(ctx->dh); DH_free(ctx->dh);
ctx->dh = NULL;
if (ctx->id) { if (ctx->id) {
if (ctx->id->cert) if (ctx->id->cert)
sk_X509_pop_free(ctx->id->cert, X509_free); sk_X509_pop_free(ctx->id->cert, X509_free);
@@ -2282,9 +2417,13 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
sk_X509_pop_free(ctx->id->trusted_certs, X509_free); sk_X509_pop_free(ctx->id->trusted_certs, X509_free);
if (ctx->id->private_key) if (ctx->id->private_key)
EVP_PKEY_free(ctx->id->private_key); EVP_PKEY_free(ctx->id->private_key);
if (ctx->id->engine) if (ctx->id->engine) {
ENGINE_finish(ctx->id->engine); /* unload shared libs etc */
ENGINE_free(ctx->id->engine); ENGINE_free(ctx->id->engine);
ctx->id->engine = NULL;
}
free(ctx->id); free(ctx->id);
ctx->id = NULL;
} }
opt->private->pk_init_ctx = NULL; opt->private->pk_init_ctx = NULL;
#endif #endif
@@ -2298,6 +2437,7 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
const char *x509_anchors, const char *x509_anchors,
int flags, int flags,
krb5_prompter_fct prompter, krb5_prompter_fct prompter,
void *prompter_data,
char *password) char *password)
{ {
#ifdef PKINIT #ifdef PKINIT
@@ -2320,6 +2460,7 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
user_id, user_id,
x509_anchors, x509_anchors,
prompter, prompter,
prompter_data,
password); password);
if (ret) { if (ret) {
free(opt->private->pk_init_ctx); free(opt->private->pk_init_ctx);