1 /* $OpenBSD: mountd.c,v 1.97 2025/01/16 12:48:45 kn Exp $ */
2 /* $NetBSD: mountd.c,v 1.31 1996/02/18 11:57:53 fvdl Exp $ */
3
4 /*
5 * Copyright (c) 1989, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Herb Hasler and Rick Macklem at The University of Guelph.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/mount.h>
39 #include <sys/queue.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/uio.h>
43 #include <sys/wait.h>
44 #include <syslog.h>
45
46 #include <rpc/rpc.h>
47 #include <rpc/pmap_clnt.h>
48 #include <rpc/pmap_prot.h>
49 #include <nfs/rpcv2.h>
50 #include <nfs/nfsproto.h>
51
52 #include <arpa/inet.h>
53
54 #include <ctype.h>
55 #include <errno.h>
56 #include <grp.h>
57 #include <imsg.h>
58 #include <netdb.h>
59 #include <netgroup.h>
60 #include <poll.h>
61 #include <pwd.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67 #include <limits.h>
68 #include "pathnames.h"
69
70 #include <stdarg.h>
71
72 #define isterminated(str, size) (memchr((str), '\0', (size)) != NULL)
73
74 /*
75 * Structures for keeping the mount list and export list
76 */
77 struct mountlist {
78 struct mountlist *ml_next;
79 char ml_host[RPCMNT_NAMELEN+1];
80 char ml_dirp[RPCMNT_PATHLEN+1];
81 };
82
83 struct dirlist {
84 struct dirlist *dp_left;
85 struct dirlist *dp_right;
86 int dp_flag;
87 struct hostlist *dp_hosts; /* List of hosts this dir exported to */
88 char dp_dirp[1]; /* Actually malloc'd to size of dir */
89 };
90 /* dp_flag bits */
91 #define DP_DEFSET 0x1
92 #define DP_HOSTSET 0x2
93
94 struct exportlist {
95 struct exportlist *ex_next;
96 struct dirlist *ex_dirl;
97 struct dirlist *ex_defdir;
98 int ex_flag;
99 fsid_t ex_fs;
100 char *ex_fsdir;
101 };
102 /* ex_flag bits */
103 #define EX_LINKED 0x1
104
105 struct netmsk {
106 in_addr_t nt_net;
107 in_addr_t nt_mask;
108 char *nt_name;
109 };
110
111 union grouptypes {
112 struct hostent *gt_hostent;
113 struct netmsk gt_net;
114 };
115
116 struct grouplist {
117 int gr_type;
118 union grouptypes gr_ptr;
119 struct grouplist *gr_next;
120 };
121 /* Group types */
122 #define GT_NULL 0x0
123 #define GT_HOST 0x1
124 #define GT_NET 0x2
125 #define GT_IGNORE 0x5
126
127 struct hostlist {
128 int ht_flag; /* Uses DP_xx bits */
129 struct grouplist *ht_grp;
130 struct hostlist *ht_next;
131 };
132
133 struct fhreturn {
134 int fhr_flag;
135 int fhr_vers;
136 nfsfh_t fhr_fh;
137 };
138
139 #define IMSG_GETFH_REQ 0x0
140 #define IMSG_GETFH_RESP 0x1
141 #define IMSG_EXPORT_REQ 0x2
142 #define IMSG_EXPORT_RESP 0x3
143 #define IMSG_DELEXPORT 0x4
144 #define IMSG_MLIST_APPEND 0x5
145 #define IMSG_MLIST_OPEN 0x6
146 #define IMSG_MLIST_CLOSE 0x7
147 #define IMSG_MLIST_WRITE 0x8
148
149 struct getfh_resp {
150 fhandle_t gr_fh;
151 int gr_error;
152 };
153
154 struct export_req {
155 char er_path[MNAMELEN];
156 struct export_args er_args;
157 struct sockaddr er_addr;
158 struct sockaddr er_mask;
159 };
160
161 /* Global defs */
162 char *add_expdir(struct dirlist **, char *, int);
163 void add_dlist(struct dirlist **, struct dirlist *, struct grouplist *, int);
164 void add_mlist(char *, char *);
165 void check_child(int);
166 int check_dirpath(char *);
167 int check_options(struct dirlist *);
168 int chk_host(struct dirlist *, in_addr_t, int *, int *);
169 void del_mlist(char *, char *);
170 struct dirlist *dirp_search(struct dirlist *, char *);
171 int do_mount(struct exportlist *, struct grouplist *, int, struct xucred *,
172 char *, int);
173 int do_opt(char **, char **, struct exportlist *, struct grouplist *,
174 int *, int *, struct xucred *);
175 struct exportlist *ex_search(fsid_t *);
176 struct exportlist *get_exp(void);
177 void free_dir(struct dirlist *);
178 void free_exp(struct exportlist *);
179 void free_grp(struct grouplist *);
180 void free_host(struct hostlist *);
181 void new_exportlist(int signo);
182 void get_exportlist(void);
183 int get_host(char *, struct grouplist *, struct grouplist *);
184 struct hostlist *get_ht(void);
185 int get_line(void);
186 void get_mountlist(void);
187 int get_net(char *, struct netmsk *, int);
188 void getexp_err(struct exportlist *, struct grouplist *);
189 struct grouplist *get_grp(void);
190 void hang_dirp(struct dirlist *, struct grouplist *, struct exportlist *,
191 int);
192 void mntsrv(struct svc_req *, SVCXPRT *);
193 void nextfield(char **, char **);
194 void out_of_mem(void);
195 void parsecred(char *, struct xucred *);
196 void privchild(int);
197 int put_exlist(struct dirlist *, XDR *, struct dirlist *, int *);
198 ssize_t recv_imsg(struct imsg *);
199 int scan_tree(struct dirlist *, in_addr_t);
200 int send_imsg(u_int32_t, void *, u_int16_t);
201 void send_umntall(int signo);
202 int umntall_each(caddr_t, struct sockaddr_in *);
203 int xdr_dir(XDR *, char *);
204 int xdr_explist(XDR *, caddr_t);
205 int xdr_fhs(XDR *, caddr_t);
206 int xdr_mlist(XDR *, caddr_t);
207 void mountd_svc_run(void);
208
209 struct exportlist *exphead;
210 struct mountlist *mlhead;
211 struct grouplist *grphead;
212 const char *exname;
213 struct xucred def_anon = {
214 .cr_uid = (uid_t) -2,
215 .cr_gid = (gid_t) -2,
216 .cr_ngroups = 0,
217 .cr_groups = { 0, }
218 };
219 int opt_flags;
220 /* Bits for above */
221 #define OP_MAPROOT 0x01
222 #define OP_MAPALL 0x02
223 #define OP_MASK 0x08
224 #define OP_NET 0x10
225 #define OP_ALLDIRS 0x40
226
227 struct imsgbuf ibuf;
228 int debug = 0;
229
230 volatile sig_atomic_t gotchld;
231 volatile sig_atomic_t gothup;
232 volatile sig_atomic_t gotterm;
233
234 /*
235 * Mountd server for NFS mount protocol as described in:
236 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
237 * The optional arguments are the exports file name
238 * default: _PATH_EXPORTS
239 * "-d" to enable debugging
240 */
241 int
main(int argc,char * argv[])242 main(int argc, char *argv[])
243 {
244 SVCXPRT *udptransp, *tcptransp;
245 FILE *pidfile;
246 int c, socks[2];
247
248 while ((c = getopt(argc, argv, "dnr")) != -1)
249 switch (c) {
250 case 'd':
251 debug = 1;
252 break;
253 case 'n':
254 case 'r':
255 /* Compatibility */
256 break;
257 default:
258 fprintf(stderr, "usage: mountd [-d] [exportsfile]\n");
259 exit(1);
260 }
261 argc -= optind;
262 argv += optind;
263 grphead = NULL;
264 exphead = NULL;
265 mlhead = NULL;
266
267 if (argc == 1)
268 exname = *argv;
269 else
270 exname = _PATH_EXPORTS;
271
272 openlog("mountd", LOG_PID, LOG_DAEMON);
273 if (debug)
274 fprintf(stderr, "Here we go.\n");
275 if (debug == 0) {
276 daemon(0, 0);
277 signal(SIGINT, SIG_IGN);
278 signal(SIGQUIT, SIG_IGN);
279 }
280 /* Store pid in file unless mountd is already running */
281 pidfile = fopen(_PATH_MOUNTDPID, "r");
282 if (pidfile != NULL) {
283 if (fscanf(pidfile, "%d\n", &c) > 0 && c > 0) {
284 if (kill(c, 0) == 0) {
285 syslog(LOG_ERR, "Already running (pid %d)", c);
286 exit(1);
287 }
288 }
289 pidfile = freopen(_PATH_MOUNTDPID, "w", pidfile);
290 } else {
291 pidfile = fopen(_PATH_MOUNTDPID, "w");
292 }
293 if (pidfile) {
294 fprintf(pidfile, "%ld\n", (long)getpid());
295 fclose(pidfile);
296 }
297
298 signal(SIGCHLD, (void (*)(int)) check_child);
299 signal(SIGHUP, (void (*)(int)) new_exportlist);
300 signal(SIGPIPE, SIG_IGN);
301
302 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
303 syslog(LOG_ERR, "socketpair: %m");
304 exit(1);
305 }
306
307 switch (fork()) {
308 case -1:
309 syslog(LOG_ERR, "fork: %m");
310 exit(1);
311 case 0:
312 close(socks[0]);
313 privchild(socks[1]);
314 }
315
316 close(socks[1]);
317
318 if (pledge("stdio rpath inet dns getpw", NULL) == -1) {
319 syslog(LOG_ERR, "pledge: %m");
320 exit(1);
321 }
322
323 signal(SIGTERM, (void (*)(int)) send_umntall);
324 if (imsgbuf_init(&ibuf, socks[0]) == -1) {
325 syslog(LOG_ERR, "imsgbuf_init: %m");
326 exit(1);
327 }
328 setproctitle("parent");
329
330 if (debug)
331 fprintf(stderr, "Getting export list.\n");
332 get_exportlist();
333 if (debug)
334 fprintf(stderr, "Getting mount list.\n");
335 get_mountlist();
336
337 if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
338 (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
339 syslog(LOG_ERR, "Can't create socket");
340 exit(1);
341 }
342 pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
343 pmap_unset(RPCPROG_MNT, RPCMNT_VER3);
344 if (!svc_register(udptransp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP) ||
345 !svc_register(udptransp, RPCPROG_MNT, RPCMNT_VER3, mntsrv, IPPROTO_UDP) ||
346 !svc_register(tcptransp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_TCP) ||
347 !svc_register(tcptransp, RPCPROG_MNT, RPCMNT_VER3, mntsrv, IPPROTO_TCP)) {
348 syslog(LOG_ERR, "Can't register mount");
349 exit(1);
350 }
351 mountd_svc_run();
352 syslog(LOG_ERR, "Mountd died");
353 exit(1);
354 }
355
356 void
check_child(int signo)357 check_child(int signo)
358 {
359 gotchld = 1;
360 }
361
362 void
privchild(int sock)363 privchild(int sock)
364 {
365 struct imsg imsg;
366 struct pollfd pfd[1];
367 struct ufs_args args;
368 struct statfs sfb;
369 struct getfh_resp resp;
370 struct export_req *req;
371 struct mountlist *ml;
372 FILE *fp;
373 char *path;
374 int error, size;
375
376 if (unveil("/", "r") == -1) {
377 syslog(LOG_ERR, "unveil /: %m");
378 _exit(1);
379 }
380 if (unveil(_PATH_RMOUNTLIST, "rwc") == -1) {
381 syslog(LOG_ERR, "unveil %s: %m", _PATH_RMOUNTLIST);
382 _exit(1);
383 }
384 if (unveil(NULL, NULL) == -1) {
385 syslog(LOG_ERR, "unveil: %m");
386 _exit(1);
387 }
388
389 if (imsgbuf_init(&ibuf, sock) == -1) {
390 syslog(LOG_ERR, "imsgbuf_init: %m");
391 _exit(1);
392 }
393 setproctitle("[priv]");
394 fp = NULL;
395
396 for (;;) {
397 if (gothup) {
398 kill(getppid(), SIGHUP);
399 gothup = 0;
400 }
401
402 pfd[0].fd = ibuf.fd;
403 pfd[0].events = POLLIN;
404 switch (poll(pfd, 1, INFTIM)) {
405 case -1:
406 if (errno == EINTR)
407 continue;
408 syslog(LOG_ERR, "poll: %m");
409 _exit(1);
410 case 0:
411 continue;
412 }
413 if (pfd[0].revents & POLLHUP) {
414 syslog(LOG_ERR, "Socket disconnected");
415 _exit(1);
416 }
417 if (!(pfd[0].revents & POLLIN))
418 continue;
419
420 switch (imsgbuf_read(&ibuf)) {
421 case -1:
422 syslog(LOG_ERR, "imsgbuf_read: %m");
423 _exit(1);
424 case 0:
425 syslog(LOG_ERR, "Socket disconnected");
426 _exit(1);
427 }
428
429 while ((size = imsg_get(&ibuf, &imsg)) != 0) {
430 if (size == -1) {
431 syslog(LOG_ERR, "imsg_get: %m");
432 _exit(1);
433 }
434 size -= IMSG_HEADER_SIZE;
435
436 switch (imsg.hdr.type) {
437 case IMSG_GETFH_REQ:
438 if (size != PATH_MAX) {
439 syslog(LOG_ERR, "Invalid message size");
440 break;
441 }
442 path = imsg.data;
443 if (getfh(path, &resp.gr_fh) == -1)
444 resp.gr_error = errno;
445 else
446 resp.gr_error = 0;
447 send_imsg(IMSG_GETFH_RESP, &resp, sizeof(resp));
448 break;
449 case IMSG_EXPORT_REQ:
450 if (size != sizeof(*req)) {
451 syslog(LOG_ERR, "Invalid message size");
452 break;
453 }
454 req = imsg.data;
455 if (statfs(req->er_path, &sfb) == -1) {
456 error = errno;
457 syslog(LOG_ERR, "statfs: %m");
458 send_imsg(IMSG_EXPORT_RESP, &error,
459 sizeof(error));
460 break;
461 }
462 args.fspec = 0;
463 args.export_info = req->er_args;
464 args.export_info.ex_addr = &req->er_addr;
465 args.export_info.ex_mask = &req->er_mask;
466 if (mount(sfb.f_fstypename, sfb.f_mntonname,
467 sfb.f_flags | MNT_UPDATE, &args) == -1) {
468 error = errno;
469 syslog(LOG_ERR, "mount: %m");
470 send_imsg(IMSG_EXPORT_RESP, &error,
471 sizeof(error));
472 break;
473 }
474 error = 0;
475 send_imsg(IMSG_EXPORT_RESP, &error, sizeof(error));
476 break;
477 case IMSG_DELEXPORT:
478 if (size != MNAMELEN) {
479 syslog(LOG_ERR, "Invalid message size");
480 break;
481 }
482 path = imsg.data;
483 if (statfs(path, &sfb) == -1) {
484 syslog(LOG_ERR, "statfs: %m");
485 break;
486 }
487 memset(&args, 0, sizeof(args));
488 args.export_info.ex_flags = MNT_DELEXPORT;
489 if (mount(sfb.f_fstypename, sfb.f_mntonname,
490 sfb.f_flags | MNT_UPDATE, &args) == -1)
491 syslog(LOG_ERR, "mount: %m");
492 break;
493 case IMSG_MLIST_APPEND:
494 if (size != sizeof(*ml)) {
495 syslog(LOG_ERR, "Invalid message size");
496 break;
497 }
498 if (fp != NULL)
499 break;
500 ml = imsg.data;
501 if (!isterminated(&ml->ml_host,
502 sizeof(ml->ml_host)) ||
503 !isterminated(&ml->ml_dirp,
504 sizeof(ml->ml_dirp)))
505 break;
506 fp = fopen(_PATH_RMOUNTLIST, "a");
507 if (fp == NULL) {
508 syslog(LOG_ERR, "fopen: %s: %m",
509 _PATH_RMOUNTLIST);
510 break;
511 }
512 fprintf(fp, "%s %s\n", ml->ml_host,
513 ml->ml_dirp);
514 fclose(fp);
515 fp = NULL;
516 break;
517 case IMSG_MLIST_OPEN:
518 if (size != 0) {
519 syslog(LOG_ERR, "Invalid message size");
520 break;
521 }
522 if (fp != NULL)
523 break;
524 fp = fopen(_PATH_RMOUNTLIST, "w");
525 if (fp == NULL)
526 syslog(LOG_ERR, "fopen: %s: %m",
527 _PATH_RMOUNTLIST);
528 break;
529 case IMSG_MLIST_WRITE:
530 if (size != sizeof(*ml)) {
531 syslog(LOG_ERR, "Invalid message size");
532 break;
533 }
534 if (fp == NULL)
535 break;
536 ml = imsg.data;
537 if (!isterminated(&ml->ml_host,
538 sizeof(ml->ml_host)) ||
539 !isterminated(&ml->ml_dirp,
540 sizeof(ml->ml_host)))
541 break;
542 fprintf(fp, "%s %s\n", ml->ml_host,
543 ml->ml_dirp);
544 break;
545 case IMSG_MLIST_CLOSE:
546 if (size != 0) {
547 syslog(LOG_ERR, "Invalid message size");
548 break;
549 }
550 if (fp != NULL) {
551 fclose(fp);
552 fp = NULL;
553 }
554 break;
555 default:
556 syslog(LOG_ERR, "Unexpected message type");
557 break;
558 }
559
560 imsg_free(&imsg);
561 }
562 }
563 }
564
565 int
imsg_getfh(char * path,fhandle_t * fh)566 imsg_getfh(char *path, fhandle_t *fh)
567 {
568 struct imsg imsg;
569 struct getfh_resp *resp;
570 ssize_t size;
571
572 if (send_imsg(IMSG_GETFH_REQ, path, PATH_MAX) == -1)
573 return (-1);
574
575 size = recv_imsg(&imsg);
576 if (size == -1)
577 return (-1);
578 if (imsg.hdr.type != IMSG_GETFH_RESP || size != sizeof(*resp)) {
579 syslog(LOG_ERR, "Invalid message");
580 imsg_free(&imsg);
581 errno = EINVAL;
582 return (-1);
583 }
584
585 resp = imsg.data;
586 *fh = resp->gr_fh;
587 if (resp->gr_error) {
588 errno = resp->gr_error;
589 imsg_free(&imsg);
590 return (-1);
591 }
592
593 imsg_free(&imsg);
594 return (0);
595 }
596
597 int
imsg_export(const char * dir,struct export_args * args)598 imsg_export(const char *dir, struct export_args *args)
599 {
600 struct export_req req;
601 struct imsg imsg;
602 ssize_t size;
603
604 if (strlcpy(req.er_path, dir, sizeof(req.er_path)) >=
605 sizeof(req.er_path)) {
606 syslog(LOG_ERR, "%s: mount dir too long", dir);
607 errno = EINVAL;
608 return (-1);
609 }
610
611 req.er_args = *args;
612 if (args->ex_addrlen)
613 req.er_addr = *args->ex_addr;
614 if (args->ex_masklen)
615 req.er_mask = *args->ex_mask;
616
617 if (send_imsg(IMSG_EXPORT_REQ, &req, sizeof(req)) == -1)
618 return (-1);
619
620 size = recv_imsg(&imsg);
621 if (size == -1)
622 return (-1);
623 if (imsg.hdr.type != IMSG_EXPORT_RESP || size != sizeof(int)) {
624 syslog(LOG_ERR, "Invalid message");
625 imsg_free(&imsg);
626 errno = EINVAL;
627 return (-1);
628 }
629
630 if (*(int *)imsg.data != 0) {
631 errno = *(int *)imsg.data;
632 imsg_free(&imsg);
633 return (-1);
634 }
635
636 imsg_free(&imsg);
637 return (0);
638 }
639
640 ssize_t
recv_imsg(struct imsg * imsg)641 recv_imsg(struct imsg *imsg)
642 {
643 while (1) {
644 switch (imsg_get(&ibuf, imsg)) {
645 case -1:
646 syslog(LOG_ERR, "imsg_get: %m");
647 return (-1);
648 case 0:
649 break;
650 default:
651 return (imsg_get_len(imsg));
652 }
653
654 switch (imsgbuf_read(&ibuf)) {
655 case -1:
656 syslog(LOG_ERR, "imsgbuf_read: %m");
657 return (-1);
658 case 0:
659 syslog(LOG_ERR, "Socket disconnected");
660 errno = EINVAL;
661 return (-1);
662 }
663 }
664 }
665
666 int
send_imsg(u_int32_t type,void * data,u_int16_t size)667 send_imsg(u_int32_t type, void *data, u_int16_t size)
668 {
669 if (imsg_compose(&ibuf, type, 0, 0, -1, data, size) == -1) {
670 syslog(LOG_ERR, "imsg_compose: %m");
671 return (-1);
672 }
673
674 if (imsgbuf_flush(&ibuf) == -1) {
675 syslog(LOG_ERR, "imsgbuf_flush: %m");
676 return (-1);
677 }
678
679 return (0);
680 }
681
682 void
mountd_svc_run(void)683 mountd_svc_run(void)
684 {
685 struct pollfd *pfd = NULL, *newp;
686 nfds_t saved_max_pollfd = 0;
687 int nready, status;
688
689 for (;;) {
690 if (gotchld) {
691 if (waitpid(WAIT_ANY, &status, WNOHANG) == -1) {
692 syslog(LOG_ERR, "waitpid: %m");
693 break;
694 }
695 if (WIFEXITED(status)) {
696 syslog(LOG_ERR, "Child exited");
697 break;
698 }
699 if (WIFSIGNALED(status)) {
700 syslog(LOG_ERR, "Child terminated by signal");
701 break;
702 }
703 gotchld = 0;
704 }
705 if (gothup) {
706 get_exportlist();
707 gothup = 0;
708 }
709 if (gotterm)
710 break;
711 if (svc_max_pollfd > saved_max_pollfd) {
712 newp = reallocarray(pfd, svc_max_pollfd, sizeof(*pfd));
713 if (!newp) {
714 free(pfd);
715 perror("mountd_svc_run: - realloc failed");
716 return;
717 }
718 pfd = newp;
719 saved_max_pollfd = svc_max_pollfd;
720 }
721 memcpy(pfd, svc_pollfd, svc_max_pollfd * sizeof(*pfd));
722
723 nready = poll(pfd, svc_max_pollfd, INFTIM);
724 switch (nready) {
725 case -1:
726 if (errno == EINTR)
727 break;
728 perror("mountd_svc_run: - poll failed");
729 free(pfd);
730 return;
731 case 0:
732 break;
733 default:
734 svc_getreq_poll(pfd, nready);
735 break;
736 }
737 }
738
739 (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
740 xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
741 exit(0);
742 }
743
744 /*
745 * The mount rpc service
746 */
747 void
mntsrv(struct svc_req * rqstp,SVCXPRT * transp)748 mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
749 {
750 char rpcpath[RPCMNT_PATHLEN+1], dirpath[PATH_MAX];
751 struct hostent *hp = NULL;
752 struct exportlist *ep;
753 int defset, hostset;
754 struct fhreturn fhr;
755 struct dirlist *dp;
756 struct statfs fsb;
757 struct stat stb;
758 in_addr_t saddr;
759 u_short sport;
760 long bad = 0;
761
762 saddr = transp->xp_raddr.sin_addr.s_addr;
763 sport = ntohs(transp->xp_raddr.sin_port);
764 switch (rqstp->rq_proc) {
765 case NULLPROC:
766 if (!svc_sendreply(transp, xdr_void, NULL))
767 syslog(LOG_ERR, "Can't send reply");
768 return;
769 case RPCMNT_MOUNT:
770 if (debug)
771 fprintf(stderr, "Got mount request from %s\n",
772 inet_ntoa(transp->xp_raddr.sin_addr));
773 if (sport >= IPPORT_RESERVED) {
774 syslog(LOG_NOTICE,
775 "Refused mount RPC from host %s port %d",
776 inet_ntoa(transp->xp_raddr.sin_addr), sport);
777 svcerr_weakauth(transp);
778 return;
779 }
780 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
781 svcerr_decode(transp);
782 return;
783 }
784 if (debug)
785 fprintf(stderr, "rpcpath: %s\n", rpcpath);
786
787 /*
788 * Get the real pathname and make sure it is a file or
789 * directory that exists.
790 */
791 if (realpath(rpcpath, dirpath) == NULL) {
792 bad = errno;
793 if (debug)
794 fprintf(stderr, "realpath failed on %s\n",
795 rpcpath);
796 strlcpy(dirpath, rpcpath, sizeof(dirpath));
797 } else if (stat(dirpath, &stb) == -1 ||
798 (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
799 statfs(dirpath, &fsb) == -1) {
800 if (debug)
801 fprintf(stderr, "stat failed on %s\n", dirpath);
802 bad = ENOENT; /* We will send error reply later */
803 }
804
805 /* Check in the exports list */
806 ep = bad ? NULL : ex_search(&fsb.f_fsid);
807 hostset = defset = 0;
808 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
809 ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
810 chk_host(dp, saddr, &defset, &hostset)) ||
811 (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
812 scan_tree(ep->ex_dirl, saddr) == 0))) {
813 if (bad) {
814 if (!svc_sendreply(transp, xdr_long,
815 (caddr_t)&bad))
816 syslog(LOG_ERR, "Can't send reply");
817 return;
818 }
819 if (hostset & DP_HOSTSET)
820 fhr.fhr_flag = hostset;
821 else
822 fhr.fhr_flag = defset;
823 fhr.fhr_vers = rqstp->rq_vers;
824 /* Get the file handle */
825 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
826 if (imsg_getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
827 bad = errno;
828 syslog(LOG_ERR, "Can't get fh for %s", dirpath);
829 if (!svc_sendreply(transp, xdr_long,
830 (caddr_t)&bad))
831 syslog(LOG_ERR, "Can't send reply");
832 return;
833 }
834 if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
835 syslog(LOG_ERR, "Can't send reply");
836 if (hp == NULL)
837 hp = gethostbyaddr((caddr_t)&saddr,
838 sizeof(saddr), AF_INET);
839 if (hp)
840 add_mlist(hp->h_name, dirpath);
841 else
842 add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),
843 dirpath);
844 if (debug) {
845 fprintf(stderr,
846 "Mount successful for %s by %s.\n",
847 dirpath,
848 inet_ntoa(transp->xp_raddr.sin_addr));
849 }
850 } else
851 bad = EACCES;
852
853 if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad))
854 syslog(LOG_ERR, "Can't send reply");
855 return;
856 case RPCMNT_DUMP:
857 if (!svc_sendreply(transp, xdr_mlist, NULL))
858 syslog(LOG_ERR, "Can't send reply");
859 return;
860 case RPCMNT_UMOUNT:
861 if (sport >= IPPORT_RESERVED) {
862 svcerr_weakauth(transp);
863 return;
864 }
865 if (!svc_getargs(transp, xdr_dir, dirpath)) {
866 svcerr_decode(transp);
867 return;
868 }
869 if (!svc_sendreply(transp, xdr_void, NULL))
870 syslog(LOG_ERR, "Can't send reply");
871 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
872 if (hp)
873 del_mlist(hp->h_name, dirpath);
874 del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
875 return;
876 case RPCMNT_UMNTALL:
877 if (sport >= IPPORT_RESERVED) {
878 svcerr_weakauth(transp);
879 return;
880 }
881 if (!svc_sendreply(transp, xdr_void, NULL))
882 syslog(LOG_ERR, "Can't send reply");
883 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
884 if (hp)
885 del_mlist(hp->h_name, NULL);
886 del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), NULL);
887 return;
888 case RPCMNT_EXPORT:
889 if (!svc_sendreply(transp, xdr_explist, NULL))
890 syslog(LOG_ERR, "Can't send reply");
891 return;
892 default:
893 svcerr_noproc(transp);
894 return;
895 }
896 }
897
898 /*
899 * Xdr conversion for a dirpath string
900 */
901 int
xdr_dir(XDR * xdrsp,char * dirp)902 xdr_dir(XDR *xdrsp, char *dirp)
903 {
904 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
905 }
906
907 /*
908 * Xdr routine to generate file handle reply
909 */
910 int
xdr_fhs(XDR * xdrsp,caddr_t cp)911 xdr_fhs(XDR *xdrsp, caddr_t cp)
912 {
913 struct fhreturn *fhrp = (struct fhreturn *)cp;
914 long ok = 0, len, auth;
915
916 if (!xdr_long(xdrsp, &ok))
917 return (0);
918 switch (fhrp->fhr_vers) {
919 case 1:
920 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
921 case 3:
922 len = NFSX_V3FH;
923 if (!xdr_long(xdrsp, &len))
924 return (0);
925 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
926 return (0);
927 auth = RPCAUTH_UNIX;
928 len = 1;
929 if (!xdr_long(xdrsp, &len))
930 return (0);
931 return (xdr_long(xdrsp, &auth));
932 }
933 return (0);
934 }
935
936 int
xdr_mlist(XDR * xdrsp,caddr_t cp)937 xdr_mlist(XDR *xdrsp, caddr_t cp)
938 {
939 int true = 1, false = 0;
940 struct mountlist *mlp;
941 char *strp;
942
943 mlp = mlhead;
944 while (mlp) {
945 if (!xdr_bool(xdrsp, &true))
946 return (0);
947 strp = &mlp->ml_host[0];
948 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
949 return (0);
950 strp = &mlp->ml_dirp[0];
951 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
952 return (0);
953 mlp = mlp->ml_next;
954 }
955 if (!xdr_bool(xdrsp, &false))
956 return (0);
957 return (1);
958 }
959
960 /*
961 * Xdr conversion for export list
962 */
963 int
xdr_explist(XDR * xdrsp,caddr_t cp)964 xdr_explist(XDR *xdrsp, caddr_t cp)
965 {
966 struct exportlist *ep;
967 int false = 0, putdef;
968
969 ep = exphead;
970 while (ep) {
971 putdef = 0;
972 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
973 goto errout;
974 if (ep->ex_defdir && putdef == 0 && put_exlist(ep->ex_defdir,
975 xdrsp, NULL, &putdef))
976 goto errout;
977 ep = ep->ex_next;
978 }
979 if (!xdr_bool(xdrsp, &false))
980 return (0);
981 return (1);
982 errout:
983 return (0);
984 }
985
986 /*
987 * Called from xdr_explist() to traverse the tree and export the
988 * directory paths.
989 */
990 int
put_exlist(struct dirlist * dp,XDR * xdrsp,struct dirlist * adp,int * putdefp)991 put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp,
992 int *putdefp)
993 {
994 int true = 1, false = 0, gotalldir = 0;
995 struct grouplist *grp;
996 struct hostlist *hp;
997 char *strp;
998
999 if (dp) {
1000 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
1001 return (1);
1002 if (!xdr_bool(xdrsp, &true))
1003 return (1);
1004 strp = dp->dp_dirp;
1005 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
1006 return (1);
1007 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1008 gotalldir = 1;
1009 *putdefp = 1;
1010 }
1011 if ((dp->dp_flag & DP_DEFSET) == 0 &&
1012 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1013 hp = dp->dp_hosts;
1014 while (hp) {
1015 grp = hp->ht_grp;
1016 if (grp->gr_type == GT_HOST) {
1017 if (!xdr_bool(xdrsp, &true))
1018 return (1);
1019 strp = grp->gr_ptr.gt_hostent->h_name;
1020 if (!xdr_string(xdrsp, &strp,
1021 RPCMNT_NAMELEN))
1022 return (1);
1023 } else if (grp->gr_type == GT_NET) {
1024 if (!xdr_bool(xdrsp, &true))
1025 return (1);
1026 strp = grp->gr_ptr.gt_net.nt_name;
1027 if (!xdr_string(xdrsp, &strp,
1028 RPCMNT_NAMELEN))
1029 return (1);
1030 }
1031 hp = hp->ht_next;
1032 if (gotalldir && hp == NULL) {
1033 hp = adp->dp_hosts;
1034 gotalldir = 0;
1035 }
1036 }
1037 }
1038 if (!xdr_bool(xdrsp, &false))
1039 return (1);
1040 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
1041 return (1);
1042 }
1043 return (0);
1044 }
1045
1046 #define LINESIZ 10240
1047 char line[LINESIZ];
1048 FILE *exp_file;
1049
1050 void
new_exportlist(int signo)1051 new_exportlist(int signo)
1052 {
1053 gothup = 1;
1054 }
1055
1056 /*
1057 * Get the export list
1058 */
1059 void
get_exportlist(void)1060 get_exportlist(void)
1061 {
1062 int len, has_host, exflags, got_nondir, dirplen = 0, num;
1063 int lookup_failed, num_hosts, i, netgrp;
1064 char *cp, *endcp, *dirp = NULL, *hst, *usr, *dom, savedc;
1065 struct exportlist *ep, *ep2;
1066 struct grouplist *grp, *tgrp;
1067 struct exportlist **epp;
1068 struct dirlist *dirhead;
1069 struct statfs fsb, *ofsp, *fsp;
1070 struct hostent *hpe;
1071 struct xucred anon;
1072 struct fsarray {
1073 int exflags;
1074 char *mntonname;
1075 } *fstbl;
1076
1077 /*
1078 * First, get rid of the old list
1079 */
1080 ep = exphead;
1081 while (ep) {
1082 ep2 = ep;
1083 ep = ep->ex_next;
1084 free_exp(ep2);
1085 }
1086 exphead = NULL;
1087
1088 grp = grphead;
1089 while (grp) {
1090 tgrp = grp;
1091 grp = grp->gr_next;
1092 free_grp(tgrp);
1093 }
1094 grphead = NULL;
1095
1096 /*
1097 * And delete exports that are in the kernel for all local
1098 * file systems.
1099 * XXX: Should know how to handle all local exportable file systems
1100 * instead of just MOUNT_FFS.
1101 */
1102 num = getmntinfo(&ofsp, MNT_NOWAIT);
1103 if (num == 0 && errno)
1104 syslog(LOG_ERR, "getmntinfo: %s", strerror(errno));
1105
1106 fsp = ofsp;
1107
1108 fstbl = calloc(num, sizeof (fstbl[0]));
1109 if (fstbl == NULL)
1110 out_of_mem();
1111
1112 for (i = 0; i < num; i++) {
1113
1114 if (!strncmp(fsp->f_fstypename, MOUNT_MFS, MFSNAMELEN) ||
1115 !strncmp(fsp->f_fstypename, MOUNT_FFS, MFSNAMELEN) ||
1116 !strncmp(fsp->f_fstypename, MOUNT_EXT2FS, MFSNAMELEN) ||
1117 !strncmp(fsp->f_fstypename, MOUNT_MSDOS, MFSNAMELEN) ||
1118 !strncmp(fsp->f_fstypename, MOUNT_CD9660, MFSNAMELEN)) {
1119 fstbl[i].exflags = MNT_DELEXPORT;
1120 fstbl[i].mntonname = fsp->f_mntonname;
1121 }
1122 fsp++;
1123 }
1124
1125 /*
1126 * Read in the exports file and build the list, calling mount() through
1127 * the privileged child as we go along to push the export rules into
1128 * the kernel.
1129 */
1130 if ((exp_file = fopen(exname, "r")) == NULL) {
1131 syslog(LOG_ERR, "Can't open %s", exname);
1132 exit(2);
1133 }
1134 dirhead = NULL;
1135 while (get_line()) {
1136 if (debug)
1137 fprintf(stderr, "Got line %s\n",line);
1138 cp = line;
1139 nextfield(&cp, &endcp);
1140 if (*cp == '#')
1141 goto nextline;
1142
1143 /*
1144 * Set defaults.
1145 */
1146 has_host = FALSE;
1147 num_hosts = 0;
1148 lookup_failed = FALSE;
1149 anon = def_anon;
1150 exflags = MNT_EXPORTED;
1151 got_nondir = 0;
1152 opt_flags = 0;
1153 ep = NULL;
1154
1155 /*
1156 * Create new exports list entry
1157 */
1158 len = endcp-cp;
1159 tgrp = grp = get_grp();
1160 while (len > 0) {
1161 if (len > RPCMNT_NAMELEN) {
1162 getexp_err(ep, tgrp);
1163 goto nextline;
1164 }
1165 if (*cp == '-') {
1166 if (ep == NULL) {
1167 getexp_err(ep, tgrp);
1168 goto nextline;
1169 }
1170 if (debug)
1171 fprintf(stderr, "doing opt %s\n", cp);
1172 got_nondir = 1;
1173 if (do_opt(&cp, &endcp, ep, grp, &has_host,
1174 &exflags, &anon)) {
1175 getexp_err(ep, tgrp);
1176 goto nextline;
1177 }
1178 } else if (*cp == '/') {
1179 savedc = *endcp;
1180 *endcp = '\0';
1181 if (check_dirpath(cp) &&
1182 statfs(cp, &fsb) >= 0) {
1183 if (got_nondir) {
1184 syslog(LOG_ERR, "Dirs must be first");
1185 getexp_err(ep, tgrp);
1186 goto nextline;
1187 }
1188 if (ep) {
1189 if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
1190 ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
1191 getexp_err(ep, tgrp);
1192 goto nextline;
1193 }
1194 } else {
1195 /*
1196 * See if this directory is already
1197 * in the list.
1198 */
1199 ep = ex_search(&fsb.f_fsid);
1200 if (ep == NULL) {
1201 int len;
1202
1203 ep = get_exp();
1204 ep->ex_fs = fsb.f_fsid;
1205 len = strlen(fsb.f_mntonname) + 1;
1206 ep->ex_fsdir = malloc(len);
1207 if (ep->ex_fsdir)
1208 strlcpy(ep->ex_fsdir,
1209 fsb.f_mntonname, len);
1210 else
1211 out_of_mem();
1212 if (debug)
1213 fprintf(stderr,
1214 "Making new ep fs=0x%x,0x%x\n",
1215 fsb.f_fsid.val[0],
1216 fsb.f_fsid.val[1]);
1217 } else if (debug)
1218 fprintf(stderr,
1219 "Found ep fs=0x%x,0x%x\n",
1220 fsb.f_fsid.val[0],
1221 fsb.f_fsid.val[1]);
1222 }
1223
1224 /*
1225 * Add dirpath to export mount point.
1226 */
1227 dirp = add_expdir(&dirhead, cp, len);
1228 dirplen = len;
1229 } else {
1230 getexp_err(ep, tgrp);
1231 goto nextline;
1232 }
1233 *endcp = savedc;
1234 } else {
1235 savedc = *endcp;
1236 *endcp = '\0';
1237 got_nondir = 1;
1238 if (ep == NULL) {
1239 getexp_err(ep, tgrp);
1240 goto nextline;
1241 }
1242
1243 /*
1244 * Get the host or netgroup.
1245 */
1246 setnetgrent(cp);
1247 netgrp = getnetgrent((const char **)&hst,
1248 (const char **)&usr, (const char **)&dom);
1249 do {
1250 if (has_host) {
1251 grp->gr_next = get_grp();
1252 grp = grp->gr_next;
1253 } else {
1254 memset(grp, 0, sizeof(*grp));
1255 }
1256 if (netgrp) {
1257 if (hst == NULL) {
1258 syslog(LOG_ERR,
1259 "NULL hostname in netgroup %s, skipping",
1260 cp);
1261 grp->gr_type = GT_IGNORE;
1262 lookup_failed = TRUE;
1263 continue;
1264 } else if (get_host(hst, grp, tgrp)) {
1265 syslog(LOG_ERR,
1266 "Unknown host (%s) in netgroup %s",
1267 hst, cp);
1268 grp->gr_type = GT_IGNORE;
1269 lookup_failed = TRUE;
1270 continue;
1271 }
1272 } else if (get_host(cp, grp, tgrp)) {
1273 syslog(LOG_ERR,
1274 "Unknown host (%s) in line %s",
1275 cp, line);
1276 grp->gr_type = GT_IGNORE;
1277 lookup_failed = TRUE;
1278 continue;
1279 }
1280 has_host = TRUE;
1281 num_hosts++;
1282 } while (netgrp && getnetgrent((const char **)&hst,
1283 (const char **)&usr, (const char **)&dom));
1284 endnetgrent();
1285 *endcp = savedc;
1286 }
1287 cp = endcp;
1288 nextfield(&cp, &endcp);
1289 len = endcp - cp;
1290 }
1291 /*
1292 * If the exports list is empty due to unresolvable hostnames
1293 * we throw away the line.
1294 */
1295 if (lookup_failed == TRUE && num_hosts == 0 &&
1296 tgrp->gr_type == GT_IGNORE) {
1297 getexp_err(ep, tgrp);
1298 goto nextline;
1299 }
1300 if (check_options(dirhead)) {
1301 getexp_err(ep, tgrp);
1302 goto nextline;
1303 }
1304 if (!has_host) {
1305 grp->gr_type = GT_HOST;
1306 if (debug)
1307 fprintf(stderr, "Adding a default entry\n");
1308 /* add a default group and make the grp list NULL */
1309 hpe = malloc(sizeof(struct hostent));
1310 if (hpe == NULL)
1311 out_of_mem();
1312 hpe->h_name = strdup("Default");
1313 if (hpe->h_name == NULL)
1314 out_of_mem();
1315 hpe->h_addrtype = AF_INET;
1316 hpe->h_length = sizeof (u_int32_t);
1317 hpe->h_addr_list = NULL;
1318 grp->gr_ptr.gt_hostent = hpe;
1319
1320 /*
1321 * Don't allow a network export coincide with a list of
1322 * host(s) on the same line.
1323 */
1324 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1325 getexp_err(ep, tgrp);
1326 goto nextline;
1327 }
1328
1329 /*
1330 * Loop through hosts, pushing the exports into the kernel.
1331 * After loop, tgrp points to the start of the list and
1332 * grp points to the last entry in the list.
1333 */
1334 grp = tgrp;
1335 do {
1336
1337 /*
1338 * remove filesystem from unexport list
1339 * add MNT_DELEXPORT to exflags to clean up
1340 * any old addrlist in the kernel
1341 */
1342
1343 for (i = 0; i < num; i++) {
1344 if ((fstbl[i].mntonname != NULL) &&
1345 (strcmp(fsb.f_mntonname,
1346 fstbl[i].mntonname) == 0) &&
1347 (fstbl[i].exflags & MNT_DELEXPORT)) {
1348 exflags |= MNT_DELEXPORT;
1349 fstbl[i].exflags = 0;
1350 if (debug)
1351 fprintf(stderr, "removing %s %s from unexport list\n", dirp, fstbl[i].mntonname);
1352 }
1353 }
1354
1355 if (debug)
1356 fprintf(stderr, "exporting %s\n", dirp);
1357 /*
1358 * Non-zero return indicates an error. Return
1359 * val of 1 means line is invalid (not just entry).
1360 */
1361 i = do_mount(ep, grp, exflags, &anon, dirp, dirplen);
1362 exflags &= ~MNT_DELEXPORT;
1363 if (i == 1) {
1364 getexp_err(ep, tgrp);
1365 goto nextline;
1366 } else if (i == 2) {
1367 syslog(LOG_ERR,
1368 "Bad exports list entry (%s) in line %s",
1369 (grp->gr_type == GT_HOST)
1370 ? grp->gr_ptr.gt_hostent->h_name
1371 : (grp->gr_type == GT_NET)
1372 ? grp->gr_ptr.gt_net.nt_name
1373 : "Unknown", line);
1374 }
1375 } while (grp->gr_next && (grp = grp->gr_next));
1376
1377 /*
1378 * Success. Update the data structures.
1379 */
1380 if (has_host) {
1381 hang_dirp(dirhead, tgrp, ep, opt_flags);
1382 grp->gr_next = grphead;
1383 grphead = tgrp;
1384 } else {
1385 hang_dirp(dirhead, NULL, ep,
1386 opt_flags);
1387 free_grp(grp);
1388 }
1389 dirhead = NULL;
1390 if ((ep->ex_flag & EX_LINKED) == 0) {
1391 ep2 = exphead;
1392 epp = &exphead;
1393
1394 /*
1395 * Insert in the list in alphabetical order.
1396 */
1397 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1398 epp = &ep2->ex_next;
1399 ep2 = ep2->ex_next;
1400 }
1401 if (ep2)
1402 ep->ex_next = ep2;
1403 *epp = ep;
1404 ep->ex_flag |= EX_LINKED;
1405 }
1406 nextline:
1407 if (dirhead) {
1408 free_dir(dirhead);
1409 dirhead = NULL;
1410 }
1411 }
1412
1413 fsp = ofsp;
1414 for (i = 0; i < num; i++, fsp++) {
1415 if ((fstbl[i].exflags & MNT_DELEXPORT) == 0)
1416 continue;
1417 if (debug)
1418 fprintf(stderr, "unexporting %s %s\n",
1419 fsp->f_mntonname, fstbl[i].mntonname);
1420 send_imsg(IMSG_DELEXPORT, fsp->f_mntonname,
1421 sizeof(fsp->f_mntonname));
1422 }
1423 free(fstbl);
1424 fclose(exp_file);
1425 }
1426
1427 /*
1428 * Allocate an export list element
1429 */
1430 struct exportlist *
get_exp(void)1431 get_exp(void)
1432 {
1433 struct exportlist *ep;
1434
1435 ep = calloc(1, sizeof (struct exportlist));
1436 if (ep == NULL)
1437 out_of_mem();
1438 return (ep);
1439 }
1440
1441 /*
1442 * Allocate a group list element
1443 */
1444 struct grouplist *
get_grp(void)1445 get_grp(void)
1446 {
1447 struct grouplist *gp;
1448
1449 gp = calloc(1, sizeof (struct grouplist));
1450 if (gp == NULL)
1451 out_of_mem();
1452 return (gp);
1453 }
1454
1455 /*
1456 * Clean up upon an error in get_exportlist().
1457 */
1458 void
getexp_err(struct exportlist * ep,struct grouplist * grp)1459 getexp_err(struct exportlist *ep, struct grouplist *grp)
1460 {
1461 struct grouplist *tgrp;
1462
1463 syslog(LOG_ERR, "Bad exports list line %s", line);
1464 if (ep && (ep->ex_flag & EX_LINKED) == 0)
1465 free_exp(ep);
1466 while (grp) {
1467 tgrp = grp;
1468 grp = grp->gr_next;
1469 free_grp(tgrp);
1470 }
1471 }
1472
1473 /*
1474 * Search the export list for a matching fs.
1475 */
1476 struct exportlist *
ex_search(fsid_t * fsid)1477 ex_search(fsid_t *fsid)
1478 {
1479 struct exportlist *ep;
1480
1481 ep = exphead;
1482 while (ep) {
1483 if (ep->ex_fs.val[0] == fsid->val[0] &&
1484 ep->ex_fs.val[1] == fsid->val[1])
1485 return (ep);
1486 ep = ep->ex_next;
1487 }
1488 return (ep);
1489 }
1490
1491 /*
1492 * Add a directory path to the list.
1493 */
1494 char *
add_expdir(struct dirlist ** dpp,char * cp,int len)1495 add_expdir(struct dirlist **dpp, char *cp, int len)
1496 {
1497 struct dirlist *dp;
1498
1499 /* do not need +1 because of dp_dirp[1] */
1500 dp = malloc(sizeof (struct dirlist) + len);
1501 if (dp == NULL)
1502 out_of_mem();
1503 dp->dp_left = *dpp;
1504 dp->dp_right = NULL;
1505 dp->dp_flag = 0;
1506 dp->dp_hosts = NULL;
1507 strlcpy(dp->dp_dirp, cp, len + 1);
1508 *dpp = dp;
1509 return (dp->dp_dirp);
1510 }
1511
1512 /*
1513 * Hang the dir list element off the dirpath binary tree as required
1514 * and update the entry for host.
1515 */
1516 void
hang_dirp(struct dirlist * dp,struct grouplist * grp,struct exportlist * ep,int flags)1517 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1518 int flags)
1519 {
1520 struct hostlist *hp;
1521 struct dirlist *dp2;
1522
1523 if (flags & OP_ALLDIRS) {
1524 if (ep->ex_defdir)
1525 free((caddr_t)dp);
1526 else
1527 ep->ex_defdir = dp;
1528 if (grp == NULL) {
1529 ep->ex_defdir->dp_flag |= DP_DEFSET;
1530 } else while (grp) {
1531 hp = get_ht();
1532 hp->ht_grp = grp;
1533 hp->ht_next = ep->ex_defdir->dp_hosts;
1534 ep->ex_defdir->dp_hosts = hp;
1535 grp = grp->gr_next;
1536 }
1537 } else {
1538
1539 /*
1540 * Loop through the directories adding them to the tree.
1541 */
1542 while (dp) {
1543 dp2 = dp->dp_left;
1544 add_dlist(&ep->ex_dirl, dp, grp, flags);
1545 dp = dp2;
1546 }
1547 }
1548 }
1549
1550 /*
1551 * Traverse the binary tree either updating a node that is already there
1552 * for the new directory or adding the new node.
1553 */
1554 void
add_dlist(struct dirlist ** dpp,struct dirlist * newdp,struct grouplist * grp,int flags)1555 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
1556 int flags)
1557 {
1558 struct dirlist *dp;
1559 struct hostlist *hp;
1560 int cmp;
1561
1562 dp = *dpp;
1563 if (dp) {
1564 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1565 if (cmp > 0) {
1566 add_dlist(&dp->dp_left, newdp, grp, flags);
1567 return;
1568 } else if (cmp < 0) {
1569 add_dlist(&dp->dp_right, newdp, grp, flags);
1570 return;
1571 } else
1572 free((caddr_t)newdp);
1573 } else {
1574 dp = newdp;
1575 dp->dp_left = NULL;
1576 *dpp = dp;
1577 }
1578 if (grp) {
1579
1580 /*
1581 * Hang all of the host(s) off of the directory point.
1582 */
1583 do {
1584 hp = get_ht();
1585 hp->ht_grp = grp;
1586 hp->ht_next = dp->dp_hosts;
1587 dp->dp_hosts = hp;
1588 grp = grp->gr_next;
1589 } while (grp);
1590 } else {
1591 dp->dp_flag |= DP_DEFSET;
1592 }
1593 }
1594
1595 /*
1596 * Search for a dirpath on the export point.
1597 */
1598 struct dirlist *
dirp_search(struct dirlist * dp,char * dirpath)1599 dirp_search(struct dirlist *dp, char *dirpath)
1600 {
1601 int cmp;
1602
1603 if (dp) {
1604 cmp = strcmp(dp->dp_dirp, dirpath);
1605 if (cmp > 0)
1606 return (dirp_search(dp->dp_left, dirpath));
1607 else if (cmp < 0)
1608 return (dirp_search(dp->dp_right, dirpath));
1609 else
1610 return (dp);
1611 }
1612 return (dp);
1613 }
1614
1615 /*
1616 * Scan for a host match in a directory tree.
1617 */
1618 int
chk_host(struct dirlist * dp,in_addr_t saddr,int * defsetp,int * hostsetp)1619 chk_host(struct dirlist *dp, in_addr_t saddr, int *defsetp, int *hostsetp)
1620 {
1621 struct hostlist *hp;
1622 struct grouplist *grp;
1623 u_int32_t **addrp;
1624
1625 if (dp) {
1626 if (dp->dp_flag & DP_DEFSET)
1627 *defsetp = dp->dp_flag;
1628 hp = dp->dp_hosts;
1629 while (hp) {
1630 grp = hp->ht_grp;
1631 switch (grp->gr_type) {
1632 case GT_HOST:
1633 addrp = (u_int32_t **)
1634 grp->gr_ptr.gt_hostent->h_addr_list;
1635 while (*addrp) {
1636 if (**addrp == saddr) {
1637 *hostsetp = (hp->ht_flag | DP_HOSTSET);
1638 return (1);
1639 }
1640 addrp++;
1641 }
1642 break;
1643 case GT_NET:
1644 if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
1645 grp->gr_ptr.gt_net.nt_net) {
1646 *hostsetp = (hp->ht_flag | DP_HOSTSET);
1647 return (1);
1648 }
1649 break;
1650 }
1651 hp = hp->ht_next;
1652 }
1653 }
1654 return (0);
1655 }
1656
1657 /*
1658 * Scan tree for a host that matches the address.
1659 */
1660 int
scan_tree(struct dirlist * dp,in_addr_t saddr)1661 scan_tree(struct dirlist *dp, in_addr_t saddr)
1662 {
1663 int defset, hostset;
1664
1665 if (dp) {
1666 if (scan_tree(dp->dp_left, saddr))
1667 return (1);
1668 if (chk_host(dp, saddr, &defset, &hostset))
1669 return (1);
1670 if (scan_tree(dp->dp_right, saddr))
1671 return (1);
1672 }
1673 return (0);
1674 }
1675
1676 /*
1677 * Traverse the dirlist tree and free it up.
1678 */
1679 void
free_dir(struct dirlist * dp)1680 free_dir(struct dirlist *dp)
1681 {
1682
1683 if (dp) {
1684 free_dir(dp->dp_left);
1685 free_dir(dp->dp_right);
1686 free_host(dp->dp_hosts);
1687 free((caddr_t)dp);
1688 }
1689 }
1690
1691 /*
1692 * Parse the option string and update fields.
1693 * Option arguments may either be -<option>=<value> or
1694 * -<option> <value>
1695 */
1696 int
do_opt(char ** cpp,char ** endcpp,struct exportlist * ep,struct grouplist * grp,int * has_hostp,int * exflagsp,struct xucred * cr)1697 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
1698 int *has_hostp, int *exflagsp, struct xucred *cr)
1699 {
1700 char *cp, *endcp, *cpopt, savedc, savedc2 = 0;
1701 char *cpoptarg, *cpoptend;
1702 int allflag, usedarg;
1703
1704 cpopt = *cpp;
1705 cpopt++;
1706 cp = *endcpp;
1707 savedc = *cp;
1708 *cp = '\0';
1709 while (cpopt && *cpopt) {
1710 allflag = 1;
1711 usedarg = -2;
1712 if ((cpoptend = strchr(cpopt, ','))) {
1713 *cpoptend++ = '\0';
1714 if ((cpoptarg = strchr(cpopt, '=')))
1715 *cpoptarg++ = '\0';
1716 } else {
1717 if ((cpoptarg = strchr(cpopt, '=')))
1718 *cpoptarg++ = '\0';
1719 else {
1720 *cp = savedc;
1721 nextfield(&cp, &endcp);
1722 **endcpp = '\0';
1723 if (endcp > cp && *cp != '-') {
1724 cpoptarg = cp;
1725 savedc2 = *endcp;
1726 *endcp = '\0';
1727 usedarg = 0;
1728 }
1729 }
1730 }
1731 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1732 *exflagsp |= MNT_EXRDONLY;
1733 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1734 !(allflag = strcmp(cpopt, "mapall")) ||
1735 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1736 usedarg++;
1737 parsecred(cpoptarg, cr);
1738 if (allflag == 0) {
1739 *exflagsp |= MNT_EXPORTANON;
1740 opt_flags |= OP_MAPALL;
1741 } else
1742 opt_flags |= OP_MAPROOT;
1743 } else
1744 if (cpoptarg && (!strcmp(cpopt, "mask") ||
1745 !strcmp(cpopt, "m"))) {
1746 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1747 syslog(LOG_ERR, "Bad mask: %s", cpoptarg);
1748 return (1);
1749 }
1750 usedarg++;
1751 opt_flags |= OP_MASK;
1752 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1753 !strcmp(cpopt, "n"))) {
1754 if (grp->gr_type != GT_NULL) {
1755 syslog(LOG_ERR, "Network/host conflict");
1756 return (1);
1757 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1758 syslog(LOG_ERR, "Bad net: %s", cpoptarg);
1759 return (1);
1760 }
1761 grp->gr_type = GT_NET;
1762 *has_hostp = 1;
1763 usedarg++;
1764 opt_flags |= OP_NET;
1765 } else if (!strcmp(cpopt, "alldirs")) {
1766 opt_flags |= OP_ALLDIRS;
1767 } else {
1768 syslog(LOG_ERR, "Bad opt %s", cpopt);
1769 return (1);
1770 }
1771 if (usedarg >= 0) {
1772 *endcp = savedc2;
1773 **endcpp = savedc;
1774 if (usedarg > 0) {
1775 *cpp = cp;
1776 *endcpp = endcp;
1777 }
1778 return (0);
1779 }
1780 cpopt = cpoptend;
1781 }
1782 **endcpp = savedc;
1783 return (0);
1784 }
1785
1786 /*
1787 * Translate a character string to the corresponding list of network
1788 * addresses for a hostname.
1789 */
1790 int
get_host(char * cp,struct grouplist * grp,struct grouplist * tgrp)1791 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
1792 {
1793 struct hostent *hp, *nhp, t_host;
1794 struct grouplist *checkgrp;
1795 char **addrp, **naddrp;
1796 struct in_addr saddr;
1797 char *aptr[2];
1798 int i;
1799
1800 if (grp->gr_type != GT_NULL)
1801 return (1);
1802 if ((hp = gethostbyname(cp)) == NULL) {
1803 if (isdigit((unsigned char)*cp)) {
1804 if (inet_aton(cp, &saddr) == 0) {
1805 syslog(LOG_ERR, "inet_aton failed for %s", cp);
1806 return (1);
1807 }
1808 if ((hp = gethostbyaddr((caddr_t)&saddr.s_addr,
1809 sizeof (saddr.s_addr), AF_INET)) == NULL) {
1810 hp = &t_host;
1811 hp->h_name = cp;
1812 hp->h_addrtype = AF_INET;
1813 hp->h_length = sizeof (u_int32_t);
1814 hp->h_addr_list = aptr;
1815 aptr[0] = (char *)&saddr;
1816 aptr[1] = NULL;
1817 }
1818 } else {
1819 syslog(LOG_ERR, "gethostbyname; failed for %s: %s", cp,
1820 hstrerror(h_errno));
1821 return (1);
1822 }
1823 }
1824
1825 /* only insert each host onto the list once */
1826 for (checkgrp = tgrp; checkgrp; checkgrp = checkgrp->gr_next) {
1827 if (checkgrp->gr_type == GT_HOST &&
1828 checkgrp->gr_ptr.gt_hostent != NULL &&
1829 !strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)) {
1830 grp->gr_type = GT_IGNORE;
1831 return (0);
1832 }
1833 }
1834
1835 grp->gr_type = GT_HOST;
1836 nhp = grp->gr_ptr.gt_hostent = malloc(sizeof(struct hostent));
1837 if (nhp == NULL)
1838 out_of_mem();
1839 memcpy(nhp, hp, sizeof(struct hostent));
1840 i = strlen(hp->h_name)+1;
1841 nhp->h_name = malloc(i);
1842 if (nhp->h_name == NULL)
1843 out_of_mem();
1844 memcpy(nhp->h_name, hp->h_name, i);
1845 addrp = hp->h_addr_list;
1846 i = 1;
1847 while (*addrp++)
1848 i++;
1849 naddrp = nhp->h_addr_list = reallocarray(NULL, i, sizeof(char *));
1850 if (naddrp == NULL)
1851 out_of_mem();
1852 addrp = hp->h_addr_list;
1853 while (*addrp) {
1854 *naddrp = malloc(hp->h_length);
1855 if (*naddrp == NULL)
1856 out_of_mem();
1857 memcpy(*naddrp, *addrp, hp->h_length);
1858 addrp++;
1859 naddrp++;
1860 }
1861 *naddrp = NULL;
1862 if (debug)
1863 fprintf(stderr, "got host %s\n", hp->h_name);
1864 return (0);
1865 }
1866
1867 /*
1868 * Free up an exports list component
1869 */
1870 void
free_exp(struct exportlist * ep)1871 free_exp(struct exportlist *ep)
1872 {
1873
1874 if (ep->ex_defdir) {
1875 free_host(ep->ex_defdir->dp_hosts);
1876 free((caddr_t)ep->ex_defdir);
1877 }
1878 free(ep->ex_fsdir);
1879 free_dir(ep->ex_dirl);
1880 free((caddr_t)ep);
1881 }
1882
1883 /*
1884 * Free hosts.
1885 */
1886 void
free_host(struct hostlist * hp)1887 free_host(struct hostlist *hp)
1888 {
1889 struct hostlist *hp2;
1890
1891 while (hp) {
1892 hp2 = hp;
1893 hp = hp->ht_next;
1894 free((caddr_t)hp2);
1895 }
1896 }
1897
1898 struct hostlist *
get_ht(void)1899 get_ht(void)
1900 {
1901 struct hostlist *hp;
1902
1903 hp = malloc(sizeof (struct hostlist));
1904 if (hp == NULL)
1905 out_of_mem();
1906 hp->ht_next = NULL;
1907 hp->ht_flag = 0;
1908 return (hp);
1909 }
1910
1911 /*
1912 * Out of memory, fatal
1913 */
1914 void
out_of_mem(void)1915 out_of_mem(void)
1916 {
1917
1918 syslog(LOG_ERR, "Out of memory");
1919 exit(2);
1920 }
1921
1922 /*
1923 * Do the mount syscall with the update flag to push the export info into
1924 * the kernel. Returns 0 on success, 1 for fatal error, and 2 for error
1925 * that only invalidates the specific entry/host.
1926 */
1927 int
do_mount(struct exportlist * ep,struct grouplist * grp,int exflags,struct xucred * anoncrp,char * dirp,int dirplen)1928 do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
1929 struct xucred *anoncrp, char *dirp, int dirplen)
1930 {
1931 struct sockaddr_in sin, imask;
1932 struct export_args args;
1933 char savedc = '\0';
1934 u_int32_t **addrp;
1935 char *cp = NULL;
1936 in_addr_t net;
1937 int done;
1938
1939 args.ex_flags = exflags;
1940 args.ex_anon = *anoncrp;
1941 memset(&sin, 0, sizeof(sin));
1942 memset(&imask, 0, sizeof(imask));
1943 sin.sin_family = AF_INET;
1944 sin.sin_len = sizeof(sin);
1945 imask.sin_family = AF_INET;
1946 imask.sin_len = sizeof(sin);
1947 if (grp->gr_type == GT_HOST)
1948 addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list;
1949 else
1950 addrp = NULL;
1951
1952 done = FALSE;
1953 while (!done) {
1954 switch (grp->gr_type) {
1955 case GT_HOST:
1956 args.ex_addr = (struct sockaddr *)&sin;
1957 args.ex_masklen = 0;
1958 if (!addrp) {
1959 args.ex_addrlen = 0;
1960 break;
1961 }
1962 sin.sin_addr.s_addr = **addrp;
1963 args.ex_addrlen = sizeof(sin);
1964 break;
1965 case GT_NET:
1966 sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
1967 args.ex_addr = (struct sockaddr *)&sin;
1968 args.ex_addrlen = sizeof (sin);
1969 args.ex_mask = (struct sockaddr *)&imask;
1970 args.ex_masklen = sizeof (imask);
1971 if (grp->gr_ptr.gt_net.nt_mask) {
1972 imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
1973 break;
1974 }
1975 net = ntohl(grp->gr_ptr.gt_net.nt_net);
1976 if (IN_CLASSA(net))
1977 imask.sin_addr.s_addr = inet_addr("255.0.0.0");
1978 else if (IN_CLASSB(net))
1979 imask.sin_addr.s_addr = inet_addr("255.255.0.0");
1980 else
1981 imask.sin_addr.s_addr = inet_addr("255.255.255.0");
1982 grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
1983 break;
1984 case GT_IGNORE:
1985 return (0);
1986 default:
1987 syslog(LOG_ERR, "Bad grouptype");
1988 if (cp)
1989 *cp = savedc;
1990 return (1);
1991 }
1992
1993 /*
1994 * XXX:
1995 * Maybe I should just use the fsb->f_mntonname path instead
1996 * of looping back up the dirp to the mount point??
1997 * Also, needs to know how to export all types of local
1998 * exportable file systems and not just MOUNT_FFS.
1999 */
2000 while (imsg_export(dirp, &args) == -1) {
2001 if (cp)
2002 *cp-- = savedc;
2003 else
2004 cp = dirp + dirplen - 1;
2005 if (errno == EPERM) {
2006 syslog(LOG_ERR,
2007 "Can't change attributes for %s (%s).\n",
2008 dirp,
2009 (grp->gr_type == GT_HOST)
2010 ?grp->gr_ptr.gt_hostent->h_name
2011 :(grp->gr_type == GT_NET)
2012 ?grp->gr_ptr.gt_net.nt_name
2013 :"Unknown");
2014 return (2);
2015 }
2016 if (opt_flags & OP_ALLDIRS) {
2017 #if 0
2018 syslog(LOG_ERR, "Could not remount %s: %m",
2019 dirp);
2020 return (2);
2021 #endif
2022 }
2023 /* back up over the last component */
2024 while (cp > dirp && *cp == '/')
2025 cp--;
2026 while (cp > dirp && *(cp - 1) != '/')
2027 cp--;
2028 if (cp == dirp) {
2029 if (debug)
2030 fprintf(stderr, "mnt unsucc\n");
2031 syslog(LOG_ERR, "Can't export %s: %m", dirp);
2032 return (2);
2033 }
2034 savedc = *cp;
2035 *cp = '\0';
2036 }
2037 if (addrp) {
2038 ++addrp;
2039 if (*addrp == NULL)
2040 done = TRUE;
2041 } else
2042 done = TRUE;
2043 }
2044 if (cp)
2045 *cp = savedc;
2046 return (0);
2047 }
2048
2049 /*
2050 * Translate a net address.
2051 */
2052 int
get_net(char * cp,struct netmsk * net,int maskflg)2053 get_net(char *cp, struct netmsk *net, int maskflg)
2054 {
2055 struct in_addr inetaddr, inetaddr2;
2056 in_addr_t netaddr;
2057 struct netent *np;
2058 char *name;
2059
2060 if ((netaddr = inet_network(cp)) != INADDR_NONE) {
2061 inetaddr = inet_makeaddr(netaddr, 0);
2062 /*
2063 * Due to arbitrary subnet masks, you don't know how many
2064 * bits to shift the address to make it into a network,
2065 * however you do know how to make a network address into
2066 * a host with host == 0 and then compare them.
2067 * (What a pest)
2068 */
2069 if (!maskflg) {
2070 setnetent(0);
2071 while ((np = getnetent())) {
2072 inetaddr2 = inet_makeaddr(np->n_net, 0);
2073 if (inetaddr2.s_addr == inetaddr.s_addr)
2074 break;
2075 }
2076 endnetent();
2077 }
2078 } else {
2079 if ((np = getnetbyname(cp)))
2080 inetaddr = inet_makeaddr(np->n_net, 0);
2081 else
2082 return (1);
2083 }
2084 if (maskflg)
2085 net->nt_mask = inetaddr.s_addr;
2086 else {
2087 int len;
2088
2089 if (np)
2090 name = np->n_name;
2091 else
2092 name = inet_ntoa(inetaddr);
2093 len = strlen(name) + 1;
2094 net->nt_name = malloc(len);
2095 if (net->nt_name == NULL)
2096 out_of_mem();
2097 strlcpy(net->nt_name, name, len);
2098 net->nt_net = inetaddr.s_addr;
2099 }
2100 return (0);
2101 }
2102
2103 /*
2104 * Parse out the next white space separated field
2105 */
2106 void
nextfield(char ** cp,char ** endcp)2107 nextfield(char **cp, char **endcp)
2108 {
2109 char *p;
2110
2111 p = *cp;
2112 while (*p == ' ' || *p == '\t')
2113 p++;
2114 if (*p == '\n' || *p == '\0')
2115 *cp = *endcp = p;
2116 else {
2117 *cp = p++;
2118 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2119 p++;
2120 *endcp = p;
2121 }
2122 }
2123
2124 /*
2125 * Get an exports file line. Skip over blank lines and handle line
2126 * continuations.
2127 */
2128 int
get_line(void)2129 get_line(void)
2130 {
2131 int totlen, cont_line, len;
2132 char *p, *cp;
2133
2134 /*
2135 * Loop around ignoring blank lines and getting all continuation lines.
2136 */
2137 p = line;
2138 totlen = 0;
2139 do {
2140 if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
2141 return (0);
2142 len = strlen(p);
2143 cp = p + len - 1;
2144 cont_line = 0;
2145 while (cp >= p && (*cp == ' ' || *cp == '\t' || *cp == '\n' ||
2146 *cp == '\\')) {
2147 if (*cp == '\\')
2148 cont_line = 1;
2149 cp--;
2150 len--;
2151 }
2152 *++cp = '\0';
2153 if (len > 0) {
2154 totlen += len;
2155 if (totlen >= LINESIZ) {
2156 syslog(LOG_ERR, "Exports line too long");
2157 exit(2);
2158 }
2159 p = cp;
2160 }
2161 } while (totlen == 0 || cont_line);
2162 return (1);
2163 }
2164
2165 /*
2166 * Parse a description of a credential.
2167 */
2168 void
parsecred(char * namelist,struct xucred * cr)2169 parsecred(char *namelist, struct xucred *cr)
2170 {
2171 gid_t groups[NGROUPS_MAX + 1];
2172 char *name, *names;
2173 struct passwd *pw;
2174 struct group *gr;
2175 int maxgroups, ngroups, cnt;
2176
2177 /*
2178 * Set up the unprivileged user.
2179 */
2180 *cr = def_anon;
2181
2182 /*
2183 * Get the user's password table entry.
2184 */
2185 names = strsep(&namelist, " \t\n");
2186 name = strsep(&names, ":");
2187 if (isdigit((unsigned char)*name) || *name == '-')
2188 pw = getpwuid(atoi(name));
2189 else
2190 pw = getpwnam(name);
2191 /*
2192 * Credentials specified as those of a user.
2193 */
2194 if (names == NULL) {
2195 if (pw == NULL) {
2196 syslog(LOG_ERR, "Unknown user: %s", name);
2197 return;
2198 }
2199 cr->cr_uid = pw->pw_uid;
2200 maxgroups = ngroups = NGROUPS_MAX + 1;
2201 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) == -1) {
2202 syslog(LOG_ERR, "Too many groups for %s: %m", pw->pw_name);
2203 /* Truncate group list */
2204 ngroups = maxgroups;
2205 }
2206 /*
2207 * compress out duplicate
2208 */
2209 cr->cr_ngroups = ngroups - 1;
2210 cr->cr_gid = groups[0];
2211 for (cnt = 1; cnt < ngroups; cnt++)
2212 cr->cr_groups[cnt - 1] = groups[cnt];
2213 return;
2214 }
2215 /*
2216 * Explicit credential specified as a colon separated list:
2217 * uid:gid:gid:...
2218 */
2219 if (pw != NULL)
2220 cr->cr_uid = pw->pw_uid;
2221 else if (isdigit((unsigned char)*name) || *name == '-')
2222 cr->cr_uid = atoi(name);
2223 else {
2224 syslog(LOG_ERR, "Unknown user: %s", name);
2225 return;
2226 }
2227 cr->cr_ngroups = 0;
2228 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS_MAX) {
2229 name = strsep(&names, ":");
2230 if (isdigit((unsigned char)*name) || *name == '-') {
2231 cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2232 } else {
2233 if ((gr = getgrnam(name)) == NULL) {
2234 syslog(LOG_ERR, "Unknown group: %s", name);
2235 continue;
2236 }
2237 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2238 }
2239 }
2240 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS_MAX)
2241 syslog(LOG_ERR, "Too many groups");
2242 }
2243
2244 #define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2245 /*
2246 * Routines that maintain the remote mounttab
2247 */
2248 void
get_mountlist(void)2249 get_mountlist(void)
2250 {
2251 struct mountlist *mlp, **mlpp;
2252 char *host, *dirp, *cp;
2253 char str[STRSIZ];
2254 FILE *mlfile;
2255
2256 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2257 syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST);
2258 return;
2259 }
2260 mlpp = &mlhead;
2261 while (fgets(str, STRSIZ, mlfile) != NULL) {
2262 cp = str;
2263 host = strsep(&cp, " \t\n");
2264 dirp = strsep(&cp, " \t\n");
2265 if (host == NULL || dirp == NULL)
2266 continue;
2267 mlp = malloc(sizeof (*mlp));
2268 if (mlp == NULL)
2269 out_of_mem();
2270 strlcpy(mlp->ml_host, host, sizeof(mlp->ml_host));
2271 strlcpy(mlp->ml_dirp, dirp, sizeof(mlp->ml_dirp));
2272 mlp->ml_next = NULL;
2273 *mlpp = mlp;
2274 mlpp = &mlp->ml_next;
2275 }
2276 fclose(mlfile);
2277 }
2278
2279 void
del_mlist(char * hostp,char * dirp)2280 del_mlist(char *hostp, char *dirp)
2281 {
2282 struct mountlist *mlp, **mlpp;
2283 struct mountlist *mlp2;
2284 int fnd = 0;
2285
2286 mlpp = &mlhead;
2287 mlp = mlhead;
2288 while (mlp) {
2289 if (!strcmp(mlp->ml_host, hostp) &&
2290 (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2291 fnd = 1;
2292 mlp2 = mlp;
2293 *mlpp = mlp = mlp->ml_next;
2294 free((caddr_t)mlp2);
2295 } else {
2296 mlpp = &mlp->ml_next;
2297 mlp = mlp->ml_next;
2298 }
2299 }
2300 if (fnd) {
2301 send_imsg(IMSG_MLIST_OPEN, NULL, 0);
2302 mlp = mlhead;
2303 while (mlp) {
2304 send_imsg(IMSG_MLIST_WRITE, mlp, sizeof(*mlp));
2305 mlp = mlp->ml_next;
2306 }
2307 send_imsg(IMSG_MLIST_CLOSE, NULL, 0);
2308 }
2309 }
2310
2311 void
add_mlist(char * hostp,char * dirp)2312 add_mlist(char *hostp, char *dirp)
2313 {
2314 struct mountlist *mlp, **mlpp;
2315
2316 mlpp = &mlhead;
2317 mlp = mlhead;
2318 while (mlp) {
2319 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2320 return;
2321 mlpp = &mlp->ml_next;
2322 mlp = mlp->ml_next;
2323 }
2324 mlp = malloc(sizeof (*mlp));
2325 if (mlp == NULL)
2326 out_of_mem();
2327 strlcpy(mlp->ml_host, hostp, sizeof(mlp->ml_host));
2328 strlcpy(mlp->ml_dirp, dirp, sizeof(mlp->ml_dirp));
2329 mlp->ml_next = NULL;
2330 *mlpp = mlp;
2331 send_imsg(IMSG_MLIST_APPEND, mlp, sizeof(*mlp));
2332 }
2333
2334 /*
2335 * This function is called via SIGTERM when the system is going down.
2336 * It sends a broadcast RPCMNT_UMNTALL.
2337 */
2338 void
send_umntall(int signo)2339 send_umntall(int signo)
2340 {
2341 gotterm = 1;
2342 }
2343
2344 int
umntall_each(caddr_t resultsp,struct sockaddr_in * raddr)2345 umntall_each(caddr_t resultsp, struct sockaddr_in *raddr)
2346 {
2347 return (1);
2348 }
2349
2350 /*
2351 * Free up a group list.
2352 */
2353 void
free_grp(struct grouplist * grp)2354 free_grp(struct grouplist *grp)
2355 {
2356 char **addrp;
2357
2358 if (grp->gr_type == GT_HOST) {
2359 if (grp->gr_ptr.gt_hostent->h_name) {
2360 addrp = grp->gr_ptr.gt_hostent->h_addr_list;
2361 while (addrp && *addrp)
2362 free(*addrp++);
2363 free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
2364 free(grp->gr_ptr.gt_hostent->h_name);
2365 }
2366 free((caddr_t)grp->gr_ptr.gt_hostent);
2367 } else if (grp->gr_type == GT_NET) {
2368 free(grp->gr_ptr.gt_net.nt_name);
2369 }
2370 free((caddr_t)grp);
2371 }
2372
2373 /*
2374 * Check options for consistency.
2375 */
2376 int
check_options(struct dirlist * dp)2377 check_options(struct dirlist *dp)
2378 {
2379
2380 if (dp == NULL)
2381 return (1);
2382 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2383 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2384 return (1);
2385 }
2386 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2387 syslog(LOG_ERR, "-mask requires -network");
2388 return (1);
2389 }
2390 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2391 syslog(LOG_ERR, "-alldirs has multiple directories");
2392 return (1);
2393 }
2394 return (0);
2395 }
2396
2397 /*
2398 * Check an absolute directory path for any symbolic links. Return true
2399 * if no symbolic links are found.
2400 */
2401 int
check_dirpath(char * dirp)2402 check_dirpath(char *dirp)
2403 {
2404 struct stat sb;
2405 int ret = 1;
2406 char *cp;
2407
2408 /* Remove trailing '/' */
2409 cp = dirp + strlen(dirp) - 1;
2410 while (cp > dirp && *cp == '/')
2411 *cp-- = '\0';
2412
2413 cp = dirp + 1;
2414 while (*cp && ret) {
2415 if (*cp == '/') {
2416 *cp = '\0';
2417 if (lstat(dirp, &sb) == -1 || !S_ISDIR(sb.st_mode))
2418 ret = 0;
2419 *cp = '/';
2420 }
2421 cp++;
2422 }
2423 if (lstat(dirp, &sb) == -1 ||
2424 (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)))
2425 ret = 0;
2426 return (ret);
2427 }
2428