]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
gssd: add upcall support for callback authentication
authorOlga Kornievskaia <aglo@citi.umich.edu>
Mon, 16 Nov 2009 14:15:25 +0000 (09:15 -0500)
committerSteve Dickson <steved@redhat.com>
Mon, 16 Nov 2009 14:15:25 +0000 (09:15 -0500)
Change the processing so that all subdirectories within the rpc_pipefs
directory are treated equally.  Any "clnt" directories that show up
within any of them are processed.  (As suggested by Bruce Fields.)

Note that the callback authentication will create a new "nfs4d_cb"
subdirectory.  Only new kernels (2.6.29) will create this new directory.
(The need for this directory will go away with NFSv4.1 where the
callback can be done on the same connection as the fore-channel.)

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

index 40a2b4d7e14d1e2e2256d9ea5950fcde1087fbe7..bd37a5f48d89f7d57a799ba833c7cfb78d7511bd 100644 (file)
@@ -56,7 +56,6 @@
 #include "krb5_util.h"
 
 char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
-char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
 char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
 char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
 char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1];
@@ -159,11 +158,6 @@ main(int argc, char *argv[])
        if (preferred_realm == NULL)
                gssd_k5_get_default_realm(&preferred_realm);
 
-       snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s",
-                pipefs_dir, GSSD_SERVICE_NAME);
-       if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0')
-               errx(1, "pipefs_nfsdir path name too long");
-
        if ((progname = strrchr(argv[0], '/')))
                progname++;
        else
index 3c52f46699de9ee1bd2ddcd6388f460b17ebf943..3c53a88dd79bf4072443f4062cc61ecb75f5ec15 100644 (file)
@@ -60,7 +60,6 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUTHTYPE_LIPKEY};
 
 
 extern char                    pipefs_dir[PATH_MAX];
-extern char                    pipefs_nfsdir[PATH_MAX];
 extern char                    keytabfile[PATH_MAX];
 extern char                    *ccachesearch[];
 extern int                     use_memcache;
@@ -86,6 +85,14 @@ struct clnt_info {
        struct sockaddr_storage addr;
 };
 
+TAILQ_HEAD(topdirs_list_head, topdirs_info) topdirs_list;
+
+struct topdirs_info {
+       TAILQ_ENTRY(topdirs_info)   list;
+       char                    *dirname;
+       int                     fd;
+};
+
 void init_client_list(void);
 int update_client_list(void);
 void handle_krb5_upcall(struct clnt_info *clp);
index 397fd1461f3c873077ecbd7c2f78548bf8b0ea9e..b5117c57cc2f3e5f7856d1b42924628bf297cdf6 100644 (file)
@@ -49,6 +49,7 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #include "gssd.h"
 #include "err_util.h"
@@ -98,12 +99,85 @@ scan_poll_results(int ret)
        }
 };
 
