GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/mountd/mountd.c Lines: 0 1150 0.0 %
Date: 2016-12-06 Branches: 0 808 0.0 %

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