diff options
Diffstat (limited to 'utils/gssd/gssd_proc.c')
-rw-r--r-- | utils/gssd/gssd_proc.c | 543 |
1 files changed, 0 insertions, 543 deletions
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index 1d8e6a7..8957b27 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -52,7 +52,6 @@ #include <sys/socket.h> #include <arpa/inet.h> #include <sys/fsuid.h> -#include <sys/resource.h> #include <stdio.h> #include <stdlib.h> @@ -80,548 +79,6 @@ #include "gss_names.h" #include "misc.h" -/* - * pollarray: - * array of struct pollfd suitable to pass to poll. initialized to - * zero - a zero struct is ignored by poll() because the events mask is 0. - * - * clnt_list: - * 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 - * {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 - * {rpc_pipefs}/{dir}/clntXX/info : stores info such as server name - * {rpc_pipefs}/{dir}/clntXX/gssd : pipe for all gss mechanisms using - * a text-based string of parameters - * - * Algorithm: - * Poll all {rpc_pipefs}/{dir}/clntXX/YYYY 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 {rpc_pipefs} or in any of the clntXX directories, - * and rescan the whole {rpc_pipefs} when this happens. - */ - -struct pollfd * pollarray; - -unsigned long pollsize; /* the size of pollaray (in pollfd's) */ - -/* Avoid DNS reverse lookups on server names */ -int avoid_dns = 1; - -/* - * convert a presentation address string to a sockaddr_storage struct. Returns - * true on success or false on failure. - * - * Note that we do not populate the sin6_scope_id field here for IPv6 addrs. - * gssd nececessarily relies on hostname resolution and DNS AAAA records - * do not generally contain scope-id's. This means that GSSAPI auth really - * can't work with IPv6 link-local addresses. - * - * We *could* consider changing this if we did something like adopt the - * Microsoft "standard" of using the ipv6-literal.net domainname, but it's - * not really feasible at present. - */ -static int -addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port) -{ - int rc; - struct addrinfo *res; - struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV }; - -#ifndef IPV6_SUPPORTED - hints.ai_family = AF_INET; -#endif /* IPV6_SUPPORTED */ - - rc = getaddrinfo(node, port, &hints, &res); - if (rc) { - printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n", - node, port, rc == EAI_SYSTEM ? strerror(errno) : - gai_strerror(rc)); - return 0; - } - -#ifdef IPV6_SUPPORTED - /* - * getnameinfo ignores the scopeid. If the address turns out to have - * a non-zero scopeid, we can't use it -- the resolved host might be - * completely different from the one intended. - */ - if (res->ai_addr->sa_family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; - if (sin6->sin6_scope_id) { - printerr(0, "ERROR: address %s has non-zero " - "sin6_scope_id!\n", node); - freeaddrinfo(res); - return 0; - } - } -#endif /* IPV6_SUPPORTED */ - - memcpy(sa, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - return 1; -} - -/* - * convert a sockaddr to a hostname - */ -static char * -get_servername(const char *name, const struct sockaddr *sa, const char *addr) -{ - socklen_t addrlen; - int err; - char *hostname; - char hbuf[NI_MAXHOST]; - unsigned char buf[sizeof(struct in6_addr)]; - - if (avoid_dns) { - /* - * Determine if this is a server name, or an IP address. - * If it is an IP address, do the DNS lookup otherwise - * skip the DNS lookup. - */ - int is_fqdn = 1; - if (strchr(name, '.') == NULL) - is_fqdn = 0; /* local name */ - else if (inet_pton(AF_INET, name, buf) == 1) - is_fqdn = 0; /* IPv4 address */ - else if (inet_pton(AF_INET6, name, buf) == 1) - is_fqdn = 0; /* IPv6 addrss */ - - if (is_fqdn) { - return strdup(name); - } - /* Sorry, cannot avoid dns after all */ - } - - switch (sa->sa_family) { - case AF_INET: - addrlen = sizeof(struct sockaddr_in); - break; -#ifdef IPV6_SUPPORTED - case AF_INET6: - addrlen = sizeof(struct sockaddr_in6); - break; -#endif /* IPV6_SUPPORTED */ - default: - printerr(0, "ERROR: unrecognized addr family %d\n", - sa->sa_family); - return NULL; - } - - err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0, - NI_NAMEREQD); - if (err) { - printerr(0, "ERROR: unable to resolve %s to hostname: %s\n", - addr, err == EAI_SYSTEM ? strerror(errno) : - gai_strerror(err)); - return NULL; - } - - hostname = strdup(hbuf); - - return hostname; -} - -/* XXX buffer problems: */ -static int -read_service_info(char *info_file_name, char **servicename, char **servername, - int *prog, int *vers, char **protocol, - struct sockaddr *addr) { -#define INFOBUFLEN 256 - char buf[INFOBUFLEN + 1]; - static char server[128]; - int nbytes; - static char service[128]; - static char address[128]; - char program[16]; - char version[16]; - char protoname[16]; - char port[128]; - char *p; - int fd = -1; - int numfields; - - *servicename = *servername = *protocol = NULL; - - if ((fd = open(info_file_name, O_RDONLY)) == -1) { - printerr(0, "ERROR: can't open %s: %s\n", info_file_name, - strerror(errno)); - goto fail; - } - if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1) - goto fail; - close(fd); - fd = -1; - buf[nbytes] = '\0'; - - numfields = sscanf(buf,"RPC server: %127s\n" - "service: %127s %15s version %15s\n" - "address: %127s\n" - "protocol: %15s\n", - server, - service, program, version, - address, - protoname); - - if (numfields == 5) { - strcpy(protoname, "tcp"); - } else if (numfields != 6) { - goto fail; - } - - port[0] = '\0'; - if ((p = strstr(buf, "port")) != NULL) - sscanf(p, "port: %127s\n", port); - - /* get program, and version numbers */ - *prog = atoi(program + 1); /* skip open paren */ - *vers = atoi(version); - - if (!addrstr_to_sockaddr(addr, address, port)) - goto fail; - - *servername = get_servername(server, addr, address); - if (*servername == NULL) - goto fail; - - nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername); - if (nbytes > INFOBUFLEN) - goto fail; - - if (!(*servicename = calloc(strlen(buf) + 1, 1))) - goto fail; - memcpy(*servicename, buf, strlen(buf)); - - if (!(*protocol = strdup(protoname))) - goto fail; - return 0; -fail: - printerr(0, "ERROR: failed to read service info\n"); - if (fd != -1) close(fd); - free(*servername); - free(*servicename); - free(*protocol); - *servicename = *servername = *protocol = NULL; - return -1; -} - -static void -destroy_client(struct clnt_info *clp) -{ - if (clp->krb5_poll_index != -1) - memset(&pollarray[clp->krb5_poll_index], 0, - sizeof(struct pollfd)); - if (clp->gssd_poll_index != -1) - memset(&pollarray[clp->gssd_poll_index], 0, - sizeof(struct pollfd)); - if (clp->dir_fd != -1) close(clp->dir_fd); - if (clp->krb5_fd != -1) close(clp->krb5_fd); - if (clp->gssd_fd != -1) close(clp->gssd_fd); - free(clp->dirname); - free(clp->pdir); - free(clp->servicename); - free(clp->servername); - free(clp->protocol); - free(clp); -} - -static struct clnt_info * -insert_new_clnt(void) -{ - struct clnt_info *clp = NULL; - - if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) { - printerr(0, "ERROR: can't malloc clnt_info: %s\n", - strerror(errno)); - goto out; - } - clp->krb5_poll_index = -1; - clp->gssd_poll_index = -1; - clp->krb5_fd = -1; - clp->gssd_fd = -1; - clp->dir_fd = -1; - - TAILQ_INSERT_HEAD(&clnt_list, clp, list); -out: - return clp; -} - -static int -process_clnt_dir_files(struct clnt_info * clp) -{ - char name[PATH_MAX]; - char gname[PATH_MAX]; - char info_file_name[PATH_MAX]; - - if (clp->gssd_close_me) { - printerr(2, "Closing 'gssd' pipe for %s\n", clp->dirname); - close(clp->gssd_fd); - memset(&pollarray[clp->gssd_poll_index], 0, - sizeof(struct pollfd)); - clp->gssd_fd = -1; - clp->gssd_poll_index = -1; - clp->gssd_close_me = 0; - } - if (clp->krb5_close_me) { - printerr(2, "Closing 'krb5' pipe for %s\n", clp->dirname); - close(clp->krb5_fd); - memset(&pollarray[clp->krb5_poll_index], 0, - sizeof(struct pollfd)); - clp->krb5_fd = -1; - clp->krb5_poll_index = -1; - clp->krb5_close_me = 0; - } - - if (clp->gssd_fd == -1) { - snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname); - clp->gssd_fd = open(gname, O_RDWR); - } - if (clp->gssd_fd == -1) { - if (clp->krb5_fd == -1) { - snprintf(name, sizeof(name), "%s/krb5", clp->dirname); - clp->krb5_fd = open(name, O_RDWR); - } - - /* If we opened a gss-specific pipe, let's try opening - * the new upcall pipe again. If we succeed, close - * gss-specific pipe(s). - */ - if (clp->krb5_fd != -1) { - clp->gssd_fd = open(gname, O_RDWR); - if (clp->gssd_fd != -1) { - if (clp->krb5_fd != -1) - close(clp->krb5_fd); - clp->krb5_fd = -1; - } - } - } - - if ((clp->krb5_fd == -1) && (clp->gssd_fd == -1)) - return -1; - snprintf(info_file_name, sizeof(info_file_name), "%s/info", - clp->dirname); - if (clp->prog == 0) - read_service_info(info_file_name, &clp->servicename, - &clp->servername, &clp->prog, &clp->vers, - &clp->protocol, (struct sockaddr *) &clp->addr); - return 0; -} - -static int -get_poll_index(int *ind) -{ - unsigned int i; - - *ind = -1; - for (i=0; i<pollsize; i++) { - if (pollarray[i].events == 0) { - *ind = i; - break; - } - } - if (*ind == -1) { - printerr(0, "ERROR: No pollarray slots open\n"); - return -1; - } - return 0; -} - - -static int -insert_clnt_poll(struct clnt_info *clp) -{ - if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) { - if (get_poll_index(&clp->gssd_poll_index)) { - printerr(0, "ERROR: Too many gssd clients\n"); - return -1; - } - pollarray[clp->gssd_poll_index].fd = clp->gssd_fd; - pollarray[clp->gssd_poll_index].events |= POLLIN; - } - - if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) { - if (get_poll_index(&clp->krb5_poll_index)) { - printerr(0, "ERROR: Too many krb5 clients\n"); - return -1; - } - pollarray[clp->krb5_poll_index].fd = clp->krb5_fd; - pollarray[clp->krb5_poll_index].events |= POLLIN; - } - - return 0; -} - -static void -process_clnt_dir(char *dir, char *pdir) -{ - struct clnt_info * clp; - - if (!(clp = insert_new_clnt())) - goto fail_destroy_client; - - if (!(clp->pdir = strdup(pdir))) - goto fail_destroy_client; - - /* An extra for the '/', and an extra for the null */ - if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) { - goto fail_destroy_client; - } - sprintf(clp->dirname, "%s/%s", pdir, dir); - if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) { - if (errno != ENOENT) - printerr(0, "ERROR: can't open %s: %s\n", - clp->dirname, strerror(errno)); - goto fail_destroy_client; - } - fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL); - fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT); - - if (process_clnt_dir_files(clp)) - goto fail_keep_client; - - if (insert_clnt_poll(clp)) - goto fail_destroy_client; - - return; - -fail_destroy_client: - if (clp) { - TAILQ_REMOVE(&clnt_list, clp, list); - destroy_client(clp); - } -fail_keep_client: - /* We couldn't find some subdirectories, but we keep the client - * around in case we get a notification on the directory when the - * subdirectories are created. */ - return; -} - -void -init_client_list(void) -{ - struct rlimit rlim; - TAILQ_INIT(&clnt_list); - /* Eventually plan to grow/shrink poll array: */ - pollsize = FD_ALLOC_BLOCK; - if (getrlimit(RLIMIT_NOFILE, &rlim) == 0 && - rlim.rlim_cur != RLIM_INFINITY) - pollsize = rlim.rlim_cur; - pollarray = calloc(pollsize, sizeof(struct pollfd)); -} - -/* - * This is run after a DNOTIFY signal, and should clear up any - * directories that are no longer around, and re-scan any existing - * directories, since the DNOTIFY could have been in there. - */ -static void -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 (strcmp(clp->pdir, pdir) != 0) continue; - - stillhere = 0; - for (i=0; i < size; i++) { - snprintf(fname, sizeof(fname), "%s/%s", - pdir, namelist[i]->d_name); - if (strcmp(clp->dirname, fname) == 0) { - stillhere = 1; - break; - } - } - if (!stillhere) { - printerr(2, "destroying client %s\n", clp->dirname); - saveprev = clp->list.tqe_prev; - TAILQ_REMOVE(&clnt_list, clp, list); - destroy_client(clp); - clp = saveprev; - } - } - for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { - if (!process_clnt_dir_files(clp)) - insert_clnt_poll(clp); - } -} - -/* Search for a client by directory name, return 1 if found, 0 otherwise */ -static int -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) { - snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname); - if (strcmp(clp->dirname, fname) == 0) - return 1; - } - return 0; -} - -static int -process_pipedir(char *pipe_name) -{ - struct dirent **namelist; - int i, j; - - if (chdir(pipe_name) < 0) { - printerr(0, "ERROR: can't chdir to %s: %s\n", - pipe_name, strerror(errno)); - return -1; - } - - j = scandir(pipe_name, &namelist, NULL, alphasort); - if (j < 0) { - printerr(0, "ERROR: can't scandir %s: %s\n", - pipe_name, strerror(errno)); - return -1; - } - - update_old_clients(namelist, j, pipe_name); - for (i=0; i < j; i++) { - if (!strncmp(namelist[i]->d_name, "clnt", 4) - && !find_client(namelist[i]->d_name, pipe_name)) - process_clnt_dir(namelist[i]->d_name, pipe_name); - free(namelist[i]); - } - - free(namelist); - - return 0; -} - -/* Used to read (and re-read) list of clients, set up poll array. */ -int -update_client_list(void) -{ - int retval = -1; - struct topdirs_info *tdi; - - TAILQ_FOREACH(tdi, &topdirs_list, list) { - retval = process_pipedir(tdi->dirname); - if (retval) - printerr(1, "WARNING: error processing %s\n", - tdi->dirname); - - } - return retval; -} - /* Encryption types supported by the kernel rpcsec_gss code */ int num_krb5_enctypes = 0; krb5_enctype *krb5_enctypes = NULL; |