+static int
+topdirs_add_entry(struct dirent *dent)
+{
+       struct topdirs_info *tdi;
+
+       tdi = calloc(sizeof(struct topdirs_info), 1);
+       if (tdi == NULL) {
+               printerr(0, "ERROR: Couldn't allocate struct topdirs_info\n");
+               return -1;
+       }
+       tdi->dirname = malloc(PATH_MAX);
+       if (tdi->dirname == NULL) {
+               printerr(0, "ERROR: Couldn't allocate directory name\n");
+               free(tdi);
+               return -1;
+       }
+       snprintf(tdi->dirname, PATH_MAX, "%s/%s", pipefs_dir, dent->d_name);
+       tdi->fd = open(tdi->dirname, O_RDONLY);
+       if (tdi->fd != -1) {
+               fcntl(tdi->fd, F_SETSIG, DNOTIFY_SIGNAL);
+               fcntl(tdi->fd, F_NOTIFY,
+                     DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+       }
+
+       TAILQ_INSERT_HEAD(&topdirs_list, tdi, list);
+       return 0;
+}
+
+static void
+topdirs_free_list(void)
+{
+       struct topdirs_info *tdi;
+
+       TAILQ_FOREACH(tdi, &topdirs_list, list) {
+               free(tdi->dirname);
+               if (tdi->fd != -1)
+                       close(tdi->fd);
+               TAILQ_REMOVE(&topdirs_list, tdi, list);
+               free(tdi);
+       }
+}
+
+static int
+topdirs_init_list(void)
+{
+       DIR             *pipedir;
+       struct dirent   *dent;
+       int             ret;
+
+       TAILQ_INIT(&topdirs_list);
+
+       pipedir = opendir(pipefs_dir);
+       if (pipedir == NULL) {
+               printerr(0, "ERROR: could not open rpc_pipefs directory '%s': "
+                        "%s\n", pipefs_dir, strerror(errno));
+               return -1;
+       }
+       for (dent = readdir(pipedir); dent != NULL; dent = readdir(pipedir)) {
+               if (dent->d_type != DT_DIR ||
+                   strcmp(dent->d_name, ".") == 0  ||
+                   strcmp(dent->d_name, "..") == 0) {
+                       continue;
+               }
+               ret = topdirs_add_entry(dent);
+               if (ret)
+                       goto out_err;
+       }
+       closedir(pipedir);
+       return 0;
+out_err:
+       topdirs_free_list();
+       return -1;
+}
+
 void
 gssd_run()
 {
        int                     ret;
        struct sigaction        dn_act;
-       int                     fd;
        sigset_t                set;
 
        /* Taken from linux/Documentation/dnotify.txt: */
@@ -117,13 +191,8 @@ gssd_run()
        sigaddset(&set, DNOTIFY_SIGNAL);
        sigprocmask(SIG_UNBLOCK, &set, NULL);
 
-       if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) {
-               printerr(0, "ERROR: failed to open %s: %s\n",
-                        pipefs_nfsdir, strerror(errno));
-               exit(1);
-       }
-       fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
-       fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+       if (topdirs_init_list() != 0)
+               return;
 
        init_client_list();
 
@@ -150,6 +219,7 @@ gssd_run()
                        scan_poll_results(ret);
                }
        }
-       close(fd);
+       topdirs_free_list();
+
        return;
 }
index 1942175bc9434cc6b197de05d1c48a1d5842a1fc..3f81462a5f552cd89f633783381d75af3a353300 100644 (file)
  *      linked list of struct clnt_info which associates a clntXXX directory
  *     with an index into pollarray[], and other basic data about that client.
  *
- * Directory structure: created by the kernel nfs client
- *      {pipefs_nfsdir}/clntXX             : one per rpc_clnt struct in the kernel
- *      {pipefs_nfsdir}/clntXX/krb5        : read uid for which kernel wants
+ * Directory structure: created by the kernel
+ *      {rpc_pipefs}/{dir}/clntXX         : one per rpc_clnt struct in the kernel
+ *      {rpc_pipefs}/{dir}/clntXX/krb5    : read uid for which kernel wants
  *                                         a context, write the resulting context
- *      {pipefs_nfsdir}/clntXX/info        : stores info such as server name
+ *      {rpc_pipefs}/{dir}/clntXX/info    : stores info such as server name
  *
  * Algorithm:
- *      Poll all {pipefs_nfsdir}/clntXX/krb5 files.  When ready, data read
- *      is a uid; performs rpcsec_gss context initialization protocol to
+ *      Poll all {rpc_pipefs}/{dir}/clntXX/krb5 files.  When data is ready,
+ *      read and process; performs rpcsec_gss context initialization protocol to
  *      get a cred for that user.  Writes result to corresponding krb5 file
  *      in a form the kernel code will understand.
  *      In addition, we make sure we are notified whenever anything is
- *      created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
- *      and rescan the whole {pipefs_nfsdir} when this happens.
+ *      created or destroyed in {rpc_pipefs} or in any of the clntXX directories,
+ *      and rescan the whole {rpc_pipefs} when this happens.
  */
 
 struct pollfd * pollarray;
@@ -232,11 +232,19 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
                sscanf(p, "port: %127s\n", cb_port);
 
        /* check service, program, and version */
-       if(memcmp(service, "nfs", 3)) return -1;
+       if (memcmp(service, "nfs", 3) != 0)
+               return -1;
        *prog = atoi(program + 1); /* skip open paren */
        *vers = atoi(version);
