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