From 24de786ec7e7a70e0587b0656a31f309b3b5eb65 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Mon, 16 Nov 2009 09:15:25 -0500 Subject: [PATCH] gssd: add upcall support for callback authentication 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 Signed-off-by: Steve Dickson --- utils/gssd/gssd.c | 6 --- utils/gssd/gssd.h | 9 +++- utils/gssd/gssd_main_loop.c | 88 ++++++++++++++++++++++++++++++++---- utils/gssd/gssd_proc.c | 89 +++++++++++++++++++++++-------------- 4 files changed, 143 insertions(+), 49 deletions(-) diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c index 40a2b4d..bd37a5f 100644 --- a/utils/gssd/gssd.c +++ b/utils/gssd/gssd.c @@ -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 diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h index 3c52f46..3c53a88 100644 --- a/utils/gssd/gssd.h +++ b/utils/gssd/gssd.h @@ -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); diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c index 397fd14..b5117c5 100644 --- a/utils/gssd/gssd_main_loop.c +++ b/utils/gssd/gssd_main_loop.c @@ -49,6 +49,7 @@ #include #include #include +#include #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; } diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index 1942175..3f81462 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -83,20 +83,20 @@ * 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; } -- 2.39.2