summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/krb5/pkinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/krb5/pkinit.c')
-rw-r--r--source4/heimdal/lib/krb5/pkinit.c309
1 files changed, 198 insertions, 111 deletions
diff --git a/source4/heimdal/lib/krb5/pkinit.c b/source4/heimdal/lib/krb5/pkinit.c
index 2f6d7854a5..f6457aa5c9 100644
--- a/source4/heimdal/lib/krb5/pkinit.c
+++ b/source4/heimdal/lib/krb5/pkinit.c
@@ -74,6 +74,7 @@ struct krb5_pk_init_ctx_data {
unsigned int require_krbtgt_otherName:1;
unsigned int require_hostname_match:1;
unsigned int trustedCertifiers:1;
+ unsigned int anonymous:1;
};
static void
@@ -193,15 +194,15 @@ find_cert(krb5_context context, struct krb5_pk_identity *id,
for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
ret = hx509_query_match_eku(q, cf[i].oid);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed setting %s OID", cf[i].type);
return ret;
}
- ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert);
+ ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
if (ret == 0)
break;
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed finding certificate with %s OID", cf[i].type);
}
return ret;
@@ -221,7 +222,7 @@ create_signature(krb5_context context,
if (id->cert == NULL)
flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
- ret = hx509_cms_create_signed_1(id->hx509ctx,
+ ret = hx509_cms_create_signed_1(context->hx509ctx,
flags,
eContentType,
eContent->data,
@@ -233,7 +234,7 @@ create_signature(krb5_context context,
id->certs,
sd_data);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Create CMS signedData");
return ret;
}
@@ -596,7 +597,7 @@ build_auth_pack(krb5_context context,
if (a->supportedCMSTypes == NULL)
return ENOMEM;
- ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
+ ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, NULL,
&a->supportedCMSTypes->val,
&a->supportedCMSTypes->len);
if (ret)
@@ -756,7 +757,7 @@ pk_mk_padata(krb5_context context,
free_PA_PK_AS_REQ(&req);
goto out;
}
- ret = build_edi(context, ctx->id->hx509ctx,
+ ret = build_edi(context, context->hx509ctx,
ctx->id->anchors, req.trustedCertifiers);
if (ret) {
krb5_set_error_message(context, ret,
@@ -806,6 +807,12 @@ _krb5_pk_mk_padata(krb5_context context,
krb5_pk_init_ctx ctx = c;
int win2k_compat;
+ if (ctx->id->certs == NULL && ctx->anonymous == 0) {
+ krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
+ N_("PKINIT: No user certificate given", ""));
+ return HEIM_PKINIT_NO_PRIVATE_KEY;
+ }
+
win2k_compat = krb5_config_get_bool_default(context, NULL,
FALSE,
"realms",
@@ -873,7 +880,7 @@ pk_verify_sign(krb5_context context,
*signer = NULL;
- ret = hx509_cms_verify_signed(id->hx509ctx,
+ ret = hx509_cms_verify_signed(context->hx509ctx,
id->verify_ctx,
HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH|HX509_CMS_VS_NO_KU_CHECK,
data,
@@ -884,7 +891,7 @@ pk_verify_sign(krb5_context context,
content,
&signer_certs);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"CMS verify signed failed");
return ret;
}
@@ -896,9 +903,9 @@ pk_verify_sign(krb5_context context,
goto out;
}
- ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
+ ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed to get on of the signer certs");
goto out;
}
@@ -1040,7 +1047,7 @@ pk_verify_host(krb5_context context,
krb5_error_code ret = 0;
if (ctx->require_eku) {
- ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
+ ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
&asn1_oid_id_pkkdcekuoid, 0);
if (ret) {
krb5_set_error_message(context, ret,
@@ -1052,7 +1059,7 @@ pk_verify_host(krb5_context context,
hx509_octet_string_list list;
int i;
- ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
+ ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
host->cert,
&asn1_oid_id_pkinit_san,
&list);
@@ -1102,7 +1109,7 @@ pk_verify_host(krb5_context context,
return ret;
if (hi) {
- ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
+ ret = hx509_verify_hostname(context->hx509ctx, host->cert,
ctx->require_hostname_match,
HX509_HN_HOSTNAME,
hi->hostname,
@@ -1145,7 +1152,7 @@ pk_rd_pa_reply_enckey(krb5_context context,
if (ctx->type == PKINIT_WIN2K)
flags |= HX509_CMS_UE_ALLOW_WEAK;
- ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
+ ret = hx509_cms_unenvelope(context->hx509ctx,
ctx->id->certs,
flags,
indata->data,
@@ -1155,7 +1162,7 @@ pk_rd_pa_reply_enckey(krb5_context context,
&contentType,
&content);
if (ret) {
- pk_copy_error(context, ctx->id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed to unenvelope CMS data in PK-INIT reply");
return ret;
}
@@ -1167,8 +1174,26 @@ pk_rd_pa_reply_enckey(krb5_context context,
heim_octet_string out;
ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
- if (ret)
- goto out;
+ if (ret) {
+ /* windows LH with interesting CMS packets */
+ size_t ph = 1 + der_length_len(content.length);
+ unsigned char *ptr = malloc(content.length + ph);
+ size_t l;
+
+ memcpy(ptr + ph, content.data, content.length);
+
+ ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
+ ASN1_C_UNIV, CONS, UT_Sequence, &l);
+ if (ret)
+ return ret;
+ free(content.data);
+ content.data = ptr;
+ content.length += ph;
+
+ ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
+ if (ret)
+ goto out;
+ }
if (der_heim_oid_cmp(&type, &asn1_oid_id_pkcs7_signedData)) {
ret = EINVAL; /* XXX */
krb5_set_error_message(context, ret,
@@ -1700,10 +1725,44 @@ hx_pass_prompter(void *data, const hx509_prompt *prompter)
return 0;
}
+static krb5_error_code
+_krb5_pk_set_user_id(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ struct hx509_certs_data *certs)
+{
+ hx509_certs c = hx509_certs_ref(certs);
+ hx509_query *q = NULL;
+ int ret;
+
+ if (ctx->id->certs)
+ hx509_certs_free(&ctx->id->certs);
+ if (ctx->id->cert) {
+ hx509_cert_free(ctx->id->cert);
+ ctx->id->cert = NULL;
+ }
+
+ ctx->id->certs = c;
+ ctx->anonymous = 0;
+
+ ret = hx509_query_alloc(context->hx509ctx, &q);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Allocate query to find signing certificate");
+ return ret;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = find_cert(context, ctx->id, q, &ctx->id->cert);
+ hx509_query_free(context->hx509ctx, q);
+
+ return ret;
+}
+
krb5_error_code KRB5_LIB_FUNCTION
_krb5_pk_load_id(krb5_context context,
struct krb5_pk_identity **ret_id,
- int flags,
const char *user_id,
const char *anchor_id,
char * const *chain_list,
@@ -1713,7 +1772,6 @@ _krb5_pk_load_id(krb5_context context,
char *password)
{
struct krb5_pk_identity *id = NULL;
- hx509_lock lock = NULL;
struct prompter p;
int ret;
@@ -1725,12 +1783,6 @@ _krb5_pk_load_id(krb5_context context,
return HEIM_PKINIT_NO_VALID_CA;
}
- if (user_id == NULL && (flags & 4) == 0) {
- krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
- N_("PKINIT: No user certificate given", ""));
- return HEIM_PKINIT_NO_PRIVATE_KEY;
- }
-
/* load cert */
id = calloc(1, sizeof(*id));
@@ -1740,33 +1792,34 @@ _krb5_pk_load_id(krb5_context context,
return ENOMEM;
}
- ret = hx509_context_init(&id->hx509ctx);
- if (ret)
- goto out;
-
- ret = hx509_lock_init(id->hx509ctx, &lock);
- if (ret) {
- pk_copy_error(context, id->hx509ctx, ret, "Failed init lock");
- goto out;
- }
-
- if (password && password[0])
- hx509_lock_add_password(lock, password);
-
- if (prompter) {
- p.context = context;
- p.prompter = prompter;
- p.prompter_data = prompter_data;
+ if (user_id) {
+ hx509_lock lock;
- ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
- if (ret)
+ ret = hx509_lock_init(context->hx509ctx, &lock);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
goto out;
- }
+ }
+
+ if (password && password[0])
+ hx509_lock_add_password(lock, password);
+
+ if (prompter) {
+ p.context = context;
+ p.prompter = prompter;
+ p.prompter_data = prompter_data;
+
+ ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
+ if (ret) {
+ hx509_lock_free(lock);
+ goto out;
+ }
+ }
- if (user_id) {
- ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
+ ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
+ hx509_lock_free(lock);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed to init cert certs");
goto out;
}
@@ -1774,26 +1827,26 @@ _krb5_pk_load_id(krb5_context context,
id->certs = NULL;
}
- ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
+ ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed to init anchors");
goto out;
}
- ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
+ ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
0, NULL, &id->certpool);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed to init chain");
goto out;
}
while (chain_list && *chain_list) {
- ret = hx509_certs_append(id->hx509ctx, id->certpool,
+ ret = hx509_certs_append(context->hx509ctx, id->certpool,
NULL, *chain_list);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed to laod chain %s",
*chain_list);
goto out;
@@ -1802,30 +1855,30 @@ _krb5_pk_load_id(krb5_context context,
}
if (revoke_list) {
- ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
+ ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed init revoke list");
goto out;
}
while (*revoke_list) {
- ret = hx509_revoke_add_crl(id->hx509ctx,
+ ret = hx509_revoke_add_crl(context->hx509ctx,
id->revokectx,
*revoke_list);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed load revoke list");
goto out;
}
revoke_list++;
}
} else
- hx509_context_set_missing_revoke(id->hx509ctx, 1);
+ hx509_context_set_missing_revoke(context->hx509ctx, 1);
- ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
+ ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
if (ret) {
- pk_copy_error(context, id->hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed init verify context");
goto out;
}
@@ -1840,14 +1893,11 @@ _krb5_pk_load_id(krb5_context context,
hx509_certs_free(&id->anchors);
hx509_certs_free(&id->certpool);
hx509_revoke_free(&id->revokectx);
- hx509_context_free(&id->hx509ctx);
+ hx509_context_free(&context->hx509ctx);
free(id);
} else
*ret_id = id;
- if (lock)
- hx509_lock_free(lock);
-
return ret;
}
@@ -2204,7 +2254,6 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
hx509_cert_free(ctx->id->cert);
hx509_certs_free(&ctx->id->anchors);
hx509_certs_free(&ctx->id->certpool);
- hx509_context_free(&ctx->id->hx509ctx);
if (ctx->clientDHNonce) {
krb5_free_data(NULL, ctx->clientDHNonce);
@@ -2275,9 +2324,11 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
x509_anchors = anchors;
}
+ if (flags & 4)
+ opt->opt_private->pk_init_ctx->anonymous = 1;
+
ret = _krb5_pk_load_id(context,
&opt->opt_private->pk_init_ctx->id,
- flags,
user_id,
x509_anchors,
pool,
@@ -2292,31 +2343,14 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
}
if (opt->opt_private->pk_init_ctx->id->certs) {
- hx509_query *q = NULL;
- hx509_cert cert = NULL;
- hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx;
-
- ret = hx509_query_alloc(hx509ctx, &q);
- if (ret) {
- pk_copy_error(context, hx509ctx, ret,
- "Allocate query to find signing certificate");
- return ret;
- }
-
- hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
- hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
-
- ret = find_cert(context, opt->opt_private->pk_init_ctx->id, q, &cert);
- hx509_query_free(hx509ctx, q);
- if (ret)
- return ret;
-
- opt->opt_private->pk_init_ctx->id->cert = cert;
+ _krb5_pk_set_user_id(context,
+ opt->opt_private->pk_init_ctx,
+ opt->opt_private->pk_init_ctx->id->certs);
} else
opt->opt_private->pk_init_ctx->id->cert = NULL;
if ((flags & 2) == 0) {
- hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx;
+ hx509_context hx509ctx = context->hx509ctx;
hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
opt->opt_private->pk_init_ctx->keyex = USE_DH;
@@ -2353,6 +2387,33 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
#endif
}
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ struct hx509_certs_data *certs)
+{
+#ifdef PKINIT
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: on non extendable opt", ""));
+ return EINVAL;
+ }
+ if (opt->opt_private->pk_init_ctx == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: on pkinit context", ""));
+ return EINVAL;
+ }
+
+ _krb5_pk_set_user_id(context, opt->opt_private->pk_init_ctx, certs);
+
+ return 0;
+#else
+ krb5_set_error_message(context, EINVAL,
+ N_("no support for PKINIT compiled in", ""));
+ return EINVAL;
+#endif
+}
+
#ifdef PKINIT
static int
@@ -2404,34 +2465,35 @@ krb5_error_code KRB5_LIB_FUNCTION
_krb5_pk_enterprise_cert(krb5_context context,
const char *user_id,
krb5_const_realm realm,
- krb5_principal *principal)
+ krb5_principal *principal,
+ struct hx509_certs_data **res)
{
#ifdef PKINIT
krb5_error_code ret;
- hx509_context hx509ctx;
hx509_certs certs, result;
hx509_cert cert;
hx509_query *q;
char *name;
*principal = NULL;
+ if (res)
+ *res = NULL;
- if (user_id == NULL)
+ if (user_id == NULL) {
+ krb5_clear_error_message(context);
return ENOENT;
+ }
- ret = hx509_context_init(&hx509ctx);
- if (ret)
- return ret;
-
- ret = hx509_certs_init(hx509ctx, user_id, 0, NULL, &certs);
+ ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
if (ret) {
- pk_copy_error(context, hx509ctx, ret,
+ pk_copy_error(context, context->hx509ctx, ret,
"Failed to init cert certs");
return ret;
}
- ret = hx509_query_alloc(hx509ctx, &q);
+ ret = hx509_query_alloc(context->hx509ctx, &q);
if (ret) {
+ krb5_set_error_message(context, ret, "out of memory");
hx509_certs_free(&certs);
return ret;
}
@@ -2441,29 +2503,54 @@ _krb5_pk_enterprise_cert(krb5_context context,
hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
hx509_query_match_cmp_func(q, find_ms_san, NULL);
- ret = hx509_certs_filter(hx509ctx, certs, q, &result);
- hx509_query_free(hx509ctx, q);
+ ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
+ hx509_query_free(context->hx509ctx, q);
hx509_certs_free(&certs);
- if (ret)
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to find PKINIT certificate");
return ret;
+ }
- ret = hx509_get_one_cert(hx509ctx, result, &cert);
+ ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
hx509_certs_free(&result);
- if (ret)
- return ret;
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to get one cert");
+ goto out;
+ }
- ret = get_ms_san(hx509ctx, cert, &name);
- if (ret)
- return ret;
+ ret = get_ms_san(context->hx509ctx, cert, &name);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to get MS SAN");
+ goto out;
+ }
ret = krb5_make_principal(context, principal, realm, name, NULL);
free(name);
- hx509_context_free(&hx509ctx);
if (ret)
- return ret;
+ goto out;
krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
-
+
+ if (res) {
+ ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
+ if (ret) {
+ hx509_cert_free(cert);
+ goto out;
+ }
+
+ ret = hx509_certs_add(context->hx509ctx, *res, cert);
+ if (ret) {
+ hx509_certs_free(res);
+ goto out;
+ }
+ }
+
+ out:
+ hx509_cert_free(cert);
+
return ret;
#else
krb5_set_error_message(context, EINVAL,