]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
Support AD style kerberos automatically in rpc.gss
authorJason Gunthorpe <jgunthorpe@obsidianresearch.com>
Wed, 9 Feb 2011 16:27:19 +0000 (11:27 -0500)
committerSteve Dickson <steved@redhat.com>
Wed, 9 Feb 2011 16:33:32 +0000 (11:33 -0500)
An Active Directory KDC will only grant a TGT for UPNs, getting
a TGT for SPNs is not possible:

$ kinit -k host/ib5@ADS.ORCORP.CA
kinit: Client not found in Kerberos database while getting initial
credentials

The correct thing to do for machine credentials is to get a TGT
for the computer UPN <HOSTNAME>$@REALM:
$ kinit -k IB5\$
$ klist
12/22/10 11:43:47  12/22/10 21:43:47  krbtgt/ADS.ORCORP.CA@ADS.ORCORP.CA

Samba automatically creates /etc/krb5.keytab entry for the computer UPN,
this patch makes gssd_refresh_krb5_machine_credential prefer it above
the SPNs if it is present.

The net result is that nfs client works automatically out of the box
if samba has been used to setup kerberos via 'net ads join' 'net ads
keytab create'

Tested using Windows Server 2003 R2 as the AD server.

Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
utils/gssd/gssd.man
utils/gssd/krb5_util.c

index 0a23cd6316745926d30d6237e399383564a10c5c..073379d37597103e7d416307e90061d5a043c5c8 100644 (file)
@@ -52,6 +52,8 @@ used only "nfs/*" keys found within the keytab.
 To be more consistent with other implementations, we now look for
 specific keytab entries.  The search order for keytabs to be used
 for "machine credentials" is now:
+.br
+  <HOSTNAME>$@<REALM>
 .br
   root/<hostname>@<REALM>
 .br
@@ -64,6 +66,9 @@ for "machine credentials" is now:
   nfs/<anyname>@<REALM>
 .br
   host/<anyname>@<REALM>
+.IP
+If this search order does not use the correct key then provide a
+keytab file that contains only correct keys.
 .TP
 .B -p path
 Tells
index f071600de4adbfb0667b6cf81f02c892ca9884aa..4b13fa1f5c2ac2e1fd64d2036fe0c6c6ad1a0e63 100644 (file)
@@ -768,6 +768,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
        krb5_error_code code;
        char **realmnames = NULL;
        char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
+       char myhostad[NI_MAXHOST+1];
        int i, j, retval;
        char *default_realm = NULL;
        char *realm;
@@ -789,6 +790,14 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
                printerr(1, "%s while getting local hostname\n", k5err);
                goto out;
        }
+
+       /* Compute the active directory machine name HOST$ */
+       strcpy(myhostad, myhostname);
+       for (i = 0; myhostad[i] != 0; ++i)
+               myhostad[i] = toupper(myhostad[i]);
+       myhostad[i] = '$';
+       myhostad[i+1] = 0;
+
        retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
        if (retval)
                goto out;
@@ -833,32 +842,47 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
                if (strcmp(realm, default_realm) == 0)
                        tried_default = 1;
                for (j = 0; svcnames[j] != NULL; j++) {
-                       code = krb5_build_principal_ext(context, &princ,
-                                                       strlen(realm),
-                                                       realm,
-                                                       strlen(svcnames[j]),
-                                                       svcnames[j],
-                                                       strlen(myhostname),
-                                                       myhostname,
-                                                       NULL);
+                       char spn[300];
+
+                       /*
+                        * The special svcname "$" means 'try the active
+                        * directory machine account'
+                        */
+                       if (strcmp(svcnames[j],"$") == 0) {
+                               snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm);
+                               code = krb5_build_principal_ext(context, &princ,
+                                                               strlen(realm),
+                                                               realm,
+                                                               strlen(myhostad),
+                                                               myhostad,
+                                                               NULL);
+                       } else {
+                               snprintf(spn, sizeof(spn), "%s/%s@%s",
+                                        svcnames[j], myhostname, realm);
+                               code = krb5_build_principal_ext(context, &princ,
+                                                               strlen(realm),
+                                                               realm,
+                                                               strlen(svcnames[j]),
+                                                               svcnames[j],
+                                                               strlen(myhostname),
+                                                               myhostname,
+                                                               NULL);
+                       }
+
                        if (code) {
                                k5err = gssd_k5_err_msg(context, code);
-                               printerr(1, "%s while building principal for "
-                                        "'%s/%s@%s'\n", k5err, svcnames[j],
-                                        myhostname, realm);
+                               printerr(1, "%s while building principal for '%s'\n",
+                                        k5err, spn);
                                continue;
                        }
                        code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
                        krb5_free_principal(context, princ);
                        if (code) {
                                k5err = gssd_k5_err_msg(context, code);
-                               printerr(3, "%s while getting keytab entry for "
-                                        "'%s/%s@%s'\n", k5err, svcnames[j],
-                                        myhostname, realm);
+                               printerr(3, "%s while getting keytab entry for '%s'\n",
+                                        k5err, spn);
                        } else {
-                               printerr(3, "Success getting keytab entry for "
-                                        "'%s/%s@%s'\n",
-                                        svcnames[j], myhostname, realm);
+                               printerr(3, "Success getting keytab entry for '%s'\n",spn);
                                retval = 0;
                                goto out;
                        }
@@ -870,6 +894,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
                 */
                for (j = 0; svcnames[j] != NULL; j++) {
                        int found = 0;
+                       if (strcmp(svcnames[j],"$") == 0)
+                               continue;
                        code = gssd_search_krb5_keytab(context, kt, realm,
                                                       svcnames[j], &found, kte);
                        if (!code && found) {
@@ -1160,7 +1186,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
        krb5_keytab kt = NULL;;
        int retval = 0;
        char *k5err = NULL;
-       const char *svcnames[4] = { "root", "nfs", "host", NULL };
+       const char *svcnames[5] = { "$", "root", "nfs", "host", NULL };
 
        /*
         * If a specific service name was specified, use it.