merge support for FAST in as-req codepath
This commit is contained in:
@@ -888,7 +888,7 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ
|
||||
fprintf(headerfile, "heim_octet_string _save;\n");
|
||||
}
|
||||
space(level + 1);
|
||||
fprintf (headerfile, "enum {\n");
|
||||
fprintf (headerfile, "enum %s_enum {\n", newbasename);
|
||||
m = have_ellipsis(t);
|
||||
if (m) {
|
||||
space(level + 2);
|
||||
|
@@ -63,6 +63,8 @@ EXPORTS
|
||||
PA-ServerReferralData,
|
||||
PA-SvrReferralData,
|
||||
PADATA-TYPE,
|
||||
PA-FX-FAST-REQUEST,
|
||||
PA-FX-FAST-REPLY,
|
||||
Principal,
|
||||
PrincipalName,
|
||||
Principals,
|
||||
@@ -72,7 +74,13 @@ EXPORTS
|
||||
Ticket,
|
||||
TicketFlags,
|
||||
TransitedEncoding,
|
||||
TypedData
|
||||
TypedData,
|
||||
KrbFastResponse,
|
||||
KrbFastFinished,
|
||||
KrbFastReq,
|
||||
KrbFastArmor,
|
||||
KDCFastState,
|
||||
KDCFastCookie
|
||||
;
|
||||
|
||||
NAME-TYPE ::= INTEGER {
|
||||
@@ -771,12 +779,12 @@ PA-ServerReferralData ::= SEQUENCE {
|
||||
FastOptions ::= BIT STRING {
|
||||
reserved(0),
|
||||
hide-client-names(1),
|
||||
kdc-follow--referrals(16)
|
||||
kdc-follow-referrals(16)
|
||||
}
|
||||
|
||||
KrbFastReq ::= SEQUENCE {
|
||||
fast-options [0] FastOptions,
|
||||
padata [1] SEQUENCE OF PA-DATA,
|
||||
padata [1] METHOD-DATA,
|
||||
req-body [2] KDC-REQ-BODY,
|
||||
...
|
||||
}
|
||||
@@ -803,15 +811,15 @@ KrbFastFinished ::= SEQUENCE {
|
||||
usec [1] krb5int32,
|
||||
crealm [2] Realm,
|
||||
cname [3] PrincipalName,
|
||||
checksum [4] Checksum,
|
||||
ticket-checksum [5] Checksum,
|
||||
ticket-checksum [4] Checksum,
|
||||
...
|
||||
}
|
||||
|
||||
KrbFastResponse ::= SEQUENCE {
|
||||
padata [0] SEQUENCE OF PA-DATA,
|
||||
rep-key [1] EncryptionKey OPTIONAL,
|
||||
finished [2] KrbFastFinished OPTIONAL,
|
||||
padata [0] METHOD-DATA,
|
||||
strengthen-key [1] EncryptionKey OPTIONAL,
|
||||
finished [2] KrbFastFinished OPTIONAL,
|
||||
nonce [3] krb5uint32,
|
||||
...
|
||||
}
|
||||
|
||||
@@ -825,6 +833,26 @@ PA-FX-FAST-REPLY ::= CHOICE {
|
||||
...
|
||||
}
|
||||
|
||||
KDCFastFlags ::= BIT STRING {
|
||||
use_reply_key(0),
|
||||
reply_key_used(1),
|
||||
reply_key_replaced(2),
|
||||
kdc_verfied(3)
|
||||
}
|
||||
|
||||
-- KDCFastState is stored in FX_COOKIE
|
||||
KDCFastState ::= SEQUENCE {
|
||||
flags [0] KDCFastFlags,
|
||||
expiration [1] GeneralizedTime,
|
||||
fast-state [2] METHOD-DATA,
|
||||
expected-pa-types [3] SEQUENCE OF PADATA-TYPE OPTIONAL
|
||||
}
|
||||
|
||||
KDCFastCookie ::= SEQUENCE {
|
||||
version [0] UTF8String,
|
||||
cookie [1] EncryptedData
|
||||
}
|
||||
|
||||
END
|
||||
|
||||
-- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1
|
||||
|
@@ -147,6 +147,7 @@ dist_libkrb5_la_SOURCES = \
|
||||
error_string.c \
|
||||
expand_hostname.c \
|
||||
expand_path.c \
|
||||
fast.c \
|
||||
fcache.c \
|
||||
free.c \
|
||||
free_host_realm.c \
|
||||
|
@@ -64,7 +64,7 @@ struct _krb5_checksum_type _krb5_checksum_none = {
|
||||
"none",
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
F_DISABLED,
|
||||
NONE_checksum,
|
||||
NULL
|
||||
};
|
||||
|
95
lib/krb5/fast.c
Normal file
95
lib/krb5/fast.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "krb5_locl.h"
|
||||
|
||||
|
||||
krb5_error_code
|
||||
_krb5_fast_cf2(krb5_context context,
|
||||
krb5_keyblock *key1,
|
||||
const char *pepper1,
|
||||
krb5_keyblock *key2,
|
||||
const char *pepper2,
|
||||
krb5_keyblock *armorkey,
|
||||
krb5_crypto *armor_crypto)
|
||||
{
|
||||
krb5_crypto crypto1, crypto2;
|
||||
krb5_data pa1, pa2;
|
||||
krb5_error_code ret;
|
||||
|
||||
ret = krb5_crypto_init(context, key1, 0, &crypto1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = krb5_crypto_init(context, key2, 0, &crypto2);
|
||||
if (ret) {
|
||||
krb5_crypto_destroy(context, crypto1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pa1.data = rk_UNCONST(pepper1);
|
||||
pa1.length = strlen(pepper1);
|
||||
pa2.data = rk_UNCONST(pepper2);
|
||||
pa2.length = strlen(pepper2);
|
||||
|
||||
ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2,
|
||||
key1->keytype, armorkey);
|
||||
krb5_crypto_destroy(context, crypto1);
|
||||
krb5_crypto_destroy(context, crypto2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (armor_crypto) {
|
||||
ret = krb5_crypto_init(context, armorkey, 0, armor_crypto);
|
||||
if (ret)
|
||||
krb5_free_keyblock_contents(context, armorkey);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_fast_armor_key(krb5_context context,
|
||||
krb5_keyblock *subkey,
|
||||
krb5_keyblock *sessionkey,
|
||||
krb5_keyblock *armorkey,
|
||||
krb5_crypto *armor_crypto)
|
||||
{
|
||||
return _krb5_fast_cf2(context,
|
||||
subkey,
|
||||
"subkeyarmor",
|
||||
sessionkey,
|
||||
"ticketarmor",
|
||||
armorkey,
|
||||
armor_crypto);
|
||||
}
|
@@ -544,6 +544,7 @@ get_cred_kdc(krb5_context context,
|
||||
&krbtgt->addresses,
|
||||
nonce,
|
||||
eflags,
|
||||
NULL,
|
||||
decrypt_tkt_with_subkey,
|
||||
subkey);
|
||||
out2:
|
||||
|
@@ -510,6 +510,7 @@ krb5_get_in_cred(krb5_context context,
|
||||
NULL,
|
||||
nonce,
|
||||
flags,
|
||||
NULL,
|
||||
decrypt_proc,
|
||||
decryptarg);
|
||||
}
|
||||
|
@@ -76,7 +76,20 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
void *prompter_data;
|
||||
|
||||
struct pa_info_data *ppaid;
|
||||
|
||||
struct fast_state {
|
||||
enum PA_FX_FAST_REQUEST_enum type;
|
||||
unsigned int flags;
|
||||
#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
|
||||
#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
|
||||
#define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
|
||||
#define KRB5_FAST_REPLY_REPLY_VERIFED 8
|
||||
#define KRB5_FAST_STRONG 16
|
||||
#define KRB5_FAST_EXPECTED 32
|
||||
krb5_keyblock *reply_key;
|
||||
krb5_ccache armor_ccache;
|
||||
krb5_crypto armor_crypto;
|
||||
krb5_keyblock armor_key;
|
||||
} fast_state;
|
||||
} krb5_get_init_creds_ctx;
|
||||
|
||||
|
||||
@@ -140,6 +153,15 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
memset(ctx->password, 0, strlen(ctx->password));
|
||||
free(ctx->password);
|
||||
}
|
||||
/*
|
||||
* FAST state
|
||||
*/
|
||||
if (ctx->fast_state.armor_ccache)
|
||||
krb5_cc_close(context, ctx->fast_state.armor_ccache);
|
||||
if (ctx->fast_state.armor_crypto)
|
||||
krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
|
||||
krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
|
||||
|
||||
krb5_data_free(&ctx->req_buffer);
|
||||
krb5_free_cred_contents(context, &ctx->cred);
|
||||
free_METHOD_DATA(&ctx->md);
|
||||
@@ -251,10 +273,10 @@ report_expiration (krb5_context context,
|
||||
* @param ctx The krb5_init_creds_context check for expiration.
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
process_last_request(krb5_context context,
|
||||
krb5_get_init_creds_opt *options,
|
||||
krb5_init_creds_context ctx)
|
||||
krb5_error_code
|
||||
krb5_process_last_request(krb5_context context,
|
||||
krb5_get_init_creds_opt *options,
|
||||
krb5_init_creds_context ctx)
|
||||
{
|
||||
krb5_const_realm realm;
|
||||
LastReq *lr;
|
||||
@@ -1237,6 +1259,10 @@ process_pa_data_to_md(krb5_context context,
|
||||
|
||||
pa_data_add_pac_request(context, ctx, *out_md);
|
||||
|
||||
ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((*out_md)->len == 0) {
|
||||
free(*out_md);
|
||||
*out_md = NULL;
|
||||
@@ -1618,6 +1644,352 @@ krb5_init_creds_set_keyblock(krb5_context context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_init_creds_set_fast_ccache(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_ccache fast_ccache)
|
||||
{
|
||||
ctx->fast_state.armor_ccache = fast_ccache;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* FAST
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
check_fast(krb5_context context, struct fast_state *state)
|
||||
{
|
||||
if (state->flags & KRB5_FAST_EXPECTED) {
|
||||
krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
|
||||
"Expected FAST, but no FAST "
|
||||
"was in the response from the KDC");
|
||||
return KRB5KRB_AP_ERR_MODIFIED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
fast_unwrap_as_rep(krb5_context context, int32_t nonce,
|
||||
krb5_data *chksumdata,
|
||||
struct fast_state *state, AS_REP *rep)
|
||||
{
|
||||
PA_FX_FAST_REPLY fxfastrep;
|
||||
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);
|
||||
if (pa == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
memset(&fxfastrep, 0, sizeof(fxfastrep));
|
||||
memset(&fastrep, 0, sizeof(fastrep));
|
||||
|
||||
ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
free_METHOD_DATA(rep->padata);
|
||||
ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (fastrep.strengthen_key) {
|
||||
krb5_keyblock result;
|
||||
|
||||
ret = _krb5_fast_cf2(context,
|
||||
fastrep.strengthen_key,
|
||||
"strengthenkey",
|
||||
state->reply_key,
|
||||
"replykey",
|
||||
&result,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
krb5_free_keyblock_contents(context, state->reply_key);
|
||||
*state->reply_key = result;
|
||||
}
|
||||
|
||||
if (nonce != fastrep.nonce) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
if (fastrep.finished) {
|
||||
PrincipalName cname;
|
||||
krb5_realm crealm = NULL;
|
||||
|
||||
if (chksumdata == NULL) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_verify_checksum(context, state->armor_crypto,
|
||||
KRB5_KU_FAST_FINISHED,
|
||||
chksumdata->data, chksumdata->length,
|
||||
&fastrep.finished->ticket_checksum);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* update */
|
||||
ret = copy_Realm(&fastrep.finished->crealm, &crealm);
|
||||
if (ret)
|
||||
goto out;
|
||||
free_Realm(&rep->crealm);
|
||||
rep->crealm = crealm;
|
||||
|
||||
ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
|
||||
if (ret)
|
||||
goto out;
|
||||
free_PrincipalName(&rep->cname);
|
||||
rep->cname = cname;
|
||||
|
||||
#if 0 /* store authenticated checksum as kdc-offset */
|
||||
fastrep->finished.timestamp;
|
||||
fastrep->finished.usec = 0;
|
||||
#endif
|
||||
|
||||
} else if (chksumdata) {
|
||||
/* expected fastrep.finish but didn't get it */
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
}
|
||||
|
||||
out:
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
|
||||
{
|
||||
if (state->armor_crypto == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
make_fast_ap_fxarmor(krb5_context context,
|
||||
struct fast_state *state,
|
||||
KrbFastArmor **armor)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
krb5_auth_context auth_context = NULL;
|
||||
krb5_creds cred, *credp = NULL;
|
||||
krb5_error_code ret;
|
||||
|
||||
ALLOC(fxarmor, 1);
|
||||
if (fxarmor == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fxarmor->armor_type = 1;
|
||||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
ret = krb5_auth_con_init (context, &auth_context);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_get_principal(context, state->armor_ccache, &cred.client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_make_principal(context, &cred.server,
|
||||
cred.client->realm,
|
||||
KRB5_TGS_NAME,
|
||||
cred.client->realm,
|
||||
NULL);
|
||||
if (ret) {
|
||||
krb5_free_principal(context, cred.client);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_get_credentials(context, 0, state->armor_ccache, &cred, &credp);
|
||||
krb5_free_principal(context, cred.server);
|
||||
krb5_free_principal(context, cred.client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_mk_req_extended(context,
|
||||
&auth_context,
|
||||
AP_OPTS_USE_SUBKEY,
|
||||
NULL,
|
||||
credp,
|
||||
&fxarmor->armor_value);
|
||||
krb5_free_creds(context, credp);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (state->armor_crypto)
|
||||
krb5_crypto_destroy(context, state->armor_crypto);
|
||||
krb5_free_keyblock_contents(context, &state->armor_key);
|
||||
|
||||
ret = _krb5_fast_armor_key(context,
|
||||
auth_context->local_subkey,
|
||||
auth_context->keyblock,
|
||||
&state->armor_key,
|
||||
&state->armor_crypto);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
*armor = fxarmor;
|
||||
fxarmor = NULL;
|
||||
out:
|
||||
if (fxarmor)
|
||||
free_KrbFastArmor(fxarmor);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
PA_FX_FAST_REQUEST fxreq;
|
||||
krb5_error_code ret;
|
||||
KrbFastReq fastreq;
|
||||
krb5_data data;
|
||||
size_t size;
|
||||
|
||||
memset(&fxreq, 0, sizeof(fxreq));
|
||||
memset(&fastreq, 0, sizeof(fastreq));
|
||||
krb5_data_zero(&data);
|
||||
|
||||
if (state->armor_crypto == NULL) {
|
||||
if (state->armor_ccache) {
|
||||
/*
|
||||
* Instead of keeping state in FX_COOKIE in the KDC, we
|
||||
* rebuild a new armor key for every request, because this
|
||||
* is what the MIT KDC expect and RFC6113 is vage about
|
||||
* what the behavior should be.
|
||||
*/
|
||||
state->type = choice_PA_FX_FAST_REQUEST_armored_data;
|
||||
} else {
|
||||
return check_fast(context, state);
|
||||
}
|
||||
}
|
||||
|
||||
state->flags |= KRB5_FAST_EXPECTED;
|
||||
|
||||
fastreq.fast_options.hide_client_names = 1;
|
||||
|
||||
ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
|
||||
free_KDC_REQ_BODY(&req->req_body);
|
||||
|
||||
req->req_body.realm = strdup(KRB5_ANON_REALM);
|
||||
ALLOC(req->req_body.cname, 1);
|
||||
req->req_body.cname->name_type = KRB5_NT_PRINCIPAL;
|
||||
ALLOC(req->req_body.cname->name_string.val, 2);
|
||||
req->req_body.cname->name_string.len = 2;
|
||||
req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
|
||||
req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
|
||||
|
||||
if (req->padata) {
|
||||
ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
|
||||
free_METHOD_DATA(req->padata);
|
||||
} else {
|
||||
ALLOC(req->padata, 1);
|
||||
}
|
||||
|
||||
|
||||
ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(data.length == size, "ASN.1 internal error");
|
||||
|
||||
fxreq.element = state->type;
|
||||
|
||||
if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
|
||||
size_t len;
|
||||
void *buf;
|
||||
|
||||
ret = make_fast_ap_fxarmor(context, state, &fxreq.u.armored_data.armor);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(len == size, "ASN.1 internal error");
|
||||
|
||||
ret = krb5_create_checksum(context, state->armor_crypto,
|
||||
KRB5_KU_FAST_REQ_CHKSUM, 0,
|
||||
buf, len,
|
||||
&fxreq.u.armored_data.req_checksum);
|
||||
free(buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
|
||||
KRB5_KU_FAST_ENC,
|
||||
data.data,
|
||||
data.length,
|
||||
0,
|
||||
&fxreq.u.armored_data.enc_fast_req);
|
||||
krb5_data_free(&data);
|
||||
|
||||
} else {
|
||||
krb5_data_free(&data);
|
||||
heim_assert(false, "unknown FAST type, internal error");
|
||||
}
|
||||
|
||||
ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(data.length == size, "ASN.1 internal error");
|
||||
|
||||
|
||||
ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
|
||||
if (ret)
|
||||
goto out;
|
||||
krb5_data_zero(&data);
|
||||
|
||||
out:
|
||||
free_PA_FX_FAST_REQUEST(&fxreq);
|
||||
if (fxarmor) {
|
||||
free_KrbFastArmor(fxarmor);
|
||||
free(fxarmor);
|
||||
}
|
||||
krb5_data_free(&data);
|
||||
|
||||
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.
|
||||
@@ -1650,6 +2022,7 @@ krb5_init_creds_step(krb5_context context,
|
||||
krb5_error_code ret;
|
||||
size_t len = 0;
|
||||
size_t size;
|
||||
AS_REQ req2;
|
||||
|
||||
krb5_data_zero(out);
|
||||
|
||||
@@ -1684,8 +2057,27 @@ krb5_init_creds_step(krb5_context context,
|
||||
|
||||
ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
|
||||
if (ret == 0) {
|
||||
krb5_keyblock *key = NULL;
|
||||
unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
|
||||
krb5_data data;
|
||||
|
||||
/*
|
||||
* Unwrap AS-REP
|
||||
*/
|
||||
ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
|
||||
&rep.kdc_rep.ticket, &size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(data.length == size, "ASN.1 internal error");
|
||||
|
||||
ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
|
||||
&ctx->fast_state, &rep.kdc_rep);
|
||||
krb5_data_free(&data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Now check and extract the ticket
|
||||
*/
|
||||
|
||||
if (ctx->flags.canonicalize) {
|
||||
eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
|
||||
@@ -1695,7 +2087,8 @@ krb5_init_creds_step(krb5_context context,
|
||||
eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
|
||||
|
||||
ret = process_pa_data_to_key(context, ctx, &ctx->cred,
|
||||
&ctx->as_req, &rep.kdc_rep, hostinfo, &key);
|
||||
&ctx->as_req, &rep.kdc_rep,
|
||||
hostinfo, &ctx->fast_state.reply_key);
|
||||
if (ret) {
|
||||
free_AS_REP(&rep.kdc_rep);
|
||||
goto out;
|
||||
@@ -1706,21 +2099,22 @@ krb5_init_creds_step(krb5_context context,
|
||||
ret = _krb5_extract_ticket(context,
|
||||
&rep,
|
||||
&ctx->cred,
|
||||
key,
|
||||
ctx->fast_state.reply_key,
|
||||
NULL,
|
||||
KRB5_KU_AS_REP_ENC_PART,
|
||||
NULL,
|
||||
ctx->nonce,
|
||||
eflags,
|
||||
&ctx->req_buffer,
|
||||
NULL,
|
||||
NULL);
|
||||
krb5_free_keyblock(context, key);
|
||||
|
||||
*flags = 0;
|
||||
|
||||
if (ret == 0)
|
||||
ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
|
||||
|
||||
krb5_free_keyblock(context, ctx->fast_state.reply_key);
|
||||
ctx->fast_state.reply_key = NULL;
|
||||
*flags = 0;
|
||||
|
||||
free_AS_REP(&rep.kdc_rep);
|
||||
free_EncASRepPart(&rep.enc_part);
|
||||
|
||||
@@ -1741,6 +2135,17 @@ krb5_init_creds_step(krb5_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unwrap KRB-ERROR
|
||||
*/
|
||||
ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
|
||||
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
|
||||
@@ -1821,11 +2226,23 @@ krb5_init_creds_step(krb5_context context,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Wrap with FAST
|
||||
*/
|
||||
copy_AS_REQ(&ctx->as_req, &req2);
|
||||
|
||||
ret = fast_wrap_req(context, &ctx->fast_state, &req2);
|
||||
if (ret) {
|
||||
free_AS_REQ(&req2);
|
||||
goto out;
|
||||
}
|
||||
|
||||
krb5_data_free(&ctx->req_buffer);
|
||||
|
||||
ASN1_MALLOC_ENCODE(AS_REQ,
|
||||
ctx->req_buffer.data, ctx->req_buffer.length,
|
||||
&ctx->as_req, &len, ret);
|
||||
&req2, &len, ret);
|
||||
free_AS_REQ(&req2);
|
||||
if (ret)
|
||||
goto out;
|
||||
if(len != ctx->req_buffer.length)
|
||||
@@ -1882,6 +2299,43 @@ krb5_init_creds_get_error(krb5_context context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @ingroup krb5_credential
|
||||
*/
|
||||
|
||||
krb5_error_code
|
||||
krb5_init_creds_store(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_ccache id)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
if (ctx->cred.client == NULL) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
|
||||
krb5_set_error_message(context, ret, "init creds not completed yet");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = krb5_cc_initialize(context, id, ctx->cred.client);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = krb5_cc_store_cred(context, id, &ctx->cred);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ctx->cred.flags.b.enc_pa_rep) {
|
||||
krb5_data data = { 3, rk_UNCONST("yes") };
|
||||
ret = krb5_cc_set_config(context, id, ctx->cred.server,
|
||||
"fast_avail", &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the krb5_init_creds_context allocated by krb5_init_creds_init().
|
||||
*
|
||||
@@ -2016,7 +2470,7 @@ krb5_get_init_creds_password(krb5_context context,
|
||||
ret = krb5_init_creds_get(context, ctx);
|
||||
|
||||
if (ret == 0)
|
||||
process_last_request(context, options, ctx);
|
||||
krb5_process_last_request(context, options, ctx);
|
||||
|
||||
|
||||
if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
|
||||
@@ -2091,7 +2545,7 @@ krb5_get_init_creds_keyblock(krb5_context context,
|
||||
ret = krb5_init_creds_get(context, ctx);
|
||||
|
||||
if (ret == 0)
|
||||
process_last_request(context, options, ctx);
|
||||
krb5_process_last_request(context, options, ctx);
|
||||
|
||||
out:
|
||||
if (ret == 0)
|
||||
@@ -2137,7 +2591,7 @@ krb5_get_init_creds_keytab(krb5_context context,
|
||||
|
||||
ret = krb5_init_creds_get(context, ctx);
|
||||
if (ret == 0)
|
||||
process_last_request(context, options, ctx);
|
||||
krb5_process_last_request(context, options, ctx);
|
||||
|
||||
out:
|
||||
if (ret == 0)
|
||||
|
@@ -274,14 +274,28 @@ typedef enum krb5_key_usage {
|
||||
/* Encryption type of the kdc session contribution in pk-init */
|
||||
KRB5_KU_AS_REQ = 56,
|
||||
/* Checksum of over the AS-REQ send by the KDC in PA-REQ-ENC-PA-REP */
|
||||
KRB5_KU_FAST_REQ_CHKSUM = 50,
|
||||
/* FAST armor checksum */
|
||||
KRB5_KU_FAST_ENC = 51,
|
||||
/* FAST armor encryption */
|
||||
KRB5_KU_FAST_REP = 52,
|
||||
/* FAST armor reply */
|
||||
KRB5_KU_FAST_FINISHED = 53,
|
||||
/* FAST finished checksum */
|
||||
KRB5_KU_ENC_CHALLENGE_CLIENT = 54,
|
||||
/* fast challange from client */
|
||||
KRB5_KU_ENC_CHALLENGE_KDC = 55,
|
||||
/* fast challange from kdc */
|
||||
KRB5_KU_DIGEST_ENCRYPT = -18,
|
||||
/* Encryption key usage used in the digest encryption field */
|
||||
KRB5_KU_DIGEST_OPAQUE = -19,
|
||||
/* Checksum key usage used in the digest opaque field */
|
||||
KRB5_KU_KRB5SIGNEDPATH = -21,
|
||||
/* Checksum key usage on KRB5SignedPath */
|
||||
KRB5_KU_CANONICALIZED_NAMES = -23
|
||||
KRB5_KU_CANONICALIZED_NAMES = -23,
|
||||
/* Checksum key usage on PA-CANONICALIZED */
|
||||
KRB5_KU_H5L_COOKIE = -25
|
||||
/* encrypted foo */
|
||||
} krb5_key_usage;
|
||||
|
||||
typedef krb5_key_usage krb5_keyusage;
|
||||
@@ -671,6 +685,8 @@ typedef EncAPRepPart krb5_ap_rep_enc_part;
|
||||
#define KRB5_TGS_NAME ("krbtgt")
|
||||
#define KRB5_WELLKNOWN_NAME ("WELLKNOWN")
|
||||
#define KRB5_ANON_NAME ("ANONYMOUS")
|
||||
#define KRB5_ANON_REALM ("WELLKNOWN:ANONYMOUS")
|
||||
#define KRB5_WELLKNOWN_ORG_H5L_REALM ("WELLKNOWN:ORG.H5L")
|
||||
#define KRB5_DIGEST_NAME ("digest")
|
||||
|
||||
typedef enum {
|
||||
|
@@ -592,7 +592,9 @@ check_client_referral(krb5_context context,
|
||||
return 0;
|
||||
|
||||
noreferral:
|
||||
if (krb5_principal_compare(context, requested, mapped) == FALSE) {
|
||||
if (krb5_principal_compare(context, requested, mapped) == FALSE &&
|
||||
!rep->enc_part.flags.enc_pa_rep)
|
||||
{
|
||||
krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
|
||||
N_("Not same client principal returned "
|
||||
"as requested", ""));
|
||||
@@ -656,6 +658,7 @@ _krb5_extract_ticket(krb5_context context,
|
||||
krb5_addresses *addrs,
|
||||
unsigned nonce,
|
||||
unsigned flags,
|
||||
krb5_data *request,
|
||||
krb5_decrypt_proc decrypt_proc,
|
||||
krb5_const_pointer decryptarg)
|
||||
{
|
||||
@@ -674,6 +677,48 @@ _krb5_extract_ticket(krb5_context context,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (rep->enc_part.flags.enc_pa_rep && request) {
|
||||
krb5_crypto crypto = NULL;
|
||||
Checksum cksum;
|
||||
PA_DATA *pa = NULL;
|
||||
int idx = 0;
|
||||
|
||||
_krb5_debug(context, 5, "processing enc-ap-rep");
|
||||
|
||||
if (rep->enc_part.encrypted_pa_data == NULL ||
|
||||
(pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
|
||||
rep->enc_part.encrypted_pa_data->len,
|
||||
KRB5_PADATA_REQ_ENC_PA_REP,
|
||||
&idx)) == NULL)
|
||||
{
|
||||
_krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
|
||||
ret = KRB5KRB_AP_ERR_MODIFIED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_crypto_init(context, key, 0, &crypto);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = decode_Checksum(pa->padata_value.data,
|
||||
pa->padata_value.length,
|
||||
&cksum, NULL);
|
||||
if (ret) {
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_verify_checksum(context, crypto,
|
||||
KRB5_KU_AS_REQ,
|
||||
request->data, request->length,
|
||||
&cksum);
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
free_Checksum(&cksum);
|
||||
_krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* save session key */
|
||||
|
||||
creds->session.keyvalue.length = 0;
|
||||
@@ -688,10 +733,10 @@ _krb5_extract_ticket(krb5_context context,
|
||||
}
|
||||
|
||||
/* compare client and save */
|
||||
ret = _krb5_principalname2krb5_principal (context,
|
||||
&tmp_principal,
|
||||
rep->kdc_rep.cname,
|
||||
rep->kdc_rep.crealm);
|
||||
ret = _krb5_principalname2krb5_principal(context,
|
||||
&tmp_principal,
|
||||
rep->kdc_rep.cname,
|
||||
rep->kdc_rep.crealm);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@@ -766,11 +766,25 @@ HEIMDAL_KRB5_2.0 {
|
||||
# kinit helper
|
||||
krb5_get_init_creds_opt_set_pkinit_user_certs;
|
||||
krb5_pk_enterprise_cert;
|
||||
krb5_process_last_request;
|
||||
krb5_init_creds_init;
|
||||
krb5_init_creds_set_service;
|
||||
krb5_init_creds_set_fast_ccache;
|
||||
krb5_init_creds_set_keytab;
|
||||
krb5_init_creds_get;
|
||||
krb5_init_creds_set_password;
|
||||
krb5_init_creds_store;
|
||||
krb5_init_creds_free;
|
||||
|
||||
# testing
|
||||
_krb5_aes_cts_encrypt;
|
||||
_krb5_n_fold;
|
||||
_krb5_expand_default_cc_name;
|
||||
|
||||
# FAST
|
||||
_krb5_fast_cf2;
|
||||
_krb5_fast_armor_key;
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
Reference in New Issue
Block a user