/* * Build a tree given a set of profile values retrieved by * walk_rtree_capath_vals(). */ static krb5_error_code rtree_capath_tree(krb5_context context, const krb5_data *client, const krb5_data *server, char **vals, krb5_principal **rettree) { krb5_error_code retval = 0; unsigned int nvals, nlinks, nprincs, i; krb5_data srcrealm, dstrealm; krb5_principal *tree, *pprinc; *rettree = NULL; tree = pprinc = NULL; for (nvals = 0; vals[nvals] != NULL; nvals++) ; if (vals[0] != NULL && *vals[0] == '.') { nlinks = 0; } else { nlinks = nvals; } nprincs = nlinks + 2; tree = calloc(nprincs + 1, sizeof(krb5_principal)); if (tree == NULL) { retval = ENOMEM; goto error; } for (i = 0; i < nprincs + 1; i++) tree[i] = NULL; /* Invariant: PPRINC points one past end of list. */ pprinc = &tree[0]; /* Local TGS name */ retval = krb5int_tgtname(context, client, client, pprinc++); if (retval) goto error; srcrealm = *client; for (i = 0; i < nlinks; i++) { dstrealm.data = vals[i]; dstrealm.length = strcspn(vals[i], "\t "); retval = krb5int_tgtname(context, &dstrealm, &srcrealm, pprinc++); if (retval) goto error; srcrealm = dstrealm; } retval = krb5int_tgtname(context, server, &srcrealm, pprinc++); if (retval) goto error; *rettree = tree; error: profile_free_list(vals); if (retval) { while (pprinc != NULL && pprinc > &tree[0]) { /* krb5_free_principal() correctly handles null input */ krb5_free_principal(context, *--pprinc); *pprinc = NULL; } free(tree); } return retval; }
krb5_error_code krb5int_fast_tgs_armor(krb5_context context, struct krb5int_fast_request_state *state, krb5_keyblock *subkey, krb5_keyblock *session_key, krb5_ccache ccache, krb5_data *target_realm) { krb5_principal target_principal = NULL; krb5_keyblock *existing_armor = NULL; krb5_error_code retval = 0; if (ccache) { retval = krb5int_tgtname(context, target_realm, target_realm, &target_principal); if (retval == 0) retval = fast_armor_ap_request(context, state, ccache, target_principal); if (retval == 0) { existing_armor = state->armor_key; state->armor_key = NULL; retval = krb5_c_fx_cf2_simple(context, existing_armor, "explicitarmor", subkey, "tgsarmor", &state->armor_key); } } else { retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor", session_key, "ticketarmor", &state->armor_key); } if (target_principal) krb5_free_principal(context, target_principal); krb5_free_keyblock(context, existing_armor); return retval; }
/* Set up a request for a TGT for realm, using ctx->cur_tgt. */ static krb5_error_code make_request_for_tgt(krb5_context context, krb5_tkt_creds_context ctx, const krb5_data *realm) { krb5_error_code code; /* Construct the principal krbtgt/<realm>@<cur-tgt-realm>. */ krb5_free_principal(context, ctx->tgt_princ); ctx->tgt_princ = NULL; code = krb5int_tgtname(context, realm, &ctx->cur_tgt->server->realm, &ctx->tgt_princ); if (code != 0) return code; TRACE_TKT_CREDS_TGT_REQ(context, ctx->tgt_princ, ctx->cur_tgt->server); /* Construct input creds using ctx->tgt_in_creds as a container. */ memset(&ctx->tgt_in_creds, 0, sizeof(ctx->tgt_in_creds)); ctx->tgt_in_creds.client = ctx->client; ctx->tgt_in_creds.server = ctx->tgt_princ; /* Make a request for the above creds with no extra options. */ ctx->tgs_in_creds = &ctx->tgt_in_creds; code = make_request(context, ctx, 0); return code; }
krb5_error_code krb5int_fast_as_armor(krb5_context context, struct krb5int_fast_request_state *state, krb5_gic_opt_ext *opte, krb5_kdc_req *request) { 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) { TRACE_FAST_ARMOR_CCACHE(context, 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) { retval = krb5int_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) { TRACE_FAST_CCACHE_CONFIG(context); 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) { TRACE_FAST_REQUIRED(context); 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, target_principal); } if (retval != 0) { const char * errmsg; errmsg = krb5_get_error_message(context, retval); if (errmsg) { krb5_set_error_message(context, retval, "%s constructing AP-REQ armor", errmsg); krb5_free_error_message(context, errmsg); } } } if (ccache) krb5_cc_close(context, ccache); if (target_principal) krb5_free_principal(context, target_principal); return retval; }
krb5_error_code krb5int_fast_as_armor(krb5_context context, struct krb5int_fast_request_state *state, krb5_get_init_creds_opt *opt, krb5_kdc_req *request) { krb5_error_code retval = 0; krb5_ccache ccache = NULL; krb5_principal target_principal = NULL; krb5_data *target_realm; const char *ccname = k5_gic_opt_get_fast_ccache_name(opt); krb5_flags fast_flags; krb5_clear_error_message(context); target_realm = &request->server->realm; if (ccname != NULL) { TRACE_FAST_ARMOR_CCACHE(context, ccname); state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL; retval = krb5_cc_resolve(context, ccname, &ccache); if (retval == 0) { retval = krb5int_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_CC_CONF_FAST_AVAIL, &config_data); if ((retval == 0) && config_data.data) { TRACE_FAST_CCACHE_CONFIG(context); state->fast_state_flags |= KRB5INT_FAST_DO_FAST; } krb5_free_data_contents(context, &config_data); retval = 0; } fast_flags = k5_gic_opt_get_fast_flags(opt); if (fast_flags & KRB5_FAST_REQUIRED) { TRACE_FAST_REQUIRED(context); 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, target_principal); } if (retval != 0) { k5_prependmsg(context, retval, _("Error constructing AP-REQ armor")); } } if (ccache) krb5_cc_close(context, ccache); if (target_principal) krb5_free_principal(context, target_principal); return retval; }
/* * Point *TGT at an allocated credentials structure containing a TGT for realm * retrieved from ctx->ccache. If we are retrieving a foreign TGT, accept any * issuing realm (i.e. match only the service principal name). If the TGT is * not found in the cache, return successfully but set *tgt to NULL. */ static krb5_error_code get_cached_tgt(krb5_context context, krb5_tkt_creds_context ctx, const krb5_data *realm, krb5_creds **tgt) { krb5_creds mcreds; krb5_error_code code; krb5_principal tgtname = NULL; krb5_flags flags; krb5_boolean local_realm = data_eq(*realm, ctx->client->realm); *tgt = NULL; /* Construct the principal krbtgt/<realm>@<client realm>. The realm * won't matter if we're getting a foreign TGT. */ code = krb5int_tgtname(context, realm, &ctx->client->realm, &tgtname); if (code != 0) goto cleanup; /* Don't match the TGT realm if we're getting a foreign TGT. */ flags = KRB5_TC_SUPPORTED_KTYPES; if (!local_realm) flags |= KRB5_TC_MATCH_SRV_NAMEONLY; /* Construct a matching cred for the ccache query. */ memset(&mcreds, 0, sizeof(mcreds)); mcreds.client = ctx->client; mcreds.server = tgtname; /* Fetch the TGT credential. */ context->use_conf_ktypes = TRUE; code = cache_get(context, ctx->ccache, flags, &mcreds, tgt); context->use_conf_ktypes = FALSE; /* Handle not-found errors. Make a note if we couldn't find a local TGT * because of enctypes. */ if (local_realm && code == KRB5_CC_NOT_KTYPE) ctx->cache_code = KRB5_CC_NOT_KTYPE; if (code != 0 && code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) goto cleanup; code = 0; cleanup: krb5_free_principal(context, tgtname); return code; }
/* * Build tree by hierarchical traversal. */ static krb5_error_code rtree_hier_tree(krb5_context context, const krb5_data *client, const krb5_data *server, krb5_principal **rettree, int sep) { krb5_error_code retval; krb5_data *realms; const krb5_data *dstrealm, *srcrealm; krb5_principal *tree, *pprinc; size_t nrealms, nprincs, i; *rettree = NULL; retval = rtree_hier_realms(context, client, server, &realms, &nrealms, sep); if (retval) return retval; nprincs = nrealms; pprinc = tree = calloc(nprincs + 1, sizeof(krb5_principal)); if (tree == NULL) { retval = ENOMEM; goto error; } for (i = 0; i < nrealms; i++) tree[i] = NULL; srcrealm = client; for (i = 0; i < nrealms; i++) { dstrealm = &realms[i]; retval = krb5int_tgtname(context, dstrealm, srcrealm, pprinc++); if (retval) goto error; srcrealm = dstrealm; } *rettree = tree; free_realmlist(context, realms, nrealms); return 0; error: while (pprinc != NULL && pprinc > tree) { krb5_free_principal(context, *--pprinc); *pprinc = NULL; } free_realmlist(context, realms, nrealms); free(tree); return retval; }
/* Get a TGT for use at the remote host */ krb5_error_code KRB5_CALLCONV krb5_fwd_tgt_creds(krb5_context context, krb5_auth_context auth_context, char *rhost, krb5_principal client, krb5_principal server, krb5_ccache cc, int forwardable, krb5_data *outbuf) /* Should forwarded TGT also be forwardable? */ { krb5_replay_data replaydata; krb5_data * scratch = 0; krb5_address **addrs = NULL; krb5_error_code retval; krb5_creds creds, tgt; krb5_creds *pcreds; krb5_flags kdcoptions; int close_cc = 0; int free_rhost = 0; krb5_enctype enctype = 0; krb5_keyblock *session_key; krb5_boolean old_use_conf_ktypes = context->use_conf_ktypes; memset(&creds, 0, sizeof(creds)); memset(&tgt, 0, sizeof(creds)); if (cc == 0) { if ((retval = krb5int_cc_default(context, &cc))) goto errout; close_cc = 1; } retval = krb5_auth_con_getkey (context, auth_context, &session_key); if (retval) goto errout; if (session_key) { enctype = session_key->enctype; krb5_free_keyblock (context, session_key); session_key = NULL; } else if (server) { /* must server be non-NULL when rhost is given? */ /* Try getting credentials to see what the remote side supports. Not bulletproof, just a heuristic. */ krb5_creds in, *out = 0; memset (&in, 0, sizeof(in)); retval = krb5_copy_principal (context, server, &in.server); if (retval) goto punt; retval = krb5_copy_principal (context, client, &in.client); if (retval) goto punt; retval = krb5_get_credentials (context, 0, cc, &in, &out); if (retval) goto punt; /* Got the credentials. Okay, now record the enctype and throw them away. */ enctype = out->keyblock.enctype; krb5_free_creds (context, out); punt: krb5_free_cred_contents (context, &in); } if ((retval = krb5_copy_principal(context, client, &creds.client))) goto errout; retval = krb5int_tgtname(context, &client->realm, &client->realm, &creds.server); if (retval) goto errout; /* fetch tgt directly from cache */ context->use_conf_ktypes = 1; retval = krb5_cc_retrieve_cred (context, cc, KRB5_TC_SUPPORTED_KTYPES, &creds, &tgt); context->use_conf_ktypes = old_use_conf_ktypes; if (retval) goto errout; /* tgt->client must be equal to creds.client */ if (!krb5_principal_compare(context, tgt.client, creds.client)) { retval = KRB5_PRINC_NOMATCH; goto errout; } if (!tgt.ticket.length) { retval = KRB5_NO_TKT_SUPPLIED; goto errout; } if (tgt.addresses && *tgt.addresses) { if (rhost == NULL) { if (krb5_princ_type(context, server) != KRB5_NT_SRV_HST) { retval = KRB5_FWD_BAD_PRINCIPAL; goto errout; } if (krb5_princ_size(context, server) < 2){ retval = KRB5_CC_BADNAME; goto errout; } rhost = malloc(server->data[1].length+1); if (!rhost) { retval = ENOMEM; goto errout; } free_rhost = 1; memcpy(rhost, server->data[1].data, server->data[1].length); rhost[server->data[1].length] = '\0'; } retval = krb5_os_hostaddr(context, rhost, &addrs); if (retval) goto errout; } creds.keyblock.enctype = enctype; creds.times = tgt.times; creds.times.starttime = 0; kdcoptions = flags2options(tgt.ticket_flags)|KDC_OPT_FORWARDED; if (!forwardable) /* Reset KDC_OPT_FORWARDABLE */ kdcoptions &= ~(KDC_OPT_FORWARDABLE); if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) { if (enctype) { creds.keyblock.enctype = 0; if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) goto errout; } else goto errout; } retval = krb5_mk_1cred(context, auth_context, pcreds, &scratch, &replaydata); krb5_free_creds(context, pcreds); if (retval) { if (scratch) krb5_free_data(context, scratch); } else { *outbuf = *scratch; free(scratch); } errout: if (addrs) krb5_free_addresses(context, addrs); if (close_cc) krb5_cc_close(context, cc); if (free_rhost) free(rhost); krb5_free_cred_contents(context, &creds); krb5_free_cred_contents(context, &tgt); return retval; }