heimdal:kdc: add support for HDB_ERR_WRONG_REALM
authorStefan Metzmacher <metze@samba.org>
Tue, 10 Feb 2015 13:37:29 +0000 (14:37 +0100)
committerStefan Metzmacher <metze@samba.org>
Wed, 8 Jul 2015 16:38:20 +0000 (18:38 +0200)
A backend can return this if asked with HDB_F_GET_CLIENT|HDB_F_FOR_AS_REQ
for a KRB5_NT_ENTERPRISE_PRINCIPAL record or for HDB_F_GET_SERVER | HDB_F_FOR_TGS_REQ.

entry_ex->entry.principal->realm needs to return the real realm of the principal
(or at least a the realm of the next cross-realm trust hop).

This is needed to route enterprise principals between AD domain trusts.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/heimdal/kdc/kerberos5.c
source4/heimdal/kdc/krb5tgs.c
source4/heimdal/kdc/misc.c
source4/heimdal/lib/hdb/hdb_err.et

index cb97390d3e3a89a028c3a587a35db73a54bccc10..7e7aefd21e5343db50cb4fa12319777ff8117732 100644 (file)
@@ -1060,6 +1060,30 @@ _kdc_as_rep(krb5_context context,
     if(ret == HDB_ERR_NOT_FOUND_HERE) {
        kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name);
        goto out;
+    } else if (ret == HDB_ERR_WRONG_REALM) {
+       char *fixed_client_name = NULL;
+
+       ret = krb5_unparse_name(context, client->entry.principal,
+                               &fixed_client_name);
+       if (ret) {
+           goto out;
+       }
+
+       kdc_log(context, config, 0, "WRONG_REALM - %s -> %s",
+               client_name, fixed_client_name);
+       free(fixed_client_name);
+
+       ret = krb5_mk_error_ext(context,
+                               KRB5_KDC_ERR_WRONG_REALM,
+                               NULL, /* e_text */
+                               NULL, /* e_data */
+                               server_princ,
+                               NULL, /* client_name */
+                               &client->entry.principal->realm,
+                               NULL, /* client_time */
+                               NULL, /* client_usec */
+                               reply);
+       goto out;
     } else if(ret){
        const char *msg = krb5_get_error_message(context, ret);
        kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg);
@@ -1779,7 +1803,7 @@ _kdc_as_rep(krb5_context context,
 
 out:
     free_AS_REP(&rep);
-    if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE){
+    if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && reply->length == 0) {
        krb5_mk_error(context,
                      ret,
                      e_text,
index ca589e87fa0e5e49c5af4dd9f5e7bafe6f6ddb15..1d0d1ddc3fa6ffc802be0edd28316bf8173ff1c7 100644 (file)
@@ -1616,6 +1616,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;
index 4ef5439cf145b567e82185a033c9f4b92ac4ffd9..b0bc38a2b6f6ce5411744e491ab1be43846092f0 100644 (file)
@@ -99,6 +99,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];
index 0bdcb385f6bc1b84f69985e696b7761931d5ec7a..135747b056498f0acbf195f6e83c4e505c0507c8 100644 (file)
@@ -27,5 +27,6 @@ error_code MANDATORY_OPTION,  "Entry contains unknown mandatory extension"
 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 WRONG_REALM,                "The principal exists in another realm."
 
 end