# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2020 Joyent, Inc. FSTYPE = nfs TYPEPROG = nfsmapid TESTPROG = nfsmapid_test ATTMK = $(TYPEPROG) include ../../Makefile.fstype LDLIBS += -L$(ROOT)/usr/lib/nfs -R/usr/lib/nfs $(TYPEPROG) : LDLIBS += -lnsl -lmapid -ldtrace -lidmap COMMON = nfs_resolve.o SRCS = nfsmapid.c ../lib/nfs_resolve.c nfsmapid_server.c DSRC = nfsmapid_dt.d DOBJ = $(DSRC:%.d=%.o) OBJS = nfsmapid.o nfsmapid_server.o $(COMMON) CPPFLAGS += -I../lib -D_POSIX_PTHREAD_SEMANTICS CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-unused-variable CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) # not linted SMATCH=off all: $(TYPEPROG) $(TESTPROG) $(TYPEPROG): $(OBJS) $(DSRC) $(COMPILE.d) -s $(DSRC) -o $(DOBJ) $(OBJS) $(LINK.c) $(ZIGNORE) -o $@ $(DOBJ) $(OBJS) $(LDLIBS) $(POST_PROCESS) nfs_resolve.o: ../lib/nfs_resolve.c $(COMPILE.c) ../lib/nfs_resolve.c TESTSRCS = nfsmapid_test.c TESTOBJS = $(TESTSRCS:%.c=%.o) TEST_OBJS = $(TESTOBJS) $(TESTPROG): $(TEST_OBJS) $(LINK.c) -o $@ $(TEST_OBJS) $(LDLIBS) $(POST_PROCESS) POFILE = nfsmapid.po catalog: $(POFILE) $(POFILE): $(SRCS) $(RM) $@ $(COMPILE.cpp) $(SRCS) > $@.i $(XGETTEXT) $(XGETFLAGS) $@.i sed "/^domain/d" messages.po > $@ $(RM) $@.i messages.po clean: $(RM) $(OBJS) $(TESTPROG) $(TESTOBJS) $(DOBJ) $(POFILE) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct group *_uncached_getgrgid_r(gid_t, struct group *, char *, int); extern struct group *_uncached_getgrnam_r(const char *, struct group *, char *, int); extern struct passwd *_uncached_getpwuid_r(uid_t, struct passwd *, char *, int); extern struct passwd *_uncached_getpwnam_r(const char *, struct passwd *, char *, int); /* * seconds to cache nfsmapid domain info */ #define NFSCFG_DEFAULT_DOMAIN_TMOUT (5 * 60) #define NFSMAPID_DOOR "/var/run/nfsmapid_door" extern void nfsmapid_func(void *, char *, size_t, door_desc_t *, uint_t); extern void check_domain(int); extern void idmap_kcall(int); extern void open_diag_file(void); size_t pwd_buflen = 0; size_t grp_buflen = 0; thread_t sig_thread; static char *MyName; /* * nfscfg_domain_tmout is used by nfsv4-test scripts to query * the nfsmapid daemon for the proper timeout. Don't delete ! */ time_t nfscfg_domain_tmout = NFSCFG_DEFAULT_DOMAIN_TMOUT; /* * Processing for daemonization */ static void daemonize(void) { switch (fork()) { case -1: perror("nfsmapid: can't fork"); exit(2); /* NOTREACHED */ case 0: /* child */ break; default: /* parent */ _exit(0); } if (chdir("/") < 0) syslog(LOG_ERR, gettext("chdir /: %m")); /* * Close stdin, stdout, and stderr. * Open again to redirect input+output */ (void) close(0); (void) close(1); (void) close(2); (void) open("/dev/null", O_RDONLY); (void) open("/dev/null", O_WRONLY); (void) dup(1); (void) setsid(); } /* ARGSUSED */ static void * sig_handler(void *arg) { siginfo_t si; sigset_t sigset; struct timespec tmout; int ret; tmout.tv_nsec = 0; (void) sigemptyset(&sigset); (void) sigaddset(&sigset, SIGHUP); (void) sigaddset(&sigset, SIGTERM); #ifdef DEBUG (void) sigaddset(&sigset, SIGINT); #endif /*CONSTCOND*/ while (1) { tmout.tv_sec = nfscfg_domain_tmout; if ((ret = sigtimedwait(&sigset, &si, &tmout)) != 0) { /* * EAGAIN: no signals arrived during timeout. * check/update config files and continue. */ if (ret == -1 && errno == EAGAIN) { check_domain(0); continue; } switch (si.si_signo) { case SIGHUP: check_domain(1); break; #ifdef DEBUG case SIGINT: exit(0); #endif case SIGTERM: default: exit(si.si_signo); } } } /*NOTREACHED*/ return (NULL); } /* * Thread initialization. Mask out all signals we want our * signal handler to handle for us from any other threads. */ static void thr_init(void) { sigset_t sigset; long thr_flags = (THR_NEW_LWP|THR_DAEMON|THR_SUSPENDED); /* * Before we kick off any other threads, mask out desired * signals from main thread so that any subsequent threads * don't receive said signals. */ (void) thr_sigsetmask(0, NULL, &sigset); (void) sigaddset(&sigset, SIGHUP); (void) sigaddset(&sigset, SIGTERM); #ifdef DEBUG (void) sigaddset(&sigset, SIGINT); #endif (void) thr_sigsetmask(SIG_SETMASK, &sigset, NULL); /* * Create the signal handler thread suspended ! We do things * this way at setup time to minimize the probability of * introducing any race conditions _if_ the process were to * get a SIGHUP signal while creating a new DNS query thread * in get_dns_txt_domain(). */ if (thr_create(NULL, 0, sig_handler, 0, thr_flags, &sig_thread)) { syslog(LOG_ERR, gettext("Failed to create signal handling thread")); exit(4); } } static void daemon_init(void) { struct passwd pwd; struct group grp; char *pwd_buf; char *grp_buf; /* * passwd/group reentrant interfaces limits */ pwd_buflen = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX); grp_buflen = (size_t)sysconf(_SC_GETGR_R_SIZE_MAX); /* * MT initialization is done first so that if there is the * need to fire an additional thread to continue to query * DNS, that thread is started off with the main thread's * sigmask. */ thr_init(); /* * Determine nfsmapid domain. */ check_domain(0); /* * In the case of nfsmapid running diskless, it is important * to get the initial connections to the nameservices * established to prevent problems like opening a devfs * node to contact a nameservice being blocked by the * resolution of an active devfs lookup. * First issue a set*ent to "open" the databases and then * get an entry and finally lookup a bogus entry to trigger * any lazy opens. */ setpwent(); setgrent(); (void) getpwent(); (void) getgrent(); if ((pwd_buf = malloc(pwd_buflen)) == NULL) return; (void) _uncached_getpwnam_r("NF21dmvP", &pwd, pwd_buf, pwd_buflen); (void) _uncached_getpwuid_r(1181794, &pwd, pwd_buf, pwd_buflen); if ((grp_buf = realloc(pwd_buf, grp_buflen)) == NULL) { free(pwd_buf); return; } (void) _uncached_getgrnam_r("NF21dmvP", &grp, grp_buf, grp_buflen); (void) _uncached_getgrgid_r(1181794, &grp, grp_buf, grp_buflen); free(grp_buf); } static int start_svcs(void) { int doorfd = -1; #ifdef DEBUG int dfd; #endif if ((doorfd = door_create(nfsmapid_func, NULL, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { syslog(LOG_ERR, "Unable to create door: %m\n"); return (1); } #ifdef DEBUG /* * Create a file system path for the door */ if ((dfd = open(NFSMAPID_DOOR, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) { syslog(LOG_ERR, "Unable to open %s: %m\n", NFSMAPID_DOOR); (void) close(doorfd); return (1); } /* * Clean up any stale associations */ (void) fdetach(NFSMAPID_DOOR); /* * Register in namespace to pass to the kernel to door_ki_open */ if (fattach(doorfd, NFSMAPID_DOOR) == -1) { syslog(LOG_ERR, "Unable to fattach door: %m\n"); (void) close(dfd); (void) close(doorfd); return (1); } (void) close(dfd); #endif /* * Now that we're actually running, go * ahead and flush the kernel flushes * Pass door name to kernel for door_ki_open */ idmap_kcall(doorfd); /* * Wait for incoming calls */ /*CONSTCOND*/ while (1) (void) pause(); syslog(LOG_ERR, gettext("Door server exited")); return (10); } /* ARGSUSED */ int main(int argc, char **argv) { MyName = argv[0]; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* _check_services() framework setup */ (void) _create_daemon_lock(NFSMAPID, DAEMON_UID, DAEMON_GID); /* * Open diag file in /var/run while we've got the perms */ open_diag_file(); /* * Initialize the daemon to basic + sys_nfs */ #ifndef DEBUG if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS, (char *)NULL) == -1) { (void) fprintf(stderr, gettext("%s PRIV_SYS_NFS privilege " "missing\n"), MyName); exit(1); } #endif /* * Take away a subset of basic, while this is not the absolute * minimum, it is important that it is unique among other * daemons to insure that we get a unique cred that will * result in a unique open_owner. If not, we run the risk * of a diskless client deadlocking with a thread holding * the open_owner seqid lock while upcalling the daemon. * XXX This restriction will go away once we stop holding * XXX open_owner lock across rfscalls! */ (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_LINK_ANY, PRIV_PROC_SESSION, (char *)NULL); #ifndef DEBUG daemonize(); switch (_enter_daemon_lock(NFSMAPID)) { case 0: break; case -1: syslog(LOG_ERR, "error locking for %s: %s", NFSMAPID, strerror(errno)); exit(3); default: /* daemon was already running */ exit(0); } #endif openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON); /* Initialize daemon subsystems */ daemon_init(); /* start services */ return (start_svcs()); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ provider nfsmapid { probe daemon__domain(string); }; #pragma D attributes Private/Private/Common provider nfsmapid provider #pragma D attributes Private/Private/Common provider nfsmapid module #pragma D attributes Private/Private/Common provider nfsmapid function #pragma D attributes Private/Private/Common provider nfsmapid name #pragma D attributes Private/Private/Common provider nfsmapid args /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Door server routines for nfsmapid daemon * Translate NFSv4 users and groups between numeric and string values */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nfs_resolve.h" #define UID_MAX_STR_LEN 11 /* Digits in UID_MAX + 1 */ #define DIAG_FILE "/var/run/nfs4_domain" /* * idmap_kcall() takes a door descriptor as it's argument when we * need to (re)establish the in-kernel door handles. When we only * want to flush the id kernel caches, we don't redo the door setup. */ #define FLUSH_KCACHES_ONLY (int)-1 FILE *n4_fp; int n4_fd; extern size_t pwd_buflen; extern size_t grp_buflen; extern thread_t sig_thread; /* * Prototypes */ extern void check_domain(int); extern void idmap_kcall(int); extern int _nfssys(int, void *); extern int valid_domain(const char *); extern int validate_id_str(const char *); extern int extract_domain(char *, char **, char **); extern void update_diag_file(char *); extern void *cb_update_domain(void *); extern int cur_domain_null(void); void nfsmapid_str_uid(struct mapid_arg *argp, size_t arg_size) { struct mapid_res result; struct passwd pwd; struct passwd *pwd_ptr; int pwd_rc; char *pwd_buf; char *user; char *domain; idmap_stat rc; if (argp->u_arg.len <= 0 || arg_size < MAPID_ARG_LEN(argp->u_arg.len)) { result.status = NFSMAPID_INVALID; result.u_res.uid = UID_NOBODY; goto done; } if (!extract_domain(argp->str, &user, &domain)) { unsigned long id; /* * Invalid "user@domain" string. Still, the user * part might be an encoded uid, so do a final check. * Remember, domain part of string was not set since * not a valid string. */ if (!validate_id_str(user)) { result.status = NFSMAPID_UNMAPPABLE; result.u_res.uid = UID_NOBODY; goto done; } errno = 0; id = strtoul(user, (char **)NULL, 10); /* * We don't accept ephemeral ids from the wire. */ if (errno || id > UID_MAX) { result.status = NFSMAPID_UNMAPPABLE; result.u_res.uid = UID_NOBODY; goto done; } result.u_res.uid = (uid_t)id; result.status = NFSMAPID_NUMSTR; goto done; } /* * String properly constructed. Now we check for domain and * group validity. */ if (!cur_domain_null() && !valid_domain(domain)) { /* * If the domain part of the string does not * match the NFS domain, try to map it using * idmap service. */ rc = idmap_getuidbywinname(user, domain, 0, &result.u_res.uid); if (rc != IDMAP_SUCCESS) { result.status = NFSMAPID_BADDOMAIN; result.u_res.uid = UID_NOBODY; goto done; } result.status = NFSMAPID_OK; goto done; } if ((pwd_buf = malloc(pwd_buflen)) == NULL || (pwd_rc = getpwnam_r(user, &pwd, pwd_buf, pwd_buflen, &pwd_ptr)) != 0 || pwd_ptr == NULL) { if (pwd_buf == NULL || pwd_rc != 0) result.status = NFSMAPID_INTERNAL; else { /* * Not a valid user */ result.status = NFSMAPID_NOTFOUND; free(pwd_buf); } result.u_res.uid = UID_NOBODY; goto done; } /* * Valid user entry */ result.u_res.uid = pwd.pw_uid; result.status = NFSMAPID_OK; free(pwd_buf); done: (void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0); } /* ARGSUSED1 */ void nfsmapid_uid_str(struct mapid_arg *argp, size_t arg_size) { struct mapid_res result; struct mapid_res *resp; struct passwd pwd; struct passwd *pwd_ptr; char *pwd_buf = NULL; char *idmap_buf = NULL; uid_t uid = argp->u_arg.uid; size_t uid_str_len; char *pw_str; size_t pw_str_len; char *at_str; size_t at_str_len; char dom_str[DNAMEMAX]; size_t dom_str_len; idmap_stat rc; if (uid == (uid_t)-1) { /* * Sentinel uid is not a valid id */ resp = &result; resp->status = NFSMAPID_BADID; resp->u_res.len = 0; goto done; } /* * Make local copy of domain for further manipuation * NOTE: mapid_get_domain() returns a ptr to TSD. */ if (cur_domain_null()) { dom_str_len = 0; dom_str[0] = '\0'; } else { dom_str_len = strlcpy(dom_str, mapid_get_domain(), DNAMEMAX); } /* * If uid is ephemeral then resolve it using idmap service */ if (uid > UID_MAX) { rc = idmap_getwinnamebyuid(uid, 0, &idmap_buf, NULL); if (rc != IDMAP_SUCCESS) { /* * We don't put stringified ephemeral uids on * the wire. */ resp = &result; resp->status = NFSMAPID_UNMAPPABLE; resp->u_res.len = 0; goto done; } /* * idmap_buf is already in the desired form i.e. name@domain */ pw_str = idmap_buf; pw_str_len = strlen(pw_str); at_str_len = dom_str_len = 0; at_str = ""; dom_str[0] = '\0'; goto gen_result; } /* * Handling non-ephemeral uids * * We want to encode the uid into a literal string... : * * - upon failure to allocate space from the heap * - if there is no current domain configured * - if there is no such uid in the passwd DB's */ if ((pwd_buf = malloc(pwd_buflen)) == NULL || dom_str_len == 0 || getpwuid_r(uid, &pwd, pwd_buf, pwd_buflen, &pwd_ptr) != 0 || pwd_ptr == NULL) { /* * If we could not allocate from the heap, try * allocating from the stack as a last resort. */ if (pwd_buf == NULL && (pwd_buf = alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) { resp = &result; resp->status = NFSMAPID_INTERNAL; resp->u_res.len = 0; goto done; } /* * Constructing literal string without '@' so that * we'll know that it's not a user, but rather a * uid encoded string. */ pw_str = pwd_buf; (void) sprintf(pw_str, "%u", uid); pw_str_len = strlen(pw_str); at_str_len = dom_str_len = 0; at_str = ""; dom_str[0] = '\0'; } else { /* * Otherwise, we construct the "user@domain" string if * it's not already in that form. */ pw_str = pwd.pw_name; pw_str_len = strlen(pw_str); if (strchr(pw_str, '@') == NULL) { at_str = "@"; at_str_len = 1; } else { at_str_len = dom_str_len = 0; at_str = ""; dom_str[0] = '\0'; } } gen_result: uid_str_len = pw_str_len + at_str_len + dom_str_len; if ((resp = alloca(MAPID_RES_LEN(uid_str_len))) == NULL) { resp = &result; resp->status = NFSMAPID_INTERNAL; resp->u_res.len = 0; goto done; } /* LINTED format argument to sprintf */ (void) sprintf(resp->str, "%s%s%s", pw_str, at_str, dom_str); resp->u_res.len = uid_str_len; if (pwd_buf) free(pwd_buf); if (idmap_buf) idmap_free(idmap_buf); resp->status = NFSMAPID_OK; done: /* * There is a chance that the door_return will fail because the * resulting string is too large, try to indicate that if possible */ if (door_return((char *)resp, MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) { resp->status = NFSMAPID_INTERNAL; resp->u_res.len = 0; (void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0); } } void nfsmapid_str_gid(struct mapid_arg *argp, size_t arg_size) { struct mapid_res result; struct group grp; struct group *grp_ptr; int grp_rc; char *grp_buf; char *group; char *domain; idmap_stat rc; if (argp->u_arg.len <= 0 || arg_size < MAPID_ARG_LEN(argp->u_arg.len)) { result.status = NFSMAPID_INVALID; result.u_res.gid = GID_NOBODY; goto done; } if (!extract_domain(argp->str, &group, &domain)) { unsigned long id; /* * Invalid "group@domain" string. Still, the * group part might be an encoded gid, so do a * final check. Remember, domain part of string * was not set since not a valid string. */ if (!validate_id_str(group)) { result.status = NFSMAPID_UNMAPPABLE; result.u_res.gid = GID_NOBODY; goto done; } errno = 0; id = strtoul(group, (char **)NULL, 10); /* * We don't accept ephemeral ids from the wire. */ if (errno || id > UID_MAX) { result.status = NFSMAPID_UNMAPPABLE; result.u_res.gid = GID_NOBODY; goto done; } result.u_res.gid = (gid_t)id; result.status = NFSMAPID_NUMSTR; goto done; } /* * String properly constructed. Now we check for domain and * group validity. */ if (!cur_domain_null() && !valid_domain(domain)) { /* * If the domain part of the string does not * match the NFS domain, try to map it using * idmap service. */ rc = idmap_getgidbywinname(group, domain, 0, &result.u_res.gid); if (rc != IDMAP_SUCCESS) { result.status = NFSMAPID_BADDOMAIN; result.u_res.gid = GID_NOBODY; goto done; } result.status = NFSMAPID_OK; goto done; } if ((grp_buf = malloc(grp_buflen)) == NULL || (grp_rc = getgrnam_r(group, &grp, grp_buf, grp_buflen, &grp_ptr)) != 0 || grp_ptr == NULL) { if (grp_buf == NULL || grp_rc != 0) result.status = NFSMAPID_INTERNAL; else { /* * Not a valid group */ result.status = NFSMAPID_NOTFOUND; free(grp_buf); } result.u_res.gid = GID_NOBODY; goto done; } /* * Valid group entry */ result.status = NFSMAPID_OK; result.u_res.gid = grp.gr_gid; free(grp_buf); done: (void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0); } /* ARGSUSED1 */ void nfsmapid_gid_str(struct mapid_arg *argp, size_t arg_size) { struct mapid_res result; struct mapid_res *resp; struct group grp; struct group *grp_ptr; char *grp_buf = NULL; char *idmap_buf = NULL; idmap_stat rc; gid_t gid = argp->u_arg.gid; size_t gid_str_len; char *gr_str; size_t gr_str_len; char *at_str; size_t at_str_len; char dom_str[DNAMEMAX]; size_t dom_str_len; if (gid == (gid_t)-1) { /* * Sentinel gid is not a valid id */ resp = &result; resp->status = NFSMAPID_BADID; resp->u_res.len = 0; goto done; } /* * Make local copy of domain for further manipuation * NOTE: mapid_get_domain() returns a ptr to TSD. */ if (cur_domain_null()) { dom_str_len = 0; dom_str[0] = '\0'; } else { dom_str_len = strlen(mapid_get_domain()); bcopy(mapid_get_domain(), dom_str, dom_str_len); dom_str[dom_str_len] = '\0'; } /* * If gid is ephemeral then resolve it using idmap service */ if (gid > UID_MAX) { rc = idmap_getwinnamebygid(gid, 0, &idmap_buf, NULL); if (rc != IDMAP_SUCCESS) { /* * We don't put stringified ephemeral gids on * the wire. */ resp = &result; resp->status = NFSMAPID_UNMAPPABLE; resp->u_res.len = 0; goto done; } /* * idmap_buf is already in the desired form i.e. name@domain */ gr_str = idmap_buf; gr_str_len = strlen(gr_str); at_str_len = dom_str_len = 0; at_str = ""; dom_str[0] = '\0'; goto gen_result; } /* * Handling non-ephemeral gids * * We want to encode the gid into a literal string... : * * - upon failure to allocate space from the heap * - if there is no current domain configured * - if there is no such gid in the group DB's */ if ((grp_buf = malloc(grp_buflen)) == NULL || dom_str_len == 0 || getgrgid_r(gid, &grp, grp_buf, grp_buflen, &grp_ptr) != 0 || grp_ptr == NULL) { /* * If we could not allocate from the heap, try * allocating from the stack as a last resort. */ if (grp_buf == NULL && (grp_buf = alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) { resp = &result; resp->status = NFSMAPID_INTERNAL; resp->u_res.len = 0; goto done; } /* * Constructing literal string without '@' so that * we'll know that it's not a group, but rather a * gid encoded string. */ gr_str = grp_buf; (void) sprintf(gr_str, "%u", gid); gr_str_len = strlen(gr_str); at_str_len = dom_str_len = 0; at_str = ""; dom_str[0] = '\0'; } else { /* * Otherwise, we construct the "group@domain" string if * it's not already in that form. */ gr_str = grp.gr_name; gr_str_len = strlen(gr_str); if (strchr(gr_str, '@') == NULL) { at_str = "@"; at_str_len = 1; } else { at_str_len = dom_str_len = 0; at_str = ""; dom_str[0] = '\0'; } } gen_result: gid_str_len = gr_str_len + at_str_len + dom_str_len; if ((resp = alloca(MAPID_RES_LEN(gid_str_len))) == NULL) { resp = &result; resp->status = NFSMAPID_INTERNAL; resp->u_res.len = 0; goto done; } /* LINTED format argument to sprintf */ (void) sprintf(resp->str, "%s%s%s", gr_str, at_str, dom_str); resp->u_res.len = gid_str_len; if (grp_buf) free(grp_buf); if (idmap_buf) idmap_free(idmap_buf); resp->status = NFSMAPID_OK; done: /* * There is a chance that the door_return will fail because the * resulting string is too large, try to indicate that if possible */ if (door_return((char *)resp, MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) { resp->status = NFSMAPID_INTERNAL; resp->u_res.len = 0; (void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0); } } void nfsmapid_server_netinfo(refd_door_args_t *referral_args, size_t arg_size) { char *res; int res_size; int error; int srsz = 0; char host[MAXHOSTNAMELEN]; utf8string *nfsfsloc_args; refd_door_res_t *door_res; refd_door_res_t failed_res; struct nfs_fsl_info *nfs_fsloc_res; if (arg_size < sizeof (refd_door_args_t)) { failed_res.res_status = EINVAL; res = (char *)&failed_res; res_size = sizeof (refd_door_res_t); syslog(LOG_ERR, "nfsmapid_server_netinfo failed: Invalid data\n"); goto send_response; } if (decode_args(xdr_utf8string, (refd_door_args_t *)referral_args, (caddr_t *)&nfsfsloc_args, sizeof (utf8string))) { syslog(LOG_ERR, "cannot allocate memory"); failed_res.res_status = ENOMEM; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = sizeof (refd_door_res_t); goto send_response; } if (nfsfsloc_args->utf8string_len >= MAXHOSTNAMELEN) { syslog(LOG_ERR, "argument too large"); failed_res.res_status = EOVERFLOW; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = sizeof (refd_door_res_t); goto send_response; } snprintf(host, nfsfsloc_args->utf8string_len + 1, "%s", nfsfsloc_args->utf8string_val); nfs_fsloc_res = get_nfs4ref_info(host, NFS_PORT, NFS_V4); xdr_free(xdr_utf8string, (char *)&nfsfsloc_args); if (nfs_fsloc_res) { error = 0; error = encode_res(xdr_nfs_fsl_info, &door_res, (caddr_t)nfs_fsloc_res, &res_size); free_nfs4ref_info(nfs_fsloc_res); if (error != 0) { syslog(LOG_ERR, "error allocating fs_locations " "results buffer"); failed_res.res_status = error; failed_res.xdr_len = srsz; res = (caddr_t)&failed_res; res_size = sizeof (refd_door_res_t); } else { door_res->res_status = 0; res = (caddr_t)door_res; } } else { failed_res.res_status = EINVAL; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = sizeof (refd_door_res_t); } send_response: srsz = res_size; errno = 0; error = door_return(res, res_size, NULL, 0); if (errno == E2BIG) { failed_res.res_status = EOVERFLOW; failed_res.xdr_len = srsz; res = (caddr_t)&failed_res; res_size = sizeof (refd_door_res_t); } else { res = NULL; res_size = 0; } door_return(res, res_size, NULL, 0); } /* ARGSUSED */ void nfsmapid_func(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n_desc) { struct mapid_arg *mapargp; struct mapid_res mapres; refd_door_args_t *referral_args; /* * Make sure we have a valid argument */ if (arg_size < sizeof (struct mapid_arg)) { mapres.status = NFSMAPID_INVALID; mapres.u_res.len = 0; (void) door_return((char *)&mapres, sizeof (struct mapid_res), NULL, 0); return; } /* LINTED pointer cast */ mapargp = (struct mapid_arg *)argp; referral_args = (refd_door_args_t *)argp; switch (mapargp->cmd) { case NFSMAPID_STR_UID: nfsmapid_str_uid(mapargp, arg_size); return; case NFSMAPID_UID_STR: nfsmapid_uid_str(mapargp, arg_size); return; case NFSMAPID_STR_GID: nfsmapid_str_gid(mapargp, arg_size); return; case NFSMAPID_GID_STR: nfsmapid_gid_str(mapargp, arg_size); return; case NFSMAPID_SRV_NETINFO: nfsmapid_server_netinfo(referral_args, arg_size); default: break; } mapres.status = NFSMAPID_INVALID; mapres.u_res.len = 0; (void) door_return((char *)&mapres, sizeof (struct mapid_res), NULL, 0); } /* * mapid_get_domain() always returns a ptr to TSD, so the * check for a NULL domain is not a simple comparison with * NULL but we need to check the contents of the TSD data. */ int cur_domain_null(void) { char *p; if ((p = mapid_get_domain()) == NULL) return (1); return (p[0] == '\0'); } int extract_domain(char *cp, char **upp, char **dpp) { /* * Caller must insure that the string is valid */ *upp = cp; if ((*dpp = strchr(cp, '@')) == NULL) return (0); *(*dpp)++ = '\0'; return (1); } int valid_domain(const char *dom) { const char *whoami = "valid_domain"; if (!mapid_stdchk_domain(dom)) { syslog(LOG_ERR, gettext("%s: Invalid inbound domain name %s."), whoami, dom); return (0); } /* * NOTE: mapid_get_domain() returns a ptr to TSD. */ return (strcasecmp(dom, mapid_get_domain()) == 0); } int validate_id_str(const char *id) { while (*id) { if (!isdigit(*id++)) return (0); } return (1); } void idmap_kcall(int door_id) { struct nfsidmap_args args; if (door_id >= 0) { args.state = 1; args.did = door_id; } else { args.state = 0; args.did = 0; } (void) _nfssys(NFS_IDMAP, &args); } /* * Get the current NFS domain. * * If nfsmapid_domain is set in NFS SMF, then it is the NFS domain; * otherwise, the DNS domain is used. */ void check_domain(int sighup) { const char *whoami = "check_domain"; static int setup_done = 0; static cb_t cb; /* * Construct the arguments to be passed to libmapid interface * If called in response to a SIGHUP, reset any cached DNS TXT * RR state. */ cb.fcn = cb_update_domain; cb.signal = sighup; mapid_reeval_domain(&cb); /* * Restart the signal handler thread if we're still setting up */ if (!setup_done) { setup_done = 1; if (thr_continue(sig_thread)) { syslog(LOG_ERR, gettext("%s: Fatal error: signal " "handler thread could not be restarted."), whoami); exit(6); } } } /* * Need to be able to open the DIAG_FILE before nfsmapid(8) * releases it's root priviledges. The DIAG_FILE then remains * open for the duration of this nfsmapid instance via n4_fd. */ void open_diag_file() { static int msg_done = 0; if ((n4_fp = fopen(DIAG_FILE, "w+")) != NULL) { n4_fd = fileno(n4_fp); return; } if (msg_done) return; syslog(LOG_ERR, "Failed to create %s. Enable syslog " "daemon.debug for more info", DIAG_FILE); msg_done = 1; } /* * When a new domain name is configured, save to DIAG_FILE * and log to syslog, with LOG_DEBUG level (if configured). */ void update_diag_file(char *new) { char buf[DNAMEMAX]; ssize_t n; size_t len; (void) lseek(n4_fd, (off_t)0, SEEK_SET); (void) ftruncate(n4_fd, 0); (void) snprintf(buf, DNAMEMAX, "%s\n", new); len = strlen(buf); n = write(n4_fd, buf, len); if (n < 0 || n < len) syslog(LOG_DEBUG, "Could not write %s to diag file", new); (void) fsync(n4_fd); syslog(LOG_DEBUG, "nfsmapid domain = %s", new); } /* * Callback function for libmapid. This will be called * by the lib, everytime the nfsmapid(8) domain changes. */ void * cb_update_domain(void *arg) { char *new_dname = (char *)arg; DTRACE_PROBE1(nfsmapid, daemon__domain, new_dname); update_diag_file(new_dname); idmap_kcall(FLUSH_KCACHES_ONLY); return (NULL); } bool_t xdr_utf8string(XDR *xdrs, utf8string *objp) { if (xdrs->x_op != XDR_FREE) return (xdr_bytes(xdrs, (char **)&objp->utf8string_val, (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING)); return (TRUE); } int decode_args(xdrproc_t xdrfunc, refd_door_args_t *argp, caddr_t *xdrargs, int size) { XDR xdrs; caddr_t tmpargs = (caddr_t)&((refd_door_args_t *)argp)->xdr_arg; size_t arg_size = ((refd_door_args_t *)argp)->xdr_len; xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE); *xdrargs = calloc(1, size); if (*xdrargs == NULL) { syslog(LOG_ERR, "error allocating arguments buffer"); return (ENOMEM); } if (!(*xdrfunc)(&xdrs, *xdrargs)) { free(*xdrargs); *xdrargs = NULL; syslog(LOG_ERR, "error decoding arguments"); return (EINVAL); } return (0); } int encode_res( xdrproc_t xdrfunc, refd_door_res_t **results, caddr_t resp, int *size) { XDR xdrs; *size = xdr_sizeof((*xdrfunc), resp); *results = malloc(sizeof (refd_door_res_t) + *size); if (*results == NULL) { return (ENOMEM); } (*results)->xdr_len = *size; *size = sizeof (refd_door_res_t) + (*results)->xdr_len; xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res), (*results)->xdr_len, XDR_ENCODE); if (!(*xdrfunc)(&xdrs, resp)) { (*results)->res_status = EINVAL; syslog(LOG_ERR, "error encoding results"); return ((*results)->res_status); } (*results)->res_status = 0; return ((*results)->res_status); } bool_t xdr_knetconfig(XDR *xdrs, struct knetconfig *objp) { rpc_inline_t *buf; int i; u_longlong_t dev64; #if !defined(_LP64) uint32_t major, minor; #endif if (!xdr_u_int(xdrs, &objp->knc_semantics)) return (FALSE); if (!xdr_opaque(xdrs, objp->knc_protofmly, KNC_STRSIZE)) return (FALSE); if (!xdr_opaque(xdrs, objp->knc_proto, KNC_STRSIZE)) return (FALSE); /* * For interoperability between 32-bit daemon and 64-bit kernel, * we always treat dev_t as 64-bit number and do the expanding * or compression of dev_t as needed. * We have to hand craft the conversion since there is no available * function in ddi.c. Besides ddi.c is available only in the kernel * and we want to keep both user and kernel of xdr_knetconfig() the * same for consistency. */ if (xdrs->x_op == XDR_ENCODE) { #if defined(_LP64) dev64 = objp->knc_rdev; #else major = (objp->knc_rdev >> NBITSMINOR32) & MAXMAJ32; minor = objp->knc_rdev & MAXMIN32; dev64 = (((unsigned long long)major) << NBITSMINOR64) | minor; #endif if (!xdr_u_longlong_t(xdrs, &dev64)) return (FALSE); } if (xdrs->x_op == XDR_DECODE) { #if defined(_LP64) if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&objp->knc_rdev)) return (FALSE); #else if (!xdr_u_longlong_t(xdrs, &dev64)) return (FALSE); major = (dev64 >> NBITSMINOR64) & L_MAXMAJ32; minor = dev64 & L_MAXMIN32; objp->knc_rdev = (major << L_BITSMINOR32) | minor; #endif } if (xdrs->x_op == XDR_ENCODE) { buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, sizeof (uint_t), (xdrproc_t)xdr_u_int)) return (FALSE); } else { uint_t *genp; for (i = 0, genp = objp->knc_unused; i < 8; i++) { #if defined(_LP64) || defined(_KERNEL) IXDR_PUT_U_INT32(buf, *genp++); #else IXDR_PUT_U_LONG(buf, *genp++); #endif } } return (TRUE); } else if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, sizeof (uint_t), (xdrproc_t)xdr_u_int)) return (FALSE); } else { uint_t *genp; for (i = 0, genp = objp->knc_unused; i < 8; i++) { #if defined(_LP64) || defined(_KERNEL) *genp++ = IXDR_GET_U_INT32(buf); #else *genp++ = IXDR_GET_U_LONG(buf); #endif } } return (TRUE); } if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, sizeof (uint_t), (xdrproc_t)xdr_u_int)) return (FALSE); return (TRUE); } /* * used by NFSv4 referrals to get info needed for NFSv4 referral mount. */ bool_t xdr_nfs_fsl_info(XDR *xdrs, struct nfs_fsl_info *objp) { if (!xdr_u_int(xdrs, &objp->netbuf_len)) return (FALSE); if (!xdr_u_int(xdrs, &objp->netnm_len)) return (FALSE); if (!xdr_u_int(xdrs, &objp->knconf_len)) return (FALSE); if (!xdr_string(xdrs, &objp->netname, ~0)) return (FALSE); if (!xdr_pointer(xdrs, (char **)&objp->addr, objp->netbuf_len, (xdrproc_t)xdr_netbuf)) return (FALSE); if (!xdr_pointer(xdrs, (char **)&objp->knconf, objp->knconf_len, (xdrproc_t)xdr_knetconfig)) return (FALSE); return (TRUE); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Test nfsmapid. This program is not shipped on the binary release. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char nobody_str[] = "nobody"; static int nfs_idmap_str_uid(utf8string *, uid_t *); static int nfs_idmap_uid_str(uid_t, utf8string *); static int nfs_idmap_str_gid(utf8string *, gid_t *); static int nfs_idmap_gid_str(gid_t, utf8string *); static void usage() { fprintf(stderr, gettext( "\nUsage:\tstr2uid string\n" "\tstr2gid string\n" "\tuid2str uid\n" "\tgid2str gid\n" "\techo string\n" "\texit|quit\n")); } static int read_line(char *buf, int size) { int len; /* read the next line. If cntl-d, return with zero char count */ printf(gettext("\n> ")); if (fgets(buf, size, stdin) == NULL) return (0); len = strlen(buf); buf[--len] = '\0'; return (len); } static int parse_input_line(char *input_line, int *argc, char ***argv) { const char nil = '\0'; char *chptr; int chr_cnt; int arg_cnt = 0; int ch_was_space = 1; int ch_is_space; chr_cnt = strlen(input_line); /* Count the arguments in the input_line string */ *argc = 1; for (chptr = &input_line[0]; *chptr != nil; chptr++) { ch_is_space = isspace(*chptr); if (ch_is_space && !ch_was_space) { (*argc)++; } ch_was_space = ch_is_space; } if (ch_was_space) { (*argc)--; } /* minus trailing spaces */ /* Now that we know how many args calloc the argv array */ *argv = calloc((*argc)+1, sizeof (char *)); chptr = (char *)(&input_line[0]); for (ch_was_space = 1; *chptr != nil; chptr++) { ch_is_space = isspace(*chptr); if (ch_is_space) { *chptr = nil; /* replace each space with nil */ } else if (ch_was_space) { /* begining of word? */ (*argv)[arg_cnt++] = chptr; /* new argument ? */ } ch_was_space = ch_is_space; } return (chr_cnt); } char * mapstat(int stat) { switch (stat) { case NFSMAPID_OK: return ("NFSMAPID_OK"); case NFSMAPID_NUMSTR: return ("NFSMAPID_NUMSTR"); case NFSMAPID_UNMAPPABLE: return ("NFSMAPID_UNMAPPABLE"); case NFSMAPID_INVALID: return ("NFSMAPID_INVALID"); case NFSMAPID_INTERNAL: return ("NFSMAPID_INTERNAL"); case NFSMAPID_BADDOMAIN: return ("NFSMAPID_BADDOMAIN"); case NFSMAPID_BADID: return ("NFSMAPID_BADID"); case NFSMAPID_NOTFOUND: return ("NFSMAPID_NOTFOUND"); case EINVAL: return ("EINVAL"); case ECOMM: return ("ECOMM"); case ENOMEM: return ("ENOMEM"); default: printf(" unknown error %d ", stat); return ("..."); } } int do_test(char *input_buf) { int argc, seal_argc; char **argv, **argv_array; char *cmd; int i, bufsize = 512; char str_buf[512]; utf8string str; uid_t uid; gid_t gid; int stat; argv = 0; if (parse_input_line(input_buf, &argc, &argv) == 0) { printf(gettext("\n")); return (1); } /* * remember argv_array address, which is memory calloc'd by * parse_input_line, so it can be free'd at the end of the loop. */ argv_array = argv; if (argc < 1) { usage(); free(argv_array); return (0); } cmd = argv[0]; if (strcmp(cmd, "str2uid") == 0) { if (argc < 2) { usage(); free(argv_array); return (0); } str.utf8string_val = argv[1]; str.utf8string_len = strlen(argv[1]); stat = nfs_idmap_str_uid(&str, &uid); printf(gettext("%u stat=%s \n"), uid, mapstat(stat)); } else if (strcmp(cmd, "str2gid") == 0) { if (argc < 2) { usage(); free(argv_array); return (0); } str.utf8string_val = argv[1]; str.utf8string_len = strlen(argv[1]); stat = nfs_idmap_str_gid(&str, &gid); printf(gettext("%u stat=%s \n"), gid, mapstat(stat)); } else if (strcmp(cmd, "uid2str") == 0) { if (argc < 2) { usage(); free(argv_array); return (0); } uid = atoi(argv[1]); bzero(str_buf, bufsize); str.utf8string_val = str_buf; stat = nfs_idmap_uid_str(uid, &str); printf(gettext("%s stat=%s\n"), str.utf8string_val, mapstat(stat)); } else if (strcmp(cmd, "gid2str") == 0) { if (argc < 2) { usage(); free(argv_array); return (0); } gid = atoi(argv[1]); bzero(str_buf, bufsize); str.utf8string_val = str_buf; stat = nfs_idmap_gid_str(gid, &str); printf(gettext("%s stat=%s\n"), str.utf8string_val, mapstat(stat)); } else if (strcmp(cmd, "echo") == 0) { for (i = 1; i < argc; i++) printf("%s ", argv[i]); printf("\n"); } else if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "quit") == 0) { printf(gettext("\n")); free(argv_array); return (1); } else usage(); /* free argv array */ free(argv_array); return (0); } int main(int argc, char **argv) { char buf[512]; int len, ret; (void) setlocale(LC_ALL, ""); #ifndef TEXT_DOMAIN #define TEXT_DOMAIN "" #endif (void) textdomain(TEXT_DOMAIN); usage(); /* * Loop, repeatedly calling parse_input_line() to get the * next line and parse it into argc and argv. Act on the * arguements found on the line. */ do { len = read_line(buf, 512); if (len) ret = do_test(buf); } while (!ret); return (0); } #define NFSMAPID_DOOR "/var/run/nfsmapid_door" /* * Gen the door handle for connecting to the nfsmapid process. * Keep the door cached. This call may be made quite often. */ int nfs_idmap_doorget() { static int doorfd = -1; if (doorfd != -1) return (doorfd); if ((doorfd = open(NFSMAPID_DOOR, O_RDWR)) == -1) { perror(NFSMAPID_DOOR); exit(1); } return (doorfd); } /* * Convert a user utf-8 string identifier into its local uid. */ int nfs_idmap_str_uid(utf8string *u8s, uid_t *uid) { struct mapid_arg *mapargp; struct mapid_res mapres; struct mapid_res *mapresp = &mapres; struct mapid_res *resp = mapresp; door_arg_t door_args; int doorfd; int error = 0; static int msg_done = 0; if (!u8s || !u8s->utf8string_val || !u8s->utf8string_len || (u8s->utf8string_val[0] == '\0')) { error = EINVAL; goto s2u_done; } if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) { /* * If "nobody", just short circuit and bail */ *uid = UID_NOBODY; goto s2u_done; } if ((mapargp = malloc(MAPID_ARG_LEN(u8s->utf8string_len))) == NULL) { (void) fprintf(stderr, "Unable to malloc %d bytes\n", MAPID_ARG_LEN(u8s->utf8string_len)); error = ENOMEM; goto s2u_done; } mapargp->cmd = NFSMAPID_STR_UID; mapargp->u_arg.len = u8s->utf8string_len; (void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len); mapargp->str[mapargp->u_arg.len] = '\0'; door_args.data_ptr = (char *)mapargp; door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len); door_args.desc_ptr = NULL; door_args.desc_num = 0; door_args.rbuf = (char *)mapresp; door_args.rsize = sizeof (struct mapid_res); /* * call to the nfsmapid daemon */ if ((doorfd = nfs_idmap_doorget()) == -1) { if (!msg_done) { fprintf(stderr, "nfs_idmap_str_uid: Can't communicate" " with mapping daemon nfsmapid\n"); msg_done = 1; } error = ECOMM; free(mapargp); goto s2u_done; } if (door_call(doorfd, &door_args) == -1) { perror("door_call failed"); error = EINVAL; free(mapargp); goto s2u_done; } free(mapargp); resp = (struct mapid_res *)door_args.rbuf; switch (resp->status) { case NFSMAPID_OK: *uid = resp->u_res.uid; break; case NFSMAPID_NUMSTR: *uid = resp->u_res.uid; error = resp->status; goto out; default: case NFSMAPID_UNMAPPABLE: case NFSMAPID_INVALID: case NFSMAPID_INTERNAL: case NFSMAPID_BADDOMAIN: case NFSMAPID_BADID: case NFSMAPID_NOTFOUND: error = resp->status; goto s2u_done; } s2u_done: if (error) *uid = UID_NOBODY; out: if (resp != mapresp) munmap(door_args.rbuf, door_args.rsize); return (error); } /* * Convert a uid into its utf-8 string representation. */ int nfs_idmap_uid_str(uid_t uid, /* uid to map */ utf8string *u8s) /* resulting utf-8 string for uid */ { struct mapid_arg maparg; struct mapid_res mapres; struct mapid_res *mapresp = &mapres; struct mapid_res *resp = mapresp; door_arg_t door_args; int doorfd; int error = 0; static int msg_done = 0; if (uid == UID_NOBODY) { u8s->utf8string_len = strlen("nobody"); u8s->utf8string_val = nobody_str; goto u2s_done; } /* * Daemon call... */ maparg.cmd = NFSMAPID_UID_STR; maparg.u_arg.uid = uid; door_args.data_ptr = (char *)&maparg; door_args.data_size = sizeof (struct mapid_arg); door_args.desc_ptr = NULL; door_args.desc_num = 0; door_args.rbuf = (char *)mapresp; door_args.rsize = sizeof (struct mapid_res); if ((doorfd = nfs_idmap_doorget()) == -1) { if (!msg_done) { fprintf(stderr, "nfs_idmap_uid_str: Can't " "communicate with mapping daemon nfsmapid\n"); msg_done = 1; } error = ECOMM; goto u2s_done; } if (door_call(doorfd, &door_args) == -1) { perror("door_call failed"); error = EINVAL; goto u2s_done; } resp = (struct mapid_res *)door_args.rbuf; if (resp->status != NFSMAPID_OK) { error = resp->status; goto u2s_done; } if (resp->u_res.len != strlen(resp->str)) { (void) fprintf(stderr, "Incorrect length %d expected %d\n", resp->u_res.len, strlen(resp->str)); error = NFSMAPID_INVALID; goto u2s_done; } u8s->utf8string_len = resp->u_res.len; bcopy(resp->str, u8s->utf8string_val, u8s->utf8string_len); u2s_done: if (resp != mapresp) munmap(door_args.rbuf, door_args.rsize); return (error); } /* * Convert a group utf-8 string identifier into its local gid. */ int nfs_idmap_str_gid(utf8string *u8s, gid_t *gid) { struct mapid_arg *mapargp; struct mapid_res mapres; struct mapid_res *mapresp = &mapres; struct mapid_res *resp = mapresp; door_arg_t door_args; int doorfd; int error = 0; static int msg_done = 0; if (!u8s || !u8s->utf8string_val || !u8s->utf8string_len || (u8s->utf8string_val[0] == '\0')) { error = EINVAL; goto s2g_done; } if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) { /* * If "nobody", just short circuit and bail */ *gid = GID_NOBODY; goto s2g_done; } if ((mapargp = malloc(MAPID_ARG_LEN(u8s->utf8string_len))) == NULL) { (void) fprintf(stderr, "Unable to malloc %d bytes\n", MAPID_ARG_LEN(u8s->utf8string_len)); error = ENOMEM; goto s2g_done; } mapargp->cmd = NFSMAPID_STR_GID; mapargp->u_arg.len = u8s->utf8string_len; (void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len); mapargp->str[mapargp->u_arg.len] = '\0'; door_args.data_ptr = (char *)mapargp; door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len); door_args.desc_ptr = NULL; door_args.desc_num = 0; door_args.rbuf = (char *)mapresp; door_args.rsize = sizeof (struct mapid_res); /* * call to the nfsmapid daemon */ if ((doorfd = nfs_idmap_doorget()) == -1) { if (!msg_done) { fprintf(stderr, "nfs_idmap_str_uid: Can't communicate" " with mapping daemon nfsmapid\n"); msg_done = 1; } error = ECOMM; free(mapargp); goto s2g_done; } if (door_call(doorfd, &door_args) == -1) { perror("door_call failed"); error = EINVAL; free(mapargp); goto s2g_done; } free(mapargp); resp = (struct mapid_res *)door_args.rbuf; switch (resp->status) { case NFSMAPID_OK: *gid = resp->u_res.gid; break; case NFSMAPID_NUMSTR: *gid = resp->u_res.gid; error = resp->status; goto out; default: case NFSMAPID_UNMAPPABLE: case NFSMAPID_INVALID: case NFSMAPID_INTERNAL: case NFSMAPID_BADDOMAIN: case NFSMAPID_BADID: case NFSMAPID_NOTFOUND: error = resp->status; goto s2g_done; } s2g_done: if (error) *gid = GID_NOBODY; out: if (resp != mapresp) munmap(door_args.rbuf, door_args.rsize); return (error); } /* * Convert a gid into its utf-8 string representation. */ int nfs_idmap_gid_str(gid_t gid, /* gid to map */ utf8string *g8s) /* resulting utf-8 string for gid */ { struct mapid_arg maparg; struct mapid_res mapres; struct mapid_res *mapresp = &mapres; struct mapid_res *resp = mapresp; door_arg_t door_args; int error = 0; int doorfd; static int msg_done = 0; if (gid == GID_NOBODY) { g8s->utf8string_len = strlen("nobody"); g8s->utf8string_val = nobody_str; goto g2s_done; } /* * Daemon call... */ maparg.cmd = NFSMAPID_GID_STR; maparg.u_arg.gid = gid; door_args.data_ptr = (char *)&maparg; door_args.data_size = sizeof (struct mapid_arg); door_args.desc_ptr = NULL; door_args.desc_num = 0; door_args.rbuf = (char *)mapresp; door_args.rsize = sizeof (struct mapid_res); if ((doorfd = nfs_idmap_doorget()) == -1) { if (!msg_done) { fprintf(stderr, "nfs_idmap_uid_str: Can't " "communicate with mapping daemon nfsmapid\n"); msg_done = 1; } error = ECOMM; goto g2s_done; } if (door_call(doorfd, &door_args) == -1) { perror("door_call failed"); error = EINVAL; goto g2s_done; } resp = (struct mapid_res *)door_args.rbuf; if (resp->status != NFSMAPID_OK) { error = resp->status; goto g2s_done; } if (resp->u_res.len != strlen(resp->str)) { (void) fprintf(stderr, "Incorrect length %d expected %d\n", resp->u_res.len, strlen(resp->str)); error = NFSMAPID_INVALID; goto g2s_done; } g8s->utf8string_len = resp->u_res.len; bcopy(resp->str, g8s->utf8string_val, g8s->utf8string_len); g2s_done: if (resp != mapresp) munmap(door_args.rbuf, door_args.rsize); return (error); }