goto cleanup;
}
}
+ code = krb5_get_init_creds_opt_set_out_ccache(k5->ctx, options, k5->cc);
+ if (code)
+ goto cleanup;
switch (opts->action) {
case INIT_PW:
goto cleanup;
}
- code = krb5_cc_initialize(k5->ctx, k5->cc,
- opts->canonicalize ? my_creds.client : k5->me);
- if (code) {
- com_err(progname, code, "when initializing cache %s",
- opts->k5_cache_name?opts->k5_cache_name:"");
- goto cleanup;
- }
+ if ((opts->action != INIT_PW) && (opts->action != INIT_KT)) {
+ code = krb5_cc_initialize(k5->ctx, k5->cc, opts->canonicalize ?
+ my_creds.client : k5->me);
+ if (code) {
+ com_err(progname, code, "when initializing cache %s",
+ opts->k5_cache_name?opts->k5_cache_name:"");
+ goto cleanup;
+ }
- code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds);
- if (code) {
- com_err(progname, code, "while storing credentials");
- goto cleanup;
+ code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds);
+ if (code) {
+ com_err(progname, code, "while storing credentials");
+ goto cleanup;
+ }
}
-
notix = 0;
cleanup:
if (show_keys) {
printf(" (0x");
{
- int i;
+ unsigned int i;
for (i = 0; i < entry.key.length; i++)
printf("%02x", entry.key.contents[i]);
}
exit(1);
}
while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
+ if (krb5_is_config_principal(kcontext, creds.server))
+ continue;
if (status_only) {
if (exit_status && creds.server->length == 2 &&
strcmp(creds.server->realm.data, princ->realm.data) == 0 &&
#define KRB5_CONF_V4_INSTANCE_CONVERT "v4_instance_convert"
#define KRB5_CONF_V4_REALM "v4_realm"
#define KRB5_CONF_ASTERISK "*"
+#define KRB5_CONF_FAST_AVAIL "fast_avail"
/* Error codes used in KRB_ERROR protocol messages.
Return values of library routines are based on a different error table
int num_preauth_data;
krb5_gic_opt_pa_data *preauth_data;
char * fast_ccache_name;
+ krb5_ccache out_ccache;
+ krb5_flags fast_flags;
} krb5_gic_opt_private;
/*
krb5_error_code
encode_krb5_cred(const krb5_cred *rep, krb5_data **code);
+krb5_error_code
+encode_krb5_checksum(const krb5_checksum *, krb5_data **);
krb5_error_code
encode_krb5_enc_cred_part(const krb5_cred_enc_part *rep, krb5_data **code);
krb5_error_code
decode_krb5_enc_priv_part(const krb5_data *output, krb5_priv_enc_part **rep);
+krb5_error_code
+decode_krb5_checksum(const krb5_data *, krb5_checksum **);
krb5_error_code
decode_krb5_cred(const krb5_data *output, krb5_cred **rep);
#define KRB5_KEYUSAGE_FAST_FINISHED 53
#define KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT 54
#define KRB5_KEYUSAGE_ENC_CHALLENGE_KDC 55
+#define KRB5_KEYUSAGE_AS_REQ 56
-#define KRB5_KEYUSAGE_FAST_REP 52
krb5_boolean KRB5_CALLCONV krb5_c_valid_enctype(krb5_enctype ktype);
krb5_boolean KRB5_CALLCONV krb5_c_valid_cksumtype(krb5_cksumtype ctype);
krb5_boolean KRB5_CALLCONV krb5_c_is_coll_proof_cksum(krb5_cksumtype ctype);
#define TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000
#define TKT_FLG_OK_AS_DELEGATE 0x00040000
#define TKT_FLG_ANONYMOUS 0x00020000
-/* #define TKT_FLG_RESERVED 0x00010000 */
+#define TKT_FLG_ENC_PA_REP 0x00010000
/* #define TKT_FLG_RESERVED 0x00008000 */
/* #define TKT_FLG_RESERVED 0x00004000 */
/* #define TKT_FLG_RESERVED 0x00002000 */
#define KRB5_PADATA_FX_FAST 136
#define KRB5_PADATA_FX_ERROR 137
#define KRB5_PADATA_ENCRYPTED_CHALLENGE 138
+#define KRB5_ENCPADATA_REQ_ENC_PA_REP 149
#define KRB5_SAM_USE_SAD_AS_KEY 0x80000000
#define KRB5_SAM_SEND_ENCRYPTED_SAD 0x40000000
krb5_error_code KRB5_CALLCONV
krb5_cc_copy_creds(krb5_context context, krb5_ccache incc, krb5_ccache outcc);
+krb5_error_code KRB5_CALLCONV
+krb5_cc_get_config(krb5_context, krb5_ccache,
+ krb5_const_principal,
+ const char *, krb5_data *);
+
+krb5_error_code KRB5_CALLCONV
+krb5_cc_set_config(krb5_context, krb5_ccache,
+ krb5_const_principal,
+ const char *, krb5_data *);
+
+krb5_boolean KRB5_CALLCONV
+krb5_is_config_principal(krb5_context,
+ krb5_const_principal);
+
/* krb5_free.c */
void KRB5_CALLCONV krb5_free_principal(krb5_context, krb5_principal );
void KRB5_CALLCONV krb5_free_authenticator(krb5_context,
krb5_get_init_creds_opt *opt, const char *attr,
const char *value);
+/**
+ * This API sets a ccache name that will contain some TGT on calls to
+ * t_init_creds functions. If set, this ccache will be used for FAST
+ * (draft-ietf-krb-wg-preauth-framework) to protect the AS-REQ from observation
+ * and active attack. If the fast_ccache_name is set, then FAST may be
+ * required by the client library. In this and future versions, FAST will be
+ * used if available; krb5_get_init_creds_opt_set_fast_flags() may be used to
+ * require that the request fail is FAST is unavailable. In MIT Kerberos 1.7
+ * setting the fast ccache at all required that FAST be present or the request
+ * would fail.
+ */
krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_fast_ccache_name(krb5_context context,
krb5_get_init_creds_opt *opt,
const char *fast_ccache_name);
-/* This API sets a ccache name that will contain some TGT on
- calls to get_init_creds functions. If set, this ccache will
- be used for FAST (draft-ietf-krb-wg-preauth-framework) to
- protect the AS-REQ from observation and active attack. If
- the fast_ccache_name is set, then FAST may be required by the
- client library. In this version FAST is required.*/
+/**
+ * Set a ccache where resulting credentials will be stored. If set, then the
+ * krb5_get_init_creds family of APIs will write out credentials to the given
+ * ccache. Setting an output ccache is desirable both because it simplifies
+ * calling code and because it permits the krb5_get_init_creds APIs to write
+ * out configuration information about the realm to the ccache.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_out_ccache(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_ccache ccache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_fast_flags(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_flags flags);
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_get_fast_flags(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_flags *out_flags);
+
+/* Fast flags*/
+#define KRB5_FAST_REQUIRED 1l<<0 /*!< Require KDC to support FAST*/
+
krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_password(krb5_context context, krb5_creds *creds,
krb5_principal client, char *password,
enc_tkt_reply.times.authtime = authtime;
setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL);
+ setflag(enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP);
/*
* It should be noted that local policy may affect the
reply.client->realm.data, reply.client->data->data);
#endif /* APPLE_PKINIT */
- errcode = return_svr_referral_data(kdc_context,
- &server, &reply_encpart);
- if (errcode) {
- status = "KDC_RETURN_ENC_PADATA";
- goto errout;
- }
errcode = handle_authdata(kdc_context,
status = "generating reply key";
goto errout;
}
+ errcode = return_enc_padata(kdc_context, req_pkt, request,
+ as_encrypting_key, &server, &reply_encpart);
+ if (errcode) {
+ status = "KDC_RETURN_ENC_PADATA";
+ goto errout;
+ }
+
errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart,
0, as_encrypting_key, &reply, response);
reply.enc_part.kvno = client_key->key_data_kvno;
*/
if (!(header_enc_tkt->times.starttime))
header_enc_tkt->times.starttime = authtime;
+ setflag(enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP);
/* don't use new addresses unless forwarded, see below */
goto cleanup;
}
- if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
- errcode = return_svr_referral_data(kdc_context,
- &server, &reply_encpart);
- if (errcode) {
- status = "KDC_RETURN_ENC_PADATA";
- goto cleanup;
- }
- }
/*
* Only add the realm of the presented tgt to the transited list if
status = "generating reply key";
goto cleanup;
}
+ if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
+ int idx = 0;
+
+ errcode = return_enc_padata(kdc_context, pkt, request,
+ reply_key, &server, &reply_encpart);
+ if (errcode) {
+ status = "KDC_RETURN_ENC_PADATA";
+ goto cleanup;
+ }
+ /* Not referral. */
+ reply_encpart.enc_padata = calloc(3, sizeof(krb5_pa_data *));
+ if (reply_encpart.enc_padata == NULL) {
+ errcode = ENOMEM;
+ status = "Allocating enc_padata";
+ goto cleanup;
+ }
+ errcode = kdc_handle_protected_negotiation(pkt, request, reply_key,
+ reply_encpart.enc_padata,
+ &idx);
+ if (errcode != 0) {
+ status = "protected negotiation";
+ goto cleanup;
+ }
+ }
+
errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart,
subkey ? 1 : 0,
reply_key,
struct kdc_request_state *state)
{
krb5_error_code retval = 0;
- krb5_pa_data *fast_padata, *cookie_padata;
+ krb5_pa_data *fast_padata, *cookie_padata = NULL;
krb5_data scratch;
krb5_fast_req * fast_req = NULL;
krb5_kdc_req *request = *requestptr;
}
krb5_error_code
-return_svr_referral_data(krb5_context context,
- krb5_db_entry *server,
- krb5_enc_kdc_rep_part *reply_encpart)
+return_enc_padata(krb5_context context, krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_keyblock *reply_key,
+ krb5_db_entry *server, krb5_enc_kdc_rep_part *reply_encpart)
{
krb5_error_code code;
krb5_tl_data tl_data;
krb5_pa_data *pa_data;
+ int idx = 0;
- /* This should be initialized and only used for Win2K compat */
+ /* This should be initialized and only used for Win2K compat and other
+ * specific standardized uses such as FAST negotiation. */
assert(reply_encpart->enc_padata == NULL);
-
+ reply_encpart->enc_padata = calloc(4, sizeof(krb5_pa_data *));
+ if (reply_encpart->enc_padata == NULL)
+ return ENOMEM;
tl_data.tl_data_type = KRB5_TL_SVR_REFERRAL_DATA;
-
code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
if (code || tl_data.tl_data_length == 0)
- return 0; /* no server referrals to return */
+ goto negotiate; /* no server referrals to return */
pa_data = (krb5_pa_data *)malloc(sizeof(*pa_data));
if (pa_data == NULL)
return ENOMEM;
-
pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SVR_REFERRAL_INFO;
pa_data->length = tl_data.tl_data_length;
}
memcpy(pa_data->contents, tl_data.tl_data_contents, tl_data.tl_data_length);
- reply_encpart->enc_padata = (krb5_pa_data **)calloc(2, sizeof(krb5_pa_data *));
- if (reply_encpart->enc_padata == NULL) {
- free(pa_data->contents);
- free(pa_data);
- return ENOMEM;
- }
- reply_encpart->enc_padata[0] = pa_data;
+ reply_encpart->enc_padata[idx++] = pa_data;
reply_encpart->enc_padata[1] = NULL;
-
- return 0;
+negotiate:
+ return kdc_handle_protected_negotiation(req_pkt, request, reply_key,
+ reply_encpart->enc_padata, &idx);
}
#if 0
*out_endtime = starttime + life;
}
+
+/**
+ * Handle protected negotiation of FAST using enc_padata
+ * - If ENCPADATA_REQ_ENC_PA_REP is present, then:
+ * - Return ENCPADATA_REQ_ENC_PA_REP with checksum of AS-REQ from client
+ * - Include PADATA_FX_FAST in the enc_padata to indicate FAST
+ * @pre @c out_enc_padata has space for at least two more padata
+ * @param index in/out index into @c out_enc_padata for next item
+ */
+krb5_error_code
+kdc_handle_protected_negotiation(krb5_data *req_pkt, krb5_kdc_req *request,
+ const krb5_keyblock *reply_key,
+ krb5_pa_data **out_enc_padata, int *idx)
+{
+ krb5_error_code retval = 0;
+ krb5_checksum checksum;
+ krb5_data *out = NULL;
+ krb5_pa_data *pa;
+ assert(out_enc_padata != NULL);
+ pa = krb5int_find_pa_data(kdc_context, request->padata,
+ KRB5_ENCPADATA_REQ_ENC_PA_REP);
+ if (pa == NULL)
+ return 0;
+ checksum.contents = NULL;
+ pa = malloc(sizeof(krb5_pa_data));
+ if (pa == NULL)
+ return ENOMEM;
+ pa->magic = KV5M_PA_DATA;
+ pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
+ retval = krb5_c_make_checksum(kdc_context,0, reply_key,
+ KRB5_KEYUSAGE_AS_REQ, req_pkt, &checksum);
+ if (retval != 0)
+ goto cleanup;
+ retval = encode_krb5_checksum(&checksum, &out);
+ if (retval != 0)
+ goto cleanup;
+ pa->contents = (krb5_octet *) out->data;
+ pa->length = out->length;
+ out_enc_padata[(*idx)++] = pa;
+ pa = NULL;
+ out->data = NULL;
+ pa = malloc(sizeof(krb5_pa_data));
+ if (pa == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ pa->magic = KV5M_PA_DATA;
+ pa->pa_type = KRB5_PADATA_FX_FAST;
+ pa->length = 0;
+ pa->contents = NULL;
+ out_enc_padata[(*idx)++] = pa;
+ pa = NULL;
+cleanup:
+ if (checksum.contents)
+ krb5_free_checksum_contents(kdc_context, &checksum);
+ if (out != NULL)
+ krb5_free_data(kdc_context, out);
+ if (pa != NULL)
+ free(pa);
+ return retval;
+}
include_pac_p(krb5_context context, krb5_kdc_req *request);
krb5_error_code
-return_svr_referral_data (krb5_context context,
- krb5_db_entry *server,
- krb5_enc_kdc_rep_part *reply_encpart);
+return_enc_padata(krb5_context context,
+ krb5_data *req_pkt, krb5_kdc_req *request,
+ krb5_keyblock *reply_key,
+ krb5_db_entry *server,
+ krb5_enc_kdc_rep_part *reply_encpart);
krb5_error_code
sign_db_authdata (krb5_context context,
krb5_error_code kdc_preauth_get_cookie(struct kdc_request_state *state,
krb5_pa_data **cookie);
-
+krb5_error_code
+kdc_handle_protected_negotiation( krb5_data *req_pkt, krb5_kdc_req *request,
+ const krb5_keyblock *reply_key,
+ krb5_pa_data **out_enc_padata, int *idx);
#include "etypes.h"
#include "dk.h"
+/* A 0 checksum type means use the mandatory checksum. */
+
krb5_error_code KRB5_CALLCONV
krb5_k_make_checksum(krb5_context context, krb5_cksumtype cksumtype,
krb5_key key, krb5_keyusage usage,
krb5_octet *trunc;
krb5_error_code ret;
+ if (cksumtype == 0) {
+ ret = krb5int_c_mandatory_cksumtype(context, key->keyblock.enctype,
+ &cksumtype);
+ if (ret != 0)
+ return ret;
+ }
ctp = find_cksumtype(cksumtype);
if (ctp == NULL)
return KRB5_BAD_ENCTYPE;
val->last_req = NULL;
val->server = NULL;
val->caddrs = NULL;
+ val->enc_padata = NULL;
{ begin_structure();
get_field(val->session,0,asn1_decode_encryption_key_ptr);
get_field(val->last_req,1,asn1_decode_last_req);
MAKE_FULL_ENCODER(encode_krb5_priv, krb5_priv);
MAKE_FULL_ENCODER(encode_krb5_enc_priv_part, priv_enc_part);
+MAKE_FULL_ENCODER(encode_krb5_checksum, checksum);
+
MAKE_FULL_ENCODER(encode_krb5_cred, krb5_cred);
MAKE_FULL_ENCODER(encode_krb5_enc_cred_part, enc_cred_part);
MAKE_FULL_ENCODER(encode_krb5_error, krb5_error);
return retval;
}
+krb5_error_code
+decode_krb5_checksum(const krb5_data *code, krb5_checksum **repptr)
+{
+ setup_buf_only(krb5_checksum *);
+ alloc_field(rep);
+ retval = asn1_decode_checksum(&buf, rep);
+ if (retval) clean_return(retval);
+ cleanup(free);
+}
+
krb5_error_code
decode_krb5_cred(const krb5_data *code, krb5_cred **repptr)
{
krb5_error_code KRB5_CALLCONV
krb5_stdccv3_remove (krb5_context context,
krb5_ccache id,
- krb5_flags flags,
+ krb5_flags whichfields,
krb5_creds *in_creds)
{
krb5_error_code err = 0;
credentials->data, &creds);
if (!err) {
- found = krb5_creds_compare (context, in_creds, &creds);
+ found = krb5int_cc_creds_match_request(context,
+ whichfields,
+ in_creds,
+ &creds);
krb5_free_cred_contents (context, &creds);
}
{
return ccache->ops->unlock(context, ccache);
}
+
+static const char conf_realm[] = "X-CACHECONF:";
+static const char conf_name[] = "krb5_ccache_conf_data";
+
+static krb5_error_code
+build_conf_principals (krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *name, krb5_creds *cred)
+{
+ krb5_principal client;
+ krb5_error_code ret;
+ char *pname = NULL;
+
+ memset(cred, 0, sizeof(*cred));
+
+ ret = krb5_cc_get_principal(context, id, &client);
+ if (ret)
+ return ret;
+
+ if (principal) {
+ ret = krb5_unparse_name(context, principal, &pname);
+ if (ret)
+ return ret;
+ }
+
+ ret = krb5_build_principal(context, &cred->server,
+ sizeof(conf_realm) - 1, conf_realm,
+ conf_name, name, pname, (char *)NULL);
+ free(pname);
+ if (ret) {
+ krb5_free_principal(context, client);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, client, &cred->client);
+ krb5_free_principal(context, client);
+ return ret;
+}
+
+/*!
+ * \param context a Keberos context
+ * \param principal principal to check if it a configuration principal
+ *
+ * \brief Return TRUE (non zero) if the principal is a configuration
+ * principal (generated part of krb5_cc_set_config()). Returns
+ * FALSE (zero) if not a configuration principal.
+ *
+ */
+
+krb5_boolean KRB5_CALLCONV
+krb5_is_config_principal (krb5_context context,
+ krb5_const_principal principal)
+{
+ const krb5_data *realm;
+
+ realm = krb5_princ_realm(context, principal);
+
+ if (realm->length != sizeof(conf_realm) - 1 ||
+ memcmp(realm->data, conf_realm, sizeof(conf_realm) - 1) != 0)
+ return FALSE;
+
+ if (principal->length == 0 ||
+ principal->data[0].length != (sizeof(conf_name) - 1) ||
+ memcmp(principal->data[0].data, conf_name, sizeof(conf_name) - 1) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*!
+ * \param context a Keberos context
+ * \param id the credential cache to store the data for
+ * \param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * \param key name under which the configuraion is stored.
+ * \param data data to store
+ *
+ * \brief Store some configuration for the credential cache in the
+ * cache. Existing configuration under the same key is
+ * over-written.
+ *
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_cc_set_config (krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *key, krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_creds cred;
+ memset(&cred, 0, sizeof(cred));
+
+ ret = build_conf_principals(context, id, principal, key, &cred);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_remove_cred(context, id, 0, &cred);
+ if (ret && ret != KRB5_CC_NOTFOUND && ret != KRB5_CC_NOSUPP)
+ goto out;
+
+ cred.ticket.data = malloc(data->length);
+ if (cred.ticket.data == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ cred.ticket.length = data->length;
+ memcpy(cred.ticket.data, data->data, data->length);
+
+ ret = krb5_cc_store_cred(context, id, &cred);
+
+out:
+ krb5_free_cred_contents(context, &cred);
+ return ret;
+}
+
+/*!
+ * \param context a Keberos context
+ * \param id the credential cache to store the data for
+ * \param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * \param key name under which the configuraion is stored.
+ * \param data data to fetched, free with krb5_data_free()
+ *
+ * \brief Get some configuration for the credential cache in the cache.
+ */
+
+
+krb5_error_code KRB5_CALLCONV
+krb5_cc_get_config (krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *key, krb5_data *data)
+{
+ krb5_creds mcred, cred;
+ krb5_error_code ret;
+
+ memset(&cred, 0, sizeof(cred));
+ memset(data, 0, sizeof(*data));
+
+ ret = build_conf_principals(context, id, principal, key, &mcred);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
+ if (ret)
+ goto out;
+
+ data->data = malloc(cred.ticket.length);
+ if (data->data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ goto out;
+ }
+ data->length = cred.ticket.length;
+ memcpy(data->data, cred.ticket.data, data->length);
+
+out:
+ krb5_free_cred_contents(context, &cred);
+ krb5_free_cred_contents(context, &mcred);
+ return ret;
+}
static krb5_error_code
fast_armor_ap_request(krb5_context context,
struct krb5int_fast_request_state *state,
- krb5_ccache ccache, krb5_data *target_realm)
+ krb5_ccache ccache, krb5_principal target_principal)
{
krb5_error_code retval = 0;
krb5_creds creds, *out_creds = NULL;
krb5_keyblock *subkey = NULL, *armor_key = NULL;
encoded_authenticator.data = NULL;
memset(&creds, 0, sizeof(creds));
- retval = krb5_tgtname(context, target_realm, target_realm, &creds.server);
- if (retval ==0)
- retval = krb5_cc_get_principal(context, ccache, &creds.client);
+ creds.server = target_principal;
+ retval = krb5_cc_get_principal(context, ccache, &creds.client);
if (retval == 0)
retval = krb5_get_credentials(context, 0, ccache, &creds, &out_creds);
if (retval == 0)
krb5_free_keyblock(context, subkey);
if (out_creds)
krb5_free_creds(context, out_creds);
+ /* target_principal is owned by caller. */
+ creds.server = NULL;
krb5_free_cred_contents(context, &creds);
if (encoded_authenticator.data)
krb5_free_data_contents(context, &encoded_authenticator);
{
krb5_error_code retval = 0;
krb5_ccache ccache = NULL;
+ krb5_principal target_principal = NULL;
+ krb5_data *target_realm;
krb5_clear_error_message(context);
+ target_realm = krb5_princ_realm(context, request->server);
if (opte->opt_private->fast_ccache_name) {
+ state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
&ccache);
- if (retval==0)
+ if (retval == 0) {
+ retval = krb5_tgtname(context, target_realm, target_realm,
+ &target_principal);
+ }
+ if (retval == 0) {
+ krb5_data config_data;
+ config_data.data = NULL;
+ retval = krb5_cc_get_config(context, ccache, target_principal,
+ KRB5_CONF_FAST_AVAIL, &config_data);
+ if ((retval == 0) && config_data.data )
+ state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+ krb5_free_data_contents(context, &config_data);
+ retval = 0;
+ }
+ if (opte->opt_private->fast_flags& KRB5_FAST_REQUIRED)
+ state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+ if (retval == 0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST)) {
retval = fast_armor_ap_request(context, state, ccache,
- krb5_princ_realm(context, request->server));
+ target_principal);
+ }
if (retval != 0) {
const char * errmsg;
errmsg = krb5_get_error_message(context, retval);
}
if (ccache)
krb5_cc_close(context, ccache);
+ if (target_principal)
+ krb5_free_principal(context, target_principal);
return retval;
}
return *tmppa;
}
+
+
+krb5_error_code
+krb5int_fast_verify_nego(krb5_context context,
+ struct krb5int_fast_request_state *state,
+ krb5_kdc_rep *rep, krb5_data *request,
+ krb5_keyblock *decrypting_key,
+ krb5_boolean *fast_avail)
+{
+ krb5_error_code retval = 0;
+ krb5_checksum *checksum = NULL;
+ krb5_pa_data *pa;
+ krb5_data scratch;
+ krb5_boolean valid;
+
+ if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
+ pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
+ KRB5_ENCPADATA_REQ_ENC_PA_REP);
+ if (pa == NULL)
+ retval = KRB5_KDCREP_MODIFIED;
+ else {
+ scratch.data = (char *) pa->contents;
+ scratch.length = pa->length;
+ }
+ if (retval == 0)
+ retval = decode_krb5_checksum(&scratch, &checksum);
+ if (retval == 0)
+ retval = krb5_c_verify_checksum(context, decrypting_key,
+ KRB5_KEYUSAGE_AS_REQ,
+ request, checksum, &valid);
+ if (retval == 0 &&valid == 0)
+ retval = KRB5_KDCREP_MODIFIED;
+ if (retval == 0) {
+ pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
+ KRB5_PADATA_FX_FAST);
+ *fast_avail = (pa != NULL);
+ }
+ }
+ if (checksum)
+ krb5_free_checksum(context, checksum);
+ return retval;
+}
+
+krb5_boolean
+krb5int_upgrade_to_fast_p(krb5_context context,
+ struct krb5int_fast_request_state *state,
+ krb5_pa_data **padata)
+{
+ if (state->armor_key != NULL)
+ return 0; /*already using FAST*/
+ if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
+ return 0;
+ if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL) {
+ state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+ return 1;
+ }
+ return 0;
+}
krb5_int32 nonce;
};
+#define KRB5INT_FAST_DO_FAST (1l<<0) /* Perform FAST */
+#define KRB5INT_FAST_ARMOR_AVAIL (1l<<1)
+
krb5_error_code
krb5int_fast_prep_req_body(krb5_context context, struct krb5int_fast_request_state *state,
krb5_kdc_req *request, krb5_data **encoded_req_body);
krb5_keyblock *output_key);
+krb5_error_code
+krb5int_fast_verify_nego(krb5_context context,
+ struct krb5int_fast_request_state *state,
+ krb5_kdc_rep *rep, krb5_data *request,
+ krb5_keyblock *decrypting_key,
+ krb5_boolean *fast_avail);
+
+krb5_boolean
+krb5int_upgrade_to_fast_p(krb5_context context,
+ struct krb5int_fast_request_state *state,
+ krb5_pa_data **padata);
#endif
&& data_eq(tgt->server->realm, tgt->client->realm));
}
+static krb5_error_code
+request_enc_pa_rep(krb5_pa_data ***padptr)
+{
+ size_t size = 0;
+ krb5_pa_data **pad = *padptr;
+ krb5_pa_data *pa= NULL;
+ if (pad)
+ for (size=0; pad[size]; size++);
+ pad = realloc(pad, sizeof(*pad)*(size+2));
+
+ if (pad == NULL)
+ return ENOMEM;
+ pad[size+1] = NULL;
+ pa = malloc(sizeof(krb5_pa_data));
+ if (pa == NULL)
+ return ENOMEM;
+ pa->contents = NULL;
+ pa->length = 0;
+ pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
+ pad[size] = pa;
+ *padptr = pad;
+ return 0;
+}
+
krb5_error_code KRB5_CALLCONV
krb5_get_in_tkt(krb5_context context,
krb5_flags options,
return code;
}
+/**
+ * Throw away any state related to specific realm either at the beginning of a
+ * request, or when a realm changes, or when we start to use FAST after
+ * assuming we would not do so.
+ *
+ * @param padata padata from an error if an error from the realm we now expect
+ * to talk to caused the restart. Used to infer negotiation characteristics
+ * such as whether FAST is used.
+ */
+static krb5_error_code
+restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
+ krb5_pa_data **padata)
+{
+ krb5_error_code code = 0;
+ unsigned char random_buf[4];
+ krb5_data random_data;
+ if (ctx->preauth_to_use) {
+ krb5_free_pa_data(context, ctx->preauth_to_use);
+ ctx->preauth_to_use = NULL;
+ }
+
+ if (ctx->fast_state) {
+ krb5int_fast_free_state(context, ctx->fast_state);
+ ctx->fast_state = NULL;
+ }
+ code = krb5int_fast_make_state(context, &ctx->fast_state);
+ if (code != 0)
+ goto cleanup;
+ ctx->get_data_rock.fast_state = ctx->fast_state;
+ krb5_preauth_request_context_init(context);
+ if (ctx->encoded_request_body) {
+ krb5_free_data(context, ctx->encoded_request_body);
+ ctx->encoded_request_body = NULL;
+ }
+ if (ctx->opte &&
+ (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
+ if ((code = make_preauth_list(context, ctx->opte->preauth_list,
+ ctx->opte->preauth_list_length,
+ &ctx->preauth_to_use)))
+ goto cleanup;
+ }
+
+ /* Set the request nonce. */
+ random_data.length = 4;
+ random_data.data = (char *)random_buf;
+ code = krb5_c_random_make_octets(context, &random_data);
+ if (code !=0)
+ goto cleanup;
+ /*
+ * See RT ticket 3196 at MIT. If we set the high bit, we may have
+ * compatibility problems with Heimdal, because we (incorrectly) encode
+ * this value as signed.
+ */
+ ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
+ krb5_free_principal(context, ctx->request->server);
+ ctx->request->server = NULL;
+
+ code = build_in_tkt_name(context, ctx->in_tkt_service,
+ ctx->request->client,
+ &ctx->request->server);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5_timeofday(context, &ctx->request_time);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5int_fast_as_armor(context, ctx->fast_state,
+ ctx->opte, ctx->request);
+ if (code != 0)
+ goto cleanup;
+ if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
+ code = krb5int_fast_as_armor(context, ctx->fast_state,
+ ctx->opte, ctx->request);
+ if (code != 0)
+ goto cleanup;
+ }
+ /* give the preauth plugins a chance to prep the request body */
+ krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
+
+ ctx->request->from = krb5int_addint32(ctx->request_time,
+ ctx->start_time);
+ ctx->request->till = krb5int_addint32(ctx->request->from,
+ ctx->tkt_life);
+
+ if (ctx->renew_life > 0) {
+ ctx->request->rtime =
+ krb5int_addint32(ctx->request->from, ctx->renew_life);
+ if (ctx->request->rtime < ctx->request->till) {
+ /* don't ask for a smaller renewable time than the lifetime */
+ ctx->request->rtime = ctx->request->till;
+ }
+ ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
+ } else
+ ctx->request->rtime = 0;
+ code = krb5int_fast_prep_req_body(context, ctx->fast_state,
+ ctx->request,
+ &ctx->encoded_request_body);
+ if (code != 0)
+ goto cleanup;
+cleanup:
+ return code;
+}
+
krb5_error_code KRB5_CALLCONV
krb5_init_creds_init(krb5_context context,
krb5_principal client,
ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
if (code != 0)
goto cleanup;
-
+ ctx->enc_pa_rep_permitted = 1;
code = krb5_copy_principal(context, client, &ctx->request->client);
if (code != 0)
goto cleanup;
opte = ctx->opte;
- code = krb5int_fast_make_state(context, &ctx->fast_state);
- if (code != 0)
- goto cleanup;
-
ctx->get_data_rock.magic = CLIENT_ROCK_MAGIC;
ctx->get_data_rock.etype = &ctx->etype;
- ctx->get_data_rock.fast_state = ctx->fast_state;
/* Initialise request parameters as per krb5_get_init_creds() */
ctx->request->kdc_options = context->kdc_default_options;
if (code != 0)
goto cleanup;
} else if (krb5_libdefault_boolean(context, &ctx->request->client->realm,
- KRB5_CONF_NOADDRESSES, &tmp) != 0
- || tmp) {
+ KRB5_CONF_NOADDRESSES, &tmp) != 0
+ || tmp) {
ctx->request->addresses = NULL;
} else {
code = krb5_os_localaddr(context, &ctx->request->addresses);
goto cleanup;
}
- /* initial preauth state */
- krb5_preauth_request_context_init(context);
-
- if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
- code = make_preauth_list(context,
- opte->preauth_list,
- opte->preauth_list_length,
- &ctx->preauth_to_use);
- if (code != 0)
- goto cleanup;
- }
-
if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
if (code != 0)
ctx->salt.data = NULL;
}
- /* nonce */
- {
- unsigned char random_buf[4];
- krb5_data random_data;
-
- random_data.length = sizeof(random_buf);
- random_data.data = (char *)random_buf;
-
- /*
- * See RT ticket 3196 at MIT. If we set the high bit, we
- * may have compatibility problems with Heimdal, because
- * we (incorrectly) encode this value as signed.
- */
- if (krb5_c_random_make_octets(context, &random_data) == 0)
- ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
- else {
- krb5_timestamp now;
-
- code = krb5_timeofday(context, &now);
- if (code != 0)
- goto cleanup;
-
- ctx->request->nonce = (krb5_int32)now;
- }
- }
-
- ctx->loopcount = 0;
+ code = restart_init_creds_loop(context, ctx, NULL);
*pctx = ctx;
free(ctx->in_tkt_service);
ctx->in_tkt_service = s;
- return 0;
+ krb5_preauth_request_context_fini(context);
+ return restart_init_creds_loop(context, ctx, NULL);
}
static krb5_error_code
{
krb5_error_code code;
- krb5_free_principal(context, ctx->request->server);
- ctx->request->server = NULL;
-
- code = build_in_tkt_name(context, ctx->in_tkt_service,
- ctx->request->client,
- &ctx->request->server);
- if (code != 0)
- goto cleanup;
-
- if (ctx->loopcount == 0) {
- code = krb5_timeofday(context, &ctx->request_time);
- if (code != 0)
- goto cleanup;
-
- code = krb5int_fast_as_armor(context, ctx->fast_state,
- ctx->opte, ctx->request);
- if (code != 0)
- goto cleanup;
-
- /* give the preauth plugins a chance to prep the request body */
- krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
- code = krb5int_fast_prep_req_body(context, ctx->fast_state,
- ctx->request,
- &ctx->encoded_request_body);
- if (code != 0)
- goto cleanup;
-
- ctx->request->from = krb5int_addint32(ctx->request_time,
- ctx->start_time);
- ctx->request->till = krb5int_addint32(ctx->request->from,
- ctx->tkt_life);
-
- if (ctx->renew_life > 0) {
- ctx->request->rtime =
- krb5int_addint32(ctx->request->from, ctx->renew_life);
- if (ctx->request->rtime < ctx->request->till) {
- /* don't ask for a smaller renewable time than the lifetime */
- ctx->request->rtime = ctx->request->till;
- }
- ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
- } else
- ctx->request->rtime = 0;
- } else if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
+ if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
code = KRB5_GET_IN_TKT_LOOP;
goto cleanup;
}
krb5_free_data(context, ctx->encoded_previous_request);
ctx->encoded_previous_request = NULL;
}
-
+ if (ctx->request->padata)
+ ctx->sent_nontrivial_preauth = 1;
+ if (ctx->enc_pa_rep_permitted)
+ code = request_enc_pa_rep(&ctx->request->padata);
+ if (code)
+ goto cleanup;
code = krb5int_fast_prep_req(context, ctx->fast_state,
ctx->request, ctx->encoded_request_body,
encode_krb5_as_req,
return code;
}
+/*
+ * The control flow is complicated. In order to switch from non-FAST mode to
+ * FAST mode, we need to reset our pre-authentication state. FAST negotiation
+ * attempts to make sure we rarely have to do this. When FAST negotiation is
+ * working, we record whether FAST is available when we obtain an armor ticket;
+ * if so, we start out with FAST enabled . There are two complicated
+ * situations.
+ *
+ * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
+ * a KDC in a case where we were not expecting to use FAST, and we have an
+ * armor ticket available, then we want to use FAST. That involves clearing
+ * out the pre-auth state, reinitializing the plugins and trying again with an
+ * armor key.
+ *
+ * Secondly, using the negotiation can cause problems with some older KDCs.
+ * Negotiation involves including a special padata item. Some KDCs, including
+ * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
+ * pre-authentication is required and unknown padata are included in the
+ * request. To make matters worse, these KDCs typically do not include a list
+ * of padata in PREAUTH_FAILED errors. So, if we get PREAUTH_FAILED and we
+ * generated no pre-authentication other than the negotiation then we want to
+ * retry without negotiation. In this case it is probably also desirable to
+ * retry with the preauth plugin state cleared.
+ *
+ * In all these cases we should not start over more than once. Control flow is
+ * managed by several variables.
+ *
+ * sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
+ * no restart on PREAUTH_FAILED
+ *
+ * KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
+ * armor; if not set, then we can't use FAST even if the KDC wants to.
+ *
+ * have_restarted: true if we've already restarted
+ */
+static krb5_boolean
+negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
+ krb5_pa_data **padata)
+{
+ if (!ctx->have_restarted &&
+ (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata) ||
+ (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
+ !ctx->sent_nontrivial_preauth)))
+ return 1;
+ return 0;
+}
+
static krb5_error_code
init_creds_step_reply(krb5_context context,
krb5_init_creds_context ctx,
int canon_flag = 0;
krb5_keyblock *strengthen_key = NULL;
krb5_keyblock encrypting_key;
+ krb5_boolean fast_avail;
encrypting_key.length = 0;
encrypting_key.contents = NULL;
&ctx->err_reply, &padata, &retry);
if (code != 0)
goto cleanup;
-
- if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) {
+ if (negotiation_requests_restart(context, ctx, padata)) {
+ ctx->have_restarted = 1;
+ krb5_preauth_request_context_fini(context);
+ if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
+ ctx->enc_pa_rep_permitted = 0;
+ code = restart_init_creds_loop(context, ctx, padata);
+ krb5_free_error(context, ctx->err_reply);
+ ctx->err_reply = NULL;
+ } else if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
+ retry) {
/* reset the list of preauth types to try */
krb5_free_pa_data(context, ctx->preauth_to_use);
ctx->preauth_to_use = padata;
/* this will trigger a new call to krb5_do_preauth() */
krb5_free_error(context, ctx->err_reply);
ctx->err_reply = NULL;
+ krb5_preauth_request_context_fini(context);
+ /* Permit another negotiation based restart. */
+ ctx->have_restarted = 0;
+ ctx->sent_nontrivial_preauth = 0;
+ code = restart_init_creds_loop(context, ctx, NULL);
+ if (code != 0)
+ goto cleanup;
} else {
if (retry) {
code = 0;
} else {
/* error + no hints = give up */
code = (krb5_error_code)ctx->err_reply->error +
- ERROR_TABLE_BASE_krb5;
+ ERROR_TABLE_BASE_krb5;
}
}
again. */
if (ctx->as_key.length) {
code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
- &encrypting_key);
+ &encrypting_key);
if (code != 0)
goto cleanup;
code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
goto cleanup;
}
+ code = krb5int_fast_verify_nego(context, ctx->fast_state,
+ ctx->reply, ctx->encoded_previous_request,
+ &encrypting_key, &fast_avail);
+ if (code)
+ goto cleanup;
code = verify_as_reply(context, ctx->request_time,
ctx->request, ctx->reply);
if (code != 0)
ctx->reply, &ctx->cred, NULL);
if (code != 0)
goto cleanup;
+ if (ctx->opte && ctx->opte->opt_private->out_ccache) {
+ krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache;
+ krb5_data config_data;
+ code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
+ if (code != 0)
+ goto cc_cleanup;
+ code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
+ if (code != 0)
+ goto cc_cleanup;
+ if (fast_avail) {
+ config_data.data = "yes";
+ config_data.length = strlen(config_data.data);
+ code = krb5_cc_set_config(context, out_ccache, ctx->cred.server,
+ KRB5_CONF_FAST_AVAIL, &config_data);
+ }
+ cc_cleanup:
+ if (code !=0) {
+ const char *msg;
+ msg = krb5_get_error_message(context, code);
+ krb5_set_error_message(context, code,
+ "%s while storing credentials", msg);
+ krb5_free_error_message(context, msg);
+ }
+ }
krb5_preauth_request_context_fini(context);
return code;
}
-
free_gic_opt_ext_preauth_data(context, opte);
if (opte->opt_private->fast_ccache_name)
free(opte->opt_private->fast_ccache_name);
+ if (opte->opt_private->out_ccache)
+ krb5_cc_close(context, opte->opt_private->out_ccache);
free(opte->opt_private);
opte->opt_private = NULL;
return 0;
retval = ENOMEM;
return retval;
}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_out_ccache(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_ccache ccache)
+{
+ krb5_error_code retval = 0;
+ krb5_gic_opt_ext *opte;
+
+ retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+ "krb5_get_init_creds_opt_set_out_ccache");
+ if (retval)
+ return retval;
+ if (opte->opt_private->out_ccache) {
+ krb5_cc_close(context, opte->opt_private->out_ccache);
+ opte->opt_private->out_ccache = NULL;
+ }
+ retval = krb5_cc_resolve(context, krb5_cc_get_name(context, ccache),
+ &opte->opt_private->out_ccache);
+ return retval;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_fast_flags(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_flags flags)
+{
+ krb5_error_code retval = 0;
+ krb5_gic_opt_ext *opte;
+
+ retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+ "krb5_get_init_creds_opt_set_fast_flags");
+ if (retval)
+ return retval;
+ opte->opt_private->fast_flags = flags;
+ return retval;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_get_fast_flags(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_flags *out_flags)
+{
+ krb5_error_code retval = 0;
+ krb5_gic_opt_ext *opte;
+ if (out_flags == NULL)
+ return EINVAL;
+ *out_flags = 0;
+ retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+ "krb5_get_init_creds_opt_get_fast_flags");
+ if (retval)
+ return retval;
+ *out_flags = opte->opt_private->fast_flags;
+ return retval;
+}
+
krb5_keyblock as_key;
krb5_enctype etype;
krb5_preauth_client_rock get_data_rock;
+ krb5_boolean enc_pa_rep_permitted;
+ krb5_boolean have_restarted;
+ krb5_boolean sent_nontrivial_preauth;
};
#define KRB5_INIT_CREDS_STEP_FLAG_COMPLETE 0x1
void *gak_data);
#endif /* !KRB5_INIT_CREDS_CONTEXT */
-
encode_krb5_as_req
encode_krb5_authdata
encode_krb5_authenticator
+encode_krb5_checksum
encode_krb5_cred
encode_krb5_enc_cred_part
encode_krb5_enc_data
krb5_cc_end_seq_get
krb5_cc_file_ops
krb5_cc_gen_new
+krb5_cc_get_config
krb5_cc_get_name
krb5_cc_get_principal
krb5_cc_get_type
krb5_cc_resolve
krb5_cc_retrieve_cred
krb5_cc_retrieve_cred_default
+krb5_cc_set_config
krb5_cc_set_default_name
krb5_cc_set_flags
krb5_cc_start_seq_get
krb5_get_init_creds_opt_alloc
krb5_get_init_creds_opt_free
krb5_get_init_creds_opt_free_pa
+krb5_get_init_creds_opt_get_fast_flags
krb5_get_init_creds_opt_get_pa
krb5_get_init_creds_opt_init
krb5_get_init_creds_opt_set_address_list
krb5_get_init_creds_opt_set_change_password_prompt
krb5_get_init_creds_opt_set_etype_list
krb5_get_init_creds_opt_set_fast_ccache_name
+krb5_get_init_creds_opt_set_fast_flags
krb5_get_init_creds_opt_set_forwardable
+krb5_get_init_creds_opt_set_out_ccache
krb5_get_init_creds_opt_set_pa
krb5_get_init_creds_opt_set_preauth_list
krb5_get_init_creds_opt_set_proxiable
krb5_init_keyblock
krb5_init_secure_context
krb5_internalize_opaque
+krb5_is_config_principal
krb5_is_permitted_enctype
krb5_is_referral_realm
krb5_is_thread_safe