kdc: support for GSS-API pre-authentication
Add support for GSS-API pre-authentication to the KDC, using a simplified variation of draft-perez-krb-wg-gss-preauth-02 that encodes GSS-API context tokens directly in PADATA, and uses FX-COOKIE for state management. More information on the protocol and implementation may be found in lib/gssapi/preauth/README.md.
This commit is contained in:
+435
-44
@@ -38,6 +38,21 @@
|
||||
#include <heim-ipc.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
struct krb5_gss_init_ctx_data {
|
||||
unsigned int flags;
|
||||
#define GSSIC_FLAG_RELEASE_CRED (1 << 0)
|
||||
#define GSSIC_FLAG_CONTEXT_OPEN (1 << 1)
|
||||
|
||||
krb5_gssic_step step;
|
||||
krb5_gssic_finish finish;
|
||||
krb5_gssic_release_cred release_cred;
|
||||
krb5_gssic_delete_sec_context delete_sec_context;
|
||||
|
||||
const struct gss_OID_desc_struct *mech;
|
||||
struct gss_cred_id_t_desc_struct *cred;
|
||||
struct gss_ctx_id_t_desc_struct *ctx;
|
||||
};
|
||||
|
||||
typedef struct krb5_get_init_creds_ctx {
|
||||
KDCOptions flags;
|
||||
krb5_creds cred;
|
||||
@@ -62,6 +77,7 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
krb5_get_init_creds_tristate req_pac;
|
||||
|
||||
krb5_pk_init_ctx pk_init_ctx;
|
||||
krb5_gss_init_ctx gss_init_ctx;
|
||||
int ic_flags;
|
||||
|
||||
struct {
|
||||
@@ -106,6 +122,7 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
krb5_keyblock *strengthen_key;
|
||||
krb5_get_init_creds_opt *anon_pkinit_opt;
|
||||
krb5_init_creds_context anon_pkinit_ctx;
|
||||
krb5_data cookie;
|
||||
} fast_state;
|
||||
} krb5_get_init_creds_ctx;
|
||||
|
||||
@@ -155,6 +172,15 @@ default_s2k_func(krb5_context context, krb5_enctype type,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
|
||||
{
|
||||
if (gssic->flags & GSSIC_FLAG_RELEASE_CRED)
|
||||
gssic->release_cred(context, gssic, gssic->cred);
|
||||
gssic->delete_sec_context(context, gssic, gssic->ctx);
|
||||
free(gssic);
|
||||
}
|
||||
|
||||
static void
|
||||
free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
{
|
||||
@@ -172,6 +198,8 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
memset_s(ctx->password, len, 0, len);
|
||||
free(ctx->password);
|
||||
}
|
||||
if (ctx->gss_init_ctx)
|
||||
free_gss_init_ctx(context, ctx->gss_init_ctx);
|
||||
/*
|
||||
* FAST state (we don't close the armor_ccache because we might have
|
||||
* to destroy it, and how would we know? also, the caller should
|
||||
@@ -183,6 +211,7 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
|
||||
if (ctx->fast_state.strengthen_key)
|
||||
krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
|
||||
krb5_data_free(&ctx->fast_state.cookie);
|
||||
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);
|
||||
@@ -1169,6 +1198,105 @@ pa_data_to_md_pkinit(krb5_context context,
|
||||
#endif
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
gss_pa_step(krb5_context context,
|
||||
const krb5_creds *creds,
|
||||
const krb5_get_init_creds_ctx *ctx,
|
||||
const METHOD_DATA *md,
|
||||
krb5_data *output_token)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
size_t len = 0;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
krb5_data req_body;
|
||||
PA_DATA *pa;
|
||||
krb5_data *input_token;
|
||||
|
||||
krb5_data_zero(&req_body);
|
||||
krb5_data_zero(output_token);
|
||||
|
||||
pa = find_pa_data(md, KRB5_PADATA_GSS);
|
||||
input_token = pa ? &pa->padata_value : NULL;
|
||||
|
||||
if (input_token == NULL || input_token->length == 0) {
|
||||
if (gssic->ctx == NULL)
|
||||
ret = 0; /* initial context token */
|
||||
else {
|
||||
krb5_set_error_message(context, KRB5_PREAUTH_BAD_TYPE,
|
||||
"Missing GSS preauthentication data from KDC");
|
||||
ret = KRB5_PREAUTH_BAD_TYPE;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (input_token && input_token->length &&
|
||||
(gssic->flags & GSSIC_FLAG_CONTEXT_OPEN)) {
|
||||
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
|
||||
"Already completed GSS authentication, looping");
|
||||
ret = KRB5_GET_IN_TKT_LOOP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
|
||||
&ctx->as_req.req_body, &len, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(req_body.length == len, "ASN.1 internal error");
|
||||
|
||||
ret = gssic->step(context, gssic, creds, ctx->flags, &req_body,
|
||||
input_token, output_token);
|
||||
|
||||
/*
|
||||
* If FAST authenticated the KDC (which will be the case unless anonymous
|
||||
* PKINIT was used without KDC certificate validation) then we can relax
|
||||
* the mutual authentication requirement.
|
||||
*/
|
||||
if (ret == KRB5_MUTUAL_FAILED &&
|
||||
(ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
|
||||
(ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
krb5_data_free(&req_body);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_data_to_md_gss(krb5_context context,
|
||||
const AS_REQ *a,
|
||||
const krb5_creds *creds,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
METHOD_DATA *in_md,
|
||||
METHOD_DATA *out_md)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data output_token;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
heim_assert(gssic != NULL, "invalid context passed to pa_data_to_md_gss");
|
||||
|
||||
ret = gss_pa_step(context, creds, ctx, in_md, &output_token);
|
||||
if (ret == 0)
|
||||
gssic->flags |= GSSIC_FLAG_CONTEXT_OPEN;
|
||||
else if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
ret = 0;
|
||||
|
||||
if (output_token.length) {
|
||||
ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
|
||||
output_token.data, output_token.length);
|
||||
if (ret) {
|
||||
krb5_data_free(&output_token);
|
||||
return ret;
|
||||
}
|
||||
krb5_data_zero(&output_token);
|
||||
} else
|
||||
krb5_data_free(&output_token);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_data_add_pac_request(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
@@ -1261,6 +1389,12 @@ process_pa_data_to_md(krb5_context context,
|
||||
else
|
||||
ctx->used_pa_types |= USED_PKINIT;
|
||||
|
||||
} else if (ctx->gss_init_ctx) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: preparing GSS credentials");
|
||||
|
||||
ret = pa_data_to_md_gss(context, a, creds, ctx, in_md, *out_md);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (in_md->len != 0) {
|
||||
struct pa_info_data *paid, *ppaid;
|
||||
unsigned flag;
|
||||
@@ -1317,6 +1451,61 @@ process_pa_data_to_md(krb5_context context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
gss_pa_data_to_key(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
krb5_creds *creds,
|
||||
krb5_enctype etype,
|
||||
METHOD_DATA *md,
|
||||
krb5_keyblock **key)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal cname = NULL;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
/*
|
||||
* Finish the GSS authentication and extract the reply key. If the GSS mechanism
|
||||
* required an odd number of legs, the context may already be open, but we still
|
||||
* needed to wait for a reply from the KDC.
|
||||
*/
|
||||
if ((gssic->flags & GSSIC_FLAG_CONTEXT_OPEN) == 0) {
|
||||
krb5_data output_token;
|
||||
|
||||
ret = gss_pa_step(context, creds, ctx, md, &output_token);
|
||||
if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED ||
|
||||
output_token.length) {
|
||||
krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
|
||||
"KDC sent AS-REP before GSS preauthentication completed");
|
||||
ret = KRB5_PREAUTH_FAILED;
|
||||
}
|
||||
krb5_data_free(&output_token);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
gssic->flags |= GSSIC_FLAG_CONTEXT_OPEN;
|
||||
}
|
||||
|
||||
ret = gssic->finish(context, gssic, creds, ctx->nonce, etype, &cname, key);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (krb5_principal_is_federated(context, creds->client)) {
|
||||
/* replace the wellknown federated name with the initiator name */
|
||||
krb5_free_principal(context, creds->client);
|
||||
creds->client = cname;
|
||||
cname = NULL;
|
||||
|
||||
/* allow the KDC to canonicalize the name */
|
||||
if (ctx->flags.canonicalize)
|
||||
ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
|
||||
}
|
||||
|
||||
out:
|
||||
krb5_free_principal(context, cname);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
process_pa_data_to_key(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
@@ -1353,18 +1542,9 @@ process_pa_data_to_key(krb5_context context,
|
||||
|
||||
pa = NULL;
|
||||
if (rep->padata) {
|
||||
int idx = 0;
|
||||
pa = krb5_find_padata(rep->padata->val,
|
||||
rep->padata->len,
|
||||
KRB5_PADATA_PK_AS_REP,
|
||||
&idx);
|
||||
if (pa == NULL) {
|
||||
idx = 0;
|
||||
pa = krb5_find_padata(rep->padata->val,
|
||||
rep->padata->len,
|
||||
KRB5_PADATA_PK_AS_REP_19,
|
||||
&idx);
|
||||
}
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_PK_AS_REP);
|
||||
if (pa == NULL)
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_PK_AS_REP_19);
|
||||
}
|
||||
if (pa && ctx->pk_init_ctx) {
|
||||
#ifdef PKINIT
|
||||
@@ -1383,6 +1563,9 @@ process_pa_data_to_key(krb5_context context,
|
||||
ret = EINVAL;
|
||||
krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
|
||||
#endif
|
||||
} else if (ctx->gss_init_ctx) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: using GSS for reply key");
|
||||
ret = gss_pa_data_to_key(context, ctx, creds, etype, rep->padata, key);
|
||||
} else if (ctx->keyseed) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
|
||||
ret = pa_data_to_key_plain(context, creds->client, ctx,
|
||||
@@ -1765,15 +1948,12 @@ fast_unwrap_as_rep(krb5_context context, int32_t nonce,
|
||||
KrbFastResponse fastrep;
|
||||
krb5_error_code ret;
|
||||
PA_DATA *pa = NULL;
|
||||
int idx = 0;
|
||||
|
||||
if (state->armor_crypto == NULL || rep->padata == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
/* find PA_FX_FAST_REPLY */
|
||||
|
||||
pa = krb5_find_padata(rep->padata->val, rep->padata->len,
|
||||
KRB5_PADATA_FX_FAST, &idx);
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_FX_FAST);
|
||||
if (pa == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
@@ -1863,17 +2043,105 @@ fast_unwrap_as_rep(krb5_context context, int32_t nonce,
|
||||
|
||||
out:
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
free_KrbFastResponse(&fastrep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
|
||||
fast_unwrap_error(krb5_context context,
|
||||
int32_t nonce,
|
||||
struct fast_state *state,
|
||||
KRB_ERROR *error,
|
||||
METHOD_DATA *error_method)
|
||||
{
|
||||
if (state->armor_crypto == NULL)
|
||||
return check_fast(context, state);
|
||||
METHOD_DATA md;
|
||||
PA_FX_FAST_REPLY fxfastrep;
|
||||
KrbFastResponse fastrep;
|
||||
krb5_error_code ret;
|
||||
PA_DATA *pa;
|
||||
KRB_ERROR fast_error;
|
||||
|
||||
return 0;
|
||||
memset(&md, 0, sizeof(md));
|
||||
memset(&fxfastrep, 0, sizeof(fxfastrep));
|
||||
memset(&fastrep, 0, sizeof(fastrep));
|
||||
memset(&fast_error, 0, sizeof(fast_error));
|
||||
|
||||
if (error->e_data) {
|
||||
ret = decode_METHOD_DATA(error->e_data->data,
|
||||
error->e_data->length, &md, NULL);
|
||||
if (ret) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Failed to decode METHOD-DATA", ""));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->armor_crypto == NULL ||
|
||||
(pa = find_pa_data(&md, KRB5_PADATA_FX_FAST)) == NULL) {
|
||||
free_METHOD_DATA(error_method);
|
||||
*error_method = md;
|
||||
return check_fast(context, state);
|
||||
}
|
||||
|
||||
ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data,
|
||||
pa->padata_value.length,
|
||||
&fxfastrep,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
|
||||
krb5_data data;
|
||||
ret = krb5_decrypt_EncryptedData(context,
|
||||
state->armor_crypto,
|
||||
KRB5_KU_FAST_REP,
|
||||
&fxfastrep.u.armored_data.enc_fast_rep,
|
||||
&data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
|
||||
krb5_data_free(&data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
} else {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* RFC 6113 5.4.3: strengthen key must be absent in error reply */
|
||||
if (fastrep.strengthen_key || nonce != fastrep.nonce) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pa = find_pa_data(&fastrep.padata, KRB5_PADATA_FX_ERROR);
|
||||
if (pa == NULL) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_rd_error(context, &pa->padata_value, &fast_error);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
free_KRB_ERROR(error);
|
||||
*error = fast_error;
|
||||
memset(&fast_error, 0, sizeof(fast_error));
|
||||
|
||||
free_METHOD_DATA(error_method);
|
||||
*error_method = fastrep.padata;
|
||||
memset(&fastrep.padata, 0, sizeof(fastrep.padata));
|
||||
|
||||
out:
|
||||
free_METHOD_DATA(&md);
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
free_KrbFastResponse(&fastrep);
|
||||
free_KRB_ERROR(&fast_error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
@@ -2058,7 +2326,9 @@ make_fast_ap_fxarmor(krb5_context context,
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
fast_wrap_req(krb5_context context,
|
||||
struct fast_state *state,
|
||||
KDC_REQ *req)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
PA_FX_FAST_REQUEST fxreq;
|
||||
@@ -2067,6 +2337,16 @@ fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
krb5_data data;
|
||||
size_t size;
|
||||
|
||||
/* FX-COOKIE can be used without FAST armor */
|
||||
if (state->cookie.data) {
|
||||
ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_COOKIE,
|
||||
state->cookie.data, state->cookie.length);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
krb5_data_zero(&state->cookie);
|
||||
}
|
||||
|
||||
if (state->flags & KRB5_FAST_DISABLED) {
|
||||
_krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
|
||||
return 0;
|
||||
@@ -2196,6 +2476,43 @@ fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_validate_conversation(METHOD_DATA *md, struct fast_state *state)
|
||||
{
|
||||
PA_DATA *pa;
|
||||
|
||||
/*
|
||||
* RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
|
||||
* expects at least one more message from the client.
|
||||
*/
|
||||
pa = find_pa_data(md, KRB5_PADATA_FX_COOKIE);
|
||||
if (pa == NULL)
|
||||
return KRB5_PREAUTH_FAILED;
|
||||
|
||||
krb5_data_free(&state->cookie);
|
||||
state->cookie = pa->padata_value;
|
||||
krb5_data_zero(&pa->padata_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
available_padata_count(METHOD_DATA *md)
|
||||
{
|
||||
size_t i, count = 0;
|
||||
|
||||
for (i = 0; i < md->len; i++) {
|
||||
PA_DATA *pa = &md->val[i];
|
||||
|
||||
if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
|
||||
pa->padata_type == KRB5_PADATA_FX_ERROR)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
init_creds_step(krb5_context context,
|
||||
@@ -2219,7 +2536,7 @@ init_creds_step(krb5_context context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MAX_PA_COUNTER 10
|
||||
#define MAX_PA_COUNTER 15
|
||||
if (ctx->pa_counter > MAX_PA_COUNTER) {
|
||||
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
|
||||
N_("Looping %d times while getting "
|
||||
@@ -2296,13 +2613,8 @@ init_creds_step(krb5_context context,
|
||||
NULL);
|
||||
if (ret == 0 && ctx->pk_init_ctx) {
|
||||
PA_DATA *pa_pkinit_kx;
|
||||
int idx = 0;
|
||||
|
||||
pa_pkinit_kx =
|
||||
krb5_find_padata(rep.kdc_rep.padata->val,
|
||||
rep.kdc_rep.padata->len,
|
||||
KRB5_PADATA_PKINIT_KX,
|
||||
&idx);
|
||||
pa_pkinit_kx = find_pa_data(rep.kdc_rep.padata, KRB5_PADATA_PKINIT_KX);
|
||||
|
||||
ret = _krb5_pk_kx_confirm(context, ctx->pk_init_ctx,
|
||||
ctx->fast_state.reply_key,
|
||||
@@ -2344,7 +2656,8 @@ init_creds_step(krb5_context context,
|
||||
/*
|
||||
* Unwrap KRB-ERROR
|
||||
*/
|
||||
ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
|
||||
ret = fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
|
||||
&ctx->error, &ctx->md);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@@ -2361,24 +2674,17 @@ init_creds_step(krb5_context context,
|
||||
* more try.
|
||||
*/
|
||||
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
|
||||
|
||||
free_METHOD_DATA(&ctx->md);
|
||||
memset_s(&ctx->md, sizeof(ctx->md), 0, sizeof(ctx->md));
|
||||
|
||||
if (ctx->error.e_data) {
|
||||
ret = decode_METHOD_DATA(ctx->error.e_data->data,
|
||||
ctx->error.e_data->length,
|
||||
&ctx->md,
|
||||
NULL);
|
||||
if (ret)
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Failed to decode METHOD-DATA", ""));
|
||||
} else {
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED ||
|
||||
ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) {
|
||||
if (available_padata_count(&ctx->md) == 0) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Preauth required but no preauth "
|
||||
"options send by KDC", ""));
|
||||
}
|
||||
if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
ret = fast_validate_conversation(&ctx->md, &ctx->fast_state);
|
||||
else
|
||||
ret = 0;
|
||||
} else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
|
||||
/*
|
||||
* Try adapt to timeskrew when we are using pre-auth, and
|
||||
@@ -2819,7 +3125,7 @@ krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if ((flags & 1) == 0)
|
||||
if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
|
||||
break;
|
||||
|
||||
ret = krb5_sendto_context (context, stctx, &out,
|
||||
@@ -3056,3 +3362,88 @@ krb5_get_init_creds_keytab(krb5_context context,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_mechanism(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
const struct gss_OID_desc_struct *gss_mech)
|
||||
{
|
||||
gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_mechanism(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->mech;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
struct gss_cred_id_t_desc_struct *gss_cred)
|
||||
{
|
||||
if (gssic->cred != gss_cred &&
|
||||
(gssic->flags & GSSIC_FLAG_RELEASE_CRED))
|
||||
gssic->release_cred(context, gssic, gssic->cred);
|
||||
|
||||
gssic->cred = gss_cred;
|
||||
gssic->flags |= GSSIC_FLAG_RELEASE_CRED;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->cred;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_context(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
struct gss_ctx_id_t_desc_struct *gss_ctx)
|
||||
{
|
||||
if (gssic->ctx != gss_ctx)
|
||||
gssic->delete_sec_context(context, gssic, gssic->ctx);
|
||||
gssic->ctx = gss_ctx;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_ctx_id_t_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_context(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->ctx;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_init_creds_init_gss(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_gssic_step step,
|
||||
krb5_gssic_finish finish,
|
||||
krb5_gssic_release_cred release_cred,
|
||||
krb5_gssic_delete_sec_context delete_sec_context,
|
||||
const struct gss_cred_id_t_desc_struct *gss_cred,
|
||||
const struct gss_OID_desc_struct *gss_mech,
|
||||
unsigned int flags)
|
||||
{
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
gssic = calloc(1, sizeof(*gssic));
|
||||
if (gssic == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
if (ctx->gss_init_ctx)
|
||||
free_gss_init_ctx(context, ctx->gss_init_ctx);
|
||||
ctx->gss_init_ctx = gssic;
|
||||
|
||||
gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
|
||||
gssic->mech = gss_mech;
|
||||
gssic->flags = flags;
|
||||
|
||||
gssic->step = step;
|
||||
gssic->finish = finish;
|
||||
gssic->release_cred = release_cred;
|
||||
gssic->delete_sec_context = delete_sec_context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -901,6 +901,32 @@ can be given as a number followed by a unit, such as
|
||||
.Dq 2d
|
||||
for
|
||||
.Dq two days .
|
||||
.It Li enable_gss_preauth = Va boolean
|
||||
Enables pre-authentication using a GSS-API mechanism supported by the client and KDC.
|
||||
The GSS-API initiator and AS request client names must match, unless the
|
||||
.Li
|
||||
WELLKNOWN/FEDERATED
|
||||
name was used in the AS request, in which case the AS reply will contain the
|
||||
GSS-API initiator name. Authorization and mapping behavior may be customized
|
||||
by plugins. If synthetic clients are enabled, then the GSS-API initiator need
|
||||
not exist in the local database. GSS-API pre-authentication is disabled by
|
||||
default.
|
||||
.It Li enable_gss_auth_data = Va boolean
|
||||
When using GSS-API pre-authentication, includes a Kerberos authorization data
|
||||
element containing naming attributes associated with the GSS-API initiator. This
|
||||
is disabled by default as it may significantly increase the size of returned
|
||||
tickets.
|
||||
.It Li gss_mechanisms_allowed = Va mechs ...
|
||||
A list of GSS-API mechanisms that may be used for GSS-API pre-authentication.
|
||||
.It Li gss_cross_realm_mechanisms_allowed = Va mechs ...
|
||||
A list of GSS-API mechanisms that, when using the default authorization
|
||||
mechanism, will be permitted to map Kerberos principals in foreign realms. The
|
||||
list is empty by default. Initiator names from mechanisms not on this list will
|
||||
be mapped to an enterprise principal in the AS-REQ realm. This option is
|
||||
intended to avoid conflating GSS-API pre-authentication and Kerberos
|
||||
cross-realm authentication. The behavior is provided by the default
|
||||
authorization mechanism and will be overridden by an authorization plugin.
|
||||
Mechanisms may be identified by dot-separated OID or a short name.
|
||||
.It Li historical_anon_realm = Va boolean
|
||||
Enables pre-7.0 non-RFC-comformant KDC behavior.
|
||||
With this option set to
|
||||
|
||||
+2
-1
@@ -715,10 +715,11 @@ typedef EncAPRepPart krb5_ap_rep_enc_part;
|
||||
#define KRB5_WELLKNOWN_NAME ("WELLKNOWN")
|
||||
#define KRB5_ANON_NAME ("ANONYMOUS")
|
||||
#define KRB5_ANON_REALM ("WELLKNOWN:ANONYMOUS")
|
||||
#define KRB5_FEDERATED_NAME ("FEDERATED")
|
||||
#define KRB5_FEDERATED_REALM ("WELLKNOWN:FEDERATED")
|
||||
#define KRB5_WELLKNOWN_ORG_H5L_REALM ("WELLKNOWN:ORG.H5L")
|
||||
#define KRB5_DIGEST_NAME ("digest")
|
||||
|
||||
|
||||
#define KRB5_PKU2U_REALM_NAME ("WELLKNOWN:PKU2U")
|
||||
#define KRB5_LKDC_REALM_NAME ("WELLKNOWN:COM.APPLE.LKDC")
|
||||
|
||||
|
||||
@@ -101,12 +101,13 @@ error_code DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signedData not accepte
|
||||
error_code PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
|
||||
|
||||
## these are never used
|
||||
#index 80
|
||||
#index 85
|
||||
#prefix KRB5_IAKERB
|
||||
#error_code ERR_KDC_NOT_FOUND, "IAKERB proxy could not find a KDC"
|
||||
#error_code ERR_KDC_NO_RESPONSE, "IAKERB proxy never reeived a response from a KDC"
|
||||
|
||||
# 82-93 are reserved
|
||||
index 91
|
||||
error_code MORE_PREAUTH_DATA_REQUIRED, "More pre-authentication data required"
|
||||
|
||||
index 94
|
||||
error_code INVALID_HASH_ALG, "Invalid OTP digest algorithm"
|
||||
|
||||
@@ -140,6 +140,13 @@ struct krb5_dh_moduli;
|
||||
/* v4 glue */
|
||||
struct _krb5_krb_auth_data;
|
||||
|
||||
struct krb5_gss_init_ctx_data;
|
||||
typedef struct krb5_gss_init_ctx_data *krb5_gss_init_ctx;
|
||||
|
||||
struct gss_ctx_id_t_desc_struct;
|
||||
struct gss_cred_id_t_desc_struct;
|
||||
struct gss_OID_desc_struct;
|
||||
|
||||
#include <der.h>
|
||||
|
||||
#include <krb5.h>
|
||||
@@ -152,6 +159,34 @@ struct _krb5_krb_auth_data;
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_step)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
const krb5_creds *,
|
||||
KDCOptions options,
|
||||
krb5_data *,
|
||||
krb5_data *,
|
||||
krb5_data *);
|
||||
|
||||
typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_finish)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
const krb5_creds *,
|
||||
krb5int32,
|
||||
krb5_enctype,
|
||||
krb5_principal *,
|
||||
krb5_keyblock **);
|
||||
|
||||
typedef void (KRB5_LIB_CALL *krb5_gssic_release_cred)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
struct gss_cred_id_t_desc_struct *);
|
||||
|
||||
typedef void (KRB5_LIB_CALL *krb5_gssic_delete_sec_context)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
struct gss_ctx_id_t_desc_struct *);
|
||||
|
||||
#include <krb5-private.h>
|
||||
|
||||
#include "heim_threads.h"
|
||||
|
||||
@@ -525,6 +525,7 @@ EXPORTS
|
||||
krb5_principal_get_realm
|
||||
krb5_principal_get_type
|
||||
krb5_principal_is_anonymous
|
||||
krb5_principal_is_federated
|
||||
krb5_principal_is_krbtgt
|
||||
krb5_principal_is_root_krbtgt
|
||||
krb5_principal_match
|
||||
@@ -792,6 +793,15 @@ EXPORTS
|
||||
_krb5_SP800_108_HMAC_KDF
|
||||
_krb5_get_ad
|
||||
|
||||
; Shared with GSSAPI preauth wrapper
|
||||
_krb5_init_creds_set_gss_mechanism
|
||||
_krb5_init_creds_get_gss_mechanism
|
||||
_krb5_init_creds_set_gss_cred
|
||||
_krb5_init_creds_get_gss_cred
|
||||
_krb5_init_creds_set_gss_context
|
||||
_krb5_init_creds_get_gss_context
|
||||
_krb5_init_creds_init_gss
|
||||
|
||||
; Shared with libkadm5
|
||||
_krb5_load_plugins
|
||||
_krb5_unload_plugins
|
||||
|
||||
@@ -1312,6 +1312,28 @@ krb5_principal_is_anonymous(krb5_context context,
|
||||
return strcmp(p->realm, KRB5_ANON_REALM) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff name is WELLKNOWN/FEDERATED
|
||||
*
|
||||
* @ingroup krb5_principal
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
|
||||
krb5_principal_is_federated(krb5_context context,
|
||||
krb5_const_principal p)
|
||||
{
|
||||
if (p->name.name_type != KRB5_NT_WELLKNOWN &&
|
||||
p->name.name_type != KRB5_NT_UNKNOWN)
|
||||
return FALSE;
|
||||
|
||||
if (p->name.name_string.len != 2 ||
|
||||
strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
|
||||
strcmp(p->name.name_string.val[1], KRB5_FEDERATED_NAME) != 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
tolower_ascii(int c)
|
||||
{
|
||||
|
||||
@@ -522,6 +522,7 @@ HEIMDAL_KRB5_2.0 {
|
||||
krb5_principal_set_realm;
|
||||
krb5_principal_set_type;
|
||||
krb5_principal_is_anonymous;
|
||||
krb5_principal_is_federated;
|
||||
krb5_principal_is_krbtgt;
|
||||
krb5_principal_is_root_krbtgt;
|
||||
krb5_print_address;
|
||||
@@ -784,6 +785,15 @@ HEIMDAL_KRB5_2.0 {
|
||||
_krb5_SP800_108_HMAC_KDF;
|
||||
_krb5_get_ad;
|
||||
|
||||
# Shared with GSSAPI preauth wrapper
|
||||
_krb5_init_creds_set_gss_mechanism;
|
||||
_krb5_init_creds_get_gss_mechanism;
|
||||
_krb5_init_creds_set_gss_cred;
|
||||
_krb5_init_creds_get_gss_cred;
|
||||
_krb5_init_creds_set_gss_context;
|
||||
_krb5_init_creds_get_gss_context;
|
||||
_krb5_init_creds_init_gss;
|
||||
|
||||
# Shared with libkadm5
|
||||
_krb5_load_plugins;
|
||||
_krb5_unload_plugins;
|
||||
|
||||
Reference in New Issue
Block a user