summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@redhat.com>2013-06-28 17:12:39 -0400
committerGreg Hudson <ghudson@mit.edu>2013-07-17 14:57:12 -0400
commita3abb0bf5fade0009c9899624d4b996a4e12a49f (patch)
tree53a9266ad3374b5d11859e9fffd29c51297c3568 /src/plugins
parentc5bf0caa8abf2b931f5ad258463d706d3cfd5f5b (diff)
downloadkrb5-a3abb0bf5fade0009c9899624d4b996a4e12a49f.tar.gz
krb5-a3abb0bf5fade0009c9899624d4b996a4e12a49f.tar.xz
krb5-a3abb0bf5fade0009c9899624d4b996a4e12a49f.zip
Support PKINIT OpenSSL deferred identity prompting
Add a password to the set of things that we can pass to a PEM password callback and the function we use for loading PKCS12 bundles. If we're meant to defer identity prompts, just store the name of the identity which we're loading. Otherwise, if we're passed a password, use it. Otherwise, use the prompter callback. Add a password to the set of things that we can pass to the function that we use for logging in to PKCS11 tokens, too, but if we're deferring identity prompts, just return the identity name without doing anything else. If not, and we're passed a password, use that. Otherwise, try to use the prompter callback to get one. ticket: 7680
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c214
1 files changed, 159 insertions, 55 deletions
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 1312555b65..af6aea8787 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -116,7 +116,7 @@ static krb5_error_code pkinit_find_private_key
CK_OBJECT_HANDLE *objp);
static krb5_error_code pkinit_login
(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
- CK_TOKEN_INFO *tip);
+ CK_TOKEN_INFO *tip, const char *password);
static krb5_error_code pkinit_open_session
(krb5_context context, pkinit_identity_crypto_context id_cryptoctx);
static void * pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p);
@@ -662,7 +662,9 @@ cleanup:
struct get_key_cb_data {
krb5_context context;
pkinit_identity_crypto_context id_cryptoctx;
+ const char *fsname;
char *filename;
+ const char *password;
};
static int
@@ -676,29 +678,50 @@ get_key_cb(char *buf, int size, int rwflag, void *userdata)
krb5_error_code retval;
char *prompt;
- if (asprintf(&prompt, "%s %s", _("Pass phrase for"), data->filename) < 0)
+ if (data->id_cryptoctx->defer_id_prompt) {
+ /* Supply the identity name to be passed to a responder callback. */
+ pkinit_set_deferred_id(&data->id_cryptoctx->deferred_ids,
+ data->fsname, 0, NULL);
return -1;
- rdat.data = buf;
- rdat.length = size;
- kprompt.prompt = prompt;
- kprompt.hidden = 1;
- kprompt.reply = &rdat;
- prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
-
- /* PROMPTER_INVOCATION */
- k5int_set_prompt_types(data->context, &prompt_type);
- id_cryptoctx = data->id_cryptoctx;
- retval = data->id_cryptoctx->prompter(data->context,
- id_cryptoctx->prompter_data, NULL,
- NULL, 1, &kprompt);
- k5int_set_prompt_types(data->context, 0);
- free(prompt);
- return retval ? -1 : (int)rdat.length;
+ }
+ if (data->password == NULL) {
+ /* We don't already have a password to use, so prompt for one. */
+ if (data->id_cryptoctx->prompter == NULL)
+ return -1;
+ if (asprintf(&prompt, "%s %s", _("Pass phrase for"),
+ data->filename) < 0)
+ return -1;
+ rdat.data = buf;
+ rdat.length = size;
+ kprompt.prompt = prompt;
+ kprompt.hidden = 1;
+ kprompt.reply = &rdat;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+
+ /* PROMPTER_INVOCATION */
+ k5int_set_prompt_types(data->context, &prompt_type);
+ id_cryptoctx = data->id_cryptoctx;
+ retval = (data->id_cryptoctx->prompter)(data->context,
+ id_cryptoctx->prompter_data,
+ NULL, NULL, 1, &kprompt);
+ k5int_set_prompt_types(data->context, 0);
+ free(prompt);
+ if (retval != 0)
+ return -1;
+ } else {
+ /* Just use the already-supplied password. */
+ rdat.length = strlen(data->password);
+ if ((int)rdat.length >= size)
+ return -1;
+ snprintf(buf, size, "%s", data->password);
+ }
+ return (int)rdat.length;
}
static krb5_error_code
get_key(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
- char *filename, EVP_PKEY **retkey)
+ char *filename, const char *fsname, EVP_PKEY **retkey,
+ const char *password)
{
EVP_PKEY *pkey = NULL;
BIO *tmp = NULL;
@@ -721,8 +744,10 @@ get_key(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
cb_data.context = context;
cb_data.id_cryptoctx = id_cryptoctx;
cb_data.filename = filename;
+ cb_data.fsname = fsname;
+ cb_data.password = password;
pkey = PEM_read_bio_PrivateKey(tmp, NULL, get_key_cb, &cb_data);
- if (pkey == NULL) {
+ if (pkey == NULL && !id_cryptoctx->defer_id_prompt) {
retval = EIO;
pkiDebug("failed to read private key from %s\n", filename);
goto cleanup;
@@ -877,7 +902,7 @@ pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx)
return;
if (ctx->p11 != NULL) {
- if (ctx->session) {
+ if (ctx->session != CK_INVALID_HANDLE) {
ctx->p11->C_CloseSession(ctx->session);
ctx->session = CK_INVALID_HANDLE;
}
@@ -1399,7 +1424,7 @@ cms_signeddata_verify(krb5_context context,
etype = CMS_get0_eContentType(cms);
/*
- * Prior to 1.10 the MIT client incorrectly omitted the pkinit structure
+ * Prior to 1.10 the MIT client incorrectly emitted the pkinit structure
* directly in a CMS ContentInfo rather than using SignedData with no
* signers. Handle that case.
*/
@@ -3688,7 +3713,7 @@ pkinit_C_UnloadModule(void *handle)
static krb5_error_code
pkinit_login(krb5_context context,
pkinit_identity_crypto_context id_cryptoctx,
- CK_TOKEN_INFO *tip)
+ CK_TOKEN_INFO *tip, const char *password)
{
krb5_data rdat;
char *prompt;
@@ -3700,6 +3725,12 @@ pkinit_login(krb5_context context,
if (tip->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
rdat.data = NULL;
rdat.length = 0;
+ } else if (password != NULL) {
+ rdat.data = strdup(password);
+ rdat.length = strlen(password);
+ } else if (id_cryptoctx->prompter == NULL) {
+ r = KRB5_LIBOS_CANTREADPWD;
+ rdat.data = NULL;
} else {
if (tip->flags & CKF_USER_PIN_LOCKED)
warning = " (Warning: PIN locked)";
@@ -3751,6 +3782,8 @@ pkinit_open_session(krb5_context context,
CK_ULONG count = 0;
CK_SLOT_ID_PTR slotlist;
CK_TOKEN_INFO tinfo;
+ char *p11name;
+ const char *password;
if (cctx->p11_module != NULL)
return 0; /* session already open */
@@ -3814,12 +3847,40 @@ pkinit_open_session(krb5_context context,
}
cctx->slotid = slotlist[i];
free(slotlist);
- pkiDebug("open_session: slotid %d (%lu of %d)\n", (int) cctx->slotid,
+ pkiDebug("open_session: slotid %d (%lu of %d)\n", (int)cctx->slotid,
i + 1, (int) count);
/* Login if needed */
- if (tinfo.flags & CKF_LOGIN_REQUIRED)
- r = pkinit_login(context, cctx, &tinfo);
+ if (tinfo.flags & CKF_LOGIN_REQUIRED) {
+ if (cctx->p11_module_name != NULL) {
+ if (cctx->slotid != PK_NOSLOT) {
+ if (asprintf(&p11name,
+ "PKCS11:module_name=%s:slotid=%ld:token=%s",
+ cctx->p11_module_name, (long)cctx->slotid,
+ tinfo.label) < 0)
+ p11name = NULL;
+ } else {
+ if (asprintf(&p11name,
+ "PKCS11:module_name=%s,token=%s",
+ cctx->p11_module_name,
+ tinfo.label) < 0)
+ p11name = NULL;
+ }
+ } else {
+ p11name = NULL;
+ }
+ if (cctx->defer_id_prompt) {
+ /* Supply the identity name to be passed to the responder. */
+ pkinit_set_deferred_id(&cctx->deferred_ids,
+ p11name, tinfo.flags, NULL);
+ free(p11name);
+ return KRB5KRB_ERR_GENERIC;
+ }
+ /* Look up a responder-supplied password for the token. */
+ password = pkinit_find_deferred_id(cctx->deferred_ids, p11name);
+ free(p11name);
+ r = pkinit_login(context, cctx, &tinfo, password);
+ }
return r;
}
@@ -4269,34 +4330,56 @@ pkinit_get_certs_pkcs12(krb5_context context,
char prompt_string[128];
char prompt_reply[128];
char *prompt_prefix = _("Pass phrase for");
+ char *p12name = reassemble_pkcs12_name(idopts->cert_filename);
+ const char *tmp;
pkiDebug("Initial PKCS12_parse with no password failed\n");
- memset(prompt_reply, '\0', sizeof(prompt_reply));
- rdat.data = prompt_reply;
- rdat.length = sizeof(prompt_reply);
-
- r = snprintf(prompt_string, sizeof(prompt_string), "%s %s",
- prompt_prefix, idopts->cert_filename);
- if (r >= (int) sizeof(prompt_string)) {
- pkiDebug("Prompt string, '%s %s', is too long!\n",
- prompt_prefix, idopts->cert_filename);
+ if (id_cryptoctx->defer_id_prompt) {
+ /* Supply the identity name to be passed to the responder. */
+ pkinit_set_deferred_id(&id_cryptoctx->deferred_ids, p12name, 0,
+ NULL);
+ free(p12name);
+ retval = 0;
goto cleanup;
}
- kprompt.prompt = prompt_string;
- kprompt.hidden = 1;
- kprompt.reply = &rdat;
- prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
-
- /* PROMPTER_INVOCATION */
- k5int_set_prompt_types(context, &prompt_type);
- r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
- NULL, NULL, 1, &kprompt);
- k5int_set_prompt_types(context, 0);
+ /* Try to read a responder-supplied password. */
+ tmp = pkinit_find_deferred_id(id_cryptoctx->deferred_ids, p12name);
+ free(p12name);
+ if (tmp != NULL) {
+ /* Try using the responder-supplied password. */
+ rdat.data = (char *)tmp;
+ rdat.length = strlen(tmp);
+ } else if (id_cryptoctx->prompter == NULL) {
+ /* We can't use a prompter. */
+ goto cleanup;
+ } else {
+ /* Ask using a prompter. */
+ memset(prompt_reply, '\0', sizeof(prompt_reply));
+ rdat.data = prompt_reply;
+ rdat.length = sizeof(prompt_reply);
+
+ r = snprintf(prompt_string, sizeof(prompt_string), "%s %s",
+ prompt_prefix, idopts->cert_filename);
+ if (r >= (int)sizeof(prompt_string)) {
+ pkiDebug("Prompt string, '%s %s', is too long!\n",
+ prompt_prefix, idopts->cert_filename);
+ goto cleanup;
+ }
+ kprompt.prompt = prompt_string;
+ kprompt.hidden = 1;
+ kprompt.reply = &rdat;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+ /* PROMPTER_INVOCATION */
+ k5int_set_prompt_types(context, &prompt_type);
+ r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+ NULL, NULL, 1, &kprompt);
+ k5int_set_prompt_types(context, 0);
+ }
ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL);
if (ret == 0) {
- pkiDebug("Seconde PKCS12_parse with password failed\n");
+ pkiDebug("Second PKCS12_parse with password failed\n");
goto cleanup;
}
}
@@ -4352,14 +4435,22 @@ pkinit_load_fs_cert_and_key(krb5_context context,
krb5_error_code retval;
X509 *x = NULL;
EVP_PKEY *y = NULL;
+ char *fsname = NULL;
+ const char *password;
- /* load the certificate */
+ fsname = reassemble_files_name(certname, keyname);
+
+ /* Try to read a responder-supplied password. */
+ password = pkinit_find_deferred_id(id_cryptoctx->deferred_ids, fsname);
+
+ /* Load the certificate. */
retval = get_cert(certname, &x);
if (retval != 0 || x == NULL) {
pkiDebug("failed to load user's certificate from '%s'\n", certname);
goto cleanup;
}
- retval = get_key(context, id_cryptoctx, keyname, &y);
+ /* Load the key. */
+ retval = get_key(context, id_cryptoctx, keyname, fsname, &y, password);
if (retval != 0 || y == NULL) {
pkiDebug("failed to load user's private key from '%s'\n", keyname);
goto cleanup;
@@ -4383,7 +4474,8 @@ pkinit_load_fs_cert_and_key(krb5_context context,
retval = 0;
cleanup:
- if (retval) {
+ free(fsname);
+ if (retval != 0 || y == NULL) {
if (x != NULL)
X509_free(x);
if (y != NULL)
@@ -4490,7 +4582,7 @@ pkinit_get_certs_dir(krb5_context context,
continue;
}
- if (i == 0) {
+ if (!id_cryptoctx->defer_id_prompt && i == 0) {
pkiDebug("%s: No cert/key pairs found in directory '%s'\n",
__FUNCTION__, idopts->cert_filename);
retval = ENOENT;
@@ -4570,6 +4662,7 @@ pkinit_get_certs_pkcs11(krb5_context context,
/* Copy stuff from idopts -> id_cryptoctx */
if (idopts->p11_module_name != NULL) {
+ free(id_cryptoctx->p11_module_name);
id_cryptoctx->p11_module_name = strdup(idopts->p11_module_name);
if (id_cryptoctx->p11_module_name == NULL)
return ENOMEM;
@@ -4604,7 +4697,18 @@ pkinit_get_certs_pkcs11(krb5_context context,
if (pkinit_open_session(context, id_cryptoctx)) {
pkiDebug("can't open pkcs11 session\n");
- return KRB5KDC_ERR_PREAUTH_FAILED;
+ if (!id_cryptoctx->defer_id_prompt)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ if (id_cryptoctx->defer_id_prompt) {
+ /*
+ * We need to reset all of the PKCS#11 state, so that the next time we
+ * poke at it, it'll be in as close to the state it was in after we
+ * loaded it the first time as we can make it.
+ */
+ pkinit_fini_pkcs11(id_cryptoctx);
+ pkinit_init_pkcs11(id_cryptoctx);
+ return 0;
}
#ifndef PKINIT_USE_MECH_LIST
@@ -6092,12 +6196,12 @@ crypto_set_deferred_id(krb5_context context,
pkinit_identity_crypto_context id_cryptoctx,
const char *identity, const char *password)
{
- unsigned long flags;
+ unsigned long ck_flags;
- flags = pkinit_get_deferred_id_flags(id_cryptoctx->deferred_ids,
- identity);
+ ck_flags = pkinit_get_deferred_id_flags(id_cryptoctx->deferred_ids,
+ identity);
return pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
- identity, flags, password);
+ identity, ck_flags, password);
}
/*