1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Herb Hasler and Rick Macklem at The University of Guelph.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1989, 1993\n\
38 The Regents of the University of California. All rights reserved.\n";
39 #endif /*not lint*/
40
41 #if 0
42 #ifndef lint
43 static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
44 #endif /*not lint*/
45 #endif
46
47 #include <sys/cdefs.h>
48 #include <sys/param.h>
49 #include <sys/conf.h>
50 #include <sys/fcntl.h>
51 #include <sys/fnv_hash.h>
52 #include <sys/linker.h>
53 #include <sys/module.h>
54 #include <sys/mount.h>
55 #include <sys/queue.h>
56 #include <sys/stat.h>
57 #include <sys/sysctl.h>
58 #include <sys/syslog.h>
59
60 #include <rpc/rpc.h>
61 #include <rpc/rpc_com.h>
62 #include <rpc/pmap_clnt.h>
63 #include <rpc/pmap_prot.h>
64 #include <rpcsvc/mount.h>
65 #include <nfs/nfsproto.h>
66 #include <nfs/nfssvc.h>
67 #include <nfsserver/nfs.h>
68
69 #include <fs/nfs/nfsport.h>
70
71 #include <arpa/inet.h>
72
73 #include <assert.h>
74 #include <ctype.h>
75 #include <err.h>
76 #include <errno.h>
77 #include <grp.h>
78 #include <libutil.h>
79 #include <limits.h>
80 #include <netdb.h>
81 #include <pwd.h>
82 #include <signal.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <unistd.h>
87 #include <vis.h>
88 #include "pathnames.h"
89 #include "mntopts.h"
90
91 #ifdef DEBUG
92 #include <stdarg.h>
93 #endif
94
95 /*
96 * Structures for keeping the mount list and export list
97 */
98 struct mountlist {
99 char ml_host[MNTNAMLEN+1];
100 char ml_dirp[MNTPATHLEN+1];
101
102 SLIST_ENTRY(mountlist) next;
103 };
104
105 struct dirlist {
106 struct dirlist *dp_left;
107 struct dirlist *dp_right;
108 int dp_flag;
109 struct hostlist *dp_hosts; /* List of hosts this dir exported to */
110 char *dp_dirp;
111 };
112 /* dp_flag bits */
113 #define DP_DEFSET 0x1
114 #define DP_HOSTSET 0x2
115
116 /*
117 * maproot/mapall credentials.
118 * cr_smallgrps can be used for a group list up to SMALLNGROUPS in size.
119 * Larger group lists are malloc'd/free'd.
120 */
121 #define SMALLNGROUPS 32
122 struct expcred {
123 uid_t cr_uid;
124 int cr_ngroups;
125 gid_t cr_smallgrps[SMALLNGROUPS];
126 gid_t *cr_groups;
127 };
128
129 struct exportlist {
130 struct dirlist *ex_dirl;
131 struct dirlist *ex_defdir;
132 struct grouplist *ex_grphead;
133 int ex_flag;
134 fsid_t ex_fs;
135 char *ex_fsdir;
136 char *ex_indexfile;
137 struct expcred ex_defanon;
138 uint64_t ex_defexflags;
139 int ex_numsecflavors;
140 int ex_secflavors[MAXSECFLAVORS];
141 int ex_defnumsecflavors;
142 int ex_defsecflavors[MAXSECFLAVORS];
143
144 SLIST_ENTRY(exportlist) entries;
145 };
146 /* ex_flag bits */
147 #define EX_LINKED 0x01
148 #define EX_DONE 0x02
149 #define EX_DEFSET 0x04
150 #define EX_PUBLICFH 0x08
151 #define EX_ADMINWARN 0x10
152
153 SLIST_HEAD(exportlisthead, exportlist);
154
155 struct netmsk {
156 struct sockaddr_storage nt_net;
157 struct sockaddr_storage nt_mask;
158 char *nt_name;
159 };
160
161 union grouptypes {
162 struct addrinfo *gt_addrinfo;
163 struct netmsk gt_net;
164 };
165
166 struct grouplist {
167 int gr_type;
168 union grouptypes gr_ptr;
169 struct grouplist *gr_next;
170 struct expcred gr_anon;
171 uint64_t gr_exflags;
172 int gr_flag;
173 int gr_numsecflavors;
174 int gr_secflavors[MAXSECFLAVORS];
175 };
176 /* Group types */
177 #define GT_NULL 0x0
178 #define GT_HOST 0x1
179 #define GT_NET 0x2
180 #define GT_DEFAULT 0x3
181 #define GT_IGNORE 0x5
182
183 /* Group flags */
184 #define GR_FND 0x1
185
186 struct hostlist {
187 int ht_flag; /* Uses DP_xx bits */
188 struct grouplist *ht_grp;
189 struct hostlist *ht_next;
190 };
191
192 struct fhreturn {
193 int fhr_flag;
194 int fhr_vers;
195 nfsfh_t fhr_fh;
196 int fhr_numsecflavors;
197 int *fhr_secflavors;
198 };
199
200 #define GETPORT_MAXTRY 20 /* Max tries to get a port # */
201
202 /*
203 * How long to delay a reload of exports when there are RPC request(s)
204 * to process, in usec. Must be less than 1second.
205 */
206 #define RELOADDELAY 250000
207
208 /* Global defs */
209 static char *add_expdir(struct dirlist **, char *, int);
210 static void add_dlist(struct dirlist **, struct dirlist *,
211 struct grouplist *, int, struct exportlist *,
212 struct expcred *, uint64_t);
213 static void add_mlist(char *, char *);
214 static int check_path_component(const char *, char **);
215 static int check_dirpath(char *, char **);
216 static int check_statfs(const char *, struct statfs *, char **);
217 static int check_options(struct dirlist *);
218 static int checkmask(struct sockaddr *sa);
219 static int chk_host(struct dirlist *, struct sockaddr *, int *, int *,
220 int *, int **);
221 static char *strsep_quote(char **stringp, const char *delim);
222 static int create_service(struct netconfig *nconf);
223 static void complete_service(struct netconfig *nconf, char *port_str);
224 static void clearout_service(void);
225 static void del_mlist(char *hostp, char *dirp);
226 static struct dirlist *dirp_search(struct dirlist *, char *);
227 static int do_export_mount(struct exportlist *, struct statfs *);
228 static int do_mount(struct exportlist *, struct grouplist *, uint64_t,
229 struct expcred *, char *, int, struct statfs *, int, int *);
230 static int do_opt(char **, char **, struct exportlist *,
231 struct grouplist *, int *, uint64_t *, struct expcred *,
232 char *);
233 static struct exportlist *ex_search(fsid_t *, struct exportlisthead *);
234 static struct exportlist *get_exp(void);
235 static void free_dir(struct dirlist *);
236 static void free_exp(struct exportlist *);
237 static void free_grp(struct grouplist *);
238 static void free_host(struct hostlist *);
239 static void free_v4rootexp(void);
240 static void get_exportlist_one(int);
241 static void get_exportlist(int);
242 static void insert_exports(struct exportlist *, struct exportlisthead *);
243 static void free_exports(struct exportlisthead *);
244 static void read_exportfile(int);
245 static int compare_nmount_exportlist(struct iovec *, int, char *);
246 static int compare_export(struct exportlist *, struct exportlist *);
247 static int compare_addr(struct grouplist *, struct grouplist *);
248 static int compare_cred(struct expcred *, struct expcred *);
249 static int compare_secflavor(int *, int *, int);
250 static void delete_export(struct iovec *, int, struct statfs *, char *);
251 static int get_host(char *, struct grouplist *, struct grouplist *);
252 static struct hostlist *get_ht(void);
253 static int get_line(void);
254 static void get_mountlist(void);
255 static int get_net(char *, struct netmsk *, int);
256 static void getexp_err(struct exportlist *, struct grouplist *, const char *);
257 static struct grouplist *get_grp(void);
258 static void hang_dirp(struct dirlist *, struct grouplist *,
259 struct exportlist *, int, struct expcred *, uint64_t);
260 static void huphandler(int sig);
261 static int makemask(struct sockaddr_storage *ssp, int bitlen);
262 static void mntsrv(struct svc_req *, SVCXPRT *);
263 static void nextfield(char **, char **);
264 static void out_of_mem(void) __dead2;
265 static void parsecred(char *, struct expcred *);
266 static int parsesec(char *, struct exportlist *);
267 static int put_exlist(struct dirlist *, XDR *, struct dirlist *,
268 int *, int);
269 static void *sa_rawaddr(struct sockaddr *sa, int *nbytes);
270 static int sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
271 struct sockaddr *samask);
272 static int scan_tree(struct dirlist *, struct sockaddr *);
273 static void usage(void);
274 static int xdr_dir(XDR *, char *);
275 static int xdr_explist(XDR *, caddr_t);
276 static int xdr_explist_brief(XDR *, caddr_t);
277 static int xdr_explist_common(XDR *, caddr_t, int);
278 static int xdr_fhs(XDR *, caddr_t);
279 static int xdr_mlist(XDR *, caddr_t);
280 static void terminate(int);
281 static void cp_cred(struct expcred *, struct expcred *);
282
283 static gid_t nogroup();
284
285 #define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
286 static struct exportlisthead *exphead = NULL;
287 static struct exportlisthead *oldexphead = NULL;
288 static int exphashsize = 0;
289 static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
290 static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
291 static char **exnames;
292 static char **hosts = NULL;
293 static int force_v2 = 0;
294 static int warn_admin = 1;
295 static int resvport_only = 1;
296 static int nhosts = 0;
297 static int dir_only = 1;
298 static int dolog = 0;
299 static _Atomic(int) got_sighup = 0;
300 static int xcreated = 0;
301
302 static char *svcport_str = NULL;
303 static int mallocd_svcport = 0;
304 static int *sock_fd;
305 static int sock_fdcnt;
306 static int sock_fdpos;
307 static int suspend_nfsd = 0;
308 static int alldirs_fail = 0;
309
310 static int opt_flags;
311 static int have_v6 = 1;
312
313 static int v4root_phase = 0;
314 static char v4root_dirpath[PATH_MAX + 1];
315 static struct exportlist *v4root_ep = NULL;
316 static int has_publicfh = 0;
317 static int has_set_publicfh = 0;
318
319 static struct pidfh *pfh = NULL;
320
321 /* Temporary storage for credentials' groups. */
322 static long tngroups_max;
323 static gid_t *tmp_groups = NULL;
324
325 /* Bits for opt_flags above */
326 #define OP_MAPROOT 0x01
327 #define OP_MAPALL 0x02
328 /* 0x4 free */
329 #define OP_MASK 0x08
330 #define OP_NET 0x10
331 #define OP_ALLDIRS 0x40
332 #define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */
333 #define OP_QUIET 0x100
334 #define OP_MASKLEN 0x200
335 #define OP_SEC 0x400
336 #define OP_CLASSMASK 0x800 /* mask not specified, is Class A/B/C default */
337 #define OP_NOTROOT 0x1000 /* Mark the the mount path is not a fs root */
338
339 #ifdef DEBUG
340 static int debug = 1;
341 static void SYSLOG(int, const char *, ...) __printflike(2, 3);
342 #define syslog SYSLOG
343 #else
344 static int debug = 0;
345 #endif
346
347 /*
348 * The LOGDEBUG() syslog() calls are always compiled into the daemon.
349 * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty.
350 * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG.
351 */
352 static int logdebug = 0;
353 #define LOGDEBUG(format, ...) \
354 (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0)
355
356 /*
357 * Similar to strsep(), but it allows for quoted strings
358 * and escaped characters.
359 *
360 * It returns the string (or NULL, if *stringp is NULL),
361 * which is a de-quoted version of the string if necessary.
362 *
363 * It modifies *stringp in place.
364 */
365 static char *
strsep_quote(char ** stringp,const char * delim)366 strsep_quote(char **stringp, const char *delim)
367 {
368 char *srcptr, *dstptr, *retval;
369 char quot = 0;
370
371 if (stringp == NULL || *stringp == NULL)
372 return (NULL);
373
374 srcptr = dstptr = retval = *stringp;
375
376 while (*srcptr) {
377 /*
378 * We're looking for several edge cases here.
379 * First: if we're in quote state (quot != 0),
380 * then we ignore the delim characters, but otherwise
381 * process as normal, unless it is the quote character.
382 * Second: if the current character is a backslash,
383 * we take the next character as-is, without checking
384 * for delim, quote, or backslash. Exception: if the
385 * next character is a NUL, that's the end of the string.
386 * Third: if the character is a quote character, we toggle
387 * quote state.
388 * Otherwise: check the current character for NUL, or
389 * being in delim, and end the string if either is true.
390 */
391 if (*srcptr == '\\') {
392 srcptr++;
393 /*
394 * The edge case here is if the next character
395 * is NUL, we want to stop processing. But if
396 * it's not NUL, then we simply want to copy it.
397 */
398 if (*srcptr) {
399 *dstptr++ = *srcptr++;
400 }
401 continue;
402 }
403 if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
404 quot = *srcptr++;
405 continue;
406 }
407 if (quot && *srcptr == quot) {
408 /* End of the quoted part */
409 quot = 0;
410 srcptr++;
411 continue;
412 }
413 if (!quot && strchr(delim, *srcptr))
414 break;
415 *dstptr++ = *srcptr++;
416 }
417
418 *stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
419 *dstptr = 0; /* Terminate the string */
420 return (retval);
421 }
422
423 /*
424 * Mountd server for NFS mount protocol as described in:
425 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
426 * The optional arguments are the exports file name
427 * default: _PATH_EXPORTS
428 * and "-n" to allow nonroot mount.
429 */
430 int
main(int argc,char ** argv)431 main(int argc, char **argv)
432 {
433 fd_set readfds;
434 struct netconfig *nconf;
435 char *endptr, **hosts_bak;
436 void *nc_handle;
437 pid_t otherpid;
438 in_port_t svcport;
439 int c, k, s;
440 int maxrec = RPC_MAXDATASIZE;
441 int attempt_cnt, port_len, port_pos, ret;
442 char **port_list;
443 uint64_t curtime, nexttime;
444 struct timeval tv;
445 struct timespec tp;
446 sigset_t sig_mask, sighup_mask;
447 int enable_rpcbind;
448
449 enable_rpcbind = 1;
450 /* Check that another mountd isn't already running. */
451 pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
452 if (pfh == NULL) {
453 if (errno == EEXIST)
454 errx(1, "mountd already running, pid: %d.", otherpid);
455 warn("cannot open or create pidfile");
456 }
457
458 openlog("mountd", LOG_PID, LOG_DAEMON);
459
460 /* How many groups do we support? */
461 tngroups_max = sysconf(_SC_NGROUPS_MAX);
462 if (tngroups_max == -1)
463 tngroups_max = NGROUPS_MAX;
464 /* Add space for the effective GID. */
465 ++tngroups_max;
466 tmp_groups = malloc(tngroups_max);
467 if (tmp_groups == NULL)
468 out_of_mem();
469
470 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
471 if (s < 0)
472 have_v6 = 0;
473 else
474 close(s);
475
476 while ((c = getopt(argc, argv, "2Aadeh:lnp:RrS")) != -1)
477 switch (c) {
478 case '2':
479 force_v2 = 1;
480 break;
481 case 'A':
482 warn_admin = 0;
483 break;
484 case 'a':
485 alldirs_fail = 1;
486 break;
487 case 'e':
488 /* now a no-op, since this is the default */
489 break;
490 case 'n':
491 resvport_only = 0;
492 break;
493 case 'R':
494 /* Do not support Mount protocol */
495 enable_rpcbind = 0;
496 break;
497 case 'r':
498 dir_only = 0;
499 break;
500 case 'd':
501 debug = debug ? 0 : 1;
502 break;
503 case 'l':
504 dolog = 1;
505 break;
506 case 'p':
507 endptr = NULL;
508 svcport = (in_port_t)strtoul(optarg, &endptr, 10);
509 if (endptr == NULL || *endptr != '\0' ||
510 svcport == 0 || svcport >= IPPORT_MAX)
511 usage();
512 svcport_str = strdup(optarg);
513 break;
514 case 'h':
515 ++nhosts;
516 hosts_bak = hosts;
517 hosts_bak = realloc(hosts, nhosts * sizeof(char *));
518 if (hosts_bak == NULL) {
519 if (hosts != NULL) {
520 for (k = 0; k < nhosts; k++)
521 free(hosts[k]);
522 free(hosts);
523 out_of_mem();
524 }
525 }
526 hosts = hosts_bak;
527 hosts[nhosts - 1] = strdup(optarg);
528 if (hosts[nhosts - 1] == NULL) {
529 for (k = 0; k < (nhosts - 1); k++)
530 free(hosts[k]);
531 free(hosts);
532 out_of_mem();
533 }
534 break;
535 case 'S':
536 suspend_nfsd = 1;
537 break;
538 default:
539 usage();
540 }
541 if (enable_rpcbind == 0) {
542 if (svcport_str != NULL) {
543 warnx("-p option not compatible with -R, ignored");
544 free(svcport_str);
545 svcport_str = NULL;
546 }
547 if (nhosts > 0) {
548 warnx("-h option not compatible with -R, ignored");
549 for (k = 0; k < nhosts; k++)
550 free(hosts[k]);
551 free(hosts);
552 hosts = NULL;
553 nhosts = 0;
554 }
555 }
556
557 if (modfind("nfsd") < 0) {
558 /* Not present in kernel, try loading it */
559 if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
560 errx(1, "NFS server is not available");
561 }
562
563 argc -= optind;
564 argv += optind;
565 if (argc > 0)
566 exnames = argv;
567 else
568 exnames = exnames_default;
569 if (debug)
570 warnx("getting export list");
571 get_exportlist(0);
572 if (debug)
573 warnx("getting mount list");
574 get_mountlist();
575 if (debug)
576 warnx("here we go");
577 if (debug == 0) {
578 daemon(0, 0);
579 signal(SIGINT, SIG_IGN);
580 signal(SIGQUIT, SIG_IGN);
581 }
582 signal(SIGHUP, huphandler);
583 signal(SIGTERM, terminate);
584 signal(SIGPIPE, SIG_IGN);
585
586 pidfile_write(pfh);
587
588 if (enable_rpcbind != 0) {
589 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
590 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
591 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
592
593 if (!resvport_only) {
594 if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
595 &resvport_only, sizeof(resvport_only)) != 0 &&
596 errno != ENOENT) {
597 syslog(LOG_ERR, "sysctl: %m");
598 exit(1);
599 }
600 }
601
602 /*
603 * If no hosts were specified, add a wildcard entry to bind to
604 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added
605 * to the list.
606 */
607 if (nhosts == 0) {
608 hosts = malloc(sizeof(char *));
609 if (hosts == NULL)
610 out_of_mem();
611 hosts[0] = "*";
612 nhosts = 1;
613 } else {
614 hosts_bak = hosts;
615 if (have_v6) {
616 hosts_bak = realloc(hosts, (nhosts + 2) *
617 sizeof(char *));
618 if (hosts_bak == NULL) {
619 for (k = 0; k < nhosts; k++)
620 free(hosts[k]);
621 free(hosts);
622 out_of_mem();
623 } else
624 hosts = hosts_bak;
625 nhosts += 2;
626 hosts[nhosts - 2] = "::1";
627 } else {
628 hosts_bak = realloc(hosts, (nhosts + 1) *
629 sizeof(char *));
630 if (hosts_bak == NULL) {
631 for (k = 0; k < nhosts; k++)
632 free(hosts[k]);
633 free(hosts);
634 out_of_mem();
635 } else {
636 nhosts += 1;
637 hosts = hosts_bak;
638 }
639 }
640
641 hosts[nhosts - 1] = "127.0.0.1";
642 }
643 }
644
645 attempt_cnt = 1;
646 sock_fdcnt = 0;
647 sock_fd = NULL;
648 port_list = NULL;
649 port_len = 0;
650 if (enable_rpcbind != 0) {
651 nc_handle = setnetconfig();
652 while ((nconf = getnetconfig(nc_handle))) {
653 if (nconf->nc_flag & NC_VISIBLE) {
654 if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
655 "inet6") == 0) {
656 /* DO NOTHING */
657 } else {
658 ret = create_service(nconf);
659 if (ret == 1)
660 /* Ignore this call */
661 continue;
662 if (ret < 0) {
663 /*
664 * Failed to bind port, so close
665 * off all sockets created and
666 * try again if the port# was
667 * dynamically assigned via
668 * bind(2).
669 */
670 clearout_service();
671 if (mallocd_svcport != 0 &&
672 attempt_cnt <
673 GETPORT_MAXTRY) {
674 free(svcport_str);
675 svcport_str = NULL;
676 mallocd_svcport = 0;
677 } else {
678 errno = EADDRINUSE;
679 syslog(LOG_ERR,
680 "bindresvport_sa:"
681 " %m");
682 exit(1);
683 }
684
685 /*
686 * Start over at the first
687 * service.
688 */
689 free(sock_fd);
690 sock_fdcnt = 0;
691 sock_fd = NULL;
692 nc_handle = setnetconfig();
693 attempt_cnt++;
694 } else if (mallocd_svcport != 0 &&
695 attempt_cnt == GETPORT_MAXTRY) {
696 /*
697 * For the last attempt, allow
698 * different port #s for each
699 * nconf by saving the
700 * svcport_str setting it back
701 * to NULL.
702 */
703 port_list = realloc(port_list,
704 (port_len + 1) *
705 sizeof(char *));
706 if (port_list == NULL)
707 out_of_mem();
708 port_list[port_len++] =
709 svcport_str;
710 svcport_str = NULL;
711 mallocd_svcport = 0;
712 }
713 }
714 }
715 }
716
717 /*
718 * Successfully bound the ports, so call complete_service() to
719 * do the rest of the setup on the service(s).
720 */
721 sock_fdpos = 0;
722 port_pos = 0;
723 nc_handle = setnetconfig();
724 while ((nconf = getnetconfig(nc_handle))) {
725 if (nconf->nc_flag & NC_VISIBLE) {
726 if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
727 "inet6") == 0) {
728 /* DO NOTHING */
729 } else if (port_list != NULL) {
730 if (port_pos >= port_len) {
731 syslog(LOG_ERR, "too many"
732 " port#s");
733 exit(1);
734 }
735 complete_service(nconf,
736 port_list[port_pos++]);
737 } else
738 complete_service(nconf, svcport_str);
739 }
740 }
741 endnetconfig(nc_handle);
742 free(sock_fd);
743 if (port_list != NULL) {
744 for (port_pos = 0; port_pos < port_len; port_pos++)
745 free(port_list[port_pos]);
746 free(port_list);
747 }
748
749 if (xcreated == 0) {
750 syslog(LOG_ERR, "could not create any services");
751 exit(1);
752 }
753 }
754
755 /* Expand svc_run() here so that we can call get_exportlist(). */
756 curtime = nexttime = 0;
757 sigemptyset(&sighup_mask);
758 sigaddset(&sighup_mask, SIGHUP);
759 for (;;) {
760 clock_gettime(CLOCK_MONOTONIC, &tp);
761 curtime = tp.tv_sec;
762 curtime = curtime * 1000000 + tp.tv_nsec / 1000;
763 sigprocmask(SIG_BLOCK, &sighup_mask, &sig_mask);
764 if (got_sighup && curtime >= nexttime) {
765 got_sighup = 0;
766 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
767 get_exportlist(1);
768 clock_gettime(CLOCK_MONOTONIC, &tp);
769 nexttime = tp.tv_sec;
770 nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 +
771 RELOADDELAY;
772 } else
773 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
774
775 /*
776 * If a reload is pending, poll for received request(s),
777 * otherwise set a RELOADDELAY timeout, since a SIGHUP
778 * could be processed between the got_sighup test and
779 * the select() system call.
780 */
781 tv.tv_sec = 0;
782 if (got_sighup)
783 tv.tv_usec = 0;
784 else
785 tv.tv_usec = RELOADDELAY;
786 if (enable_rpcbind != 0) {
787 readfds = svc_fdset;
788 switch (select(svc_maxfd + 1, &readfds, NULL, NULL,
789 &tv)) {
790 case -1:
791 if (errno == EINTR) {
792 /* Allow a reload now. */
793 nexttime = 0;
794 continue;
795 }
796 syslog(LOG_ERR, "mountd died: select: %m");
797 exit(1);
798 case 0:
799 /* Allow a reload now. */
800 nexttime = 0;
801 continue;
802 default:
803 svc_getreqset(&readfds);
804 }
805 } else {
806 /* Simply wait for a signal. */
807 sigsuspend(&sig_mask);
808 }
809 }
810 }
811
812 /*
813 * This routine creates and binds sockets on the appropriate
814 * addresses. It gets called one time for each transport.
815 * It returns 0 upon success, 1 for ignore the call and -1 to indicate
816 * bind failed with EADDRINUSE.
817 * Any file descriptors that have been created are stored in sock_fd and
818 * the total count of them is maintained in sock_fdcnt.
819 */
820 static int
create_service(struct netconfig * nconf)821 create_service(struct netconfig *nconf)
822 {
823 struct addrinfo hints, *res = NULL;
824 struct sockaddr_in *sin;
825 struct sockaddr_in6 *sin6;
826 struct __rpc_sockinfo si;
827 int aicode;
828 int fd;
829 int nhostsbak;
830 int one = 1;
831 int r;
832 u_int32_t host_addr[4]; /* IPv4 or IPv6 */
833 int mallocd_res;
834
835 if ((nconf->nc_semantics != NC_TPI_CLTS) &&
836 (nconf->nc_semantics != NC_TPI_COTS) &&
837 (nconf->nc_semantics != NC_TPI_COTS_ORD))
838 return (1); /* not my type */
839
840 /*
841 * XXX - using RPC library internal functions.
842 */
843 if (!__rpc_nconf2sockinfo(nconf, &si)) {
844 syslog(LOG_ERR, "cannot get information for %s",
845 nconf->nc_netid);
846 return (1);
847 }
848
849 /* Get mountd's address on this transport */
850 memset(&hints, 0, sizeof hints);
851 hints.ai_family = si.si_af;
852 hints.ai_socktype = si.si_socktype;
853 hints.ai_protocol = si.si_proto;
854
855 /*
856 * Bind to specific IPs if asked to
857 */
858 nhostsbak = nhosts;
859 while (nhostsbak > 0) {
860 --nhostsbak;
861 sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
862 if (sock_fd == NULL)
863 out_of_mem();
864 sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */
865 mallocd_res = 0;
866
867 hints.ai_flags = AI_PASSIVE;
868
869 /*
870 * XXX - using RPC library internal functions.
871 */
872 if ((fd = __rpc_nconf2fd(nconf)) < 0) {
873 int non_fatal = 0;
874 if (errno == EAFNOSUPPORT &&
875 nconf->nc_semantics != NC_TPI_CLTS)
876 non_fatal = 1;
877
878 syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
879 "cannot create socket for %s", nconf->nc_netid);
880 if (non_fatal != 0)
881 continue;
882 exit(1);
883 }
884
885 switch (hints.ai_family) {
886 case AF_INET:
887 if (inet_pton(AF_INET, hosts[nhostsbak],
888 host_addr) == 1) {
889 hints.ai_flags |= AI_NUMERICHOST;
890 } else {
891 /*
892 * Skip if we have an AF_INET6 address.
893 */
894 if (inet_pton(AF_INET6, hosts[nhostsbak],
895 host_addr) == 1) {
896 close(fd);
897 continue;
898 }
899 }
900 break;
901 case AF_INET6:
902 if (inet_pton(AF_INET6, hosts[nhostsbak],
903 host_addr) == 1) {
904 hints.ai_flags |= AI_NUMERICHOST;
905 } else {
906 /*
907 * Skip if we have an AF_INET address.
908 */
909 if (inet_pton(AF_INET, hosts[nhostsbak],
910 host_addr) == 1) {
911 close(fd);
912 continue;
913 }
914 }
915
916 /*
917 * We're doing host-based access checks here, so don't
918 * allow v4-in-v6 to confuse things. The kernel will
919 * disable it by default on NFS sockets too.
920 */
921 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
922 sizeof one) < 0) {
923 syslog(LOG_ERR,
924 "can't disable v4-in-v6 on IPv6 socket");
925 exit(1);
926 }
927 break;
928 default:
929 break;
930 }
931
932 /*
933 * If no hosts were specified, just bind to INADDR_ANY
934 */
935 if (strcmp("*", hosts[nhostsbak]) == 0) {
936 if (svcport_str == NULL) {
937 res = malloc(sizeof(struct addrinfo));
938 if (res == NULL)
939 out_of_mem();
940 mallocd_res = 1;
941 res->ai_flags = hints.ai_flags;
942 res->ai_family = hints.ai_family;
943 res->ai_protocol = hints.ai_protocol;
944 switch (res->ai_family) {
945 case AF_INET:
946 sin = malloc(sizeof(struct sockaddr_in));
947 if (sin == NULL)
948 out_of_mem();
949 sin->sin_family = AF_INET;
950 sin->sin_port = htons(0);
951 sin->sin_addr.s_addr = htonl(INADDR_ANY);
952 res->ai_addr = (struct sockaddr*) sin;
953 res->ai_addrlen = (socklen_t)
954 sizeof(struct sockaddr_in);
955 break;
956 case AF_INET6:
957 sin6 = malloc(sizeof(struct sockaddr_in6));
958 if (sin6 == NULL)
959 out_of_mem();
960 sin6->sin6_family = AF_INET6;
961 sin6->sin6_port = htons(0);
962 sin6->sin6_addr = in6addr_any;
963 res->ai_addr = (struct sockaddr*) sin6;
964 res->ai_addrlen = (socklen_t)
965 sizeof(struct sockaddr_in6);
966 break;
967 default:
968 syslog(LOG_ERR, "bad addr fam %d",
969 res->ai_family);
970 exit(1);
971 }
972 } else {
973 if ((aicode = getaddrinfo(NULL, svcport_str,
974 &hints, &res)) != 0) {
975 syslog(LOG_ERR,
976 "cannot get local address for %s: %s",
977 nconf->nc_netid,
978 gai_strerror(aicode));
979 close(fd);
980 continue;
981 }
982 }
983 } else {
984 if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
985 &hints, &res)) != 0) {
986 syslog(LOG_ERR,
987 "cannot get local address for %s: %s",
988 nconf->nc_netid, gai_strerror(aicode));
989 close(fd);
990 continue;
991 }
992 }
993
994 /* Store the fd. */
995 sock_fd[sock_fdcnt - 1] = fd;
996
997 /* Now, attempt the bind. */
998 r = bindresvport_sa(fd, res->ai_addr);
999 if (r != 0) {
1000 if (errno == EADDRINUSE && mallocd_svcport != 0) {
1001 if (mallocd_res != 0) {
1002 free(res->ai_addr);
1003 free(res);
1004 } else
1005 freeaddrinfo(res);
1006 return (-1);
1007 }
1008 syslog(LOG_ERR, "bindresvport_sa: %m");
1009 exit(1);
1010 }
1011
1012 if (svcport_str == NULL) {
1013 svcport_str = malloc(NI_MAXSERV * sizeof(char));
1014 if (svcport_str == NULL)
1015 out_of_mem();
1016 mallocd_svcport = 1;
1017
1018 if (getnameinfo(res->ai_addr,
1019 res->ai_addr->sa_len, NULL, NI_MAXHOST,
1020 svcport_str, NI_MAXSERV * sizeof(char),
1021 NI_NUMERICHOST | NI_NUMERICSERV))
1022 errx(1, "Cannot get port number");
1023 }
1024 if (mallocd_res != 0) {
1025 free(res->ai_addr);
1026 free(res);
1027 } else
1028 freeaddrinfo(res);
1029 res = NULL;
1030 }
1031 return (0);
1032 }
1033
1034 /*
1035 * Called after all the create_service() calls have succeeded, to complete
1036 * the setup and registration.
1037 */
1038 static void
complete_service(struct netconfig * nconf,char * port_str)1039 complete_service(struct netconfig *nconf, char *port_str)
1040 {
1041 struct addrinfo hints, *res = NULL;
1042 struct __rpc_sockinfo si;
1043 struct netbuf servaddr;
1044 SVCXPRT *transp = NULL;
1045 int aicode, fd, nhostsbak;
1046 int registered = 0;
1047
1048 if ((nconf->nc_semantics != NC_TPI_CLTS) &&
1049 (nconf->nc_semantics != NC_TPI_COTS) &&
1050 (nconf->nc_semantics != NC_TPI_COTS_ORD))
1051 return; /* not my type */
1052
1053 /*
1054 * XXX - using RPC library internal functions.
1055 */
1056 if (!__rpc_nconf2sockinfo(nconf, &si)) {
1057 syslog(LOG_ERR, "cannot get information for %s",
1058 nconf->nc_netid);
1059 return;
1060 }
1061
1062 nhostsbak = nhosts;
1063 while (nhostsbak > 0) {
1064 --nhostsbak;
1065 if (sock_fdpos >= sock_fdcnt) {
1066 /* Should never happen. */
1067 syslog(LOG_ERR, "Ran out of socket fd's");
1068 return;
1069 }
1070 fd = sock_fd[sock_fdpos++];
1071 if (fd < 0)
1072 continue;
1073
1074 /*
1075 * Using -1 tells listen(2) to use
1076 * kern.ipc.soacceptqueue for the backlog.
1077 */
1078 if (nconf->nc_semantics != NC_TPI_CLTS)
1079 listen(fd, -1);
1080
1081 if (nconf->nc_semantics == NC_TPI_CLTS )
1082 transp = svc_dg_create(fd, 0, 0);
1083 else
1084 transp = svc_vc_create(fd, RPC_MAXDATASIZE,
1085 RPC_MAXDATASIZE);
1086
1087 if (transp != (SVCXPRT *) NULL) {
1088 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
1089 NULL))
1090 syslog(LOG_ERR,
1091 "can't register %s MOUNTVERS service",
1092 nconf->nc_netid);
1093 if (!force_v2) {
1094 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
1095 mntsrv, NULL))
1096 syslog(LOG_ERR,
1097 "can't register %s MOUNTVERS3 service",
1098 nconf->nc_netid);
1099 }
1100 } else
1101 syslog(LOG_WARNING, "can't create %s services",
1102 nconf->nc_netid);
1103
1104 if (registered == 0) {
1105 registered = 1;
1106 memset(&hints, 0, sizeof hints);
1107 hints.ai_flags = AI_PASSIVE;
1108 hints.ai_family = si.si_af;
1109 hints.ai_socktype = si.si_socktype;
1110 hints.ai_protocol = si.si_proto;
1111
1112 if ((aicode = getaddrinfo(NULL, port_str, &hints,
1113 &res)) != 0) {
1114 syslog(LOG_ERR, "cannot get local address: %s",
1115 gai_strerror(aicode));
1116 exit(1);
1117 }
1118
1119 servaddr.buf = malloc(res->ai_addrlen);
1120 memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
1121 servaddr.len = res->ai_addrlen;
1122
1123 rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
1124 rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
1125
1126 xcreated++;
1127 freeaddrinfo(res);
1128 }
1129 } /* end while */
1130 }
1131
1132 /*
1133 * Clear out sockets after a failure to bind one of them, so that the
1134 * cycle of socket creation/binding can start anew.
1135 */
1136 static void
clearout_service(void)1137 clearout_service(void)
1138 {
1139 int i;
1140
1141 for (i = 0; i < sock_fdcnt; i++) {
1142 if (sock_fd[i] >= 0) {
1143 shutdown(sock_fd[i], SHUT_RDWR);
1144 close(sock_fd[i]);
1145 }
1146 }
1147 }
1148
1149 static void
usage(void)1150 usage(void)
1151 {
1152 fprintf(stderr,
1153 "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
1154 "[-S] [-h <bindip>] [export_file ...]\n");
1155 exit(1);
1156 }
1157
1158 /*
1159 * The mount rpc service
1160 */
1161 void
mntsrv(struct svc_req * rqstp,SVCXPRT * transp)1162 mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
1163 {
1164 struct exportlist *ep;
1165 struct dirlist *dp;
1166 struct fhreturn fhr;
1167 struct stat stb;
1168 struct statfs fsb;
1169 char host[NI_MAXHOST], numerichost[NI_MAXHOST];
1170 int lookup_failed = 1;
1171 struct sockaddr *saddr;
1172 u_short sport;
1173 char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
1174 int defset, hostset;
1175 long bad = 0;
1176 sigset_t sighup_mask;
1177 int numsecflavors, *secflavorsp;
1178
1179 sigemptyset(&sighup_mask);
1180 sigaddset(&sighup_mask, SIGHUP);
1181 saddr = svc_getrpccaller(transp)->buf;
1182 switch (saddr->sa_family) {
1183 case AF_INET6:
1184 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1185 break;
1186 case AF_INET:
1187 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1188 break;
1189 default:
1190 syslog(LOG_ERR, "request from unknown address family");
1191 return;
1192 }
1193 switch (rqstp->rq_proc) {
1194 case MOUNTPROC_MNT:
1195 case MOUNTPROC_UMNT:
1196 case MOUNTPROC_UMNTALL:
1197 lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1198 sizeof host, NULL, 0, 0);
1199 }
1200 getnameinfo(saddr, saddr->sa_len, numerichost,
1201 sizeof numerichost, NULL, 0, NI_NUMERICHOST);
1202 switch (rqstp->rq_proc) {
1203 case NULLPROC:
1204 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
1205 syslog(LOG_ERR, "can't send reply");
1206 return;
1207 case MOUNTPROC_MNT:
1208 if (sport >= IPPORT_RESERVED && resvport_only) {
1209 syslog(LOG_NOTICE,
1210 "mount request from %s from unprivileged port",
1211 numerichost);
1212 svcerr_weakauth(transp);
1213 return;
1214 }
1215 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1216 syslog(LOG_NOTICE, "undecodable mount request from %s",
1217 numerichost);
1218 svcerr_decode(transp);
1219 return;
1220 }
1221
1222 /*
1223 * Get the real pathname and make sure it is a directory
1224 * or a regular file if the -r option was specified
1225 * and it exists.
1226 */
1227 if (realpath(rpcpath, dirpath) == NULL ||
1228 stat(dirpath, &stb) < 0 ||
1229 statfs(dirpath, &fsb) < 0) {
1230 chdir("/"); /* Just in case realpath doesn't */
1231 syslog(LOG_NOTICE,
1232 "mount request from %s for non existent path %s",
1233 numerichost, dirpath);
1234 if (debug)
1235 warnx("stat failed on %s", dirpath);
1236 bad = ENOENT; /* We will send error reply later */
1237 }
1238 if (!bad &&
1239 !S_ISDIR(stb.st_mode) &&
1240 (dir_only || !S_ISREG(stb.st_mode))) {
1241 syslog(LOG_NOTICE,
1242 "mount request from %s for non-directory path %s",
1243 numerichost, dirpath);
1244 if (debug)
1245 warnx("mounting non-directory %s", dirpath);
1246 bad = ENOTDIR; /* We will send error reply later */
1247 }
1248
1249 /* Check in the exports list */
1250 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1251 if (bad)
1252 ep = NULL;
1253 else
1254 ep = ex_search(&fsb.f_fsid, exphead);
1255 hostset = defset = 0;
1256 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1257 &numsecflavors, &secflavorsp) ||
1258 ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1259 chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1260 &secflavorsp)) ||
1261 (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1262 scan_tree(ep->ex_dirl, saddr) == 0))) {
1263 if (bad) {
1264 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1265 (caddr_t)&bad))
1266 syslog(LOG_ERR, "can't send reply");
1267 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1268 return;
1269 }
1270 if (hostset & DP_HOSTSET) {
1271 fhr.fhr_flag = hostset;
1272 fhr.fhr_numsecflavors = numsecflavors;
1273 fhr.fhr_secflavors = secflavorsp;
1274 } else {
1275 fhr.fhr_flag = defset;
1276 fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1277 fhr.fhr_secflavors = ep->ex_defsecflavors;
1278 }
1279 fhr.fhr_vers = rqstp->rq_vers;
1280 /* Get the file handle */
1281 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1282 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1283 bad = errno;
1284 syslog(LOG_ERR, "can't get fh for %s", dirpath);
1285 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1286 (caddr_t)&bad))
1287 syslog(LOG_ERR, "can't send reply");
1288 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1289 return;
1290 }
1291 if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1292 (caddr_t)&fhr))
1293 syslog(LOG_ERR, "can't send reply");
1294 if (!lookup_failed)
1295 add_mlist(host, dirpath);
1296 else
1297 add_mlist(numerichost, dirpath);
1298 if (debug)
1299 warnx("mount successful");
1300 if (dolog)
1301 syslog(LOG_NOTICE,
1302 "mount request succeeded from %s for %s",
1303 numerichost, dirpath);
1304 } else {
1305 if (!bad)
1306 bad = EACCES;
1307 syslog(LOG_NOTICE,
1308 "mount request denied from %s for %s",
1309 numerichost, dirpath);
1310 }
1311
1312 if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1313 (caddr_t)&bad))
1314 syslog(LOG_ERR, "can't send reply");
1315 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1316 return;
1317 case MOUNTPROC_DUMP:
1318 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1319 syslog(LOG_ERR, "can't send reply");
1320 else if (dolog)
1321 syslog(LOG_NOTICE,
1322 "dump request succeeded from %s",
1323 numerichost);
1324 return;
1325 case MOUNTPROC_UMNT:
1326 if (sport >= IPPORT_RESERVED && resvport_only) {
1327 syslog(LOG_NOTICE,
1328 "umount request from %s from unprivileged port",
1329 numerichost);
1330 svcerr_weakauth(transp);
1331 return;
1332 }
1333 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1334 syslog(LOG_NOTICE, "undecodable umount request from %s",
1335 numerichost);
1336 svcerr_decode(transp);
1337 return;
1338 }
1339 if (realpath(rpcpath, dirpath) == NULL) {
1340 syslog(LOG_NOTICE, "umount request from %s "
1341 "for non existent path %s",
1342 numerichost, dirpath);
1343 }
1344 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1345 syslog(LOG_ERR, "can't send reply");
1346 if (!lookup_failed)
1347 del_mlist(host, dirpath);
1348 del_mlist(numerichost, dirpath);
1349 if (dolog)
1350 syslog(LOG_NOTICE,
1351 "umount request succeeded from %s for %s",
1352 numerichost, dirpath);
1353 return;
1354 case MOUNTPROC_UMNTALL:
1355 if (sport >= IPPORT_RESERVED && resvport_only) {
1356 syslog(LOG_NOTICE,
1357 "umountall request from %s from unprivileged port",
1358 numerichost);
1359 svcerr_weakauth(transp);
1360 return;
1361 }
1362 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1363 syslog(LOG_ERR, "can't send reply");
1364 if (!lookup_failed)
1365 del_mlist(host, NULL);
1366 del_mlist(numerichost, NULL);
1367 if (dolog)
1368 syslog(LOG_NOTICE,
1369 "umountall request succeeded from %s",
1370 numerichost);
1371 return;
1372 case MOUNTPROC_EXPORT:
1373 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1374 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1375 (caddr_t)NULL))
1376 syslog(LOG_ERR, "can't send reply");
1377 if (dolog)
1378 syslog(LOG_NOTICE,
1379 "export request succeeded from %s",
1380 numerichost);
1381 return;
1382 default:
1383 svcerr_noproc(transp);
1384 return;
1385 }
1386 }
1387
1388 /*
1389 * Xdr conversion for a dirpath string
1390 */
1391 static int
xdr_dir(XDR * xdrsp,char * dirp)1392 xdr_dir(XDR *xdrsp, char *dirp)
1393 {
1394 return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1395 }
1396
1397 /*
1398 * Xdr routine to generate file handle reply
1399 */
1400 static int
xdr_fhs(XDR * xdrsp,caddr_t cp)1401 xdr_fhs(XDR *xdrsp, caddr_t cp)
1402 {
1403 struct fhreturn *fhrp = (struct fhreturn *)cp;
1404 u_long ok = 0, len, auth;
1405 int i;
1406
1407 if (!xdr_long(xdrsp, &ok))
1408 return (0);
1409 switch (fhrp->fhr_vers) {
1410 case 1:
1411 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1412 case 3:
1413 len = NFSX_V3FH;
1414 if (!xdr_long(xdrsp, &len))
1415 return (0);
1416 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1417 return (0);
1418 if (fhrp->fhr_numsecflavors) {
1419 if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1420 return (0);
1421 for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1422 if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1423 return (0);
1424 return (1);
1425 } else {
1426 auth = AUTH_SYS;
1427 len = 1;
1428 if (!xdr_long(xdrsp, &len))
1429 return (0);
1430 return (xdr_long(xdrsp, &auth));
1431 }
1432 }
1433 return (0);
1434 }
1435
1436 static int
xdr_mlist(XDR * xdrsp,caddr_t cp __unused)1437 xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1438 {
1439 struct mountlist *mlp;
1440 int true = 1;
1441 int false = 0;
1442 char *strp;
1443
1444 SLIST_FOREACH(mlp, &mlhead, next) {
1445 if (!xdr_bool(xdrsp, &true))
1446 return (0);
1447 strp = &mlp->ml_host[0];
1448 if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1449 return (0);
1450 strp = &mlp->ml_dirp[0];
1451 if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1452 return (0);
1453 }
1454 if (!xdr_bool(xdrsp, &false))
1455 return (0);
1456 return (1);
1457 }
1458
1459 /*
1460 * Xdr conversion for export list
1461 */
1462 static int
xdr_explist_common(XDR * xdrsp,caddr_t cp __unused,int brief)1463 xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1464 {
1465 struct exportlist *ep;
1466 int false = 0;
1467 int putdef;
1468 sigset_t sighup_mask;
1469 int i;
1470
1471 sigemptyset(&sighup_mask);
1472 sigaddset(&sighup_mask, SIGHUP);
1473 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1474
1475 for (i = 0; i < exphashsize; i++)
1476 SLIST_FOREACH(ep, &exphead[i], entries) {
1477 putdef = 0;
1478 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1479 &putdef, brief))
1480 goto errout;
1481 if (ep->ex_defdir && putdef == 0 &&
1482 put_exlist(ep->ex_defdir, xdrsp, NULL,
1483 &putdef, brief))
1484 goto errout;
1485 }
1486 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1487 if (!xdr_bool(xdrsp, &false))
1488 return (0);
1489 return (1);
1490 errout:
1491 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1492 return (0);
1493 }
1494
1495 /*
1496 * Called from xdr_explist() to traverse the tree and export the
1497 * directory paths.
1498 */
1499 static int
put_exlist(struct dirlist * dp,XDR * xdrsp,struct dirlist * adp,int * putdefp,int brief)1500 put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1501 int brief)
1502 {
1503 struct grouplist *grp;
1504 struct hostlist *hp;
1505 int true = 1;
1506 int false = 0;
1507 int gotalldir = 0;
1508 char *strp;
1509
1510 if (dp) {
1511 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1512 return (1);
1513 if (!xdr_bool(xdrsp, &true))
1514 return (1);
1515 strp = dp->dp_dirp;
1516 if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1517 return (1);
1518 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1519 gotalldir = 1;
1520 *putdefp = 1;
1521 }
1522 if (brief) {
1523 if (!xdr_bool(xdrsp, &true))
1524 return (1);
1525 strp = "(...)";
1526 if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1527 return (1);
1528 } else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1529 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1530 hp = dp->dp_hosts;
1531 while (hp) {
1532 grp = hp->ht_grp;
1533 if (grp->gr_type == GT_HOST) {
1534 if (!xdr_bool(xdrsp, &true))
1535 return (1);
1536 strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1537 if (!xdr_string(xdrsp, &strp,
1538 MNTNAMLEN))
1539 return (1);
1540 } else if (grp->gr_type == GT_NET) {
1541 if (!xdr_bool(xdrsp, &true))
1542 return (1);
1543 strp = grp->gr_ptr.gt_net.nt_name;
1544 if (!xdr_string(xdrsp, &strp,
1545 MNTNAMLEN))
1546 return (1);
1547 }
1548 hp = hp->ht_next;
1549 if (gotalldir && hp == (struct hostlist *)NULL) {
1550 hp = adp->dp_hosts;
1551 gotalldir = 0;
1552 }
1553 }
1554 }
1555 if (!xdr_bool(xdrsp, &false))
1556 return (1);
1557 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1558 return (1);
1559 }
1560 return (0);
1561 }
1562
1563 static int
xdr_explist(XDR * xdrsp,caddr_t cp)1564 xdr_explist(XDR *xdrsp, caddr_t cp)
1565 {
1566
1567 return xdr_explist_common(xdrsp, cp, 0);
1568 }
1569
1570 static int
xdr_explist_brief(XDR * xdrsp,caddr_t cp)1571 xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1572 {
1573
1574 return xdr_explist_common(xdrsp, cp, 1);
1575 }
1576
1577 static char *line;
1578 static size_t linesize;
1579 static FILE *exp_file;
1580
1581 /*
1582 * Get the export list from one, currently open file
1583 */
1584 static void
get_exportlist_one(int passno)1585 get_exportlist_one(int passno)
1586 {
1587 struct exportlist *ep;
1588 struct grouplist *grp, *tgrp, *savgrp;
1589 struct dirlist *dirhead;
1590 struct statfs fsb;
1591 struct expcred anon;
1592 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1593 char *err_msg = NULL;
1594 int len, has_host, got_nondir, dirplen, netgrp;
1595 uint64_t exflags;
1596 char unvis_dir[PATH_MAX + 1];
1597 int unvis_len;
1598
1599 v4root_phase = 0;
1600 dirhead = (struct dirlist *)NULL;
1601 unvis_dir[0] = '\0';
1602 fsb.f_mntonname[0] = '\0';
1603
1604 while (get_line()) {
1605 if (debug)
1606 warnx("got line %s", line);
1607 cp = line;
1608 nextfield(&cp, &endcp);
1609 if (*cp == '#')
1610 goto nextline;
1611
1612 /*
1613 * Set defaults.
1614 */
1615 has_host = FALSE;
1616 anon.cr_uid = UID_NOBODY;
1617 anon.cr_ngroups = 1;
1618 anon.cr_groups = tmp_groups;
1619 anon.cr_groups[0] = nogroup();
1620 exflags = MNT_EXPORTED;
1621 got_nondir = 0;
1622 opt_flags = 0;
1623 ep = (struct exportlist *)NULL;
1624 dirp = NULL;
1625
1626 /*
1627 * Handle the V4 root dir.
1628 */
1629 if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1630 /*
1631 * V4: just indicates that it is the v4 root point,
1632 * so skip over that and set v4root_phase.
1633 */
1634 if (v4root_phase > 0) {
1635 syslog(LOG_ERR, "V4:duplicate line, ignored");
1636 goto nextline;
1637 }
1638 v4root_phase = 1;
1639 cp += 3;
1640 nextfield(&cp, &endcp);
1641 }
1642
1643 /*
1644 * Create new exports list entry
1645 */
1646 len = endcp-cp;
1647 tgrp = grp = get_grp();
1648 while (len > 0) {
1649 if (len > MNTNAMLEN) {
1650 getexp_err(ep, tgrp, "mountpoint too long");
1651 goto nextline;
1652 }
1653 if (*cp == '-') {
1654 if (ep == (struct exportlist *)NULL) {
1655 getexp_err(ep, tgrp,
1656 "flag before export path definition");
1657 goto nextline;
1658 }
1659 if (debug)
1660 warnx("doing opt %s", cp);
1661 got_nondir = 1;
1662 if (do_opt(&cp, &endcp, ep, grp, &has_host,
1663 &exflags, &anon, unvis_dir)) {
1664 getexp_err(ep, tgrp, NULL);
1665 goto nextline;
1666 }
1667 } else if (*cp == '/') {
1668 savedc = *endcp;
1669 *endcp = '\0';
1670 unvis_len = strnunvis(unvis_dir, sizeof(unvis_dir),
1671 cp);
1672 if (unvis_len <= 0) {
1673 getexp_err(ep, tgrp, "Cannot strunvis "
1674 "decode dir");
1675 goto nextline;
1676 }
1677 if (v4root_phase > 1) {
1678 if (dirp != NULL) {
1679 getexp_err(ep, tgrp, "Multiple V4 dirs");
1680 goto nextline;
1681 }
1682 }
1683 if (check_dirpath(unvis_dir, &err_msg) &&
1684 check_statfs(unvis_dir, &fsb, &err_msg)) {
1685 if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1686 syslog(LOG_ERR, "Warning: exporting of "
1687 "automounted fs %s not supported",
1688 unvis_dir);
1689 if (got_nondir) {
1690 getexp_err(ep, tgrp, "dirs must be first");
1691 goto nextline;
1692 }
1693 if (v4root_phase == 1) {
1694 if (dirp != NULL) {
1695 getexp_err(ep, tgrp, "Multiple V4 dirs");
1696 goto nextline;
1697 }
1698 if (strlen(v4root_dirpath) == 0) {
1699 strlcpy(v4root_dirpath, unvis_dir,
1700 sizeof (v4root_dirpath));
1701 } else if (strcmp(v4root_dirpath, unvis_dir)
1702 != 0) {
1703 syslog(LOG_ERR,
1704 "different V4 dirpath %s",
1705 unvis_dir);
1706 getexp_err(ep, tgrp, NULL);
1707 goto nextline;
1708 }
1709 dirp = unvis_dir;
1710 v4root_phase = 2;
1711 got_nondir = 1;
1712 ep = get_exp();
1713 } else {
1714 if (ep) {
1715 if (fsidcmp(&ep->ex_fs, &fsb.f_fsid)
1716 != 0) {
1717 getexp_err(ep, tgrp,
1718 "fsid mismatch");
1719 goto nextline;
1720 }
1721 } else {
1722 /*
1723 * See if this directory is already
1724 * in the list.
1725 */
1726 ep = ex_search(&fsb.f_fsid, exphead);
1727 if (ep == (struct exportlist *)NULL) {
1728 ep = get_exp();
1729 ep->ex_fs = fsb.f_fsid;
1730 ep->ex_fsdir = strdup(fsb.f_mntonname);
1731 if (ep->ex_fsdir == NULL)
1732 out_of_mem();
1733 if (debug)
1734 warnx(
1735 "making new ep fs=0x%x,0x%x",
1736 fsb.f_fsid.val[0],
1737 fsb.f_fsid.val[1]);
1738 } else if (debug)
1739 warnx("found ep fs=0x%x,0x%x",
1740 fsb.f_fsid.val[0],
1741 fsb.f_fsid.val[1]);
1742 }
1743
1744 if (strcmp(unvis_dir, fsb.f_mntonname) !=
1745 0)
1746 opt_flags |= OP_NOTROOT;
1747
1748 /*
1749 * Add dirpath to export mount point.
1750 */
1751 dirp = add_expdir(&dirhead, unvis_dir,
1752 unvis_len);
1753 dirplen = unvis_len;
1754 }
1755 } else {
1756 if (err_msg != NULL) {
1757 getexp_err(ep, tgrp, err_msg);
1758 free(err_msg);
1759 err_msg = NULL;
1760 } else {
1761 getexp_err(ep, tgrp,
1762 "symbolic link in export path or "
1763 "statfs failed");
1764 }
1765 goto nextline;
1766 }
1767 *endcp = savedc;
1768 } else {
1769 savedc = *endcp;
1770 *endcp = '\0';
1771 got_nondir = 1;
1772 if (ep == (struct exportlist *)NULL) {
1773 getexp_err(ep, tgrp,
1774 "host(s) before export path definition");
1775 goto nextline;
1776 }
1777
1778 /*
1779 * Get the host or netgroup.
1780 */
1781 setnetgrent(cp);
1782 netgrp = getnetgrent(&hst, &usr, &dom);
1783 do {
1784 if (has_host) {
1785 grp->gr_next = get_grp();
1786 grp = grp->gr_next;
1787 }
1788 if (netgrp) {
1789 if (hst == 0) {
1790 syslog(LOG_ERR,
1791 "null hostname in netgroup %s, skipping", cp);
1792 grp->gr_type = GT_IGNORE;
1793 } else if (get_host(hst, grp, tgrp)) {
1794 syslog(LOG_ERR,
1795 "bad host %s in netgroup %s, skipping", hst, cp);
1796 grp->gr_type = GT_IGNORE;
1797 }
1798 } else if (get_host(cp, grp, tgrp)) {
1799 syslog(LOG_ERR, "bad host %s, skipping", cp);
1800 grp->gr_type = GT_IGNORE;
1801 }
1802 has_host = TRUE;
1803 } while (netgrp && getnetgrent(&hst, &usr, &dom));
1804 endnetgrent();
1805 *endcp = savedc;
1806 }
1807 cp = endcp;
1808 nextfield(&cp, &endcp);
1809 len = endcp - cp;
1810 }
1811 if (opt_flags & OP_CLASSMASK)
1812 syslog(LOG_WARNING,
1813 "WARNING: No mask specified for %s, "
1814 "using out-of-date default",
1815 (&grp->gr_ptr.gt_net)->nt_name);
1816 if ((opt_flags & OP_NOTROOT) != 0 && warn_admin != 0 &&
1817 (ep->ex_flag & EX_ADMINWARN) == 0 && unvis_dir[0] != '\0' &&
1818 fsb.f_mntonname[0] != '\0') {
1819 if (debug)
1820 warnx("exporting %s exports entire %s file "
1821 "system", unvis_dir, fsb.f_mntonname);
1822 syslog(LOG_ERR, "Warning: exporting %s exports "
1823 "entire %s file system", unvis_dir,
1824 fsb.f_mntonname);
1825 ep->ex_flag |= EX_ADMINWARN;
1826 }
1827 if (check_options(dirhead)) {
1828 getexp_err(ep, tgrp, NULL);
1829 goto nextline;
1830 }
1831 if (!has_host) {
1832 grp->gr_type = GT_DEFAULT;
1833 if (debug)
1834 warnx("adding a default entry");
1835
1836 /*
1837 * Don't allow a network export coincide with a list of
1838 * host(s) on the same line.
1839 */
1840 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1841 getexp_err(ep, tgrp, "network/host conflict");
1842 goto nextline;
1843
1844 /*
1845 * If an export list was specified on this line, make sure
1846 * that we have at least one valid entry, otherwise skip it.
1847 */
1848 } else {
1849 grp = tgrp;
1850 while (grp && grp->gr_type == GT_IGNORE)
1851 grp = grp->gr_next;
1852 if (! grp) {
1853 getexp_err(ep, tgrp, "no valid entries");
1854 goto nextline;
1855 }
1856 }
1857
1858 if (v4root_phase == 1) {
1859 getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1860 goto nextline;
1861 }
1862
1863 /*
1864 * Loop through hosts, pushing the exports into the kernel.
1865 * After loop, tgrp points to the start of the list and
1866 * grp points to the last entry in the list.
1867 * Do not do the do_mount() for passno == 1, since the
1868 * second pass will do it, as required.
1869 */
1870 grp = tgrp;
1871 do {
1872 grp->gr_exflags = exflags;
1873 cp_cred(&grp->gr_anon, &anon);
1874 if (v4root_phase == 2 && passno == 0)
1875 LOGDEBUG("do_mount v4root");
1876 if (passno == 0 && do_mount(ep, grp, exflags, &anon,
1877 dirp, dirplen, &fsb, ep->ex_numsecflavors,
1878 ep->ex_secflavors)) {
1879 getexp_err(ep, tgrp, NULL);
1880 goto nextline;
1881 }
1882 } while (grp->gr_next && (grp = grp->gr_next));
1883
1884 /*
1885 * For V4: don't enter in mount lists.
1886 */
1887 if (v4root_phase > 0 && v4root_phase <= 2) {
1888 /*
1889 * These structures are used for the reload,
1890 * so save them for that case. Otherwise, just
1891 * free them up now.
1892 */
1893 if (passno == 1 && ep != NULL) {
1894 savgrp = tgrp;
1895 while (tgrp != NULL) {
1896 /*
1897 * Save the security flavors and exflags
1898 * for this host set in the groups.
1899 */
1900 tgrp->gr_numsecflavors =
1901 ep->ex_numsecflavors;
1902 if (ep->ex_numsecflavors > 0)
1903 memcpy(tgrp->gr_secflavors,
1904 ep->ex_secflavors,
1905 sizeof(ep->ex_secflavors));
1906 tgrp = tgrp->gr_next;
1907 }
1908 if (v4root_ep == NULL) {
1909 v4root_ep = ep;
1910 ep = NULL; /* Don't free below. */
1911 }
1912 grp->gr_next = v4root_ep->ex_grphead;
1913 v4root_ep->ex_grphead = savgrp;
1914 }
1915 if (ep != NULL)
1916 free_exp(ep);
1917 while (tgrp != NULL) {
1918 grp = tgrp;
1919 tgrp = tgrp->gr_next;
1920 free_grp(grp);
1921 }
1922 goto nextline;
1923 }
1924
1925 /*
1926 * Success. Update the data structures.
1927 */
1928 if (has_host) {
1929 hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags);
1930 grp->gr_next = ep->ex_grphead;
1931 ep->ex_grphead = tgrp;
1932 } else {
1933 hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1934 opt_flags, &anon, exflags);
1935 free_grp(grp);
1936 }
1937 dirhead = (struct dirlist *)NULL;
1938 if ((ep->ex_flag & EX_LINKED) == 0) {
1939 insert_exports(ep, exphead);
1940
1941 ep->ex_flag |= EX_LINKED;
1942 }
1943 nextline:
1944 v4root_phase = 0;
1945 if (dirhead) {
1946 free_dir(dirhead);
1947 dirhead = (struct dirlist *)NULL;
1948 }
1949 }
1950 }
1951
1952 /*
1953 * Get the export list from all specified files
1954 */
1955 static void
get_exportlist(int passno)1956 get_exportlist(int passno)
1957 {
1958 struct export_args export;
1959 struct iovec *iov;
1960 struct statfs *mntbufp;
1961 char errmsg[255];
1962 int error, i, nfs_maxvers, num;
1963 int iovlen;
1964 struct nfsex_args eargs;
1965 FILE *debug_file;
1966 size_t nfs_maxvers_size;
1967
1968 if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) {
1969 fclose(debug_file);
1970 logdebug = 1;
1971 } else
1972 logdebug = 0;
1973 LOGDEBUG("passno=%d", passno);
1974 v4root_dirpath[0] = '\0';
1975 free_v4rootexp();
1976 if (passno == 1) {
1977 /*
1978 * Save the current lists as old ones, so that the new lists
1979 * can be compared with the old ones in the 2nd pass.
1980 */
1981 for (i = 0; i < exphashsize; i++) {
1982 SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]);
1983 SLIST_INIT(&exphead[i]);
1984 }
1985
1986 /* Note that the public fh has not yet been set. */
1987 has_set_publicfh = 0;
1988
1989 /* Read the export file(s) and process them */
1990 read_exportfile(passno);
1991 } else {
1992 /*
1993 * Just make the old lists empty.
1994 * exphashsize == 0 for the first call, before oldexphead
1995 * has been initialized-->loop won't be executed.
1996 */
1997 for (i = 0; i < exphashsize; i++)
1998 SLIST_INIT(&oldexphead[i]);
1999 }
2000
2001 bzero(&export, sizeof(export));
2002 export.ex_flags = MNT_DELEXPORT;
2003 iov = NULL;
2004 iovlen = 0;
2005 bzero(errmsg, sizeof(errmsg));
2006
2007 if (suspend_nfsd != 0)
2008 (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
2009 /*
2010 * Delete the old V4 root dir.
2011 */
2012 bzero(&eargs, sizeof (eargs));
2013 eargs.export.ex_flags = MNT_DELEXPORT;
2014 if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, (caddr_t)&eargs) < 0 &&
2015 errno != ENOENT)
2016 syslog(LOG_ERR, "Can't delete exports for V4:");
2017
2018 build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2019 build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2020 build_iovec(&iov, &iovlen, "from", NULL, 0);
2021 build_iovec(&iov, &iovlen, "update", NULL, 0);
2022 build_iovec(&iov, &iovlen, "export", &export,
2023 sizeof(export));
2024 build_iovec(&iov, &iovlen, "errmsg", errmsg,
2025 sizeof(errmsg));
2026
2027 /*
2028 * For passno == 1, compare the old and new lists updating the kernel
2029 * exports for any cases that have changed.
2030 * This call is doing the second pass through the lists.
2031 * If it fails, fall back on the bulk reload.
2032 */
2033 if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) ==
2034 0) {
2035 LOGDEBUG("compareok");
2036 /* Free up the old lists. */
2037 free_exports(oldexphead);
2038 } else {
2039 LOGDEBUG("doing passno=0");
2040 /*
2041 * Clear flag that notes if a public fh has been exported.
2042 * It is set by do_mount() if MNT_EXPUBLIC is set for the entry.
2043 */
2044 has_publicfh = 0;
2045
2046 /* exphead == NULL if not yet allocated (first call). */
2047 if (exphead != NULL) {
2048 /*
2049 * First, get rid of the old lists.
2050 */
2051 free_exports(exphead);
2052 free_exports(oldexphead);
2053 }
2054
2055 /*
2056 * And delete exports that are in the kernel for all local
2057 * filesystems.
2058 * XXX: Should know how to handle all local exportable
2059 * filesystems.
2060 */
2061 num = getmntinfo(&mntbufp, MNT_NOWAIT);
2062
2063 /* Allocate hash tables, for first call. */
2064 if (exphead == NULL) {
2065 /* Target an average linked list length of 10. */
2066 exphashsize = num / 10;
2067 if (exphashsize < 1)
2068 exphashsize = 1;
2069 else if (exphashsize > 100000)
2070 exphashsize = 100000;
2071 exphead = malloc(exphashsize * sizeof(*exphead));
2072 oldexphead = malloc(exphashsize * sizeof(*oldexphead));
2073 if (exphead == NULL || oldexphead == NULL)
2074 errx(1, "Can't malloc hash tables");
2075
2076 for (i = 0; i < exphashsize; i++) {
2077 SLIST_INIT(&exphead[i]);
2078 SLIST_INIT(&oldexphead[i]);
2079 }
2080 }
2081
2082 for (i = 0; i < num; i++)
2083 delete_export(iov, iovlen, &mntbufp[i], errmsg);
2084
2085
2086 /* Read the export file(s) and process them */
2087 read_exportfile(0);
2088 }
2089
2090 if (strlen(v4root_dirpath) == 0) {
2091 /* Check to see if a V4: line is needed. */
2092 nfs_maxvers_size = sizeof(nfs_maxvers);
2093 error = sysctlbyname("vfs.nfsd.server_max_nfsvers",
2094 &nfs_maxvers, &nfs_maxvers_size, NULL, 0);
2095 if (error != 0 || nfs_maxvers < NFS_VER2 || nfs_maxvers >
2096 NFS_VER4) {
2097 syslog(LOG_ERR, "sysctlbyname(vfs.nfsd."
2098 "server_max_nfsvers) failed, defaulting to NFSv3");
2099 nfs_maxvers = NFS_VER3;
2100 }
2101 if (nfs_maxvers == NFS_VER4)
2102 syslog(LOG_ERR, "NFSv4 requires at least one V4: line");
2103 }
2104
2105 if (iov != NULL) {
2106 /* Free strings allocated by strdup() in getmntopts.c */
2107 free(iov[0].iov_base); /* fstype */
2108 free(iov[2].iov_base); /* fspath */
2109 free(iov[4].iov_base); /* from */
2110 free(iov[6].iov_base); /* update */
2111 free(iov[8].iov_base); /* export */
2112 free(iov[10].iov_base); /* errmsg */
2113
2114 /* free iov, allocated by realloc() */
2115 free(iov);
2116 iovlen = 0;
2117 }
2118
2119 /*
2120 * If there was no public fh, clear any previous one set.
2121 */
2122 if (has_publicfh == 0) {
2123 LOGDEBUG("clear public fh");
2124 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
2125 }
2126
2127 /* Resume the nfsd. If they weren't suspended, this is harmless. */
2128 (void)nfssvc(NFSSVC_RESUMENFSD, NULL);
2129 LOGDEBUG("eo get_exportlist");
2130 }
2131
2132 /*
2133 * Insert an export entry in the appropriate list.
2134 */
2135 static void
insert_exports(struct exportlist * ep,struct exportlisthead * exhp)2136 insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
2137 {
2138 uint32_t i;
2139
2140 i = EXPHASH(&ep->ex_fs);
2141 LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i);
2142 SLIST_INSERT_HEAD(&exhp[i], ep, entries);
2143 }
2144
2145 /*
2146 * Free up the exports lists passed in as arguments.
2147 */
2148 static void
free_exports(struct exportlisthead * exhp)2149 free_exports(struct exportlisthead *exhp)
2150 {
2151 struct exportlist *ep, *ep2;
2152 int i;
2153
2154 for (i = 0; i < exphashsize; i++) {
2155 SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
2156 SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
2157 free_exp(ep);
2158 }
2159 SLIST_INIT(&exhp[i]);
2160 }
2161 }
2162
2163 /*
2164 * Read the exports file(s) and call get_exportlist_one() for each line.
2165 */
2166 static void
read_exportfile(int passno)2167 read_exportfile(int passno)
2168 {
2169 int done, i;
2170
2171 /*
2172 * Read in the exports file and build the list, calling
2173 * nmount() as we go along to push the export rules into the kernel.
2174 */
2175 done = 0;
2176 for (i = 0; exnames[i] != NULL; i++) {
2177 if (debug)
2178 warnx("reading exports from %s", exnames[i]);
2179 if ((exp_file = fopen(exnames[i], "r")) == NULL) {
2180 syslog(LOG_WARNING, "can't open %s", exnames[i]);
2181 continue;
2182 }
2183 get_exportlist_one(passno);
2184 fclose(exp_file);
2185 done++;
2186 }
2187 if (done == 0) {
2188 syslog(LOG_ERR, "can't open any exports file");
2189 exit(2);
2190 }
2191 }
2192
2193 /*
2194 * Compare the export lists against the old ones and do nmount() operations
2195 * for any cases that have changed. This avoids doing nmount() for entries
2196 * that have not changed.
2197 * Return 0 upon success, 1 otherwise.
2198 */
2199 static int
compare_nmount_exportlist(struct iovec * iov,int iovlen,char * errmsg)2200 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg)
2201 {
2202 struct exportlist *ep, *oep;
2203 struct grouplist *grp;
2204 struct statfs fs, ofs;
2205 int i, ret;
2206
2207 /*
2208 * Loop through the current list and look for an entry in the old
2209 * list.
2210 * If found, check to see if it the same.
2211 * If it is not the same, delete and re-export.
2212 * Then mark it done on the old list.
2213 * else (not found)
2214 * export it.
2215 * Any entries left in the old list after processing must have their
2216 * exports deleted.
2217 */
2218 for (i = 0; i < exphashsize; i++)
2219 SLIST_FOREACH(ep, &exphead[i], entries) {
2220 LOGDEBUG("foreach ep=%s", ep->ex_fsdir);
2221 oep = ex_search(&ep->ex_fs, oldexphead);
2222 if (oep != NULL) {
2223 /*
2224 * Check the mount paths are the same.
2225 * If not, return 1 so that the reload of the
2226 * exports will be done in bulk, the
2227 * passno == 0 way.
2228 */
2229 LOGDEBUG("found old exp");
2230 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2231 return (1);
2232 LOGDEBUG("same fsdir");
2233 /*
2234 * Test to see if the entry is the same.
2235 * If not the same delete exports and
2236 * re-export.
2237 */
2238 if (compare_export(ep, oep) != 0) {
2239 /*
2240 * Clear has_publicfh if if was set
2241 * in the old exports, but only if it
2242 * has not been set during processing of
2243 * the exports for this pass, as
2244 * indicated by has_set_publicfh.
2245 */
2246 if (has_set_publicfh == 0 &&
2247 (oep->ex_flag & EX_PUBLICFH) != 0)
2248 has_publicfh = 0;
2249
2250 /* Delete and re-export. */
2251 if (statfs(ep->ex_fsdir, &fs) < 0)
2252 return (1);
2253 delete_export(iov, iovlen, &fs, errmsg);
2254 ret = do_export_mount(ep, &fs);
2255 if (ret != 0)
2256 return (ret);
2257 }
2258 oep->ex_flag |= EX_DONE;
2259 LOGDEBUG("exdone");
2260 } else {
2261 LOGDEBUG("not found so export");
2262 /* Not found, so do export. */
2263 if (statfs(ep->ex_fsdir, &fs) < 0)
2264 return (1);
2265 ret = do_export_mount(ep, &fs);
2266 if (ret != 0)
2267 return (ret);
2268 }
2269 }
2270
2271 /* Delete exports not done. */
2272 for (i = 0; i < exphashsize; i++)
2273 SLIST_FOREACH(oep, &oldexphead[i], entries) {
2274 if ((oep->ex_flag & EX_DONE) == 0) {
2275 LOGDEBUG("not done delete=%s", oep->ex_fsdir);
2276 if (statfs(oep->ex_fsdir, &ofs) >= 0 &&
2277 fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) {
2278 LOGDEBUG("do delete");
2279 /*
2280 * Clear has_publicfh if if was set
2281 * in the old exports, but only if it
2282 * has not been set during processing of
2283 * the exports for this pass, as
2284 * indicated by has_set_publicfh.
2285 */
2286 if (has_set_publicfh == 0 &&
2287 (oep->ex_flag & EX_PUBLICFH) != 0)
2288 has_publicfh = 0;
2289
2290 delete_export(iov, iovlen, &ofs,
2291 errmsg);
2292 }
2293 }
2294 }
2295
2296 /* Do the V4 root exports, as required. */
2297 grp = NULL;
2298 if (v4root_ep != NULL)
2299 grp = v4root_ep->ex_grphead;
2300 v4root_phase = 2;
2301 while (v4root_ep != NULL && grp != NULL) {
2302 LOGDEBUG("v4root expath=%s", v4root_dirpath);
2303 ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon,
2304 v4root_dirpath, strlen(v4root_dirpath), &fs,
2305 grp->gr_numsecflavors, grp->gr_secflavors);
2306 if (ret != 0) {
2307 v4root_phase = 0;
2308 return (ret);
2309 }
2310 grp = grp->gr_next;
2311 }
2312 v4root_phase = 0;
2313 free_v4rootexp();
2314 return (0);
2315 }
2316
2317 /*
2318 * Compare old and current exportlist entries for the fsid and return 0
2319 * if they are the same, 1 otherwise.
2320 */
2321 static int
compare_export(struct exportlist * ep,struct exportlist * oep)2322 compare_export(struct exportlist *ep, struct exportlist *oep)
2323 {
2324 struct grouplist *grp, *ogrp;
2325
2326 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2327 return (1);
2328 if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET))
2329 return (1);
2330 if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) ||
2331 (ep->ex_defdir == NULL && oep->ex_defdir != NULL))
2332 return (1);
2333 if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) !=
2334 (oep->ex_defdir->dp_flag & DP_DEFSET))
2335 return (1);
2336 if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors !=
2337 oep->ex_defnumsecflavors || ep->ex_defexflags !=
2338 oep->ex_defexflags || compare_cred(&ep->ex_defanon,
2339 &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors,
2340 oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0))
2341 return (1);
2342
2343 /* Now, check all the groups. */
2344 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2345 ogrp->gr_flag = 0;
2346 for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) {
2347 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp =
2348 ogrp->gr_next)
2349 if ((ogrp->gr_flag & GR_FND) == 0 &&
2350 grp->gr_numsecflavors == ogrp->gr_numsecflavors &&
2351 grp->gr_exflags == ogrp->gr_exflags &&
2352 compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
2353 compare_secflavor(grp->gr_secflavors,
2354 ogrp->gr_secflavors, grp->gr_numsecflavors) == 0 &&
2355 compare_addr(grp, ogrp) == 0)
2356 break;
2357 if (ogrp != NULL)
2358 ogrp->gr_flag |= GR_FND;
2359 else
2360 return (1);
2361 }
2362 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2363 if ((ogrp->gr_flag & GR_FND) == 0)
2364 return (1);
2365 return (0);
2366 }
2367
2368 /*
2369 * Compare the addresses in the group. It is safe to return they are not
2370 * the same when the are, so only return they are the same when they are
2371 * exactly the same.
2372 */
2373 static int
compare_addr(struct grouplist * grp,struct grouplist * ogrp)2374 compare_addr(struct grouplist *grp, struct grouplist *ogrp)
2375 {
2376 struct addrinfo *ai, *oai;
2377
2378 if (grp->gr_type != ogrp->gr_type)
2379 return (1);
2380 switch (grp->gr_type) {
2381 case GT_HOST:
2382 ai = grp->gr_ptr.gt_addrinfo;
2383 oai = ogrp->gr_ptr.gt_addrinfo;
2384 for (; ai != NULL && oai != NULL; ai = ai->ai_next,
2385 oai = oai->ai_next) {
2386 if (sacmp(ai->ai_addr, oai->ai_addr, NULL) != 0)
2387 return (1);
2388 }
2389 if (ai != NULL || oai != NULL)
2390 return (1);
2391 break;
2392 case GT_NET:
2393 /* First compare the masks and then the nets. */
2394 if (sacmp((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask,
2395 (struct sockaddr *)&ogrp->gr_ptr.gt_net.nt_mask, NULL) != 0)
2396 return (1);
2397 if (sacmp((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net,
2398 (struct sockaddr *)&ogrp->gr_ptr.gt_net.nt_net,
2399 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask) != 0)
2400 return (1);
2401 break;
2402 default:
2403 return (1);
2404 }
2405 return (0);
2406 }
2407
2408 /*
2409 * This algorithm compares two arrays of "n" items. It returns 0 if they are
2410 * the "same" and 1 otherwise. Although suboptimal, it is always safe to
2411 * return 1, which makes compare_nmount_export() reload the exports entry.
2412 * "same" refers to having the same set of values in the two arrays.
2413 * The arrays are in no particular order and duplicates (multiple entries
2414 * in an array with the same value) is allowed.
2415 * The algorithm is inefficient, but the common case of identical arrays is
2416 * handled first and "n" is normally fairly small.
2417 * Since the two functions need the same algorithm but for arrays of
2418 * different types (gid_t vs int), this is done as a macro.
2419 */
2420 #define COMPARE_ARRAYS(a1, a2, n) \
2421 do { \
2422 int fnd, fndarray[(n)], i, j; \
2423 /* Handle common case of identical arrays. */ \
2424 for (i = 0; i < (n); i++) \
2425 if ((a1)[i] != (a2)[i]) \
2426 break; \
2427 if (i == (n)) \
2428 return (0); \
2429 for (i = 0; i < (n); i++) \
2430 fndarray[i] = 0; \
2431 for (i = 0; i < (n); i++) { \
2432 fnd = 0; \
2433 for (j = 0; j < (n); j++) { \
2434 if ((a1)[i] == (a2)[j]) { \
2435 fndarray[j] = 1; \
2436 fnd = 1; \
2437 } \
2438 } \
2439 if (fnd == 0) \
2440 return (1); \
2441 } \
2442 for (i = 0; i < (n); i++) \
2443 if (fndarray[i] == 0) \
2444 return (1); \
2445 return (0); \
2446 } while (0)
2447
2448 /*
2449 * Compare two struct expcred's. Return 0 if the same and 1 otherwise.
2450 */
2451 static int
compare_cred(struct expcred * cr0,struct expcred * cr1)2452 compare_cred(struct expcred *cr0, struct expcred *cr1)
2453 {
2454
2455 if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups)
2456 return (1);
2457
2458 COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups);
2459 }
2460
2461 /*
2462 * Compare two lists of security flavors. Return 0 if the same and 1 otherwise.
2463 */
2464 static int
compare_secflavor(int * sec1,int * sec2,int nsec)2465 compare_secflavor(int *sec1, int *sec2, int nsec)
2466 {
2467
2468 COMPARE_ARRAYS(sec1, sec2, nsec);
2469 }
2470
2471 /*
2472 * Delete an exports entry.
2473 */
2474 static void
delete_export(struct iovec * iov,int iovlen,struct statfs * fsp,char * errmsg)2475 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
2476 {
2477 struct xvfsconf vfc;
2478
2479 if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
2480 syslog(LOG_ERR, "getvfsbyname() failed for %s",
2481 fsp->f_fstypename);
2482 return;
2483 }
2484
2485 /*
2486 * We do not need to delete "export" flag from
2487 * filesystems that do not have it set.
2488 */
2489 if (!(fsp->f_flags & MNT_EXPORTED))
2490 return;
2491 /*
2492 * Do not delete export for network filesystem by
2493 * passing "export" arg to nmount().
2494 * It only makes sense to do this for local filesystems.
2495 */
2496 if (vfc.vfc_flags & VFCF_NETWORK)
2497 return;
2498
2499 iov[1].iov_base = fsp->f_fstypename;
2500 iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
2501 iov[3].iov_base = fsp->f_mntonname;
2502 iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
2503 iov[5].iov_base = fsp->f_mntfromname;
2504 iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
2505 errmsg[0] = '\0';
2506
2507 /*
2508 * EXDEV is returned when path exists but is not a
2509 * mount point. May happens if raced with unmount.
2510 */
2511 if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
2512 errno != ENOTSUP && errno != EXDEV) {
2513 syslog(LOG_ERR,
2514 "can't delete exports for %s: %m %s",
2515 fsp->f_mntonname, errmsg);
2516 }
2517 }
2518
2519 /*
2520 * Allocate an export list element
2521 */
2522 static struct exportlist *
get_exp(void)2523 get_exp(void)
2524 {
2525 struct exportlist *ep;
2526
2527 ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
2528 if (ep == (struct exportlist *)NULL)
2529 out_of_mem();
2530 return (ep);
2531 }
2532
2533 /*
2534 * Allocate a group list element
2535 */
2536 static struct grouplist *
get_grp(void)2537 get_grp(void)
2538 {
2539 struct grouplist *gp;
2540
2541 gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
2542 if (gp == (struct grouplist *)NULL)
2543 out_of_mem();
2544 return (gp);
2545 }
2546
2547 /*
2548 * Clean up upon an error in get_exportlist().
2549 */
2550 static void
getexp_err(struct exportlist * ep,struct grouplist * grp,const char * reason)2551 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
2552 {
2553 struct grouplist *tgrp;
2554
2555 if (!(opt_flags & OP_QUIET)) {
2556 if (reason != NULL)
2557 syslog(LOG_ERR, "bad exports list line '%s': %s", line,
2558 reason);
2559 else
2560 syslog(LOG_ERR, "bad exports list line '%s'", line);
2561 }
2562 if (ep && (ep->ex_flag & EX_LINKED) == 0)
2563 free_exp(ep);
2564 while (grp) {
2565 tgrp = grp;
2566 grp = grp->gr_next;
2567 free_grp(tgrp);
2568 }
2569 }
2570
2571 /*
2572 * Search the export list for a matching fs.
2573 */
2574 static struct exportlist *
ex_search(fsid_t * fsid,struct exportlisthead * exhp)2575 ex_search(fsid_t *fsid, struct exportlisthead *exhp)
2576 {
2577 struct exportlist *ep;
2578 uint32_t i;
2579
2580 i = EXPHASH(fsid);
2581 SLIST_FOREACH(ep, &exhp[i], entries) {
2582 if (fsidcmp(&ep->ex_fs, fsid) == 0)
2583 return (ep);
2584 }
2585
2586 return (ep);
2587 }
2588
2589 /*
2590 * Add a directory path to the list.
2591 */
2592 static char *
add_expdir(struct dirlist ** dpp,char * cp,int len)2593 add_expdir(struct dirlist **dpp, char *cp, int len)
2594 {
2595 struct dirlist *dp;
2596
2597 dp = malloc(sizeof (struct dirlist));
2598 if (dp == (struct dirlist *)NULL)
2599 out_of_mem();
2600 dp->dp_left = *dpp;
2601 dp->dp_right = (struct dirlist *)NULL;
2602 dp->dp_flag = 0;
2603 dp->dp_hosts = (struct hostlist *)NULL;
2604 dp->dp_dirp = strndup(cp, len);
2605 if (dp->dp_dirp == NULL)
2606 out_of_mem();
2607 *dpp = dp;
2608 return (dp->dp_dirp);
2609 }
2610
2611 /*
2612 * Hang the dir list element off the dirpath binary tree as required
2613 * and update the entry for host.
2614 */
2615 static void
hang_dirp(struct dirlist * dp,struct grouplist * grp,struct exportlist * ep,int flags,struct expcred * anoncrp,uint64_t exflags)2616 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2617 int flags, struct expcred *anoncrp, uint64_t exflags)
2618 {
2619 struct hostlist *hp;
2620 struct dirlist *dp2;
2621
2622 if (flags & OP_ALLDIRS) {
2623 if (ep->ex_defdir)
2624 free((caddr_t)dp);
2625 else
2626 ep->ex_defdir = dp;
2627 if (grp == (struct grouplist *)NULL) {
2628 ep->ex_flag |= EX_DEFSET;
2629 ep->ex_defdir->dp_flag |= DP_DEFSET;
2630 /* Save the default security flavors list. */
2631 ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2632 if (ep->ex_numsecflavors > 0)
2633 memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2634 sizeof(ep->ex_secflavors));
2635 cp_cred(&ep->ex_defanon, anoncrp);
2636 ep->ex_defexflags = exflags;
2637 } else while (grp) {
2638 hp = get_ht();
2639 hp->ht_grp = grp;
2640 hp->ht_next = ep->ex_defdir->dp_hosts;
2641 ep->ex_defdir->dp_hosts = hp;
2642 /* Save the security flavors list for this host set. */
2643 grp->gr_numsecflavors = ep->ex_numsecflavors;
2644 if (ep->ex_numsecflavors > 0)
2645 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2646 sizeof(ep->ex_secflavors));
2647 grp = grp->gr_next;
2648 }
2649 } else {
2650
2651 /*
2652 * Loop through the directories adding them to the tree.
2653 */
2654 while (dp) {
2655 dp2 = dp->dp_left;
2656 add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp,
2657 exflags);
2658 dp = dp2;
2659 }
2660 }
2661 }
2662
2663 /*
2664 * Traverse the binary tree either updating a node that is already there
2665 * for the new directory or adding the new node.
2666 */
2667 static void
add_dlist(struct dirlist ** dpp,struct dirlist * newdp,struct grouplist * grp,int flags,struct exportlist * ep,struct expcred * anoncrp,uint64_t exflags)2668 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2669 int flags, struct exportlist *ep, struct expcred *anoncrp,
2670 uint64_t exflags)
2671 {
2672 struct dirlist *dp;
2673 struct hostlist *hp;
2674 int cmp;
2675
2676 dp = *dpp;
2677 if (dp) {
2678 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2679 if (cmp > 0) {
2680 add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp,
2681 exflags);
2682 return;
2683 } else if (cmp < 0) {
2684 add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp,
2685 exflags);
2686 return;
2687 } else
2688 free((caddr_t)newdp);
2689 } else {
2690 dp = newdp;
2691 dp->dp_left = (struct dirlist *)NULL;
2692 *dpp = dp;
2693 }
2694 if (grp) {
2695
2696 /*
2697 * Hang all of the host(s) off of the directory point.
2698 */
2699 do {
2700 hp = get_ht();
2701 hp->ht_grp = grp;
2702 hp->ht_next = dp->dp_hosts;
2703 dp->dp_hosts = hp;
2704 /* Save the security flavors list for this host set. */
2705 grp->gr_numsecflavors = ep->ex_numsecflavors;
2706 if (ep->ex_numsecflavors > 0)
2707 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2708 sizeof(ep->ex_secflavors));
2709 grp = grp->gr_next;
2710 } while (grp);
2711 } else {
2712 ep->ex_flag |= EX_DEFSET;
2713 dp->dp_flag |= DP_DEFSET;
2714 /* Save the default security flavors list. */
2715 ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2716 if (ep->ex_numsecflavors > 0)
2717 memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2718 sizeof(ep->ex_secflavors));
2719 cp_cred(&ep->ex_defanon, anoncrp);
2720 ep->ex_defexflags = exflags;
2721 }
2722 }
2723
2724 /*
2725 * Search for a dirpath on the export point.
2726 */
2727 static struct dirlist *
dirp_search(struct dirlist * dp,char * dirp)2728 dirp_search(struct dirlist *dp, char *dirp)
2729 {
2730 int cmp;
2731
2732 if (dp) {
2733 cmp = strcmp(dp->dp_dirp, dirp);
2734 if (cmp > 0)
2735 return (dirp_search(dp->dp_left, dirp));
2736 else if (cmp < 0)
2737 return (dirp_search(dp->dp_right, dirp));
2738 else
2739 return (dp);
2740 }
2741 return (dp);
2742 }
2743
2744 /*
2745 * Scan for a host match in a directory tree.
2746 */
2747 static int
chk_host(struct dirlist * dp,struct sockaddr * saddr,int * defsetp,int * hostsetp,int * numsecflavors,int ** secflavorsp)2748 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2749 int *hostsetp, int *numsecflavors, int **secflavorsp)
2750 {
2751 struct hostlist *hp;
2752 struct grouplist *grp;
2753 struct addrinfo *ai;
2754
2755 if (dp) {
2756 if (dp->dp_flag & DP_DEFSET)
2757 *defsetp = dp->dp_flag;
2758 hp = dp->dp_hosts;
2759 while (hp) {
2760 grp = hp->ht_grp;
2761 switch (grp->gr_type) {
2762 case GT_HOST:
2763 ai = grp->gr_ptr.gt_addrinfo;
2764 for (; ai; ai = ai->ai_next) {
2765 if (!sacmp(ai->ai_addr, saddr, NULL)) {
2766 *hostsetp =
2767 (hp->ht_flag | DP_HOSTSET);
2768 if (numsecflavors != NULL) {
2769 *numsecflavors =
2770 grp->gr_numsecflavors;
2771 *secflavorsp =
2772 grp->gr_secflavors;
2773 }
2774 return (1);
2775 }
2776 }
2777 break;
2778 case GT_NET:
2779 if (!sacmp(saddr, (struct sockaddr *)
2780 &grp->gr_ptr.gt_net.nt_net,
2781 (struct sockaddr *)
2782 &grp->gr_ptr.gt_net.nt_mask)) {
2783 *hostsetp = (hp->ht_flag | DP_HOSTSET);
2784 if (numsecflavors != NULL) {
2785 *numsecflavors =
2786 grp->gr_numsecflavors;
2787 *secflavorsp =
2788 grp->gr_secflavors;
2789 }
2790 return (1);
2791 }
2792 break;
2793 }
2794 hp = hp->ht_next;
2795 }
2796 }
2797 return (0);
2798 }
2799
2800 /*
2801 * Scan tree for a host that matches the address.
2802 */
2803 static int
scan_tree(struct dirlist * dp,struct sockaddr * saddr)2804 scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2805 {
2806 int defset, hostset;
2807
2808 if (dp) {
2809 if (scan_tree(dp->dp_left, saddr))
2810 return (1);
2811 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2812 return (1);
2813 if (scan_tree(dp->dp_right, saddr))
2814 return (1);
2815 }
2816 return (0);
2817 }
2818
2819 /*
2820 * Traverse the dirlist tree and free it up.
2821 */
2822 static void
free_dir(struct dirlist * dp)2823 free_dir(struct dirlist *dp)
2824 {
2825
2826 if (dp) {
2827 free_dir(dp->dp_left);
2828 free_dir(dp->dp_right);
2829 free_host(dp->dp_hosts);
2830 free(dp->dp_dirp);
2831 free(dp);
2832 }
2833 }
2834
2835 /*
2836 * Parse a colon separated list of security flavors
2837 */
2838 static int
parsesec(char * seclist,struct exportlist * ep)2839 parsesec(char *seclist, struct exportlist *ep)
2840 {
2841 char *cp, savedc;
2842 int flavor;
2843
2844 ep->ex_numsecflavors = 0;
2845 for (;;) {
2846 cp = strchr(seclist, ':');
2847 if (cp) {
2848 savedc = *cp;
2849 *cp = '\0';
2850 }
2851
2852 if (!strcmp(seclist, "sys"))
2853 flavor = AUTH_SYS;
2854 else if (!strcmp(seclist, "krb5"))
2855 flavor = RPCSEC_GSS_KRB5;
2856 else if (!strcmp(seclist, "krb5i"))
2857 flavor = RPCSEC_GSS_KRB5I;
2858 else if (!strcmp(seclist, "krb5p"))
2859 flavor = RPCSEC_GSS_KRB5P;
2860 else {
2861 if (cp)
2862 *cp = savedc;
2863 syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2864 return (1);
2865 }
2866 if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2867 if (cp)
2868 *cp = savedc;
2869 syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2870 return (1);
2871 }
2872 ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2873 ep->ex_numsecflavors++;
2874 if (cp) {
2875 *cp = savedc;
2876 seclist = cp + 1;
2877 } else {
2878 break;
2879 }
2880 }
2881 return (0);
2882 }
2883
2884 /*
2885 * Parse the option string and update fields.
2886 * Option arguments may either be -<option>=<value> or
2887 * -<option> <value>
2888 */
2889 static int
do_opt(char ** cpp,char ** endcpp,struct exportlist * ep,struct grouplist * grp,int * has_hostp,uint64_t * exflagsp,struct expcred * cr,char * unvis_dir)2890 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2891 int *has_hostp, uint64_t *exflagsp, struct expcred *cr, char *unvis_dir)
2892 {
2893 char *cpoptarg, *cpoptend;
2894 char *cp, *endcp, *cpopt, savedc, savedc2;
2895 int allflag, usedarg, fnd_equal;
2896
2897 savedc2 = '\0';
2898 cpopt = *cpp;
2899 cpopt++;
2900 cp = *endcpp;
2901 savedc = *cp;
2902 *cp = '\0';
2903 while (cpopt && *cpopt) {
2904 allflag = 1;
2905 usedarg = -2;
2906 fnd_equal = 0;
2907 if ((cpoptend = strchr(cpopt, ','))) {
2908 *cpoptend++ = '\0';
2909 if ((cpoptarg = strchr(cpopt, '='))) {
2910 *cpoptarg++ = '\0';
2911 fnd_equal = 1;
2912 }
2913 } else {
2914 if ((cpoptarg = strchr(cpopt, '='))) {
2915 *cpoptarg++ = '\0';
2916 fnd_equal = 1;
2917 } else {
2918 *cp = savedc;
2919 nextfield(&cp, &endcp);
2920 **endcpp = '\0';
2921 if (endcp > cp && *cp != '-') {
2922 cpoptarg = cp;
2923 savedc2 = *endcp;
2924 *endcp = '\0';
2925 usedarg = 0;
2926 }
2927 }
2928 }
2929 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2930 if (fnd_equal == 1) {
2931 syslog(LOG_ERR, "= after op: %s", cpopt);
2932 return (1);
2933 }
2934 *exflagsp |= MNT_EXRDONLY;
2935 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2936 !(allflag = strcmp(cpopt, "mapall")) ||
2937 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2938 usedarg++;
2939 parsecred(cpoptarg, cr);
2940 if (allflag == 0) {
2941 *exflagsp |= MNT_EXPORTANON;
2942 opt_flags |= OP_MAPALL;
2943 } else
2944 opt_flags |= OP_MAPROOT;
2945 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2946 !strcmp(cpopt, "m"))) {
2947 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2948 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2949 return (1);
2950 }
2951 usedarg++;
2952 opt_flags |= OP_MASK;
2953 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
2954 !strcmp(cpopt, "n"))) {
2955 if (strchr(cpoptarg, '/') != NULL) {
2956 if (debug)
2957 fprintf(stderr, "setting OP_MASKLEN\n");
2958 opt_flags |= OP_MASKLEN;
2959 }
2960 if (grp->gr_type != GT_NULL) {
2961 syslog(LOG_ERR, "network/host conflict");
2962 return (1);
2963 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2964 syslog(LOG_ERR, "bad net: %s", cpoptarg);
2965 return (1);
2966 }
2967 grp->gr_type = GT_NET;
2968 *has_hostp = 1;
2969 usedarg++;
2970 opt_flags |= OP_NET;
2971 } else if (!strcmp(cpopt, "alldirs")) {
2972 if (fnd_equal == 1) {
2973 syslog(LOG_ERR, "= after op: %s", cpopt);
2974 return (1);
2975 }
2976 if ((opt_flags & OP_NOTROOT) != 0) {
2977 syslog(LOG_ERR, "%s: path %s not mount point",
2978 cpopt, unvis_dir);
2979 if (alldirs_fail != 0)
2980 return (1);
2981 }
2982 opt_flags |= OP_ALLDIRS;
2983 } else if (!strcmp(cpopt, "public")) {
2984 if (fnd_equal == 1) {
2985 syslog(LOG_ERR, "= after op: %s", cpopt);
2986 return (1);
2987 }
2988 *exflagsp |= MNT_EXPUBLIC;
2989 } else if (!strcmp(cpopt, "webnfs")) {
2990 if (fnd_equal == 1) {
2991 syslog(LOG_ERR, "= after op: %s", cpopt);
2992 return (1);
2993 }
2994 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2995 opt_flags |= OP_MAPALL;
2996 } else if (cpoptarg && !strcmp(cpopt, "index")) {
2997 ep->ex_indexfile = strdup(cpoptarg);
2998 } else if (!strcmp(cpopt, "quiet")) {
2999 if (fnd_equal == 1) {
3000 syslog(LOG_ERR, "= after op: %s", cpopt);
3001 return (1);
3002 }
3003 opt_flags |= OP_QUIET;
3004 } else if (cpoptarg && !strcmp(cpopt, "sec")) {
3005 if (parsesec(cpoptarg, ep))
3006 return (1);
3007 opt_flags |= OP_SEC;
3008 usedarg++;
3009 } else if (!strcmp(cpopt, "tls")) {
3010 if (fnd_equal == 1) {
3011 syslog(LOG_ERR, "= after op: %s", cpopt);
3012 return (1);
3013 }
3014 *exflagsp |= MNT_EXTLS;
3015 } else if (!strcmp(cpopt, "tlscert")) {
3016 if (fnd_equal == 1) {
3017 syslog(LOG_ERR, "= after op: %s", cpopt);
3018 return (1);
3019 }
3020 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT);
3021 } else if (!strcmp(cpopt, "tlscertuser")) {
3022 if (fnd_equal == 1) {
3023 syslog(LOG_ERR, "= after op: %s", cpopt);
3024 return (1);
3025 }
3026 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT |
3027 MNT_EXTLSCERTUSER);
3028 } else {
3029 syslog(LOG_ERR, "bad opt %s", cpopt);
3030 return (1);
3031 }
3032 if (usedarg >= 0) {
3033 *endcp = savedc2;
3034 **endcpp = savedc;
3035 if (usedarg > 0) {
3036 *cpp = cp;
3037 *endcpp = endcp;
3038 }
3039 return (0);
3040 }
3041 cpopt = cpoptend;
3042 }
3043 **endcpp = savedc;
3044 return (0);
3045 }
3046
3047 /*
3048 * Translate a character string to the corresponding list of network
3049 * addresses for a hostname.
3050 */
3051 static int
get_host(char * cp,struct grouplist * grp,struct grouplist * tgrp)3052 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
3053 {
3054 struct grouplist *checkgrp;
3055 struct addrinfo *ai, *tai, hints;
3056 int ecode;
3057 char host[NI_MAXHOST];
3058
3059 if (grp->gr_type != GT_NULL) {
3060 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
3061 return (1);
3062 }
3063 memset(&hints, 0, sizeof hints);
3064 hints.ai_flags = AI_CANONNAME;
3065 hints.ai_protocol = IPPROTO_UDP;
3066 ecode = getaddrinfo(cp, NULL, &hints, &ai);
3067 if (ecode != 0) {
3068 syslog(LOG_ERR,"can't get address info for host %s", cp);
3069 return 1;
3070 }
3071 grp->gr_ptr.gt_addrinfo = ai;
3072 while (ai != NULL) {
3073 if (ai->ai_canonname == NULL) {
3074 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
3075 sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
3076 strlcpy(host, "?", sizeof(host));
3077 ai->ai_canonname = strdup(host);
3078 ai->ai_flags |= AI_CANONNAME;
3079 }
3080 if (debug)
3081 fprintf(stderr, "got host %s\n", ai->ai_canonname);
3082 /*
3083 * Sanity check: make sure we don't already have an entry
3084 * for this host in the grouplist.
3085 */
3086 for (checkgrp = tgrp; checkgrp != NULL;
3087 checkgrp = checkgrp->gr_next) {
3088 if (checkgrp->gr_type != GT_HOST)
3089 continue;
3090 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
3091 tai = tai->ai_next) {
3092 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
3093 continue;
3094 if (debug)
3095 fprintf(stderr,
3096 "ignoring duplicate host %s\n",
3097 ai->ai_canonname);
3098 grp->gr_type = GT_IGNORE;
3099 return (0);
3100 }
3101 }
3102 ai = ai->ai_next;
3103 }
3104 grp->gr_type = GT_HOST;
3105 return (0);
3106 }
3107
3108 /*
3109 * Free up an exports list component
3110 */
3111 static void
free_exp(struct exportlist * ep)3112 free_exp(struct exportlist *ep)
3113 {
3114 struct grouplist *grp, *tgrp;
3115
3116 if (ep->ex_defdir) {
3117 free_host(ep->ex_defdir->dp_hosts);
3118 free((caddr_t)ep->ex_defdir);
3119 }
3120 if (ep->ex_fsdir)
3121 free(ep->ex_fsdir);
3122 if (ep->ex_indexfile)
3123 free(ep->ex_indexfile);
3124 free_dir(ep->ex_dirl);
3125 grp = ep->ex_grphead;
3126 while (grp) {
3127 tgrp = grp;
3128 grp = grp->gr_next;
3129 free_grp(tgrp);
3130 }
3131 if (ep->ex_defanon.cr_groups != ep->ex_defanon.cr_smallgrps)
3132 free(ep->ex_defanon.cr_groups);
3133 free((caddr_t)ep);
3134 }
3135
3136 /*
3137 * Free up the v4root exports.
3138 */
3139 static void
free_v4rootexp(void)3140 free_v4rootexp(void)
3141 {
3142
3143 if (v4root_ep != NULL) {
3144 free_exp(v4root_ep);
3145 v4root_ep = NULL;
3146 }
3147 }
3148
3149 /*
3150 * Free hosts.
3151 */
3152 static void
free_host(struct hostlist * hp)3153 free_host(struct hostlist *hp)
3154 {
3155 struct hostlist *hp2;
3156
3157 while (hp) {
3158 hp2 = hp;
3159 hp = hp->ht_next;
3160 free((caddr_t)hp2);
3161 }
3162 }
3163
3164 static struct hostlist *
get_ht(void)3165 get_ht(void)
3166 {
3167 struct hostlist *hp;
3168
3169 hp = (struct hostlist *)malloc(sizeof (struct hostlist));
3170 if (hp == (struct hostlist *)NULL)
3171 out_of_mem();
3172 hp->ht_next = (struct hostlist *)NULL;
3173 hp->ht_flag = 0;
3174 return (hp);
3175 }
3176
3177 /*
3178 * Out of memory, fatal
3179 */
3180 static void
out_of_mem(void)3181 out_of_mem(void)
3182 {
3183
3184 syslog(LOG_ERR, "out of memory");
3185 exit(2);
3186 }
3187
3188 /*
3189 * Call do_mount() from the struct exportlist, for each case needed.
3190 */
3191 static int
do_export_mount(struct exportlist * ep,struct statfs * fsp)3192 do_export_mount(struct exportlist *ep, struct statfs *fsp)
3193 {
3194 struct grouplist *grp, defgrp;
3195 int ret;
3196 size_t dirlen;
3197
3198 LOGDEBUG("do_export_mount=%s", ep->ex_fsdir);
3199 dirlen = strlen(ep->ex_fsdir);
3200 if ((ep->ex_flag & EX_DEFSET) != 0) {
3201 defgrp.gr_type = GT_DEFAULT;
3202 defgrp.gr_next = NULL;
3203 /* We have an entry for all other hosts/nets. */
3204 LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags);
3205 ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon,
3206 ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors,
3207 ep->ex_defsecflavors);
3208 if (ret != 0)
3209 return (ret);
3210 }
3211
3212 /* Do a mount for each group. */
3213 grp = ep->ex_grphead;
3214 while (grp != NULL) {
3215 LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx",
3216 grp->gr_type, (uintmax_t)grp->gr_exflags);
3217 ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon,
3218 ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors,
3219 grp->gr_secflavors);
3220 if (ret != 0)
3221 return (ret);
3222 grp = grp->gr_next;
3223 }
3224 return (0);
3225 }
3226
3227 /*
3228 * Do the nmount() syscall with the update flag to push the export info into
3229 * the kernel.
3230 */
3231 static int
do_mount(struct exportlist * ep,struct grouplist * grp,uint64_t exflags,struct expcred * anoncrp,char * dirp,int dirplen,struct statfs * fsb,int numsecflavors,int * secflavors)3232 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags,
3233 struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb,
3234 int numsecflavors, int *secflavors)
3235 {
3236 struct statfs fsb1;
3237 struct addrinfo *ai;
3238 struct export_args *eap;
3239 char errmsg[255];
3240 char *cp;
3241 int done;
3242 char savedc;
3243 struct iovec *iov;
3244 int i, iovlen;
3245 int ret;
3246 struct nfsex_args nfsea;
3247
3248 eap = &nfsea.export;
3249
3250 cp = NULL;
3251 savedc = '\0';
3252 iov = NULL;
3253 iovlen = 0;
3254 ret = 0;
3255
3256 bzero(eap, sizeof (struct export_args));
3257 bzero(errmsg, sizeof(errmsg));
3258 eap->ex_flags = exflags;
3259 eap->ex_uid = anoncrp->cr_uid;
3260 eap->ex_ngroups = anoncrp->cr_ngroups;
3261 /*
3262 * Use the memory pointed to by 'anoncrp', as it outlives 'eap' which is
3263 * local to this function.
3264 */
3265 eap->ex_groups = anoncrp->cr_groups;
3266 LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags);
3267 eap->ex_indexfile = ep->ex_indexfile;
3268 if (grp->gr_type == GT_HOST)
3269 ai = grp->gr_ptr.gt_addrinfo;
3270 else
3271 ai = NULL;
3272 eap->ex_numsecflavors = numsecflavors;
3273 LOGDEBUG("do_mount numsec=%d", numsecflavors);
3274 for (i = 0; i < eap->ex_numsecflavors; i++)
3275 eap->ex_secflavors[i] = secflavors[i];
3276 if (eap->ex_numsecflavors == 0) {
3277 eap->ex_numsecflavors = 1;
3278 eap->ex_secflavors[0] = AUTH_SYS;
3279 }
3280 done = FALSE;
3281
3282 if (v4root_phase == 0) {
3283 build_iovec(&iov, &iovlen, "fstype", NULL, 0);
3284 build_iovec(&iov, &iovlen, "fspath", NULL, 0);
3285 build_iovec(&iov, &iovlen, "from", NULL, 0);
3286 build_iovec(&iov, &iovlen, "update", NULL, 0);
3287 build_iovec(&iov, &iovlen, "export", eap,
3288 sizeof (struct export_args));
3289 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
3290 }
3291
3292 while (!done) {
3293 switch (grp->gr_type) {
3294 case GT_HOST:
3295 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
3296 goto skip;
3297 eap->ex_addr = ai->ai_addr;
3298 eap->ex_addrlen = ai->ai_addrlen;
3299 eap->ex_masklen = 0;
3300 break;
3301 case GT_NET:
3302 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
3303 have_v6 == 0)
3304 goto skip;
3305 eap->ex_addr =
3306 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
3307 eap->ex_addrlen =
3308 ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
3309 eap->ex_mask =
3310 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
3311 eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
3312 break;
3313 case GT_DEFAULT:
3314 eap->ex_addr = NULL;
3315 eap->ex_addrlen = 0;
3316 eap->ex_mask = NULL;
3317 eap->ex_masklen = 0;
3318 break;
3319 case GT_IGNORE:
3320 ret = 0;
3321 goto error_exit;
3322 break;
3323 default:
3324 syslog(LOG_ERR, "bad grouptype");
3325 if (cp)
3326 *cp = savedc;
3327 ret = 1;
3328 goto error_exit;
3329 }
3330
3331 /*
3332 * For V4:, use the nfssvc() syscall, instead of mount().
3333 */
3334 if (v4root_phase == 2) {
3335 nfsea.fspec = v4root_dirpath;
3336 if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT,
3337 (caddr_t)&nfsea) < 0) {
3338 syslog(LOG_ERR, "Exporting V4: failed");
3339 ret = 2;
3340 goto error_exit;
3341 }
3342 } else {
3343 /*
3344 * XXX:
3345 * Maybe I should just use the fsb->f_mntonname path
3346 * instead of looping back up the dirp to the mount
3347 * point??
3348 * Also, needs to know how to export all types of local
3349 * exportable filesystems and not just "ufs".
3350 */
3351 iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
3352 iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
3353 iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
3354 iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
3355 iov[5].iov_base = fsb->f_mntfromname; /* "from" */
3356 iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
3357 errmsg[0] = '\0';
3358
3359 while (nmount(iov, iovlen, fsb->f_flags) < 0) {
3360 if (cp)
3361 *cp-- = savedc;
3362 else
3363 cp = dirp + dirplen - 1;
3364 if (opt_flags & OP_QUIET) {
3365 ret = 1;
3366 goto error_exit;
3367 }
3368 if (errno == EPERM) {
3369 if (debug)
3370 warnx("can't change attributes for %s: %s",
3371 dirp, errmsg);
3372 syslog(LOG_ERR,
3373 "can't change attributes for %s: %s",
3374 dirp, errmsg);
3375 ret = 1;
3376 goto error_exit;
3377 }
3378 if ((opt_flags & OP_ALLDIRS) &&
3379 alldirs_fail != 0) {
3380 if (errno == EINVAL)
3381 syslog(LOG_ERR,
3382 "-alldirs requested but %s is not a filesystem mountpoint",
3383 dirp);
3384 else
3385 syslog(LOG_ERR,
3386 "could not remount %s: %m",
3387 dirp);
3388 ret = 1;
3389 goto error_exit;
3390 }
3391 /* back up over the last component */
3392 while (cp > dirp && *cp == '/')
3393 cp--;
3394 while (cp > dirp && *(cp - 1) != '/')
3395 cp--;
3396 if (cp == dirp) {
3397 if (debug)
3398 warnx("mnt unsucc");
3399 syslog(LOG_ERR, "can't export %s %s",
3400 dirp, errmsg);
3401 ret = 1;
3402 goto error_exit;
3403 }
3404 savedc = *cp;
3405 *cp = '\0';
3406 /*
3407 * Check that we're still on the same
3408 * filesystem.
3409 */
3410 if (statfs(dirp, &fsb1) != 0 ||
3411 fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) {
3412 *cp = savedc;
3413 syslog(LOG_ERR,
3414 "can't export %s %s", dirp,
3415 errmsg);
3416 ret = 1;
3417 goto error_exit;
3418 }
3419 }
3420 }
3421
3422 /*
3423 * For the experimental server:
3424 * If this is the public directory, get the file handle
3425 * and load it into the kernel via the nfssvc() syscall.
3426 */
3427 if ((exflags & MNT_EXPUBLIC) != 0) {
3428 fhandle_t fh;
3429 char *public_name;
3430
3431 if (eap->ex_indexfile != NULL)
3432 public_name = eap->ex_indexfile;
3433 else
3434 public_name = dirp;
3435 if (getfh(public_name, &fh) < 0)
3436 syslog(LOG_ERR,
3437 "Can't get public fh for %s", public_name);
3438 else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
3439 syslog(LOG_ERR,
3440 "Can't set public fh for %s", public_name);
3441 else {
3442 has_publicfh = 1;
3443 has_set_publicfh = 1;
3444 ep->ex_flag |= EX_PUBLICFH;
3445 }
3446 }
3447 skip:
3448 if (ai != NULL)
3449 ai = ai->ai_next;
3450 if (ai == NULL)
3451 done = TRUE;
3452 }
3453 if (cp)
3454 *cp = savedc;
3455 error_exit:
3456 /* free strings allocated by strdup() in getmntopts.c */
3457 if (iov != NULL) {
3458 free(iov[0].iov_base); /* fstype */
3459 free(iov[2].iov_base); /* fspath */
3460 free(iov[4].iov_base); /* from */
3461 free(iov[6].iov_base); /* update */
3462 free(iov[8].iov_base); /* export */
3463 free(iov[10].iov_base); /* errmsg */
3464
3465 /* free iov, allocated by realloc() */
3466 free(iov);
3467 }
3468 return (ret);
3469 }
3470
3471 /*
3472 * Translate a net address.
3473 *
3474 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
3475 */
3476 static int
get_net(char * cp,struct netmsk * net,int maskflg)3477 get_net(char *cp, struct netmsk *net, int maskflg)
3478 {
3479 struct netent *np = NULL;
3480 char *name, *p, *prefp;
3481 struct sockaddr_in sin;
3482 struct sockaddr *sa = NULL;
3483 struct addrinfo hints, *ai = NULL;
3484 char netname[NI_MAXHOST];
3485 long preflen;
3486
3487 p = prefp = NULL;
3488 if ((opt_flags & OP_MASKLEN) && !maskflg) {
3489 p = strchr(cp, '/');
3490 *p = '\0';
3491 prefp = p + 1;
3492 }
3493
3494 /*
3495 * Check for a numeric address first. We wish to avoid
3496 * possible DNS lookups in getnetbyname().
3497 */
3498 if (isxdigit(*cp) || *cp == ':') {
3499 memset(&hints, 0, sizeof hints);
3500 /* Ensure the mask and the network have the same family. */
3501 if (maskflg && (opt_flags & OP_NET))
3502 hints.ai_family = net->nt_net.ss_family;
3503 else if (!maskflg && (opt_flags & OP_HAVEMASK))
3504 hints.ai_family = net->nt_mask.ss_family;
3505 else
3506 hints.ai_family = AF_UNSPEC;
3507 hints.ai_flags = AI_NUMERICHOST;
3508 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
3509 sa = ai->ai_addr;
3510 if (sa != NULL && ai->ai_family == AF_INET) {
3511 /*
3512 * The address in `cp' is really a network address, so
3513 * use inet_network() to re-interpret this correctly.
3514 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
3515 */
3516 bzero(&sin, sizeof sin);
3517 sin.sin_family = AF_INET;
3518 sin.sin_len = sizeof sin;
3519 sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
3520 if (debug)
3521 fprintf(stderr, "get_net: v4 addr %s\n",
3522 inet_ntoa(sin.sin_addr));
3523 sa = (struct sockaddr *)&sin;
3524 }
3525 }
3526 if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
3527 bzero(&sin, sizeof sin);
3528 sin.sin_family = AF_INET;
3529 sin.sin_len = sizeof sin;
3530 sin.sin_addr = inet_makeaddr(np->n_net, 0);
3531 sa = (struct sockaddr *)&sin;
3532 }
3533 if (sa == NULL)
3534 goto fail;
3535
3536 if (maskflg) {
3537 /* The specified sockaddr is a mask. */
3538 if (checkmask(sa) != 0)
3539 goto fail;
3540 bcopy(sa, &net->nt_mask, sa->sa_len);
3541 opt_flags |= OP_HAVEMASK;
3542 opt_flags &= ~OP_CLASSMASK;
3543 } else {
3544 /* The specified sockaddr is a network address. */
3545 bcopy(sa, &net->nt_net, sa->sa_len);
3546
3547 /* Get a network name for the export list. */
3548 if (np) {
3549 name = np->n_name;
3550 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
3551 NULL, 0, NI_NUMERICHOST) == 0) {
3552 name = netname;
3553 } else {
3554 goto fail;
3555 }
3556 if ((net->nt_name = strdup(name)) == NULL)
3557 out_of_mem();
3558
3559 /*
3560 * Extract a mask from either a "/<masklen>" suffix, or
3561 * from the class of an IPv4 address.
3562 */
3563 if (opt_flags & OP_MASKLEN) {
3564 preflen = strtol(prefp, NULL, 10);
3565 if (preflen < 0L || preflen == LONG_MAX)
3566 goto fail;
3567 bcopy(sa, &net->nt_mask, sa->sa_len);
3568 if (makemask(&net->nt_mask, (int)preflen) != 0)
3569 goto fail;
3570 opt_flags |= OP_HAVEMASK;
3571 *p = '/';
3572 } else if (sa->sa_family == AF_INET &&
3573 (opt_flags & OP_MASK) == 0) {
3574 in_addr_t addr;
3575
3576 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
3577 if (IN_CLASSA(addr))
3578 preflen = 8;
3579 else if (IN_CLASSB(addr))
3580 preflen = 16;
3581 else if (IN_CLASSC(addr))
3582 preflen = 24;
3583 else if (IN_CLASSD(addr)) /* XXX Multicast??? */
3584 preflen = 28;
3585 else
3586 preflen = 32; /* XXX */
3587
3588 bcopy(sa, &net->nt_mask, sa->sa_len);
3589 makemask(&net->nt_mask, (int)preflen);
3590 opt_flags |= OP_HAVEMASK | OP_CLASSMASK;
3591 }
3592 }
3593
3594 if (ai)
3595 freeaddrinfo(ai);
3596 return 0;
3597
3598 fail:
3599 if (ai)
3600 freeaddrinfo(ai);
3601 return 1;
3602 }
3603
3604 /*
3605 * Parse out the next white space separated field
3606 */
3607 static void
nextfield(char ** cp,char ** endcp)3608 nextfield(char **cp, char **endcp)
3609 {
3610 char *p;
3611 char quot = 0;
3612
3613 p = *cp;
3614 while (*p == ' ' || *p == '\t')
3615 p++;
3616 *cp = p;
3617 while (*p != '\0') {
3618 if (quot) {
3619 if (*p == quot)
3620 quot = 0;
3621 } else {
3622 if (*p == '\\' && *(p + 1) != '\0')
3623 p++;
3624 else if (*p == '\'' || *p == '"')
3625 quot = *p;
3626 else if (*p == ' ' || *p == '\t')
3627 break;
3628 }
3629 p++;
3630 };
3631 *endcp = p;
3632 }
3633
3634 /*
3635 * Get an exports file line. Skip over blank lines and handle line
3636 * continuations.
3637 */
3638 static int
get_line(void)3639 get_line(void)
3640 {
3641 char *p, *cp;
3642 size_t len;
3643 int totlen, cont_line;
3644
3645 /*
3646 * Loop around ignoring blank lines and getting all continuation lines.
3647 */
3648 p = line;
3649 totlen = 0;
3650 do {
3651 if ((p = fgetln(exp_file, &len)) == NULL)
3652 return (0);
3653 cp = p + len - 1;
3654 cont_line = 0;
3655 while (cp >= p &&
3656 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
3657 if (*cp == '\\')
3658 cont_line = 1;
3659 cp--;
3660 len--;
3661 }
3662 if (cont_line) {
3663 *++cp = ' ';
3664 len++;
3665 }
3666 if (linesize < len + totlen + 1) {
3667 linesize = len + totlen + 1;
3668 line = realloc(line, linesize);
3669 if (line == NULL)
3670 out_of_mem();
3671 }
3672 memcpy(line + totlen, p, len);
3673 totlen += len;
3674 line[totlen] = '\0';
3675 } while (totlen == 0 || cont_line);
3676 return (1);
3677 }
3678
3679 /*
3680 * Parse a description of a credential.
3681 */
3682 static void
parsecred(char * names,struct expcred * cr)3683 parsecred(char *names, struct expcred *cr)
3684 {
3685 char *name;
3686 struct passwd *pw;
3687 unsigned long name_ul;
3688 char *end = NULL;
3689
3690 assert(cr->cr_groups == tmp_groups);
3691
3692 /*
3693 * Parse the user and if possible get its password table entry.
3694 * 'cr_uid' is filled when exiting this block.
3695 */
3696 name = strsep_quote(&names, ":");
3697 name_ul = strtoul(name, &end, 10);
3698 if (*end != '\0' || end == name)
3699 pw = getpwnam(name);
3700 else
3701 pw = getpwuid((uid_t)name_ul);
3702 if (pw != NULL) {
3703 cr->cr_uid = pw->pw_uid;
3704 } else if (*end != '\0' || end == name) {
3705 syslog(LOG_ERR, "unknown user: %s", name);
3706 cr->cr_uid = UID_NOBODY;
3707 goto nogroup;
3708 } else {
3709 cr->cr_uid = name_ul;
3710 }
3711
3712 /*
3713 * Credentials specified as those of a user (i.e., use its associated
3714 * groups as specified in the password database).
3715 */
3716 if (names == NULL) {
3717 if (pw == NULL) {
3718 syslog(LOG_ERR, "no passwd entry for user: %s, "
3719 "can't determine groups", name);
3720 goto nogroup;
3721 }
3722
3723 cr->cr_ngroups = tngroups_max;
3724 if (getgrouplist(pw->pw_name, pw->pw_gid,
3725 cr->cr_groups, &cr->cr_ngroups) != 0) {
3726 syslog(LOG_ERR, "too many groups");
3727 cr->cr_ngroups = tngroups_max;
3728 }
3729 return;
3730 }
3731
3732 /*
3733 * Explicit credentials specified as a colon separated list:
3734 * uid:gid:gid:...
3735 */
3736 cr->cr_ngroups = 0;
3737 while (names != NULL && *names != '\0') {
3738 const struct group *gr;
3739 gid_t group;
3740
3741 name = strsep_quote(&names, ":");
3742 name_ul = strtoul(name, &end, 10);
3743 if (*end != '\0' || end == name) {
3744 if ((gr = getgrnam(name)) == NULL) {
3745 syslog(LOG_ERR, "unknown group: %s", name);
3746 continue;
3747 }
3748 group = gr->gr_gid;
3749 } else {
3750 group = name_ul;
3751 }
3752 if (cr->cr_ngroups == tngroups_max) {
3753 syslog(LOG_ERR, "too many groups");
3754 break;
3755 }
3756 cr->cr_groups[cr->cr_ngroups++] = group;
3757 }
3758 if (cr->cr_ngroups == 0)
3759 goto nogroup;
3760 return;
3761
3762 nogroup:
3763 cr->cr_ngroups = 1;
3764 cr->cr_groups[0] = nogroup();
3765 }
3766
3767 #define STRSIZ (MNTNAMLEN+MNTPATHLEN+50)
3768 /*
3769 * Routines that maintain the remote mounttab
3770 */
3771 static void
get_mountlist(void)3772 get_mountlist(void)
3773 {
3774 struct mountlist *mlp;
3775 char *host, *dirp, *cp;
3776 char str[STRSIZ];
3777 FILE *mlfile;
3778
3779 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3780 if (errno == ENOENT)
3781 return;
3782 else {
3783 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3784 return;
3785 }
3786 }
3787 while (fgets(str, STRSIZ, mlfile) != NULL) {
3788 cp = str;
3789 host = strsep(&cp, " \t\n");
3790 dirp = strsep(&cp, " \t\n");
3791 if (host == NULL || dirp == NULL)
3792 continue;
3793 mlp = (struct mountlist *)malloc(sizeof (*mlp));
3794 if (mlp == (struct mountlist *)NULL)
3795 out_of_mem();
3796 strncpy(mlp->ml_host, host, MNTNAMLEN);
3797 mlp->ml_host[MNTNAMLEN] = '\0';
3798 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3799 mlp->ml_dirp[MNTPATHLEN] = '\0';
3800
3801 SLIST_INSERT_HEAD(&mlhead, mlp, next);
3802 }
3803 fclose(mlfile);
3804 }
3805
3806 static void
del_mlist(char * hostp,char * dirp)3807 del_mlist(char *hostp, char *dirp)
3808 {
3809 struct mountlist *mlp, *mlp2;
3810 FILE *mlfile;
3811 int fnd = 0;
3812
3813 SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3814 if (!strcmp(mlp->ml_host, hostp) &&
3815 (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3816 fnd = 1;
3817 SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3818 free((caddr_t)mlp);
3819 }
3820 }
3821 if (fnd) {
3822 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3823 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3824 return;
3825 }
3826 SLIST_FOREACH(mlp, &mlhead, next) {
3827 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3828 }
3829 fclose(mlfile);
3830 }
3831 }
3832
3833 static void
add_mlist(char * hostp,char * dirp)3834 add_mlist(char *hostp, char *dirp)
3835 {
3836 struct mountlist *mlp;
3837 FILE *mlfile;
3838
3839 SLIST_FOREACH(mlp, &mlhead, next) {
3840 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3841 return;
3842 }
3843
3844 mlp = (struct mountlist *)malloc(sizeof (*mlp));
3845 if (mlp == (struct mountlist *)NULL)
3846 out_of_mem();
3847 strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3848 mlp->ml_host[MNTNAMLEN] = '\0';
3849 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3850 mlp->ml_dirp[MNTPATHLEN] = '\0';
3851 SLIST_INSERT_HEAD(&mlhead, mlp, next);
3852 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3853 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3854 return;
3855 }
3856 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3857 fclose(mlfile);
3858 }
3859
3860 /*
3861 * Free up a group list.
3862 */
3863 static void
free_grp(struct grouplist * grp)3864 free_grp(struct grouplist *grp)
3865 {
3866 if (grp->gr_type == GT_HOST) {
3867 if (grp->gr_ptr.gt_addrinfo != NULL)
3868 freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3869 } else if (grp->gr_type == GT_NET) {
3870 if (grp->gr_ptr.gt_net.nt_name)
3871 free(grp->gr_ptr.gt_net.nt_name);
3872 }
3873 if (grp->gr_anon.cr_groups != grp->gr_anon.cr_smallgrps)
3874 free(grp->gr_anon.cr_groups);
3875 free((caddr_t)grp);
3876 }
3877
3878 #ifdef DEBUG
3879 static void
SYSLOG(int pri,const char * fmt,...)3880 SYSLOG(int pri, const char *fmt, ...)
3881 {
3882 va_list ap;
3883
3884 va_start(ap, fmt);
3885 vfprintf(stderr, fmt, ap);
3886 va_end(ap);
3887 }
3888 #endif /* DEBUG */
3889
3890 /*
3891 * Check options for consistency.
3892 */
3893 static int
check_options(struct dirlist * dp)3894 check_options(struct dirlist *dp)
3895 {
3896
3897 if (v4root_phase == 0 && dp == NULL)
3898 return (1);
3899 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3900 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3901 return (1);
3902 }
3903 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3904 syslog(LOG_ERR, "-mask requires -network");
3905 return (1);
3906 }
3907 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3908 syslog(LOG_ERR, "-network requires mask specification");
3909 return (1);
3910 }
3911 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3912 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3913 return (1);
3914 }
3915 if (v4root_phase > 0 &&
3916 (opt_flags &
3917 ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3918 syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3919 return (1);
3920 }
3921 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3922 syslog(LOG_ERR, "-alldirs has multiple directories");
3923 return (1);
3924 }
3925 return (0);
3926 }
3927
3928 static int
check_path_component(const char * path,char ** err)3929 check_path_component(const char *path, char **err)
3930 {
3931 struct stat sb;
3932
3933 if (lstat(path, &sb)) {
3934 asprintf(err, "%s: lstat() failed: %s.\n",
3935 path, strerror(errno));
3936 return (0);
3937 }
3938
3939 switch (sb.st_mode & S_IFMT) {
3940 case S_IFDIR:
3941 return (1);
3942 case S_IFLNK:
3943 asprintf(err, "%s: path is a symbolic link.\n", path);
3944 break;
3945 case S_IFREG:
3946 asprintf(err, "%s: path is a file rather than a directory.\n",
3947 path);
3948 break;
3949 default:
3950 asprintf(err, "%s: path is not a directory.\n", path);
3951 }
3952
3953 return (0);
3954 }
3955
3956 /*
3957 * Check each path component for the presence of symbolic links. Return true
3958 */
3959 static int
check_dirpath(char * dirp,char ** err)3960 check_dirpath(char *dirp, char **err)
3961 {
3962 char *cp;
3963
3964 cp = dirp + 1;
3965 while (*cp) {
3966 if (*cp == '/') {
3967 *cp = '\0';
3968
3969 if (!check_path_component(dirp, err)) {
3970 *cp = '/';
3971 return (0);
3972 }
3973
3974 *cp = '/';
3975 }
3976 cp++;
3977 }
3978
3979 if (!check_path_component(dirp, err))
3980 return (0);
3981
3982 return (1);
3983 }
3984
3985 /*
3986 * Populate statfs information. Return true on success.
3987 */
3988 static int
check_statfs(const char * dirp,struct statfs * fsb,char ** err)3989 check_statfs(const char *dirp, struct statfs *fsb, char **err)
3990 {
3991 if (statfs(dirp, fsb)) {
3992 asprintf(err, "%s: statfs() failed: %s\n", dirp,
3993 strerror(errno));
3994 return (0);
3995 }
3996
3997 return (1);
3998 }
3999
4000 /*
4001 * Make a netmask according to the specified prefix length. The ss_family
4002 * and other non-address fields must be initialised before calling this.
4003 */
4004 static int
makemask(struct sockaddr_storage * ssp,int bitlen)4005 makemask(struct sockaddr_storage *ssp, int bitlen)
4006 {
4007 u_char *p;
4008 int bits, i, len;
4009
4010 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
4011 return (-1);
4012 if (bitlen > len * CHAR_BIT)
4013 return (-1);
4014
4015 for (i = 0; i < len; i++) {
4016 bits = MIN(CHAR_BIT, bitlen);
4017 *p++ = (u_char)~0 << (CHAR_BIT - bits);
4018 bitlen -= bits;
4019 }
4020 return 0;
4021 }
4022
4023 /*
4024 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
4025 * is acceptable (i.e. of the form 1...10....0).
4026 */
4027 static int
checkmask(struct sockaddr * sa)4028 checkmask(struct sockaddr *sa)
4029 {
4030 u_char *mask;
4031 int i, len;
4032
4033 if ((mask = sa_rawaddr(sa, &len)) == NULL)
4034 return (-1);
4035
4036 for (i = 0; i < len; i++)
4037 if (mask[i] != 0xff)
4038 break;
4039 if (i < len) {
4040 if (~mask[i] & (u_char)(~mask[i] + 1))
4041 return (-1);
4042 i++;
4043 }
4044 for (; i < len; i++)
4045 if (mask[i] != 0)
4046 return (-1);
4047 return (0);
4048 }
4049
4050 /*
4051 * Compare two sockaddrs according to a specified mask. Return zero if
4052 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
4053 * If samask is NULL, perform a full comparison.
4054 */
4055 static int
sacmp(struct sockaddr * sa1,struct sockaddr * sa2,struct sockaddr * samask)4056 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
4057 {
4058 unsigned char *p1, *p2, *mask;
4059 int len, i;
4060
4061 if (sa1->sa_family != sa2->sa_family ||
4062 (p1 = sa_rawaddr(sa1, &len)) == NULL ||
4063 (p2 = sa_rawaddr(sa2, NULL)) == NULL)
4064 return (1);
4065
4066 switch (sa1->sa_family) {
4067 case AF_INET6:
4068 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
4069 ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
4070 return (1);
4071 break;
4072 }
4073
4074 /* Simple binary comparison if no mask specified. */
4075 if (samask == NULL)
4076 return (memcmp(p1, p2, len));
4077
4078 /* Set up the mask, and do a mask-based comparison. */
4079 if (sa1->sa_family != samask->sa_family ||
4080 (mask = sa_rawaddr(samask, NULL)) == NULL)
4081 return (1);
4082
4083 for (i = 0; i < len; i++)
4084 if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
4085 return (1);
4086 return (0);
4087 }
4088
4089 /*
4090 * Return a pointer to the part of the sockaddr that contains the
4091 * raw address, and set *nbytes to its length in bytes. Returns
4092 * NULL if the address family is unknown.
4093 */
4094 static void *
sa_rawaddr(struct sockaddr * sa,int * nbytes)4095 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
4096 void *p;
4097 int len;
4098
4099 switch (sa->sa_family) {
4100 case AF_INET:
4101 len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
4102 p = &((struct sockaddr_in *)sa)->sin_addr;
4103 break;
4104 case AF_INET6:
4105 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
4106 p = &((struct sockaddr_in6 *)sa)->sin6_addr;
4107 break;
4108 default:
4109 p = NULL;
4110 len = 0;
4111 }
4112
4113 if (nbytes != NULL)
4114 *nbytes = len;
4115 return (p);
4116 }
4117
4118 static void
huphandler(int sig __unused)4119 huphandler(int sig __unused)
4120 {
4121
4122 got_sighup = 1;
4123 }
4124
4125 static void
terminate(int sig __unused)4126 terminate(int sig __unused)
4127 {
4128 free(tmp_groups);
4129 pidfile_remove(pfh);
4130 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
4131 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
4132 exit (0);
4133 }
4134
4135 static void
cp_cred(struct expcred * outcr,struct expcred * incr)4136 cp_cred(struct expcred *outcr, struct expcred *incr)
4137 {
4138
4139 outcr->cr_uid = incr->cr_uid;
4140 outcr->cr_ngroups = incr->cr_ngroups;
4141 if (outcr->cr_ngroups > SMALLNGROUPS)
4142 outcr->cr_groups = malloc(outcr->cr_ngroups * sizeof(gid_t));
4143 else
4144 outcr->cr_groups = outcr->cr_smallgrps;
4145 memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups *
4146 sizeof(gid_t));
4147 }
4148
4149 static gid_t
nogroup()4150 nogroup()
4151 {
4152 static gid_t nogroup = 0; /* 0 means unset. */
4153
4154 if (nogroup == 0) {
4155 const struct group *gr = getgrnam("nogroup");
4156
4157 if (gr != NULL && gr->gr_gid != 0)
4158 nogroup = gr->gr_gid;
4159 else
4160 nogroup = GID_NOGROUP;
4161 }
4162 return (nogroup);
4163 }
4164