| /* |
| * idmapd.c |
| * |
| * Userland daemon for idmap. |
| * |
| * Copyright (c) 2002 The Regents of the University of Michigan. |
| * All rights reserved. |
| * |
| * Marius Aamodt Eriksen <marius@umich.edu> |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the University nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/poll.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <time.h> |
| |
| #include "nfs_idmap.h" |
| |
| #include <err.h> |
| #include <errno.h> |
| #include <event.h> |
| #include <fcntl.h> |
| #include <dirent.h> |
| #include <unistd.h> |
| #include <netdb.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <pwd.h> |
| #include <grp.h> |
| #include <limits.h> |
| #include <ctype.h> |
| #include <nfsidmap.h> |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif /* HAVE_CONFIG_H */ |
| |
| #include "xlog.h" |
| #include "conffile.h" |
| #include "queue.h" |
| #include "nfslib.h" |
| |
| #ifndef PIPEFS_DIR |
| #define PIPEFS_DIR "/var/lib/nfs/rpc_pipefs/" |
| #endif |
| |
| #ifndef NFSD_DIR |
| #define NFSD_DIR "/proc/net/rpc" |
| #endif |
| |
| #ifndef CLIENT_CACHE_TIMEOUT_FILE |
| #define CLIENT_CACHE_TIMEOUT_FILE "/proc/sys/fs/nfs/idmap_cache_timeout" |
| #endif |
| |
| #ifndef NFS4NOBODY_USER |
| #define NFS4NOBODY_USER "nobody" |
| #endif |
| |
| #ifndef NFS4NOBODY_GROUP |
| #define NFS4NOBODY_GROUP "nobody" |
| #endif |
| |
| /* From Niels */ |
| #define CONF_SAVE(w, f) do { \ |
| char *p = f; \ |
| if (p != NULL) \ |
| (w) = p; \ |
| } while (0) |
| |
| #define IC_IDNAME 0 |
| #define IC_IDNAME_CHAN NFSD_DIR "/nfs4.idtoname/channel" |
| #define IC_IDNAME_FLUSH NFSD_DIR "/nfs4.idtoname/flush" |
| |
| #define IC_NAMEID 1 |
| #define IC_NAMEID_CHAN NFSD_DIR "/nfs4.nametoid/channel" |
| #define IC_NAMEID_FLUSH NFSD_DIR "/nfs4.nametoid/flush" |
| |
| struct idmap_client { |
| short ic_which; |
| char ic_clid[30]; |
| char *ic_id; |
| char ic_path[PATH_MAX]; |
| int ic_fd; |
| int ic_dirfd; |
| int ic_scanned; |
| struct event ic_event; |
| TAILQ_ENTRY(idmap_client) ic_next; |
| }; |
| static struct idmap_client nfsd_ic[2] = { |
| {IC_IDNAME, "Server", "", IC_IDNAME_CHAN, -1, -1, 0}, |
| {IC_NAMEID, "Server", "", IC_NAMEID_CHAN, -1, -1, 0}, |
| }; |
| |
| TAILQ_HEAD(idmap_clientq, idmap_client); |
| |
| static void dirscancb(int, short, void *); |
| static void clntscancb(int, short, void *); |
| static void svrreopen(int, short, void *); |
| static int nfsopen(struct idmap_client *); |
| static void nfscb(int, short, void *); |
| static void nfsdcb(int, short, void *); |
| static int validateascii(char *, u_int32_t); |
| static int addfield(char **, ssize_t *, char *); |
| static int getfield(char **, char *, size_t); |
| |
| static void imconv(struct idmap_client *, struct idmap_msg *); |
| static void idtonameres(struct idmap_msg *); |
| static void nametoidres(struct idmap_msg *); |
| |
| static int nfsdopen(void); |
| static int nfsdopenone(struct idmap_client *); |
| static void nfsdreopen_one(struct idmap_client *); |
| static void nfsdreopen(void); |
| |
| size_t strlcat(char *, const char *, size_t); |
| size_t strlcpy(char *, const char *, size_t); |
| ssize_t atomicio(ssize_t (*f) (int, void*, size_t), |
| int, void *, size_t); |
| void mydaemon(int, int); |
| void release_parent(void); |
| |
| static int verbose = 0; |
| #define DEFAULT_IDMAP_CACHE_EXPIRY 600 /* seconds */ |
| static int cache_entry_expiration = 0; |
| static char pipefsdir[PATH_MAX]; |
| static char *nobodyuser, *nobodygroup; |
| static uid_t nobodyuid; |
| static gid_t nobodygid; |
| |
| /* Used by conffile.c in libnfs.a */ |
| char *conf_path; |
| |
| static int |
| flush_nfsd_cache(char *path, time_t now) |
| { |
| int fd; |
| char stime[20]; |
| |
| sprintf(stime, "%ld\n", now); |
| fd = open(path, O_RDWR); |
| if (fd == -1) |
| return -1; |
| if (write(fd, stime, strlen(stime)) != strlen(stime)) { |
| errx(1, "Flushing nfsd cache failed: errno %d (%s)", |
| errno, strerror(errno)); |
| } |
| close(fd); |
| return 0; |
| } |
| |
| static int |
| flush_nfsd_idmap_cache(void) |
| { |
| time_t now = time(NULL); |
| int ret; |
| |
| ret = flush_nfsd_cache(IC_IDNAME_FLUSH, now); |
| if (ret) |
| return ret; |
| ret = flush_nfsd_cache(IC_NAMEID_FLUSH, now); |
| return ret; |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| int fd = 0, opt, fg = 0, nfsdret = -1; |
| struct idmap_clientq icq; |
| struct event rootdirev, clntdirev, svrdirev; |
| struct event initialize; |
| struct passwd *pw; |
| struct group *gr; |
| struct stat sb; |
| char *xpipefsdir = NULL; |
| int serverstart = 1, clientstart = 1; |
| int ret; |
| char *progname; |
| |
| conf_path = _PATH_IDMAPDCONF; |
| nobodyuser = NFS4NOBODY_USER; |
| nobodygroup = NFS4NOBODY_GROUP; |
| strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir)); |
| |
| if ((progname = strrchr(argv[0], '/'))) |
| progname++; |
| else |
| progname = argv[0]; |
| xlog_open(progname); |
| |
| #define GETOPTSTR "vfd:p:U:G:c:CS" |
| opterr=0; /* Turn off error messages */ |
| while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) { |
| if (opt == 'c') |
| conf_path = optarg; |
| if (opt == '?') { |
| if (strchr(GETOPTSTR, optopt)) |
| errx(1, "'-%c' option requires an argument.", optopt); |
| else |
| errx(1, "'-%c' is an invalid argument.", optopt); |
| } |
| } |
| optind = 1; |
| |
| if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) { |
| warn("Skipping configuration file \"%s\"", conf_path); |
| conf_path = NULL; |
| } else { |
| conf_init(); |
| verbose = conf_get_num("General", "Verbosity", 0); |
| cache_entry_expiration = conf_get_num("General", |
| "Cache-Expiration", DEFAULT_IDMAP_CACHE_EXPIRY); |
| CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory")); |
| if (xpipefsdir != NULL) |
| strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir)); |
| CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User")); |
| CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group")); |
| } |
| |
| while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) |
| switch (opt) { |
| case 'v': |
| verbose++; |
| break; |
| case 'f': |
| fg = 1; |
| break; |
| case 'p': |
| strlcpy(pipefsdir, optarg, sizeof(pipefsdir)); |
| break; |
| case 'd': |
| case 'U': |
| case 'G': |
| errx(1, "the -d, -U, and -G options have been removed;" |
| " please use the configuration file instead."); |
| case 'C': |
| serverstart = 0; |
| break; |
| case 'S': |
| clientstart = 0; |
| break; |
| default: |
| break; |
| } |
| |
| if (!serverstart && !clientstart) |
| errx(1, "it is illegal to specify both -C and -S"); |
| |
| strncat(pipefsdir, "/nfs", sizeof(pipefsdir)); |
| |
| if ((pw = getpwnam(nobodyuser)) == NULL) |
| errx(1, "Could not find user \"%s\"", nobodyuser); |
| nobodyuid = pw->pw_uid; |
| |
| if ((gr = getgrnam(nobodygroup)) == NULL) |
| errx(1, "Could not find group \"%s\"", nobodygroup); |
| nobodygid = gr->gr_gid; |
| |
| #ifdef HAVE_NFS4_SET_DEBUG |
| nfs4_set_debug(verbose, xlog_warn); |
| #endif |
| if (conf_path == NULL) |
| conf_path = _PATH_IDMAPDCONF; |
| if (nfs4_init_name_mapping(conf_path)) |
| errx(1, "Unable to create name to user id mappings."); |
| |
| if (!fg) |
| mydaemon(0, 0); |
| |
| event_init(); |
| |
| if (verbose > 0) |
| xlog_warn("Expiration time is %d seconds.", |
| cache_entry_expiration); |
| if (serverstart) { |
| nfsdret = nfsdopen(); |
| if (nfsdret == 0) { |
| ret = flush_nfsd_idmap_cache(); |
| if (ret) |
| xlog_err("main: Failed to flush nfsd idmap cache\n: %s", strerror(errno)); |
| } |
| } |
| |
| if (clientstart) { |
| struct timeval now = { |
| .tv_sec = 0, |
| .tv_usec = 0, |
| }; |
| |
| if (cache_entry_expiration != DEFAULT_IDMAP_CACHE_EXPIRY) { |
| int timeout_fd, len; |
| char timeout_buf[12]; |
| if ((timeout_fd = open(CLIENT_CACHE_TIMEOUT_FILE, |
| O_RDWR)) == -1) { |
| xlog_warn("Unable to open '%s' to set " |
| "client cache expiration time " |
| "to %d seconds\n", |
| CLIENT_CACHE_TIMEOUT_FILE, |
| cache_entry_expiration); |
| } else { |
| len = snprintf(timeout_buf, sizeof(timeout_buf), |
| "%d", cache_entry_expiration); |
| if ((write(timeout_fd, timeout_buf, len)) != len) |
| xlog_warn("Error writing '%s' to " |
| "'%s' to set client " |
| "cache expiration time\n", |
| timeout_buf, |
| CLIENT_CACHE_TIMEOUT_FILE); |
| close(timeout_fd); |
| } |
| } |
| |
| if ((fd = open(pipefsdir, O_RDONLY)) == -1) |
| xlog_err("main: open(%s): %s", pipefsdir, strerror(errno)); |
| |
| if (fcntl(fd, F_SETSIG, SIGUSR1) == -1) |
| xlog_err("main: fcntl(%s): %s", pipefsdir, strerror(errno)); |
| |
| if (fcntl(fd, F_NOTIFY, |
| DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1) { |
| xlog_err("main: fcntl(%s): %s", pipefsdir, strerror(errno)); |
| if (errno == EINVAL) |
| xlog_err("main: Possibly no Dnotify support in kernel."); |
| } |
| TAILQ_INIT(&icq); |
| |
| /* These events are persistent */ |
| signal_set(&rootdirev, SIGUSR1, dirscancb, &icq); |
| signal_add(&rootdirev, NULL); |
| signal_set(&clntdirev, SIGUSR2, clntscancb, &icq); |
| signal_add(&clntdirev, NULL); |
| signal_set(&svrdirev, SIGHUP, svrreopen, NULL); |
| signal_add(&svrdirev, NULL); |
| |
| /* Fetch current state */ |
| /* (Delay till start of event_dispatch to avoid possibly losing |
| * a SIGUSR1 between here and the call to event_dispatch().) */ |
| evtimer_set(&initialize, dirscancb, &icq); |
| evtimer_add(&initialize, &now); |
| } |
| |
| if (nfsdret != 0 && fd == 0) |
| xlog_err("main: Neither NFS client nor NFSd found"); |
| |
| release_parent(); |
| |
| if (event_dispatch() < 0) |
| xlog_err("main: event_dispatch returns errno %d (%s)", |
| errno, strerror(errno)); |
| /* NOTREACHED */ |
| return 1; |
| } |
| |
| static void |
| dirscancb(int fd, short which, void *data) |
| { |
| int nent, i; |
| struct dirent **ents; |
| struct idmap_client *ic, *nextic; |
| char path[PATH_MAX]; |
| struct idmap_clientq *icq = data; |
| |
| nent = scandir(pipefsdir, &ents, NULL, alphasort); |
| if (nent == -1) { |
| xlog_warn("dirscancb: scandir(%s): %s", pipefsdir, strerror(errno)); |
| return; |
| } |
| |
| for (i = 0; i < nent; i++) { |
| if (ents[i]->d_reclen > 4 && |
| strncmp(ents[i]->d_name, "clnt", 4) == 0) { |
| TAILQ_FOREACH(ic, icq, ic_next) |
| if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0) |
| break; |
| if (ic != NULL) |
| goto next; |
| |
| if ((ic = calloc(1, sizeof(*ic))) == NULL) |
| goto out; |
| strlcpy(ic->ic_clid, ents[i]->d_name + 4, |
| sizeof(ic->ic_clid)); |
| path[0] = '\0'; |
| snprintf(path, sizeof(path), "%s/%s", |
| pipefsdir, ents[i]->d_name); |
| |
| if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) { |
| xlog_warn("dirscancb: open(%s): %s", path, strerror(errno)); |
| free(ic); |
| goto out; |
| } |
| |
| strlcat(path, "/idmap", sizeof(path)); |
| strlcpy(ic->ic_path, path, sizeof(ic->ic_path)); |
| |
| if (verbose > 0) |
| xlog_warn("New client: %s", ic->ic_clid); |
| |
| if (nfsopen(ic) == -1) { |
| close(ic->ic_dirfd); |
| free(ic); |
| goto out; |
| } |
| |
| ic->ic_id = "Client"; |
| |
| TAILQ_INSERT_TAIL(icq, ic, ic_next); |
| |
| next: |
| ic->ic_scanned = 1; |
| } |
| } |
| |
| ic = TAILQ_FIRST(icq); |
| while(ic != NULL) { |
| nextic=TAILQ_NEXT(ic, ic_next); |
| if (!ic->ic_scanned) { |
| event_del(&ic->ic_event); |
| close(ic->ic_fd); |
| close(ic->ic_dirfd); |
| TAILQ_REMOVE(icq, ic, ic_next); |
| if (verbose > 0) { |
| xlog_warn("Stale client: %s", ic->ic_clid); |
| xlog_warn("\t-> closed %s", ic->ic_path); |
| } |
| free(ic); |
| } else |
| ic->ic_scanned = 0; |
| ic = nextic; |
| } |
| |
| out: |
| for (i = 0; i < nent; i++) |
| free(ents[i]); |
| free(ents); |
| return; |
| } |
| |
| static void |
| svrreopen(int fd, short which, void *data) |
| { |
| nfsdreopen(); |
| } |
| |
| static void |
| clntscancb(int fd, short which, void *data) |
| { |
| struct idmap_clientq *icq = data; |
| struct idmap_client *ic; |
| |
| TAILQ_FOREACH(ic, icq, ic_next) |
| if (ic->ic_fd == -1 && nfsopen(ic) == -1) { |
| close(ic->ic_dirfd); |
| TAILQ_REMOVE(icq, ic, ic_next); |
| free(ic); |
| } |
| } |
| |
| static void |
| nfsdcb(int fd, short which, void *data) |
| { |
| struct idmap_client *ic = data; |
| struct idmap_msg im; |
| u_char buf[IDMAP_MAXMSGSZ + 1]; |
| size_t len; |
| ssize_t bsiz; |
| char *bp, typebuf[IDMAP_MAXMSGSZ], |
| buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p; |
| unsigned long tmp; |
| |
| if (which != EV_READ) |
| goto out; |
| |
| if ((len = read(ic->ic_fd, buf, sizeof(buf))) <= 0) { |
| xlog_warn("nfsdcb: read(%s) failed: errno %d (%s)", |
| ic->ic_path, len?errno:0, |
| len?strerror(errno):"End of File"); |
| nfsdreopen_one(ic); |
| return; |
| } |
| |
| /* Get rid of newline and terminate buffer*/ |
| buf[len - 1] = '\0'; |
| bp = (char *)buf; |
| |
| memset(&im, 0, sizeof(im)); |
| |
| /* Authentication name -- ignored for now*/ |
| if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) { |
| xlog_warn("nfsdcb: bad authentication name in upcall\n"); |
| goto out; |
| } |
| if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) { |
| xlog_warn("nfsdcb: bad type in upcall\n"); |
| goto out; |
| } |
| if (verbose > 0) |
| xlog_warn("nfsdcb: authbuf=%s authtype=%s", |
| authbuf, typebuf); |
| |
| im.im_type = strcmp(typebuf, "user") == 0 ? |
| IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; |
| |
| switch (ic->ic_which) { |
| case IC_NAMEID: |
| im.im_conv = IDMAP_CONV_NAMETOID; |
| if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) { |
| xlog_warn("nfsdcb: bad name in upcall\n"); |
| goto out; |
| } |
| break; |
| case IC_IDNAME: |
| im.im_conv = IDMAP_CONV_IDTONAME; |
| if (getfield(&bp, buf1, sizeof(buf1)) == -1) { |
| xlog_warn("nfsdcb: bad id in upcall\n"); |
| goto out; |
| } |
| tmp = strtoul(buf1, (char **)NULL, 10); |
| im.im_id = (u_int32_t)tmp; |
| if ((tmp == ULONG_MAX && errno == ERANGE) |
| || (unsigned long)im.im_id != tmp) { |
| xlog_warn("nfsdcb: id '%s' too big!\n", buf1); |
| goto out; |
| } |
| break; |
| default: |
| xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); |
| goto out; |
| } |
| |
| imconv(ic, &im); |
| |
| buf[0] = '\0'; |
| bp = (char *)buf; |
| bsiz = sizeof(buf); |
| |
| /* Authentication name */ |
| addfield(&bp, &bsiz, authbuf); |
| |
| switch (ic->ic_which) { |
| case IC_NAMEID: |
| /* Type */ |
| p = im.im_type == IDMAP_TYPE_USER ? "user" : "group"; |
| addfield(&bp, &bsiz, p); |
| /* Name */ |
| addfield(&bp, &bsiz, im.im_name); |
| /* expiry */ |
| snprintf(buf1, sizeof(buf1), "%lu", |
| time(NULL) + cache_entry_expiration); |
| addfield(&bp, &bsiz, buf1); |
| /* Note that we don't want to write the id if the mapping |
| * failed; instead, by leaving it off, we write a negative |
| * cache entry which will result in an error returned to |
| * the client. We don't want a chown or setacl referring |
| * to an unknown user to result in giving permissions to |
| * "nobody"! */ |
| if (im.im_status == IDMAP_STATUS_SUCCESS) { |
| /* ID */ |
| snprintf(buf1, sizeof(buf1), "%u", im.im_id); |
| addfield(&bp, &bsiz, buf1); |
| |
| } |
| //if (bsiz == sizeof(buf)) /* XXX */ |
| |
| bp[-1] = '\n'; |
| |
| break; |
| case IC_IDNAME: |
| /* Type */ |
| p = im.im_type == IDMAP_TYPE_USER ? "user" : "group"; |
| addfield(&bp, &bsiz, p); |
| /* ID */ |
| snprintf(buf1, sizeof(buf1), "%u", im.im_id); |
| addfield(&bp, &bsiz, buf1); |
| /* expiry */ |
| snprintf(buf1, sizeof(buf1), "%lu", |
| time(NULL) + cache_entry_expiration); |
| addfield(&bp, &bsiz, buf1); |
| /* Note we're ignoring the status field in this case; we'll |
| * just map to nobody instead. */ |
| /* Name */ |
| addfield(&bp, &bsiz, im.im_name); |
| |
| bp[-1] = '\n'; |
| |
| break; |
| default: |
| xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); |
| goto out; |
| } |
| |
| bsiz = sizeof(buf) - bsiz; |
| |
| if (atomicio((void*)write, ic->ic_fd, buf, bsiz) != bsiz) |
| xlog_warn("nfsdcb: write(%s) failed: errno %d (%s)", |
| ic->ic_path, errno, strerror(errno)); |
| |
| out: |
| event_add(&ic->ic_event, NULL); |
| } |
| |
| static void |
| imconv(struct idmap_client *ic, struct idmap_msg *im) |
| { |
| switch (im->im_conv) { |
| case IDMAP_CONV_IDTONAME: |
| idtonameres(im); |
| if (verbose > 1) |
| xlog_warn("%s %s: (%s) id \"%d\" -> name \"%s\"", |
| ic->ic_id, ic->ic_clid, |
| im->im_type == IDMAP_TYPE_USER ? "user" : "group", |
| im->im_id, im->im_name); |
| break; |
| case IDMAP_CONV_NAMETOID: |
| if (validateascii(im->im_name, sizeof(im->im_name)) == -1) { |
| im->im_status |= IDMAP_STATUS_INVALIDMSG; |
| return; |
| } |
| nametoidres(im); |
| if (verbose > 1) |
| xlog_warn("%s %s: (%s) name \"%s\" -> id \"%d\"", |
| ic->ic_id, ic->ic_clid, |
| im->im_type == IDMAP_TYPE_USER ? "user" : "group", |
| im->im_name, im->im_id); |
| break; |
| default: |
| xlog_warn("imconv: Invalid conversion type (%d) in message", |
| im->im_conv); |
| im->im_status |= IDMAP_STATUS_INVALIDMSG; |
| break; |
| } |
| } |
| |
| static void |
| nfscb(int fd, short which, void *data) |
| { |
| struct idmap_client *ic = data; |
| struct idmap_msg im; |
| |
| if (which != EV_READ) |
| goto out; |
| |
| if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) { |
| if (verbose > 0) |
| xlog_warn("nfscb: read(%s): %s", ic->ic_path, strerror(errno)); |
| if (errno == EPIPE) |
| return; |
| goto out; |
| } |
| |
| imconv(ic, &im); |
| |
| /* XXX: I don't like ignoring this error in the id->name case, |
| * but we've never returned it, and I need to check that the client |
| * can handle it gracefully before starting to return it now. */ |
| |
| if (im.im_status == IDMAP_STATUS_LOOKUPFAIL) |
| im.im_status = IDMAP_STATUS_SUCCESS; |
| |
| if (atomicio((void*)write, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) |
| xlog_warn("nfscb: write(%s): %s", ic->ic_path, strerror(errno)); |
| out: |
| event_add(&ic->ic_event, NULL); |
| } |
| |
| static void |
| nfsdreopen_one(struct idmap_client *ic) |
| { |
| int fd; |
| |
| if (verbose > 0) |
| xlog_warn("ReOpening %s", ic->ic_path); |
| |
| if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) { |
| if ((ic->ic_event.ev_flags & EVLIST_INIT)) |
| event_del(&ic->ic_event); |
| if (ic->ic_fd != -1) |
| close(ic->ic_fd); |
| |
| ic->ic_event.ev_fd = ic->ic_fd = fd; |
| event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic); |
| event_add(&ic->ic_event, NULL); |
| } else { |
| xlog_warn("nfsdreopen: Opening '%s' failed: errno %d (%s)", |
| ic->ic_path, errno, strerror(errno)); |
| } |
| } |
| |
| static void |
| nfsdreopen() |
| { |
| nfsdreopen_one(&nfsd_ic[IC_NAMEID]); |
| nfsdreopen_one(&nfsd_ic[IC_IDNAME]); |
| return; |
| } |
| |
| static int |
| nfsdopen(void) |
| { |
| return ((nfsdopenone(&nfsd_ic[IC_NAMEID]) == 0 && |
| nfsdopenone(&nfsd_ic[IC_IDNAME]) == 0) ? 0 : -1); |
| } |
| |
| static int |
| nfsdopenone(struct idmap_client *ic) |
| { |
| if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) { |
| if (verbose > 0) |
| xlog_warn("nfsdopenone: Opening %s failed: " |
| "errno %d (%s)", |
| ic->ic_path, errno, strerror(errno)); |
| return (-1); |
| } |
| |
| event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic); |
| event_add(&ic->ic_event, NULL); |
| |
| if (verbose > 0) |
| xlog_warn("Opened %s", ic->ic_path); |
| |
| return (0); |
| } |
| |
| static int |
| nfsopen(struct idmap_client *ic) |
| { |
| if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) { |
| switch (errno) { |
| case ENOENT: |
| fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2); |
| fcntl(ic->ic_dirfd, F_NOTIFY, |
| DN_CREATE | DN_DELETE | DN_MULTISHOT); |
| break; |
| default: |
| xlog_warn("nfsopen: open(%s): %s", ic->ic_path, strerror(errno)); |
| return (-1); |
| } |
| } else { |
| event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic); |
| event_add(&ic->ic_event, NULL); |
| fcntl(ic->ic_dirfd, F_SETSIG, 0); |
| fcntl(ic->ic_dirfd, F_NOTIFY, 0); |
| if (verbose > 0) |
| xlog_warn("Opened %s", ic->ic_path); |
| } |
| |
| return (0); |
| } |
| |
| static void |
| idtonameres(struct idmap_msg *im) |
| { |
| char domain[NFS4_MAX_DOMAIN_LEN]; |
| int ret = 0; |
| |
| ret = nfs4_get_default_domain(NULL, domain, sizeof(domain)); |
| switch (im->im_type) { |
| case IDMAP_TYPE_USER: |
| ret = nfs4_uid_to_name(im->im_id, domain, im->im_name, |
| sizeof(im->im_name)); |
| if (ret) { |
| if (strlen(nobodyuser) < sizeof(im->im_name)) |
| strcpy(im->im_name, nobodyuser); |
| else |
| strcpy(im->im_name, NFS4NOBODY_USER); |
| } |
| break; |
| case IDMAP_TYPE_GROUP: |
| ret = nfs4_gid_to_name(im->im_id, domain, im->im_name, |
| sizeof(im->im_name)); |
| if (ret) { |
| if (strlen(nobodygroup) < sizeof(im->im_name)) |
| strcpy(im->im_name, nobodygroup); |
| else |
| strcpy(im->im_name, NFS4NOBODY_GROUP); |
| } |
| break; |
| } |
| if (ret) |
| im->im_status = IDMAP_STATUS_LOOKUPFAIL; |
| else |
| im->im_status = IDMAP_STATUS_SUCCESS; |
| } |
| |
| static void |
| nametoidres(struct idmap_msg *im) |
| { |
| uid_t uid; |
| gid_t gid; |
| int ret = 0; |
| |
| /* XXX: move nobody stuff to library calls |
| * (nfs4_get_nobody_user(domain), nfs4_get_nobody_group(domain)) */ |
| |
| im->im_status = IDMAP_STATUS_SUCCESS; |
| |
| switch (im->im_type) { |
| case IDMAP_TYPE_USER: |
| ret = nfs4_name_to_uid(im->im_name, &uid); |
| im->im_id = (u_int32_t) uid; |
| if (ret) { |
| im->im_status = IDMAP_STATUS_LOOKUPFAIL; |
| im->im_id = nobodyuid; |
| } |
| return; |
| case IDMAP_TYPE_GROUP: |
| ret = nfs4_name_to_gid(im->im_name, &gid); |
| im->im_id = (u_int32_t) gid; |
| if (ret) { |
| im->im_status = IDMAP_STATUS_LOOKUPFAIL; |
| im->im_id = nobodygid; |
| } |
| return; |
| } |
| } |
| |
| static int |
| validateascii(char *string, u_int32_t len) |
| { |
| int i; |
| |
| for (i = 0; i < len; i++) { |
| if (string[i] == '\0') |
| break; |
| |
| if (string[i] & 0x80) |
| return (-1); |
| } |
| |
| if ((i >= len) || string[i] != '\0') |
| return (-1); |
| |
| return (i + 1); |
| } |
| |
| static int |
| addfield(char **bpp, ssize_t *bsizp, char *fld) |
| { |
| char ch, *bp = *bpp; |
| ssize_t bsiz = *bsizp; |
| |
| while ((ch = *fld++) != '\0' && bsiz > 0) { |
| switch(ch) { |
| case ' ': |
| case '\t': |
| case '\n': |
| case '\\': |
| if (bsiz >= 4) { |
| bp += snprintf(bp, bsiz, "\\%03o", ch); |
| bsiz -= 4; |
| } |
| break; |
| default: |
| *bp++ = ch; |
| bsiz--; |
| break; |
| } |
| } |
| |
| if (bsiz < 1 || ch != '\0') |
| return (-1); |
| |
| *bp++ = ' '; |
| bsiz--; |
| |
| *bpp = bp; |
| *bsizp = bsiz; |
| |
| return (0); |
| } |
| |
| static int |
| getfield(char **bpp, char *fld, size_t fldsz) |
| { |
| char *bp; |
| u_int val, n; |
| |
| while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0') |
| ; |
| |
| if (bp == NULL || bp[0] == '\0' || bp[0] == '\n') |
| return (-1); |
| |
| while (*bp != '\0' && fldsz > 1) { |
| if (*bp == '\\') { |
| if ((n = sscanf(bp, "\\%03o", &val)) != 1) |
| return (-1); |
| if (val > (char)-1) |
| return (-1); |
| *fld++ = (char)val; |
| bp += 4; |
| } else { |
| *fld++ = *bp; |
| bp++; |
| } |
| fldsz--; |
| } |
| |
| if (*bp != '\0') |
| return (-1); |
| *fld = '\0'; |
| |
| return (0); |
| } |
| /* |
| * mydaemon creates a pipe between the partent and child |
| * process. The parent process will wait until the |
| * child dies or writes a '1' on the pipe signaling |
| * that it started successfully. |
| */ |
| int pipefds[2] = { -1, -1}; |
| |
| void |
| mydaemon(int nochdir, int noclose) |
| { |
| int pid, status, tempfd; |
| |
| if (pipe(pipefds) < 0) |
| err(1, "mydaemon: pipe() failed: errno %d", errno); |
| |
| if ((pid = fork ()) < 0) |
| err(1, "mydaemon: fork() failed: errno %d", errno); |
| |
| if (pid != 0) { |
| /* |
| * Parent. Wait for status from child. |
| */ |
| close(pipefds[1]); |
| if (read(pipefds[0], &status, 1) != 1) |
| exit(1); |
| exit (0); |
| } |
| /* Child. */ |
| close(pipefds[0]); |
| setsid (); |
| if (nochdir == 0) { |
| if (chdir ("/") == -1) |
| err(1, "mydaemon: chdir() failed: errno %d", errno); |
| } |
| |
| while (pipefds[1] <= 2) { |
| pipefds[1] = dup(pipefds[1]); |
| if (pipefds[1] < 0) |
| err(1, "mydaemon: dup() failed: errno %d", errno); |
| } |
| |
| if (noclose == 0) { |
| tempfd = open("/dev/null", O_RDWR); |
| if (tempfd < 0) |
| tempfd = open("/", O_RDONLY); |
| if (tempfd >= 0) { |
| dup2(tempfd, 0); |
| dup2(tempfd, 1); |
| dup2(tempfd, 2); |
| close(tempfd); |
| } else { |
| err(1, "mydaemon: can't open /dev/null: errno %d", |
| errno); |
| exit(1); |
| } |
| } |
| |
| return; |
| } |
| void |
| release_parent(void) |
| { |
| int status; |
| |
| if (pipefds[1] > 0) { |
| if (write(pipefds[1], &status, 1) != 1) { |
| err(1, "Writing to parent pipe failed: errno %d (%s)\n", |
| errno, strerror(errno)); |
| } |
| close(pipefds[1]); |
| pipefds[1] = -1; |
| } |
| } |