gssd: process service= attribute in new upcall nfs-utils-1-2-2-rc1
authorOlga Kornievskaia <aglo@citi.umich.edu>
Mon, 16 Nov 2009 14:44:03 +0000 (09:44 -0500)
committerSteve Dickson <steved@redhat.com>
Mon, 16 Nov 2009 14:44:03 +0000 (09:44 -0500)
Add processing of the "service=" attribute in the new gssd upcall.

If "service" is specified, then the kernel is indicating that
we must use machine credentials for this request.  (Regardless
of the uid value or the setting of root_uses_machine_creds.)
If the service value is "*", then any service name can be used.
Otherwise, it specifies the service name that should be used.
(For now, the values of service will only be "*" or "nfs".)

Restricting gssd to use "nfs" service name is needed for when
the NFS server is doing a callback to the NFS client.  In this
case, the NFS server has to authenticate itself as "nfs" --
even if there are other service keys such as "host" or "root"
in the keytab.

Another case when the kernel may specify the service attribute
is when gssd is being asked to create the context for a
SETCLIENT_ID operation.  In this case, machine credentials
must be used for the authentication.  However, the service name
used for this case is not important.

Signed-off-by: Olga Kornievskaia <aglo@citi.umich.edu>
Signed-off-by: Kevin Coffman <kwc@citi.umich.edu>
Signed-off-by: Steve Dickson <steved@redhat.com>
utils/gssd/gssd_proc.c
utils/gssd/krb5_util.c
utils/gssd/krb5_util.h

index 799a207..33a31c3 100644 (file)
@@ -883,7 +883,8 @@ int create_auth_rpc_client(struct clnt_info *clp,
  * context on behalf of the kernel
  */
 static void
-process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname)
+process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
+                   char *service)
 {
        CLIENT                  *rpc_clnt = NULL;
        AUTH                    *auth = NULL;
@@ -906,7 +907,31 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname)
        token.value = NULL;
        memset(&pd, 0, sizeof(struct authgss_private_data));
 
