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