xref: /illumos-gate/usr/src/cmd/fs.d/nfs/mountd/mountd.c (revision 3db86aab)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * Portions of this source code were derived from Berkeley 4.3 BSD
31  * under license from the Regents of the University of California.
32  */
33 
34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <sys/param.h>
43 #include <rpc/rpc.h>
44 #include <sys/stat.h>
45 #include <netconfig.h>
46 #include <netdir.h>
47 #include <sys/file.h>
48 #include <sys/time.h>
49 #include <sys/errno.h>
50 #include <rpcsvc/mount.h>
51 #include <sys/pathconf.h>
52 #include <sys/systeminfo.h>
53 #include <sys/utsname.h>
54 #include <signal.h>
55 #include <locale.h>
56 #include <unistd.h>
57 #include <errno.h>
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
61 #include <netdb.h>
62 #include <thread.h>
63 #include <assert.h>
64 #include <priv_utils.h>
65 #include <nfs/auth.h>
66 #include <nfs/nfssys.h>
67 #include <nfs/nfs.h>
68 #include <nfs/nfs_sec.h>
69 #include <rpcsvc/daemon_utils.h>
70 #include <deflt.h>
71 #include "../../fslib.h"
72 #include "../lib/sharetab.h"
73 #include "mountd.h"
74 
75 struct sh_list *share_list;
76 
77 rwlock_t sharetab_lock;		/* lock to protect the cached sharetab */
78 static mutex_t mnttab_lock;	/* prevent concurrent mnttab readers */
79 
80 static struct share *find_lofsentry(char *, int *);
81 static void getclientsnames(SVCXPRT *, struct netbuf **,
82 					struct nd_hostservlist **);
83 static int getclientsflavors_old(struct share *, struct netbuf *,
84 	struct nd_hostservlist *, int *);
85 static int getclientsflavors_new(struct share *, struct netbuf *,
86 	struct nd_hostservlist *, int *);
87 static int check_client_old(struct share *, struct netbuf *,
88     struct nd_hostservlist *, int);
89 static int check_client_new(struct share *, struct netbuf *,
90 	struct nd_hostservlist *, int);
91 static int  in_access_list(struct netbuf *, struct nd_hostservlist *, char *);
92 static void mnt(struct svc_req *, SVCXPRT *);
93 static void mnt_pathconf(struct svc_req *);
94 static void mount(struct svc_req *r);
95 static void sh_free(struct sh_list *);
96 static void umount(struct svc_req *);
97 static void umountall(struct svc_req *);
98 static int netmatch(struct netbuf *, char *);
99 static void sigexit(int);
100 static int newopts(char *);
101 
102 static int verbose;
103 static int rejecting;
104 static int mount_vers_min = MOUNTVERS;
105 static int mount_vers_max = MOUNTVERS3;
106 
107 thread_t	nfsauth_thread;
108 
109 /* ARGSUSED */
110 static void *
111 nfsauth_svc(void *arg)
112 {
113 	int	doorfd = -1;
114 	uint_t	darg;
115 #ifdef DEBUG
116 	int	dfd;
117 #endif
118 
119 	if ((doorfd = door_create(nfsauth_func, NULL,
120 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
121 		syslog(LOG_ERR, "Unable to create door: %m\n");
122 		exit(10);
123 	}
124 
125 #ifdef DEBUG
126 	/*
127 	 * Create a file system path for the door
128 	 */
129 	if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
130 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
131 		syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
132 		(void) close(doorfd);
133 		exit(11);
134 	}
135 
136 	/*
137 	 * Clean up any stale namespace associations
138 	 */
139 	(void) fdetach(MOUNTD_DOOR);
140 
141 	/*
142 	 * Register in namespace to pass to the kernel to door_ki_open
143 	 */
144 	if (fattach(doorfd, MOUNTD_DOOR) == -1) {
145 		syslog(LOG_ERR, "Unable to fattach door: %m\n");
146 		(void) close(dfd);
147 		(void) close(doorfd);
148 		exit(12);
149 	}
150 	(void) close(dfd);
151 #endif
152 
153 	/*
154 	 * Must pass the doorfd down to the kernel.
155 	 */
156 	darg = doorfd;
157 	(void) _nfssys(MOUNTD_ARGS, &darg);
158 
159 	/*
160 	 * Wait for incoming calls
161 	 */
162 	/*CONSTCOND*/
163 	for (;;)
164 		(void) pause();
165 
166 	/*NOTREACHED*/
167 	syslog(LOG_ERR, gettext("Door server exited"));
168 	return (NULL);
169 }
170 
171 int
172 main(int argc, char *argv[])
173 {
174 	int	pid;
175 	int	c;
176 	int	rpc_svc_mode = RPC_SVC_MT_AUTO;
177 	int	maxthreads;
178 	int	maxrecsz = RPC_MAXDATASIZE;
179 	bool_t	exclbind = TRUE;
180 	bool_t	can_do_mlp;
181 	long	thr_flags = (THR_NEW_LWP|THR_DAEMON);
182 
183 	/*
184 	 * Mountd requires uid 0 for:
185 	 *	/etc/rmtab updates (we could chown it to daemon)
186 	 *	/etc/dfs/dfstab reading (it wants to lock out share which
187 	 *		doesn't do any locking before first truncate;
188 	 *		NFS share does; should use fcntl locking instead)
189 	 *	Needed privileges:
190 	 *		auditing
191 	 *		nfs syscall
192 	 *		file dac search (so it can stat all files)
193 	 *	Optional privileges:
194 	 *		MLP
195 	 */
196 	can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
197 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
198 	    PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
199 	    can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
200 		(void) fprintf(stderr,
201 			"%s must be run as with sufficient privileges\n",
202 			argv[0]);
203 		exit(1);
204 	}
205 
206 	maxthreads = 0;
207 
208 	while ((c = getopt(argc, argv, "vrm:")) != EOF) {
209 		switch (c) {
210 		case 'v':
211 			verbose++;
212 			break;
213 		case 'r':
214 			rejecting = 1;
215 			break;
216 		case 'm':
217 			maxthreads = atoi(optarg);
218 			if (maxthreads < 1) {
219 				(void) fprintf(stderr,
220 	"%s: must specify positive maximum threads count, using default\n",
221 						argv[0]);
222 				maxthreads = 0;
223 			}
224 			break;
225 		}
226 	}
227 
228 	/*
229 	 * Read in the NFS version values from config file.
230 	 */
231 	if ((defopen(NFSADMIN)) == 0) {
232 		char *defval;
233 		int defvers;
234 
235 		if ((defval = defread("NFS_SERVER_VERSMIN=")) != NULL) {
236 			errno = 0;
237 			defvers = strtol(defval, (char **)NULL, 10);
238 			if (errno == 0) {
239 				mount_vers_min = defvers;
240 				/*
241 				 * special because NFSv2 is
242 				 * supported by mount v1 & v2
243 				 */
244 				if (defvers == NFS_VERSION)
245 					mount_vers_min = MOUNTVERS;
246 			}
247 		}
248 		if ((defval = defread("NFS_SERVER_VERSMAX=")) != NULL) {
249 			errno = 0;
250 			defvers = strtol(defval, (char **)NULL, 10);
251 			if (errno == 0) {
252 				mount_vers_max = defvers;
253 			}
254 		}
255 
256 		/* close defaults file */
257 		defopen(NULL);
258 	}
259 
260 	/*
261 	 * Sanity check versions,
262 	 * even though we may get versions > MOUNTVERS3, we still need
263 	 * to start nfsauth service, so continue on regardless of values.
264 	 */
265 	if (mount_vers_min > mount_vers_max) {
266 		syslog(LOG_NOTICE, "NFS_SERVER_VERSMIN > NFS_SERVER_VERSMAX");
267 		mount_vers_max = mount_vers_min;
268 	}
269 	(void) setlocale(LC_ALL, "");
270 	(void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
271 	(void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
272 	netgroup_init();
273 
274 #if !defined(TEXT_DOMAIN)
275 #define	TEXT_DOMAIN "SYS_TEST"
276 #endif
277 	(void) textdomain(TEXT_DOMAIN);
278 
279 	/* Don't drop core if the NFS module isn't loaded. */
280 	(void) signal(SIGSYS, SIG_IGN);
281 
282 	(void) signal(SIGHUP, sigexit);
283 	(void) signal(SIGCLD, sigexit);
284 
285 	switch (fork()) {
286 	case 0:		/* child */
287 		break;
288 	case -1:
289 		perror("mountd: can't fork");
290 		exit(1);
291 	default:	/* parent */
292 		for (;;)
293 			(void) pause();
294 		/* NOTREACHED */
295 	}
296 
297 	(void) signal(SIGHUP, SIG_DFL);
298 	(void) signal(SIGCLD, SIG_DFL);
299 
300 	/*
301 	 * If we coredump it'll be in /core
302 	 */
303 	if (chdir("/") < 0)
304 		syslog(LOG_ERR, "chdir /: %m");
305 
306 	/*
307 	 * Close existing file descriptors, open "/dev/null" as
308 	 * standard input, output, and error, and detach from
309 	 * controlling terminal.
310 	 */
311 	closefrom(0);
312 	(void) open("/dev/null", O_RDONLY);
313 	(void) open("/dev/null", O_WRONLY);
314 	(void) dup(1);
315 	(void) setsid();
316 
317 	openlog("mountd", LOG_PID, LOG_DAEMON);
318 
319 	/*
320 	 * establish our lock on the lock file and write our pid to it.
321 	 * exit if some other process holds the lock, or if there's any
322 	 * error in writing/locking the file.
323 	 */
324 	pid = _enter_daemon_lock(MOUNTD);
325 	switch (pid) {
326 	case 0:
327 		break;
328 	case -1:
329 		syslog(LOG_ERR, "error locking for %s: %s", MOUNTD,
330 		    strerror(errno));
331 		exit(2);
332 	default:
333 		/* daemon was already running */
334 		exit(0);
335 	}
336 
337 	audit_mountd_setup();	/* BSM */
338 
339 	/*
340 	 * Tell RPC that we want automatic thread mode.
341 	 * A new thread will be spawned for each request.
342 	 */
343 	if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
344 		syslog(LOG_ERR, "unable to set automatic MT mode");
345 		exit(1);
346 	}
347 
348 	/*
349 	 * Enable non-blocking mode and maximum record size checks for
350 	 * connection oriented transports.
351 	 */
352 	if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
353 		syslog(LOG_INFO, "unable to set RPC max record size");
354 	}
355 
356 	/*
357 	 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
358 	 * from being hijacked by a bind to a more specific addr.
359 	 */
360 	if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
361 		syslog(LOG_INFO,  "warning: unable to set udp/tcp EXCLBIND");
362 	}
363 
364 	/*
365 	 * If the -m argument was specified, then set the
366 	 * maximum number of threads to the value specified.
367 	 */
368 	if (maxthreads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &maxthreads)) {
369 		syslog(LOG_ERR, "unable to set maxthreads");
370 		exit(1);
371 	}
372 
373 	/*
374 	 * Make sure to unregister any previous versions in case the
375 	 * user is reconfiguring the server in interesting ways.
376 	 */
377 	svc_unreg(MOUNTPROG, MOUNTVERS);
378 	svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
379 	svc_unreg(MOUNTPROG, MOUNTVERS3);
380 
381 	/*
382 	 * Create the nfsauth thread with same signal disposition
383 	 * as the main thread. We need to create a separate thread
384 	 * since mountd() will be both an RPC server (for remote
385 	 * traffic) _and_ a doors server (for kernel upcalls).
386 	 */
387 	if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
388 		syslog(LOG_ERR, gettext("Failed to create NFSAUTH svc thread"));
389 		exit(2);
390 	}
391 
392 	/*
393 	 * Create datagram and connection oriented services
394 	 */
395 	if (mount_vers_max >= MOUNTVERS) {
396 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
397 			syslog(LOG_ERR,
398 				"couldn't register datagram_v MOUNTVERS");
399 			exit(1);
400 		}
401 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
402 			syslog(LOG_ERR,
403 				"couldn't register circuit_v MOUNTVERS");
404 			exit(1);
405 		}
406 	}
407 	if (mount_vers_max >= MOUNTVERS_POSIX) {
408 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
409 							"datagram_v") == 0) {
410 			syslog(LOG_ERR,
411 				"couldn't register datagram_v MOUNTVERS_POSIX");
412 			exit(1);
413 		}
414 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
415 							"circuit_v") == 0) {
416 			syslog(LOG_ERR,
417 				"couldn't register circuit_v MOUNTVERS_POSIX");
418 			exit(1);
419 		}
420 	}
421 
422 	if (mount_vers_max >= MOUNTVERS3) {
423 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
424 			syslog(LOG_ERR,
425 				"couldn't register datagram_v MOUNTVERS3");
426 			exit(1);
427 		}
428 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
429 			syslog(LOG_ERR,
430 				"couldn't register circuit_v MOUNTVERS3");
431 			exit(1);
432 		}
433 	}
434 
435 	/*
436 	 * Start serving
437 	 */
438 	rmtab_load();
439 	(void) kill(getppid(), SIGHUP);
440 
441 	/* Get rid of the most dangerous basic privileges. */
442 	__fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
443 	    (char *)NULL);
444 
445 	svc_run();
446 	syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
447 	abort();
448 	/* NOTREACHED */
449 	return (0);
450 }
451 
452 /*
453  * Server procedure switch routine
454  */
455 void
456 mnt(struct svc_req *rqstp, SVCXPRT *transp)
457 {
458 	switch (rqstp->rq_proc) {
459 	case NULLPROC:
460 		errno = 0;
461 		if (!svc_sendreply(transp, xdr_void, (char *)0))
462 			log_cant_reply(transp);
463 		return;
464 
465 	case MOUNTPROC_MNT:
466 		mount(rqstp);
467 		return;
468 
469 	case MOUNTPROC_DUMP:
470 		mntlist_send(transp);
471 		return;
472 
473 	case MOUNTPROC_UMNT:
474 		umount(rqstp);
475 		return;
476 
477 	case MOUNTPROC_UMNTALL:
478 		umountall(rqstp);
479 		return;
480 
481 	case MOUNTPROC_EXPORT:
482 	case MOUNTPROC_EXPORTALL:
483 		export(rqstp);
484 		return;
485 
486 	case MOUNTPROC_PATHCONF:
487 		if (rqstp->rq_vers == MOUNTVERS_POSIX)
488 			mnt_pathconf(rqstp);
489 		else
490 			svcerr_noproc(transp);
491 		return;
492 
493 	default:
494 		svcerr_noproc(transp);
495 		return;
496 	}
497 }
498 
499 /* Set up anonymous client */
500 
501 struct nd_hostservlist *
502 anon_client(char *host)
503 {
504 	struct nd_hostservlist *anon_hsl;
505 	struct nd_hostserv *anon_hs;
506 
507 	anon_hsl = malloc(sizeof (*anon_hsl));
508 	if (anon_hsl == NULL)
509 		return (NULL);
510 
511 	anon_hs = malloc(sizeof (*anon_hs));
512 	if (anon_hs == NULL) {
513 		free(anon_hsl);
514 		return (NULL);
515 	}
516 
517 	if (host == NULL)
518 		anon_hs->h_host = strdup("(anon)");
519 	else
520 		anon_hs->h_host = strdup(host);
521 
522 	if (anon_hs->h_host == NULL) {
523 		free(anon_hs);
524 		free(anon_hsl);
525 		return (NULL);
526 	}
527 	anon_hs->h_serv = '\0';
528 
529 	anon_hsl->h_cnt = 1;
530 	anon_hsl->h_hostservs = anon_hs;
531 
532 	return (anon_hsl);
533 }
534 
535 /*
536  * Get the client's hostname from the transport handle
537  * If the name is not available then return "(anon)".
538  */
539 void
540 getclientsnames(SVCXPRT *transp, struct netbuf **nbuf,
541     struct nd_hostservlist **serv)
542 {
543 	struct netconfig *nconf;
544 	char tmp[MAXIPADDRLEN];
545 	char *host = NULL;
546 
547 	nconf = getnetconfigent(transp->xp_netid);
548 	if (nconf == NULL) {
549 		syslog(LOG_ERR, "%s: getnetconfigent failed",
550 			transp->xp_netid);
551 		*serv = anon_client(host);
552 		return;
553 	}
554 
555 	*nbuf = svc_getrpccaller(transp);
556 	if (*nbuf == NULL) {
557 		freenetconfigent(nconf);
558 		*serv = anon_client(host);
559 		return;
560 	}
561 
562 	/*
563 	 * Use the this API instead of the netdir_getbyaddr()
564 	 * to avoid service lookup.
565 	 */
566 	if (__netdir_getbyaddr_nosrv(nconf, serv, *nbuf)) {
567 		host = &tmp[0];
568 		if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
569 			struct sockaddr_in *sa;
570 
571 			/* LINTED pointer alignment */
572 			sa = (struct sockaddr_in *)((*nbuf)->buf);
573 			(void) inet_ntoa_r(sa->sin_addr, tmp);
574 			*serv =	anon_client(host);
575 			freenetconfigent(nconf);
576 			return;
577 		} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
578 			struct sockaddr_in6 *sa;
579 
580 			/* LINTED pointer alignment */
581 			sa = (struct sockaddr_in6 *)((*nbuf)->buf);
582 			(void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr,
583 					tmp, INET6_ADDRSTRLEN);
584 			*serv =	anon_client(host);
585 			freenetconfigent(nconf);
586 			return;
587 		}
588 		freenetconfigent(nconf);
589 		*serv = anon_client(host);
590 		return;
591 	}
592 	freenetconfigent(nconf);
593 }
594 
595 void
596 log_cant_reply(SVCXPRT *transp)
597 {
598 	int saverrno;
599 	struct nd_hostservlist *clnames = NULL;
600 	register char *host;
601 	struct netbuf *nb;
602 
603 	saverrno = errno;	/* save error code */
604 	getclientsnames(transp, &nb, &clnames);
605 	if (clnames == NULL)
606 		return;
607 	host = clnames->h_hostservs->h_host;
608 
609 	errno = saverrno;
610 	if (errno == 0)
611 		syslog(LOG_ERR, "couldn't send reply to %s", host);
612 	else
613 		syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
614 
615 	netdir_free(clnames, ND_HOSTSERVLIST);
616 }
617 
618 /*
619  * Answer pathconf questions for the mount point fs
620  */
621 static void
622 mnt_pathconf(struct svc_req *rqstp)
623 {
624 	SVCXPRT *transp;
625 	struct pathcnf p;
626 	char *path, rpath[MAXPATHLEN];
627 	struct stat st;
628 
629 	transp = rqstp->rq_xprt;
630 	path = NULL;
631 	(void) memset((caddr_t)&p, 0, sizeof (p));
632 
633 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
634 		svcerr_decode(transp);
635 		return;
636 	}
637 	if (lstat(path, &st) < 0) {
638 		_PC_SET(_PC_ERROR, p.pc_mask);
639 		goto done;
640 	}
641 	/*
642 	 * Get a path without symbolic links.
643 	 */
644 	if (realpath(path, rpath) == NULL) {
645 		syslog(LOG_DEBUG,
646 			"mount request: realpath failed on %s: %m",
647 			path);
648 		_PC_SET(_PC_ERROR, p.pc_mask);
649 		goto done;
650 	}
651 	(void) memset((caddr_t)&p, 0, sizeof (p));
652 	/*
653 	 * can't ask about devices over NFS
654 	 */
655 	_PC_SET(_PC_MAX_CANON, p.pc_mask);
656 	_PC_SET(_PC_MAX_INPUT, p.pc_mask);
657 	_PC_SET(_PC_PIPE_BUF, p.pc_mask);
658 	_PC_SET(_PC_VDISABLE, p.pc_mask);
659 
660 	errno = 0;
661 	p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
662 	if (errno)
663 		_PC_SET(_PC_LINK_MAX, p.pc_mask);
664 	p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
665 	if (errno)
666 		_PC_SET(_PC_NAME_MAX, p.pc_mask);
667 	p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
668 	if (errno)
669 		_PC_SET(_PC_PATH_MAX, p.pc_mask);
670 	if (pathconf(rpath, _PC_NO_TRUNC) == 1)
671 		_PC_SET(_PC_NO_TRUNC, p.pc_mask);
672 	if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
673 		_PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
674 
675 done:
676 	errno = 0;
677 	if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
678 		log_cant_reply(transp);
679 	if (path != NULL)
680 		svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
681 }
682 
683 /*
684  * If the rootmount (export) option is specified, the all mount requests for
685  * subdirectories return EACCES.
686  */
687 static int
688 checkrootmount(struct share *sh, char *rpath)
689 {
690 	char *val;
691 
692 	if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
693 		free(val);
694 		if (strcmp(sh->sh_path, rpath) != 0)
695 			return (0);
696 		else
697 			return (1);
698 	} else
699 		return (1);
700 }
701 
702 #define	MAX_FLAVORS	128
703 
704 /*
705  * Return only EACCES if client does not have access
706  *  to this directory.
707  * "If the server exports only /a/b, an attempt to
708  *  mount a/b/c will fail with ENOENT if the directory
709  *  does not exist"... However, if the client
710  *  does not have access to /a/b, an attacker can
711  *  determine whether the directory exists.
712  * This routine checks either existence of the file or
713  * existence of the file name entry in the mount table.
714  * If the file exists and there is no file name entry,
715  * the error returned should be EACCES.
716  * If the file does not exist, it must be determined
717  * whether the client has access to a parent
718  * directory.  If the client has access to a parent
719  * directory, the error returned should be ENOENT,
720  * otherwise EACCES.
721  */
722 static int
723 mount_enoent_error(char *path, char *rpath, struct nd_hostservlist *clnames,
724     struct netbuf *nb, int *flavor_list)
725 {
726 	char *checkpath, *dp;
727 	struct share *sh = NULL;
728 	int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
729 	int flavor_count;
730 
731 	checkpath = strdup(path);
732 	if (checkpath == NULL) {
733 		syslog(LOG_ERR, "mount_enoent: no memory");
734 		return (EACCES);
735 	}
736 
737 	/* CONSTCOND */
738 	while (1) {
739 		if (sh) {
740 			sharefree(sh);
741 			sh = NULL;
742 		}
743 		if ((sh = findentry(rpath)) == NULL &&
744 			(sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
745 			/*
746 			 * There is no file name entry.
747 			 * If the file (with symbolic links resolved) exists,
748 			 * the error returned should be EACCES.
749 			 */
750 			if (realpath_error == 0)
751 				break;
752 		} else if (checkrootmount(sh, rpath) == 0) {
753 			/*
754 			 * This is a "nosub" only export, in which case,
755 			 * mounting subdirectories isn't allowed.
756 			 * If the file (with symbolic links resolved) exists,
757 			 * the error returned should be EACCES.
758 			 */
759 			if (realpath_error == 0)
760 				break;
761 		} else {
762 			/*
763 			 * Check permissions in mount table.
764 			 */
765 			if (newopts(sh->sh_opts))
766 				flavor_count = getclientsflavors_new(sh, nb,
767 					clnames, flavor_list);
768 			else
769 				flavor_count = getclientsflavors_old(sh, nb,
770 					clnames, flavor_list);
771 			if (flavor_count != 0) {
772 				/*
773 				 * Found entry in table and
774 				 * client has correct permissions.
775 				 */
776 				reply_error = ENOENT;
777 				break;
778 			}
779 		}
780 		/*
781 		 * Check all parent directories.
782 		 */
783 		dp = strrchr(checkpath, '/');
784 		if (dp == NULL)
785 			break;
786 		*dp = '\0';
787 		if (strlen(checkpath) == 0)
788 			break;
789 		/*
790 		 * Get the real path (no symbolic links in it)
791 		 */
792 		if (realpath(checkpath, rpath) == NULL) {
793 			if (errno != ENOENT)
794 				break;
795 		} else {
796 			realpath_error = 0;
797 		}
798 	}
799 
800 	if (sh)
801 		sharefree(sh);
802 	free(checkpath);
803 	return (reply_error);
804 }
805 
806 /*
807  * Check mount requests, add to mounted list if ok
808  */
809 static void
810 mount(struct svc_req *rqstp)
811 {
812 	SVCXPRT *transp;
813 	int version, vers;
814 	struct fhstatus fhs;
815 	struct mountres3 mountres3;
816 	char fh[FHSIZE3];
817 	int len = FHSIZE3;
818 	char *path, rpath[MAXPATHLEN];
819 	struct share *sh = NULL;
820 	struct nd_hostservlist *clnames = NULL;
821 	char *host = NULL;
822 	int error = 0, lofs_tried = 0;
823 	int flavor_list[MAX_FLAVORS];
824 	int flavor_count;
825 	struct netbuf *nb;
826 
827 	transp = rqstp->rq_xprt;
828 	version = rqstp->rq_vers;
829 	path = NULL;
830 
831 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
832 		svcerr_decode(transp);
833 		return;
834 	}
835 
836 	getclientsnames(transp, &nb, &clnames);
837 	if (clnames == NULL || nb == NULL) {
838 		/*
839 		 * We failed to get a name for the client, even 'anon',
840 		 * probably because we ran out of memory. In this situation
841 		 * it doesn't make sense to allow the mount to succeed.
842 		 */
843 		error = EACCES;
844 		goto reply;
845 	}
846 	host = clnames->h_hostservs[0].h_host;
847 
848 	/*
849 	 * If the version being used is less than the minimum version,
850 	 * the filehandle translation should not be provided to the
851 	 * client.
852 	 */
853 	if (rejecting || version < mount_vers_min) {
854 		if (verbose)
855 			syslog(LOG_NOTICE, "Rejected mount: %s for %s",
856 				host, path);
857 		error = EACCES;
858 		goto reply;
859 	}
860 
861 	/*
862 	 * Trusted Extension doesn't support older versions of nfs(v2, v3).
863 	 * To prevent circumventing TX label policy via using an older
864 	 * version of nfs client, reject the mount request and log an
865 	 * error.
866 	 */
867 	if (is_system_labeled()) {
868 		syslog(LOG_ERR,
869 		    "mount rejected: Solaris TX only supports nfs4 clients");
870 		error = EACCES;
871 		goto reply;
872 	}
873 
874 	/*
875 	 * Get the real path (no symbolic links in it)
876 	 */
877 	if (realpath(path, rpath) == NULL) {
878 		error = errno;
879 		if (verbose)
880 			syslog(LOG_ERR,
881 				"mount request: realpath: %s: %m", path);
882 		if (error == ENOENT)
883 			error = mount_enoent_error(path, rpath, clnames, nb,
884 					flavor_list);
885 		goto reply;
886 	}
887 
888 	if ((sh = findentry(rpath)) == NULL &&
889 		(sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
890 		error = EACCES;
891 		goto reply;
892 	}
893 
894 	/*
895 	 * Check if this is a "nosub" only export, in which case, mounting
896 	 * subdirectories isn't allowed. Bug 1184573.
897 	 */
898 	if (checkrootmount(sh, rpath) == 0) {
899 		error = EACCES;
900 		goto reply;
901 	}
902 
903 	if (newopts(sh->sh_opts))
904 		flavor_count = getclientsflavors_new(sh, nb, clnames,
905 					flavor_list);
906 	else
907 		flavor_count = getclientsflavors_old(sh, nb, clnames,
908 					flavor_list);
909 
910 	if (flavor_count == 0) {
911 		error = EACCES;
912 		goto reply;
913 	}
914 
915 	/*
916 	 * Now get the filehandle.
917 	 *
918 	 * NFS V2 clients get a 32 byte filehandle.
919 	 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
920 	 * the embedded FIDs.
921 	 */
922 	vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
923 
924 	/* LINTED pointer alignment */
925 	while (nfs_getfh(rpath, vers, &len, fh) < 0) {
926 		if (errno == EINVAL &&
927 			(sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
928 			errno = 0;
929 			continue;
930 		}
931 		error = errno == EINVAL ? EACCES : errno;
932 		syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
933 			path);
934 		break;
935 	}
936 
937 	if (version == MOUNTVERS3) {
938 		mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
939 		mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
940 	} else {
941 		bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
942 	}
943 
944 reply:
945 	switch (version) {
946 	case MOUNTVERS:
947 	case MOUNTVERS_POSIX:
948 		if (error == EINVAL)
949 			fhs.fhs_status = NFSERR_ACCES;
950 		else if (error == EREMOTE)
951 			fhs.fhs_status = NFSERR_REMOTE;
952 		else
953 			fhs.fhs_status = error;
954 		if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
955 			log_cant_reply(transp);
956 		audit_mountd_mount(host, path, fhs.fhs_status); /* BSM */
957 		break;
958 
959 	case MOUNTVERS3:
960 		if (!error) {
961 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
962 			flavor_list;
963 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
964 			flavor_count;
965 
966 		} else if (error == ENAMETOOLONG)
967 			error = MNT3ERR_NAMETOOLONG;
968 
969 		mountres3.fhs_status = error;
970 		if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
971 			log_cant_reply(transp);
972 		audit_mountd_mount(host, path, mountres3.fhs_status); /* BSM */
973 
974 		break;
975 	}
976 
977 	if (verbose)
978 		syslog(LOG_NOTICE, "MOUNT: %s %s %s",
979 			(host == NULL) ? "unknown host" : host,
980 			error ? "denied" : "mounted", path);
981 
982 	if (path != NULL)
983 		svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
984 
985 	if (!error)
986 		mntlist_new(host, rpath); /* add entry to mount list */
987 done:
988 	if (sh)
989 		sharefree(sh);
990 	netdir_free(clnames, ND_HOSTSERVLIST);
991 }
992 
993 struct share *
994 findentry(char *path)
995 {
996 	struct share *sh = NULL;
997 	struct sh_list *shp;
998 	register char *p1, *p2;
999 	struct stat st1;
1000 	struct stat64 st2;
1001 
1002 	check_sharetab();
1003 
1004 	(void) rw_rdlock(&sharetab_lock);
1005 
1006 	for (shp = share_list; shp; shp = shp->shl_next) {
1007 		sh = shp->shl_sh;
1008 		for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1009 			if (*p1 == '\0')
1010 				goto done;	/* exact match */
1011 
1012 		/*
1013 		 * Now compare the pathnames for three cases:
1014 		 *
1015 		 * Parent: /export/foo		(no trailing slash on parent)
1016 		 * Child:  /export/foo/bar
1017 		 *
1018 		 * Parent: /export/foo/		(trailing slash on parent)
1019 		 * Child:  /export/foo/bar
1020 		 *
1021 		 * Parent: /export/foo/		(no trailing slash on child)
1022 		 * Child:  /export/foo
1023 		 *
1024 		 * Then compare the dev_t of the parent and child to
1025 		 * make sure that they're both in the same filesystem.
1026 		 */
1027 		if ((*p1 == '\0' && *p2 == '/') ||
1028 		    (*p1 == '\0' && *(p1-1) == '/') ||
1029 		    (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1030 			if (stat(sh->sh_path, &st1) < 0) {
1031 				if (verbose)
1032 					syslog(LOG_NOTICE, "%s: %m", p1);
1033 				shp = NULL;
1034 				goto done;
1035 			}
1036 			/*
1037 			 * Use stat64 on "path" since it might be larger
1038 			 * than 2 Gb and 32 bit stat would fail EOVERFLOW
1039 			 */
1040 			if (stat64(path, &st2) < 0) {
1041 				if (verbose)
1042 					syslog(LOG_NOTICE, "%s: %m", p2);
1043 				shp = NULL;
1044 				goto done;
1045 			}
1046 			if (st1.st_dev == st2.st_dev)
1047 				goto done;
1048 		}
1049 	}
1050 done:
1051 	sh = shp ? sharedup(sh) : NULL;
1052 
1053 	(void) rw_unlock(&sharetab_lock);
1054 
1055 	return (sh);
1056 }
1057 
1058 
1059 static int
1060 is_substring(char **mntp, char **path)
1061 {
1062 	char *p1 = *mntp, *p2 = *path;
1063 
1064 	if (*p1 == '\0' && *p2 == '\0') /* exact match */
1065 		return (1);
1066 	else if (*p1 == '\0' && *p2 == '/')
1067 		return (1);
1068 	else if (*p1 == '\0' && *(p1-1) == '/') {
1069 		*path = --p2; /* we need the slash in p2 */
1070 		return (1);
1071 	} else if (*p2 == '\0') {
1072 		while (*p1 == '/')
1073 			p1++;
1074 		if (*p1 == '\0') /* exact match */
1075 			return (1);
1076 	}
1077 	return (0);
1078 }
1079 
1080 /*
1081  * find_lofsentry() searches for the real path which this requested LOFS path
1082  * (rpath) shadows. If found, it will return the sharetab entry of
1083  * the real path that corresponds to the LOFS path.
1084  * We first search mnttab to see if the requested path is an automounted
1085  * path. If it is an automounted path, it will trigger the mount by stat()ing
1086  * the requested path. Note that it is important to check that this path is
1087  * actually an automounted path, otherwise we would stat() a path which may
1088  * turn out to be NFS and block indefinitely on a dead server. The automounter
1089  * times-out if the server is dead, so there's no risk of hanging this
1090  * thread waiting for stat().
1091  * After the mount has been triggered (if necessary), we look for a
1092  * mountpoint of type LOFS (by searching /etc/mnttab again) which
1093  * is a substring of the rpath. If found, we construct a new path by
1094  * concatenating the mnt_special and the remaining of rpath, call findentry()
1095  * to make sure the 'real path' is shared.
1096  */
1097 static struct share *
1098 find_lofsentry(char *rpath, int *done_flag)
1099 {
1100 	struct stat r_stbuf;
1101 	mntlist_t *ml, *mntl, *mntpnt = NULL;
1102 	struct share *retcode = NULL;
1103 	char tmp_path[MAXPATHLEN];
1104 	int mntpnt_len = 0, tmp;
1105 	char *p1, *p2;
1106 
1107 	if ((*done_flag)++)
1108 		return (retcode);
1109 
1110 	/*
1111 	 * While fsgetmntlist() uses lockf() to
1112 	 * lock the mnttab before reading it in,
1113 	 * the lock ignores threads in the same process.
1114 	 * Read in the mnttab with the protection of a mutex.
1115 	 */
1116 	(void) mutex_lock(&mnttab_lock);
1117 	mntl = fsgetmntlist();
1118 	(void) mutex_unlock(&mnttab_lock);
1119 
1120 	/*
1121 	 * Obtain the mountpoint for the requested path.
1122 	 */
1123 	for (ml = mntl; ml; ml = ml->mntl_next) {
1124 		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1125 			*p1 == *p2 && *p1; p1++, p2++);
1126 		if (is_substring(&p1, &p2) &&
1127 		    (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1128 			mntpnt = ml;
1129 			mntpnt_len = tmp;
1130 		}
1131 	}
1132 
1133 	/*
1134 	 * If the path needs to be autoFS mounted, trigger the mount by
1135 	 * stat()ing it. This is determined by checking whether the
1136 	 * mountpoint we just found is of type autofs.
1137 	 */
1138 	if (mntpnt != NULL &&
1139 	    strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1140 		/*
1141 		 * The requested path is a substring of an autoFS filesystem.
1142 		 * Trigger the mount.
1143 		 */
1144 		if (stat(rpath, &r_stbuf) < 0) {
1145 			if (verbose)
1146 				syslog(LOG_NOTICE, "%s: %m", rpath);
1147 			goto done;
1148 		}
1149 		if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1150 			/*
1151 			 * The requested path is a directory, stat(2) it
1152 			 * again with a trailing '.' to force the autoFS
1153 			 * module to trigger the mount of indirect
1154 			 * automount entries, such as /net/jurassic/.
1155 			 */
1156 			if (strlen(rpath) + 2 > MAXPATHLEN) {
1157 				if (verbose) {
1158 					syslog(LOG_NOTICE,
1159 						"%s/.: exceeds MAXPATHLEN %d",
1160 						rpath, MAXPATHLEN);
1161 				}
1162 				goto done;
1163 			}
1164 			(void) strcpy(tmp_path, rpath);
1165 			(void) strcat(tmp_path, "/.");
1166 
1167 			if (stat(tmp_path, &r_stbuf) < 0) {
1168 				if (verbose)
1169 					syslog(LOG_NOTICE, "%s: %m", tmp_path);
1170 				goto done;
1171 			}
1172 		}
1173 		/*
1174 		 * The mount has been triggered, re-read mnttab to pick up
1175 		 * the changes made by autoFS.
1176 		 */
1177 		fsfreemntlist(mntl);
1178 		(void) mutex_lock(&mnttab_lock);
1179 		mntl = fsgetmntlist();
1180 		(void) mutex_unlock(&mnttab_lock);
1181 	}
1182 
1183 	/*
1184 	 * The autoFS mountpoint has been triggered if necessary,
1185 	 * now search mnttab again to determine if the requested path
1186 	 * is an LOFS mount of a shared path.
1187 	 */
1188 	mntpnt_len = 0;
1189 	for (ml = mntl; ml; ml = ml->mntl_next) {
1190 		if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1191 			continue;
1192 
1193 		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1194 				*p1 == *p2 && *p1; p1++, p2++);
1195 
1196 		if (is_substring(&p1, &p2) &&
1197 		    ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1198 			mntpnt_len = tmp;
1199 
1200 			if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1201 			    MAXPATHLEN) {
1202 				if (verbose) {
1203 					syslog(LOG_NOTICE, "%s%s: exceeds %d",
1204 						ml->mntl_mnt->mnt_special, p2,
1205 						MAXPATHLEN);
1206 				}
1207 				if (retcode)
1208 					sharefree(retcode);
1209 				retcode = NULL;
1210 				goto done;
1211 			}
1212 
1213 			(void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1214 			(void) strcat(tmp_path, p2);
1215 			if (retcode)
1216 				sharefree(retcode);
1217 			retcode = findentry(tmp_path);
1218 		}
1219 	}
1220 
1221 	if (retcode) {
1222 		assert(strlen(tmp_path) > 0);
1223 		(void) strcpy(rpath, tmp_path);
1224 	}
1225 
1226 done:
1227 	fsfreemntlist(mntl);
1228 	return (retcode);
1229 }
1230 
1231 /*
1232  * Determine whether an access list grants rights to a particular host.
1233  * We match on aliases of the hostname as well as on the canonical name.
1234  * Names in the access list may be either hosts or netgroups;  they're
1235  * not distinguished syntactically.  We check for hosts first because
1236  * it's cheaper (just M*N strcmp()s), then try netgroups.
1237  */
1238 int
1239 in_access_list(struct netbuf *nb, struct nd_hostservlist *clnames,
1240     char *access_list)	/* N.B. we clobber this "input" parameter */
1241 {
1242 	int nentries;
1243 	char *gr;
1244 	char *lasts;
1245 	char *host;
1246 	int off;
1247 	int i;
1248 	int netgroup_match;
1249 	int response;
1250 
1251 	/*
1252 	 * If no access list - then it's unrestricted
1253 	 */
1254 	if (access_list == NULL || *access_list == '\0')
1255 		return (1);
1256 
1257 	nentries = 0;
1258 
1259 	for (gr = strtok_r(access_list, ":", &lasts);
1260 		gr != NULL; gr = strtok_r(NULL, ":", &lasts)) {
1261 
1262 		/*
1263 		 * If the list name has a '-' prepended
1264 		 * then a match of the following name
1265 		 * implies failure instead of success.
1266 		 */
1267 		if (*gr == '-') {
1268 			response = 0;
1269 			gr++;
1270 		} else
1271 			response = 1;
1272 
1273 		/*
1274 		 * The following loops through all the
1275 		 * client's aliases.  Usually it's just one name.
1276 		 */
1277 		for (i = 0; i < clnames->h_cnt; i++) {
1278 			host = clnames->h_hostservs[i].h_host;
1279 
1280 			/*
1281 			 * If the list name begins with a dot then
1282 			 * do a domain name suffix comparison.
1283 			 * A single dot matches any name with no
1284 			 * suffix.
1285 			 */
1286 			if (*gr == '.') {
1287 				if (*(gr + 1) == '\0') {  /* single dot */
1288 					if (strchr(host, '.') == NULL)
1289 						return (response);
1290 				} else {
1291 					off = strlen(host) - strlen(gr);
1292 					if (off > 0 &&
1293 					    strcasecmp(host + off, gr) == 0) {
1294 						return (response);
1295 					}
1296 				}
1297 			} else
1298 
1299 			/*
1300 			 * If the list name begins with an at
1301 			 * sign then do a network comparison.
1302 			 */
1303 			if (*gr == '@') {
1304 				if (netmatch(nb, gr + 1))
1305 					return (response);
1306 			} else
1307 
1308 			/*
1309 			 * Just do a hostname match
1310 			 */
1311 			if (strcasecmp(gr, host) == 0) {
1312 				return (response);	/* Matched a hostname */
1313 			}
1314 		}
1315 
1316 		nentries++;
1317 	}
1318 
1319 	netgroup_match = netgroup_check(clnames, access_list, nentries);
1320 
1321 	return (netgroup_match);
1322 }
1323 
1324 
1325 int
1326 netmatch(struct netbuf *nb, char *name)
1327 {
1328 	uint_t claddr;
1329 	struct netent n, *np;
1330 	char *mp, *p;
1331 	uint_t addr, mask;
1332 	int i, bits;
1333 	char buff[256];
1334 
1335 	/*
1336 	 * Check if it's an IPv4 addr
1337 	 */
1338 	if (nb->len != sizeof (struct sockaddr_in))
1339 		return (0);
1340 
1341 	(void) memcpy(&claddr,
1342 		/* LINTED pointer alignment */
1343 		&((struct sockaddr_in *)nb->buf)->sin_addr.s_addr,
1344 		sizeof (struct in_addr));
1345 	claddr = ntohl(claddr);
1346 
1347 	mp = strchr(name, '/');
1348 	if (mp)
1349 		*mp++ = '\0';
1350 
1351 	if (isdigit(*name)) {
1352 		/*
1353 		 * Convert a dotted IP address
1354 		 * to an IP address. The conversion
1355 		 * is not the same as that in inet_addr().
1356 		 */
1357 		p = name;
1358 		addr = 0;
1359 		for (i = 0; i < 4; i++) {
1360 			addr |= atoi(p) << ((3-i) * 8);
1361 			p = strchr(p, '.');
1362 			if (p == NULL)
1363 				break;
1364 			p++;
1365 		}
1366 	} else {
1367 		/*
1368 		 * Turn the netname into
1369 		 * an IP address.
1370 		 */
1371 		np = getnetbyname_r(name, &n, buff, sizeof (buff));
1372 		if (np == NULL) {
1373 			syslog(LOG_DEBUG, "getnetbyname_r: %s: %m", name);
1374 			return (0);
1375 		}
1376 		addr = np->n_net;
1377 	}
1378 
1379 	/*
1380 	 * If the mask is specified explicitly then
1381 	 * use that value, e.g.
1382 	 *
1383 	 *    @109.104.56/28
1384 	 *
1385 	 * otherwise assume a mask from the zero octets
1386 	 * in the least significant bits of the address, e.g.
1387 	 *
1388 	 *   @109.104  or  @109.104.0.0
1389 	 */
1390 	if (mp) {
1391 		bits = atoi(mp);
1392 		mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits)
1393 			: 0;
1394 		addr &= mask;
1395 	} else {
1396 		if ((addr & 0x00ffffff) == 0)
1397 			mask = 0xff000000;
1398 		else if ((addr & 0x0000ffff) == 0)
1399 			mask = 0xffff0000;
1400 		else if ((addr & 0x000000ff) == 0)
1401 			mask = 0xffffff00;
1402 	}
1403 
1404 	return ((claddr & mask) == addr);
1405 }
1406 
1407 
1408 static char *optlist[] = {
1409 #define	OPT_RO		0
1410 	SHOPT_RO,
1411 #define	OPT_RW		1
1412 	SHOPT_RW,
1413 #define	OPT_ROOT	2
1414 	SHOPT_ROOT,
1415 #define	OPT_SECURE	3
1416 	SHOPT_SECURE,
1417 #define	OPT_ANON	4
1418 	SHOPT_ANON,
1419 #define	OPT_WINDOW	5
1420 	SHOPT_WINDOW,
1421 #define	OPT_NOSUID	6
1422 	SHOPT_NOSUID,
1423 #define	OPT_ACLOK	7
1424 	SHOPT_ACLOK,
1425 #define	OPT_SEC		8
1426 	SHOPT_SEC,
1427 	NULL
1428 };
1429 
1430 static int
1431 map_flavor(char *str)
1432 {
1433 	seconfig_t sec;
1434 
1435 	if (nfs_getseconfig_byname(str, &sec))
1436 		return (-1);
1437 
1438 	return (sec.sc_nfsnum);
1439 }
1440 
1441 /*
1442  * If the option string contains a "sec="
1443  * option, then use new option syntax.
1444  */
1445 static int
1446 newopts(char *opts)
1447 {
1448 	char *head, *p, *val;
1449 
1450 	if (!opts || *opts == '\0')
1451 		return (0);
1452 
1453 	head = strdup(opts);
1454 	if (head == NULL) {
1455 		syslog(LOG_ERR, "opts: no memory");
1456 		return (0);
1457 	}
1458 
1459 	p = head;
1460 	while (*p) {
1461 		if (getsubopt(&p, optlist, &val) == OPT_SEC) {
1462 			free(head);
1463 			return (1);
1464 		}
1465 	}
1466 
1467 	free(head);
1468 	return (0);
1469 }
1470 
1471 /*
1472  * Given an export and the clients hostname(s)
1473  * determine the security flavors that this
1474  * client is permitted to use.
1475  *
1476  * This routine is called only for "old" syntax, i.e.
1477  * only one security flavor is allowed.  So we need
1478  * to determine two things: the particular flavor,
1479  * and whether the client is allowed to use this
1480  * flavor, i.e. is in the access list.
1481  *
1482  * Note that if there is no access list, then the
1483  * default is that access is granted.
1484  */
1485 static int
1486 getclientsflavors_old(struct share *sh, struct netbuf *nb,
1487     struct nd_hostservlist *clnames, int *flavors)
1488 {
1489 	char *opts, *p, *val;
1490 	int ok = 0;
1491 	int defaultaccess = 1;
1492 
1493 	opts = strdup(sh->sh_opts);
1494 	if (opts == NULL) {
1495 		syslog(LOG_ERR, "getclientsflavors: no memory");
1496 		return (0);
1497 	}
1498 
1499 	flavors[0] = AUTH_SYS;
1500 	p = opts;
1501 
1502 	while (*p) {
1503 
1504 		switch (getsubopt(&p, optlist, &val)) {
1505 		case OPT_SECURE:
1506 			flavors[0] = AUTH_DES;
1507 			break;
1508 
1509 		case OPT_RO:
1510 		case OPT_RW:
1511 			defaultaccess = 0;
1512 			if (in_access_list(nb, clnames, val))
1513 				ok++;
1514 			break;
1515 		}
1516 	}
1517 
1518 	free(opts);
1519 
1520 	return (defaultaccess || ok);
1521 }
1522 
1523 /*
1524  * Given an export and the clients hostname(s)
1525  * determine the security flavors that this
1526  * client is permitted to use.
1527  *
1528  * This is somewhat more complicated than the "old"
1529  * routine because the options may contain multiple
1530  * security flavors (sec=) each with its own access
1531  * lists.  So a client could be granted access based
1532  * on a number of security flavors.  Note that the
1533  * type of access might not always be the same, the
1534  * client may get readonly access with one flavor
1535  * and readwrite with another, however the client
1536  * is not told this detail, it gets only the list
1537  * of flavors, and only if the client is using
1538  * version 3 of the mount protocol.
1539  */
1540 static int
1541 getclientsflavors_new(struct share *sh, struct netbuf *nb,
1542     struct nd_hostservlist *clnames, int *flavors)
1543 {
1544 	char *opts, *p, *val;
1545 	char *lasts;
1546 	char *f;
1547 	int access_ok, count, c;
1548 
1549 	opts = strdup(sh->sh_opts);
1550 	if (opts == NULL) {
1551 		syslog(LOG_ERR, "getclientsflavors: no memory");
1552 		return (0);
1553 	}
1554 
1555 	p = opts;
1556 	count = c = 0;
1557 	/* default access is rw */
1558 	access_ok = 1;
1559 
1560 	while (*p) {
1561 		switch (getsubopt(&p, optlist, &val)) {
1562 		case OPT_SEC:
1563 			/*
1564 			 * Before a new sec=xxx option, check if we need
1565 			 * to move the c index back to the previous count.
1566 			 */
1567 			if (!access_ok) {
1568 				c = count;
1569 			}
1570 
1571 			/* get all the sec=f1[:f2] flavors */
1572 			while ((f = strtok_r(val, ":", &lasts))
1573 					!= NULL) {
1574 				flavors[c++] = map_flavor(f);
1575 				val = NULL;
1576 			}
1577 			/* for a new sec=xxx option, default is rw access */
1578 			access_ok = 1;
1579 			break;
1580 
1581 		case OPT_RO:
1582 		case OPT_RW:
1583 			if (in_access_list(nb, clnames, val)) {
1584 				count = c;
1585 				access_ok = 1;
1586 			} else {
1587 				access_ok = 0;
1588 			}
1589 			break;
1590 		}
1591 	}
1592 
1593 	if (!access_ok) {
1594 		c = count;
1595 	}
1596 	free(opts);
1597 
1598 	return (c);
1599 }
1600 
1601 /*
1602  * This is a tricky piece of code that parses the
1603  * share options looking for a match on the auth
1604  * flavor that the client is using. If it finds
1605  * a match, then the client is given ro, rw, or
1606  * no access depending whether it is in the access
1607  * list.  There is a special case for "secure"
1608  * flavor.  Other flavors are values of the new "sec=" option.
1609  */
1610 int
1611 check_client(struct share *sh, struct netbuf *nb,
1612     struct nd_hostservlist *clnames, int flavor)
1613 {
1614 	if (newopts(sh->sh_opts))
1615 		return (check_client_new(sh, nb, clnames, flavor));
1616 	else
1617 		return (check_client_old(sh, nb, clnames, flavor));
1618 }
1619 
1620 static int
1621 check_client_old(struct share *sh, struct netbuf *nb,
1622     struct nd_hostservlist *clnames, int flavor)
1623 {
1624 	char *opts, *p, *val;
1625 	int match;	/* Set when a flavor is matched */
1626 	int perm = 0;	/* Set when "ro", "rw" or "root" is matched */
1627 	int list = 0;	/* Set when "ro", "rw" is found */
1628 	int ro_val = 0;	/* Set if ro option is 'ro=' */
1629 	int rw_val = 0;	/* Set if rw option is 'rw=' */
1630 
1631 	opts = strdup(sh->sh_opts);
1632 	if (opts == NULL) {
1633 		syslog(LOG_ERR, "check_client: no memory");
1634 		return (0);
1635 	}
1636 
1637 	p = opts;
1638 	match = AUTH_UNIX;
1639 
1640 	while (*p) {
1641 		switch (getsubopt(&p, optlist, &val)) {
1642 
1643 		case OPT_SECURE:
1644 			match = AUTH_DES;
1645 			break;
1646 
1647 		case OPT_RO:
1648 			list++;
1649 			if (val) ro_val++;
1650 			if (in_access_list(nb, clnames, val))
1651 				perm |= NFSAUTH_RO;
1652 			break;
1653 
1654 		case OPT_RW:
1655 			list++;
1656 			if (val) rw_val++;
1657 			if (in_access_list(nb, clnames, val))
1658 				perm |= NFSAUTH_RW;
1659 			break;
1660 
1661 		case OPT_ROOT:
1662 			/*
1663 			 * Check if the client is in
1664 			 * the root list. Only valid
1665 			 * for AUTH_SYS.
1666 			 */
1667 			if (flavor != AUTH_SYS)
1668 				break;
1669 
1670 			if (val == NULL || *val == '\0')
1671 				break;
1672 
1673 			if (in_access_list(nb, clnames, val))
1674 				perm |= NFSAUTH_ROOT;
1675 			break;
1676 		}
1677 	}
1678 
1679 	free(opts);
1680 
1681 	if (flavor != match)
1682 		return (NFSAUTH_DENIED);
1683 
1684 	if (list) {
1685 		/*
1686 		 * If the client doesn't match an "ro" or "rw"
1687 		 * list then set no access.
1688 		 */
1689 		if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
1690 			perm |= NFSAUTH_DENIED;
1691 	} else {
1692 		/*
1693 		 * The client matched a flavor entry that
1694 		 * has no explicit "rw" or "ro" determination.
1695 		 * Default it to "rw".
1696 		 */
1697 		perm |= NFSAUTH_RW;
1698 	}
1699 
1700 
1701 	/*
1702 	 * The client may show up in both ro= and rw=
1703 	 * lists.  If so, then turn off the RO access
1704 	 * bit leaving RW access.
1705 	 */
1706 	if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
1707 		/*
1708 		 * Logically cover all permutations of rw=,ro=.
1709 		 * In the case where, rw,ro=<host> we would like
1710 		 * to remove RW access for the host.  In all other cases
1711 		 * RW wins the precedence battle.
1712 		 */
1713 		if (!rw_val && ro_val) {
1714 			perm &= ~(NFSAUTH_RW);
1715 		} else {
1716 			perm &= ~(NFSAUTH_RO);
1717 		}
1718 	}
1719 
1720 	return (perm);
1721 }
1722 
1723 /*
1724  * Check if the client has access by using a flavor different from
1725  * the given "flavor". If "flavor" is not in the flavor list,
1726  * return TRUE to indicate that this "flavor" is a wrong sec.
1727  */
1728 static bool_t
1729 is_wrongsec(struct share *sh, struct netbuf *nb,
1730 		struct nd_hostservlist *clnames, int flavor)
1731 {
1732 	int flavor_list[MAX_FLAVORS];
1733 	int flavor_count, i;
1734 
1735 	/* get the flavor list that the client has access with */
1736 	flavor_count = getclientsflavors_new(sh, nb, clnames, flavor_list);
1737 
1738 	if (flavor_count == 0)
1739 		return (FALSE);
1740 
1741 	/*
1742 	 * Check if the given "flavor" is in the flavor_list.
1743 	 */
1744 	for (i = 0; i < flavor_count; i++) {
1745 		if (flavor == flavor_list[i])
1746 			return (FALSE);
1747 	}
1748 
1749 	/*
1750 	 * If "flavor" is not in the flavor_list, return TRUE to indicate
1751 	 * that the client should have access by using a security flavor
1752 	 * different from this "flavor".
1753 	 */
1754 	return (TRUE);
1755 }
1756 
1757 /*
1758  * Given an export and the client's hostname, we
1759  * check the security options to see whether the
1760  * client is allowed to use the given security flavor.
1761  *
1762  * The strategy is to proceed through the options looking
1763  * for a flavor match, then pay attention to the ro, rw,
1764  * and root options.
1765  *
1766  * Note that an entry may list several flavors in a
1767  * single entry, e.g.
1768  *
1769  *   sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
1770  *
1771  */
1772 
1773 static int
1774 check_client_new(struct share *sh, struct netbuf *nb,
1775     struct nd_hostservlist *clnames, int flavor)
1776 {
1777 	char *opts, *p, *val;
1778 	char *lasts;
1779 	char *f;
1780 	int match = 0;	/* Set when a flavor is matched */
1781 	int perm = 0;	/* Set when "ro", "rw" or "root" is matched */
1782 	int list = 0;	/* Set when "ro", "rw" is found */
1783 	int ro_val = 0;	/* Set if ro option is 'ro=' */
1784 	int rw_val = 0;	/* Set if rw option is 'rw=' */
1785 
1786 	opts = strdup(sh->sh_opts);
1787 	if (opts == NULL) {
1788 		syslog(LOG_ERR, "check_client: no memory");
1789 		return (0);
1790 	}
1791 
1792 	p = opts;
1793 
1794 	while (*p) {
1795 		switch (getsubopt(&p, optlist, &val)) {
1796 
1797 		case OPT_SEC:
1798 			if (match)
1799 				goto done;
1800 
1801 			while ((f = strtok_r(val, ":", &lasts))
1802 					!= NULL) {
1803 				if (flavor == map_flavor(f)) {
1804 					match = 1;
1805 					break;
1806 				}
1807 				val = NULL;
1808 			}
1809 			break;
1810 
1811 		case OPT_RO:
1812 			if (!match)
1813 				break;
1814 
1815 			list++;
1816 			if (val) ro_val++;
1817 			if (in_access_list(nb, clnames, val))
1818 				perm |= NFSAUTH_RO;
1819 			break;
1820 
1821 		case OPT_RW:
1822 			if (!match)
1823 				break;
1824 
1825 			list++;
1826 			if (val) rw_val++;
1827 			if (in_access_list(nb, clnames, val))
1828 				perm |= NFSAUTH_RW;
1829 			break;
1830 
1831 		case OPT_ROOT:
1832 			/*
1833 			 * Check if the client is in
1834 			 * the root list. Only valid
1835 			 * for AUTH_SYS.
1836 			 */
1837 			if (flavor != AUTH_SYS)
1838 				break;
1839 
1840 			if (!match)
1841 				break;
1842 
1843 			if (val == NULL || *val == '\0')
1844 				break;
1845 
1846 			if (in_access_list(nb, clnames, val))
1847 				perm |= NFSAUTH_ROOT;
1848 			break;
1849 		}
1850 	}
1851 
1852 done:
1853 	/*
1854 	 * If no match then set the perm accordingly
1855 	 */
1856 	if (!match)
1857 		return (NFSAUTH_DENIED);
1858 
1859 	if (list) {
1860 		/*
1861 		 * If the client doesn't match an "ro" or "rw" list then
1862 		 * check if it may have access by using a different flavor.
1863 		 * If so, return NFSAUTH_WRONGSEC.
1864 		 * If not, return NFSAUTH_DENIED.
1865 		 */
1866 		if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
1867 			if (is_wrongsec(sh, nb, clnames, flavor))
1868 				perm |= NFSAUTH_WRONGSEC;
1869 			else
1870 				perm |= NFSAUTH_DENIED;
1871 		}
1872 	} else {
1873 		/*
1874 		 * The client matched a flavor entry that
1875 		 * has no explicit "rw" or "ro" determination.
1876 		 * Make sure it defaults to "rw".
1877 		 */
1878 		perm |= NFSAUTH_RW;
1879 	}
1880 
1881 	/*
1882 	 * The client may show up in both ro= and rw=
1883 	 * lists.  If so, then turn off the RO access
1884 	 * bit leaving RW access.
1885 	 */
1886 	if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
1887 		/*
1888 		 * Logically cover all permutations of rw=,ro=.
1889 		 * In the case where, rw,ro=<host> we would like
1890 		 * to remove RW access for the host.  In all other cases
1891 		 * RW wins the precedence battle.
1892 		 */
1893 		if (!rw_val && ro_val) {
1894 			perm &= ~(NFSAUTH_RW);
1895 		} else {
1896 			perm &= ~(NFSAUTH_RO);
1897 		}
1898 	}
1899 
1900 	free(opts);
1901 
1902 	return (perm);
1903 }
1904 
1905 void
1906 check_sharetab()
1907 {
1908 	FILE *f;
1909 	struct stat st;
1910 	static timestruc_t last_sharetab_time;
1911 	timestruc_t prev_sharetab_time;
1912 	struct share *sh;
1913 	struct sh_list *shp, *shp_prev;
1914 	int res, c = 0;
1915 
1916 	/*
1917 	 *  read in /etc/dfs/sharetab if it has changed
1918 	 */
1919 
1920 	if (stat(SHARETAB, &st) != 0) {
1921 		syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
1922 		return;
1923 	}
1924 
1925 	if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
1926 	    st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
1927 		/*
1928 		 * No change.
1929 		 */
1930 		return;
1931 	}
1932 
1933 	/*
1934 	 * Remember the mod time, then after getting the
1935 	 * write lock check again.  If another thread
1936 	 * already did the update, then there's no
1937 	 * work to do.
1938 	 */
1939 	prev_sharetab_time = last_sharetab_time;
1940 
1941 	(void) rw_wrlock(&sharetab_lock);
1942 
1943 	if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
1944 	    prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
1945 		(void) rw_unlock(&sharetab_lock);
1946 		return;
1947 	}
1948 
1949 	f = fopen(SHARETAB, "r+");
1950 	if (f == NULL) {
1951 		syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
1952 		(void) rw_unlock(&sharetab_lock);
1953 		return;
1954 	}
1955 
1956 	/*
1957 	 * Lock the file so that unshare can't
1958 	 * truncate it while we're reading
1959 	 */
1960 	if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1961 		syslog(LOG_ERR, "Cannot lock %s: %m", SHARETAB);
1962 		(void) rw_unlock(&sharetab_lock);
1963 		(void) fclose(f);
1964 		return;
1965 	}
1966 
1967 	/*
1968 	 * Once we are sure /etc/dfs/sharetab has been
1969 	 * modified, flush netgroup cache entries.
1970 	 */
1971 	netgrp_cache_flush();
1972 
1973 	sh_free(share_list);			/* free old list */
1974 	share_list = NULL;
1975 
1976 	while ((res = getshare(f, &sh)) > 0) {
1977 		c++;
1978 		if (strcmp(sh->sh_fstype, "nfs") != 0)
1979 			continue;
1980 
1981 		shp = malloc(sizeof (*shp));
1982 		if (shp == NULL)
1983 			goto alloc_failed;
1984 		if (share_list == NULL)
1985 			share_list = shp;
1986 		else
1987 			/* LINTED not used before set */
1988 			shp_prev->shl_next = shp;
1989 		shp_prev = shp;
1990 		shp->shl_next = NULL;
1991 		shp->shl_sh = sharedup(sh);
1992 		if (shp->shl_sh == NULL)
1993 			goto alloc_failed;
1994 	}
1995 	if (res < 0)
1996 		syslog(LOG_ERR, "%s: invalid at line %d\n",
1997 			SHARETAB, c + 1);
1998 
1999 	if (stat(SHARETAB, &st) != 0) {
2000 		syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
2001 		(void) rw_unlock(&sharetab_lock);
2002 		return;
2003 	}
2004 	last_sharetab_time = st.st_mtim;
2005 	(void) fclose(f);
2006 	(void) rw_unlock(&sharetab_lock);
2007 	return;
2008 
2009 alloc_failed:
2010 	syslog(LOG_ERR, "check_sharetab: no memory");
2011 	sh_free(share_list);
2012 	share_list = NULL;
2013 	(void) fclose(f);
2014 	(void) rw_unlock(&sharetab_lock);
2015 }
2016 
2017 static void
2018 sh_free(struct sh_list *shp)
2019 {
2020 	register struct sh_list *next;
2021 
2022 	while (shp) {
2023 		sharefree(shp->shl_sh);
2024 		next = shp->shl_next;
2025 		free(shp);
2026 		shp = next;
2027 	}
2028 }
2029 
2030 
2031 /*
2032  * Remove an entry from mounted list
2033  */
2034 static void
2035 umount(struct svc_req *rqstp)
2036 {
2037 	char *host, *path, *remove_path;
2038 	char rpath[MAXPATHLEN];
2039 	struct nd_hostservlist *clnames = NULL;
2040 	SVCXPRT *transp;
2041 	struct netbuf *nb;
2042 
2043 	transp = rqstp->rq_xprt;
2044 	path = NULL;
2045 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
2046 		svcerr_decode(transp);
2047 		return;
2048 	}
2049 	errno = 0;
2050 	if (!svc_sendreply(transp, xdr_void, (char *)NULL))
2051 		log_cant_reply(transp);
2052 
2053 	getclientsnames(transp, &nb, &clnames);
2054 	if (clnames == NULL) {
2055 		/*
2056 		 * Without the hostname we can't do audit or delete
2057 		 * this host from the mount entries.
2058 		 */
2059 		svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
2060 		return;
2061 	}
2062 	host = clnames->h_hostservs[0].h_host;
2063 
2064 	if (verbose)
2065 		syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
2066 
2067 	audit_mountd_umount(host, path);
2068 
2069 	remove_path = rpath;	/* assume we will use the cannonical path */
2070 	if (realpath(path, rpath) == NULL) {
2071 		if (verbose)
2072 			syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
2073 		remove_path = path;	/* use path provided instead */
2074 	}
2075 
2076 	mntlist_delete(host, remove_path);	/* remove from mount list */
2077 
2078 	svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
2079 	netdir_free(clnames, ND_HOSTSERVLIST);
2080 }
2081 
2082 /*
2083  * Remove all entries for one machine from mounted list
2084  */
2085 static void
2086 umountall(struct svc_req *rqstp)
2087 {
2088 	struct nd_hostservlist *clnames = NULL;
2089 	SVCXPRT *transp;
2090 	char *host;
2091 	struct netbuf *nb;
2092 
2093 	transp = rqstp->rq_xprt;
2094 	if (!svc_getargs(transp, xdr_void, NULL)) {
2095 		svcerr_decode(transp);
2096 		return;
2097 	}
2098 	/*
2099 	 * We assume that this call is asynchronous and made via rpcbind
2100 	 * callit routine.  Therefore return control immediately. The error
2101 	 * causes rpcbind to remain silent, as opposed to every machine
2102 	 * on the net blasting the requester with a response.
2103 	 */
2104 	svcerr_systemerr(transp);
2105 	getclientsnames(transp, &nb, &clnames);
2106 	if (clnames == NULL) {
2107 		/* Can't do anything without the name of the client */
2108 		return;
2109 	}
2110 
2111 	host = clnames->h_hostservs[0].h_host;
2112 
2113 	/*
2114 	 * Remove all hosts entries from mount list
2115 	 */
2116 	mntlist_delete_all(host);
2117 
2118 	if (verbose)
2119 		syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
2120 
2121 	netdir_free(clnames, ND_HOSTSERVLIST);
2122 }
2123 
2124 void *
2125 exmalloc(size_t size)
2126 {
2127 	void *ret;
2128 
2129 	if ((ret = malloc(size)) == NULL) {
2130 		syslog(LOG_ERR, "Out of memory");
2131 		exit(1);
2132 	}
2133 	return (ret);
2134 }
2135 
2136 static void
2137 sigexit(int signum)
2138 {
2139 
2140 	if (signum == SIGHUP)
2141 		_exit(0);
2142 	_exit(1);
2143 }
2144