aboutsummaryrefslogtreecommitdiff
path: root/lib/krb5/init_creds_pw.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/krb5/init_creds_pw.c')
-rw-r--r--lib/krb5/init_creds_pw.c875
1 files changed, 789 insertions, 86 deletions
diff --git a/lib/krb5/init_creds_pw.c b/lib/krb5/init_creds_pw.c
index 37f4147c372a..1eece1760daa 100644
--- a/lib/krb5/init_creds_pw.c
+++ b/lib/krb5/init_creds_pw.c
@@ -34,6 +34,9 @@
*/
#include "krb5_locl.h"
+#ifndef WIN32
+#include <heim-ipc.h>
+#endif /* WIN32 */
typedef struct krb5_get_init_creds_ctx {
KDCOptions flags;
@@ -61,6 +64,10 @@ typedef struct krb5_get_init_creds_ctx {
krb5_pk_init_ctx pk_init_ctx;
int ic_flags;
+ struct {
+ unsigned change_password:1;
+ } runflags;
+
int used_pa_types;
#define USED_PKINIT 1
#define USED_PKINIT_W2K 2
@@ -76,7 +83,25 @@ 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 /* in exchange with KDC, fast was discovered */
+#define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
+#define KRB5_FAST_DISABLED 128
+#define KRB5_FAST_AP_ARMOR_SERVICE 256
+ krb5_keyblock *reply_key;
+ krb5_ccache armor_ccache;
+ krb5_principal armor_service;
+ krb5_crypto armor_crypto;
+ krb5_keyblock armor_key;
+ krb5_keyblock *strengthen_key;
+ } fast_state;
} krb5_get_init_creds_ctx;
@@ -140,6 +165,19 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
memset(ctx->password, 0, strlen(ctx->password));
free(ctx->password);
}
+ /*
+ * 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
+ * take care of cleaning up the armor_ccache).
+ */
+ if (ctx->fast_state.armor_service)
+ krb5_free_principal(context, ctx->fast_state.armor_service);
+ if (ctx->fast_state.armor_crypto)
+ 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_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);
@@ -194,13 +232,11 @@ init_cred (krb5_context context,
memset (cred, 0, sizeof(*cred));
if (client)
- krb5_copy_principal(context, client, &cred->client);
- else {
- ret = krb5_get_default_principal (context,
- &cred->client);
- if (ret)
- goto out;
- }
+ ret = krb5_copy_principal(context, client, &cred->client);
+ else
+ ret = krb5_get_default_principal(context, &cred->client);
+ if (ret)
+ goto out;
if (start_time)
cred->times.starttime = now + start_time;
@@ -208,12 +244,15 @@ init_cred (krb5_context context,
if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
tmp = options->tkt_life;
else
- tmp = 10 * 60 * 60;
+ tmp = KRB5_TKT_LIFETIME_DEFAULT;
cred->times.endtime = now + tmp;
- if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
- options->renew_life > 0) {
- cred->times.renew_till = now + options->renew_life;
+ if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
+ if (options->renew_life > 0)
+ tmp = options->renew_life;
+ else
+ tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
+ cred->times.renew_till = now + tmp;
}
return 0;
@@ -251,10 +290,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;
@@ -273,12 +312,9 @@ process_last_request(krb5_context context,
if (options && options->opt_private && options->opt_private->lr.func) {
krb5_last_req_entry **lre;
- lre = calloc(lr->len + 1, sizeof(**lre));
- if (lre == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ lre = calloc(lr->len + 1, sizeof(*lre));
+ if (lre == NULL)
+ return krb5_enomem(context);
for (i = 0; i < lr->len; i++) {
lre[i] = calloc(1, sizeof(*lre[i]));
if (lre[i] == NULL)
@@ -311,7 +347,7 @@ process_last_request(krb5_context context,
for (i = 0; i < lr->len; ++i) {
if (lr->val[i].lr_value <= t) {
- switch (abs(lr->val[i].lr_type)) {
+ switch (lr->val[i].lr_type) {
case LR_PW_EXPTIME :
report_expiration(context, ctx->prompter,
ctx->prompter_data,
@@ -326,6 +362,8 @@ process_last_request(krb5_context context,
lr->val[i].lr_value);
reported = TRUE;
break;
+ default:
+ break;
}
}
}
@@ -440,8 +478,7 @@ get_init_creds_common(krb5_context context,
etypes = malloc((options->etype_list_length + 1)
* sizeof(krb5_enctype));
if (etypes == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
memcpy (etypes, options->etype_list,
@@ -453,8 +490,7 @@ get_init_creds_common(krb5_context context,
pre_auth_types = malloc((options->preauth_list_length + 1)
* sizeof(krb5_preauthtype));
if (pre_auth_types == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
memcpy (pre_auth_types, options->preauth_list,
@@ -494,6 +530,8 @@ change_password (krb5_context context,
char *p;
krb5_get_init_creds_opt *options;
+ heim_assert(prompter != NULL, "unexpected NULL prompter");
+
memset (&cpw_cred, 0, sizeof(cpw_cred));
ret = krb5_get_init_creds_opt_alloc(context, &options);
@@ -502,10 +540,15 @@ change_password (krb5_context context,
krb5_get_init_creds_opt_set_tkt_life (options, 60);
krb5_get_init_creds_opt_set_forwardable (options, FALSE);
krb5_get_init_creds_opt_set_proxiable (options, FALSE);
- if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
- krb5_get_init_creds_opt_set_preauth_list (options,
- old_options->preauth_list,
- old_options->preauth_list_length);
+ if (old_options &&
+ (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
+ krb5_get_init_creds_opt_set_preauth_list(options,
+ old_options->preauth_list,
+ old_options->preauth_list_length);
+ if (old_options &&
+ (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
+ krb5_get_init_creds_opt_set_change_password_prompt(options,
+ old_options->change_password_prompt);
krb5_data_zero (&result_code_string);
krb5_data_zero (&result_string);
@@ -626,14 +669,12 @@ init_as_req (krb5_context context,
a->req_body.kdc_options = opts;
a->req_body.cname = malloc(sizeof(*a->req_body.cname));
if (a->req_body.cname == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
a->req_body.sname = malloc(sizeof(*a->req_body.sname));
if (a->req_body.sname == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
@@ -651,21 +692,23 @@ init_as_req (krb5_context context,
if(creds->times.starttime) {
a->req_body.from = malloc(sizeof(*a->req_body.from));
if (a->req_body.from == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
*a->req_body.from = creds->times.starttime;
}
if(creds->times.endtime){
- ALLOC(a->req_body.till, 1);
- *a->req_body.till = creds->times.endtime;
+ if ((ALLOC(a->req_body.till, 1)) != NULL)
+ *a->req_body.till = creds->times.endtime;
+ else {
+ ret = krb5_enomem(context);
+ goto fail;
+ }
}
if(creds->times.renew_till){
a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
if (a->req_body.rtime == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
*a->req_body.rtime = creds->times.renew_till;
@@ -688,8 +731,7 @@ init_as_req (krb5_context context,
} else {
a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
if (a->req_body.addresses == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
@@ -859,7 +901,7 @@ pa_pw_or_afs3_salt(krb5_context context,
heim_octet_string *data)
{
krb5_error_code ret;
- if (paid->etype == ENCTYPE_NULL)
+ if (paid->etype == KRB5_ENCTYPE_NULL)
return NULL;
ret = set_paid(paid, context,
paid->etype,
@@ -1002,7 +1044,7 @@ add_enc_ts_padata(krb5_context context,
if (!enctypes) {
enctypes = context->etypes;
netypes = 0;
- for (ep = enctypes; *ep != ETYPE_NULL; ep++)
+ for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
netypes++;
}
@@ -1156,10 +1198,9 @@ process_pa_data_to_md(krb5_context context,
krb5_error_code ret;
ALLOC(*out_md, 1);
- if (*out_md == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*out_md == NULL)
+ return krb5_enomem(context);
+
(*out_md)->len = 0;
(*out_md)->val = NULL;
@@ -1203,8 +1244,10 @@ process_pa_data_to_md(krb5_context context,
unsigned flag;
paid = calloc(1, sizeof(*paid));
+ if (paid == NULL)
+ return krb5_enomem(context);
- paid->etype = ENCTYPE_NULL;
+ paid->etype = KRB5_ENCTYPE_NULL;
ppaid = process_pa_info(context, creds->client, a, paid, in_md);
if (ppaid)
@@ -1215,6 +1258,7 @@ process_pa_data_to_md(krb5_context context,
if (ctx->used_pa_types & flag) {
if (ppaid)
free_paid(context, ppaid);
+ free(paid);
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
"Already tried ENC-TS-%s, looping",
flag == USED_ENC_TS_INFO ? "info" : "guess");
@@ -1237,6 +1281,12 @@ process_pa_data_to_md(krb5_context context,
pa_data_add_pac_request(context, ctx, *out_md);
+ if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
+ 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;
@@ -1357,10 +1407,8 @@ krb5_init_creds_init(krb5_context context,
*rctx = NULL;
ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ctx == NULL)
+ return krb5_enomem(context);
ret = get_init_creds_common(context, client, start_time, options, ctx);
if (ret) {
@@ -1458,10 +1506,8 @@ krb5_init_creds_set_password(krb5_context context,
}
if (password) {
ctx->password = strdup(password);
- if (ctx->password == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ctx->password == NULL)
+ return krb5_enomem(context);
ctx->keyseed = (void *) ctx->password;
} else {
ctx->keyseed = NULL;
@@ -1526,14 +1572,11 @@ krb5_init_creds_set_keytab(krb5_context context,
krb5_enctype *etypes = NULL;
krb5_error_code ret;
size_t netypes = 0;
- int kvno = 0;
+ int kvno = 0, found = 0;
a = malloc(sizeof(*a));
- if (a == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (a == NULL)
+ return krb5_enomem(context);
a->principal = ctx->cred.client;
a->keytab = keytab;
@@ -1559,6 +1602,8 @@ krb5_init_creds_set_keytab(krb5_context context,
if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
goto next;
+ found = 1;
+
/* check if we ahve this kvno already */
if (entry.vno > kvno) {
/* remove old list of etype */
@@ -1576,8 +1621,11 @@ krb5_init_creds_set_keytab(krb5_context context,
/* add enctype to supported list */
ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
- if (ptr == NULL)
- goto next;
+ if (ptr == NULL) {
+ free(etypes);
+ ret = krb5_enomem(context);
+ goto out;
+ }
etypes = ptr;
etypes[netypes] = entry.keyblock.keytype;
@@ -1595,7 +1643,13 @@ krb5_init_creds_set_keytab(krb5_context context,
}
out:
- return 0;
+ if (!found) {
+ if (ret == 0)
+ ret = KRB5_KT_NOTFOUND;
+ _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
+ }
+
+ return ret;
}
static krb5_error_code KRB5_CALLCONV
@@ -1618,6 +1672,483 @@ 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;
+ ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_const_principal armor_service)
+{
+ krb5_error_code ret;
+
+ if (ctx->fast_state.armor_service)
+ krb5_free_principal(context, ctx->fast_state.armor_service);
+ if (armor_service) {
+ ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
+ if (ret)
+ return ret;
+ } else {
+ ctx->fast_state.armor_service = NULL;
+ }
+ ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
+ 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) {
+ if (state->strengthen_key)
+ krb5_free_keyblock(context, state->strengthen_key);
+
+ ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
+ if (ret)
+ goto out;
+ }
+
+ 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;
+}
+
+krb5_error_code
+_krb5_make_fast_ap_fxarmor(krb5_context context,
+ krb5_ccache armor_ccache,
+ krb5_data *armor_value,
+ krb5_keyblock *armor_key,
+ krb5_crypto *armor_crypto)
+{
+ krb5_auth_context auth_context = NULL;
+ krb5_creds cred, *credp = NULL;
+ krb5_error_code ret;
+ krb5_data empty;
+
+ krb5_data_zero(&empty);
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_auth_con_init (context, &auth_context);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_get_principal(context, 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, armor_ccache, &cred, &credp);
+ krb5_free_principal(context, cred.server);
+ krb5_free_principal(context, cred.client);
+ if (ret)
+ goto out;
+
+ ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
+ if (ret)
+ goto out;
+
+ ret = krb5_mk_req_extended(context,
+ &auth_context,
+ AP_OPTS_USE_SUBKEY,
+ NULL,
+ credp,
+ armor_value);
+ krb5_free_creds(context, credp);
+ if (ret)
+ goto out;
+
+ ret = _krb5_fast_armor_key(context,
+ auth_context->local_subkey,
+ auth_context->keyblock,
+ armor_key,
+ armor_crypto);
+ if (ret)
+ goto out;
+
+ out:
+ krb5_auth_con_free(context, auth_context);
+ return ret;
+}
+
+#ifndef WIN32
+static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
+static heim_ipc armor_service = NULL;
+
+static void
+fast_armor_init_ipc(void *ctx)
+{
+ heim_ipc *ipc = ctx;
+ heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
+}
+#endif /* WIN32 */
+
+
+static krb5_error_code
+make_fast_ap_fxarmor(krb5_context context,
+ struct fast_state *state,
+ const char *realm,
+ KrbFastArmor **armor)
+{
+ KrbFastArmor *fxarmor = NULL;
+ krb5_error_code ret;
+
+ if (state->armor_crypto)
+ krb5_crypto_destroy(context, state->armor_crypto);
+ krb5_free_keyblock_contents(context, &state->armor_key);
+
+
+ ALLOC(fxarmor, 1);
+ if (fxarmor == NULL)
+ return krb5_enomem(context);
+
+ if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
+#ifdef WIN32
+ krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
+ ret = ENOTSUP;
+ goto out;
+#else /* WIN32 */
+ KERB_ARMOR_SERVICE_REPLY msg;
+ krb5_data request, reply;
+
+ heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
+ if (armor_service == NULL) {
+ krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
+ ret = ENOENT;
+ goto out;
+ }
+
+ krb5_data_zero(&reply);
+
+ request.data = rk_UNCONST(realm);
+ request.length = strlen(realm);
+
+ ret = heim_ipc_call(armor_service, &request, &reply, NULL);
+ heim_release(send);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to get armor service credential");
+ goto out;
+ }
+
+ ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
+ krb5_data_free(&reply);
+ if (ret)
+ goto out;
+
+ ret = copy_KrbFastArmor(fxarmor, &msg.armor);
+ if (ret) {
+ free_KERB_ARMOR_SERVICE_REPLY(&msg);
+ goto out;
+ }
+
+ ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
+ free_KERB_ARMOR_SERVICE_REPLY(&msg);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
+ if (ret)
+ goto out;
+#endif /* WIN32 */
+ } else {
+
+ fxarmor->armor_type = 1;
+
+ ret = _krb5_make_fast_ap_fxarmor(context,
+ state->armor_ccache,
+ &fxarmor->armor_value,
+ &state->armor_key,
+ &state->armor_crypto);
+ if (ret)
+ goto out;
+ }
+
+
+ *armor = fxarmor;
+ fxarmor = NULL;
+ out:
+ if (fxarmor) {
+ free_KrbFastArmor(fxarmor);
+ free(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;
+
+ if (state->flags & KRB5_FAST_DISABLED) {
+ _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
+ return 0;
+ }
+
+ 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);
+ if ((ALLOC(req->req_body.cname, 1)) != NULL) {
+ req->req_body.cname->name_type = KRB5_NT_WELLKNOWN;
+ if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) {
+ 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->req_body.cname->name_string.val[0] == NULL ||
+ req->req_body.cname->name_string.val[1] == NULL)
+ ret = krb5_enomem(context);
+ } else
+ ret = krb5_enomem(context);
+ } else
+ ret = krb5_enomem(context);
+ if ((ALLOC(req->req_body.till, 1)) != NULL)
+ *req->req_body.till = 0;
+ else
+ ret = krb5_enomem(context);
+ if (ret)
+ goto out;
+
+ if (req->padata) {
+ ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
+ free_METHOD_DATA(req->padata);
+ } else {
+ if ((ALLOC(req->padata, 1)) == NULL)
+ ret = krb5_enomem(context);
+ }
+ if (ret)
+ goto out;
+
+ 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, fastreq.req_body.realm, &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);
+ if (ret)
+ goto out;
+
+ } 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);
+ free_KrbFastReq(&fastreq);
+ 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 +2181,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 +2216,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 +2246,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 +2258,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 +2294,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);
@@ -1797,14 +2361,79 @@ krb5_init_creds_step(krb5_context context,
ret = krb5_principal_set_realm(context,
ctx->cred.client,
*ctx->error.crealm);
+ if (ret)
+ goto out;
+
+ if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
+ ret = krb5_init_creds_set_service(context, ctx, NULL);
+ if (ret)
+ goto out;
+ }
+
+ free_AS_REQ(&ctx->as_req);
+ memset(&ctx->as_req, 0, sizeof(ctx->as_req));
ctx->used_pa_types = 0;
+ } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) {
+ char buf2[1024];
+
+ ctx->runflags.change_password = 1;
+
+ ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
+
+
+ /* try to avoid recursion */
+ if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
+ goto out;
+
+ /* don't try to change password where then where none */
+ if (ctx->prompter == NULL)
+ goto out;
+
+ ret = change_password(context,
+ ctx->cred.client,
+ ctx->password,
+ buf2,
+ sizeof(buf2),
+ ctx->prompter,
+ ctx->prompter_data,
+ NULL);
+ if (ret)
+ goto out;
+
+ krb5_init_creds_set_password(context, ctx, buf2);
+
+ ctx->used_pa_types = 0;
+ ret = 0;
+
+ } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
+
+ if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
+ goto out;
+ if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
+ goto out;
+
+ _krb5_debug(context, 10, "preauth failed with FAST, "
+ "and told by KD or user, trying w/o FAST");
+
+ ctx->fast_state.flags |= KRB5_FAST_DISABLED;
+ ctx->used_pa_types = 0;
+ ret = 0;
}
if (ret)
goto out;
}
}
+ 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);
+ return ret;
+ }
+ }
+
if (ctx->as_req.padata) {
free_METHOD_DATA(ctx->as_req.padata);
free(ctx->as_req.padata);
@@ -1821,11 +2450,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)
@@ -1877,7 +2518,44 @@ krb5_init_creds_get_error(krb5_context context,
ret = copy_KRB_ERROR(&ctx->error, error);
if (ret)
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ krb5_enomem(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;
}
@@ -1968,7 +2646,7 @@ krb5_get_init_creds_password(krb5_context context,
krb5_get_init_creds_opt *options)
{
krb5_init_creds_context ctx;
- char buf[BUFSIZ];
+ char buf[BUFSIZ], buf2[BUFSIZ];
krb5_error_code ret;
int chpw = 0;
@@ -1984,11 +2662,19 @@ krb5_get_init_creds_password(krb5_context context,
if (prompter != NULL && ctx->password == NULL && password == NULL) {
krb5_prompt prompt;
krb5_data password_data;
- char *p, *q;
+ char *p, *q = NULL;
+ int aret;
- krb5_unparse_name (context, client, &p);
- asprintf (&q, "%s's Password: ", p);
+ ret = krb5_unparse_name(context, client, &p);
+ if (ret)
+ goto out;
+
+ aret = asprintf(&q, "%s's Password: ", p);
free (p);
+ if (aret == -1 || q == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
prompt.prompt = q;
password_data.data = buf;
password_data.length = sizeof(buf);
@@ -2016,12 +2702,10 @@ 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) {
- char buf2[1024];
-
/* try to avoid recursion */
if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
goto out;
@@ -2030,16 +2714,21 @@ krb5_get_init_creds_password(krb5_context context,
if (prompter == NULL)
goto out;
+ if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
+ !options->change_password_prompt)
+ goto out;
+
ret = change_password (context,
client,
ctx->password,
buf2,
- sizeof(buf),
+ sizeof(buf2),
prompter,
data,
options);
if (ret)
goto out;
+ password = buf2;
chpw = 1;
krb5_init_creds_free(context, ctx);
goto again;
@@ -2053,6 +2742,7 @@ krb5_get_init_creds_password(krb5_context context,
krb5_init_creds_free(context, ctx);
memset(buf, 0, sizeof(buf));
+ memset(buf2, 0, sizeof(buf2));
return ret;
}
@@ -2091,7 +2781,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)
@@ -2119,10 +2809,22 @@ krb5_get_init_creds_keytab(krb5_context context,
krb5_get_init_creds_opt *options)
{
krb5_init_creds_context ctx;
+ krb5_keytab_entry ktent;
krb5_error_code ret;
+ memset(&ktent, 0, sizeof(ktent));
memset(creds, 0, sizeof(*creds));
+ if (strcmp(client->realm, "") == 0) {
+ /*
+ * Referral realm. We have a keytab, so pick a realm by
+ * matching in the keytab.
+ */
+ ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
+ if (ret == 0)
+ client = ktent.principal;
+ }
+
ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
if (ret)
goto out;
@@ -2137,9 +2839,10 @@ 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:
+ krb5_kt_free_entry(context, &ktent);
if (ret == 0)
krb5_init_creds_get_creds(context, ctx, creds);