-       if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
-               goto fail;
+
+       if (strlen(service) == 3 ) {
+               if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) &&
+                   (*vers != 4)))
+                       goto fail;
+       } else if (memcmp(service, "nfs4_cb", 7) == 0) {
+               if (*vers != 1)
+                       goto fail;
+       }
 
        if (cb_port[0] != '\0') {
                port = atoi(cb_port);
@@ -315,19 +323,18 @@ out:
 static int
 process_clnt_dir_files(struct clnt_info * clp)
 {
-       char    kname[32];
-       char    sname[32];
-       char    info_file_name[32];
+       char    name[PATH_MAX];
+       char    info_file_name[PATH_MAX];
 
        if (clp->krb5_fd == -1) {
-               snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
-               clp->krb5_fd = open(kname, O_RDWR);
+               snprintf(name, sizeof(name), "%s/krb5", clp->dirname);
+               clp->krb5_fd = open(name, O_RDWR);
        }
        if (clp->spkm3_fd == -1) {
-               snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
-               clp->spkm3_fd = open(sname, O_RDWR);
+               snprintf(name, sizeof(name), "%s/spkm3", clp->dirname);
+               clp->spkm3_fd = open(name, O_RDWR);
        }
-       if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
+       if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
                return -1;
        snprintf(info_file_name, sizeof(info_file_name), "%s/info",
                        clp->dirname);
@@ -384,17 +391,18 @@ insert_clnt_poll(struct clnt_info *clp)
 }
 
 static void
-process_clnt_dir(char *dir)
+process_clnt_dir(char *dir, char *pdir)
 {
        struct clnt_info *      clp;
 
        if (!(clp = insert_new_clnt()))
                goto fail_destroy_client;
 
-       if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
+       /* An extra for the '/', and an extra for the null */
+       if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) {
                goto fail_destroy_client;
        }
-       memcpy(clp->dirname, dir, strlen(dir));
+       sprintf(clp->dirname, "%s/%s", pdir, dir);
        if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
                printerr(0, "ERROR: can't open %s: %s\n",
                         clp->dirname, strerror(errno));
@@ -438,16 +446,24 @@ init_client_list(void)
  * directories, since the DNOTIFY could have been in there.
  */
 static void
-update_old_clients(struct dirent **namelist, int size)
+update_old_clients(struct dirent **namelist, int size, char *pdir)
 {
        struct clnt_info *clp;
        void *saveprev;
        int i, stillhere;
+       char fname[PATH_MAX];
 
        for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
+               /* only compare entries in the global list that are from the
+                * same pipefs parent directory as "pdir"
+                */
+               if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue;
+
                stillhere = 0;
                for (i=0; i < size; i++) {
-                       if (!strcmp(clp->dirname, namelist[i]->d_name)) {
+                       snprintf(fname, sizeof(fname), "%s/%s",
+                                pdir, namelist[i]->d_name);
+                       if (strcmp(clp->dirname, fname) == 0) {
                                stillhere = 1;
                                break;
                        }
@@ -468,13 +484,16 @@ update_old_clients(struct dirent **namelist, int size)
 
 /* Search for a client by directory name, return 1 if found, 0 otherwise */
 static int
-find_client(char *dirname)
+find_client(char *dirname, char *pdir)
 {
        struct clnt_info        *clp;
+       char fname[PATH_MAX];
 
-       for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
-               if (!strcmp(clp->dirname, dirname))
+       for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
+               snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname);
+               if (strcmp(clp->dirname, fname) == 0)
                        return 1;
+       }
        return 0;
 }
 
@@ -497,12 +516,12 @@ process_pipedir(char *pipe_name)
                return -1;
        }
 
-       update_old_clients(namelist, j);
+       update_old_clients(namelist, j, pipe_name);
        for (i=0; i < j; i++) {
                if (i < FD_ALLOC_BLOCK
                                && !strncmp(namelist[i]->d_name, "clnt", 4)
-                               && !find_client(namelist[i]->d_name))
-                       process_clnt_dir(namelist[i]->d_name);
+                               && !find_client(namelist[i]->d_name, pipe_name))
+                       process_clnt_dir(namelist[i]->d_name, pipe_name);
                free(namelist[i]);
        }
 
@@ -516,11 +535,15 @@ int
 update_client_list(void)
 {
        int retval = -1;
+       struct topdirs_info *tdi;
 
-       retval = process_pipedir(pipefs_nfsdir);
-       if (retval)
-               printerr(0, "ERROR: processing %s\n", pipefs_nfsdir);
+       TAILQ_FOREACH(tdi, &topdirs_list, list) {
+               retval = process_pipedir(tdi->dirname);
+               if (retval)
+                       printerr(1, "WARNING: error processing %s\n",
+                                tdi->dirname);
 
+       }
        return retval;
 }