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:
@@ -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);
|
||||||
|
Reference in New Issue
Block a user