/* * Construct list of realms between client and server. */ static krb5_error_code rtree_hier_realms(krb5_context context, const krb5_data *client, const krb5_data *server, krb5_data **realms, size_t *nrealms, int sep) { krb5_error_code retval; struct hstate c, s; krb5_data *ctweens = NULL, *stweens = NULL, *twp, *r, *rp; size_t nctween, nstween; *realms = NULL; *nrealms = 0; r = rp = NULL; c.str = client->data; c.len = client->length; c.dot = c.tail = NULL; s.str = server->data; s.len = server->length; s.dot = s.tail = NULL; comtail(&c, &s, sep); adjtail(&c, &s, sep); retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep); if (retval) goto error; retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep); if (retval) goto error; rp = r = calloc(nctween + nstween, sizeof(krb5_data)); if (r == NULL) { retval = ENOMEM; goto error; } /* Copy client realm "tweens" forward. */ for (twp = ctweens; twp < &ctweens[nctween]; twp++) { retval = krb5int_copy_data_contents(context, twp, rp); if (retval) goto error; rp++; } /* Copy server realm "tweens" backward. */ for (twp = &stweens[nstween]; twp-- > stweens;) { retval = krb5int_copy_data_contents(context, twp, rp); if (retval) goto error; rp++; } error: free(ctweens); free(stweens); if (retval) { free_realmlist(context, r, rp - r); return retval; } *realms = r; *nrealms = rp - r; return 0; }
static krb5_error_code mspac_get_attribute_types(krb5_context kcontext, krb5_authdata_context context, void *plugin_context, void *request_context, krb5_data **out_attrs) { struct mspac_context *pacctx = (struct mspac_context *)request_context; unsigned int i, j; krb5_data *attrs; krb5_error_code code; if (pacctx->pac == NULL) return ENOENT; attrs = calloc(1 + pacctx->pac->pac->cBuffers + 1, sizeof(krb5_data)); if (attrs == NULL) return ENOMEM; j = 0; /* The entire PAC */ code = krb5int_copy_data_contents(kcontext, &mspac_attribute_types[0].attribute, &attrs[j++]); if (code != 0) { free(attrs); return code; } /* PAC buffers */ for (i = 0; i < pacctx->pac->pac->cBuffers; i++) { krb5_data attr; code = mspac_type2attr(pacctx->pac->pac->Buffers[i].ulType, &attr); if (code == 0) { code = krb5int_copy_data_contents(kcontext, &attr, &attrs[j++]); if (code != 0) { krb5int_free_data_list(kcontext, attrs); return code; } } else { int length; length = asprintf(&attrs[j].data, "urn:mspac:%d", pacctx->pac->pac->Buffers[i].ulType); if (length < 0) { krb5int_free_data_list(kcontext, attrs); return ENOMEM; } attrs[j++].length = length; } } attrs[j].data = NULL; attrs[j].length = 0; *out_attrs = attrs; return 0; }
/* * Fill in the caller out, realm, and flags output variables. out is filled in * with ctx->previous_request, which the caller should set, and realm is filled * in with the realm of ctx->cur_tgt. */ static krb5_error_code set_caller_request(krb5_context context, krb5_tkt_creds_context ctx) { krb5_error_code code; const krb5_data *req = &ctx->previous_request; const krb5_data *realm = &ctx->cur_tgt->server->data[1]; krb5_data out_copy = empty_data(), realm_copy = empty_data(); code = krb5int_copy_data_contents(context, req, &out_copy); if (code != 0) goto cleanup; code = krb5int_copy_data_contents(context, realm, &realm_copy); if (code != 0) goto cleanup; *ctx->caller_out = out_copy; *ctx->caller_realm = realm_copy; *ctx->caller_flags = KRB5_TKT_CREDS_STEP_FLAG_CONTINUE; return 0; cleanup: krb5_free_data_contents(context, &out_copy); krb5_free_data_contents(context, &realm_copy); return code; }
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 = k5_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; ret = krb5int_copy_data_contents(context, &cred.ticket, data); if (ret) goto out; TRACE_CC_GET_CONFIG(context, id, principal, key, data); out: krb5_free_cred_contents(context, &cred); krb5_free_cred_contents(context, &mcred); return ret; }
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)); TRACE_CC_SET_CONFIG(context, id, principal, key, data); ret = k5_build_conf_principals(context, id, principal, key, &cred); if (ret) goto out; if (data == NULL) { ret = krb5_cc_remove_cred(context, id, 0, &cred); } else { ret = krb5int_copy_data_contents(context, data, &cred.ticket); if (ret) goto out; ret = krb5_cc_store_cred(context, id, &cred); } out: krb5_free_cred_contents(context, &cred); return ret; }
static krb5_error_code mspac_get_attribute(krb5_context kcontext, krb5_authdata_context context, void *plugin_context, void *request_context, const krb5_data *attribute, krb5_boolean *authenticated, krb5_boolean *complete, krb5_data *value, krb5_data *display_value, int *more) { struct mspac_context *pacctx = (struct mspac_context *)request_context; krb5_error_code code; krb5_ui_4 type; if (display_value != NULL) { display_value->data = NULL; display_value->length = 0; } if (*more != -1 || pacctx->pac == NULL) return ENOENT; /* If it didn't verify, pretend it didn't exist. */ if (!pacctx->pac->verified) { TRACE_MSPAC_DISCARD_UNVERF(kcontext); return ENOENT; } code = mspac_attr2type(attribute, &type); if (code != 0) return code; /* -1 is a magic type that refers to the entire PAC */ if (type == (krb5_ui_4)-1) { if (value != NULL) code = krb5int_copy_data_contents(kcontext, &pacctx->pac->data, value); else code = 0; } else { if (value != NULL) code = krb5_pac_get_buffer(kcontext, pacctx->pac, type, value); else code = k5_pac_locate_buffer(kcontext, pacctx->pac, type, NULL); } if (code == 0) { *authenticated = pacctx->pac->verified; *complete = TRUE; } *more = 0; return code; }
krb5_error_code k5_client_realm_path(krb5_context context, const krb5_data *client, const krb5_data *server, krb5_data **rpath_out) { krb5_error_code retval; char **capvals; size_t i; krb5_data *rpath = NULL, d; retval = rtree_capath_vals(context, client, server, &capvals); if (retval) return retval; /* Count capaths (if any) and allocate space. Leave room for the client * realm, server realm, and terminator. */ for (i = 0; capvals != NULL && capvals[i] != NULL; i++); rpath = calloc(i + 3, sizeof(*rpath)); if (rpath == NULL) return ENOMEM; /* Populate rpath with the client realm, capaths, and server realm. */ retval = krb5int_copy_data_contents(context, client, &rpath[0]); if (retval) goto cleanup; for (i = 0; capvals != NULL && capvals[i] != NULL; i++) { d = make_data(capvals[i], strcspn(capvals[i], "\t ")); retval = krb5int_copy_data_contents(context, &d, &rpath[i + 1]); if (retval) goto cleanup; } retval = krb5int_copy_data_contents(context, server, &rpath[i + 1]); if (retval) goto cleanup; /* Terminate rpath and return it. */ rpath[i + 2] = empty_data(); *rpath_out = rpath; rpath = NULL; cleanup: krb5int_free_data_list(context, rpath); return retval; }
krb5_error_code krb5_kt_find_realm(krb5_context context, krb5_keytab keytab, krb5_principal princ, krb5_data *realm) { krb5_kt_cursor cur; krb5_keytab_entry ent; krb5_boolean match; krb5_data tmp_realm; krb5_error_code ret, ret2; ret = krb5_kt_start_seq_get(context, keytab, &cur); if (ret != 0) { return (ret); } while ((ret = krb5_kt_next_entry(context, keytab, &ent, &cur)) == 0) { /* For the comparison the realms should be the same. */ memcpy(&tmp_realm, &ent.principal->realm, sizeof (krb5_data)); memcpy(&ent.principal->realm, &princ->realm, sizeof (krb5_data)); match = krb5_principal_compare(context, ent.principal, princ); /* Copy the realm back */ memcpy(&ent.principal->realm, &tmp_realm, sizeof (krb5_data)); if (match) { /* * A suitable entry was found in the keytab. * Copy its realm */ ret = krb5int_copy_data_contents(context, &ent.principal->realm, realm); if (ret) { krb5_kt_free_entry(context, &ent); krb5_kt_end_seq_get(context, keytab, &cur); return (ret); } krb5_kt_free_entry(context, &ent); break; } krb5_kt_free_entry(context, &ent); } ret2 = krb5_kt_end_seq_get(context, keytab, &cur); if (ret == KRB5_KT_END) { return (KRB5_KT_NOTFOUND); } return (ret ? ret : ret2); }
krb5_error_code krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code_out, krb5_data *result_data_out) { krb5_error_code ret; krb5_data result_data, *clear = NULL; krb5_boolean is_error; char *ptr; int result_code; *result_code_out = 0; *result_data_out = empty_data(); ret = get_clear_result(context, auth_context, packet, &clear, &is_error); if (ret) return ret; if (clear->length < 2) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* Decode and check the result code. */ ptr = clear->data; result_code = (*ptr++ & 0xff); result_code = (result_code << 8) | (*ptr++ & 0xff); if (result_code < KRB5_KPASSWD_SUCCESS || result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* Successful replies must not come from errors. */ if (is_error && result_code == KRB5_KPASSWD_SUCCESS) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } result_data = make_data(ptr, clear->data + clear->length - ptr); ret = krb5int_copy_data_contents(context, &result_data, result_data_out); if (ret) goto cleanup; *result_code_out = result_code; cleanup: krb5_free_data(context, clear); return ret; }
/* * Copy a principal structure, with fresh allocation. */ krb5_error_code KRB5_CALLCONV krb5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc) { register krb5_principal tempprinc; register int i, nelems; tempprinc = (krb5_principal)malloc(sizeof(krb5_principal_data)); if (tempprinc == 0) return ENOMEM; *tempprinc = *inprinc; nelems = (int) krb5_princ_size(context, inprinc); tempprinc->data = malloc(nelems * sizeof(krb5_data)); if (tempprinc->data == 0) { free((char *)tempprinc); return ENOMEM; } for (i = 0; i < nelems; i++) { if (krb5int_copy_data_contents(context, krb5_princ_component(context, inprinc, i), krb5_princ_component(context, tempprinc, i)) != 0) { while (--i >= 0) free(krb5_princ_component(context, tempprinc, i)->data); free (tempprinc->data); free (tempprinc); return ENOMEM; } } if (krb5int_copy_data_contents_add0(context, &inprinc->realm, &tempprinc->realm) != 0) { for (i = 0; i < nelems; i++) free(krb5_princ_component(context, tempprinc, i)->data); free(tempprinc->data); free(tempprinc); return ENOMEM; } *outprinc = tempprinc; return 0; }
/* Set salt in rock based on pw-salt or afs3-salt elements in padata. */ static krb5_error_code get_salt(krb5_context context, krb5_pa_data **padata, krb5_kdc_req *request, krb5_clpreauth_rock rock) { krb5_error_code ret; krb5_pa_data *pa; krb5_data d; const char *p; /* Look for a pw-salt or afs3-salt element. */ pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_PW_SALT); if (pa == NULL) pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_AFS3_SALT); if (pa == NULL) return 0; /* Set rock->salt based on the element we found. */ krb5_free_data_contents(context, rock->salt); d = padata2data(*pa); ret = krb5int_copy_data_contents(context, &d, rock->salt); if (ret) return ret; /* Adjust the salt if we got it from an afs3-salt element. */ if (pa->pa_type == KRB5_PADATA_AFS3_SALT) { /* Work around a (possible) old Heimdal KDC foible. */ p = memchr(rock->salt->data, '@', rock->salt->length); if (p != NULL) rock->salt->length = p - rock->salt->data; /* Tolerate extra null in MIT KDC afs3-salt value. */ if (rock->salt->length > 0 && rock->salt->data[rock->salt->length - 1] == '\0') rock->salt->length--; /* Set an s2kparams value to indicate AFS string-to-key. */ krb5_free_data_contents(context, rock->s2kparams); ret = alloc_data(rock->s2kparams, 1); if (ret) return ret; rock->s2kparams->data[0] = '\1'; } *rock->default_salt = FALSE; TRACE_PREAUTH_SALT(context, rock->salt, pa->pa_type); return 0; }
/* Add realm to ctx->realms_seen so that we can avoid revisiting it later. */ static krb5_error_code remember_realm(krb5_context context, krb5_tkt_creds_context ctx, const krb5_data *realm) { size_t len = 0; krb5_data *new_list; if (ctx->realms_seen != NULL) { for (len = 0; ctx->realms_seen[len].data != NULL; len++); } new_list = realloc(ctx->realms_seen, (len + 2) * sizeof(krb5_data)); if (new_list == NULL) return ENOMEM; ctx->realms_seen = new_list; new_list[len] = empty_data(); new_list[len + 1] = empty_data(); return krb5int_copy_data_contents(context, realm, &new_list[len]); }
static krb5_error_code mspac_export_authdata(krb5_context kcontext, krb5_authdata_context context, void *plugin_context, void *request_context, krb5_flags usage, krb5_authdata ***out_authdata) { struct mspac_context *pacctx = (struct mspac_context *)request_context; krb5_error_code code; krb5_authdata **authdata; krb5_data data; if (pacctx->pac == NULL) return 0; authdata = calloc(2, sizeof(krb5_authdata *)); if (authdata == NULL) return ENOMEM; authdata[0] = calloc(1, sizeof(krb5_authdata)); if (authdata[0] == NULL) { free(authdata); return ENOMEM; } authdata[1] = NULL; code = krb5int_copy_data_contents(kcontext, &pacctx->pac->data, &data); if (code != 0) { krb5_free_authdata(kcontext, authdata); return code; } authdata[0]->magic = KV5M_AUTHDATA; authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC; authdata[0]->length = data.length; authdata[0]->contents = (krb5_octet *)data.data; authdata[1] = NULL; *out_authdata = authdata; return 0; }
/* * Copy a principal structure, with fresh allocation. */ krb5_error_code KRB5_CALLCONV krb5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc) { krb5_principal tempprinc; krb5_int32 i; tempprinc = (krb5_principal)malloc(sizeof(krb5_principal_data)); if (tempprinc == 0) return ENOMEM; *tempprinc = *inprinc; tempprinc->data = malloc(inprinc->length * sizeof(krb5_data)); if (tempprinc->data == 0) { free(tempprinc); return ENOMEM; } for (i = 0; i < inprinc->length; i++) { if (krb5int_copy_data_contents(context, &inprinc->data[i], &tempprinc->data[i]) != 0) { while (--i >= 0) free(tempprinc->data[i].data); free (tempprinc->data); free (tempprinc); return ENOMEM; } } if (krb5int_copy_data_contents_add0(context, &inprinc->realm, &tempprinc->realm) != 0) { for (i = 0; i < inprinc->length; i++) free(tempprinc->data[i].data); free(tempprinc->data); free(tempprinc); return ENOMEM; } *outprinc = tempprinc; return 0; }
static krb5_error_code s4u2proxy_get_attribute_types(krb5_context kcontext, krb5_authdata_context context, void *plugin_context, void *request_context, krb5_data **out_attrs) { struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context; krb5_error_code code; krb5_data *attrs; int i = 0; if (s4uctx->count == 0) return ENOENT; attrs = k5calloc(2, sizeof(krb5_data), &code); if (attrs == NULL) goto cleanup; code = krb5int_copy_data_contents(kcontext, &s4u2proxy_transited_services_attr, &attrs[i++]); if (code != 0) goto cleanup; attrs[i].data = NULL; attrs[i].length = 0; *out_attrs = attrs; attrs = NULL; cleanup: if (attrs != NULL) { for (i = 0; attrs[i].data; i++) krb5_free_data_contents(kcontext, &attrs[i]); free(attrs); } return 0; }
static krb5_error_code k5_pac_copy(krb5_context context, krb5_pac src, krb5_pac *dst) { size_t header_len; krb5_ui_4 cbuffers; krb5_error_code code; krb5_pac pac; cbuffers = src->pac->cBuffers; if (cbuffers != 0) cbuffers--; header_len = sizeof(PACTYPE) + cbuffers * sizeof(PAC_INFO_BUFFER); pac = (krb5_pac)malloc(sizeof(*pac)); if (pac == NULL) return ENOMEM; pac->pac = (PACTYPE *)malloc(header_len); if (pac->pac == NULL) { free(pac); return ENOMEM; } memcpy(pac->pac, src->pac, header_len); code = krb5int_copy_data_contents(context, &src->data, &pac->data); if (code != 0) { free(pac->pac); free(pac); return ENOMEM; } pac->verified = src->verified; *dst = pac; return 0; }
/* Decide where to begin the acquisition process. */ static krb5_error_code begin(krb5_context context, krb5_tkt_creds_context ctx) { krb5_error_code code; code = check_cache(context, ctx); if (code != 0 || ctx->state == STATE_COMPLETE) return code; /* If the server realm is unspecified, start with the client realm. */ if (krb5_is_referral_realm(&ctx->server->realm)) { krb5_free_data_contents(context, &ctx->server->realm); code = krb5int_copy_data_contents(context, &ctx->client->realm, &ctx->server->realm); TRACE_TKT_CREDS_REFERRAL_REALM(context, ctx->server); if (code != 0) return code; } /* Obtain a TGT for the service realm. */ ctx->getting_tgt_for = STATE_REFERRALS; return begin_get_tgt(context, ctx); }
/*ARGSUSED*/ static krb5_error_code pa_salt(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata, krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data) { krb5_data tmp; tmp.data = (char *)in_padata->contents; tmp.length = in_padata->length; krb5_free_data_contents(context, salt); krb5int_copy_data_contents(context, &tmp, salt); if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT) salt->length = -1; return(0); }
static krb5_error_code krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts, int kdcopt) { krb5_error_code retval, subretval; krb5_principal client, server, supplied_server, out_supplied_server; krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS]; krb5_creds *otgtptr = NULL; int tgtptr_isoffpath = 0; krb5_boolean old_use_conf_ktypes; char **hrealms; unsigned int referral_count, i; /* * Set up client and server pointers. Make a fresh and modifyable * copy of the in_cred server and save the supplied version. */ client = in_cred->client; if ((retval=krb5_copy_principal(context, in_cred->server, &server))) return retval; /* We need a second copy for the output creds. */ if ((retval = krb5_copy_principal(context, server, &out_supplied_server)) != 0 ) { krb5_free_principal(context, server); return retval; } supplied_server = in_cred->server; in_cred->server=server; DUMP_PRINC("gc_from_kdc initial client", client); DUMP_PRINC("gc_from_kdc initial server", server); memset(&cc_tgt, 0, sizeof(cc_tgt)); memset(&tgtq, 0, sizeof(tgtq)); memset(&referral_tgts, 0, sizeof(referral_tgts)); tgtptr = NULL; *tgts = NULL; *out_cred=NULL; old_use_conf_ktypes = context->use_conf_ktypes; /* Copy client realm to server if no hint. */ if (krb5_is_referral_realm(&server->realm)) { /* Use the client realm. */ DPRINTF(("gc_from_kdc: no server realm supplied, " "using client realm.\n")); krb5_free_data_contents(context, &server->realm); if (!( server->realm.data = (char *)malloc(client->realm.length+1))) return ENOMEM; memcpy(server->realm.data, client->realm.data, client->realm.length); server->realm.length = client->realm.length; server->realm.data[server->realm.length] = 0; } /* * Retreive initial TGT to match the specified server, either for the * local realm in the default (referral) case or for the remote * realm if we're starting someplace non-local. */ retval = tgt_mcred(context, client, server, client, &tgtq); if (retval) goto cleanup; /* Fast path: Is it in the ccache? */ context->use_conf_ktypes = 1; retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, &tgtq, &cc_tgt); if (!retval) { tgtptr = &cc_tgt; } else if (!HARD_CC_ERR(retval)) { DPRINTF(("gc_from_kdc: starting do_traversal to find " "initial TGT for referral\n")); tgtptr_isoffpath = 0; otgtptr = NULL; retval = do_traversal(context, ccache, client, server, &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath); } if (retval) { DPRINTF(("gc_from_kdc: failed to find initial TGT for referral\n")); goto cleanup; } DUMP_PRINC("gc_from_kdc: server as requested", supplied_server); /* * Try requesting a service ticket from our local KDC with referrals * turned on. If the first referral succeeds, follow a referral-only * path, otherwise fall back to old-style assumptions. */ /* * Save TGTPTR because we rewrite it in the referral loop, and * we might need to explicitly free it later. */ otgtptr = tgtptr; for (referral_count = 0; referral_count < KRB5_REFERRAL_MAXHOPS; referral_count++) { #if 0 DUMP_PRINC("gc_from_kdc: referral loop: tgt in use", tgtptr->server); DUMP_PRINC("gc_from_kdc: referral loop: request is for", server); #endif retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); if (retval) { DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n", error_message(retval))); /* If we haven't gone anywhere yet, fail through to the non-referral case. */ if (referral_count==0) { DPRINTF(("gc_from_kdc: initial referral failed; " "punting to fallback.\n")); break; } /* Otherwise, try the same query without canonicalization set, and fail hard if that doesn't work. */ DPRINTF(("gc_from_kdc: referral #%d failed; " "retrying without option.\n", referral_count + 1)); retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); /* Whether or not that succeeded, we're done. */ goto cleanup; } /* Referral request succeeded; let's see what it is. */ if (krb5_principal_compare(context, in_cred->server, (*out_cred)->server)) { DPRINTF(("gc_from_kdc: request generated ticket " "for requested server principal\n")); DUMP_PRINC("gc_from_kdc final referred reply", in_cred->server); /* * Check if the return enctype is one that we requested if * needed. */ if (old_use_conf_ktypes || context->tgs_ktype_count == 0) goto cleanup; for (i = 0; i < context->tgs_ktype_count; i++) { if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) { /* Found an allowable etype, so we're done */ goto cleanup; } } /* * We need to try again, but this time use the * tgs_ktypes in the context. At this point we should * have all the tgts to succeed. */ /* Free "wrong" credential */ krb5_free_creds(context, *out_cred); *out_cred = NULL; /* Re-establish tgs etypes */ context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); goto cleanup; } else if (IS_TGS_PRINC(context, (*out_cred)->server)) { krb5_data *r1, *r2; DPRINTF(("gc_from_kdc: request generated referral tgt\n")); DUMP_PRINC("gc_from_kdc credential received", (*out_cred)->server); if (referral_count == 0) r1 = &tgtptr->server->data[1]; else r1 = &referral_tgts[referral_count-1]->server->data[1]; r2 = &(*out_cred)->server->data[1]; if (data_eq(*r1, *r2)) { DPRINTF(("gc_from_kdc: referred back to " "previous realm; fall back\n")); krb5_free_creds(context, *out_cred); *out_cred = NULL; break; } /* Check for referral routing loop. */ for (i=0;i<referral_count;i++) { #if 0 DUMP_PRINC("gc_from_kdc: loop compare #1", (*out_cred)->server); DUMP_PRINC("gc_from_kdc: loop compare #2", referral_tgts[i]->server); #endif if (krb5_principal_compare(context, (*out_cred)->server, referral_tgts[i]->server)) { DFPRINTF((stderr, "krb5_get_cred_from_kdc_opt: " "referral routing loop - " "got referral back to hop #%d\n", i)); retval=KRB5_KDC_UNREACH; goto cleanup; } } /* Point current tgt pointer at newly-received TGT. */ if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); tgtptr=*out_cred; /* Save pointer to tgt in referral_tgts. */ referral_tgts[referral_count]=*out_cred; *out_cred = NULL; /* Copy krbtgt realm to server principal. */ krb5_free_data_contents(context, &server->realm); retval = krb5int_copy_data_contents(context, &tgtptr->server->data[1], &server->realm); if (retval) return retval; /* * Future work: rewrite server principal per any * supplied padata. */ } else { /* Not a TGT; punt to fallback. */ krb5_free_creds(context, *out_cred); *out_cred = NULL; break; } } DUMP_PRINC("gc_from_kdc client at fallback", client); DUMP_PRINC("gc_from_kdc server at fallback", server); /* * At this point referrals have been tried and have failed. Go * back to the server principal as originally issued and try the * conventional path. */ /* * Referrals have failed. Look up fallback realm if not * originally provided. */ if (krb5_is_referral_realm(&supplied_server->realm)) { if (server->length >= 2) { retval=krb5_get_fallback_host_realm(context, &server->data[1], &hrealms); if (retval) goto cleanup; #if 0 DPRINTF(("gc_from_kdc: using fallback realm of %s\n", hrealms[0])); #endif krb5_free_data_contents(context,&in_cred->server->realm); server->realm.data=hrealms[0]; server->realm.length=strlen(hrealms[0]); free(hrealms); } else { /* * Problem case: Realm tagged for referral but apparently not * in a <type>/<host> format that * krb5_get_fallback_host_realm can deal with. */ DPRINTF(("gc_from_kdc: referral specified " "but no fallback realm avaiable!\n")); return KRB5_ERR_HOST_REALM_UNKNOWN; } } DUMP_PRINC("gc_from_kdc server at fallback after fallback rewrite", server); /* * Get a TGT for the target realm. */ krb5_free_cred_contents(context, &tgtq); retval = tgt_mcred(context, client, server, client, &tgtq); if (retval) goto cleanup; /* Fast path: Is it in the ccache? */ /* Free tgtptr data if reused from above. */ if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); tgtptr = NULL; /* Free saved TGT in OTGTPTR if it was off-path. */ if (tgtptr_isoffpath) krb5_free_creds(context, otgtptr); otgtptr = NULL; /* Free TGTS if previously filled by do_traversal() */ if (*tgts != NULL) { for (i = 0; (*tgts)[i] != NULL; i++) { krb5_free_creds(context, (*tgts)[i]); } free(*tgts); *tgts = NULL; } context->use_conf_ktypes = 1; retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, &tgtq, &cc_tgt); if (!retval) { tgtptr = &cc_tgt; } else if (!HARD_CC_ERR(retval)) { tgtptr_isoffpath = 0; retval = do_traversal(context, ccache, client, server, &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath); } if (retval) goto cleanup; otgtptr = tgtptr; /* * Finally have TGT for target realm! Try using it to get creds. */ if (!krb5_c_valid_enctype(tgtptr->keyblock.enctype)) { retval = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); cleanup: krb5_free_cred_contents(context, &tgtq); if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); if (tgtptr_isoffpath) krb5_free_creds(context, otgtptr); context->use_conf_ktypes = old_use_conf_ktypes; /* Drop the original principal back into in_cred so that it's cached in the expected format. */ DUMP_PRINC("gc_from_kdc: final hacked server principal at cleanup", server); krb5_free_principal(context, server); in_cred->server = supplied_server; if (*out_cred && !retval) { /* Success: free server, swap supplied server back in. */ krb5_free_principal (context, (*out_cred)->server); (*out_cred)->server= out_supplied_server; } else { /* * Failure: free out_supplied_server. Don't free out_cred here * since it's either null or a referral TGT that we free below, * and we may need it to return. */ krb5_free_principal (context, out_supplied_server); } DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server); /* * Deal with ccache TGT management: If tgts has been set from * initial non-referral TGT discovery, leave it alone. Otherwise, if * referral_tgts[0] exists return it as the only entry in tgts. * (Further referrals are never cached, only the referral from the * local KDC.) This is part of cleanup because useful received TGTs * should be cached even if the main request resulted in failure. */ if (*tgts == NULL) { if (referral_tgts[0]) { #if 0 /* * This should possibly be a check on the candidate return * credential against the cache, in the circumstance where we * don't want to clutter the cache with near-duplicate * credentials on subsequent iterations. For now, it is * disabled. */ subretval=...?; if (subretval) { #endif /* Allocate returnable TGT list. */ if (!(*tgts=calloc(sizeof (krb5_creds *), 2))) return ENOMEM; subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0])); if(subretval) return subretval; (*tgts)[1]=NULL; DUMP_PRINC("gc_from_kdc: returning referral TGT for ccache", (*tgts)[0]->server); #if 0 } #endif } } /* Free referral TGTs list. */ for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) { if(referral_tgts[i]) { krb5_free_creds(context, referral_tgts[i]); } } DPRINTF(("gc_from_kdc finishing with %s\n", retval ? error_message(retval) : "no error")); return retval; }
krb5_error_code krb5_do_preauth(krb5_context context, krb5_kdc_req *request, krb5_pa_data **in_padata, krb5_pa_data ***out_padata, krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data) { int h, i, j, out_pa_list_size; int seen_etype_info2 = 0; krb5_pa_data *out_pa = NULL, **out_pa_list = NULL; krb5_data scratch; krb5_etype_info etype_info = NULL; krb5_error_code ret; static const int paorder[] = { PA_INFO, PA_REAL }; int realdone; KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() start"); if (in_padata == NULL) { *out_padata = NULL; return(0); } #ifdef DEBUG if (salt && salt->data && salt->length > 0) { fprintf (stderr, "salt len=%d", salt->length); if (salt->length > 0) fprintf (stderr, " '%*s'", salt->length, salt->data); fprintf (stderr, "; preauth data types:"); for (i = 0; in_padata[i]; i++) { fprintf (stderr, " %d", in_padata[i]->pa_type); } fprintf (stderr, "\n"); } #endif out_pa_list = NULL; out_pa_list_size = 0; /* first do all the informational preauths, then the first real one */ for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) { realdone = 0; for (i=0; in_padata[i] && !realdone; i++) { int k, l, etype_found, valid_etype_found; /* * This is really gross, but is necessary to prevent * lossge when talking to a 1.0.x KDC, which returns an * erroneous PA-PW-SALT when it returns a KRB-ERROR * requiring additional preauth. */ switch (in_padata[i]->pa_type) { case KRB5_PADATA_ETYPE_INFO: case KRB5_PADATA_ETYPE_INFO2: { krb5_preauthtype pa_type = in_padata[i]->pa_type; if (etype_info) { if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2) continue; if (pa_type == KRB5_PADATA_ETYPE_INFO2) { krb5_free_etype_info( context, etype_info); etype_info = NULL; } } scratch.length = in_padata[i]->length; scratch.data = (char *) in_padata[i]->contents; if (pa_type == KRB5_PADATA_ETYPE_INFO2) { seen_etype_info2++; ret = decode_krb5_etype_info2(&scratch, &etype_info); } else ret = decode_krb5_etype_info(&scratch, &etype_info); if (ret) { ret = 0; /*Ignore error and etype_info element*/ krb5_free_etype_info( context, etype_info); etype_info = NULL; continue; } if (etype_info[0] == NULL) { krb5_free_etype_info(context, etype_info); etype_info = NULL; break; } /* * Select first etype in our request which is also in * etype-info (preferring client request ktype order). */ for (etype_found = 0, valid_etype_found = 0, k = 0; !etype_found && k < request->nktypes; k++) { for (l = 0; etype_info[l]; l++) { if (etype_info[l]->etype == request->ktype[k]) { etype_found++; break; } /* check if program has support for this etype for more * precise error reporting. */ if (valid_enctype(etype_info[l]->etype)) valid_etype_found++; } } if (!etype_found) { KRB5_LOG(KRB5_ERR, "error !etype_found, " "valid_etype_found = %d", valid_etype_found); if (valid_etype_found) { /* supported enctype but not requested */ ret = KRB5_CONFIG_ETYPE_NOSUPP; goto cleanup; } else { /* unsupported enctype */ ret = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } } scratch.data = (char *) etype_info[l]->salt; scratch.length = etype_info[l]->length; krb5_free_data_contents(context, salt); if (scratch.length == KRB5_ETYPE_NO_SALT) salt->data = NULL; else if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0) goto cleanup; *etype = etype_info[l]->etype; krb5_free_data_contents(context, s2kparams); if ((ret = krb5int_copy_data_contents(context, &etype_info[l]->s2kparams, s2kparams)) != 0) goto cleanup; break; } case KRB5_PADATA_PW_SALT: case KRB5_PADATA_AFS3_SALT: if (etype_info) continue; break; default: ; } for (j=0; pa_types[j].type >= 0; j++) { if ((in_padata[i]->pa_type == pa_types[j].type) && (pa_types[j].flags & paorder[h])) { out_pa = NULL; if ((ret = ((*pa_types[j].fct)(context, request, in_padata[i], &out_pa, salt, s2kparams, etype, as_key, prompter, prompter_data, gak_fct, gak_data)))) { goto cleanup; } if (out_pa) { if (out_pa_list == NULL) { if ((out_pa_list = (krb5_pa_data **) malloc(2*sizeof(krb5_pa_data *))) == NULL) { ret = ENOMEM; goto cleanup; } } else { if ((out_pa_list = (krb5_pa_data **) realloc(out_pa_list, (out_pa_list_size+2)* sizeof(krb5_pa_data *))) == NULL) { /* XXX this will leak the pointers which have already been allocated. oh well. */ ret = ENOMEM; goto cleanup; } } out_pa_list[out_pa_list_size++] = out_pa; } if (paorder[h] == PA_REAL) realdone = 1; } } } } if (out_pa_list) out_pa_list[out_pa_list_size++] = NULL; *out_padata = out_pa_list; if (etype_info) krb5_free_etype_info(context, etype_info); KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); return(0); cleanup: if (out_pa_list) { out_pa_list[out_pa_list_size++] = NULL; krb5_free_pa_data(context, out_pa_list); } if (etype_info) krb5_free_etype_info(context, etype_info); KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); return (ret); }
/* Builds a request using the specified tokeninfo, value and pin. */ static krb5_error_code make_request(krb5_context ctx, krb5_otp_tokeninfo *ti, const krb5_data *value, const krb5_data *pin, krb5_pa_otp_req **out_req) { krb5_pa_otp_req *req = NULL; krb5_error_code retval = 0; if (ti == NULL) return 0; if (ti->format == KRB5_OTP_FORMAT_BASE64) return ENOTSUP; req = calloc(1, sizeof(krb5_pa_otp_req)); if (req == NULL) return ENOMEM; req->flags = ti->flags & KRB5_OTP_FLAG_NEXTOTP; retval = krb5int_copy_data_contents(ctx, &ti->vendor, &req->vendor); if (retval != 0) goto error; req->format = ti->format; retval = krb5int_copy_data_contents(ctx, &ti->token_id, &req->token_id); if (retval != 0) goto error; retval = krb5int_copy_data_contents(ctx, &ti->alg_id, &req->alg_id); if (retval != 0) goto error; retval = krb5int_copy_data_contents(ctx, value, &req->otp_value); if (retval != 0) goto error; if (ti->flags & KRB5_OTP_FLAG_COLLECT_PIN) { if (ti->flags & KRB5_OTP_FLAG_SEPARATE_PIN) { if (pin == NULL || pin->data == NULL) { retval = EINVAL; /* No pin found! */ goto error; } retval = krb5int_copy_data_contents(ctx, pin, &req->pin); if (retval != 0) goto error; } else if (pin != NULL && pin->data != NULL) { krb5_free_data_contents(ctx, &req->otp_value); retval = asprintf(&req->otp_value.data, "%.*s%.*s", pin->length, pin->data, value->length, value->data); if (retval < 0) { retval = ENOMEM; req->otp_value = empty_data(); goto error; } req->otp_value.length = req->pin.length + req->otp_value.length; } /* Otherwise, the responder has already combined them. */ } *out_req = req; return 0; error: k5_free_pa_otp_req(ctx, req); return retval; }
/* Advance the referral request loop. */ static krb5_error_code step_referrals(krb5_context context, krb5_tkt_creds_context ctx) { krb5_error_code code; const krb5_data *referral_realm; /* Possibly retry with the fallback realm on error. */ if (ctx->reply_code != 0) return try_fallback_realm(context, ctx); if (krb5_principal_compare(context, ctx->reply_creds->server, ctx->server)) { /* We got the ticket we asked for... but we didn't necessarily ask for * it with the right enctypes. Try a non-referral request if so. */ if (wrong_enctype(context, ctx->reply_creds->keyblock.enctype)) { TRACE_TKT_CREDS_WRONG_ENCTYPE(context); return begin_non_referral(context, ctx); } return complete(context, ctx); } /* Old versions of Active Directory can rewrite the server name instead of * returning a referral. Try a non-referral query if we see this. */ if (!IS_TGS_PRINC(context, ctx->reply_creds->server)) { TRACE_TKT_CREDS_NON_TGT(context, ctx->reply_creds->server); return begin_non_referral(context, ctx); } if (ctx->referral_count == 1) { /* Cache the referral TGT only if it's from the local realm. * Make sure to note the associated authdata, if any. */ code = krb5_copy_authdata(context, ctx->authdata, &ctx->reply_creds->authdata); if (code != 0) return code; (void) krb5_cc_store_cred(context, ctx->ccache, ctx->reply_creds); /* The authdata in this TGT will be copied into subsequent TGTs or the * final credentials, so we don't need to request it again. */ krb5_free_authdata(context, ctx->in_creds->authdata); ctx->in_creds->authdata = NULL; } /* Give up if we've gotten too many referral TGTs. */ if (ctx->referral_count++ >= KRB5_REFERRAL_MAXHOPS) return KRB5_KDC_UNREACH; /* Check for referral loops. */ referral_realm = &ctx->reply_creds->server->data[1]; if (seen_realm_before(context, ctx, referral_realm)) return KRB5_KDC_UNREACH; code = remember_realm(context, ctx, referral_realm); if (code != 0) return code; /* Use the referral TGT for the next request. */ krb5_free_creds(context, ctx->cur_tgt); ctx->cur_tgt = ctx->reply_creds; ctx->reply_creds = NULL; TRACE_TKT_CREDS_REFERRAL(context, ctx->cur_tgt->server); /* Rewrite the server realm to be the referral realm. */ krb5_free_data_contents(context, &ctx->server->realm); code = krb5int_copy_data_contents(context, referral_realm, &ctx->server->realm); if (code != 0) return code; /* Generate the next referral request. */ return make_request_for_service(context, ctx, TRUE); }