krb5 commit: Use fallback realm for GSSAPI ccache selection

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/234b64bd6139d5b75dadd5abbd5bef5a162e298a
commit 234b64bd6139d5b75dadd5abbd5bef5a162e298a
Author: Matt Rogers <[email protected]>
Date:   Fri Feb 10 12:53:42 2017 -0500

    Use fallback realm for GSSAPI ccache selection
    
    In krb5_cc_select(), if the server principal has an empty realm, use
    krb5_get_fallback_host_realm() and set the server realm to the first
    fallback found.  This helps with the selection of a non-default ccache
    when there is no [domain_realms] configuration for the server domain.
    Modify t_ccselect.py tests to account for fallback behavior.
    
    ticket: 8549 (new)

 src/lib/krb5/ccache/ccselect.c |   37 ++++++++++++++++++++++++----
 src/tests/gssapi/t_ccselect.py |   52 +++++++++++++++++++++++++++++++--------
 2 files changed, 73 insertions(+), 16 deletions(-)

diff --git a/src/lib/krb5/ccache/ccselect.c b/src/lib/krb5/ccache/ccselect.c
index 2f3071a..ee4b83a 100644
--- a/src/lib/krb5/ccache/ccselect.c
+++ b/src/lib/krb5/ccache/ccselect.c
@@ -132,6 +132,8 @@ krb5_cc_select(krb5_context context, krb5_principal server,
     struct ccselect_module_handle **hp, *h;
     krb5_ccache cache;
     krb5_principal princ;
+    krb5_principal srvcp = NULL;
+    char **fbrealms = NULL;
 
     *cache_out = NULL;
     *princ_out = NULL;
@@ -139,7 +141,27 @@ krb5_cc_select(krb5_context context, krb5_principal server,
     if (context->ccselect_handles == NULL) {
         ret = load_modules(context);
         if (ret)
-            return ret;
+            goto cleanup;
+    }
+
+    /* Try to use the fallback host realm for the server if there is no
+     * authoritative realm. */
+    if (krb5_is_referral_realm(&server->realm) &&
+        server->type == KRB5_NT_SRV_HST && server->length == 2) {
+        ret = krb5_get_fallback_host_realm(context, &server->data[1],
+                                           &fbrealms);
+        if (ret)
+            goto cleanup;
+
+        /* Make a copy with the first fallback realm. */
+        ret = krb5_copy_principal(context, server, &srvcp);
+        if (ret)
+            goto cleanup;
+        ret = krb5_set_principal_realm(context, srvcp, fbrealms[0]);
+        if (ret)
+            goto cleanup;
+
+        server = srvcp;
     }
 
     /* Consult authoritative modules first, then heuristic ones. */
@@ -155,20 +177,25 @@ krb5_cc_select(krb5_context context, krb5_principal server,
                                          princ);
                 *cache_out = cache;
                 *princ_out = princ;
-                return 0;
+                goto cleanup;
             } else if (ret == KRB5_CC_NOTFOUND) {
                 TRACE_CCSELECT_MODNOTFOUND(context, h->vt.name, server, princ);
                 *princ_out = princ;
-                return ret;
+                goto cleanup;
             } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
                 TRACE_CCSELECT_MODFAIL(context, h->vt.name, ret, server);
-                return ret;
+                goto cleanup;
             }
         }
     }
 
     TRACE_CCSELECT_NOTFOUND(context, server);
-    return KRB5_CC_NOTFOUND;
+    ret = KRB5_CC_NOTFOUND;
+
+cleanup:
+    krb5_free_principal(context, srvcp);
+    krb5_free_host_realm(context, fbrealms);
+    return ret;
 }
 
 void
