diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c index 8125a2eb9..6096fd78c 100644 --- a/kdc/kerberos5.c +++ b/kdc/kerberos5.c @@ -1712,6 +1712,31 @@ _kdc_as_rep(kdc_request_t r, kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", r->client_name); goto out; + } else if (ret == HDB_ERR_WRONG_REALM) { + char *fixed_client_name = NULL; + + ret = krb5_unparse_name(context, r->client->entry.principal, + &fixed_client_name); + if (ret) { + goto out; + } + + kdc_log(context, config, 0, "WRONG_REALM - %s -> %s", + r->client_name, fixed_client_name); + free(fixed_client_name); + + ret = _kdc_fast_mk_error(context, r, + &error_method, + r->armor_crypto, + &req->req_body, + KRB5_KDC_ERR_WRONG_REALM, + NULL, + r->server_princ, + NULL, + &r->client->entry.principal->realm, + NULL, NULL, + reply); + goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", r->client_name, msg); @@ -2193,7 +2218,7 @@ out: /* * In case of a non proxy error, build an error message. */ - if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE) { + if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && reply->length == 0) { ret = _kdc_fast_mk_error(context, r, &error_method, r->armor_crypto, diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index d7bddb300..6551ad961 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -1647,6 +1647,32 @@ server_lookup: if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp); goto out; + } else if (ret == HDB_ERR_WRONG_REALM) { + if (ref_realm) + free(ref_realm); + ref_realm = strdup(server->entry.principal->realm); + if (ref_realm == NULL) { + ret = ENOMEM; + goto out; + } + + kdc_log(context, config, 5, + "Returning a referral to realm %s for " + "server %s.", + ref_realm, spn); + krb5_free_principal(context, sp); + sp = NULL; + free(spn); + spn = NULL; + ret = krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, + ref_realm, NULL); + if (ret) + goto out; + ret = krb5_unparse_name(context, sp, &spn); + if (ret) + goto out; + + goto server_lookup; } else if(ret){ const char *new_rlm, *msg; Realm req_rlm; diff --git a/kdc/misc.c b/kdc/misc.c index c32ffbb45..5bd3e785d 100644 --- a/kdc/misc.c +++ b/kdc/misc.c @@ -101,6 +101,13 @@ _kdc_db_fetch(krb5_context context, config->db[i]->hdb_close(context, config->db[i]); switch (ret) { + case HDB_ERR_WRONG_REALM: + /* + * the ent->entry.principal just contains hints for the client + * to retry. This is important for enterprise principal routing + * between trusts. + */ + /* fall through */ case 0: if (db) *db = config->db[i]; diff --git a/lib/hdb/hdb_err.et b/lib/hdb/hdb_err.et index cbaa01d0d..6a79ffa29 100644 --- a/lib/hdb/hdb_err.et +++ b/lib/hdb/hdb_err.et @@ -28,5 +28,6 @@ error_code NO_WRITE_SUPPORT, "HDB backend doesn't contain write support" error_code NOT_FOUND_HERE, "The secret for this entry is not replicated to this database" error_code MISUSE, "Incorrect use of the API" error_code KVNO_NOT_FOUND, "Entry key version number not found" +error_code WRONG_REALM, "The principal exists in another realm." end