kinit: add --pk-anon-fast-armor option
Add the --pk-anon-fast-armor option, which acquires a temporary anonymous PKINIT TGT to use as a FAST armor key.
This commit is contained in:
@@ -78,6 +78,7 @@ int pk_enterprise_flag = 0;
|
||||
struct hx509_certs_data *ent_user_id = NULL;
|
||||
char *pk_x509_anchors = NULL;
|
||||
int pk_use_enckey = 0;
|
||||
int pk_anon_fast_armor = 0;
|
||||
static int canonicalize_flag = 0;
|
||||
static int enterprise_flag = 0;
|
||||
static int ok_as_delegate_flag = 0;
|
||||
@@ -183,6 +184,9 @@ static struct getargs args[] = {
|
||||
|
||||
{ "pk-use-enckey", 0, arg_flag, &pk_use_enckey,
|
||||
NP_("Use RSA encrypted reply (instead of DH)", ""), NULL },
|
||||
|
||||
{ "pk-anon-fast-armor", 0, arg_flag, &pk_anon_fast_armor,
|
||||
NP_("use unauthenticated anonymous PKINIT as FAST armor", ""), NULL },
|
||||
#endif
|
||||
#ifndef NO_NTLM
|
||||
{ "ntlm-domain", 0, arg_string, &ntlm_domain,
|
||||
@@ -620,6 +624,7 @@ get_new_tickets(krb5_context context,
|
||||
krb5_init_creds_context ctx = NULL;
|
||||
krb5_get_init_creds_opt *opt = NULL;
|
||||
krb5_prompter_fct prompter = krb5_prompter_posix;
|
||||
|
||||
#ifndef NO_NTLM
|
||||
struct ntlm_buf ntlmkey;
|
||||
memset(&ntlmkey, 0, sizeof(ntlmkey));
|
||||
@@ -785,19 +790,30 @@ get_new_tickets(krb5_context context,
|
||||
}
|
||||
|
||||
if (fast_armor_cache_string) {
|
||||
krb5_ccache fastid;
|
||||
|
||||
krb5_ccache fastid = NULL;
|
||||
|
||||
if (pk_anon_fast_armor)
|
||||
krb5_errx(context, 1,
|
||||
N_("cannot specify FAST armor cache with FAST "
|
||||
"anonymous PKINIT option", ""));
|
||||
|
||||
ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache");
|
||||
goto out;
|
||||
}
|
||||
} else if (pk_anon_fast_armor) {
|
||||
ret = krb5_init_creds_set_fast_anon_pkinit(context, ctx);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_init_creds_set_fast_anon_pkinit");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_keytab || keytab_str) {
|
||||
@@ -968,7 +984,6 @@ out:
|
||||
krb5_init_creds_free(context, ctx);
|
||||
if (tempccache)
|
||||
krb5_cc_destroy(context, tempccache);
|
||||
|
||||
if (enctype)
|
||||
free(enctype);
|
||||
|
||||
|
@@ -800,6 +800,60 @@ hx509_cms_verify_signed(hx509_context context,
|
||||
heim_oid *contentType,
|
||||
heim_octet_string *content,
|
||||
hx509_certs *signer_certs)
|
||||
{
|
||||
unsigned int verify_flags;
|
||||
|
||||
return hx509_cms_verify_signed_ext(context,
|
||||
ctx,
|
||||
flags,
|
||||
data,
|
||||
length,
|
||||
signedContent,
|
||||
pool,
|
||||
contentType,
|
||||
content,
|
||||
signer_certs,
|
||||
&verify_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode SignedData and verify that the signature is correct.
|
||||
*
|
||||
* @param context A hx509 context.
|
||||
* @param ctx a hx509 verify context.
|
||||
* @param flags to control the behaivor of the function.
|
||||
* - HX509_CMS_VS_NO_KU_CHECK - Don't check KeyUsage
|
||||
* - HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH - allow oid mismatch
|
||||
* - HX509_CMS_VS_ALLOW_ZERO_SIGNER - no signer, see below.
|
||||
* @param data pointer to CMS SignedData encoded data.
|
||||
* @param length length of the data that data point to.
|
||||
* @param signedContent external data used for signature.
|
||||
* @param pool certificate pool to build certificates paths.
|
||||
* @param contentType free with der_free_oid().
|
||||
* @param content the output of the function, free with
|
||||
* der_free_octet_string().
|
||||
* @param signer_certs list of the cerficates used to sign this
|
||||
* request, free with hx509_certs_free().
|
||||
* @param verify_flags flags indicating whether the certificate
|
||||
* was verified or not
|
||||
*
|
||||
* @return an hx509 error code.
|
||||
*
|
||||
* @ingroup hx509_cms
|
||||
*/
|
||||
|
||||
HX509_LIB_FUNCTION int HX509_LIB_CALL
|
||||
hx509_cms_verify_signed_ext(hx509_context context,
|
||||
hx509_verify_ctx ctx,
|
||||
unsigned int flags,
|
||||
const void *data,
|
||||
size_t length,
|
||||
const heim_octet_string *signedContent,
|
||||
hx509_certs pool,
|
||||
heim_oid *contentType,
|
||||
heim_octet_string *content,
|
||||
hx509_certs *signer_certs,
|
||||
unsigned int *verify_flags)
|
||||
{
|
||||
SignerInfo *signer_info;
|
||||
hx509_cert cert = NULL;
|
||||
@@ -810,6 +864,8 @@ hx509_cms_verify_signed(hx509_context context,
|
||||
size_t i;
|
||||
|
||||
*signer_certs = NULL;
|
||||
*verify_flags = 0;
|
||||
|
||||
content->data = NULL;
|
||||
content->length = 0;
|
||||
contentType->length = 0;
|
||||
@@ -1038,22 +1094,19 @@ hx509_cms_verify_signed(hx509_context context,
|
||||
goto next_sigature;
|
||||
|
||||
/**
|
||||
* If HX509_CMS_VS_NO_VALIDATE flags is set, do not verify the
|
||||
* signing certificates and leave that up to the caller.
|
||||
* If HX509_CMS_VS_NO_VALIDATE flags is set, return the signer
|
||||
* certificate unconditionally but do not set HX509_CMS_VSE_VALIDATED.
|
||||
*/
|
||||
ret = hx509_verify_path(context, ctx, cert, certs);
|
||||
if (ret == 0 || (flags & HX509_CMS_VS_NO_VALIDATE)) {
|
||||
if (ret == 0)
|
||||
*verify_flags |= HX509_CMS_VSE_VALIDATED;
|
||||
|
||||
if ((flags & HX509_CMS_VS_NO_VALIDATE) == 0) {
|
||||
ret = hx509_verify_path(context, ctx, cert, certs);
|
||||
if (ret)
|
||||
goto next_sigature;
|
||||
ret = hx509_certs_add(context, *signer_certs, cert);
|
||||
if (ret == 0)
|
||||
found_valid_sig++;
|
||||
}
|
||||
|
||||
ret = hx509_certs_add(context, *signer_certs, cert);
|
||||
if (ret)
|
||||
goto next_sigature;
|
||||
|
||||
found_valid_sig++;
|
||||
|
||||
next_sigature:
|
||||
if (cert)
|
||||
hx509_cert_free(cert);
|
||||
|
@@ -182,6 +182,9 @@ typedef enum {
|
||||
#define HX509_CMS_VS_ALLOW_ZERO_SIGNER 0x04
|
||||
#define HX509_CMS_VS_NO_VALIDATE 0x08
|
||||
|
||||
/* flags from hx509_cms_verify_signed_ext (out verify_flags) */
|
||||
#define HX509_CMS_VSE_VALIDATED 0x01
|
||||
|
||||
/* selectors passed to hx509_crypto_select and hx509_crypto_available */
|
||||
#define HX509_SELECT_ALL 0
|
||||
#define HX509_SELECT_DIGEST 1
|
||||
|
@@ -150,6 +150,7 @@ EXPORTS
|
||||
hx509_cms_unenvelope
|
||||
hx509_cms_unwrap_ContentInfo
|
||||
hx509_cms_verify_signed
|
||||
hx509_cms_verify_signed_ext
|
||||
hx509_cms_wrap_ContentInfo
|
||||
hx509_context_free
|
||||
hx509_context_init
|
||||
|
@@ -137,6 +137,7 @@ HEIMDAL_X509_1.2 {
|
||||
hx509_cms_unenvelope;
|
||||
hx509_cms_unwrap_ContentInfo;
|
||||
hx509_cms_verify_signed;
|
||||
hx509_cms_verify_signed_ext;
|
||||
hx509_cms_wrap_ContentInfo;
|
||||
hx509_context_free;
|
||||
hx509_context_init;
|
||||
|
@@ -96,12 +96,16 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
#define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
|
||||
#define KRB5_FAST_DISABLED 128
|
||||
#define KRB5_FAST_AP_ARMOR_SERVICE 256
|
||||
#define KRB5_FAST_ANON_PKINIT_ARMOR 512
|
||||
#define KRB5_FAST_KDC_VERIFIED 1024
|
||||
krb5_keyblock *reply_key;
|
||||
krb5_ccache armor_ccache;
|
||||
krb5_principal armor_service;
|
||||
krb5_crypto armor_crypto;
|
||||
krb5_keyblock armor_key;
|
||||
krb5_keyblock *strengthen_key;
|
||||
krb5_get_init_creds_opt *anon_pkinit_opt;
|
||||
krb5_init_creds_context anon_pkinit_ctx;
|
||||
} fast_state;
|
||||
} krb5_get_init_creds_ctx;
|
||||
|
||||
@@ -180,7 +184,12 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
if (ctx->fast_state.strengthen_key)
|
||||
krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
|
||||
krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
|
||||
|
||||
if (ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR)
|
||||
krb5_cc_destroy(context, ctx->fast_state.armor_ccache);
|
||||
if (ctx->fast_state.anon_pkinit_ctx)
|
||||
free_init_creds_ctx(context, ctx->fast_state.anon_pkinit_ctx);
|
||||
if (ctx->fast_state.anon_pkinit_opt)
|
||||
krb5_get_init_creds_opt_free(context, ctx->fast_state.anon_pkinit_opt);
|
||||
krb5_data_free(&ctx->req_buffer);
|
||||
krb5_free_cred_contents(context, &ctx->cred);
|
||||
free_METHOD_DATA(&ctx->md);
|
||||
@@ -1692,6 +1701,21 @@ krb5_init_creds_set_fast_ccache(krb5_context context,
|
||||
{
|
||||
ctx->fast_state.armor_ccache = fast_ccache;
|
||||
ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
|
||||
ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
|
||||
ctx->fast_state.flags &= ~(KRB5_FAST_ANON_PKINIT_ARMOR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_init_creds_set_fast_anon_pkinit(krb5_context context,
|
||||
krb5_init_creds_context ctx)
|
||||
{
|
||||
if (ctx->fast_state.armor_ccache)
|
||||
return EINVAL;
|
||||
|
||||
ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
|
||||
ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1855,6 +1879,7 @@ fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *err
|
||||
krb5_error_code
|
||||
_krb5_make_fast_ap_fxarmor(krb5_context context,
|
||||
krb5_ccache armor_ccache,
|
||||
krb5_const_realm realm,
|
||||
krb5_data *armor_value,
|
||||
krb5_keyblock *armor_key,
|
||||
krb5_crypto *armor_crypto)
|
||||
@@ -1863,6 +1888,7 @@ _krb5_make_fast_ap_fxarmor(krb5_context context,
|
||||
krb5_creds cred, *credp = NULL;
|
||||
krb5_error_code ret;
|
||||
krb5_data empty;
|
||||
krb5_const_realm tgs_realm;
|
||||
|
||||
krb5_data_zero(&empty);
|
||||
|
||||
@@ -1875,11 +1901,19 @@ _krb5_make_fast_ap_fxarmor(krb5_context context,
|
||||
ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
||||
/*
|
||||
* Make sure we don't ask for a krbtgt/WELLKNOWN:ANONYMOUS
|
||||
*/
|
||||
if (strcmp(cred.client->realm, KRB5_ANON_REALM) == 0)
|
||||
tgs_realm = realm;
|
||||
else
|
||||
tgs_realm = cred.client->realm;
|
||||
|
||||
ret = krb5_make_principal(context, &cred.server,
|
||||
cred.client->realm,
|
||||
tgs_realm,
|
||||
KRB5_TGS_NAME,
|
||||
cred.client->realm,
|
||||
tgs_realm,
|
||||
NULL);
|
||||
if (ret) {
|
||||
krb5_free_principal(context, cred.client);
|
||||
@@ -2162,34 +2196,13 @@ fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The core loop if krb5_get_init_creds() function family. Create the
|
||||
* packets and have the caller send them off to the KDC.
|
||||
*
|
||||
* If the caller want all work been done for them, use
|
||||
* krb5_init_creds_get() instead.
|
||||
*
|
||||
* @param context a Kerberos 5 context.
|
||||
* @param ctx ctx krb5_init_creds_context context.
|
||||
* @param in input data from KDC, first round it should be reset by krb5_data_zer().
|
||||
* @param out reply to KDC.
|
||||
* @param hostinfo KDC address info, first round it can be NULL.
|
||||
* @param flags status of the round, if
|
||||
* KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
|
||||
*
|
||||
* @return 0 for success, or an Kerberos 5 error code, see
|
||||
* krb5_get_error_message().
|
||||
*
|
||||
* @ingroup krb5_credential
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_init_creds_step(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_data *in,
|
||||
krb5_data *out,
|
||||
krb5_krbhst_info *hostinfo,
|
||||
unsigned int *flags)
|
||||
static krb5_error_code
|
||||
init_creds_step(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_data *in,
|
||||
krb5_data *out,
|
||||
krb5_krbhst_info *hostinfo,
|
||||
unsigned int *flags)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
size_t len = 0;
|
||||
@@ -2201,10 +2214,8 @@ krb5_init_creds_step(krb5_context context,
|
||||
if (ctx->as_req.req_body.cname == NULL) {
|
||||
ret = init_as_req(context, ctx->flags, &ctx->cred,
|
||||
ctx->addrs, ctx->etypes, &ctx->as_req);
|
||||
if (ret) {
|
||||
free_init_creds_ctx(context, ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_PA_COUNTER 10
|
||||
@@ -2468,10 +2479,8 @@ krb5_init_creds_step(krb5_context context,
|
||||
if (ctx->as_req.req_body.cname == NULL) {
|
||||
ret = init_as_req(context, ctx->flags, &ctx->cred,
|
||||
ctx->addrs, ctx->etypes, &ctx->as_req);
|
||||
if (ret) {
|
||||
free_init_creds_ctx(context, ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->as_req.padata) {
|
||||
@@ -2522,6 +2531,165 @@ krb5_init_creds_step(krb5_context context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_anon_pkinit_step(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_data *in,
|
||||
krb5_data *out,
|
||||
krb5_krbhst_info *hostinfo,
|
||||
unsigned int *flags)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_const_realm realm = ctx->cred.client->realm;
|
||||
struct fast_state *state = &ctx->fast_state;
|
||||
krb5_init_creds_context anon_pk_ctx;
|
||||
krb5_principal principal = NULL;
|
||||
krb5_ccache ccache = NULL;
|
||||
krb5_creds cred;
|
||||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
if (state->anon_pkinit_opt == NULL) {
|
||||
ret = krb5_get_init_creds_opt_alloc(context, &state->anon_pkinit_opt);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* ticket lifetime matches FAST_EXPIRATION_TIME in kdc_locl.h */
|
||||
krb5_get_init_creds_opt_set_tkt_life(state->anon_pkinit_opt, 3 * 60);
|
||||
krb5_get_init_creds_opt_set_anonymous(state->anon_pkinit_opt, TRUE);
|
||||
|
||||
ret = krb5_make_principal(context, &principal, realm,
|
||||
KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = krb5_get_init_creds_opt_set_pkinit(context,
|
||||
state->anon_pkinit_opt,
|
||||
principal,
|
||||
NULL, NULL, NULL, NULL,
|
||||
KRB5_GIC_OPT_PKINIT_ANONYMOUS |
|
||||
KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR,
|
||||
NULL, NULL, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_init_creds_init(context, principal, NULL, NULL,
|
||||
ctx->cred.times.starttime,
|
||||
state->anon_pkinit_opt,
|
||||
&state->anon_pkinit_ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
heim_assert((state->anon_pkinit_ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) == 0,
|
||||
"Invalid recursive PKINIT armor state");
|
||||
}
|
||||
|
||||
anon_pk_ctx = state->anon_pkinit_ctx;
|
||||
|
||||
ret = init_creds_step(context, anon_pk_ctx, in, out, hostinfo, flags);
|
||||
if (ret ||
|
||||
(*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
|
||||
goto out;
|
||||
|
||||
ret = krb5_process_last_request(context, state->anon_pkinit_opt, anon_pk_ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_init_creds_get_creds(context, anon_pk_ctx, &cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!cred.flags.b.enc_pa_rep) {
|
||||
ret = KRB5KDC_ERR_BADOPTION; /* KDC does not support FAST */
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_cc_initialize(context, ccache, anon_pk_ctx->cred.client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_store_cred(context, ccache, &cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (_krb5_pk_is_kdc_verified(context, state->anon_pkinit_opt))
|
||||
state->flags |= KRB5_FAST_KDC_VERIFIED;
|
||||
else
|
||||
state->flags &= ~(KRB5_FAST_KDC_VERIFIED);
|
||||
|
||||
state->armor_ccache = ccache;
|
||||
ccache = NULL;
|
||||
|
||||
free_init_creds_ctx(context, state->anon_pkinit_ctx);
|
||||
state->anon_pkinit_ctx = NULL;
|
||||
|
||||
krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
|
||||
state->anon_pkinit_opt = NULL;
|
||||
|
||||
*flags |= KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
|
||||
|
||||
out:
|
||||
krb5_free_principal(context, principal);
|
||||
krb5_free_cred_contents(context, &cred);
|
||||
if (ccache)
|
||||
krb5_cc_destroy(context, ccache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* The core loop if krb5_get_init_creds() function family. Create the
|
||||
* packets and have the caller send them off to the KDC.
|
||||
*
|
||||
* If the caller want all work been done for them, use
|
||||
* krb5_init_creds_get() instead.
|
||||
*
|
||||
* @param context a Kerberos 5 context.
|
||||
* @param ctx ctx krb5_init_creds_context context.
|
||||
* @param in input data from KDC, first round it should be reset by krb5_data_zer().
|
||||
* @param out reply to KDC.
|
||||
* @param hostinfo KDC address info, first round it can be NULL.
|
||||
* @param flags status of the round, if
|
||||
* KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
|
||||
*
|
||||
* @return 0 for success, or an Kerberos 5 error code, see
|
||||
* krb5_get_error_message().
|
||||
*
|
||||
* @ingroup krb5_credential
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_init_creds_step(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_data *in,
|
||||
krb5_data *out,
|
||||
krb5_krbhst_info *hostinfo,
|
||||
unsigned int *flags)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data empty;
|
||||
|
||||
krb5_data_zero(&empty);
|
||||
|
||||
if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) &&
|
||||
ctx->fast_state.armor_ccache == NULL) {
|
||||
ret = fast_anon_pkinit_step(context, ctx, in, out,
|
||||
hostinfo, flags);
|
||||
if (ret ||
|
||||
((*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0) ||
|
||||
out->length)
|
||||
return ret;
|
||||
|
||||
in = ∅
|
||||
}
|
||||
|
||||
return init_creds_step(context, ctx, in, out, hostinfo, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the newly acquired credentials from krb5_init_creds_context
|
||||
* context.
|
||||
|
@@ -354,6 +354,7 @@ struct krb5_pk_identity {
|
||||
hx509_revoke_ctx revokectx;
|
||||
int flags;
|
||||
#define PKINIT_BTMM 1
|
||||
#define PKINIT_NO_KDC_ANCHOR 2
|
||||
};
|
||||
|
||||
enum krb5_pk_type {
|
||||
@@ -380,6 +381,7 @@ struct krb5_pk_init_ctx_data {
|
||||
unsigned int require_hostname_match:1;
|
||||
unsigned int trustedCertifiers:1;
|
||||
unsigned int anonymous:1;
|
||||
unsigned int kdc_verified:1;
|
||||
};
|
||||
|
||||
#endif /* PKINIT */
|
||||
|
@@ -828,6 +828,7 @@ EXPORTS
|
||||
krb5_init_creds_get_creds
|
||||
krb5_init_creds_get_error
|
||||
krb5_init_creds_init
|
||||
krb5_init_creds_set_fast_anon_pkinit
|
||||
krb5_init_creds_set_fast_ccache
|
||||
krb5_init_creds_set_keytab
|
||||
krb5_init_creds_set_password
|
||||
|
@@ -787,7 +787,7 @@ _krb5_pk_mk_padata(krb5_context context,
|
||||
NULL);
|
||||
if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
|
||||
ctx->require_eku = 0;
|
||||
if (ctx->id->flags & PKINIT_BTMM)
|
||||
if (ctx->id->flags & (PKINIT_BTMM | PKINIT_NO_KDC_ANCHOR))
|
||||
ctx->require_eku = 0;
|
||||
|
||||
ctx->require_krbtgt_otherName =
|
||||
@@ -829,33 +829,39 @@ pk_verify_sign(krb5_context context,
|
||||
struct krb5_pk_cert **signer)
|
||||
{
|
||||
hx509_certs signer_certs;
|
||||
int ret, flags = 0;
|
||||
int ret;
|
||||
unsigned flags = 0, verify_flags = 0;
|
||||
|
||||
*signer = NULL;
|
||||
|
||||
/* BTMM is broken in Leo and SnowLeo */
|
||||
if (id->flags & PKINIT_BTMM) {
|
||||
flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
|
||||
flags |= HX509_CMS_VS_NO_KU_CHECK;
|
||||
flags |= HX509_CMS_VS_NO_VALIDATE;
|
||||
}
|
||||
if (id->flags & PKINIT_NO_KDC_ANCHOR)
|
||||
flags |= HX509_CMS_VS_NO_VALIDATE;
|
||||
|
||||
*signer = NULL;
|
||||
|
||||
ret = hx509_cms_verify_signed(context->hx509ctx,
|
||||
id->verify_ctx,
|
||||
flags,
|
||||
data,
|
||||
length,
|
||||
NULL,
|
||||
id->certpool,
|
||||
contentType,
|
||||
content,
|
||||
&signer_certs);
|
||||
ret = hx509_cms_verify_signed_ext(context->hx509ctx,
|
||||
id->verify_ctx,
|
||||
flags,
|
||||
data,
|
||||
length,
|
||||
NULL,
|
||||
id->certpool,
|
||||
contentType,
|
||||
content,
|
||||
&signer_certs,
|
||||
&verify_flags);
|
||||
if (ret) {
|
||||
pk_copy_error(context, context->hx509ctx, ret,
|
||||
"CMS verify signed failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((verify_flags & HX509_CMS_VSE_VALIDATED) == 0)
|
||||
goto out;
|
||||
|
||||
*signer = calloc(1, sizeof(**signer));
|
||||
if (*signer == NULL) {
|
||||
krb5_clear_error_message(context);
|
||||
@@ -1061,7 +1067,9 @@ pk_verify_host(krb5_context context,
|
||||
free_KRB5PrincipalName(&r);
|
||||
}
|
||||
hx509_free_octet_string_list(&list);
|
||||
if (matched == 0) {
|
||||
|
||||
if (matched == 0 &&
|
||||
(ctx->id->flags & PKINIT_NO_KDC_ANCHOR) == 0) {
|
||||
ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
|
||||
/* XXX: Lost in translation... */
|
||||
krb5_set_error_message(context, ret,
|
||||
@@ -1192,10 +1200,16 @@ pk_rd_pa_reply_enckey(krb5_context context,
|
||||
ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
|
||||
der_free_octet_string(&unwrapped);
|
||||
|
||||
/* make sure that it is the kdc's certificate */
|
||||
ret = pk_verify_host(context, realm, hi, ctx, host);
|
||||
if (ret) {
|
||||
goto out;
|
||||
heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
|
||||
"KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
|
||||
|
||||
if (host) {
|
||||
/* make sure that it is the kdc's certificate */
|
||||
ret = pk_verify_host(context, realm, hi, ctx, host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ctx->kdc_verified = 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -1374,10 +1388,17 @@ pk_rd_pa_reply_dh(krb5_context context,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* make sure that it is the kdc's certificate */
|
||||
ret = pk_verify_host(context, realm, hi, ctx, host);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
|
||||
"KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
|
||||
|
||||
if (host) {
|
||||
/* make sure that it is the kdc's certificate */
|
||||
ret = pk_verify_host(context, realm, hi, ctx, host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ctx->kdc_verified = 1;
|
||||
}
|
||||
|
||||
if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
|
||||
ret = KRB5KRB_AP_ERR_MSG_TYPE;
|
||||
@@ -1836,12 +1857,6 @@ _krb5_pk_load_id(krb5_context context,
|
||||
|
||||
*ret_id = NULL;
|
||||
|
||||
if (anchor_id == NULL) {
|
||||
krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
|
||||
N_("PKINIT: No anchor given", ""));
|
||||
return HEIM_PKINIT_NO_VALID_CA;
|
||||
}
|
||||
|
||||
/* load cert */
|
||||
|
||||
id = calloc(1, sizeof(*id));
|
||||
@@ -2383,6 +2398,13 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
|
||||
if (flags & KRB5_GIC_OPT_PKINIT_ANONYMOUS)
|
||||
opt->opt_private->pk_init_ctx->anonymous = 1;
|
||||
|
||||
if ((flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR) == 0 &&
|
||||
x509_anchors == NULL) {
|
||||
krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
|
||||
N_("PKINIT: No anchor given", ""));
|
||||
return HEIM_PKINIT_NO_VALID_CA;
|
||||
}
|
||||
|
||||
ret = _krb5_pk_load_id(context,
|
||||
&opt->opt_private->pk_init_ctx->id,
|
||||
user_id,
|
||||
@@ -2402,9 +2424,10 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
|
||||
}
|
||||
if (flags & KRB5_GIC_OPT_PKINIT_BTMM)
|
||||
opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
|
||||
|
||||
if (principal && krb5_principal_is_lkdc(context, principal))
|
||||
opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
|
||||
if (flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR)
|
||||
opt->opt_private->pk_init_ctx->id->flags |= PKINIT_NO_KDC_ANCHOR;
|
||||
|
||||
if (opt->opt_private->pk_init_ctx->id->certs) {
|
||||
ret = _krb5_pk_set_user_id(context,
|
||||
@@ -2624,3 +2647,15 @@ krb5_pk_enterprise_cert(krb5_context context,
|
||||
return EINVAL;
|
||||
#endif
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
|
||||
_krb5_pk_is_kdc_verified(krb5_context context,
|
||||
krb5_get_init_creds_opt *opt)
|
||||
{
|
||||
if (opt == NULL ||
|
||||
opt->opt_private == NULL ||
|
||||
opt->opt_private->pk_init_ctx == NULL)
|
||||
return FALSE;
|
||||
|
||||
return opt->opt_private->pk_init_ctx->kdc_verified;
|
||||
}
|
||||
|
@@ -814,6 +814,7 @@ HEIMDAL_KRB5_2.0 {
|
||||
krb5_process_last_request;
|
||||
krb5_init_creds_init;
|
||||
krb5_init_creds_set_service;
|
||||
krb5_init_creds_set_fast_anon_pkinit;
|
||||
krb5_init_creds_set_fast_ccache;
|
||||
krb5_init_creds_set_keytab;
|
||||
krb5_init_creds_get;
|
||||
|
Reference in New Issue
Block a user