16615 ena assertion failure in ena_tx_intr_work()
[illumos-gate.git] / usr / src / cmd / fs.d / nfs / mountd / mountd.c
blob31472e2422a5d36f1503a0daf50a63ac593a3393
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
25 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2022 RackTop Systems.
29 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
33 * Portions of this source code were derived from Berkeley 4.3 BSD
34 * under license from the Regents of the University of California.
37 #include <stdio.h>
38 #include <stdio_ext.h>
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <string.h>
43 #include <syslog.h>
44 #include <sys/param.h>
45 #include <rpc/rpc.h>
46 #include <sys/stat.h>
47 #include <netconfig.h>
48 #include <netdir.h>
49 #include <sys/file.h>
50 #include <sys/time.h>
51 #include <sys/errno.h>
52 #include <rpcsvc/mount.h>
53 #include <sys/pathconf.h>
54 #include <sys/systeminfo.h>
55 #include <sys/utsname.h>
56 #include <sys/wait.h>
57 #include <sys/resource.h>
58 #include <signal.h>
59 #include <locale.h>
60 #include <unistd.h>
61 #include <errno.h>
62 #include <sys/socket.h>
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
65 #include <netdb.h>
66 #include <thread.h>
67 #include <assert.h>
68 #include <priv_utils.h>
69 #include <nfs/auth.h>
70 #include <nfs/nfssys.h>
71 #include <nfs/nfs.h>
72 #include <nfs/nfs_sec.h>
73 #include <rpcsvc/daemon_utils.h>
74 #include <deflt.h>
75 #include "../../fslib.h"
76 #include <sharefs/share.h>
77 #include <sharefs/sharetab.h>
78 #include "../lib/sharetab.h"
79 #include "mountd.h"
80 #include <tsol/label.h>
81 #include <sys/tsol/label_macro.h>
82 #include <libtsnet.h>
83 #include <sys/sdt.h>
84 #include <libscf.h>
85 #include <limits.h>
86 #include <sys/nvpair.h>
87 #include <attr.h>
88 #include "smfcfg.h"
89 #include <pwd.h>
90 #include <grp.h>
91 #include <alloca.h>
92 #include <libinetutil.h>
93 #include <libsocket_priv.h>
95 extern int daemonize_init(void);
96 extern void daemonize_fini(int);
98 extern int _nfssys(int, void *);
100 struct sh_list *share_list;
102 rwlock_t sharetab_lock; /* lock to protect the cached sharetab */
103 static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */
105 static mutex_t logging_queue_lock;
106 static cond_t logging_queue_cv;
108 static share_t *find_lofsentry(char *, int *);
109 static int getclientsflavors_old(share_t *, struct cln *, int *);
110 static int getclientsflavors_new(share_t *, struct cln *, int *);
111 static int check_client_old(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
112 gid_t *, uid_t *, gid_t *, uint_t *, gid_t **);
113 static int check_client_new(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
114 gid_t *, uid_t *, gid_t *i, uint_t *, gid_t **);
115 static void mnt(struct svc_req *, SVCXPRT *);
116 static void mnt_pathconf(struct svc_req *);
117 static int mount(struct svc_req *r);
118 static void sh_free(struct sh_list *);
119 static void umount(struct svc_req *);
120 static void umountall(struct svc_req *);
121 static int newopts(char *);
122 static tsol_tpent_t *get_client_template(struct sockaddr *);
124 static int debug;
125 static int verbose;
126 static int rejecting;
127 static int mount_vers_min = MOUNTVERS;
128 static int mount_vers_max = MOUNTVERS3;
129 static int mountd_port = 0;
130 static boolean_t mountd_remote_dump = B_FALSE;
132 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
134 thread_t nfsauth_thread;
135 thread_t cmd_thread;
136 thread_t logging_thread;
138 typedef struct logging_data {
139 char *ld_host;
140 char *ld_path;
141 char *ld_rpath;
142 int ld_status;
143 char *ld_netid;
144 struct netbuf *ld_nb;
145 struct logging_data *ld_next;
146 } logging_data;
148 static logging_data *logging_head = NULL;
149 static logging_data *logging_tail = NULL;
152 * Our copy of some system variables obtained using sysconf(3c)
154 static long ngroups_max; /* _SC_NGROUPS_MAX */
155 static long pw_size; /* _SC_GETPW_R_SIZE_MAX */
157 /* Cached address info for this host. */
158 static struct addrinfo *host_ai = NULL;
160 static void *
161 nfsauth_svc(void *arg __unused)
163 int doorfd = -1;
164 uint_t darg;
165 #ifdef DEBUG
166 int dfd;
167 #endif
169 if ((doorfd = door_create(nfsauth_func, NULL,
170 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
171 syslog(LOG_ERR, "Unable to create door: %m\n");
172 exit(10);
175 #ifdef DEBUG
177 * Create a file system path for the door
179 if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
180 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
181 syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
182 (void) close(doorfd);
183 exit(11);
187 * Clean up any stale namespace associations
189 (void) fdetach(MOUNTD_DOOR);
192 * Register in namespace to pass to the kernel to door_ki_open
194 if (fattach(doorfd, MOUNTD_DOOR) == -1) {
195 syslog(LOG_ERR, "Unable to fattach door: %m\n");
196 (void) close(dfd);
197 (void) close(doorfd);
198 exit(12);
200 (void) close(dfd);
201 #endif
204 * Must pass the doorfd down to the kernel.
206 darg = doorfd;
207 (void) _nfssys(MOUNTD_ARGS, &darg);
210 * Wait for incoming calls
212 /*CONSTCOND*/
213 for (;;)
214 (void) pause();
216 /*NOTREACHED*/
217 syslog(LOG_ERR, gettext("Door server exited"));
218 return (NULL);
222 * NFS command service thread code for setup and handling of the
223 * nfs_cmd requests for character set conversion and other future
224 * events.
227 static void *
228 cmd_svc(void *arg)
230 int doorfd = -1;
231 uint_t darg;
233 if ((doorfd = door_create(nfscmd_func, NULL,
234 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
235 syslog(LOG_ERR, "Unable to create cmd door: %m\n");
236 exit(10);
240 * Must pass the doorfd down to the kernel.
242 darg = doorfd;
243 (void) _nfssys(NFSCMD_ARGS, &darg);
246 * Wait for incoming calls
248 /*CONSTCOND*/
249 for (;;)
250 (void) pause();
252 /*NOTREACHED*/
253 syslog(LOG_ERR, gettext("Cmd door server exited"));
254 return (NULL);
257 static void
258 free_logging_data(logging_data *lq)
260 if (lq != NULL) {
261 free(lq->ld_host);
262 free(lq->ld_netid);
264 if (lq->ld_nb != NULL) {
265 free(lq->ld_nb->buf);
266 free(lq->ld_nb);
269 free(lq->ld_path);
270 free(lq->ld_rpath);
272 free(lq);
276 static logging_data *
277 remove_head_of_queue(void)
279 logging_data *lq;
282 * Pull it off the queue.
284 lq = logging_head;
285 if (lq) {
286 logging_head = lq->ld_next;
289 * Drained it.
291 if (logging_head == NULL) {
292 logging_tail = NULL;
296 return (lq);
299 static void
300 do_logging_queue(logging_data *lq)
302 int cleared = 0;
303 char *host;
305 while (lq) {
306 struct cln cln;
308 if (lq->ld_host == NULL) {
309 DTRACE_PROBE(mountd, name_by_lazy);
310 cln_init_lazy(&cln, lq->ld_netid, lq->ld_nb);
311 host = cln_gethost(&cln);
312 } else
313 host = lq->ld_host;
315 audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
317 /* add entry to mount list */
318 if (lq->ld_rpath)
319 mntlist_new(host, lq->ld_rpath);
321 if (lq->ld_host == NULL)
322 cln_fini(&cln);
324 free_logging_data(lq);
325 cleared++;
327 (void) mutex_lock(&logging_queue_lock);
328 lq = remove_head_of_queue();
329 (void) mutex_unlock(&logging_queue_lock);
332 DTRACE_PROBE1(mountd, logging_cleared, cleared);
335 static void *
336 logging_svc(void *arg __unused)
338 logging_data *lq;
340 for (;;) {
341 (void) mutex_lock(&logging_queue_lock);
342 while (logging_head == NULL) {
343 (void) cond_wait(&logging_queue_cv,
344 &logging_queue_lock);
347 lq = remove_head_of_queue();
348 (void) mutex_unlock(&logging_queue_lock);
350 do_logging_queue(lq);
353 /*NOTREACHED*/
354 syslog(LOG_ERR, gettext("Logging server exited"));
355 return (NULL);
359 * This function is called for each configured network type to
360 * bind and register our RPC service programs.
362 * On TCP or UDP, we may want to bind MOUNTPROG on a specific port
363 * (when mountd_port is specified) in which case we'll use the
364 * variant of svc_tp_create() that lets us pass a bind address.
366 static void
367 md_svc_tp_create(struct netconfig *nconf)
369 char port_str[8];
370 struct nd_hostserv hs;
371 struct nd_addrlist *al = NULL;
372 SVCXPRT *xprt = NULL;
373 rpcvers_t vers;
375 vers = mount_vers_max;
378 * If mountd_port is set and this is an inet transport,
379 * bind this service on the specified port. The TLI way
380 * to create such a bind address is netdir_getbyname()
381 * with the special "host" HOST_SELF_BIND. This builds
382 * an all-zeros IP address with the specified port.
384 if (mountd_port != 0 &&
385 (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
386 strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
387 int err;
389 (void) snprintf(port_str, sizeof (port_str), "%u",
390 (unsigned short)mountd_port);
392 hs.h_host = HOST_SELF_BIND;
393 hs.h_serv = port_str;
394 err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
395 if (err == 0 && al != NULL) {
396 xprt = svc_tp_create_addr(mnt, MOUNTPROG, vers,
397 nconf, al->n_addrs);
398 netdir_free(al, ND_ADDRLIST);
400 if (xprt == NULL) {
401 syslog(LOG_ERR, "mountd: unable to create "
402 "(MOUNTD,%d) on transport %s (port %d)",
403 vers, nconf->nc_netid, mountd_port);
405 /* fall-back to default bind */
407 if (xprt == NULL) {
409 * Had mountd_port=0, or non-inet transport,
410 * or the bind to a specific port failed.
411 * Do a default bind.
413 xprt = svc_tp_create(mnt, MOUNTPROG, vers, nconf);
415 if (xprt == NULL) {
416 syslog(LOG_ERR, "mountd: unable to create "
417 "(MOUNTD,%d) on transport %s",
418 vers, nconf->nc_netid);
419 return;
423 * Register additional versions on this transport.
425 while (--vers >= mount_vers_min) {
426 if (!svc_reg(xprt, MOUNTPROG, vers, mnt, nconf)) {
427 (void) syslog(LOG_ERR, "mountd: "
428 "failed to register vers %d on %s",
429 vers, nconf->nc_netid);
435 main(int argc, char *argv[])
437 int pid;
438 int c;
439 int rpc_svc_fdunlim = 1;
440 int rpc_svc_mode = RPC_SVC_MT_AUTO;
441 int maxrecsz = RPC_MAXDATASIZE;
442 bool_t exclbind = TRUE;
443 bool_t can_do_mlp;
444 long thr_flags = (THR_NEW_LWP|THR_DAEMON);
445 char defval[5];
446 int ret, bufsz;
447 struct rlimit rl;
448 int listen_backlog = 0;
449 int max_threads = 0;
450 int tmp;
451 struct netconfig *nconf;
452 NCONF_HANDLE *nc;
453 const char *errstr;
454 int pipe_fd = -1;
455 char hostbuf[256];
458 * Mountd requires uid 0 for:
459 * /etc/rmtab updates (we could chown it to daemon)
460 * /etc/dfs/dfstab reading (it wants to lock out share which
461 * doesn't do any locking before first truncate;
462 * NFS share does; should use fcntl locking instead)
463 * Needed privileges:
464 * auditing
465 * nfs syscall
466 * file dac search (so it can stat all files)
467 * Optional privileges:
468 * MLP
470 can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
471 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
472 PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
473 PRIV_NET_PRIVADDR,
474 can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
475 (void) fprintf(stderr,
476 "%s: must be run with sufficient privileges\n",
477 argv[0]);
478 exit(1);
481 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
482 syslog(LOG_ERR, "getrlimit failed");
483 } else {
484 rl.rlim_cur = rl.rlim_max;
485 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
486 syslog(LOG_ERR, "setrlimit failed");
489 (void) enable_extended_FILE_stdio(-1, -1);
491 /* Upgrade SMF settings, if necessary. */
492 nfs_config_upgrade(NFSD);
494 ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
495 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
496 if (ret != SA_OK) {
497 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
498 "failed, using default value");
501 ret = nfs_smf_get_iprop("mountd_port", &mountd_port,
502 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
503 if (ret != SA_OK) {
504 syslog(LOG_ERR, "Reading of mountd_port from SMF "
505 "failed, using default value");
508 while ((c = getopt(argc, argv, "dvrm:p:")) != EOF) {
509 switch (c) {
510 case 'd':
511 debug++;
512 break;
513 case 'v':
514 verbose++;
515 break;
516 case 'r':
517 rejecting = 1;
518 break;
519 case 'm':
520 tmp = strtonum(optarg, 1, INT_MAX, &errstr);
521 if (errstr != NULL) {
522 (void) fprintf(stderr, "%s: invalid "
523 "max_threads option: %s, using defaults\n",
524 argv[0], errstr);
525 break;
527 max_threads = tmp;
528 break;
529 case 'p':
530 tmp = strtonum(optarg, 1, UINT16_MAX, &errstr);
531 if (errstr != NULL) {
532 (void) fprintf(stderr, "%s: invalid port "
533 "number: %s\n", argv[0], errstr);
534 break;
536 mountd_port = tmp;
537 break;
538 default:
539 fprintf(stderr, "usage: mountd [-v] [-r]\n");
540 exit(1);
545 * One might be tempted to use the NFS configuration values
546 * server_versmin, server_versmax to constrain the range of
547 * mountd versions supported here. However, older clients
548 * use mountd V1 for showmount, so just leave all mountd
549 * versions enabled here. (mount_vers_min, mount_vers_max)
552 ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
553 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
554 if (ret != SA_OK) {
555 syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
556 "failed, using default value");
559 bufsz = sizeof (defval);
560 ret = nfs_smf_get_prop("mountd_remote_dump", defval, DEFAULT_INSTANCE,
561 SCF_TYPE_BOOLEAN, NFSD, &bufsz);
562 if (ret == SA_OK) {
563 mountd_remote_dump = string_to_boolean(defval);
565 if (!mountd_remote_dump) {
566 /* Cache host address list */
567 if (gethostname(hostbuf, sizeof (hostbuf)) < 0) {
568 syslog(LOG_ERR, "gethostname() failed");
569 exit(1);
571 if (getaddrinfo(hostbuf, NULL, NULL, &host_ai) != 0) {
572 syslog(LOG_ERR, "getaddrinfo() failed");
573 exit(1);
578 * Sanity check versions,
579 * even though we may get versions > MOUNTVERS3, we still need
580 * to start nfsauth service, so continue on regardless of values.
582 if (mount_vers_max > MOUNTVERS3)
583 mount_vers_max = MOUNTVERS3;
584 if (mount_vers_min > mount_vers_max) {
585 fprintf(stderr, "server_versmin > server_versmax\n");
586 mount_vers_max = mount_vers_min;
588 (void) setlocale(LC_ALL, "");
589 (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
590 (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
591 (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
592 (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
594 netgroup_init();
596 #if !defined(TEXT_DOMAIN)
597 #define TEXT_DOMAIN "SYS_TEST"
598 #endif
599 (void) textdomain(TEXT_DOMAIN);
601 /* Don't drop core if the NFS module isn't loaded. */
602 (void) signal(SIGSYS, SIG_IGN);
604 if (!debug)
605 pipe_fd = daemonize_init();
608 * If we coredump it'll be in /core
610 if (chdir("/") < 0)
611 fprintf(stderr, "chdir /: %s\n", strerror(errno));
613 if (!debug)
614 openlog("mountd", LOG_PID, LOG_DAEMON);
617 * establish our lock on the lock file and write our pid to it.
618 * exit if some other process holds the lock, or if there's any
619 * error in writing/locking the file.
621 pid = _enter_daemon_lock(MOUNTD);
622 switch (pid) {
623 case 0:
624 break;
625 case -1:
626 fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
627 strerror(errno));
628 exit(2);
629 default:
630 /* daemon was already running */
631 exit(0);
634 audit_mountd_setup(); /* BSM */
637 * Get required system variables
639 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
640 syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
641 exit(1);
643 if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
644 syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
645 exit(1);
649 * Set number of file descriptors to unlimited
651 if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
652 syslog(LOG_INFO, "unable to set number of FDs to unlimited");
656 * Tell RPC that we want automatic thread mode.
657 * A new thread will be spawned for each request.
659 if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
660 fprintf(stderr, "unable to set automatic MT mode\n");
661 exit(1);
665 * Enable non-blocking mode and maximum record size checks for
666 * connection oriented transports.
668 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
669 fprintf(stderr, "unable to set RPC max record size\n");
673 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
674 * from being hijacked by a bind to a more specific addr.
676 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
677 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
681 * Set the maximum number of outstanding connection
682 * indications (listen backlog) to the value specified.
684 if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
685 &listen_backlog)) {
686 fprintf(stderr, "unable to set listen backlog\n");
687 exit(1);
691 * If max_threads was specified, then set the
692 * maximum number of threads to the value specified.
694 if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
695 fprintf(stderr, "unable to set max_threads\n");
696 exit(1);
699 if (mountd_port < 0 || mountd_port > UINT16_MAX) {
700 fprintf(stderr, "unable to use specified port\n");
701 exit(1);
705 * Make sure to unregister any previous versions in case the
706 * user is reconfiguring the server in interesting ways.
708 svc_unreg(MOUNTPROG, MOUNTVERS);
709 svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
710 svc_unreg(MOUNTPROG, MOUNTVERS3);
713 * Create the nfsauth thread with same signal disposition
714 * as the main thread. We need to create a separate thread
715 * since mountd() will be both an RPC server (for remote
716 * traffic) _and_ a doors server (for kernel upcalls).
718 if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
719 fprintf(stderr,
720 gettext("Failed to create NFSAUTH svc thread\n"));
721 exit(2);
725 * Create the cmd service thread with same signal disposition
726 * as the main thread. We need to create a separate thread
727 * since mountd() will be both an RPC server (for remote
728 * traffic) _and_ a doors server (for kernel upcalls).
730 if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
731 syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
732 exit(2);
736 * Create an additional thread to service the rmtab and
737 * audit_mountd_mount logging for mount requests. Use the same
738 * signal disposition as the main thread. We create
739 * a separate thread to allow the mount request threads to
740 * clear as soon as possible.
742 if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
743 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
744 exit(2);
748 * Enumerate network transports and create service listeners
749 * as appropriate for each.
751 if ((nc = setnetconfig()) == NULL) {
752 syslog(LOG_ERR, "setnetconfig failed: %m");
753 return (-1);
755 while ((nconf = getnetconfig(nc)) != NULL) {
757 * Skip things like tpi_raw, invisible...
759 if ((nconf->nc_flag & NC_VISIBLE) == 0)
760 continue;
761 if (nconf->nc_semantics != NC_TPI_CLTS &&
762 nconf->nc_semantics != NC_TPI_COTS &&
763 nconf->nc_semantics != NC_TPI_COTS_ORD)
764 continue;
766 md_svc_tp_create(nconf);
768 (void) endnetconfig(nc);
771 * Start serving
773 rmtab_load();
775 daemonize_fini(pipe_fd);
777 /* Get rid of the most dangerous basic privileges. */
778 __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
779 (char *)NULL);
781 svc_run();
782 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
783 abort();
785 /* NOTREACHED */
786 return (0);
790 * copied from usr/src/uts/common/klm/nlm_impl.c
792 static bool_t
793 caller_is_local(SVCXPRT *transp)
795 struct addrinfo *a;
796 char *netid;
797 struct netbuf *rtaddr;
798 struct sockaddr_storage addr;
799 bool_t rv = FALSE;
801 netid = transp->xp_netid;
802 rtaddr = svc_getrpccaller(transp);
804 if (netid == NULL)
805 return (FALSE);
807 if (strcmp(netid, "ticlts") == 0 ||
808 strcmp(netid, "ticotsord") == 0)
809 return (TRUE);
811 if (strcmp(netid, "tcp") == 0 || strcmp(netid, "udp") == 0) {
812 struct sockaddr_in *sin = (void *)rtaddr->buf;
814 if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
815 return (TRUE);
817 memmove(&addr, sin, sizeof (*sin));
819 if (strcmp(netid, "tcp6") == 0 || strcmp(netid, "udp6") == 0) {
820 struct sockaddr_in6 *sin6 = (void *)rtaddr->buf;
822 if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
823 return (TRUE);
825 memmove(&addr, sin6, sizeof (*sin6));
828 for (a = host_ai; a != NULL; a = a->ai_next) {
829 if (sockaddrcmp(&addr,
830 (struct sockaddr_storage *)a->ai_addr)) {
831 rv = TRUE;
832 break;
835 return (rv);
839 * Server procedure switch routine
841 void
842 mnt(struct svc_req *rqstp, SVCXPRT *transp)
844 switch (rqstp->rq_proc) {
845 case NULLPROC:
846 errno = 0;
847 if (!svc_sendreply(transp, xdr_void, (char *)0))
848 log_cant_reply(transp);
849 return;
851 case MOUNTPROC_MNT:
852 (void) mount(rqstp);
853 return;
855 case MOUNTPROC_DUMP:
856 if (mountd_remote_dump || caller_is_local(transp))
857 mntlist_send(transp);
858 else
859 svcerr_noproc(transp);
860 return;
862 case MOUNTPROC_UMNT:
863 umount(rqstp);
864 return;
866 case MOUNTPROC_UMNTALL:
867 umountall(rqstp);
868 return;
870 case MOUNTPROC_EXPORT:
871 case MOUNTPROC_EXPORTALL:
872 export(rqstp);
873 return;
875 case MOUNTPROC_PATHCONF:
876 if (rqstp->rq_vers == MOUNTVERS_POSIX)
877 mnt_pathconf(rqstp);
878 else
879 svcerr_noproc(transp);
880 return;
882 default:
883 svcerr_noproc(transp);
884 return;
888 void
889 log_cant_reply_cln(struct cln *cln)
891 int saverrno;
892 char *host;
894 saverrno = errno; /* save error code */
896 host = cln_gethost(cln);
897 if (host == NULL)
898 return;
900 errno = saverrno;
901 if (errno == 0)
902 syslog(LOG_ERR, "couldn't send reply to %s", host);
903 else
904 syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
907 void
908 log_cant_reply(SVCXPRT *transp)
910 int saverrno;
911 struct cln cln;
913 saverrno = errno; /* save error code */
914 cln_init(&cln, transp);
915 errno = saverrno;
917 log_cant_reply_cln(&cln);
919 cln_fini(&cln);
923 * Answer pathconf questions for the mount point fs
925 static void
926 mnt_pathconf(struct svc_req *rqstp)
928 SVCXPRT *transp;
929 struct pathcnf p;
930 char *path, rpath[MAXPATHLEN];
931 struct stat st;
933 transp = rqstp->rq_xprt;
934 path = NULL;
935 (void) memset((caddr_t)&p, 0, sizeof (p));
937 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
938 svcerr_decode(transp);
939 return;
941 if (lstat(path, &st) < 0) {
942 _PC_SET(_PC_ERROR, p.pc_mask);
943 goto done;
946 * Get a path without symbolic links.
948 if (realpath(path, rpath) == NULL) {
949 syslog(LOG_DEBUG,
950 "mount request: realpath failed on %s: %m",
951 path);
952 _PC_SET(_PC_ERROR, p.pc_mask);
953 goto done;
955 (void) memset((caddr_t)&p, 0, sizeof (p));
957 * can't ask about devices over NFS
959 _PC_SET(_PC_MAX_CANON, p.pc_mask);
960 _PC_SET(_PC_MAX_INPUT, p.pc_mask);
961 _PC_SET(_PC_PIPE_BUF, p.pc_mask);
962 _PC_SET(_PC_VDISABLE, p.pc_mask);
964 errno = 0;
965 p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
966 if (errno)
967 _PC_SET(_PC_LINK_MAX, p.pc_mask);
968 p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
969 if (errno)
970 _PC_SET(_PC_NAME_MAX, p.pc_mask);
971 p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
972 if (errno)
973 _PC_SET(_PC_PATH_MAX, p.pc_mask);
974 if (pathconf(rpath, _PC_NO_TRUNC) == 1)
975 _PC_SET(_PC_NO_TRUNC, p.pc_mask);
976 if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
977 _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
979 done:
980 errno = 0;
981 if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
982 log_cant_reply(transp);
983 if (path != NULL)
984 (void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
988 * If the rootmount (export) option is specified, the all mount requests for
989 * subdirectories return EACCES.
991 static int
992 checkrootmount(share_t *sh, char *rpath)
994 char *val;
996 if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
997 free(val);
998 if (strcmp(sh->sh_path, rpath) != 0)
999 return (0);
1000 else
1001 return (1);
1002 } else
1003 return (1);
1006 #define MAX_FLAVORS 128
1009 * Return only EACCES if client does not have access
1010 * to this directory.
1011 * "If the server exports only /a/b, an attempt to
1012 * mount a/b/c will fail with ENOENT if the directory
1013 * does not exist"... However, if the client
1014 * does not have access to /a/b, an attacker can
1015 * determine whether the directory exists.
1016 * This routine checks either existence of the file or
1017 * existence of the file name entry in the mount table.
1018 * If the file exists and there is no file name entry,
1019 * the error returned should be EACCES.
1020 * If the file does not exist, it must be determined
1021 * whether the client has access to a parent
1022 * directory. If the client has access to a parent
1023 * directory, the error returned should be ENOENT,
1024 * otherwise EACCES.
1026 static int
1027 mount_enoent_error(struct cln *cln, char *path, char *rpath, int *flavor_list)
1029 char *checkpath, *dp;
1030 share_t *sh = NULL;
1031 int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
1032 int flavor_count;
1034 checkpath = strdup(path);
1035 if (checkpath == NULL) {
1036 syslog(LOG_ERR, "mount_enoent: no memory");
1037 return (EACCES);
1040 /* CONSTCOND */
1041 while (1) {
1042 if (sh) {
1043 sharefree(sh);
1044 sh = NULL;
1047 if ((sh = findentry(rpath)) == NULL &&
1048 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1050 * There is no file name entry.
1051 * If the file (with symbolic links resolved) exists,
1052 * the error returned should be EACCES.
1054 if (realpath_error == 0)
1055 break;
1056 } else if (checkrootmount(sh, rpath) == 0) {
1058 * This is a "nosub" only export, in which case,
1059 * mounting subdirectories isn't allowed.
1060 * If the file (with symbolic links resolved) exists,
1061 * the error returned should be EACCES.
1063 if (realpath_error == 0)
1064 break;
1065 } else {
1067 * Check permissions in mount table.
1069 if (newopts(sh->sh_opts))
1070 flavor_count = getclientsflavors_new(sh, cln,
1071 flavor_list);
1072 else
1073 flavor_count = getclientsflavors_old(sh, cln,
1074 flavor_list);
1075 if (flavor_count != 0) {
1077 * Found entry in table and
1078 * client has correct permissions.
1080 reply_error = ENOENT;
1081 break;
1086 * Check all parent directories.
1088 dp = strrchr(checkpath, '/');
1089 if (dp == NULL)
1090 break;
1091 *dp = '\0';
1092 if (strlen(checkpath) == 0)
1093 break;
1095 * Get the real path (no symbolic links in it)
1097 if (realpath(checkpath, rpath) == NULL) {
1098 if (errno != ENOENT)
1099 break;
1100 } else {
1101 realpath_error = 0;
1105 if (sh)
1106 sharefree(sh);
1107 free(checkpath);
1108 return (reply_error);
1112 * We need to inform the caller whether or not we were
1113 * able to add a node to the queue. If we are not, then
1114 * it is up to the caller to go ahead and log the data.
1116 static int
1117 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
1118 char *rpath, int status, int error)
1120 logging_data *lq;
1121 struct netbuf *nb;
1123 lq = (logging_data *)calloc(1, sizeof (logging_data));
1124 if (lq == NULL)
1125 goto cleanup;
1128 * We might not yet have the host...
1130 if (host) {
1131 DTRACE_PROBE1(mountd, log_host, host);
1132 lq->ld_host = strdup(host);
1133 if (lq->ld_host == NULL)
1134 goto cleanup;
1135 } else {
1136 DTRACE_PROBE(mountd, log_no_host);
1138 lq->ld_netid = strdup(transp->xp_netid);
1139 if (lq->ld_netid == NULL)
1140 goto cleanup;
1142 lq->ld_nb = calloc(1, sizeof (struct netbuf));
1143 if (lq->ld_nb == NULL)
1144 goto cleanup;
1146 nb = svc_getrpccaller(transp);
1147 if (nb == NULL) {
1148 DTRACE_PROBE(mountd, e__nb__enqueue);
1149 goto cleanup;
1152 DTRACE_PROBE(mountd, nb_set_enqueue);
1154 lq->ld_nb->maxlen = nb->maxlen;
1155 lq->ld_nb->len = nb->len;
1157 lq->ld_nb->buf = malloc(lq->ld_nb->len);
1158 if (lq->ld_nb->buf == NULL)
1159 goto cleanup;
1161 bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1164 lq->ld_path = strdup(path);
1165 if (lq->ld_path == NULL)
1166 goto cleanup;
1168 if (!error) {
1169 lq->ld_rpath = strdup(rpath);
1170 if (lq->ld_rpath == NULL)
1171 goto cleanup;
1174 lq->ld_status = status;
1177 * Add to the tail of the logging queue.
1179 (void) mutex_lock(&logging_queue_lock);
1180 if (logging_tail == NULL) {
1181 logging_tail = logging_head = lq;
1182 } else {
1183 logging_tail->ld_next = lq;
1184 logging_tail = lq;
1186 (void) cond_signal(&logging_queue_cv);
1187 (void) mutex_unlock(&logging_queue_lock);
1189 return (TRUE);
1191 cleanup:
1193 free_logging_data(lq);
1195 return (FALSE);
1199 #define CLN_CLNAMES (1 << 0)
1200 #define CLN_HOST (1 << 1)
1202 static void
1203 cln_init_common(struct cln *cln, SVCXPRT *transp, char *netid,
1204 struct netbuf *nbuf)
1206 if ((cln->transp = transp) != NULL) {
1207 assert(netid == NULL && nbuf == NULL);
1208 cln->netid = transp->xp_netid;
1209 cln->nbuf = svc_getrpccaller(transp);
1210 } else {
1211 cln->netid = netid;
1212 cln->nbuf = nbuf;
1215 cln->nconf = NULL;
1216 cln->clnames = NULL;
1217 cln->host = NULL;
1219 cln->flags = 0;
1222 void
1223 cln_init(struct cln *cln, SVCXPRT *transp)
1225 cln_init_common(cln, transp, NULL, NULL);
1228 void
1229 cln_init_lazy(struct cln *cln, char *netid, struct netbuf *nbuf)
1231 cln_init_common(cln, NULL, netid, nbuf);
1234 void
1235 cln_fini(struct cln *cln)
1237 if (cln->nconf != NULL)
1238 freenetconfigent(cln->nconf);
1240 if (cln->clnames != NULL)
1241 netdir_free(cln->clnames, ND_HOSTSERVLIST);
1243 free(cln->host);
1246 struct netbuf *
1247 cln_getnbuf(struct cln *cln)
1249 return (cln->nbuf);
1252 struct nd_hostservlist *
1253 cln_getclientsnames(struct cln *cln)
1255 if ((cln->flags & CLN_CLNAMES) == 0) {
1257 * nconf is not needed if we do not have nbuf (see
1258 * cln_gethost() too), so we check for nbuf and in a case it is
1259 * NULL we do not try to get nconf.
1261 if (cln->netid != NULL && cln->nbuf != NULL) {
1262 cln->nconf = getnetconfigent(cln->netid);
1263 if (cln->nconf == NULL)
1264 syslog(LOG_ERR, "%s: getnetconfigent failed",
1265 cln->netid);
1268 if (cln->nconf != NULL && cln->nbuf != NULL)
1269 (void) __netdir_getbyaddr_nosrv(cln->nconf,
1270 &cln->clnames, cln->nbuf);
1272 cln->flags |= CLN_CLNAMES;
1275 return (cln->clnames);
1279 * Return B_TRUE if the host is already available at no cost
1281 boolean_t
1282 cln_havehost(struct cln *cln)
1284 return ((cln->flags & (CLN_CLNAMES | CLN_HOST)) != 0);
1287 char *
1288 cln_gethost(struct cln *cln)
1290 if (cln_getclientsnames(cln) != NULL)
1291 return (cln->clnames->h_hostservs[0].h_host);
1293 if ((cln->flags & CLN_HOST) == 0) {
1294 if (cln->nconf == NULL || cln->nbuf == NULL) {
1295 cln->host = strdup("(anon)");
1296 } else {
1297 char host[MAXIPADDRLEN];
1299 if (strcmp(cln->nconf->nc_protofmly, NC_INET) == 0) {
1300 struct sockaddr_in *sa;
1302 /* LINTED pointer alignment */
1303 sa = (struct sockaddr_in *)(cln->nbuf->buf);
1304 (void) inet_ntoa_r(sa->sin_addr, host);
1306 cln->host = strdup(host);
1307 } else if (strcmp(cln->nconf->nc_protofmly,
1308 NC_INET6) == 0) {
1309 struct sockaddr_in6 *sa;
1311 /* LINTED pointer alignment */
1312 sa = (struct sockaddr_in6 *)(cln->nbuf->buf);
1313 (void) inet_ntop(AF_INET6,
1314 sa->sin6_addr.s6_addr,
1315 host, INET6_ADDRSTRLEN);
1317 cln->host = strdup(host);
1318 } else {
1319 syslog(LOG_ERR, gettext("Client's address is "
1320 "neither IPv4 nor IPv6"));
1322 cln->host = strdup("(anon)");
1326 cln->flags |= CLN_HOST;
1329 return (cln->host);
1333 * Check mount requests, add to mounted list if ok
1335 static int
1336 mount(struct svc_req *rqstp)
1338 SVCXPRT *transp;
1339 int version, vers;
1340 struct fhstatus fhs;
1341 struct mountres3 mountres3;
1342 char fh[FHSIZE3];
1343 int len = FHSIZE3;
1344 char *path, rpath[MAXPATHLEN];
1345 share_t *sh = NULL;
1346 struct cln cln;
1347 char *host = NULL;
1348 int error = 0, lofs_tried = 0, enqueued;
1349 int flavor_list[MAX_FLAVORS];
1350 int flavor_count;
1351 ucred_t *uc = NULL;
1353 int audit_status;
1355 transp = rqstp->rq_xprt;
1356 version = rqstp->rq_vers;
1357 path = NULL;
1359 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1360 svcerr_decode(transp);
1361 return (EACCES);
1364 cln_init(&cln, transp);
1367 * Put off getting the name for the client until we
1368 * need it. This is a performance gain. If we are logging,
1369 * then we don't care about performance and might as well
1370 * get the host name now in case we need to spit out an
1371 * error message.
1373 if (verbose) {
1374 DTRACE_PROBE(mountd, name_by_verbose);
1375 if ((host = cln_gethost(&cln)) == NULL) {
1377 * We failed to get a name for the client, even
1378 * 'anon', probably because we ran out of memory.
1379 * In this situation it doesn't make sense to
1380 * allow the mount to succeed.
1382 error = EACCES;
1383 goto reply;
1388 * If the version being used is less than the minimum version,
1389 * the filehandle translation should not be provided to the
1390 * client.
1392 if (rejecting || version < mount_vers_min) {
1393 if (verbose)
1394 syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1395 host, path);
1396 error = EACCES;
1397 goto reply;
1401 * Trusted Extension doesn't support nfsv2. nfsv2 client
1402 * uses MOUNT protocol v1 and v2. To prevent circumventing
1403 * TX label policy via using nfsv2 client, reject a mount
1404 * request with version less than 3 and log an error.
1406 if (is_system_labeled()) {
1407 if (version < 3) {
1408 if (verbose)
1409 syslog(LOG_ERR,
1410 "Rejected mount: TX doesn't support NFSv2");
1411 error = EACCES;
1412 goto reply;
1417 * Get the real path (no symbolic links in it)
1419 if (realpath(path, rpath) == NULL) {
1420 error = errno;
1421 if (verbose)
1422 syslog(LOG_ERR,
1423 "mount request: realpath: %s: %m", path);
1424 if (error == ENOENT)
1425 error = mount_enoent_error(&cln, path, rpath,
1426 flavor_list);
1427 goto reply;
1430 if ((sh = findentry(rpath)) == NULL &&
1431 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1432 error = EACCES;
1433 goto reply;
1437 * Check if this is a "nosub" only export, in which case, mounting
1438 * subdirectories isn't allowed. Bug 1184573.
1440 if (checkrootmount(sh, rpath) == 0) {
1441 error = EACCES;
1442 goto reply;
1445 if (newopts(sh->sh_opts))
1446 flavor_count = getclientsflavors_new(sh, &cln, flavor_list);
1447 else
1448 flavor_count = getclientsflavors_old(sh, &cln, flavor_list);
1450 if (flavor_count == 0) {
1451 error = EACCES;
1452 goto reply;
1456 * Check MAC policy here. The server side policy should be
1457 * consistent with client side mount policy, i.e.
1458 * - we disallow an admin_low unlabeled client to mount
1459 * - we disallow mount from a lower labeled client.
1461 if (is_system_labeled()) {
1462 m_label_t *clabel = NULL;
1463 m_label_t *slabel = NULL;
1464 m_label_t admin_low;
1466 if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
1467 syslog(LOG_ERR,
1468 "mount request: Failed to get caller's ucred : %m");
1469 error = EACCES;
1470 goto reply;
1472 if ((clabel = ucred_getlabel(uc)) == NULL) {
1473 syslog(LOG_ERR,
1474 "mount request: can't get client label from ucred");
1475 error = EACCES;
1476 goto reply;
1479 bsllow(&admin_low);
1480 if (blequal(&admin_low, clabel)) {
1481 struct sockaddr *ca;
1482 tsol_tpent_t *tp;
1484 ca = (struct sockaddr *)(void *)svc_getrpccaller(
1485 rqstp->rq_xprt)->buf;
1486 if (ca == NULL) {
1487 error = EACCES;
1488 goto reply;
1491 * get trusted network template associated
1492 * with the client.
1494 tp = get_client_template(ca);
1495 if (tp == NULL || tp->host_type != SUN_CIPSO) {
1496 if (tp != NULL)
1497 tsol_freetpent(tp);
1498 error = EACCES;
1499 goto reply;
1501 tsol_freetpent(tp);
1502 } else {
1503 if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
1504 error = EACCES;
1505 goto reply;
1508 if (getlabel(rpath, slabel) != 0) {
1509 m_label_free(slabel);
1510 error = EACCES;
1511 goto reply;
1514 if (!bldominates(clabel, slabel)) {
1515 m_label_free(slabel);
1516 error = EACCES;
1517 goto reply;
1519 m_label_free(slabel);
1524 * Now get the filehandle.
1526 * NFS V2 clients get a 32 byte filehandle.
1527 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1528 * the embedded FIDs.
1530 vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1532 /* LINTED pointer alignment */
1533 while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1534 if (errno == EINVAL &&
1535 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1536 errno = 0;
1537 continue;
1539 error = errno == EINVAL ? EACCES : errno;
1540 syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1541 path);
1542 break;
1545 if (version == MOUNTVERS3) {
1546 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1547 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1548 } else {
1549 bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1552 reply:
1553 if (uc != NULL)
1554 ucred_free(uc);
1556 switch (version) {
1557 case MOUNTVERS:
1558 case MOUNTVERS_POSIX:
1559 if (error == EINVAL)
1560 fhs.fhs_status = NFSERR_ACCES;
1561 else if (error == EREMOTE)
1562 fhs.fhs_status = NFSERR_REMOTE;
1563 else
1564 fhs.fhs_status = error;
1566 if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1567 log_cant_reply_cln(&cln);
1569 audit_status = fhs.fhs_status;
1570 break;
1572 case MOUNTVERS3:
1573 if (!error) {
1574 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1575 flavor_list;
1576 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1577 flavor_count;
1579 } else if (error == ENAMETOOLONG)
1580 error = MNT3ERR_NAMETOOLONG;
1582 mountres3.fhs_status = error;
1583 if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1584 log_cant_reply_cln(&cln);
1586 audit_status = mountres3.fhs_status;
1587 break;
1590 if (cln_havehost(&cln))
1591 host = cln_gethost(&cln);
1593 if (verbose)
1594 syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1595 (host == NULL) ? "unknown host" : host,
1596 error ? "denied" : "mounted", path);
1599 * If we can not create a queue entry, go ahead and do it
1600 * in the context of this thread.
1602 enqueued = enqueue_logging_data(host, transp, path, rpath,
1603 audit_status, error);
1604 if (enqueued == FALSE) {
1605 if (host == NULL) {
1606 DTRACE_PROBE(mountd, name_by_in_thread);
1607 host = cln_gethost(&cln);
1610 DTRACE_PROBE(mountd, logged_in_thread);
1611 audit_mountd_mount(host, path, audit_status); /* BSM */
1612 if (!error)
1613 mntlist_new(host, rpath); /* add entry to mount list */
1616 if (path != NULL)
1617 (void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1619 if (sh)
1620 sharefree(sh);
1622 cln_fini(&cln);
1624 return (error);
1628 * Determine whether two paths are within the same file system.
1629 * Returns nonzero (true) if paths are the same, zero (false) if
1630 * they are different. If an error occurs, return false.
1632 * Use the actual FSID if it's available (via getattrat()); otherwise,
1633 * fall back on st_dev.
1635 * With ZFS snapshots, st_dev differs from the regular file system
1636 * versus the snapshot. But the fsid is the same throughout. Thus
1637 * the fsid is a better test.
1639 static int
1640 same_file_system(const char *path1, const char *path2)
1642 uint64_t fsid1, fsid2;
1643 struct stat64 st1, st2;
1644 nvlist_t *nvl1 = NULL;
1645 nvlist_t *nvl2 = NULL;
1647 if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1648 (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1649 (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1650 (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1651 nvlist_free(nvl1);
1652 nvlist_free(nvl2);
1654 * We have found fsid's for both paths.
1657 if (fsid1 == fsid2)
1658 return (B_TRUE);
1660 return (B_FALSE);
1663 nvlist_free(nvl1);
1664 nvlist_free(nvl2);
1667 * We were unable to find fsid's for at least one of the paths.
1668 * fall back on st_dev.
1671 if (stat64(path1, &st1) < 0) {
1672 syslog(LOG_NOTICE, "%s: %m", path1);
1673 return (B_FALSE);
1675 if (stat64(path2, &st2) < 0) {
1676 syslog(LOG_NOTICE, "%s: %m", path2);
1677 return (B_FALSE);
1680 if (st1.st_dev == st2.st_dev)
1681 return (B_TRUE);
1683 return (B_FALSE);
1686 share_t *
1687 findentry(char *path)
1689 share_t *sh = NULL;
1690 struct sh_list *shp;
1691 char *p1, *p2;
1693 check_sharetab();
1695 (void) rw_rdlock(&sharetab_lock);
1697 for (shp = share_list; shp; shp = shp->shl_next) {
1698 sh = shp->shl_sh;
1699 for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1700 if (*p1 == '\0')
1701 goto done; /* exact match */
1704 * Now compare the pathnames for three cases:
1706 * Parent: /export/foo (no trailing slash on parent)
1707 * Child: /export/foo/bar
1709 * Parent: /export/foo/ (trailing slash on parent)
1710 * Child: /export/foo/bar
1712 * Parent: /export/foo/ (no trailing slash on child)
1713 * Child: /export/foo
1715 if ((*p1 == '\0' && *p2 == '/') ||
1716 (*p1 == '\0' && *(p1-1) == '/') ||
1717 (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1719 * We have a subdirectory. Test whether the
1720 * subdirectory is in the same file system.
1722 if (same_file_system(path, sh->sh_path))
1723 goto done;
1726 done:
1727 sh = shp ? sharedup(sh) : NULL;
1729 (void) rw_unlock(&sharetab_lock);
1731 return (sh);
1735 static int
1736 is_substring(char **mntp, char **path)
1738 char *p1 = *mntp, *p2 = *path;
1740 if (*p1 == '\0' && *p2 == '\0') /* exact match */
1741 return (1);
1742 else if (*p1 == '\0' && *p2 == '/')
1743 return (1);
1744 else if (*p1 == '\0' && *(p1-1) == '/') {
1745 *path = --p2; /* we need the slash in p2 */
1746 return (1);
1747 } else if (*p2 == '\0') {
1748 while (*p1 == '/')
1749 p1++;
1750 if (*p1 == '\0') /* exact match */
1751 return (1);
1753 return (0);
1757 * find_lofsentry() searches for the real path which this requested LOFS path
1758 * (rpath) shadows. If found, it will return the sharetab entry of
1759 * the real path that corresponds to the LOFS path.
1760 * We first search mnttab to see if the requested path is an automounted
1761 * path. If it is an automounted path, it will trigger the mount by stat()ing
1762 * the requested path. Note that it is important to check that this path is
1763 * actually an automounted path, otherwise we would stat() a path which may
1764 * turn out to be NFS and block indefinitely on a dead server. The automounter
1765 * times-out if the server is dead, so there's no risk of hanging this
1766 * thread waiting for stat().
1767 * After the mount has been triggered (if necessary), we look for a
1768 * mountpoint of type LOFS (by searching /etc/mnttab again) which
1769 * is a substring of the rpath. If found, we construct a new path by
1770 * concatenating the mnt_special and the remaining of rpath, call findentry()
1771 * to make sure the 'real path' is shared.
1773 static share_t *
1774 find_lofsentry(char *rpath, int *done_flag)
1776 struct stat r_stbuf;
1777 mntlist_t *ml, *mntl, *mntpnt = NULL;
1778 share_t *retcode = NULL;
1779 char tmp_path[MAXPATHLEN];
1780 int mntpnt_len = 0, tmp;
1781 char *p1, *p2;
1783 if ((*done_flag)++)
1784 return (retcode);
1787 * While fsgetmntlist() uses lockf() to
1788 * lock the mnttab before reading it in,
1789 * the lock ignores threads in the same process.
1790 * Read in the mnttab with the protection of a mutex.
1792 (void) mutex_lock(&mnttab_lock);
1793 mntl = fsgetmntlist();
1794 (void) mutex_unlock(&mnttab_lock);
1797 * Obtain the mountpoint for the requested path.
1799 for (ml = mntl; ml; ml = ml->mntl_next) {
1800 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1801 *p1 == *p2 && *p1; p1++, p2++)
1803 if (is_substring(&p1, &p2) &&
1804 (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1805 mntpnt = ml;
1806 mntpnt_len = tmp;
1811 * If the path needs to be autoFS mounted, trigger the mount by
1812 * stat()ing it. This is determined by checking whether the
1813 * mountpoint we just found is of type autofs.
1815 if (mntpnt != NULL &&
1816 strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1818 * The requested path is a substring of an autoFS filesystem.
1819 * Trigger the mount.
1821 if (stat(rpath, &r_stbuf) < 0) {
1822 if (verbose)
1823 syslog(LOG_NOTICE, "%s: %m", rpath);
1824 goto done;
1826 if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1828 * The requested path is a directory, stat(2) it
1829 * again with a trailing '.' to force the autoFS
1830 * module to trigger the mount of indirect
1831 * automount entries, such as /net/jurassic/.
1833 if (strlen(rpath) + 2 > MAXPATHLEN) {
1834 if (verbose) {
1835 syslog(LOG_NOTICE,
1836 "%s/.: exceeds MAXPATHLEN %d",
1837 rpath, MAXPATHLEN);
1839 goto done;
1841 (void) strcpy(tmp_path, rpath);
1842 (void) strcat(tmp_path, "/.");
1844 if (stat(tmp_path, &r_stbuf) < 0) {
1845 if (verbose)
1846 syslog(LOG_NOTICE, "%s: %m", tmp_path);
1847 goto done;
1852 * The mount has been triggered, re-read mnttab to pick up
1853 * the changes made by autoFS.
1855 fsfreemntlist(mntl);
1856 (void) mutex_lock(&mnttab_lock);
1857 mntl = fsgetmntlist();
1858 (void) mutex_unlock(&mnttab_lock);
1862 * The autoFS mountpoint has been triggered if necessary,
1863 * now search mnttab again to determine if the requested path
1864 * is an LOFS mount of a shared path.
1866 mntpnt_len = 0;
1867 for (ml = mntl; ml; ml = ml->mntl_next) {
1868 if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1869 continue;
1871 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1872 *p1 == *p2 && *p1; p1++, p2++)
1875 if (is_substring(&p1, &p2) &&
1876 ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1877 mntpnt_len = tmp;
1879 if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1880 MAXPATHLEN) {
1881 if (verbose) {
1882 syslog(LOG_NOTICE, "%s%s: exceeds %d",
1883 ml->mntl_mnt->mnt_special, p2,
1884 MAXPATHLEN);
1886 if (retcode)
1887 sharefree(retcode);
1888 retcode = NULL;
1889 goto done;
1892 (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1893 (void) strcat(tmp_path, p2);
1894 if (retcode)
1895 sharefree(retcode);
1896 retcode = findentry(tmp_path);
1900 if (retcode) {
1901 assert(strlen(tmp_path) > 0);
1902 (void) strcpy(rpath, tmp_path);
1905 done:
1906 fsfreemntlist(mntl);
1907 return (retcode);
1911 * Determine whether an access list grants rights to a particular host.
1912 * We match on aliases of the hostname as well as on the canonical name.
1913 * Names in the access list may be either hosts or netgroups; they're
1914 * not distinguished syntactically. We check for hosts first because
1915 * it's cheaper, then try netgroups.
1917 * Return values:
1918 * 1 - access is granted
1919 * 0 - access is denied
1920 * -1 - an error occured
1923 in_access_list(struct cln *cln,
1924 char *access_list) /* N.B. we clobber this "input" parameter */
1926 char addr[INET_ADDRSTRLEN];
1927 char buff[256];
1928 int nentries = 0;
1929 char *cstr = access_list;
1930 char *gr = access_list;
1931 int i;
1932 int response;
1933 int ret;
1934 struct netbuf *pnb;
1935 struct nd_hostservlist *clnames = NULL;
1937 /* If no access list - then it's unrestricted */
1938 if (access_list == NULL || *access_list == '\0')
1939 return (1);
1941 if ((pnb = cln_getnbuf(cln)) == NULL)
1942 return (-1);
1944 for (;;) {
1945 if ((cstr = strpbrk(cstr, "[:")) != NULL) {
1946 if (*cstr == ':') {
1947 *cstr = '\0';
1948 } else {
1949 assert(*cstr == '[');
1950 cstr = strchr(cstr + 1, ']');
1951 if (cstr == NULL)
1952 return (-1);
1953 cstr++;
1954 continue;
1959 * If the list name has a '-' prepended then a match of
1960 * the following name implies failure instead of success.
1962 if (*gr == '-') {
1963 response = 0;
1964 gr++;
1965 } else {
1966 response = 1;
1970 * First check if we have '@' entry, as it doesn't
1971 * require client hostname.
1973 if (*gr == '@') {
1974 gr++;
1976 /* Netname support */
1977 if (!isdigit(*gr) && *gr != '[') {
1978 struct netent n, *np;
1980 if ((np = getnetbyname_r(gr, &n, buff,
1981 sizeof (buff))) != NULL &&
1982 np->n_net != 0) {
1983 while ((np->n_net & 0xFF000000u) == 0)
1984 np->n_net <<= 8;
1985 np->n_net = htonl(np->n_net);
1986 if (inet_ntop(AF_INET, &np->n_net, addr,
1987 INET_ADDRSTRLEN) == NULL)
1988 break;
1989 ret = inet_matchaddr(pnb->buf, addr);
1990 if (ret == -1) {
1991 if (errno == EINVAL) {
1992 syslog(LOG_WARNING,
1993 "invalid access "
1994 "list entry: %s",
1995 addr);
1997 return (-1);
1998 } else if (ret == 1) {
1999 return (response);
2002 } else {
2003 ret = inet_matchaddr(pnb->buf, gr);
2004 if (ret == -1) {
2005 if (errno == EINVAL) {
2006 syslog(LOG_WARNING,
2007 "invalid access list "
2008 "entry: %s", gr);
2010 return (-1);
2011 } else if (ret == 1) {
2012 return (response);
2016 goto next;
2020 * No other checks can be performed if client address
2021 * can't be resolved.
2023 if ((clnames = cln_getclientsnames(cln)) == NULL)
2024 goto next;
2026 /* Otherwise loop through all client hostname aliases */
2027 for (i = 0; i < clnames->h_cnt; i++) {
2028 char *host = clnames->h_hostservs[i].h_host;
2031 * If the list name begins with a dot then
2032 * do a domain name suffix comparison.
2033 * A single dot matches any name with no
2034 * suffix.
2036 if (*gr == '.') {
2037 if (*(gr + 1) == '\0') { /* single dot */
2038 if (strchr(host, '.') == NULL)
2039 return (response);
2040 } else {
2041 int off = strlen(host) - strlen(gr);
2042 if (off > 0 &&
2043 strcasecmp(host + off, gr) == 0) {
2044 return (response);
2047 } else {
2048 /* Just do a hostname match */
2049 if (strcasecmp(gr, host) == 0)
2050 return (response);
2054 nentries++;
2056 next:
2057 if (cstr == NULL)
2058 break;
2060 gr = ++cstr;
2063 if (clnames == NULL)
2064 return (0);
2066 return (netgroup_check(clnames, access_list, nentries));
2070 static char *optlist[] = {
2071 #define OPT_RO 0
2072 SHOPT_RO,
2073 #define OPT_RW 1
2074 SHOPT_RW,
2075 #define OPT_ROOT 2
2076 SHOPT_ROOT,
2077 #define OPT_SECURE 3
2078 SHOPT_SECURE,
2079 #define OPT_ANON 4
2080 SHOPT_ANON,
2081 #define OPT_WINDOW 5
2082 SHOPT_WINDOW,
2083 #define OPT_NOSUID 6
2084 SHOPT_NOSUID,
2085 #define OPT_ACLOK 7
2086 SHOPT_ACLOK,
2087 #define OPT_SEC 8
2088 SHOPT_SEC,
2089 #define OPT_NONE 9
2090 SHOPT_NONE,
2091 #define OPT_UIDMAP 10
2092 SHOPT_UIDMAP,
2093 #define OPT_GIDMAP 11
2094 SHOPT_GIDMAP,
2095 NULL
2098 static int
2099 map_flavor(char *str)
2101 seconfig_t sec;
2103 if (nfs_getseconfig_byname(str, &sec))
2104 return (-1);
2106 return (sec.sc_nfsnum);
2110 * If the option string contains a "sec="
2111 * option, then use new option syntax.
2113 static int
2114 newopts(char *opts)
2116 char *head, *p, *val;
2118 if (!opts || *opts == '\0')
2119 return (0);
2121 head = strdup(opts);
2122 if (head == NULL) {
2123 syslog(LOG_ERR, "opts: no memory");
2124 return (0);
2127 p = head;
2128 while (*p) {
2129 if (getsubopt(&p, optlist, &val) == OPT_SEC) {
2130 free(head);
2131 return (1);
2135 free(head);
2136 return (0);
2140 * Given an export and the clients hostname(s)
2141 * determine the security flavors that this
2142 * client is permitted to use.
2144 * This routine is called only for "old" syntax, i.e.
2145 * only one security flavor is allowed. So we need
2146 * to determine two things: the particular flavor,
2147 * and whether the client is allowed to use this
2148 * flavor, i.e. is in the access list.
2150 * Note that if there is no access list, then the
2151 * default is that access is granted.
2153 static int
2154 getclientsflavors_old(share_t *sh, struct cln *cln, int *flavors)
2156 char *opts, *p, *val;
2157 boolean_t ok = B_FALSE;
2158 int defaultaccess = 1;
2159 boolean_t reject = B_FALSE;
2161 opts = strdup(sh->sh_opts);
2162 if (opts == NULL) {
2163 syslog(LOG_ERR, "getclientsflavors: no memory");
2164 return (0);
2167 flavors[0] = AUTH_SYS;
2168 p = opts;
2170 while (*p) {
2172 switch (getsubopt(&p, optlist, &val)) {
2173 case OPT_SECURE:
2174 flavors[0] = AUTH_DES;
2175 break;
2177 case OPT_RO:
2178 case OPT_RW:
2179 defaultaccess = 0;
2180 if (in_access_list(cln, val) > 0)
2181 ok = B_TRUE;
2182 break;
2184 case OPT_NONE:
2185 defaultaccess = 0;
2186 if (in_access_list(cln, val) > 0)
2187 reject = B_TRUE;
2191 free(opts);
2193 /* none takes precedence over everything else */
2194 if (reject)
2195 ok = B_FALSE;
2197 return (defaultaccess || ok);
2201 * Given an export and the clients hostname(s)
2202 * determine the security flavors that this
2203 * client is permitted to use.
2205 * This is somewhat more complicated than the "old"
2206 * routine because the options may contain multiple
2207 * security flavors (sec=) each with its own access
2208 * lists. So a client could be granted access based
2209 * on a number of security flavors. Note that the
2210 * type of access might not always be the same, the
2211 * client may get readonly access with one flavor
2212 * and readwrite with another, however the client
2213 * is not told this detail, it gets only the list
2214 * of flavors, and only if the client is using
2215 * version 3 of the mount protocol.
2217 static int
2218 getclientsflavors_new(share_t *sh, struct cln *cln, int *flavors)
2220 char *opts, *p, *val;
2221 char *lasts;
2222 char *f;
2223 boolean_t defaultaccess = B_TRUE; /* default access is rw */
2224 boolean_t access_ok = B_FALSE;
2225 int count, c;
2226 boolean_t reject = B_FALSE;
2228 opts = strdup(sh->sh_opts);
2229 if (opts == NULL) {
2230 syslog(LOG_ERR, "getclientsflavors: no memory");
2231 return (0);
2234 p = opts;
2235 count = c = 0;
2237 while (*p) {
2238 switch (getsubopt(&p, optlist, &val)) {
2239 case OPT_SEC:
2240 if (reject)
2241 access_ok = B_FALSE;
2244 * Before a new sec=xxx option, check if we need
2245 * to move the c index back to the previous count.
2247 if (!defaultaccess && !access_ok) {
2248 c = count;
2251 /* get all the sec=f1[:f2] flavors */
2252 while ((f = strtok_r(val, ":", &lasts)) != NULL) {
2253 flavors[c++] = map_flavor(f);
2254 val = NULL;
2257 /* for a new sec=xxx option, default is rw access */
2258 defaultaccess = B_TRUE;
2259 access_ok = B_FALSE;
2260 reject = B_FALSE;
2261 break;
2263 case OPT_RO:
2264 case OPT_RW:
2265 defaultaccess = B_FALSE;
2266 if (in_access_list(cln, val) > 0)
2267 access_ok = B_TRUE;
2268 break;
2270 case OPT_NONE:
2271 defaultaccess = B_FALSE;
2272 if (in_access_list(cln, val) > 0)
2273 reject = B_TRUE; /* none overides rw/ro */
2274 break;
2278 if (reject)
2279 access_ok = B_FALSE;
2281 if (!defaultaccess && !access_ok)
2282 c = count;
2284 free(opts);
2286 return (c);
2290 * This is a tricky piece of code that parses the
2291 * share options looking for a match on the auth
2292 * flavor that the client is using. If it finds
2293 * a match, then the client is given ro, rw, or
2294 * no access depending whether it is in the access
2295 * list. There is a special case for "secure"
2296 * flavor. Other flavors are values of the new "sec=" option.
2299 check_client(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2300 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2301 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2303 if (newopts(sh->sh_opts))
2304 return (check_client_new(sh, cln, flavor, clnt_uid, clnt_gid,
2305 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2306 srv_gids));
2307 else
2308 return (check_client_old(sh, cln, flavor, clnt_uid, clnt_gid,
2309 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2310 srv_gids));
2313 extern int _getgroupsbymember(const char *, gid_t[], int, int);
2316 * Get supplemental groups for uid
2318 static int
2319 getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2321 struct passwd pwd;
2322 char *pwbuf = alloca(pw_size);
2323 gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2324 int tmpngrps;
2326 if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL)
2327 return (-1);
2329 tmpgrps[0] = pwd.pw_gid;
2331 tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2332 if (tmpngrps <= 0) {
2333 syslog(LOG_WARNING,
2334 "getusergroups(): Unable to get groups for user %s",
2335 pwd.pw_name);
2337 return (-1);
2340 *grps = malloc(tmpngrps * sizeof (gid_t));
2341 if (*grps == NULL) {
2342 syslog(LOG_ERR,
2343 "getusergroups(): Memory allocation failed: %m");
2345 return (-1);
2348 *ngrps = tmpngrps;
2349 (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2351 return (0);
2355 * is_a_number(number)
2357 * is the string a number in one of the forms we want to use?
2360 static int
2361 is_a_number(char *number)
2363 int ret = 1;
2364 int hex = 0;
2366 if (strncmp(number, "0x", 2) == 0) {
2367 number += 2;
2368 hex = 1;
2369 } else if (*number == '-') {
2370 number++; /* skip the minus */
2372 while (ret == 1 && *number != '\0') {
2373 if (hex) {
2374 ret = isxdigit(*number++);
2375 } else {
2376 ret = isdigit(*number++);
2379 return (ret);
2382 static boolean_t
2383 get_uid(char *value, uid_t *uid)
2385 if (!is_a_number(value)) {
2386 struct passwd *pw;
2388 * in this case it would have to be a
2389 * user name
2391 pw = getpwnam(value);
2392 if (pw == NULL)
2393 return (B_FALSE);
2394 *uid = pw->pw_uid;
2395 endpwent();
2396 } else {
2397 uint64_t intval;
2398 intval = strtoull(value, NULL, 0);
2399 if (intval > UID_MAX && intval != -1)
2400 return (B_FALSE);
2401 *uid = (uid_t)intval;
2404 return (B_TRUE);
2407 static boolean_t
2408 get_gid(char *value, gid_t *gid)
2410 if (!is_a_number(value)) {
2411 struct group *gr;
2413 * in this case it would have to be a
2414 * group name
2416 gr = getgrnam(value);
2417 if (gr == NULL)
2418 return (B_FALSE);
2419 *gid = gr->gr_gid;
2420 endgrent();
2421 } else {
2422 uint64_t intval;
2423 intval = strtoull(value, NULL, 0);
2424 if (intval > UID_MAX && intval != -1)
2425 return (B_FALSE);
2426 *gid = (gid_t)intval;
2429 return (B_TRUE);
2432 static int
2433 check_client_old(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2434 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2435 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2437 char *opts, *p, *val;
2438 int match; /* Set when a flavor is matched */
2439 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2440 int list = 0; /* Set when "ro", "rw" is found */
2441 int ro_val = 0; /* Set if ro option is 'ro=' */
2442 int rw_val = 0; /* Set if rw option is 'rw=' */
2444 boolean_t map_deny = B_FALSE;
2446 opts = strdup(sh->sh_opts);
2447 if (opts == NULL) {
2448 syslog(LOG_ERR, "check_client: no memory");
2449 return (0);
2453 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2454 * locally for all of them
2456 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2457 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2458 perm |= NFSAUTH_GROUPS;
2460 p = opts;
2461 match = AUTH_UNIX;
2463 while (*p) {
2464 switch (getsubopt(&p, optlist, &val)) {
2466 case OPT_SECURE:
2467 match = AUTH_DES;
2469 if (perm & NFSAUTH_GROUPS) {
2470 free(*srv_gids);
2471 *srv_ngids = 0;
2472 *srv_gids = NULL;
2473 perm &= ~NFSAUTH_GROUPS;
2476 break;
2478 case OPT_RO:
2479 list++;
2480 if (val != NULL)
2481 ro_val++;
2482 if (in_access_list(cln, val) > 0)
2483 perm |= NFSAUTH_RO;
2484 break;
2486 case OPT_RW:
2487 list++;
2488 if (val != NULL)
2489 rw_val++;
2490 if (in_access_list(cln, val) > 0)
2491 perm |= NFSAUTH_RW;
2492 break;
2494 case OPT_ROOT:
2496 * Check if the client is in
2497 * the root list. Only valid
2498 * for AUTH_SYS.
2500 if (flavor != AUTH_SYS)
2501 break;
2503 if (val == NULL || *val == '\0')
2504 break;
2506 if (clnt_uid != 0)
2507 break;
2509 if (in_access_list(cln, val) > 0) {
2510 perm |= NFSAUTH_ROOT;
2511 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2512 map_deny = B_FALSE;
2514 if (perm & NFSAUTH_GROUPS) {
2515 free(*srv_gids);
2516 *srv_ngids = 0;
2517 *srv_gids = NULL;
2518 perm &= ~NFSAUTH_GROUPS;
2521 break;
2523 case OPT_NONE:
2525 * Check if the client should have no access
2526 * to this share at all. This option behaves
2527 * more like "root" than either "rw" or "ro".
2529 if (in_access_list(cln, val) > 0)
2530 perm |= NFSAUTH_DENIED;
2531 break;
2533 case OPT_UIDMAP: {
2534 char *c;
2535 char *n;
2538 * The uidmap is supported for AUTH_SYS only.
2540 if (flavor != AUTH_SYS)
2541 break;
2543 if (perm & NFSAUTH_UIDMAP || map_deny)
2544 break;
2546 for (c = val; c != NULL; c = n) {
2547 char *s;
2548 char *al;
2549 uid_t srv;
2551 n = strchr(c, '~');
2552 if (n != NULL)
2553 *n++ = '\0';
2555 s = strchr(c, ':');
2556 if (s != NULL) {
2557 *s++ = '\0';
2558 al = strchr(s, ':');
2559 if (al != NULL)
2560 *al++ = '\0';
2563 if (s == NULL || al == NULL)
2564 continue;
2566 if (*c == '\0') {
2567 if (clnt_uid != (uid_t)-1)
2568 continue;
2569 } else if (strcmp(c, "*") != 0) {
2570 uid_t clnt;
2572 if (!get_uid(c, &clnt))
2573 continue;
2575 if (clnt_uid != clnt)
2576 continue;
2579 if (*s == '\0')
2580 srv = UID_NOBODY;
2581 else if (!get_uid(s, &srv))
2582 continue;
2583 else if (srv == (uid_t)-1) {
2584 map_deny = B_TRUE;
2585 break;
2588 if (in_access_list(cln, al) > 0) {
2589 *srv_uid = srv;
2590 perm |= NFSAUTH_UIDMAP;
2592 if (perm & NFSAUTH_GROUPS) {
2593 free(*srv_gids);
2594 *srv_ngids = 0;
2595 *srv_gids = NULL;
2596 perm &= ~NFSAUTH_GROUPS;
2599 break;
2603 break;
2606 case OPT_GIDMAP: {
2607 char *c;
2608 char *n;
2611 * The gidmap is supported for AUTH_SYS only.
2613 if (flavor != AUTH_SYS)
2614 break;
2616 if (perm & NFSAUTH_GIDMAP || map_deny)
2617 break;
2619 for (c = val; c != NULL; c = n) {
2620 char *s;
2621 char *al;
2622 gid_t srv;
2624 n = strchr(c, '~');
2625 if (n != NULL)
2626 *n++ = '\0';
2628 s = strchr(c, ':');
2629 if (s != NULL) {
2630 *s++ = '\0';
2631 al = strchr(s, ':');
2632 if (al != NULL)
2633 *al++ = '\0';
2636 if (s == NULL || al == NULL)
2637 break;
2639 if (*c == '\0') {
2640 if (clnt_gid != (gid_t)-1)
2641 continue;
2642 } else if (strcmp(c, "*") != 0) {
2643 gid_t clnt;
2645 if (!get_gid(c, &clnt))
2646 continue;
2648 if (clnt_gid != clnt)
2649 continue;
2652 if (*s == '\0')
2653 srv = UID_NOBODY;
2654 else if (!get_gid(s, &srv))
2655 continue;
2656 else if (srv == (gid_t)-1) {
2657 map_deny = B_TRUE;
2658 break;
2661 if (in_access_list(cln, al) > 0) {
2662 *srv_gid = srv;
2663 perm |= NFSAUTH_GIDMAP;
2665 if (perm & NFSAUTH_GROUPS) {
2666 free(*srv_gids);
2667 *srv_ngids = 0;
2668 *srv_gids = NULL;
2669 perm &= ~NFSAUTH_GROUPS;
2672 break;
2676 break;
2679 default:
2680 break;
2684 free(opts);
2686 if (perm & NFSAUTH_ROOT) {
2687 *srv_uid = 0;
2688 *srv_gid = 0;
2691 if (map_deny)
2692 perm |= NFSAUTH_DENIED;
2694 if (!(perm & NFSAUTH_UIDMAP))
2695 *srv_uid = clnt_uid;
2696 if (!(perm & NFSAUTH_GIDMAP))
2697 *srv_gid = clnt_gid;
2699 if (flavor != match || perm & NFSAUTH_DENIED)
2700 return (NFSAUTH_DENIED);
2702 if (list) {
2704 * If the client doesn't match an "ro" or "rw"
2705 * list then set no access.
2707 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2708 perm |= NFSAUTH_DENIED;
2709 } else {
2711 * The client matched a flavor entry that
2712 * has no explicit "rw" or "ro" determination.
2713 * Default it to "rw".
2715 perm |= NFSAUTH_RW;
2719 * The client may show up in both ro= and rw=
2720 * lists. If so, then turn off the RO access
2721 * bit leaving RW access.
2723 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2725 * Logically cover all permutations of rw=,ro=.
2726 * In the case where, rw,ro=<host> we would like
2727 * to remove RW access for the host. In all other cases
2728 * RW wins the precedence battle.
2730 if (!rw_val && ro_val) {
2731 perm &= ~(NFSAUTH_RW);
2732 } else {
2733 perm &= ~(NFSAUTH_RO);
2737 return (perm);
2741 * Check if the client has access by using a flavor different from
2742 * the given "flavor". If "flavor" is not in the flavor list,
2743 * return TRUE to indicate that this "flavor" is a wrong sec.
2745 static bool_t
2746 is_wrongsec(share_t *sh, struct cln *cln, int flavor)
2748 int flavor_list[MAX_FLAVORS];
2749 int flavor_count, i;
2751 /* get the flavor list that the client has access with */
2752 flavor_count = getclientsflavors_new(sh, cln, flavor_list);
2754 if (flavor_count == 0)
2755 return (FALSE);
2758 * Check if the given "flavor" is in the flavor_list.
2760 for (i = 0; i < flavor_count; i++) {
2761 if (flavor == flavor_list[i])
2762 return (FALSE);
2766 * If "flavor" is not in the flavor_list, return TRUE to indicate
2767 * that the client should have access by using a security flavor
2768 * different from this "flavor".
2770 return (TRUE);
2774 * Given an export and the client's hostname, we
2775 * check the security options to see whether the
2776 * client is allowed to use the given security flavor.
2778 * The strategy is to proceed through the options looking
2779 * for a flavor match, then pay attention to the ro, rw,
2780 * and root options.
2782 * Note that an entry may list several flavors in a
2783 * single entry, e.g.
2785 * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2789 static int
2790 check_client_new(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2791 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2792 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2794 char *opts, *p, *val;
2795 char *lasts;
2796 char *f;
2797 int match = 0; /* Set when a flavor is matched */
2798 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2799 int list = 0; /* Set when "ro", "rw" is found */
2800 int ro_val = 0; /* Set if ro option is 'ro=' */
2801 int rw_val = 0; /* Set if rw option is 'rw=' */
2803 boolean_t map_deny = B_FALSE;
2805 opts = strdup(sh->sh_opts);
2806 if (opts == NULL) {
2807 syslog(LOG_ERR, "check_client: no memory");
2808 return (0);
2812 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2813 * locally for all of them
2815 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2816 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2817 perm |= NFSAUTH_GROUPS;
2819 p = opts;
2821 while (*p) {
2822 switch (getsubopt(&p, optlist, &val)) {
2824 case OPT_SEC:
2825 if (match)
2826 goto done;
2828 while ((f = strtok_r(val, ":", &lasts))
2829 != NULL) {
2830 if (flavor == map_flavor(f)) {
2831 match = 1;
2832 break;
2834 val = NULL;
2836 break;
2838 case OPT_RO:
2839 if (!match)
2840 break;
2842 list++;
2843 if (val != NULL)
2844 ro_val++;
2845 if (in_access_list(cln, val) > 0)
2846 perm |= NFSAUTH_RO;
2847 break;
2849 case OPT_RW:
2850 if (!match)
2851 break;
2853 list++;
2854 if (val != NULL)
2855 rw_val++;
2856 if (in_access_list(cln, val) > 0)
2857 perm |= NFSAUTH_RW;
2858 break;
2860 case OPT_ROOT:
2862 * Check if the client is in
2863 * the root list. Only valid
2864 * for AUTH_SYS.
2866 if (flavor != AUTH_SYS)
2867 break;
2869 if (!match)
2870 break;
2872 if (val == NULL || *val == '\0')
2873 break;
2875 if (clnt_uid != 0)
2876 break;
2878 if (in_access_list(cln, val) > 0) {
2879 perm |= NFSAUTH_ROOT;
2880 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2881 map_deny = B_FALSE;
2883 if (perm & NFSAUTH_GROUPS) {
2884 free(*srv_gids);
2885 *srv_gids = NULL;
2886 *srv_ngids = 0;
2887 perm &= ~NFSAUTH_GROUPS;
2890 break;
2892 case OPT_NONE:
2894 * Check if the client should have no access
2895 * to this share at all. This option behaves
2896 * more like "root" than either "rw" or "ro".
2898 if (in_access_list(cln, val) > 0)
2899 perm |= NFSAUTH_DENIED;
2900 break;
2902 case OPT_UIDMAP: {
2903 char *c;
2904 char *n;
2907 * The uidmap is supported for AUTH_SYS only.
2909 if (flavor != AUTH_SYS)
2910 break;
2912 if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2913 break;
2915 for (c = val; c != NULL; c = n) {
2916 char *s;
2917 char *al;
2918 uid_t srv;
2920 n = strchr(c, '~');
2921 if (n != NULL)
2922 *n++ = '\0';
2924 s = strchr(c, ':');
2925 if (s != NULL) {
2926 *s++ = '\0';
2927 al = strchr(s, ':');
2928 if (al != NULL)
2929 *al++ = '\0';
2932 if (s == NULL || al == NULL)
2933 continue;
2935 if (*c == '\0') {
2936 if (clnt_uid != (uid_t)-1)
2937 continue;
2938 } else if (strcmp(c, "*") != 0) {
2939 uid_t clnt;
2941 if (!get_uid(c, &clnt))
2942 continue;
2944 if (clnt_uid != clnt)
2945 continue;
2948 if (*s == '\0')
2949 srv = UID_NOBODY;
2950 else if (!get_uid(s, &srv))
2951 continue;
2952 else if (srv == (uid_t)-1) {
2953 map_deny = B_TRUE;
2954 break;
2957 if (in_access_list(cln, al) > 0) {
2958 *srv_uid = srv;
2959 perm |= NFSAUTH_UIDMAP;
2961 if (perm & NFSAUTH_GROUPS) {
2962 free(*srv_gids);
2963 *srv_gids = NULL;
2964 *srv_ngids = 0;
2965 perm &= ~NFSAUTH_GROUPS;
2968 break;
2972 break;
2975 case OPT_GIDMAP: {
2976 char *c;
2977 char *n;
2980 * The gidmap is supported for AUTH_SYS only.
2982 if (flavor != AUTH_SYS)
2983 break;
2985 if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2986 break;
2988 for (c = val; c != NULL; c = n) {
2989 char *s;
2990 char *al;
2991 gid_t srv;
2993 n = strchr(c, '~');
2994 if (n != NULL)
2995 *n++ = '\0';
2997 s = strchr(c, ':');
2998 if (s != NULL) {
2999 *s++ = '\0';
3000 al = strchr(s, ':');
3001 if (al != NULL)
3002 *al++ = '\0';
3005 if (s == NULL || al == NULL)
3006 break;
3008 if (*c == '\0') {
3009 if (clnt_gid != (gid_t)-1)
3010 continue;
3011 } else if (strcmp(c, "*") != 0) {
3012 gid_t clnt;
3014 if (!get_gid(c, &clnt))
3015 continue;
3017 if (clnt_gid != clnt)
3018 continue;
3021 if (*s == '\0')
3022 srv = UID_NOBODY;
3023 else if (!get_gid(s, &srv))
3024 continue;
3025 else if (srv == (gid_t)-1) {
3026 map_deny = B_TRUE;
3027 break;
3030 if (in_access_list(cln, al) > 0) {
3031 *srv_gid = srv;
3032 perm |= NFSAUTH_GIDMAP;
3034 if (perm & NFSAUTH_GROUPS) {
3035 free(*srv_gids);
3036 *srv_gids = NULL;
3037 *srv_ngids = 0;
3038 perm &= ~NFSAUTH_GROUPS;
3041 break;
3045 break;
3048 default:
3049 break;
3053 done:
3054 if (perm & NFSAUTH_ROOT) {
3055 *srv_uid = 0;
3056 *srv_gid = 0;
3059 if (map_deny)
3060 perm |= NFSAUTH_DENIED;
3062 if (!(perm & NFSAUTH_UIDMAP))
3063 *srv_uid = clnt_uid;
3064 if (!(perm & NFSAUTH_GIDMAP))
3065 *srv_gid = clnt_gid;
3068 * If no match then set the perm accordingly
3070 if (!match || perm & NFSAUTH_DENIED) {
3071 free(opts);
3072 return (NFSAUTH_DENIED);
3075 if (list) {
3077 * If the client doesn't match an "ro" or "rw" list then
3078 * check if it may have access by using a different flavor.
3079 * If so, return NFSAUTH_WRONGSEC.
3080 * If not, return NFSAUTH_DENIED.
3082 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
3083 if (is_wrongsec(sh, cln, flavor))
3084 perm |= NFSAUTH_WRONGSEC;
3085 else
3086 perm |= NFSAUTH_DENIED;
3088 } else {
3090 * The client matched a flavor entry that
3091 * has no explicit "rw" or "ro" determination.
3092 * Make sure it defaults to "rw".
3094 perm |= NFSAUTH_RW;
3098 * The client may show up in both ro= and rw=
3099 * lists. If so, then turn off the RO access
3100 * bit leaving RW access.
3102 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
3104 * Logically cover all permutations of rw=,ro=.
3105 * In the case where, rw,ro=<host> we would like
3106 * to remove RW access for the host. In all other cases
3107 * RW wins the precedence battle.
3109 if (!rw_val && ro_val) {
3110 perm &= ~(NFSAUTH_RW);
3111 } else {
3112 perm &= ~(NFSAUTH_RO);
3116 free(opts);
3118 return (perm);
3121 void
3122 check_sharetab()
3124 FILE *f;
3125 struct stat st;
3126 static timestruc_t last_sharetab_time;
3127 timestruc_t prev_sharetab_time;
3128 share_t *sh;
3129 struct sh_list *shp, *shp_prev;
3130 int res, c = 0;
3133 * read in /etc/dfs/sharetab if it has changed
3135 if (stat(SHARETAB, &st) != 0) {
3136 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3137 return;
3140 if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
3141 st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
3143 * No change.
3145 return;
3149 * Remember the mod time, then after getting the
3150 * write lock check again. If another thread
3151 * already did the update, then there's no
3152 * work to do.
3154 prev_sharetab_time = last_sharetab_time;
3156 (void) rw_wrlock(&sharetab_lock);
3158 if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
3159 prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
3160 (void) rw_unlock(&sharetab_lock);
3161 return;
3165 * Note that since the sharetab is now in memory
3166 * and a snapshot is taken, we no longer have to
3167 * lock the file.
3169 f = fopen(SHARETAB, "r");
3170 if (f == NULL) {
3171 syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
3172 (void) rw_unlock(&sharetab_lock);
3173 return;
3177 * Once we are sure /etc/dfs/sharetab has been
3178 * modified, flush netgroup cache entries.
3180 netgrp_cache_flush();
3182 sh_free(share_list); /* free old list */
3183 share_list = NULL;
3185 while ((res = getshare(f, &sh)) > 0) {
3186 c++;
3187 if (strcmp(sh->sh_fstype, "nfs") != 0)
3188 continue;
3190 shp = malloc(sizeof (*shp));
3191 if (shp == NULL)
3192 goto alloc_failed;
3193 if (share_list == NULL)
3194 share_list = shp;
3195 else
3196 /* LINTED not used before set */
3197 shp_prev->shl_next = shp;
3198 shp_prev = shp;
3199 shp->shl_next = NULL;
3200 shp->shl_sh = sharedup(sh);
3201 if (shp->shl_sh == NULL)
3202 goto alloc_failed;
3205 if (res < 0)
3206 syslog(LOG_ERR, "%s: invalid at line %d\n",
3207 SHARETAB, c + 1);
3209 if (stat(SHARETAB, &st) != 0) {
3210 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3211 (void) fclose(f);
3212 (void) rw_unlock(&sharetab_lock);
3213 return;
3216 last_sharetab_time = st.st_mtim;
3217 (void) fclose(f);
3218 (void) rw_unlock(&sharetab_lock);
3220 return;
3222 alloc_failed:
3224 syslog(LOG_ERR, "check_sharetab: no memory");
3225 sh_free(share_list);
3226 share_list = NULL;
3227 (void) fclose(f);
3228 (void) rw_unlock(&sharetab_lock);
3231 static void
3232 sh_free(struct sh_list *shp)
3234 struct sh_list *next;
3236 while (shp) {
3237 sharefree(shp->shl_sh);
3238 next = shp->shl_next;
3239 free(shp);
3240 shp = next;
3246 * Remove an entry from mounted list
3248 static void
3249 umount(struct svc_req *rqstp)
3251 char *host, *path, *remove_path;
3252 char rpath[MAXPATHLEN];
3253 SVCXPRT *transp;
3254 struct cln cln;
3256 transp = rqstp->rq_xprt;
3257 path = NULL;
3258 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3259 svcerr_decode(transp);
3260 return;
3263 cln_init(&cln, transp);
3265 errno = 0;
3266 if (!svc_sendreply(transp, xdr_void, (char *)NULL))
3267 log_cant_reply_cln(&cln);
3269 host = cln_gethost(&cln);
3270 if (host == NULL) {
3272 * Without the hostname we can't do audit or delete
3273 * this host from the mount entries.
3275 (void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3276 return;
3279 if (verbose)
3280 syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3282 audit_mountd_umount(host, path);
3284 remove_path = rpath; /* assume we will use the cannonical path */
3285 if (realpath(path, rpath) == NULL) {
3286 if (verbose)
3287 syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3288 remove_path = path; /* use path provided instead */
3291 mntlist_delete(host, remove_path); /* remove from mount list */
3293 cln_fini(&cln);
3295 (void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3299 * Remove all entries for one machine from mounted list
3301 static void
3302 umountall(struct svc_req *rqstp)
3304 SVCXPRT *transp;
3305 char *host;
3306 struct cln cln;
3308 transp = rqstp->rq_xprt;
3309 if (!svc_getargs(transp, xdr_void, NULL)) {
3310 svcerr_decode(transp);
3311 return;
3314 * We assume that this call is asynchronous and made via rpcbind
3315 * callit routine. Therefore return control immediately. The error
3316 * causes rpcbind to remain silent, as opposed to every machine
3317 * on the net blasting the requester with a response.
3319 svcerr_systemerr(transp);
3321 cln_init(&cln, transp);
3323 host = cln_gethost(&cln);
3324 if (host == NULL) {
3325 /* Can't do anything without the name of the client */
3326 return;
3330 * Remove all hosts entries from mount list
3332 mntlist_delete_all(host);
3334 if (verbose)
3335 syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3337 cln_fini(&cln);
3340 void *
3341 exmalloc(size_t size)
3343 void *ret;
3345 if ((ret = malloc(size)) == NULL) {
3346 syslog(LOG_ERR, "Out of memory");
3347 exit(1);
3349 return (ret);
3352 static tsol_tpent_t *
3353 get_client_template(struct sockaddr *sock)
3355 in_addr_t v4client;
3356 in6_addr_t v6client;
3357 char v4_addr[INET_ADDRSTRLEN];
3358 char v6_addr[INET6_ADDRSTRLEN];
3359 tsol_rhent_t *rh;
3360 tsol_tpent_t *tp;
3362 switch (sock->sa_family) {
3363 case AF_INET:
3364 v4client = ((struct sockaddr_in *)(void *)sock)->
3365 sin_addr.s_addr;
3366 if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
3367 NULL)
3368 return (NULL);
3369 rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
3370 if (rh == NULL)
3371 return (NULL);
3372 tp = tsol_gettpbyname(rh->rh_template);
3373 tsol_freerhent(rh);
3374 return (tp);
3375 break;
3376 case AF_INET6:
3377 v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
3378 if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
3379 NULL)
3380 return (NULL);
3381 rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
3382 if (rh == NULL)
3383 return (NULL);
3384 tp = tsol_gettpbyname(rh->rh_template);
3385 tsol_freerhent(rh);
3386 return (tp);
3387 break;
3388 default:
3389 return (NULL);