diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index 66a28edd3..534eae564 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -1518,6 +1518,7 @@ tgs_build_reply(astgs_request_t priv, server_lookup: priv->server = NULL; + priv->server_princ = sp; if (server) _kdc_free_ent(context, server); server = NULL; @@ -1560,7 +1561,25 @@ server_lookup: Realm req_rlm; krb5_realm *realms; - if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { + priv->ret = ret; /* advise policy plugin of failure reason */ + ret2 = _kdc_referral_policy(priv); + if (ret2 == 0) { + heim_assert(priv->server_princ != sp, + "Referral policy plugin must update server principal"); + + krb5_free_principal(context, sp); + sp = priv->server_princ; + + krb5_xfree(priv->sname); + priv->sname = NULL; + ret = krb5_unparse_name(context, sp, &priv->sname); + if (ret) + goto out; + spn = priv->sname; + goto server_lookup; + } else if (ret2 != KRB5_PLUGIN_NO_HANDLE) { + ret = ret2; + } else if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { if (capath == NULL) { /* With referalls, hierarchical capaths are always enabled */ ret2 = _krb5_find_capath(context, tgt->crealm, our_realm, diff --git a/kdc/windc.c b/kdc/windc.c index a63fadcfb..e7e2b9d4d 100644 --- a/kdc/windc.c +++ b/kdc/windc.c @@ -234,6 +234,27 @@ _kdc_finalize_reply(astgs_request_t r) return ret; } +static krb5_error_code KRB5_LIB_CALL +referral_policy(krb5_context context, const void *plug, void *plugctx, void *userctx) +{ + krb5plugin_windc_ftable *ft = (krb5plugin_windc_ftable *)plug; + + if (ft->referral_policy == NULL) + return KRB5_PLUGIN_NO_HANDLE; + return ft->referral_policy((void *)plug, userctx); +} + +krb5_error_code +_kdc_referral_policy(astgs_request_t r) +{ + krb5_error_code ret = KRB5_PLUGIN_NO_HANDLE; + + if (have_plugin) + ret = _krb5_plugin_run_f(r->context, &windc_plugin_data, 0, r, referral_policy); + + return ret; +} + uintptr_t KRB5_CALLCONV kdc_get_instance(const char *libname) { diff --git a/kdc/windc_plugin.h b/kdc/windc_plugin.h index b5ff8af43..2ecfe70fc 100644 --- a/kdc/windc_plugin.h +++ b/kdc/windc_plugin.h @@ -84,6 +84,20 @@ typedef krb5_error_code typedef krb5_error_code (KRB5_CALLCONV *krb5plugin_windc_finalize_reply)(void *, astgs_request_t r); +/* + * A referral policy plugin can either rewrite the server principal + * by resetting priv->server_princ, or it can disable referral + * processing entirely by returning an error. + * + * The error code from the previous server lookup is available as r->ret. + * + * If the function returns KRB5_PLUGIN_NO_HANDLE, the TGS will continue + * with its default referral handling. + */ + +typedef krb5_error_code +(KRB5_CALLCONV *krb5plugin_windc_referral_policy)(void *, astgs_request_t r); + #define KRB5_WINDC_PLUGIN_MINOR 8 #define KRB5_WINDC_PLUGING_MINOR KRB5_WINDC_PLUGIN_MINOR @@ -94,6 +108,7 @@ typedef struct krb5plugin_windc_ftable { krb5plugin_windc_pac_generate pac_generate; krb5plugin_windc_pac_verify pac_verify; krb5plugin_windc_client_access client_access; + krb5plugin_windc_referral_policy referral_policy; krb5plugin_windc_finalize_reply finalize_reply; } krb5plugin_windc_ftable; diff --git a/tests/plugin/windc.c b/tests/plugin/windc.c index 357148019..3168291ce 100644 --- a/tests/plugin/windc.c +++ b/tests/plugin/windc.c @@ -123,6 +123,7 @@ static krb5plugin_windc_ftable windc = { pac_generate, pac_verify, client_access, + NULL, /* referral_policy */ finalize_reply };