static void store_tgts(krb5_context context, krb5_ccache ccache, krb5_creds **tgts) { size_t n; for (n = 0; tgts && tgts[n]; n++) { krb5_const_principal server = tgts[n]->server; if (krb5_principal_is_krbtgt(context, server) && strcmp(server->name.name_string.val[1], server->realm) != 0) krb5_cc_store_cred(context, ccache, tgts[n]); } for (n = 0; tgts && tgts[n]; n++) krb5_free_creds(context, tgts[n]); }
static krb5_error_code get_cred_kdc_referral(krb5_context context, krb5_kdc_flags flags, krb5_ccache ccache, krb5_creds *in_creds, krb5_principal impersonate_principal, Ticket *second_ticket, krb5_creds **out_creds, krb5_creds ***ret_tgts) { krb5_const_realm client_realm; krb5_error_code ret; krb5_creds tgt, referral, ticket; int loop = 0; int ok_as_delegate = 1; if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) { krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, N_("Name too short to do referals, skipping", "")); return KRB5KDC_ERR_PATH_NOT_ACCEPTED; } memset(&tgt, 0, sizeof(tgt)); memset(&ticket, 0, sizeof(ticket)); flags.b.canonicalize = 1; *out_creds = NULL; client_realm = krb5_principal_get_realm(context, in_creds->client); /* find tgt for the clients base realm */ { krb5_principal tgtname; ret = krb5_make_principal(context, &tgtname, client_realm, KRB5_TGS_NAME, client_realm, NULL); if(ret) return ret; ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt); krb5_free_principal(context, tgtname); if (ret) return ret; } referral = *in_creds; ret = krb5_copy_principal(context, in_creds->server, &referral.server); if (ret) { krb5_free_cred_contents(context, &tgt); return ret; } ret = krb5_principal_set_realm(context, referral.server, client_realm); if (ret) { krb5_free_cred_contents(context, &tgt); krb5_free_principal(context, referral.server); return ret; } while (loop++ < 17) { krb5_creds **tickets; krb5_creds mcreds; char *referral_realm; /* Use cache if we are not doing impersonation or contrainte deleg */ if (impersonate_principal == NULL || flags.b.constrained_delegation) { krb5_cc_clear_mcred(&mcreds); mcreds.server = referral.server; ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket); } else ret = EINVAL; if (ret) { ret = get_cred_kdc_address(context, ccache, flags, NULL, &referral, &tgt, impersonate_principal, second_ticket, &ticket); if (ret) goto out; } /* Did we get the right ticket ? */ if (krb5_principal_compare_any_realm(context, referral.server, ticket.server)) break; if (!krb5_principal_is_krbtgt(context, ticket.server)) { krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, N_("Got back an non krbtgt " "ticket referrals", "")); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } referral_realm = ticket.server->name.name_string.val[1]; /* check that there are no referrals loops */ tickets = *ret_tgts; krb5_cc_clear_mcred(&mcreds); mcreds.server = ticket.server; while(tickets && *tickets){ if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, &mcreds, *tickets)) { krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, N_("Referral from %s " "loops back to realm %s", ""), tgt.server->realm, referral_realm); ret = KRB5_GET_IN_TKT_LOOP; goto out; } tickets++; } /* * if either of the chain or the ok_as_delegate was stripped * by the kdc, make sure we strip it too. */ if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) { ok_as_delegate = 0; ticket.flags.b.ok_as_delegate = 0; } ret = add_cred(context, &ticket, ret_tgts); if (ret) goto out; /* try realm in the referral */ ret = krb5_principal_set_realm(context, referral.server, referral_realm); krb5_free_cred_contents(context, &tgt); tgt = ticket; memset(&ticket, 0, sizeof(ticket)); if (ret) goto out; } ret = krb5_copy_creds(context, &ticket, out_creds); out: krb5_free_principal(context, referral.server); krb5_free_cred_contents(context, &tgt); krb5_free_cred_contents(context, &ticket); return ret; }
static krb5_error_code tkt_referral_recv(krb5_context context, krb5_tkt_creds_context ctx, krb5_data *in, krb5_data *out, krb5_realm *realm, unsigned int *flags) { krb5_error_code ret; krb5_creds outcred, mcred; unsigned long n; _krb5_debugx(context, 10, "tkt_referral_recv: %s", ctx->server_name); memset(&outcred, 0, sizeof(outcred)); ret = parse_tgs_rep(context, ctx, in, &outcred); if (ret) { _krb5_debugx(context, 10, "tkt_referral_recv: parse_tgs_rep %d", ret); tkt_reset(context, ctx); ctx->state = tkt_capath_init; return 0; } /* * Check if we found the right ticket */ if (krb5_principal_compare_any_realm(context, ctx->next.server, outcred.server)) { ret = krb5_copy_creds(context, &outcred, &ctx->cred); if (ret) return (ctx->error = ret); krb5_free_cred_contents(context, &outcred); ctx->state = tkt_store; return 0; } if (!krb5_principal_is_krbtgt(context, outcred.server)) { krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, N_("Got back an non krbtgt " "ticket referrals", "")); krb5_free_cred_contents(context, &outcred); ctx->state = tkt_capath_init; return 0; } _krb5_debugx(context, 10, "KDC for realm %s sends a referrals to %s", ctx->tgt.server->realm, outcred.server->name.name_string.val[1]); /* * check if there is a loop */ krb5_cc_clear_mcred(&mcred); mcred.server = outcred.server; for (n = 0; ctx->tickets && ctx->tickets[n]; n++) { if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, &mcred, ctx->tickets[n])) { _krb5_debugx(context, 5, "Referral from %s loops back to realm %s", ctx->tgt.server->realm, outcred.server->realm); ctx->state = tkt_capath_init; return 0; } } #define MAX_KDC_REFERRALS_LOOPS 15 if (n > MAX_KDC_REFERRALS_LOOPS) { ctx->state = tkt_capath_init; return 0; } /* * filter out ok-as-delegate if needed */ if (ctx->ok_as_delegate == 0 || outcred.flags.b.ok_as_delegate == 0) { ctx->ok_as_delegate = 0; outcred.flags.b.ok_as_delegate = 0; } /* add to iteration cache */ ret = add_cred(context, &outcred, &ctx->tickets); if (ret) { ctx->state = tkt_capath_init; return 0; } /* set up next server to talk to */ krb5_free_cred_contents(context, &ctx->tgt); ctx->tgt = outcred; /* * Setup next target principal to target */ ret = krb5_principal_set_realm(context, ctx->next.server, ctx->tgt.server->realm); if (ret) { ctx->state = tkt_capath_init; return 0; } ctx->state = tkt_referral_send; return 0; }
static krb5_error_code check_server_referral(krb5_context context, krb5_kdc_rep *rep, unsigned flags, krb5_const_principal requested, krb5_const_principal returned, krb5_keyblock * key) { krb5_error_code ret; PA_ServerReferralData ref; krb5_crypto session; EncryptedData ed; size_t len; krb5_data data; PA_DATA *pa; int i = 0, cmp; if (rep->kdc_rep.padata == NULL) goto noreferral; pa = krb5_find_padata(rep->kdc_rep.padata->val, rep->kdc_rep.padata->len, KRB5_PADATA_SERVER_REFERRAL, &i); if (pa == NULL) goto noreferral; memset(&ed, 0, sizeof(ed)); memset(&ref, 0, sizeof(ref)); ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &ed, &len); if (ret) return ret; if (len != pa->padata_value.length) { free_EncryptedData(&ed); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Referral EncryptedData wrong for realm %s", "realm"), requested->realm); return KRB5KRB_AP_ERR_MODIFIED; } ret = krb5_crypto_init(context, key, 0, &session); if (ret) { free_EncryptedData(&ed); return ret; } ret = krb5_decrypt_EncryptedData(context, session, KRB5_KU_PA_SERVER_REFERRAL, &ed, &data); free_EncryptedData(&ed); krb5_crypto_destroy(context, session); if (ret) return ret; ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len); if (ret) { krb5_data_free(&data); return ret; } krb5_data_free(&data); if (strcmp(requested->realm, returned->realm) != 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("server ref realm mismatch, " "requested realm %s got back %s", ""), requested->realm, returned->realm); return KRB5KRB_AP_ERR_MODIFIED; } if (krb5_principal_is_krbtgt(context, returned)) { const char *realm = returned->name.name_string.val[1]; if (ref.referred_realm == NULL || strcmp(*ref.referred_realm, realm) != 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("tgt returned with wrong ref", "")); return KRB5KRB_AP_ERR_MODIFIED; } } else if (krb5_principal_compare(context, returned, requested) == 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("req princ no same as returned", "")); return KRB5KRB_AP_ERR_MODIFIED; } if (ref.requested_principal_name) { cmp = _krb5_principal_compare_PrincipalName(context, requested, ref.requested_principal_name); if (!cmp) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("referred principal not same " "as requested", "")); return KRB5KRB_AP_ERR_MODIFIED; } } else if (flags & EXTRACT_TICKET_AS_REQ) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Requested principal missing on AS-REQ", "")); return KRB5KRB_AP_ERR_MODIFIED; } free_PA_ServerReferralData(&ref); return ret; noreferral: /* * Expect excact match or that we got a krbtgt */ if (krb5_principal_compare(context, requested, returned) != TRUE && (krb5_realm_compare(context, requested, returned) != TRUE && krb5_principal_is_krbtgt(context, returned) != TRUE)) { krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Not same server principal returned " "as requested", "")); return KRB5KRB_AP_ERR_MODIFIED; } return 0; }