mountd.c revision 1.107 1 /* $NetBSD: mountd.c,v 1.107 2006/07/13 12:00:26 martin Exp $ */
2
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
36 /*
37 * XXX The ISO support can't possibly work..
38 */
39
40 #include <sys/cdefs.h>
41 #ifndef lint
42 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
43 The Regents of the University of California. All rights reserved.\n");
44 #endif /* not lint */
45
46 #ifndef lint
47 #if 0
48 static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
49 #else
50 __RCSID("$NetBSD: mountd.c,v 1.107 2006/07/13 12:00:26 martin Exp $");
51 #endif
52 #endif /* not lint */
53
54 #include <sys/param.h>
55 #include <sys/file.h>
56 #include <sys/ioctl.h>
57 #include <sys/mount.h>
58 #include <sys/socket.h>
59 #include <sys/stat.h>
60 #include <syslog.h>
61 #include <sys/ucred.h>
62
63 #include <rpc/rpc.h>
64 #include <rpc/pmap_clnt.h>
65 #include <rpc/pmap_prot.h>
66 #include <rpcsvc/mount.h>
67 #ifdef ISO
68 #include <netiso/iso.h>
69 #endif
70 #include <nfs/rpcv2.h>
71 #include <nfs/nfsproto.h>
72 #include <nfs/nfs.h>
73 #include <nfs/nfsmount.h>
74
75 #include <arpa/inet.h>
76
77 #include <ctype.h>
78 #include <errno.h>
79 #include <grp.h>
80 #include <netdb.h>
81 #include <pwd.h>
82 #include <netgroup.h>
83 #include <signal.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include <netgroup.h>
89 #include <err.h>
90 #include <util.h>
91 #include "pathnames.h"
92
93 #ifdef IPSEC
94 #include <netinet6/ipsec.h>
95 #ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */
96 #undef IPSEC
97 #endif
98 #include "ipsec.h"
99 #endif
100
101 #include <stdarg.h>
102
103 /*
104 * Structures for keeping the mount list and export list
105 */
106 struct mountlist {
107 struct mountlist *ml_next;
108 char ml_host[RPCMNT_NAMELEN + 1];
109 char ml_dirp[RPCMNT_PATHLEN + 1];
110 int ml_flag;/* XXX more flags (same as dp_flag) */
111 };
112
113 struct dirlist {
114 struct dirlist *dp_left;
115 struct dirlist *dp_right;
116 int dp_flag;
117 struct hostlist *dp_hosts; /* List of hosts this dir exported to */
118 char dp_dirp[1]; /* Actually malloc'd to size of dir */
119 };
120 /* dp_flag bits */
121 #define DP_DEFSET 0x1
122 #define DP_HOSTSET 0x2
123 #define DP_KERB 0x4
124 #define DP_NORESMNT 0x8
125
126 struct exportlist {
127 struct exportlist *ex_next;
128 struct dirlist *ex_dirl;
129 struct dirlist *ex_defdir;
130 int ex_flag;
131 fsid_t ex_fs;
132 char *ex_fsdir;
133 char *ex_indexfile;
134 };
135 /* ex_flag bits */
136 #define EX_LINKED 0x1
137
138 struct netmsk {
139 struct sockaddr_storage nt_net;
140 int nt_len;
141 char *nt_name;
142 };
143
144 union grouptypes {
145 struct addrinfo *gt_addrinfo;
146 struct netmsk gt_net;
147 #ifdef ISO
148 struct sockaddr_iso *gt_isoaddr;
149 #endif
150 };
151
152 struct grouplist {
153 int gr_type;
154 union grouptypes gr_ptr;
155 struct grouplist *gr_next;
156 };
157 /* Group types */
158 #define GT_NULL 0x0
159 #define GT_HOST 0x1
160 #define GT_NET 0x2
161 #define GT_ISO 0x4
162
163 struct hostlist {
164 int ht_flag;/* Uses DP_xx bits */
165 struct grouplist *ht_grp;
166 struct hostlist *ht_next;
167 };
168
169 struct fhreturn {
170 int fhr_flag;
171 int fhr_vers;
172 nfsfh_t fhr_fh;
173 };
174
175 /* Global defs */
176 static char *add_expdir __P((struct dirlist **, char *, int));
177 static void add_dlist __P((struct dirlist **, struct dirlist *,
178 struct grouplist *, int));
179 static void add_mlist __P((char *, char *, int));
180 static int check_dirpath __P((const char *, size_t, char *));
181 static int check_options __P((const char *, size_t, struct dirlist *));
182 static int chk_host __P((struct dirlist *, struct sockaddr *, int *, int *));
183 static int del_mlist __P((char *, char *, struct sockaddr *));
184 static struct dirlist *dirp_search __P((struct dirlist *, char *));
185 static int do_nfssvc __P((const char *, size_t, struct exportlist *,
186 struct grouplist *, int, struct uucred *, char *, int, struct statvfs *));
187 static int do_opt __P((const char *, size_t, char **, char **,
188 struct exportlist *, struct grouplist *, int *, int *, struct uucred *));
189 static struct exportlist *ex_search __P((fsid_t *));
190 static int parse_directory __P((const char *, size_t, struct grouplist *,
191 int, char *, struct exportlist **, struct statvfs *));
192 static int parse_host_netgroup __P((const char *, size_t, struct exportlist *,
193 struct grouplist *, char *, int *, struct grouplist **));
194 static struct exportlist *get_exp __P((void));
195 static void free_dir __P((struct dirlist *));
196 static void free_exp __P((struct exportlist *));
197 static void free_grp __P((struct grouplist *));
198 static void free_host __P((struct hostlist *));
199 static void get_exportlist __P((int));
200 static int get_host __P((const char *, size_t, const char *,
201 struct grouplist *));
202 static struct hostlist *get_ht __P((void));
203 static void get_mountlist __P((void));
204 static int get_net __P((char *, struct netmsk *, int));
205 static void free_exp_grp __P((struct exportlist *, struct grouplist *));
206 static struct grouplist *get_grp __P((void));
207 static void hang_dirp __P((struct dirlist *, struct grouplist *,
208 struct exportlist *, int));
209 static void mntsrv __P((struct svc_req *, SVCXPRT *));
210 static void nextfield __P((char **, char **));
211 static void parsecred __P((char *, struct uucred *));
212 static int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
213 static int scan_tree __P((struct dirlist *, struct sockaddr *));
214 static void send_umntall __P((int));
215 static int umntall_each __P((caddr_t, struct sockaddr_in *));
216 static int xdr_dir __P((XDR *, char *));
217 static int xdr_explist __P((XDR *, caddr_t));
218 static int xdr_fhs __P((XDR *, caddr_t));
219 static int xdr_mlist __P((XDR *, caddr_t));
220 static void *emalloc __P((size_t));
221 static char *estrdup __P((const char *));
222 static int bitcmp __P((void *, void *, int));
223 static int netpartcmp __P((struct sockaddr *, struct sockaddr *, int));
224 static int sacmp __P((struct sockaddr *, struct sockaddr *));
225 static int allones __P((struct sockaddr_storage *, int));
226 static int countones __P((struct sockaddr *));
227 #ifdef ISO
228 static int get_isoaddr __P((const char *, size_t, char *, struct grouplist *));
229 #endif
230 static void bind_resv_port __P((int, sa_family_t, in_port_t));
231 static struct exportlist *exphead;
232 static struct mountlist *mlhead;
233 static struct grouplist *grphead;
234 static char *exname;
235 static struct uucred def_anon = {
236 1,
237 (uid_t) -2,
238 (gid_t) -2,
239 0,
240 {}
241 };
242
243 static int opt_flags;
244 static int have_v6 = 1;
245 static const int ninumeric = NI_NUMERICHOST;
246
247 /* Bits for above */
248 #define OP_MAPROOT 0x001
249 #define OP_MAPALL 0x002
250 #define OP_KERB 0x004
251 #define OP_MASK 0x008
252 #define OP_NET 0x010
253 #define OP_ISO 0x020
254 #define OP_ALLDIRS 0x040
255 #define OP_NORESPORT 0x080
256 #define OP_NORESMNT 0x100
257 #define OP_MASKLEN 0x200
258
259 static int debug = 0;
260 #if 0
261 static void SYSLOG __P((int, const char *,...));
262 #endif
263 int main __P((int, char *[]));
264
265 /*
266 * If this is non-zero, -noresvport and -noresvmnt are implied for
267 * each export.
268 */
269 static int noprivports;
270
271 /*
272 * Mountd server for NFS mount protocol as described in:
273 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
274 * The optional arguments are the exports file name
275 * default: _PATH_EXPORTS
276 * "-d" to enable debugging
277 * and "-n" to allow nonroot mount.
278 */
279 int
280 main(argc, argv)
281 int argc;
282 char **argv;
283 {
284 SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
285 struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
286 int udpsock, tcpsock, udp6sock, tcp6sock;
287 int xcreated = 0, s;
288 int c, one = 1;
289 int maxrec = RPC_MAXDATASIZE;
290 in_port_t forcedport = 0;
291 #ifdef IPSEC
292 char *policy = NULL;
293 #define ADDOPTS "P:"
294 #else
295 #define ADDOPTS
296 #endif
297
298 while ((c = getopt(argc, argv, "dNnrp:" ADDOPTS)) != -1)
299 switch (c) {
300 #ifdef IPSEC
301 case 'P':
302 if (ipsecsetup_test(policy = optarg))
303 errx(1, "Invalid ipsec policy `%s'", policy);
304 break;
305 #endif
306 case 'p':
307 /* A forced port "0" will dynamically allocate a port */
308 forcedport = atoi(optarg);
309 break;
310 case 'd':
311 debug = 1;
312 break;
313 case 'N':
314 noprivports = 1;
315 break;
316 /* Compatibility */
317 case 'n':
318 case 'r':
319 break;
320 default:
321 fprintf(stderr, "usage: %s [-dNn]"
322 #ifdef IPSEC
323 " [-P policy]"
324 #endif
325 " [-p port] [exportsfile]\n", getprogname());
326 exit(1);
327 };
328 argc -= optind;
329 argv += optind;
330 grphead = NULL;
331 exphead = NULL;
332 mlhead = NULL;
333 if (argc == 1)
334 exname = *argv;
335 else
336 exname = _PATH_EXPORTS;
337 openlog("mountd", LOG_PID | (debug ? LOG_PERROR : 0), LOG_DAEMON);
338
339 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
340 if (s < 0)
341 have_v6 = 0;
342 else
343 close(s);
344
345 if (debug)
346 (void)fprintf(stderr, "Getting export list.\n");
347 get_exportlist(0);
348 if (debug)
349 (void)fprintf(stderr, "Getting mount list.\n");
350 get_mountlist();
351 if (debug)
352 (void)fprintf(stderr, "Here we go.\n");
353 if (debug == 0) {
354 daemon(0, 0);
355 (void)signal(SIGINT, SIG_IGN);
356 (void)signal(SIGQUIT, SIG_IGN);
357 }
358 (void)signal(SIGHUP, get_exportlist);
359 (void)signal(SIGTERM, send_umntall);
360 pidfile(NULL);
361
362 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
363 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
364
365 udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
366 tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
367 udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
368 tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
369
370 /*
371 * We're doing host-based access checks here, so don't allow
372 * v4-in-v6 to confuse things. The kernel will disable it
373 * by default on NFS sockets too.
374 */
375 if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
376 IPV6_V6ONLY, &one, sizeof one) < 0){
377 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
378 exit(1);
379 }
380 if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
381 IPV6_V6ONLY, &one, sizeof one) < 0){
382 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
383 exit(1);
384 }
385
386 udpconf = getnetconfigent("udp");
387 tcpconf = getnetconfigent("tcp");
388 udp6conf = getnetconfigent("udp6");
389 tcp6conf = getnetconfigent("tcp6");
390
391 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
392
393 if (udpsock != -1 && udpconf != NULL) {
394 bind_resv_port(udpsock, AF_INET, forcedport);
395 #ifdef IPSEC
396 if (policy)
397 ipsecsetup(AF_INET, udpsock, policy);
398 #endif
399 udptransp = svc_dg_create(udpsock, 0, 0);
400 if (udptransp != NULL) {
401 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
402 mntsrv, udpconf) ||
403 !svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
404 mntsrv, udpconf))
405 syslog(LOG_WARNING, "can't register UDP service");
406 else
407 xcreated++;
408 } else
409 syslog(LOG_WARNING, "can't create UDP service");
410
411 }
412
413 if (tcpsock != -1 && tcpconf != NULL) {
414 bind_resv_port(tcpsock, AF_INET, forcedport);
415 #ifdef IPSEC
416 if (policy)
417 ipsecsetup(AF_INET, tcpsock, policy);
418 #endif
419 listen(tcpsock, SOMAXCONN);
420 tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE,
421 RPC_MAXDATASIZE);
422 if (tcptransp != NULL) {
423 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
424 mntsrv, tcpconf) ||
425 !svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
426 mntsrv, tcpconf))
427 syslog(LOG_WARNING, "can't register TCP service");
428 else
429 xcreated++;
430 } else
431 syslog(LOG_WARNING, "can't create TCP service");
432
433 }
434
435 if (udp6sock != -1 && udp6conf != NULL) {
436 bind_resv_port(udp6sock, AF_INET6, forcedport);
437 #ifdef IPSEC
438 if (policy)
439 ipsecsetup(AF_INET6, tcpsock, policy);
440 #endif
441 udp6transp = svc_dg_create(udp6sock, 0, 0);
442 if (udp6transp != NULL) {
443 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
444 mntsrv, udp6conf) ||
445 !svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
446 mntsrv, udp6conf))
447 syslog(LOG_WARNING, "can't register UDP6 service");
448 else
449 xcreated++;
450 } else
451 syslog(LOG_WARNING, "can't create UDP6 service");
452
453 }
454
455 if (tcp6sock != -1 && tcp6conf != NULL) {
456 bind_resv_port(tcp6sock, AF_INET6, forcedport);
457 #ifdef IPSEC
458 if (policy)
459 ipsecsetup(AF_INET6, tcpsock, policy);
460 #endif
461 listen(tcp6sock, SOMAXCONN);
462 tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE,
463 RPC_MAXDATASIZE);
464 if (tcp6transp != NULL) {
465 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
466 mntsrv, tcp6conf) ||
467 !svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
468 mntsrv, tcp6conf))
469 syslog(LOG_WARNING, "can't register TCP6 service");
470 else
471 xcreated++;
472 } else
473 syslog(LOG_WARNING, "can't create TCP6 service");
474
475 }
476
477 if (xcreated == 0) {
478 syslog(LOG_ERR, "could not create any services");
479 exit(1);
480 }
481
482 svc_run();
483 syslog(LOG_ERR, "Mountd died");
484 exit(1);
485 }
486
487 /*
488 * The mount rpc service
489 */
490 void
491 mntsrv(rqstp, transp)
492 struct svc_req *rqstp;
493 SVCXPRT *transp;
494 {
495 struct exportlist *ep;
496 struct dirlist *dp;
497 struct fhreturn fhr;
498 struct stat stb;
499 struct statvfs fsb;
500 struct addrinfo *ai;
501 char host[NI_MAXHOST], numerichost[NI_MAXHOST];
502 int lookup_failed = 1;
503 struct sockaddr *saddr;
504 u_short sport;
505 char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
506 long bad = EACCES;
507 int defset, hostset, ret;
508 sigset_t sighup_mask;
509 struct sockaddr_in6 *sin6;
510 struct sockaddr_in *sin;
511 size_t fh_size;
512
513 (void)sigemptyset(&sighup_mask);
514 (void)sigaddset(&sighup_mask, SIGHUP);
515 saddr = svc_getrpccaller(transp)->buf;
516 switch (saddr->sa_family) {
517 case AF_INET6:
518 sin6 = (struct sockaddr_in6 *)saddr;
519 sport = ntohs(sin6->sin6_port);
520 break;
521 case AF_INET:
522 sin = (struct sockaddr_in *)saddr;
523 sport = ntohs(sin->sin_port);
524 break;
525 default:
526 syslog(LOG_ERR, "request from unknown address family");
527 return;
528 }
529 lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
530 NULL, 0, 0);
531 if (getnameinfo(saddr, saddr->sa_len, numerichost,
532 sizeof numerichost, NULL, 0, ninumeric) != 0)
533 strlcpy(numerichost, "?", sizeof(numerichost));
534 ai = NULL;
535 ret = 0;
536 switch (rqstp->rq_proc) {
537 case NULLPROC:
538 if (!svc_sendreply(transp, xdr_void, NULL))
539 syslog(LOG_ERR, "Can't send reply");
540 return;
541 case MOUNTPROC_MNT:
542 if (debug)
543 fprintf(stderr,
544 "got mount request from %s\n", numerichost);
545 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
546 if (debug)
547 fprintf(stderr, "-> garbage args\n");
548 svcerr_decode(transp);
549 return;
550 }
551 if (debug)
552 fprintf(stderr,
553 "-> rpcpath: %s\n", rpcpath);
554 /*
555 * Get the real pathname and make sure it is a file or
556 * directory that exists.
557 */
558 if (realpath(rpcpath, dirpath) == 0 ||
559 stat(dirpath, &stb) < 0 ||
560 (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
561 statvfs(dirpath, &fsb) < 0) {
562 (void)chdir("/"); /* Just in case realpath doesn't */
563 if (debug)
564 (void)fprintf(stderr, "-> stat failed on %s\n",
565 dirpath);
566 if (!svc_sendreply(transp, xdr_long, (caddr_t) &bad))
567 syslog(LOG_ERR, "Can't send reply");
568 return;
569 }
570 if (debug)
571 fprintf(stderr,
572 "-> dirpath: %s\n", dirpath);
573 /* Check in the exports list */
574 (void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
575 ep = ex_search(&fsb.f_fsidx);
576 hostset = defset = 0;
577 if (ep && (chk_host(ep->ex_defdir, saddr, &defset,
578 &hostset) || ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
579 chk_host(dp, saddr, &defset, &hostset)) ||
580 (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
581 scan_tree(ep->ex_dirl, saddr) == 0))) {
582 if ((hostset & DP_HOSTSET) == 0) {
583 hostset = defset;
584 }
585 if (sport >= IPPORT_RESERVED &&
586 !(hostset & DP_NORESMNT)) {
587 syslog(LOG_NOTICE,
588 "Refused mount RPC from host %s port %d",
589 numerichost, sport);
590 svcerr_weakauth(transp);
591 goto out;
592 }
593 fhr.fhr_flag = hostset;
594 fhr.fhr_vers = rqstp->rq_vers;
595 /* Get the file handle */
596 (void)memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
597 fh_size = sizeof(nfsfh_t);
598 if (getfh(dirpath, (fhandle_t *) &fhr.fhr_fh, &fh_size) < 0) {
599 bad = errno;
600 syslog(LOG_ERR, "Can't get fh for %s", dirpath);
601 if (!svc_sendreply(transp, xdr_long,
602 (char *)&bad))
603 syslog(LOG_ERR, "Can't send reply");
604 goto out;
605 }
606 if (!svc_sendreply(transp, xdr_fhs, (char *) &fhr))
607 syslog(LOG_ERR, "Can't send reply");
608 if (!lookup_failed)
609 add_mlist(host, dirpath, hostset);
610 else
611 add_mlist(numerichost, dirpath, hostset);
612 if (debug)
613 (void)fprintf(stderr, "Mount successful.\n");
614 } else {
615 if (!svc_sendreply(transp, xdr_long, (caddr_t) &bad))
616 syslog(LOG_ERR, "Can't send reply");
617 }
618 out:
619 (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
620 return;
621 case MOUNTPROC_DUMP:
622 if (!svc_sendreply(transp, xdr_mlist, NULL))
623 syslog(LOG_ERR, "Can't send reply");
624 return;
625 case MOUNTPROC_UMNT:
626 if (!svc_getargs(transp, xdr_dir, dirpath)) {
627 svcerr_decode(transp);
628 return;
629 }
630 if (!lookup_failed)
631 ret = del_mlist(host, dirpath, saddr);
632 ret |= del_mlist(numerichost, dirpath, saddr);
633 if (ret) {
634 svcerr_weakauth(transp);
635 return;
636 }
637 if (!svc_sendreply(transp, xdr_void, NULL))
638 syslog(LOG_ERR, "Can't send reply");
639 return;
640 case MOUNTPROC_UMNTALL:
641 if (!lookup_failed)
642 ret = del_mlist(host, NULL, saddr);
643 ret |= del_mlist(numerichost, NULL, saddr);
644 if (ret) {
645 svcerr_weakauth(transp);
646 return;
647 }
648 if (!svc_sendreply(transp, xdr_void, NULL))
649 syslog(LOG_ERR, "Can't send reply");
650 return;
651 case MOUNTPROC_EXPORT:
652 case MOUNTPROC_EXPORTALL:
653 if (!svc_sendreply(transp, xdr_explist, NULL))
654 syslog(LOG_ERR, "Can't send reply");
655 return;
656
657
658 default:
659 svcerr_noproc(transp);
660 return;
661 }
662 }
663
664 /*
665 * Xdr conversion for a dirpath string
666 */
667 static int
668 xdr_dir(xdrsp, dirp)
669 XDR *xdrsp;
670 char *dirp;
671 {
672
673 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
674 }
675
676 /*
677 * Xdr routine to generate file handle reply
678 */
679 static int
680 xdr_fhs(xdrsp, cp)
681 XDR *xdrsp;
682 caddr_t cp;
683 {
684 struct fhreturn *fhrp = (struct fhreturn *) cp;
685 long ok = 0, len, auth;
686
687 if (!xdr_long(xdrsp, &ok))
688 return (0);
689 switch (fhrp->fhr_vers) {
690 case 1:
691 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
692 case 3:
693 len = NFSX_V3FH;
694 if (!xdr_long(xdrsp, &len))
695 return (0);
696 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
697 return (0);
698 if (fhrp->fhr_flag & DP_KERB)
699 auth = RPCAUTH_KERB4;
700 else
701 auth = RPCAUTH_UNIX;
702 len = 1;
703 if (!xdr_long(xdrsp, &len))
704 return (0);
705 return (xdr_long(xdrsp, &auth));
706 };
707 return (0);
708 }
709
710 int
711 xdr_mlist(xdrsp, cp)
712 XDR *xdrsp;
713 caddr_t cp;
714 {
715 struct mountlist *mlp;
716 int true = 1;
717 int false = 0;
718 char *strp;
719
720 mlp = mlhead;
721 while (mlp) {
722 if (!xdr_bool(xdrsp, &true))
723 return (0);
724 strp = &mlp->ml_host[0];
725 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
726 return (0);
727 strp = &mlp->ml_dirp[0];
728 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
729 return (0);
730 mlp = mlp->ml_next;
731 }
732 if (!xdr_bool(xdrsp, &false))
733 return (0);
734 return (1);
735 }
736
737 /*
738 * Xdr conversion for export list
739 */
740 int
741 xdr_explist(xdrsp, cp)
742 XDR *xdrsp;
743 caddr_t cp;
744 {
745 struct exportlist *ep;
746 int false = 0;
747 int putdef;
748 sigset_t sighup_mask;
749
750 (void)sigemptyset(&sighup_mask);
751 (void)sigaddset(&sighup_mask, SIGHUP);
752 (void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
753 ep = exphead;
754 while (ep) {
755 putdef = 0;
756 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
757 goto errout;
758 if (ep->ex_defdir && putdef == 0 &&
759 put_exlist(ep->ex_defdir, xdrsp, NULL, &putdef))
760 goto errout;
761 ep = ep->ex_next;
762 }
763 (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
764 if (!xdr_bool(xdrsp, &false))
765 return (0);
766 return (1);
767 errout:
768 (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
769 return (0);
770 }
771
772 /*
773 * Called from xdr_explist() to traverse the tree and export the
774 * directory paths. Assumes SIGHUP has already been masked.
775 */
776 int
777 put_exlist(dp, xdrsp, adp, putdefp)
778 struct dirlist *dp;
779 XDR *xdrsp;
780 struct dirlist *adp;
781 int *putdefp;
782 {
783 struct grouplist *grp;
784 struct hostlist *hp;
785 int true = 1;
786 int false = 0;
787 int gotalldir = 0;
788 char *strp;
789
790 if (dp) {
791 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
792 return (1);
793 if (!xdr_bool(xdrsp, &true))
794 return (1);
795 strp = dp->dp_dirp;
796 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
797 return (1);
798 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
799 gotalldir = 1;
800 *putdefp = 1;
801 }
802 if ((dp->dp_flag & DP_DEFSET) == 0 &&
803 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
804 hp = dp->dp_hosts;
805 while (hp) {
806 grp = hp->ht_grp;
807 if (grp->gr_type == GT_HOST) {
808 if (!xdr_bool(xdrsp, &true))
809 return (1);
810 strp =
811 grp->gr_ptr.gt_addrinfo->ai_canonname;
812 if (!xdr_string(xdrsp, &strp,
813 RPCMNT_NAMELEN))
814 return (1);
815 } else if (grp->gr_type == GT_NET) {
816 if (!xdr_bool(xdrsp, &true))
817 return (1);
818 strp = grp->gr_ptr.gt_net.nt_name;
819 if (!xdr_string(xdrsp, &strp,
820 RPCMNT_NAMELEN))
821 return (1);
822 }
823 hp = hp->ht_next;
824 if (gotalldir && hp == NULL) {
825 hp = adp->dp_hosts;
826 gotalldir = 0;
827 }
828 }
829 }
830 if (!xdr_bool(xdrsp, &false))
831 return (1);
832 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
833 return (1);
834 }
835 return (0);
836 }
837
838 static int
839 parse_host_netgroup(line, lineno, ep, tgrp, cp, has_host, grp)
840 const char *line;
841 size_t lineno;
842 struct exportlist *ep;
843 struct grouplist *tgrp;
844 char *cp;
845 int *has_host;
846 struct grouplist **grp;
847 {
848 const char *hst, *usr, *dom;
849 int netgrp;
850
851 if (ep == NULL) {
852 syslog(LOG_ERR, "\"%s\", line %ld: No current export",
853 line, (unsigned long)lineno);
854 return 0;
855 }
856 setnetgrent(cp);
857 netgrp = getnetgrent(&hst, &usr, &dom);
858 do {
859 if (*has_host) {
860 (*grp)->gr_next = get_grp();
861 *grp = (*grp)->gr_next;
862 }
863 if (netgrp) {
864 if (hst == NULL) {
865 syslog(LOG_ERR,
866 "\"%s\", line %ld: No host in netgroup %s",
867 line, (unsigned long)lineno, cp);
868 goto bad;
869 }
870 if (get_host(line, lineno, hst, *grp))
871 goto bad;
872 } else if (get_host(line, lineno, cp, *grp))
873 goto bad;
874 *has_host = TRUE;
875 } while (netgrp && getnetgrent(&hst, &usr, &dom));
876
877 endnetgrent();
878 return 1;
879 bad:
880 endnetgrent();
881 return 0;
882
883 }
884
885 static int
886 parse_directory(line, lineno, tgrp, got_nondir, cp, ep, fsp)
887 const char *line;
888 size_t lineno;
889 struct grouplist *tgrp;
890 int got_nondir;
891 char *cp;
892 struct exportlist **ep;
893 struct statvfs *fsp;
894 {
895 if (!check_dirpath(line, lineno, cp))
896 return 0;
897
898 if (statvfs(cp, fsp) == -1) {
899 syslog(LOG_ERR, "\"%s\", line %ld: statvfs for `%s' failed: %m",
900 line, (unsigned long)lineno, cp);
901 return 0;
902 }
903
904 if (got_nondir) {
905 syslog(LOG_ERR,
906 "\"%s\", line %ld: Directories must precede files",
907 line, (unsigned long)lineno);
908 return 0;
909 }
910 if (*ep) {
911 if ((*ep)->ex_fs.__fsid_val[0] != fsp->f_fsidx.__fsid_val[0] ||
912 (*ep)->ex_fs.__fsid_val[1] != fsp->f_fsidx.__fsid_val[1]) {
913 syslog(LOG_ERR,
914 "\"%s\", line %ld: filesystem ids disagree",
915 line, (unsigned long)lineno);
916 return 0;
917 }
918 } else {
919 /*
920 * See if this directory is already
921 * in the list.
922 */
923 *ep = ex_search(&fsp->f_fsidx);
924 if (*ep == NULL) {
925 *ep = get_exp();
926 (*ep)->ex_fs = fsp->f_fsidx;
927 (*ep)->ex_fsdir = estrdup(fsp->f_mntonname);
928 if (debug)
929 (void)fprintf(stderr,
930 "Making new ep fs=0x%x,0x%x\n",
931 fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
932 } else {
933 if (debug)
934 (void)fprintf(stderr,
935 "Found ep fs=0x%x,0x%x\n",
936 fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
937 }
938 }
939
940 return 1;
941 }
942
943
944 /*
945 * Get the export list
946 */
947 /* ARGSUSED */
948 void
949 get_exportlist(n)
950 int n;
951 {
952 struct exportlist *ep, *ep2;
953 struct grouplist *grp, *tgrp;
954 struct exportlist **epp;
955 struct dirlist *dirhead;
956 struct statvfs fsb, *fsp;
957 struct addrinfo *ai;
958 struct uucred anon;
959 char *cp, *endcp, *dirp, savedc;
960 int has_host, exflags, got_nondir, dirplen, num, i;
961 FILE *exp_file;
962 char *line;
963 size_t lineno = 0, len;
964
965
966 /*
967 * First, get rid of the old list
968 */
969 ep = exphead;
970 while (ep) {
971 ep2 = ep;
972 ep = ep->ex_next;
973 free_exp(ep2);
974 }
975 exphead = NULL;
976
977 dirp = NULL;
978 dirplen = 0;
979 grp = grphead;
980 while (grp) {
981 tgrp = grp;
982 grp = grp->gr_next;
983 free_grp(tgrp);
984 }
985 grphead = NULL;
986
987 /*
988 * And delete exports that are in the kernel for all local
989 * file systems.
990 */
991 num = getmntinfo(&fsp, MNT_NOWAIT);
992 for (i = 0; i < num; i++) {
993 struct mountd_exports_list mel;
994
995 /* Delete all entries from the export list. */
996 mel.mel_path = fsp->f_mntonname;
997 mel.mel_nexports = 0;
998 if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) == -1 &&
999 errno != EOPNOTSUPP)
1000 syslog(LOG_ERR, "Can't delete exports for %s (%m)",
1001 fsp->f_mntonname);
1002
1003 fsp++;
1004 }
1005
1006 /*
1007 * Read in the exports file and build the list, calling
1008 * mount() as we go along to push the export rules into the kernel.
1009 */
1010 if ((exp_file = fopen(exname, "r")) == NULL) {
1011 /*
1012 * Don't exit here; we can still reload the config
1013 * after a SIGHUP.
1014 */
1015 if (debug)
1016 (void)fprintf(stderr, "Can't open %s: %s\n", exname,
1017 strerror(errno));
1018 return;
1019 }
1020 dirhead = NULL;
1021 while ((line = fparseln(exp_file, &len, &lineno, NULL, 0)) != NULL) {
1022 if (debug)
1023 (void)fprintf(stderr, "Got line %s\n", line);
1024 cp = line;
1025 nextfield(&cp, &endcp);
1026 if (cp == endcp)
1027 goto nextline; /* skip empty line */
1028 /*
1029 * Set defaults.
1030 */
1031 has_host = FALSE;
1032 anon = def_anon;
1033 exflags = MNT_EXPORTED;
1034 got_nondir = 0;
1035 opt_flags = 0;
1036 ep = NULL;
1037
1038 if (noprivports) {
1039 opt_flags |= OP_NORESMNT | OP_NORESPORT;
1040 exflags |= MNT_EXNORESPORT;
1041 }
1042
1043 /*
1044 * Create new exports list entry
1045 */
1046 len = endcp - cp;
1047 tgrp = grp = get_grp();
1048 while (len > 0) {
1049 if (len > RPCMNT_NAMELEN) {
1050 *endcp = '\0';
1051 syslog(LOG_ERR,
1052 "\"%s\", line %ld: name `%s' is too long",
1053 line, (unsigned long)lineno, cp);
1054 goto badline;
1055 }
1056 switch (*cp) {
1057 case '-':
1058 /*
1059 * Option
1060 */
1061 if (ep == NULL) {
1062 syslog(LOG_ERR,
1063 "\"%s\", line %ld: No current export list",
1064 line, (unsigned long)lineno);
1065 goto badline;
1066 }
1067 if (debug)
1068 (void)fprintf(stderr, "doing opt %s\n",
1069 cp);
1070 got_nondir = 1;
1071 if (do_opt(line, lineno, &cp, &endcp, ep, grp,
1072 &has_host, &exflags, &anon))
1073 goto badline;
1074 break;
1075
1076 case '/':
1077 /*
1078 * Directory
1079 */
1080 savedc = *endcp;
1081 *endcp = '\0';
1082
1083 if (!parse_directory(line, lineno, tgrp,
1084 got_nondir, cp, &ep, &fsb))
1085 goto badline;
1086 /*
1087 * Add dirpath to export mount point.
1088 */
1089 dirp = add_expdir(&dirhead, cp, len);
1090 dirplen = len;
1091
1092 *endcp = savedc;
1093 break;
1094
1095 default:
1096 /*
1097 * Host or netgroup.
1098 */
1099 savedc = *endcp;
1100 *endcp = '\0';
1101
1102 if (!parse_host_netgroup(line, lineno, ep,
1103 tgrp, cp, &has_host, &grp))
1104 goto badline;
1105
1106 got_nondir = 1;
1107
1108 *endcp = savedc;
1109 break;
1110 }
1111
1112 cp = endcp;
1113 nextfield(&cp, &endcp);
1114 len = endcp - cp;
1115 }
1116 if (check_options(line, lineno, dirhead))
1117 goto badline;
1118
1119 if (!has_host) {
1120 grp->gr_type = GT_HOST;
1121 if (debug)
1122 (void)fprintf(stderr,
1123 "Adding a default entry\n");
1124 /* add a default group and make the grp list NULL */
1125 ai = emalloc(sizeof(struct addrinfo));
1126 ai->ai_flags = 0;
1127 ai->ai_family = AF_INET; /* XXXX */
1128 ai->ai_socktype = SOCK_DGRAM;
1129 /* setting the length to 0 will match anything */
1130 ai->ai_addrlen = 0;
1131 ai->ai_flags = AI_CANONNAME;
1132 ai->ai_canonname = estrdup("Default");
1133 ai->ai_addr = NULL;
1134 ai->ai_next = NULL;
1135 grp->gr_ptr.gt_addrinfo = ai;
1136
1137 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1138 /*
1139 * Don't allow a network export coincide with a list of
1140 * host(s) on the same line.
1141 */
1142 syslog(LOG_ERR,
1143 "\"%s\", line %ld: Mixed exporting of networks and hosts is disallowed",
1144 line, (unsigned long)lineno);
1145 goto badline;
1146 }
1147 /*
1148 * Loop through hosts, pushing the exports into the kernel.
1149 * After loop, tgrp points to the start of the list and
1150 * grp points to the last entry in the list.
1151 */
1152 grp = tgrp;
1153 do {
1154 if (do_nfssvc(line, lineno, ep, grp, exflags, &anon,
1155 dirp, dirplen, &fsb))
1156 goto badline;
1157 } while (grp->gr_next && (grp = grp->gr_next));
1158
1159 /*
1160 * Success. Update the data structures.
1161 */
1162 if (has_host) {
1163 hang_dirp(dirhead, tgrp, ep, opt_flags);
1164 grp->gr_next = grphead;
1165 grphead = tgrp;
1166 } else {
1167 hang_dirp(dirhead, NULL, ep, opt_flags);
1168 free_grp(tgrp);
1169 }
1170 tgrp = NULL;
1171 dirhead = NULL;
1172 if ((ep->ex_flag & EX_LINKED) == 0) {
1173 ep2 = exphead;
1174 epp = &exphead;
1175
1176 /*
1177 * Insert in the list in alphabetical order.
1178 */
1179 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1180 epp = &ep2->ex_next;
1181 ep2 = ep2->ex_next;
1182 }
1183 if (ep2)
1184 ep->ex_next = ep2;
1185 *epp = ep;
1186 ep->ex_flag |= EX_LINKED;
1187 }
1188 goto nextline;
1189 badline:
1190 free_exp_grp(ep, grp);
1191 nextline:
1192 if (dirhead) {
1193 free_dir(dirhead);
1194 dirhead = NULL;
1195 }
1196 free(line);
1197 }
1198 (void)fclose(exp_file);
1199 }
1200
1201 /*
1202 * Allocate an export list element
1203 */
1204 static struct exportlist *
1205 get_exp()
1206 {
1207 struct exportlist *ep;
1208
1209 ep = emalloc(sizeof(struct exportlist));
1210 (void)memset(ep, 0, sizeof(struct exportlist));
1211 return (ep);
1212 }
1213
1214 /*
1215 * Allocate a group list element
1216 */
1217 static struct grouplist *
1218 get_grp()
1219 {
1220 struct grouplist *gp;
1221
1222 gp = emalloc(sizeof(struct grouplist));
1223 (void)memset(gp, 0, sizeof(struct grouplist));
1224 return (gp);
1225 }
1226
1227 /*
1228 * Clean up upon an error in get_exportlist().
1229 */
1230 static void
1231 free_exp_grp(ep, grp)
1232 struct exportlist *ep;
1233 struct grouplist *grp;
1234 {
1235 struct grouplist *tgrp;
1236
1237 if (ep && (ep->ex_flag & EX_LINKED) == 0)
1238 free_exp(ep);
1239 while (grp) {
1240 tgrp = grp;
1241 grp = grp->gr_next;
1242 free_grp(tgrp);
1243 }
1244 }
1245
1246 /*
1247 * Search the export list for a matching fs.
1248 */
1249 static struct exportlist *
1250 ex_search(fsid)
1251 fsid_t *fsid;
1252 {
1253 struct exportlist *ep;
1254
1255 ep = exphead;
1256 while (ep) {
1257 if (ep->ex_fs.__fsid_val[0] == fsid->__fsid_val[0] &&
1258 ep->ex_fs.__fsid_val[1] == fsid->__fsid_val[1])
1259 return (ep);
1260 ep = ep->ex_next;
1261 }
1262 return (ep);
1263 }
1264
1265 /*
1266 * Add a directory path to the list.
1267 */
1268 static char *
1269 add_expdir(dpp, cp, len)
1270 struct dirlist **dpp;
1271 char *cp;
1272 int len;
1273 {
1274 struct dirlist *dp;
1275
1276 dp = emalloc(sizeof(struct dirlist) + len);
1277 dp->dp_left = *dpp;
1278 dp->dp_right = NULL;
1279 dp->dp_flag = 0;
1280 dp->dp_hosts = NULL;
1281 (void)strcpy(dp->dp_dirp, cp);
1282 *dpp = dp;
1283 return (dp->dp_dirp);
1284 }
1285
1286 /*
1287 * Hang the dir list element off the dirpath binary tree as required
1288 * and update the entry for host.
1289 */
1290 void
1291 hang_dirp(dp, grp, ep, flags)
1292 struct dirlist *dp;
1293 struct grouplist *grp;
1294 struct exportlist *ep;
1295 int flags;
1296 {
1297 struct hostlist *hp;
1298 struct dirlist *dp2;
1299
1300 if (flags & OP_ALLDIRS) {
1301 if (ep->ex_defdir)
1302 free(dp);
1303 else
1304 ep->ex_defdir = dp;
1305 if (grp == NULL) {
1306 ep->ex_defdir->dp_flag |= DP_DEFSET;
1307 if (flags & OP_KERB)
1308 ep->ex_defdir->dp_flag |= DP_KERB;
1309 if (flags & OP_NORESMNT)
1310 ep->ex_defdir->dp_flag |= DP_NORESMNT;
1311 } else
1312 while (grp) {
1313 hp = get_ht();
1314 if (flags & OP_KERB)
1315 hp->ht_flag |= DP_KERB;
1316 if (flags & OP_NORESMNT)
1317 hp->ht_flag |= DP_NORESMNT;
1318 hp->ht_grp = grp;
1319 hp->ht_next = ep->ex_defdir->dp_hosts;
1320 ep->ex_defdir->dp_hosts = hp;
1321 grp = grp->gr_next;
1322 }
1323 } else {
1324
1325 /*
1326 * Loop through the directories adding them to the tree.
1327 */
1328 while (dp) {
1329 dp2 = dp->dp_left;
1330 add_dlist(&ep->ex_dirl, dp, grp, flags);
1331 dp = dp2;
1332 }
1333 }
1334 }
1335
1336 /*
1337 * Traverse the binary tree either updating a node that is already there
1338 * for the new directory or adding the new node.
1339 */
1340 static void
1341 add_dlist(dpp, newdp, grp, flags)
1342 struct dirlist **dpp;
1343 struct dirlist *newdp;
1344 struct grouplist *grp;
1345 int flags;
1346 {
1347 struct dirlist *dp;
1348 struct hostlist *hp;
1349 int cmp;
1350
1351 dp = *dpp;
1352 if (dp) {
1353 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1354 if (cmp > 0) {
1355 add_dlist(&dp->dp_left, newdp, grp, flags);
1356 return;
1357 } else if (cmp < 0) {
1358 add_dlist(&dp->dp_right, newdp, grp, flags);
1359 return;
1360 } else
1361 free(newdp);
1362 } else {
1363 dp = newdp;
1364 dp->dp_left = NULL;
1365 *dpp = dp;
1366 }
1367 if (grp) {
1368
1369 /*
1370 * Hang all of the host(s) off of the directory point.
1371 */
1372 do {
1373 hp = get_ht();
1374 if (flags & OP_KERB)
1375 hp->ht_flag |= DP_KERB;
1376 if (flags & OP_NORESMNT)
1377 hp->ht_flag |= DP_NORESMNT;
1378 hp->ht_grp = grp;
1379 hp->ht_next = dp->dp_hosts;
1380 dp->dp_hosts = hp;
1381 grp = grp->gr_next;
1382 } while (grp);
1383 } else {
1384 dp->dp_flag |= DP_DEFSET;
1385 if (flags & OP_KERB)
1386 dp->dp_flag |= DP_KERB;
1387 if (flags & OP_NORESMNT)
1388 dp->dp_flag |= DP_NORESMNT;
1389 }
1390 }
1391
1392 /*
1393 * Search for a dirpath on the export point.
1394 */
1395 static struct dirlist *
1396 dirp_search(dp, dirp)
1397 struct dirlist *dp;
1398 char *dirp;
1399 {
1400 int cmp;
1401
1402 if (dp) {
1403 cmp = strcmp(dp->dp_dirp, dirp);
1404 if (cmp > 0)
1405 return (dirp_search(dp->dp_left, dirp));
1406 else if (cmp < 0)
1407 return (dirp_search(dp->dp_right, dirp));
1408 else
1409 return (dp);
1410 }
1411 return (dp);
1412 }
1413
1414 /*
1415 * Some helper functions for netmasks. They all assume masks in network
1416 * order (big endian).
1417 */
1418 static int
1419 bitcmp(void *dst, void *src, int bitlen)
1420 {
1421 int i;
1422 u_int8_t *p1 = dst, *p2 = src;
1423 u_int8_t bitmask;
1424 int bytelen, bitsleft;
1425
1426 bytelen = bitlen / 8;
1427 bitsleft = bitlen % 8;
1428
1429 if (debug) {
1430 printf("comparing:\n");
1431 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
1432 printf("%02x", p1[i]);
1433 printf("\n");
1434 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
1435 printf("%02x", p2[i]);
1436 printf("\n");
1437 }
1438
1439 for (i = 0; i < bytelen; i++) {
1440 if (*p1 != *p2)
1441 return 1;
1442 p1++;
1443 p2++;
1444 }
1445
1446 for (i = 0; i < bitsleft; i++) {
1447 bitmask = 1 << (7 - i);
1448 if ((*p1 & bitmask) != (*p2 & bitmask))
1449 return 1;
1450 }
1451
1452 return 0;
1453 }
1454
1455 static int
1456 netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen)
1457 {
1458 void *src, *dst;
1459
1460 if (s1->sa_family != s2->sa_family)
1461 return 1;
1462
1463 switch (s1->sa_family) {
1464 case AF_INET:
1465 src = &((struct sockaddr_in *)s1)->sin_addr;
1466 dst = &((struct sockaddr_in *)s2)->sin_addr;
1467 if (bitlen > sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8)
1468 return 1;
1469 break;
1470 case AF_INET6:
1471 src = &((struct sockaddr_in6 *)s1)->sin6_addr;
1472 dst = &((struct sockaddr_in6 *)s2)->sin6_addr;
1473 if (((struct sockaddr_in6 *)s1)->sin6_scope_id !=
1474 ((struct sockaddr_in6 *)s2)->sin6_scope_id)
1475 return 1;
1476 if (bitlen > sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8)
1477 return 1;
1478 break;
1479 default:
1480 return 1;
1481 }
1482
1483 return bitcmp(src, dst, bitlen);
1484 }
1485
1486 static int
1487 allones(struct sockaddr_storage *ssp, int bitlen)
1488 {
1489 u_int8_t *p;
1490 int bytelen, bitsleft, i;
1491 int zerolen;
1492
1493 switch (ssp->ss_family) {
1494 case AF_INET:
1495 p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr;
1496 zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr);
1497 break;
1498 case AF_INET6:
1499 p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr;
1500 zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr);
1501 break;
1502 default:
1503 return -1;
1504 }
1505
1506 memset(p, 0, zerolen);
1507
1508 bytelen = bitlen / 8;
1509 bitsleft = bitlen % 8;
1510
1511 if (bytelen > zerolen)
1512 return -1;
1513
1514 for (i = 0; i < bytelen; i++)
1515 *p++ = 0xff;
1516
1517 for (i = 0; i < bitsleft; i++)
1518 *p |= 1 << (7 - i);
1519
1520 return 0;
1521 }
1522
1523 static int
1524 countones(struct sockaddr *sa)
1525 {
1526 void *mask;
1527 int i, bits = 0, bytelen;
1528 u_int8_t *p;
1529
1530 switch (sa->sa_family) {
1531 case AF_INET:
1532 mask = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr;
1533 bytelen = 4;
1534 break;
1535 case AF_INET6:
1536 mask = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr;
1537 bytelen = 16;
1538 break;
1539 default:
1540 return 0;
1541 }
1542
1543 p = mask;
1544
1545 for (i = 0; i < bytelen; i++, p++) {
1546 if (*p != 0xff) {
1547 for (bits = 0; bits < 8; bits++) {
1548 if (!(*p & (1 << (7 - bits))))
1549 break;
1550 }
1551 break;
1552 }
1553 }
1554
1555 return (i * 8 + bits);
1556 }
1557
1558 static int
1559 sacmp(struct sockaddr *sa1, struct sockaddr *sa2)
1560 {
1561 void *p1, *p2;
1562 int len;
1563
1564 if (sa1->sa_family != sa2->sa_family)
1565 return 1;
1566
1567 switch (sa1->sa_family) {
1568 case AF_INET:
1569 p1 = &((struct sockaddr_in *)sa1)->sin_addr;
1570 p2 = &((struct sockaddr_in *)sa2)->sin_addr;
1571 len = 4;
1572 break;
1573 case AF_INET6:
1574 p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
1575 p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
1576 len = 16;
1577 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
1578 ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
1579 return 1;
1580 break;
1581 default:
1582 return 1;
1583 }
1584
1585 return memcmp(p1, p2, len);
1586 }
1587
1588 /*
1589 * Scan for a host match in a directory tree.
1590 */
1591 static int
1592 chk_host(dp, saddr, defsetp, hostsetp)
1593 struct dirlist *dp;
1594 struct sockaddr *saddr;
1595 int *defsetp;
1596 int *hostsetp;
1597 {
1598 struct hostlist *hp;
1599 struct grouplist *grp;
1600 struct addrinfo *ai;
1601
1602 if (dp) {
1603 if (dp->dp_flag & DP_DEFSET)
1604 *defsetp = dp->dp_flag;
1605 hp = dp->dp_hosts;
1606 while (hp) {
1607 grp = hp->ht_grp;
1608 switch (grp->gr_type) {
1609 case GT_HOST:
1610 ai = grp->gr_ptr.gt_addrinfo;
1611 for (; ai; ai = ai->ai_next) {
1612 if (!sacmp(ai->ai_addr, saddr)) {
1613 *hostsetp =
1614 (hp->ht_flag | DP_HOSTSET);
1615 return (1);
1616 }
1617 }
1618 break;
1619 case GT_NET:
1620 if (!netpartcmp(saddr,
1621 (struct sockaddr *)
1622 &grp->gr_ptr.gt_net.nt_net,
1623 grp->gr_ptr.gt_net.nt_len)) {
1624 *hostsetp = (hp->ht_flag | DP_HOSTSET);
1625 return (1);
1626 }
1627 break;
1628 };
1629 hp = hp->ht_next;
1630 }
1631 }
1632 return (0);
1633 }
1634
1635 /*
1636 * Scan tree for a host that matches the address.
1637 */
1638 static int
1639 scan_tree(dp, saddr)
1640 struct dirlist *dp;
1641 struct sockaddr *saddr;
1642 {
1643 int defset, hostset;
1644
1645 if (dp) {
1646 if (scan_tree(dp->dp_left, saddr))
1647 return (1);
1648 if (chk_host(dp, saddr, &defset, &hostset))
1649 return (1);
1650 if (scan_tree(dp->dp_right, saddr))
1651 return (1);
1652 }
1653 return (0);
1654 }
1655
1656 /*
1657 * Traverse the dirlist tree and free it up.
1658 */
1659 static void
1660 free_dir(dp)
1661 struct dirlist *dp;
1662 {
1663
1664 if (dp) {
1665 free_dir(dp->dp_left);
1666 free_dir(dp->dp_right);
1667 free_host(dp->dp_hosts);
1668 free(dp);
1669 }
1670 }
1671
1672 /*
1673 * Parse the option string and update fields.
1674 * Option arguments may either be -<option>=<value> or
1675 * -<option> <value>
1676 */
1677 static int
1678 do_opt(line, lineno, cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1679 const char *line;
1680 size_t lineno;
1681 char **cpp, **endcpp;
1682 struct exportlist *ep;
1683 struct grouplist *grp;
1684 int *has_hostp;
1685 int *exflagsp;
1686 struct uucred *cr;
1687 {
1688 char *cpoptarg, *cpoptend;
1689 char *cp, *cpopt, savedc, savedc2;
1690 char *endcp = NULL; /* XXX: GCC */
1691 int allflag, usedarg;
1692
1693 cpopt = *cpp;
1694 cpopt++;
1695 cp = *endcpp;
1696 savedc = *cp;
1697 *cp = '\0';
1698 while (cpopt && *cpopt) {
1699 allflag = 1;
1700 usedarg = -2;
1701 savedc2 = '\0';
1702 if ((cpoptend = strchr(cpopt, ',')) != NULL) {
1703 *cpoptend++ = '\0';
1704 if ((cpoptarg = strchr(cpopt, '=')) != NULL)
1705 *cpoptarg++ = '\0';
1706 } else {
1707 if ((cpoptarg = strchr(cpopt, '=')) != NULL)
1708 *cpoptarg++ = '\0';
1709 else {
1710 *cp = savedc;
1711 nextfield(&cp, &endcp);
1712 **endcpp = '\0';
1713 if (endcp > cp && *cp != '-') {
1714 cpoptarg = cp;
1715 savedc2 = *endcp;
1716 *endcp = '\0';
1717 usedarg = 0;
1718 }
1719 }
1720 }
1721 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1722 *exflagsp |= MNT_EXRDONLY;
1723 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1724 !(allflag = strcmp(cpopt, "mapall")) ||
1725 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1726 usedarg++;
1727 parsecred(cpoptarg, cr);
1728 if (allflag == 0) {
1729 *exflagsp |= MNT_EXPORTANON;
1730 opt_flags |= OP_MAPALL;
1731 } else
1732 opt_flags |= OP_MAPROOT;
1733 } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1734 *exflagsp |= MNT_EXKERB;
1735 opt_flags |= OP_KERB;
1736 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1737 !strcmp(cpopt, "m"))) {
1738 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1739 syslog(LOG_ERR,
1740 "\"%s\", line %ld: Bad mask: %s",
1741 line, (unsigned long)lineno, cpoptarg);
1742 return (1);
1743 }
1744 usedarg++;
1745 opt_flags |= OP_MASK;
1746 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1747 !strcmp(cpopt, "n"))) {
1748 if (strchr(cpoptarg, '/') != NULL) {
1749 if (debug)
1750 fprintf(stderr, "setting OP_MASKLEN\n");
1751 opt_flags |= OP_MASKLEN;
1752 }
1753 if (grp->gr_type != GT_NULL) {
1754 syslog(LOG_ERR,
1755 "\"%s\", line %ld: Network/host conflict",
1756 line, (unsigned long)lineno);
1757 return (1);
1758 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1759 syslog(LOG_ERR,
1760 "\"%s\", line %ld: Bad net: %s",
1761 line, (unsigned long)lineno, cpoptarg);
1762 return (1);
1763 }
1764 grp->gr_type = GT_NET;
1765 *has_hostp = 1;
1766 usedarg++;
1767 opt_flags |= OP_NET;
1768 } else if (!strcmp(cpopt, "alldirs")) {
1769 opt_flags |= OP_ALLDIRS;
1770 } else if (!strcmp(cpopt, "noresvmnt")) {
1771 opt_flags |= OP_NORESMNT;
1772 } else if (!strcmp(cpopt, "noresvport")) {
1773 opt_flags |= OP_NORESPORT;
1774 *exflagsp |= MNT_EXNORESPORT;
1775 } else if (!strcmp(cpopt, "public")) {
1776 *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC);
1777 opt_flags |= OP_NORESPORT;
1778 } else if (!strcmp(cpopt, "webnfs")) {
1779 *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC |
1780 MNT_EXRDONLY | MNT_EXPORTANON);
1781 opt_flags |= (OP_MAPALL | OP_NORESPORT);
1782 } else if (cpoptarg && !strcmp(cpopt, "index")) {
1783 ep->ex_indexfile = strdup(cpoptarg);
1784 #ifdef ISO
1785 } else if (cpoptarg && !strcmp(cpopt, "iso")) {
1786 if (get_isoaddr(line, lineno, cpoptarg, grp))
1787 return (1);
1788 *has_hostp = 1;
1789 usedarg++;
1790 opt_flags |= OP_ISO;
1791 #endif /* ISO */
1792 } else {
1793 syslog(LOG_ERR,
1794 "\"%s\", line %ld: Bad opt %s",
1795 line, (unsigned long)lineno, cpopt);
1796 return (1);
1797 }
1798 if (usedarg >= 0) {
1799 *endcp = savedc2;
1800 **endcpp = savedc;
1801 if (usedarg > 0) {
1802 *cpp = cp;
1803 *endcpp = endcp;
1804 }
1805 return (0);
1806 }
1807 cpopt = cpoptend;
1808 }
1809 **endcpp = savedc;
1810 return (0);
1811 }
1812
1813 /*
1814 * Translate a character string to the corresponding list of network
1815 * addresses for a hostname.
1816 */
1817 static int
1818 get_host(line, lineno, cp, grp)
1819 const char *line;
1820 size_t lineno;
1821 const char *cp;
1822 struct grouplist *grp;
1823 {
1824 struct addrinfo *ai, hints;
1825 int ecode;
1826 char host[NI_MAXHOST];
1827
1828 if (grp->gr_type != GT_NULL) {
1829 syslog(LOG_ERR,
1830 "\"%s\", line %ld: Bad netgroup type for ip host %s",
1831 line, (unsigned long)lineno, cp);
1832 return (1);
1833 }
1834 memset(&hints, 0, sizeof hints);
1835 hints.ai_flags = AI_CANONNAME;
1836 hints.ai_protocol = IPPROTO_UDP;
1837 ecode = getaddrinfo(cp, NULL, &hints, &ai);
1838 if (ecode != 0) {
1839 syslog(LOG_ERR, "\"%s\", line %ld: can't get address info for "
1840 "host %s",
1841 line, (long)lineno, cp);
1842 return 1;
1843 }
1844 grp->gr_type = GT_HOST;
1845 grp->gr_ptr.gt_addrinfo = ai;
1846 while (ai != NULL) {
1847 if (ai->ai_canonname == NULL) {
1848 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1849 sizeof host, NULL, 0, ninumeric) != 0)
1850 strlcpy(host, "?", sizeof(host));
1851 ai->ai_canonname = estrdup(host);
1852 ai->ai_flags |= AI_CANONNAME;
1853 } else
1854 ai->ai_flags &= ~AI_CANONNAME;
1855 if (debug)
1856 (void)fprintf(stderr, "got host %s\n", ai->ai_canonname);
1857 ai = ai->ai_next;
1858 }
1859 return (0);
1860 }
1861
1862 /*
1863 * Free up an exports list component
1864 */
1865 static void
1866 free_exp(ep)
1867 struct exportlist *ep;
1868 {
1869
1870 if (ep->ex_defdir) {
1871 free_host(ep->ex_defdir->dp_hosts);
1872 free(ep->ex_defdir);
1873 }
1874 if (ep->ex_fsdir)
1875 free(ep->ex_fsdir);
1876 if (ep->ex_indexfile)
1877 free(ep->ex_indexfile);
1878 free_dir(ep->ex_dirl);
1879 free(ep);
1880 }
1881
1882 /*
1883 * Free hosts.
1884 */
1885 static void
1886 free_host(hp)
1887 struct hostlist *hp;
1888 {
1889 struct hostlist *hp2;
1890
1891 while (hp) {
1892 hp2 = hp;
1893 hp = hp->ht_next;
1894 free(hp2);
1895 }
1896 }
1897
1898 static struct hostlist *
1899 get_ht()
1900 {
1901 struct hostlist *hp;
1902
1903 hp = emalloc(sizeof(struct hostlist));
1904 hp->ht_next = NULL;
1905 hp->ht_flag = 0;
1906 return (hp);
1907 }
1908
1909 #ifdef ISO
1910 /*
1911 * Translate an iso address.
1912 */
1913 static int
1914 get_isoaddr(line, lineno, cp, grp)
1915 const char *line;
1916 size_t lineno;
1917 char *cp;
1918 struct grouplist *grp;
1919 {
1920 struct iso_addr *isop;
1921 struct sockaddr_iso *isoaddr;
1922
1923 if (grp->gr_type != GT_NULL) {
1924 syslog(LOG_ERR,
1925 "\"%s\", line %ld: Bad netgroup type for iso addr %s",
1926 line, (unsigned long)lineno, cp);
1927 return (1);
1928 }
1929 if ((isop = iso_addr(cp)) == NULL) {
1930 syslog(LOG_ERR,
1931 "\"%s\", line %ld: Bad iso addr %s",
1932 line, (unsigned long)lineno, cp);
1933 return (1);
1934 }
1935 isoaddr = emalloc(sizeof(struct sockaddr_iso));
1936 (void)memset(isoaddr, 0, sizeof(struct sockaddr_iso));
1937 (void)memcpy(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
1938 isoaddr->siso_len = sizeof(struct sockaddr_iso);
1939 isoaddr->siso_family = AF_ISO;
1940 grp->gr_type = GT_ISO;
1941 grp->gr_ptr.gt_isoaddr = isoaddr;
1942 return (0);
1943 }
1944 #endif /* ISO */
1945
1946 /*
1947 * error checked malloc and strdup
1948 */
1949 static void *
1950 emalloc(n)
1951 size_t n;
1952 {
1953 void *ptr = malloc(n);
1954
1955 if (ptr == NULL) {
1956 syslog(LOG_ERR, "%m");
1957 exit(2);
1958 }
1959 return ptr;
1960 }
1961
1962 static char *
1963 estrdup(s)
1964 const char *s;
1965 {
1966 char *n = strdup(s);
1967
1968 if (n == NULL) {
1969 syslog(LOG_ERR, "%m");
1970 exit(2);
1971 }
1972 return n;
1973 }
1974
1975 /*
1976 * Do the nfssvc syscall to push the export info into the kernel.
1977 */
1978 static int
1979 do_nfssvc(line, lineno, ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1980 const char *line;
1981 size_t lineno;
1982 struct exportlist *ep;
1983 struct grouplist *grp;
1984 int exflags;
1985 struct uucred *anoncrp;
1986 char *dirp;
1987 int dirplen;
1988 struct statvfs *fsb;
1989 {
1990 struct sockaddr *addrp;
1991 struct sockaddr_storage ss;
1992 struct addrinfo *ai;
1993 int addrlen;
1994 int done;
1995 struct export_args export;
1996
1997 export.ex_flags = exflags;
1998 export.ex_anon = *anoncrp;
1999 export.ex_indexfile = ep->ex_indexfile;
2000 if (grp->gr_type == GT_HOST) {
2001 ai = grp->gr_ptr.gt_addrinfo;
2002 addrp = ai->ai_addr;
2003 addrlen = ai->ai_addrlen;
2004 } else {
2005 addrp = NULL;
2006 ai = NULL; /* XXXGCC -Wuninitialized */
2007 addrlen = 0; /* XXXGCC -Wuninitialized */
2008 }
2009 done = FALSE;
2010 while (!done) {
2011 struct mountd_exports_list mel;
2012
2013 switch (grp->gr_type) {
2014 case GT_HOST:
2015 if (addrp != NULL && addrp->sa_family == AF_INET6 &&
2016 have_v6 == 0)
2017 goto skip;
2018 export.ex_addr = addrp;
2019 export.ex_addrlen = addrlen;
2020 export.ex_masklen = 0;
2021 break;
2022 case GT_NET:
2023 export.ex_addr = (struct sockaddr *)
2024 &grp->gr_ptr.gt_net.nt_net;
2025 if (export.ex_addr->sa_family == AF_INET6 &&
2026 have_v6 == 0)
2027 goto skip;
2028 export.ex_addrlen = export.ex_addr->sa_len;
2029 memset(&ss, 0, sizeof ss);
2030 ss.ss_family = export.ex_addr->sa_family;
2031 ss.ss_len = export.ex_addr->sa_len;
2032 if (allones(&ss, grp->gr_ptr.gt_net.nt_len) != 0) {
2033 syslog(LOG_ERR,
2034 "\"%s\", line %ld: Bad network flag",
2035 line, (unsigned long)lineno);
2036 return (1);
2037 }
2038 export.ex_mask = (struct sockaddr *)&ss;
2039 export.ex_masklen = ss.ss_len;
2040 break;
2041 #ifdef ISO
2042 case GT_ISO:
2043 export.ex_addr =
2044 (struct sockaddr *) grp->gr_ptr.gt_isoaddr;
2045 export.ex_addrlen =
2046 sizeof(struct sockaddr_iso);
2047 export.ex_masklen = 0;
2048 break;
2049 #endif /* ISO */
2050 default:
2051 syslog(LOG_ERR, "\"%s\", line %ld: Bad netgroup type",
2052 line, (unsigned long)lineno);
2053 return (1);
2054 };
2055
2056 /*
2057 * XXX:
2058 * Maybe I should just use the fsb->f_mntonname path?
2059 */
2060
2061 mel.mel_path = dirp;
2062 mel.mel_nexports = 1;
2063 mel.mel_exports = &export;
2064
2065 if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) != 0) {
2066 syslog(LOG_ERR,
2067 "\"%s\", line %ld: Can't change attributes for %s to %s: %m",
2068 line, (unsigned long)lineno,
2069 dirp, (grp->gr_type == GT_HOST) ?
2070 grp->gr_ptr.gt_addrinfo->ai_canonname :
2071 (grp->gr_type == GT_NET) ?
2072 grp->gr_ptr.gt_net.nt_name :
2073 "Unknown");
2074 return (1);
2075 }
2076 skip:
2077 if (addrp) {
2078 ai = ai->ai_next;
2079 if (ai == NULL)
2080 done = TRUE;
2081 else {
2082 addrp = ai->ai_addr;
2083 addrlen = ai->ai_addrlen;
2084 }
2085 } else
2086 done = TRUE;
2087 }
2088 return (0);
2089 }
2090
2091 /*
2092 * Translate a net address.
2093 */
2094 static int
2095 get_net(cp, net, maskflg)
2096 char *cp;
2097 struct netmsk *net;
2098 int maskflg;
2099 {
2100 struct netent *np;
2101 char *name, *p, *prefp;
2102 struct sockaddr_in sin, *sinp;
2103 struct sockaddr *sa;
2104 struct addrinfo hints, *ai = NULL;
2105 char netname[NI_MAXHOST];
2106 long preflen;
2107 int ecode;
2108
2109 (void)memset(&sin, 0, sizeof(sin));
2110 if ((opt_flags & OP_MASKLEN) && !maskflg) {
2111 p = strchr(cp, '/');
2112 *p = '\0';
2113 prefp = p + 1;
2114 } else {
2115 p = NULL; /* XXXGCC -Wuninitialized */
2116 prefp = NULL; /* XXXGCC -Wuninitialized */
2117 }
2118
2119 if ((np = getnetbyname(cp)) != NULL) {
2120 sin.sin_family = AF_INET;
2121 sin.sin_len = sizeof sin;
2122 sin.sin_addr = inet_makeaddr(np->n_net, 0);
2123 sa = (struct sockaddr *)&sin;
2124 } else if (isdigit((unsigned char)*cp)) {
2125 memset(&hints, 0, sizeof hints);
2126 hints.ai_family = AF_UNSPEC;
2127 hints.ai_flags = AI_NUMERICHOST;
2128 if (getaddrinfo(cp, NULL, &hints, &ai) != 0) {
2129 /*
2130 * If getaddrinfo() failed, try the inet4 network
2131 * notation with less than 3 dots.
2132 */
2133 sin.sin_family = AF_INET;
2134 sin.sin_len = sizeof sin;
2135 sin.sin_addr = inet_makeaddr(inet_network(cp),0);
2136 if (debug)
2137 fprintf(stderr, "get_net: v4 addr %x\n",
2138 sin.sin_addr.s_addr);
2139 sa = (struct sockaddr *)&sin;
2140 } else
2141 sa = ai->ai_addr;
2142 } else if (isxdigit((unsigned char)*cp) || *cp == ':') {
2143 memset(&hints, 0, sizeof hints);
2144 hints.ai_family = AF_UNSPEC;
2145 hints.ai_flags = AI_NUMERICHOST;
2146 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2147 sa = ai->ai_addr;
2148 else
2149 goto fail;
2150 } else
2151 goto fail;
2152
2153 /*
2154 * Only allow /pref notation for v6 addresses.
2155 */
2156 if (sa->sa_family == AF_INET6 && (!(opt_flags & OP_MASKLEN) || maskflg))
2157 return 1;
2158
2159 ecode = getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2160 NULL, 0, ninumeric);
2161 if (ecode != 0)
2162 goto fail;
2163
2164 if (maskflg)
2165 net->nt_len = countones(sa);
2166 else {
2167 if (opt_flags & OP_MASKLEN) {
2168 errno = 0;
2169 preflen = strtol(prefp, NULL, 10);
2170 if (preflen == LONG_MIN && errno == ERANGE)
2171 goto fail;
2172 net->nt_len = (int)preflen;
2173 *p = '/';
2174 }
2175
2176 if (np)
2177 name = np->n_name;
2178 else {
2179 if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2180 NULL, 0, ninumeric) != 0)
2181 strlcpy(netname, "?", sizeof(netname));
2182 name = netname;
2183 }
2184 net->nt_name = estrdup(name);
2185 memcpy(&net->nt_net, sa, sa->sa_len);
2186 }
2187
2188 if (!maskflg && sa->sa_family == AF_INET &&
2189 !(opt_flags & (OP_MASK|OP_MASKLEN))) {
2190 sinp = (struct sockaddr_in *)sa;
2191 if (IN_CLASSA(sinp->sin_addr.s_addr))
2192 net->nt_len = 8;
2193 else if (IN_CLASSB(sinp->sin_addr.s_addr))
2194 net->nt_len = 16;
2195 else if (IN_CLASSC(sinp->sin_addr.s_addr))
2196 net->nt_len = 24;
2197 else if (IN_CLASSD(sinp->sin_addr.s_addr))
2198 net->nt_len = 28;
2199 else
2200 net->nt_len = 32; /* XXX */
2201 }
2202
2203 if (ai)
2204 freeaddrinfo(ai);
2205 return 0;
2206
2207 fail:
2208 if (ai)
2209 freeaddrinfo(ai);
2210 return 1;
2211 }
2212
2213 /*
2214 * Parse out the next white space separated field
2215 */
2216 static void
2217 nextfield(cp, endcp)
2218 char **cp;
2219 char **endcp;
2220 {
2221 char *p;
2222
2223 p = *cp;
2224 while (*p == ' ' || *p == '\t')
2225 p++;
2226 if (*p == '\n' || *p == '\0')
2227 *cp = *endcp = p;
2228 else {
2229 *cp = p++;
2230 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2231 p++;
2232 *endcp = p;
2233 }
2234 }
2235
2236 /*
2237 * Parse a description of a credential.
2238 */
2239 static void
2240 parsecred(namelist, cr)
2241 char *namelist;
2242 struct uucred *cr;
2243 {
2244 char *name;
2245 int cnt;
2246 char *names;
2247 struct passwd *pw;
2248 struct group *gr;
2249 int ngroups;
2250 gid_t groups[NGROUPS + 1];
2251
2252 /*
2253 * Set up the unprivileged user.
2254 */
2255 *cr = def_anon;
2256 /*
2257 * Get the user's password table entry.
2258 */
2259 names = strsep(&namelist, " \t\n");
2260 name = strsep(&names, ":");
2261 if (isdigit((unsigned char)*name) || *name == '-')
2262 pw = getpwuid(atoi(name));
2263 else
2264 pw = getpwnam(name);
2265 /*
2266 * Credentials specified as those of a user.
2267 */
2268 if (names == NULL) {
2269 if (pw == NULL) {
2270 syslog(LOG_ERR, "Unknown user: %s", name);
2271 return;
2272 }
2273 cr->cr_uid = pw->pw_uid;
2274 ngroups = NGROUPS + 1;
2275 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2276 syslog(LOG_ERR, "Too many groups");
2277 /*
2278 * Convert from int's to gid_t's and compress out duplicate
2279 */
2280 cr->cr_ngroups = ngroups - 1;
2281 cr->cr_gid = groups[0];
2282 for (cnt = 1; cnt < ngroups; cnt++)
2283 cr->cr_groups[cnt - 1] = groups[cnt];
2284 return;
2285 }
2286 /*
2287 * Explicit credential specified as a colon separated list:
2288 * uid:gid:gid:...
2289 */
2290 if (pw != NULL)
2291 cr->cr_uid = pw->pw_uid;
2292 else if (isdigit((unsigned char)*name) || *name == '-')
2293 cr->cr_uid = atoi(name);
2294 else {
2295 syslog(LOG_ERR, "Unknown user: %s", name);
2296 return;
2297 }
2298 cr->cr_ngroups = 0;
2299 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2300 name = strsep(&names, ":");
2301 if (isdigit((unsigned char)*name) || *name == '-') {
2302 cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2303 } else {
2304 if ((gr = getgrnam(name)) == NULL) {
2305 syslog(LOG_ERR, "Unknown group: %s", name);
2306 continue;
2307 }
2308 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2309 }
2310 }
2311 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2312 syslog(LOG_ERR, "Too many groups");
2313 }
2314
2315 #define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2316 /*
2317 * Routines that maintain the remote mounttab
2318 */
2319 static void
2320 get_mountlist()
2321 {
2322 struct mountlist *mlp, **mlpp;
2323 char *host, *dirp, *cp;
2324 char str[STRSIZ];
2325 FILE *mlfile;
2326
2327 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2328 syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST);
2329 return;
2330 }
2331 mlpp = &mlhead;
2332 while (fgets(str, STRSIZ, mlfile) != NULL) {
2333 cp = str;
2334 host = strsep(&cp, " \t\n");
2335 dirp = strsep(&cp, " \t\n");
2336 if (host == NULL || dirp == NULL)
2337 continue;
2338 mlp = emalloc(sizeof(*mlp));
2339 (void)strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2340 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2341 (void)strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2342 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2343 mlp->ml_next = NULL;
2344 *mlpp = mlp;
2345 mlpp = &mlp->ml_next;
2346 }
2347 (void)fclose(mlfile);
2348 }
2349
2350 static int
2351 del_mlist(hostp, dirp, saddr)
2352 char *hostp, *dirp;
2353 struct sockaddr *saddr;
2354 {
2355 struct mountlist *mlp, **mlpp;
2356 struct mountlist *mlp2;
2357 u_short sport;
2358 FILE *mlfile;
2359 int fnd = 0, ret = 0;
2360 char host[NI_MAXHOST];
2361
2362 switch (saddr->sa_family) {
2363 case AF_INET6:
2364 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
2365 break;
2366 case AF_INET:
2367 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
2368 break;
2369 default:
2370 return -1;
2371 }
2372 mlpp = &mlhead;
2373 mlp = mlhead;
2374 while (mlp) {
2375 if (!strcmp(mlp->ml_host, hostp) &&
2376 (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2377 if (!(mlp->ml_flag & DP_NORESMNT) &&
2378 sport >= IPPORT_RESERVED) {
2379 if (getnameinfo(saddr, saddr->sa_len, host,
2380 sizeof host, NULL, 0, ninumeric) != 0)
2381 strlcpy(host, "?", sizeof(host));
2382 syslog(LOG_NOTICE,
2383 "Umount request for %s:%s from %s refused\n",
2384 mlp->ml_host, mlp->ml_dirp, host);
2385 ret = -1;
2386 goto cont;
2387 }
2388 fnd = 1;
2389 mlp2 = mlp;
2390 *mlpp = mlp = mlp->ml_next;
2391 free(mlp2);
2392 } else {
2393 cont:
2394 mlpp = &mlp->ml_next;
2395 mlp = mlp->ml_next;
2396 }
2397 }
2398 if (fnd) {
2399 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2400 syslog(LOG_ERR, "Can't update %s: %m",
2401 _PATH_RMOUNTLIST);
2402 return ret;
2403 }
2404 mlp = mlhead;
2405 while (mlp) {
2406 (void)fprintf(mlfile, "%s %s\n", mlp->ml_host,
2407 mlp->ml_dirp);
2408 mlp = mlp->ml_next;
2409 }
2410 (void)fclose(mlfile);
2411 }
2412 return ret;
2413 }
2414
2415 static void
2416 add_mlist(hostp, dirp, flags)
2417 char *hostp, *dirp;
2418 int flags;
2419 {
2420 struct mountlist *mlp, **mlpp;
2421 FILE *mlfile;
2422
2423 mlpp = &mlhead;
2424 mlp = mlhead;
2425 while (mlp) {
2426 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2427 return;
2428 mlpp = &mlp->ml_next;
2429 mlp = mlp->ml_next;
2430 }
2431 mlp = emalloc(sizeof(*mlp));
2432 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2433 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2434 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2435 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2436 mlp->ml_flag = flags;
2437 mlp->ml_next = NULL;
2438 *mlpp = mlp;
2439 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2440 syslog(LOG_ERR, "Can't update %s: %m", _PATH_RMOUNTLIST);
2441 return;
2442 }
2443 (void)fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2444 (void)fclose(mlfile);
2445 }
2446
2447 /*
2448 * This function is called via. SIGTERM when the system is going down.
2449 * It sends a broadcast RPCMNT_UMNTALL.
2450 */
2451 /* ARGSUSED */
2452 static void
2453 send_umntall(n)
2454 int n;
2455 {
2456 (void)clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
2457 xdr_void, NULL, xdr_void, NULL, (resultproc_t)umntall_each);
2458 exit(0);
2459 }
2460
2461 static int
2462 umntall_each(resultsp, raddr)
2463 caddr_t resultsp;
2464 struct sockaddr_in *raddr;
2465 {
2466 return (1);
2467 }
2468
2469 /*
2470 * Free up a group list.
2471 */
2472 static void
2473 free_grp(grp)
2474 struct grouplist *grp;
2475 {
2476
2477 if (grp->gr_type == GT_HOST) {
2478 if (grp->gr_ptr.gt_addrinfo != NULL)
2479 freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2480 } else if (grp->gr_type == GT_NET) {
2481 if (grp->gr_ptr.gt_net.nt_name)
2482 free(grp->gr_ptr.gt_net.nt_name);
2483 }
2484 #ifdef ISO
2485 else if (grp->gr_type == GT_ISO)
2486 free(grp->gr_ptr.gt_isoaddr);
2487 #endif
2488 free(grp);
2489 }
2490
2491 #if 0
2492 static void
2493 SYSLOG(int pri, const char *fmt,...)
2494 {
2495 va_list ap;
2496
2497 va_start(ap, fmt);
2498
2499 if (debug)
2500 vfprintf(stderr, fmt, ap);
2501 else
2502 vsyslog(pri, fmt, ap);
2503
2504 va_end(ap);
2505 }
2506 #endif
2507
2508 /*
2509 * Check options for consistency.
2510 */
2511 static int
2512 check_options(line, lineno, dp)
2513 const char *line;
2514 size_t lineno;
2515 struct dirlist *dp;
2516 {
2517
2518 if (dp == NULL) {
2519 syslog(LOG_ERR,
2520 "\"%s\", line %ld: missing directory list",
2521 line, (unsigned long)lineno);
2522 return (1);
2523 }
2524 if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL) ||
2525 (opt_flags & (OP_MAPROOT|OP_KERB)) == (OP_MAPROOT|OP_KERB) ||
2526 (opt_flags & (OP_MAPALL|OP_KERB)) == (OP_MAPALL|OP_KERB)) {
2527 syslog(LOG_ERR,
2528 "\"%s\", line %ld: -mapall, -maproot and -kerb mutually exclusive",
2529 line, (unsigned long)lineno);
2530 return (1);
2531 }
2532 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2533 syslog(LOG_ERR, "\"%s\", line %ld: -mask requires -net",
2534 line, (unsigned long)lineno);
2535 return (1);
2536 }
2537 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN) != 0) {
2538 syslog(LOG_ERR, "\"%s\", line %ld: /pref and -mask mutually"
2539 " exclusive",
2540 line, (unsigned long)lineno);
2541 return (1);
2542 }
2543 if ((opt_flags & (OP_NET|OP_ISO)) == (OP_NET|OP_ISO)) {
2544 syslog(LOG_ERR,
2545 "\"%s\", line %ld: -net and -iso mutually exclusive",
2546 line, (unsigned long)lineno);
2547 return (1);
2548 }
2549 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2550 syslog(LOG_ERR,
2551 "\"%s\", line %ld: -alldir has multiple directories",
2552 line, (unsigned long)lineno);
2553 return (1);
2554 }
2555 return (0);
2556 }
2557
2558 /*
2559 * Check an absolute directory path for any symbolic links. Return true
2560 * if no symbolic links are found.
2561 */
2562 static int
2563 check_dirpath(line, lineno, dirp)
2564 const char *line;
2565 size_t lineno;
2566 char *dirp;
2567 {
2568 char *cp;
2569 struct stat sb;
2570 char *file = "";
2571
2572 for (cp = dirp + 1; *cp; cp++) {
2573 if (*cp == '/') {
2574 *cp = '\0';
2575 if (lstat(dirp, &sb) == -1)
2576 goto bad;
2577 if (!S_ISDIR(sb.st_mode))
2578 goto bad1;
2579 *cp = '/';
2580 }
2581 }
2582
2583 cp = NULL;
2584 if (lstat(dirp, &sb) == -1)
2585 goto bad;
2586
2587 if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) {
2588 file = " file or a";
2589 goto bad1;
2590 }
2591
2592 return 1;
2593
2594 bad:
2595 syslog(LOG_ERR,
2596 "\"%s\", line %ld: lstat for `%s' failed: %m",
2597 line, (unsigned long)lineno, dirp);
2598 if (cp)
2599 *cp = '/';
2600 return 0;
2601
2602 bad1:
2603 syslog(LOG_ERR,
2604 "\"%s\", line %ld: `%s' is not a%s directory",
2605 line, (unsigned long)lineno, dirp, file);
2606 if (cp)
2607 *cp = '/';
2608 return 0;
2609 }
2610
2611 static void
2612 bind_resv_port(int sock, sa_family_t family, in_port_t port)
2613 {
2614 struct sockaddr *sa;
2615 struct sockaddr_in sasin;
2616 struct sockaddr_in6 sasin6;
2617
2618 switch (family) {
2619 case AF_INET:
2620 (void)memset(&sasin, 0, sizeof(sasin));
2621 sasin.sin_len = sizeof(sasin);
2622 sasin.sin_family = family;
2623 sasin.sin_port = htons(port);
2624 sa = (struct sockaddr *)(void *)&sasin;
2625 break;
2626 case AF_INET6:
2627 (void)memset(&sasin6, 0, sizeof(sasin6));
2628 sasin6.sin6_len = sizeof(sasin6);
2629 sasin6.sin6_family = family;
2630 sasin6.sin6_port = htons(port);
2631 sa = (struct sockaddr *)(void *)&sasin6;
2632 break;
2633 default:
2634 syslog(LOG_ERR, "Unsupported address family %d", family);
2635 return;
2636 }
2637 if (bindresvport_sa(sock, sa) == -1)
2638 syslog(LOG_ERR, "Cannot bind to reserved port %d (%m)", port);
2639 }
2640