summaryrefslogtreecommitdiff
path: root/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/preauth/pkinit/pkinit_crypto_openssl.c')
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c838
1 files changed, 386 insertions, 452 deletions
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 74fffbf32129..ac107c2c1b67 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -67,10 +67,6 @@ static krb5_error_code pkinit_decode_data
const uint8_t *data, unsigned int data_len, uint8_t **decoded,
unsigned int *decoded_len);
-static krb5_error_code decode_data
-(uint8_t **, unsigned int *, const uint8_t *, unsigned int, EVP_PKEY *pkey,
- X509 *cert);
-
#ifdef DEBUG_DH
static void print_dh(DH *, char *);
static void print_pubkey(BIGNUM *, char *);
@@ -1154,7 +1150,7 @@ cms_signeddata_create(krb5_context context,
X509_ALGOR *alg = NULL;
ASN1_OCTET_STRING *digest = NULL;
unsigned int alg_len = 0, digest_len = 0;
- unsigned char *y = NULL, *alg_buf = NULL, *digest_buf = NULL;
+ unsigned char *y = NULL;
X509 *cert = NULL;
ASN1_OBJECT *oid = NULL, *oid_copy;
@@ -1321,18 +1317,12 @@ cms_signeddata_create(krb5_context context,
goto cleanup2;
X509_ALGOR_set0(alg, OBJ_nid2obj(NID_sha1), V_ASN1_NULL, NULL);
alg_len = i2d_X509_ALGOR(alg, NULL);
- alg_buf = malloc(alg_len);
- if (alg_buf == NULL)
- goto cleanup2;
digest = ASN1_OCTET_STRING_new();
if (digest == NULL)
goto cleanup2;
ASN1_OCTET_STRING_set(digest, md_data2, (int)md_len2);
digest_len = i2d_ASN1_OCTET_STRING(digest, NULL);
- digest_buf = malloc(digest_len);
- if (digest_buf == NULL)
- goto cleanup2;
digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len),
V_ASN1_SEQUENCE);
@@ -1421,9 +1411,7 @@ cleanup2:
#ifndef WITHOUT_PKCS11
if (id_cryptoctx->pkcs11_method == 1 &&
id_cryptoctx->mech == CKM_RSA_PKCS) {
- free(digest_buf);
free(digestInfo_buf);
- free(alg_buf);
if (digest != NULL)
ASN1_OCTET_STRING_free(digest);
}
@@ -2101,11 +2089,21 @@ crypto_retrieve_X509_sans(krb5_context context,
{
krb5_error_code retval = EINVAL;
char buf[DN_BUF_LEN];
- int p = 0, u = 0, d = 0, l;
+ int p = 0, u = 0, d = 0, ret = 0, l;
krb5_principal *princs = NULL;
krb5_principal *upns = NULL;
unsigned char **dnss = NULL;
- unsigned int i, num_found = 0;
+ unsigned int i, num_found = 0, num_sans = 0;
+ X509_EXTENSION *ext = NULL;
+ GENERAL_NAMES *ialt = NULL;
+ GENERAL_NAME *gen = NULL;
+
+ if (princs_ret != NULL)
+ *princs_ret = NULL;
+ if (upn_ret != NULL)
+ *upn_ret = NULL;
+ if (dns_ret != NULL)
+ *dns_ret = NULL;
if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) {
pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__);
@@ -2121,143 +2119,137 @@ crypto_retrieve_X509_sans(krb5_context context,
buf, sizeof(buf));
pkiDebug("%s: looking for SANs in cert = %s\n", __FUNCTION__, buf);
- if ((l = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1)) >= 0) {
- X509_EXTENSION *ext = NULL;
- GENERAL_NAMES *ialt = NULL;
- GENERAL_NAME *gen = NULL;
- int ret = 0;
- unsigned int num_sans = 0;
+ l = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
+ if (l < 0)
+ return 0;
- if (!(ext = X509_get_ext(cert, l)) || !(ialt = X509V3_EXT_d2i(ext))) {
- pkiDebug("%s: found no subject alt name extensions\n",
- __FUNCTION__);
- goto cleanup;
- }
- num_sans = sk_GENERAL_NAME_num(ialt);
+ if (!(ext = X509_get_ext(cert, l)) || !(ialt = X509V3_EXT_d2i(ext))) {
+ pkiDebug("%s: found no subject alt name extensions\n", __FUNCTION__);
+ goto cleanup;
+ }
+ num_sans = sk_GENERAL_NAME_num(ialt);
- pkiDebug("%s: found %d subject alt name extension(s)\n",
- __FUNCTION__, num_sans);
+ pkiDebug("%s: found %d subject alt name extension(s)\n", __FUNCTION__,
+ num_sans);
- /* OK, we're likely returning something. Allocate return values */
- if (princs_ret != NULL) {
- princs = calloc(num_sans + 1, sizeof(krb5_principal));
- if (princs == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
+ /* OK, we're likely returning something. Allocate return values */
+ if (princs_ret != NULL) {
+ princs = calloc(num_sans + 1, sizeof(krb5_principal));
+ if (princs == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
}
- if (upn_ret != NULL) {
- upns = calloc(num_sans + 1, sizeof(krb5_principal));
- if (upns == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
+ }
+ if (upn_ret != NULL) {
+ upns = calloc(num_sans + 1, sizeof(krb5_principal));
+ if (upns == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
}
- if (dns_ret != NULL) {
- dnss = calloc(num_sans + 1, sizeof(*dnss));
- if (dnss == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
+ }
+ if (dns_ret != NULL) {
+ dnss = calloc(num_sans + 1, sizeof(*dnss));
+ if (dnss == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
}
+ }
- for (i = 0; i < num_sans; i++) {
- krb5_data name = { 0, 0, NULL };
+ for (i = 0; i < num_sans; i++) {
+ krb5_data name = { 0, 0, NULL };
- gen = sk_GENERAL_NAME_value(ialt, i);
- switch (gen->type) {
- case GEN_OTHERNAME:
- name.length = gen->d.otherName->value->value.sequence->length;
- name.data = (char *)gen->d.otherName->value->value.sequence->data;
- if (princs != NULL
- && OBJ_cmp(plgctx->id_pkinit_san,
- gen->d.otherName->type_id) == 0) {
+ gen = sk_GENERAL_NAME_value(ialt, i);
+ switch (gen->type) {
+ case GEN_OTHERNAME:
+ name.length = gen->d.otherName->value->value.sequence->length;
+ name.data = (char *)gen->d.otherName->value->value.sequence->data;
+ if (princs != NULL &&
+ OBJ_cmp(plgctx->id_pkinit_san,
+ gen->d.otherName->type_id) == 0) {
#ifdef DEBUG_ASN1
- print_buffer_bin((unsigned char *)name.data, name.length,
- "/tmp/pkinit_san");
+ print_buffer_bin((unsigned char *)name.data, name.length,
+ "/tmp/pkinit_san");
#endif
- ret = k5int_decode_krb5_principal_name(&name, &princs[p]);
- if (ret) {
- pkiDebug("%s: failed decoding pkinit san value\n",
- __FUNCTION__);
- } else {
- p++;
- num_found++;
- }
- } else if (upns != NULL
- && OBJ_cmp(plgctx->id_ms_san_upn,
- gen->d.otherName->type_id) == 0) {
- /* Prevent abuse of embedded null characters. */
- if (memchr(name.data, '\0', name.length))
- break;
- ret = krb5_parse_name(context, name.data, &upns[u]);
- if (ret) {
- pkiDebug("%s: failed parsing ms-upn san value\n",
- __FUNCTION__);
- } else {
- u++;
- num_found++;
- }
+ ret = k5int_decode_krb5_principal_name(&name, &princs[p]);
+ if (ret) {
+ pkiDebug("%s: failed decoding pkinit san value\n",
+ __FUNCTION__);
} else {
- pkiDebug("%s: unrecognized othername oid in SAN\n",
+ p++;
+ num_found++;
+ }
+ } else if (upns != NULL &&
+ OBJ_cmp(plgctx->id_ms_san_upn,
+ gen->d.otherName->type_id) == 0) {
+ /* Prevent abuse of embedded null characters. */
+ if (memchr(name.data, '\0', name.length))
+ break;
+ ret = krb5_parse_name_flags(context, name.data,
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE,
+ &upns[u]);
+ if (ret) {
+ pkiDebug("%s: failed parsing ms-upn san value\n",
__FUNCTION__);
- continue;
+ } else {
+ u++;
+ num_found++;
}
+ } else {
+ pkiDebug("%s: unrecognized othername oid in SAN\n",
+ __FUNCTION__);
+ continue;
+ }
- break;
- case GEN_DNS:
- if (dnss != NULL) {
- /* Prevent abuse of embedded null characters. */
- if (memchr(gen->d.dNSName->data, '\0',
- gen->d.dNSName->length))
- break;
- pkiDebug("%s: found dns name = %s\n",
- __FUNCTION__, gen->d.dNSName->data);
- dnss[d] = (unsigned char *)
- strdup((char *)gen->d.dNSName->data);
- if (dnss[d] == NULL) {
- pkiDebug("%s: failed to duplicate dns name\n",
- __FUNCTION__);
- } else {
- d++;
- num_found++;
- }
+ break;
+ case GEN_DNS:
+ if (dnss != NULL) {
+ /* Prevent abuse of embedded null characters. */
+ if (memchr(gen->d.dNSName->data, '\0', gen->d.dNSName->length))
+ break;
+ pkiDebug("%s: found dns name = %s\n", __FUNCTION__,
+ gen->d.dNSName->data);
+ dnss[d] = (unsigned char *)
+ strdup((char *)gen->d.dNSName->data);
+ if (dnss[d] == NULL) {
+ pkiDebug("%s: failed to duplicate dns name\n",
+ __FUNCTION__);
+ } else {
+ d++;
+ num_found++;
}
- break;
- default:
- pkiDebug("%s: SAN type = %d expecting %d\n",
- __FUNCTION__, gen->type, GEN_OTHERNAME);
}
+ break;
+ default:
+ pkiDebug("%s: SAN type = %d expecting %d\n", __FUNCTION__,
+ gen->type, GEN_OTHERNAME);
}
- sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
}
+ sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
retval = 0;
- if (princs)
+ if (princs != NULL && *princs != NULL) {
*princs_ret = princs;
- if (upns)
+ princs = NULL;
+ }
+ if (upns != NULL && *upns != NULL) {
*upn_ret = upns;
- if (dnss)
+ upns = NULL;
+ }
+ if (dnss != NULL && *dnss != NULL) {
*dns_ret = dnss;
+ dnss = NULL;
+ }
cleanup:
- if (retval) {
- if (princs != NULL) {
- for (i = 0; princs[i] != NULL; i++)
- krb5_free_principal(context, princs[i]);
- free(princs);
- }
- if (upns != NULL) {
- for (i = 0; upns[i] != NULL; i++)
- krb5_free_principal(context, upns[i]);
- free(upns);
- }
- if (dnss != NULL) {
- for (i = 0; dnss[i] != NULL; i++)
- free(dnss[i]);
- free(dnss);
- }
- }
+ for (i = 0; princs != NULL && princs[i] != NULL; i++)
+ krb5_free_principal(context, princs[i]);
+ free(princs);
+ for (i = 0; upns != NULL && upns[i] != NULL; i++)
+ krb5_free_principal(context, upns[i]);
+ free(upns);
+ for (i = 0; dnss != NULL && dnss[i] != NULL; i++)
+ free(dnss[i]);
+ free(dnss);
return retval;
}
@@ -2313,7 +2305,6 @@ crypto_check_cert_eku(krb5_context context,
X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert),
buf, sizeof(buf));
- pkiDebug("%s: looking for EKUs in cert = %s\n", __FUNCTION__, buf);
if ((i = X509_get_ext_by_NID(reqctx->received_cert,
NID_ext_key_usage, -1)) >= 0) {
@@ -2347,7 +2338,6 @@ crypto_check_cert_eku(krb5_context context,
if (found_eku) {
ASN1_BIT_STRING *usage = NULL;
- pkiDebug("%s: found acceptable EKU, checking for digitalSignature\n", __FUNCTION__);
/* check that digitalSignature KeyUsage is present */
X509_check_ca(reqctx->received_cert);
@@ -2356,12 +2346,10 @@ crypto_check_cert_eku(krb5_context context,
if (!ku_reject(reqctx->received_cert,
X509v3_KU_DIGITAL_SIGNATURE)) {
- pkiDebug("%s: found digitalSignature KU\n",
- __FUNCTION__);
+ TRACE_PKINIT_EKU(context);
*valid_eku = 1;
} else
- pkiDebug("%s: didn't find digitalSignature KU\n",
- __FUNCTION__);
+ TRACE_PKINIT_EKU_NO_KU(context);
}
ASN1_BIT_STRING_free(usage);
}
@@ -3581,12 +3569,14 @@ openssl_callback(int ok, X509_STORE_CTX * ctx)
{
#ifdef DEBUG
if (!ok) {
+ X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+ int err = X509_STORE_CTX_get_error(ctx);
+ const char *errmsg = X509_verify_cert_error_string(err);
char buf[DN_BUF_LEN];
- X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, sizeof(buf));
+ X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
pkiDebug("cert = %s\n", buf);
- pkiDebug("callback function: %d (%s)\n", ctx->error,
- X509_verify_cert_error_string(ctx->error));
+ pkiDebug("callback function: %d (%s)\n", err, errmsg);
}
#endif
return ok;
@@ -3979,12 +3969,34 @@ pkinit_decode_data_fs(krb5_context context,
const uint8_t *data, unsigned int data_len,
uint8_t **decoded_data, unsigned int *decoded_data_len)
{
- if (decode_data(decoded_data, decoded_data_len, data, data_len,
- id_cryptoctx->my_key, sk_X509_value(id_cryptoctx->my_certs,
- id_cryptoctx->cert_index)) <= 0) {
- pkiDebug("failed to decode data\n");
+ X509 *cert = sk_X509_value(id_cryptoctx->my_certs,
+ id_cryptoctx->cert_index);
+ EVP_PKEY *pkey = id_cryptoctx->my_key;
+ uint8_t *buf;
+ int buf_len, decrypt_len;
+
+ *decoded_data = NULL;
+ *decoded_data_len = 0;
+
+ if (cert != NULL && !X509_check_private_key(cert, pkey)) {
+ pkiDebug("private key does not match certificate\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ buf_len = EVP_PKEY_size(pkey);
+ buf = malloc(buf_len + 10);
+ if (buf == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ decrypt_len = EVP_PKEY_decrypt_old(buf, data, data_len, pkey);
+ if (decrypt_len <= 0) {
+ pkiDebug("unable to decrypt received data (len=%d)\n", data_len);
+ free(buf);
return KRB5KDC_ERR_PREAUTH_FAILED;
}
+
+ *decoded_data = buf;
+ *decoded_data_len = decrypt_len;
return 0;
}
@@ -4027,6 +4039,9 @@ pkinit_decode_data_pkcs11(krb5_context context,
uint8_t *cp;
int r;
+ *decoded_data = NULL;
+ *decoded_data_len = 0;
+
if (pkinit_open_session(context, id_cryptoctx)) {
pkiDebug("can't open pkcs11 session\n");
return KRB5KDC_ERR_PREAUTH_FAILED;
@@ -4075,6 +4090,9 @@ pkinit_decode_data(krb5_context context,
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ *decoded_data = NULL;
+ *decoded_data_len = 0;
+
if (id_cryptoctx->pkcs11_method != 1)
retval = pkinit_decode_data_fs(context, id_cryptoctx, data, data_len,
decoded_data, decoded_data_len);
@@ -4188,41 +4206,6 @@ pkinit_sign_data(krb5_context context,
}
-static int
-decode_data(uint8_t **out_data, unsigned int *out_data_len,
- const uint8_t *data, unsigned int data_len, EVP_PKEY *pkey,
- X509 *cert)
-{
- int retval;
- unsigned char *buf = NULL;
- int buf_len = 0;
-
- if (cert && !X509_check_private_key(cert, pkey)) {
- pkiDebug("private key does not match certificate\n");
- return 0;
- }
-
- buf_len = EVP_PKEY_size(pkey);
- buf = malloc((size_t) buf_len + 10);
- if (buf == NULL)
- return 0;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
- retval = EVP_PKEY_decrypt_old(buf, data, (int)data_len, pkey);
-#else
- retval = EVP_PKEY_decrypt(buf, data, (int)data_len, pkey);
-#endif
- if (retval <= 0) {
- pkiDebug("unable to decrypt received data (len=%d)\n", data_len);
- free(buf);
- return 0;
- }
- *out_data = buf;
- *out_data_len = retval;
-
- return 1;
-}
-
static krb5_error_code
create_signature(unsigned char **sig, unsigned int *sig_len,
unsigned char *data, unsigned int data_len, EVP_PKEY *pkey)
@@ -4310,8 +4293,7 @@ pkinit_get_certs_pkcs12(krb5_context context,
fp = fopen(idopts->cert_filename, "rb");
if (fp == NULL) {
- pkiDebug("Failed to open PKCS12 file '%s', error %d\n",
- idopts->cert_filename, errno);
+ TRACE_PKINIT_PKCS_OPEN_FAIL(context, idopts->cert_filename, errno);
goto cleanup;
}
set_cloexec_file(fp);
@@ -4319,8 +4301,7 @@ pkinit_get_certs_pkcs12(krb5_context context,
p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (p12 == NULL) {
- pkiDebug("Failed to decode PKCS12 file '%s' contents\n",
- idopts->cert_filename);
+ TRACE_PKINIT_PKCS_DECODE_FAIL(context, idopts->cert_filename);
goto cleanup;
}
/*
@@ -4338,7 +4319,7 @@ pkinit_get_certs_pkcs12(krb5_context context,
char *p12name = reassemble_pkcs12_name(idopts->cert_filename);
const char *tmp;
- pkiDebug("Initial PKCS12_parse with no password failed\n");
+ TRACE_PKINIT_PKCS_PARSE_FAIL_FIRST(context);
if (id_cryptoctx->defer_id_prompt) {
/* Supply the identity name to be passed to the responder. */
@@ -4379,14 +4360,14 @@ pkinit_get_certs_pkcs12(krb5_context context,
NULL, NULL, 1, &kprompt);
k5int_set_prompt_types(context, 0);
if (r) {
- pkiDebug("Failed to prompt for PKCS12 password");
+ TRACE_PKINIT_PKCS_PROMPT_FAIL(context);
goto cleanup;
}
}
ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL);
if (ret == 0) {
- pkiDebug("Second PKCS12_parse with password failed\n");
+ TRACE_PKINIT_PKCS_PARSE_FAIL_SECOND(context);
goto cleanup;
}
}
@@ -4509,8 +4490,7 @@ pkinit_get_certs_fs(krb5_context context,
}
if (idopts->key_filename == NULL) {
- pkiDebug("%s: failed to get user's private key location\n",
- __FUNCTION__);
+ TRACE_PKINIT_NO_PRIVKEY(context);
goto cleanup;
}
@@ -4538,8 +4518,7 @@ pkinit_get_certs_dir(krb5_context context,
char *dirname, *suf;
if (idopts->cert_filename == NULL) {
- pkiDebug("%s: failed to get user's certificate directory location\n",
- __FUNCTION__);
+ TRACE_PKINIT_NO_CERT(context);
return ENOENT;
}
@@ -4583,8 +4562,7 @@ pkinit_get_certs_dir(krb5_context context,
retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx,
certname, keyname, i);
if (retval == 0) {
- pkiDebug("%s: Successfully loaded cert (and key) for %s\n",
- __FUNCTION__, dentry->d_name);
+ TRACE_PKINIT_LOADED_CERT(context, dentry->d_name);
i++;
}
else
@@ -4592,8 +4570,7 @@ pkinit_get_certs_dir(krb5_context context,
}
if (!id_cryptoctx->defer_id_prompt && i == 0) {
- pkiDebug("%s: No cert/key pairs found in directory '%s'\n",
- __FUNCTION__, idopts->cert_filename);
+ TRACE_PKINIT_NO_CERT_AND_KEY(context, idopts->cert_filename);
retval = ENOENT;
goto cleanup;
}
@@ -4948,135 +4925,15 @@ cleanup:
}
/*
- * Get number of certificates available after crypto_load_certs()
- */
-krb5_error_code
-crypto_cert_get_count(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int *cert_count)
-{
- int count;
-
- if (id_cryptoctx == NULL || id_cryptoctx->creds[0] == NULL)
- return EINVAL;
-
- for (count = 0;
- count <= MAX_CREDS_ALLOWED && id_cryptoctx->creds[count] != NULL;
- count++);
- *cert_count = count;
- return 0;
-}
-
-
-/*
- * Begin iteration over the certs loaded in crypto_load_certs()
- */
-krb5_error_code
-crypto_cert_iteration_begin(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- pkinit_cert_iter_handle *ih_ret)
-{
- struct _pkinit_cert_iter_data *id;
-
- if (id_cryptoctx == NULL || ih_ret == NULL)
- return EINVAL;
- if (id_cryptoctx->creds[0] == NULL) /* No cred info available */
- return ENOENT;
-
- id = calloc(1, sizeof(*id));
- if (id == NULL)
- return ENOMEM;
- id->magic = ITER_MAGIC;
- id->plgctx = plg_cryptoctx,
- id->reqctx = req_cryptoctx,
- id->idctx = id_cryptoctx;
- id->index = 0;
- *ih_ret = (pkinit_cert_iter_handle) id;
- return 0;
-}
-
-/*
- * End iteration over the certs loaded in crypto_load_certs()
- */
-krb5_error_code
-crypto_cert_iteration_end(krb5_context context,
- pkinit_cert_iter_handle ih)
-{
- struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih;
-
- if (id == NULL || id->magic != ITER_MAGIC)
- return EINVAL;
- free(ih);
- return 0;
-}
-
-/*
- * Get next certificate handle
- */
-krb5_error_code
-crypto_cert_iteration_next(krb5_context context,
- pkinit_cert_iter_handle ih,
- pkinit_cert_handle *ch_ret)
-{
- struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih;
- struct _pkinit_cert_data *cd;
- pkinit_identity_crypto_context id_cryptoctx;
-
- if (id == NULL || id->magic != ITER_MAGIC)
- return EINVAL;
-
- if (ch_ret == NULL)
- return EINVAL;
-
- id_cryptoctx = id->idctx;
- if (id_cryptoctx == NULL)
- return EINVAL;
-
- if (id_cryptoctx->creds[id->index] == NULL)
- return PKINIT_ITER_NO_MORE;
-
- cd = calloc(1, sizeof(*cd));
- if (cd == NULL)
- return ENOMEM;
-
- cd->magic = CERT_MAGIC;
- cd->plgctx = id->plgctx;
- cd->reqctx = id->reqctx;
- cd->idctx = id->idctx;
- cd->index = id->index;
- cd->cred = id_cryptoctx->creds[id->index++];
- *ch_ret = (pkinit_cert_handle)cd;
- return 0;
-}
-
-/*
- * Release cert handle
- */
-krb5_error_code
-crypto_cert_release(krb5_context context,
- pkinit_cert_handle ch)
-{
- struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch;
- if (cd == NULL || cd->magic != CERT_MAGIC)
- return EINVAL;
- free(cd);
- return 0;
-}
-
-/*
* Get certificate Key Usage and Extended Key Usage
*/
static krb5_error_code
-crypto_retieve_X509_key_usage(krb5_context context,
- pkinit_plg_crypto_context plgcctx,
- pkinit_req_crypto_context reqcctx,
- X509 *x,
- unsigned int *ret_ku_bits,
- unsigned int *ret_eku_bits)
+crypto_retrieve_X509_key_usage(krb5_context context,
+ pkinit_plg_crypto_context plgcctx,
+ pkinit_req_crypto_context reqcctx,
+ X509 *x,
+ unsigned int *ret_ku_bits,
+ unsigned int *ret_eku_bits)
{
krb5_error_code retval = 0;
int i;
@@ -5145,85 +5002,112 @@ out:
return retval;
}
-/*
- * Return a string format of an X509_NAME in buf where
- * size is an in/out parameter. On input it is the size
- * of the buffer, and on output it is the actual length
- * of the name.
- * If buf is NULL, returns the length req'd to hold name
- */
-static char *
-X509_NAME_oneline_ex(X509_NAME * a,
- char *buf,
- unsigned int *size,
- unsigned long flag)
+static krb5_error_code
+rfc2253_name(X509_NAME *name, char **str_out)
{
- BIO *out = NULL;
+ BIO *b = NULL;
+ char *str;
- out = BIO_new(BIO_s_mem ());
- if (X509_NAME_print_ex(out, a, 0, flag) > 0) {
- if (buf != NULL && (*size) > (unsigned int) BIO_number_written(out)) {
- memset(buf, 0, *size);
- BIO_read(out, buf, (int) BIO_number_written(out));
- }
- else {
- *size = BIO_number_written(out);
- }
- }
- BIO_free(out);
- return (buf);
+ *str_out = NULL;
+ b = BIO_new(BIO_s_mem());
+ if (b == NULL)
+ return ENOMEM;
+ if (X509_NAME_print_ex(b, name, 0, XN_FLAG_SEP_COMMA_PLUS) < 0)
+ goto error;
+ str = calloc(BIO_number_written(b) + 1, 1);
+ if (str == NULL)
+ goto error;
+ BIO_read(b, str, BIO_number_written(b));
+ BIO_free(b);
+ *str_out = str;
+ return 0;
+
+error:
+ BIO_free(b);
+ return ENOMEM;
}
/*
- * Get certificate information
+ * Get number of certificates available after crypto_load_certs()
*/
-krb5_error_code
-crypto_cert_get_matching_data(krb5_context context,
- pkinit_cert_handle ch,
- pkinit_cert_matching_data **ret_md)
+static krb5_error_code
+crypto_cert_get_count(pkinit_identity_crypto_context id_cryptoctx,
+ int *cert_count)
{
- krb5_error_code retval;
- pkinit_cert_matching_data *md;
- krb5_principal *pkinit_sans =NULL, *upn_sans = NULL;
- struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch;
- unsigned int i, j;
- char buf[DN_BUF_LEN];
- unsigned int bufsize = sizeof(buf);
+ int count;
- if (cd == NULL || cd->magic != CERT_MAGIC)
- return EINVAL;
- if (ret_md == NULL)
+ *cert_count = 0;
+ if (id_cryptoctx == NULL || id_cryptoctx->creds[0] == NULL)
return EINVAL;
- md = calloc(1, sizeof(*md));
+ for (count = 0;
+ count <= MAX_CREDS_ALLOWED && id_cryptoctx->creds[count] != NULL;
+ count++);
+ *cert_count = count;
+ return 0;
+}
+
+void
+crypto_cert_free_matching_data(krb5_context context,
+ pkinit_cert_matching_data *md)
+{
+ int i;
+
if (md == NULL)
- return ENOMEM;
+ return;
+ free(md->subject_dn);
+ free(md->issuer_dn);
+ for (i = 0; md->sans != NULL && md->sans[i] != NULL; i++)
+ krb5_free_principal(context, md->sans[i]);
+ free(md->sans);
+ free(md);
+}
- md->ch = ch;
+/*
+ * Free certificate matching data.
+ */
+void
+crypto_cert_free_matching_data_list(krb5_context context,
+ pkinit_cert_matching_data **list)
+{
+ int i;
- /* get the subject name (in rfc2253 format) */
- X509_NAME_oneline_ex(X509_get_subject_name(cd->cred->cert),
- buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS);
- md->subject_dn = strdup(buf);
- if (md->subject_dn == NULL) {
- retval = ENOMEM;
+ for (i = 0; list != NULL && list[i] != NULL; i++)
+ crypto_cert_free_matching_data(context, list[i]);
+ free(list);
+}
+
+/*
+ * Get certificate matching data for cert.
+ */
+static krb5_error_code
+get_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx, X509 *cert,
+ pkinit_cert_matching_data **md_out)
+{
+ krb5_error_code ret = ENOMEM;
+ pkinit_cert_matching_data *md = NULL;
+ krb5_principal *pkinit_sans = NULL, *upn_sans = NULL;
+ size_t i, j;
+
+ *md_out = NULL;
+
+ md = calloc(1, sizeof(*md));
+ if (md == NULL)
goto cleanup;
- }
- /* get the issuer name (in rfc2253 format) */
- X509_NAME_oneline_ex(X509_get_issuer_name(cd->cred->cert),
- buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS);
- md->issuer_dn = strdup(buf);
- if (md->issuer_dn == NULL) {
- retval = ENOMEM;
+ ret = rfc2253_name(X509_get_subject_name(cert), &md->subject_dn);
+ if (ret)
+ goto cleanup;
+ ret = rfc2253_name(X509_get_issuer_name(cert), &md->issuer_dn);
+ if (ret)
goto cleanup;
- }
- /* get the san data */
- retval = crypto_retrieve_X509_sans(context, cd->plgctx, cd->reqctx,
- cd->cred->cert, &pkinit_sans,
- &upn_sans, NULL);
- if (retval)
+ /* Get the SAN data. */
+ ret = crypto_retrieve_X509_sans(context, plg_cryptoctx, req_cryptoctx,
+ cert, &pkinit_sans, &upn_sans, NULL);
+ if (ret)
goto cleanup;
j = 0;
@@ -5238,7 +5122,7 @@ crypto_cert_get_matching_data(krb5_context context,
if (j != 0) {
md->sans = calloc((size_t)j+1, sizeof(*md->sans));
if (md->sans == NULL) {
- retval = ENOMEM;
+ ret = ENOMEM;
goto cleanup;
}
j = 0;
@@ -5256,88 +5140,96 @@ crypto_cert_get_matching_data(krb5_context context,
} else
md->sans = NULL;
- /* get the KU and EKU data */
-
- retval = crypto_retieve_X509_key_usage(context, cd->plgctx, cd->reqctx,
- cd->cred->cert,
- &md->ku_bits, &md->eku_bits);
- if (retval)
+ /* Get the KU and EKU data. */
+ ret = crypto_retrieve_X509_key_usage(context, plg_cryptoctx,
+ req_cryptoctx, cert, &md->ku_bits,
+ &md->eku_bits);
+ if (ret)
goto cleanup;
- *ret_md = md;
- retval = 0;
+ *md_out = md;
+ md = NULL;
+
cleanup:
- if (retval) {
- if (md)
- crypto_cert_free_matching_data(context, md);
- }
- return retval;
+ crypto_cert_free_matching_data(context, md);
+ return ret;
}
-/*
- * Free certificate information
- */
krb5_error_code
-crypto_cert_free_matching_data(krb5_context context,
- pkinit_cert_matching_data *md)
+crypto_cert_get_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ pkinit_cert_matching_data ***md_out)
{
- krb5_principal p;
- int i;
+ krb5_error_code ret;
+ pkinit_cert_matching_data **md_list = NULL;
+ int count, i;
- if (md == NULL)
- return EINVAL;
- if (md->subject_dn)
- free(md->subject_dn);
- if (md->issuer_dn)
- free(md->issuer_dn);
- if (md->sans) {
- for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i])
- krb5_free_principal(context, p);
- free(md->sans);
+ ret = crypto_cert_get_count(id_cryptoctx, &count);
+ if (ret)
+ goto cleanup;
+
+ md_list = calloc(count + 1, sizeof(*md_list));
+ if (md_list == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
}
- free(md);
- return 0;
+
+ for (i = 0; i < count; i++) {
+ ret = get_matching_data(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx->creds[i]->cert, &md_list[i]);
+ if (ret) {
+ pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
+ __FUNCTION__, ret, error_message(ret));
+ goto cleanup;
+ }
+ }
+
+ *md_out = md_list;
+ md_list = NULL;
+
+cleanup:
+ crypto_cert_free_matching_data_list(context, md_list);
+ return ret;
}
/*
- * Make this matching certificate "the chosen one"
+ * Set the certificate in idctx->creds[cred_index] as the selected certificate.
*/
krb5_error_code
-crypto_cert_select(krb5_context context,
- pkinit_cert_matching_data *md)
+crypto_cert_select(krb5_context context, pkinit_identity_crypto_context idctx,
+ size_t cred_index)
{
- struct _pkinit_cert_data *cd;
- if (md == NULL)
- return EINVAL;
+ pkinit_cred_info ci = NULL;
- cd = (struct _pkinit_cert_data *)md->ch;
- if (cd == NULL || cd->magic != CERT_MAGIC)
- return EINVAL;
+ if (cred_index >= MAX_CREDS_ALLOWED || idctx->creds[cred_index] == NULL)
+ return ENOENT;
+ ci = idctx->creds[cred_index];
/* copy the selected cert into our id_cryptoctx */
- if (cd->idctx->my_certs != NULL) {
- sk_X509_pop_free(cd->idctx->my_certs, X509_free);
- }
- cd->idctx->my_certs = sk_X509_new_null();
- sk_X509_push(cd->idctx->my_certs, cd->cred->cert);
- free(cd->idctx->identity);
+ if (idctx->my_certs != NULL)
+ sk_X509_pop_free(idctx->my_certs, X509_free);
+ idctx->my_certs = sk_X509_new_null();
+ sk_X509_push(idctx->my_certs, ci->cert);
+ free(idctx->identity);
/* hang on to the selected credential name */
- if (cd->idctx->creds[cd->index]->name != NULL)
- cd->idctx->identity = strdup(cd->idctx->creds[cd->index]->name);
+ if (ci->name != NULL)
+ idctx->identity = strdup(ci->name);
else
- cd->idctx->identity = NULL;
- cd->idctx->creds[cd->index]->cert = NULL; /* Don't free it twice */
- cd->idctx->cert_index = 0;
+ idctx->identity = NULL;
- if (cd->idctx->pkcs11_method != 1) {
- cd->idctx->my_key = cd->cred->key;
- cd->idctx->creds[cd->index]->key = NULL; /* Don't free it twice */
+ ci->cert = NULL; /* Don't free it twice */
+ idctx->cert_index = 0;
+ if (idctx->pkcs11_method != 1) {
+ idctx->my_key = ci->key;
+ ci->key = NULL; /* Don't free it twice */
}
#ifndef WITHOUT_PKCS11
else {
- cd->idctx->cert_id = cd->cred->cert_id;
- cd->idctx->creds[cd->index]->cert_id = NULL; /* Don't free it twice */
- cd->idctx->cert_id_len = cd->cred->cert_id_len;
+ idctx->cert_id = ci->cert_id;
+ ci->cert_id = NULL; /* Don't free it twice */
+ idctx->cert_id_len = ci->cert_id_len;
}
#endif
return 0;
@@ -5353,19 +5245,14 @@ crypto_cert_select_default(krb5_context context,
pkinit_identity_crypto_context id_cryptoctx)
{
krb5_error_code retval;
- int cert_count = 0;
+ int cert_count;
- retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
- id_cryptoctx, &cert_count);
- if (retval) {
- pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
+ retval = crypto_cert_get_count(id_cryptoctx, &cert_count);
+ if (retval)
goto errout;
- }
+
if (cert_count != 1) {
- pkiDebug("%s: ERROR: There are %d certs to choose from, "
- "but there must be exactly one.\n",
- __FUNCTION__, cert_count);
+ TRACE_PKINIT_NO_DEFAULT_CERT(context, cert_count);
retval = EINVAL;
goto errout;
}
@@ -5513,7 +5400,7 @@ load_cas_and_crls(krb5_context context,
switch(catype) {
case CATYPE_ANCHORS:
if (sk_X509_num(ca_certs) == 0) {
- pkiDebug("no anchors in file, %s\n", filename);
+ TRACE_PKINIT_NO_CA_ANCHOR(context, filename);
if (id_cryptoctx->trustedCAs == NULL)
sk_X509_free(ca_certs);
} else {
@@ -5523,7 +5410,7 @@ load_cas_and_crls(krb5_context context,
break;
case CATYPE_INTERMEDIATES:
if (sk_X509_num(ca_certs) == 0) {
- pkiDebug("no intermediates in file, %s\n", filename);
+ TRACE_PKINIT_NO_CA_INTERMEDIATE(context, filename);
if (id_cryptoctx->intermediateCAs == NULL)
sk_X509_free(ca_certs);
} else {
@@ -5533,7 +5420,7 @@ load_cas_and_crls(krb5_context context,
break;
case CATYPE_CRLS:
if (sk_X509_CRL_num(ca_crls) == 0) {
- pkiDebug("no crls in file, %s\n", filename);
+ TRACE_PKINIT_NO_CRL(context, filename);
if (id_cryptoctx->revoked == NULL)
sk_X509_CRL_free(ca_crls);
} else {
@@ -5619,14 +5506,14 @@ crypto_load_cas_and_crls(krb5_context context,
int catype,
char *id)
{
- pkiDebug("%s: called with idtype %s and catype %s\n",
- __FUNCTION__, idtype2string(idtype), catype2string(catype));
switch (idtype) {
case IDTYPE_FILE:
+ TRACE_PKINIT_LOAD_FROM_FILE(context);
return load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx, catype, id);
break;
case IDTYPE_DIR:
+ TRACE_PKINIT_LOAD_FROM_DIR(context);
return load_cas_and_crls_dir(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx, catype, id);
break;
@@ -6170,3 +6057,50 @@ crypto_get_deferred_ids(krb5_context context,
ret = (const pkinit_deferred_id *)deferred;
return ret;
}
+
+/* Return the received certificate as DER-encoded data. */
+krb5_error_code
+crypto_encode_der_cert(krb5_context context, pkinit_req_crypto_context reqctx,
+ uint8_t **der_out, size_t *der_len)
+{
+ int len;
+ unsigned char *der, *p;
+
+ *der_out = NULL;
+ *der_len = 0;
+
+ if (reqctx->received_cert == NULL)
+ return EINVAL;
+ p = NULL;
+ len = i2d_X509(reqctx->received_cert, NULL);
+ if (len <= 0)
+ return EINVAL;
+ p = der = malloc(len);
+ if (der == NULL)
+ return ENOMEM;
+ if (i2d_X509(reqctx->received_cert, &p) <= 0) {
+ free(der);
+ return EINVAL;
+ }
+ *der_out = der;
+ *der_len = len;
+ return 0;
+}
+
+/*
+ * Get the certificate matching data from the request certificate.
+ */
+krb5_error_code
+crypto_req_cert_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_cert_matching_data **md_out)
+{
+ *md_out = NULL;
+
+ if (reqctx == NULL || reqctx->received_cert == NULL)
+ return ENOENT;
+
+ return get_matching_data(context, plgctx, reqctx, reqctx->received_cert,
+ md_out);
+}