From 4f8ba5ad6ac9b7153b0e13654e59f47e67b3f608 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 13 Nov 2009 10:51:14 +1100 Subject: s4:heimdal: import lorikeet-heimdal-200911122202 (commit 9291fd2d101f3eecec550178634faa94ead3e9a1) --- source4/heimdal/lib/krb5/pkinit.c | 309 ++++++++++++++++++++++++-------------- 1 file changed, 198 insertions(+), 111 deletions(-) (limited to 'source4/heimdal/lib/krb5/pkinit.c') 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, -- cgit