-       if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
+       /*
+        * If "service" is specified, then the kernel is indicating that
+        * we must use machine credentials for this request.  (Regardless
+        * of the uid value or the setting of root_uses_machine_creds.)
+        * If the service value is "*", then any service name can be used.
+        * Otherwise, it specifies the service name that should be used.
+        * (For now, the values of service will only be "*" or "nfs".)
+        *
+        * Restricting gssd to use "nfs" service name is needed for when
+        * the NFS server is doing a callback to the NFS client.  In this
+        * case, the NFS server has to authenticate itself as "nfs" --
+        * even if there are other service keys such as "host" or "root"
+        * in the keytab.
+        *
+        * Another case when the kernel may specify the service attribute
+        * is when gssd is being asked to create the context for a
+        * SETCLIENT_ID operation.  In this case, machine credentials
+        * must be used for the authentication.  However, the service name
+        * used for this case is not important.
+        *
+        */
+       printerr(2, "%s: service is '%s'\n", __func__,
+                service ? service : "<null>");
+       if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
+                               service == NULL)) {
                /* Tell krb5 gss which credentials cache to use */
                for (dirname = ccachesearch; *dirname != NULL; dirname++) {
                        if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
@@ -917,12 +942,13 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname)
                }
        }
        if (create_resp != 0) {
-               if (uid == 0 && root_uses_machine_creds == 1) {
+               if (uid == 0 && (root_uses_machine_creds == 1 ||
+                               service != NULL)) {
                        int nocache = 0;
                        int success = 0;
                        do {
                                gssd_refresh_krb5_machine_credential(clp->servername,
-                                                                    NULL, nocache);
+                                                                    NULL, service);
                                /*
                                 * Get a list of credential cache names and try each
                                 * of them until one works or we've tried them all
@@ -1066,7 +1092,7 @@ handle_krb5_upcall(struct clnt_info *clp)
                return;
        }
 
-       return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL);
+       return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
 }
 
 void
@@ -1092,6 +1118,7 @@ handle_gssd_upcall(struct clnt_info *clp)
        char                    *p;
        char                    *mech = NULL;
        char                    *target = NULL;
+       char                    *service = NULL;
 
        printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
 
@@ -1148,8 +1175,30 @@ handle_gssd_upcall(struct clnt_info *clp)
                }
        }
 
+       /*
+        * read the service name
+        *
+        * The presence of attribute "service=" indicates that machine
+        * credentials should be used for this request.  If the value
+        * is "*", then any machine credentials available can be used.
+        * If the value is anything else, then machine credentials for
+        * the specified service name (always "nfs" for now) should be
+        * used.
+        */
+       if ((p = strstr(lbuf, "service=")) != NULL) {
+               service = malloc(lbuflen);
+               if (!service)
+                       goto out;
+               if (sscanf(p, "service=%s", service) != 1) {
+                       printerr(0, "WARNING: handle_gssd_upcall: "
+                                   "failed to parse service type "
+                                   "in upcall string '%s'\n", lbuf);
+                       goto out;
+               }
+       }
+
        if (strcmp(mech, "krb5") == 0)
-               process_krb5_upcall(clp, uid, clp->gssd_fd, target);
+               process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
        else if (strcmp(mech, "spkm3") == 0)
                process_spkm3_upcall(clp, uid, clp->gssd_fd);
        else
@@ -1160,6 +1209,7 @@ out:
        free(lbuf);
        free(mech);
        free(target);
+       free(service);
        return; 
 }
 
index 78e9775..c3c131b 100644 (file)
@@ -797,10 +797,9 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
  */
 static int
 find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
-                 krb5_keytab_entry *kte)
+                 krb5_keytab_entry *kte, const char **svcnames)
 {
        krb5_error_code code;
-       const char *svcnames[] = { "root", "nfs", "host", NULL };
        char **realmnames = NULL;
        char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
        int i, j, retval;
@@ -1096,7 +1095,8 @@ gssd_get_krb5_machine_cred_list(char ***list)
        for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
                if (ple->ccname) {
                        /* Make sure cred is up-to-date before returning it */
-                       retval = gssd_refresh_krb5_machine_credential(NULL, ple, 0);
+                       retval = gssd_refresh_krb5_machine_credential(NULL, ple,
+                               NULL);
                        if (retval)
                                continue;
                        if (i + 1 > listsize) {
@@ -1186,14 +1186,24 @@ gssd_destroy_krb5_machine_creds(void)
  */
 int
 gssd_refresh_krb5_machine_credential(char *hostname,
-                                    struct gssd_k5_kt_princ *ple, int nocache)
+                                    struct gssd_k5_kt_princ *ple, 
+                                        char *service)
 {
        krb5_error_code code = 0;
        krb5_context context;
        krb5_keytab kt = NULL;;
        int retval = 0;
        char *k5err = NULL;
+       const char *svcnames[4] = { "root", "nfs", "host", NULL };
 
+       /*
+        * If a specific service name was specified, use it.
+        * Otherwise, use the default list.
+        */
+       if (service != NULL && strcmp(service, "*") != 0) {
+               svcnames[0] = service;
+               svcnames[1] = NULL;
+       }
        if (hostname == NULL && ple == NULL)
                return EINVAL;
 
@@ -1216,7 +1226,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
        if (ple == NULL) {
                krb5_keytab_entry kte;
 
-               code = find_keytab_entry(context, kt, hostname, &kte);
+               code = find_keytab_entry(context, kt, hostname, &kte, svcnames);
                if (code) {
                        printerr(0, "ERROR: %s: no usable keytab entry found "
                                 "in keytab %s for connection with host %s\n",
@@ -1241,7 +1251,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
                        goto out;
                }
        }
-       retval = gssd_get_single_krb5_cred(context, kt, ple, nocache);
+       retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
 out:
        if (kt)
                krb5_kt_close(context, kt);
index 4b6b281..4602cc3 100644 (file)
@@ -30,7 +30,8 @@ void gssd_free_krb5_machine_cred_list(char **list);
 void gssd_setup_krb5_machine_gss_ccache(char *servername);
 void gssd_destroy_krb5_machine_creds(void);
 int  gssd_refresh_krb5_machine_credential(char *hostname,
-                                         struct gssd_k5_kt_princ *ple, int nocache);
+                                         struct gssd_k5_kt_princ *ple, 
+                                         char *service);
 char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
 void gssd_k5_get_default_realm(char **def_realm);