diff --git a/src/tests/gssapi/t_ccselect.py b/src/tests/gssapi/t_ccselect.py
index 1ea614d..668a2cc 100755
--- a/src/tests/gssapi/t_ccselect.py
+++ b/src/tests/gssapi/t_ccselect.py
@@ -31,12 +31,18 @@ r2 = K5Realm(create_user=False, realm='KRBTEST2.COM', portbase=62000,
 
 host1 = 'p:' + r1.host_princ
 host2 = 'p:' + r2.host_princ
-
-# gsserver specifies the target as a GSS name.  The resulting
-# principal will have the host-based type, but the realm won't be
-# known before the client cache is selected (since k5test realms have
-# no domain-realm mapping by default).
-gssserver = 'h:host@' + hostname
+foo = 'foo.krbtest.com'
+foo2 = 'foo.krbtest2.com'
+
+# These strings specify the target as a GSS name.  The resulting
+# principal will have the host-based type, with the referral realm
+# (since k5test realms have no domain-realm mapping by default).
+# krb5_cc_select() will use the fallback realm, which is either the
+# uppercased parent domain, or the default realm if the hostname is a
+# single component.
+gssserver = 'h:host@' + foo
+gssserver2 = 'h:host@' + foo2
+gsslocal = 'h:host@localhost'
 
 # refserver specifies the target as a principal in the referral realm.
 # The principal won't be treated as a host principal by the
@@ -66,6 +72,16 @@ r1.addprinc(alice, password('alice'))
 r1.addprinc(bob, password('bob'))
 r2.addprinc(zaphod, password('zaphod'))
 
+# Create host principals and keytabs for fallback realm tests.
+r1.addprinc('host/localhost')
+r2.addprinc('host/localhost')
+r1.addprinc('host/' + foo)
+r2.addprinc('host/' + foo2)
+r1.extract_keytab('host/localhost', r1.keytab)
+r2.extract_keytab('host/localhost', r2.keytab)
+r1.extract_keytab('host/' + foo, r1.keytab)
+r2.extract_keytab('host/' + foo2, r2.keytab)
+
 # Get tickets for one user in each realm (zaphod will be primary).
 r1.kinit(alice, password('alice'))
 r2.kinit(zaphod, password('zaphod'))
@@ -93,10 +109,24 @@ if output != (zaphod + '\n'):
     fail('zaphod not chosen as default initiator name for server in r1')
 
 # Check that primary cache is used if server realm is unknown.
-output = r2.run(['./t_ccselect', gssserver])
+output = r2.run(['./t_ccselect', refserver])
 if output != (zaphod + '\n'):
     fail('zaphod not chosen via primary cache for unknown server realm')
-r1.run(['./t_ccselect', gssserver], expected_code=1)
+r1.run(['./t_ccselect', gssserver2], expected_code=1)
+# Check ccache selection using a fallback realm.
+output = r1.run(['./t_ccselect', gssserver])
+if output != (alice + '\n'):
+    fail('alice not chosen via parent domain fallback')
+output = r2.run(['./t_ccselect', gssserver2])
+if output != (zaphod + '\n'):
+    fail('zaphod not chosen via parent domain fallback')
+# Check ccache selection using a fallback realm (default realm).
+output = r1.run(['./t_ccselect', gsslocal])
+if output != (alice + '\n'):
+    fail('alice not chosen via default realm fallback')
+output = r2.run(['./t_ccselect', gsslocal])
+if output != (zaphod + '\n'):
+    fail('zaphod not chosen via default realm fallback')
 
 # Get a second cred in r1 (bob will be primary).
 r1.kinit(bob, password('bob'))
@@ -104,19 +134,19 @@ r1.kinit(bob, password('bob'))
 # Try some cache selections using .k5identity.
 k5id = open(os.path.join(r1.testdir, '.k5identity'), 'w')
 k5id.write('%s realm=%s\n' % (alice, r1.realm))
-k5id.write('%s service=ho*t host=%s\n' % (zaphod, hostname))
+k5id.write('%s service=ho*t host=localhost\n' % zaphod)
 k5id.write('noprinc service=bogus')
 k5id.close()
 output = r1.run(['./t_ccselect', host1])
 if output != (alice + '\n'):
     fail('alice not chosen via .k5identity realm line.')
-output = r2.run(['./t_ccselect', gssserver])
+output = r2.run(['./t_ccselect', gsslocal])
 if output != (zaphod + '\n'):
     fail('zaphod not chosen via .k5identity service/host line.')
 output = r1.run(['./t_ccselect', refserver])
 if output != (bob + '\n'):
     fail('bob not chosen via primary cache when no .k5identity line matches.')
-r1.run(['./t_ccselect', 'h:bogus@' + hostname], expected_code=1,
+r1.run(['./t_ccselect', 'h:bogus@' + foo2], expected_code=1,
        expected_msg="Can't find client principal noprinc")
 
 success('GSSAPI credential selection tests')
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.