xref: /csrg-svn/sbin/mountd/mountd.c (revision 42703)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 char copyright[] =
13 "@(#) Copyright (c) 1989 Regents of the University of California.\n\
14  All rights reserved.\n";
15 #endif not lint
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)mountd.c	5.8 (Berkeley) 06/01/90";
19 #endif not lint
20 
21 #include <sys/param.h>
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24 #include <sys/file.h>
25 #include <sys/mount.h>
26 #include <sys/socket.h>
27 #include <sys/errno.h>
28 #include <sys/signal.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <netdb.h>
33 #include <rpc/rpc.h>
34 #include <rpc/pmap_clnt.h>
35 #include <rpc/pmap_prot.h>
36 #include <nfs/rpcv2.h>
37 #include <nfs/nfsv2.h>
38 #include "pathnames.h"
39 
40 struct ufid {
41 	u_short	ufid_len;
42 	ino_t	ufid_ino;
43 	long	ufid_gen;
44 };
45 /*
46  * Structures for keeping the mount list and export list
47  */
48 struct mountlist {
49 	char	ml_host[RPCMNT_NAMELEN+1];
50 	char	ml_dirp[RPCMNT_PATHLEN+1];
51 };
52 
53 struct exportlist {
54 	struct exportlist *ex_next;
55 	struct exportlist *ex_prev;
56 	struct grouplist *ex_groups;
57 	int	ex_rootuid;
58 	int	ex_exflags;
59 	char	ex_dirp[RPCMNT_PATHLEN+1];
60 };
61 
62 struct grouplist {
63 	struct grouplist *gr_next;
64 	struct hostent *gr_hp;
65 };
66 
67 /* Global defs */
68 int xdr_fhs(), xdr_mlist(), xdr_dir(), xdr_explist();
69 int mntsrv(), get_exportlist();
70 struct exportlist exphead;
71 int mlfile;
72 char exname[MAXPATHLEN];
73 int def_rootuid = -2;
74 extern int errno;
75 #ifdef DEBUG
76 int debug = 1;
77 #else
78 int debug = 0;
79 #endif
80 
81 /*
82  * Mountd server for NFS mount protocol as described in:
83  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
84  * The optional argument is the exports file name
85  * default: _PATH_EXPORTS
86  */
87 main(argc, argv)
88 	int argc;
89 	char *argv[];
90 {
91 	SVCXPRT *transp;
92 
93 	if (debug == 0) {
94 		if (fork())
95 			exit(0);
96 		{ int s;
97 		for (s = 0; s < 10; s++)
98 			(void) close(s);
99 		}
100 		(void) open("/", O_RDONLY);
101 		(void) dup2(0, 1);
102 		(void) dup2(0, 2);
103 		{ int tt = open("/dev/tty", O_RDWR);
104 		  if (tt > 0) {
105 			ioctl(tt, TIOCNOTTY, (char *)0);
106 			close(tt);
107 		  }
108 		}
109 		(void) setpgrp(0, 0);
110 		signal(SIGTSTP, SIG_IGN);
111 		signal(SIGTTIN, SIG_IGN);
112 		signal(SIGTTOU, SIG_IGN);
113 		signal(SIGINT, SIG_IGN);
114 		signal(SIGQUIT, SIG_IGN);
115 		signal(SIGTERM, SIG_IGN);
116 	}
117 	openlog("mountd:", LOG_PID, LOG_DAEMON);
118 	exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
119 	if (argc == 2) {
120 		strncpy(exname, argv[1], MAXPATHLEN-1);
121 		exname[MAXPATHLEN-1] = '\0';
122 	} else
123 		strcpy(exname, _PATH_EXPORTS);
124 	get_exportlist();
125 	signal(SIGHUP, get_exportlist);
126 	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
127 	  if (pidfile != NULL) {
128 		fprintf(pidfile, "%d\n", getpid());
129 		fclose(pidfile);
130 	  }
131 	}
132 	if ((mlfile = open(_PATH_RMOUNTLIST, (O_RDWR|O_CREAT), 0600)) < 0) {
133 		syslog(LOG_ERR, "Can't open mountlist file");
134 		exit(1);
135 	}
136 	if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) {
137 		syslog(LOG_ERR, "Can't create socket");
138 		exit(1);
139 	}
140 	pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
141 	if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP)) {
142 		syslog(LOG_ERR, "Can't register mount");
143 		exit(1);
144 	}
145 	svc_run();
146 	syslog(LOG_ERR, "Mountd died");
147 }
148 
149 /*
150  * The mount rpc service
151  */
152 mntsrv(rqstp, transp)
153 	register struct svc_req *rqstp;
154 	register SVCXPRT *transp;
155 {
156 	register struct grouplist *grp;
157 	register u_long **addrp;
158 	register struct exportlist *ep;
159 	struct mountlist ml;
160 	nfsv2fh_t nfh;
161 	struct authunix_parms *ucr;
162 	struct stat stb;
163 	struct hostent *hp;
164 	u_long saddr;
165 	char dirpath[RPCMNT_PATHLEN+1];
166 	int ok = 0;
167 	int bad = ENOENT;
168 	int omask;
169 	uid_t uid = -2;
170 
171 	/* Get authorization */
172 	switch (rqstp->rq_cred.oa_flavor) {
173 	case AUTH_UNIX:
174 		ucr = (struct authunix_parms *)rqstp->rq_clntcred;
175 		uid = ucr->aup_uid;
176 		break;
177 	case AUTH_NULL:
178 	default:
179 		break;
180 	}
181 
182 	saddr = transp->xp_raddr.sin_addr.s_addr;
183 	hp = (struct hostent *)0;
184 	switch (rqstp->rq_proc) {
185 	case NULLPROC:
186 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
187 			syslog(LOG_ERR, "Can't send reply");
188 		return;
189 	case RPCMNT_MOUNT:
190 		if (uid != 0) {
191 			svcerr_weakauth(transp);
192 			return;
193 		}
194 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
195 			svcerr_decode(transp);
196 			return;
197 		}
198 
199 		/* Check to see if it's a valid dirpath */
200 		if (stat(dirpath, &stb) < 0 || (stb.st_mode&S_IFMT) !=
201 			S_IFDIR) {
202 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
203 				syslog(LOG_ERR, "Can't send reply");
204 			return;
205 		}
206 
207 		/* Check in the exports list */
208 		omask = sigblock(sigmask(SIGHUP));
209 		ep = exphead.ex_next;
210 		while (ep != NULL) {
211 			if (!strcmp(ep->ex_dirp, dirpath)) {
212 				grp = ep->ex_groups;
213 				if (grp == NULL)
214 					break;
215 
216 				/* Check for a host match */
217 				addrp = (u_long **)grp->gr_hp->h_addr_list;
218 				for (;;) {
219 					if (**addrp == saddr)
220 						break;
221 					if (*++addrp == NULL)
222 						if (grp = grp->gr_next) {
223 							addrp = (u_long **)
224 								grp->gr_hp->h_addr_list;
225 						} else {
226 							bad = EACCES;
227 							if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
228 								syslog(LOG_ERR, "Can't send reply");
229 							sigsetmask(omask);
230 							return;
231 						}
232 				}
233 				hp = grp->gr_hp;
234 				break;
235 			}
236 			ep = ep->ex_next;
237 		}
238 		sigsetmask(omask);
239 		if (ep == NULL) {
240 			bad = EACCES;
241 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
242 				syslog(LOG_ERR, "Can't send reply");
243 			return;
244 		}
245 
246 		/* Get the file handle */
247 		bzero((caddr_t)&nfh, sizeof(nfh));
248 		if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
249 			bad = errno;
250 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
251 				syslog(LOG_ERR, "Can't send reply");
252 			return;
253 		}
254 #ifdef notnow
255 nfh.fh_generic.fh_fid.fid_gen = htonl(nfh.fh_generic.fh_fid.fid_gen);
256 #endif
257 		if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
258 			syslog(LOG_ERR, "Can't send reply");
259 		if (hp == NULL)
260 			hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
261 		if (hp) {
262 			if (!fnd_mnt(hp->h_name, dirpath)) {
263 				strcpy(ml.ml_host, hp->h_name);
264 				strcpy(ml.ml_dirp, dirpath);
265 				write(mlfile, (caddr_t)&ml, sizeof(ml));
266 			}
267 		}
268 		return;
269 	case RPCMNT_DUMP:
270 		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0))
271 			syslog(LOG_ERR, "Can't send reply");
272 		return;
273 	case RPCMNT_UMOUNT:
274 		if (uid != 0) {
275 			svcerr_weakauth(transp);
276 			return;
277 		}
278 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
279 			svcerr_decode(transp);
280 			return;
281 		}
282 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
283 		if (hp && fnd_mnt(hp->h_name, dirpath)) {
284 			ml.ml_host[0] = '\0';
285 			write(mlfile, (caddr_t)&ml, sizeof(ml));
286 		}
287 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
288 			syslog(LOG_ERR, "Can't send reply");
289 		return;
290 	case RPCMNT_UMNTALL:
291 		if (uid != 0) {
292 			svcerr_weakauth(transp);
293 			return;
294 		}
295 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
296 		if (hp) {
297 			lseek(mlfile, (off_t)0, L_SET);
298 			while (read(mlfile, (caddr_t)&ml, sizeof(ml)) ==
299 				sizeof(ml)) {
300 				if (!strcmp(hp->h_name, ml.ml_host)) {
301 					lseek(mlfile, (off_t)-sizeof(ml),
302 						L_INCR);
303 					ml.ml_host[0] = '\0';
304 					write(mlfile, (caddr_t)&ml, sizeof(ml));
305 				}
306 			}
307 		}
308 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
309 			syslog(LOG_ERR, "Can't send reply");
310 		return;
311 	case RPCMNT_EXPORT:
312 		if (!svc_sendreply(transp, xdr_explist, (caddr_t)0))
313 			syslog(LOG_ERR, "Can't send reply");
314 		return;
315 	default:
316 		svcerr_noproc(transp);
317 		return;
318 	}
319 }
320 
321 /*
322  * Xdr conversion for a dirpath string
323  */
324 xdr_dir(xdrsp, dirp)
325 	XDR *xdrsp;
326 	char *dirp;
327 {
328 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
329 }
330 
331 /*
332  * Xdr routine to generate fhstatus
333  */
334 xdr_fhs(xdrsp, nfh)
335 	XDR *xdrsp;
336 	nfsv2fh_t *nfh;
337 {
338 	int ok = 0;
339 
340 	if (!xdr_long(xdrsp, &ok))
341 		return (0);
342 	return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
343 }
344 
345 xdr_mlist(xdrsp, cp)
346 	XDR *xdrsp;
347 	caddr_t cp;
348 {
349 	struct mountlist ml;
350 	int true = 1;
351 	int false = 0;
352 	char *strp;
353 
354 	lseek(mlfile, (off_t)0, L_SET);
355 	while (read(mlfile, (caddr_t)&ml, sizeof(ml)) == sizeof(ml)) {
356 		if (ml.ml_host[0] != '\0') {
357 			if (!xdr_bool(xdrsp, &true))
358 				return (0);
359 			strp = &ml.ml_host[0];
360 			if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
361 				return (0);
362 			strp = &ml.ml_dirp[0];
363 			if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
364 				return (0);
365 		}
366 	}
367 	if (!xdr_bool(xdrsp, &false))
368 		return (0);
369 	return (1);
370 }
371 
372 /*
373  * Xdr conversion for export list
374  */
375 xdr_explist(xdrsp, cp)
376 	XDR *xdrsp;
377 	caddr_t cp;
378 {
379 	register struct exportlist *ep;
380 	register struct grouplist *grp;
381 	int true = 1;
382 	int false = 0;
383 	char *strp;
384 	int omask;
385 
386 	omask = sigblock(sigmask(SIGHUP));
387 	ep = exphead.ex_next;
388 	while (ep != NULL) {
389 		if (!xdr_bool(xdrsp, &true))
390 			goto errout;
391 		strp = &ep->ex_dirp[0];
392 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
393 			goto errout;
394 		grp = ep->ex_groups;
395 		while (grp != NULL) {
396 			if (!xdr_bool(xdrsp, &true))
397 				goto errout;
398 			strp = grp->gr_hp->h_name;
399 			if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
400 				goto errout;
401 			grp = grp->gr_next;
402 		}
403 		if (!xdr_bool(xdrsp, &false))
404 			goto errout;
405 		ep = ep->ex_next;
406 	}
407 	sigsetmask(omask);
408 	if (!xdr_bool(xdrsp, &false))
409 		return (0);
410 	return (1);
411 errout:
412 	sigsetmask(omask);
413 	return (0);
414 }
415 
416 #define LINESIZ	10240
417 char line[LINESIZ];
418 
419 /*
420  * Get the export list
421  */
422 get_exportlist()
423 {
424 	register struct hostent *hp, *nhp;
425 	register char **addrp, **naddrp;
426 	register int i;
427 	register struct grouplist *grp, *grp2;
428 	register struct exportlist *ep, *ep2;
429 	struct ufs_args args;
430 	FILE *inf;
431 	char *cp, *endcp;
432 	char savedc;
433 	int len;
434 	int rootuid, exflags;
435 
436 	/*
437 	 * First, get rid of the old list
438 	 */
439 	ep = exphead.ex_next;
440 	while (ep != NULL) {
441 		grp = ep->ex_groups;
442 		while (grp != NULL) {
443 			addrp = grp->gr_hp->h_addr_list;
444 			while (*addrp)
445 				free(*addrp++);
446 			free((caddr_t)grp->gr_hp->h_addr_list);
447 			free(grp->gr_hp->h_name);
448 			free((caddr_t)grp->gr_hp);
449 			grp2 = grp;
450 			grp = grp->gr_next;
451 			free((caddr_t)grp2);
452 		}
453 		ep2 = ep;
454 		ep = ep->ex_next;
455 		free((caddr_t)ep2);
456 	}
457 
458 	/*
459 	 * Read in the exports file and build the list, calling
460 	 * exportfs() as we go along
461 	 */
462 	exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
463 	if ((inf = fopen(exname, "r")) == NULL) {
464 		syslog(LOG_ERR, "Can't open %s", exname);
465 		exit(2);
466 	}
467 	while (fgets(line, LINESIZ, inf)) {
468 		exflags = MNT_EXPORTED;
469 		rootuid = def_rootuid;
470 		cp = line;
471 		nextfield(&cp, &endcp);
472 		len = endcp-cp;
473 		if (len <= RPCMNT_PATHLEN && len > 0) {
474 			ep = (struct exportlist *)malloc(sizeof(*ep));
475 			ep->ex_next = ep->ex_prev = (struct exportlist *)0;
476 			ep->ex_groups = (struct grouplist *)0;
477 			bcopy(cp, ep->ex_dirp, len);
478 			ep->ex_dirp[len] = '\0';
479 		} else
480 			goto err;
481 		cp = endcp;
482 		nextfield(&cp, &endcp);
483 		len = endcp-cp;
484 		while (len > 0) {
485 			savedc = *endcp;
486 			*endcp = '\0';
487 			if (len <= RPCMNT_NAMELEN) {
488 				if (*cp == '-') {
489 					cp++;
490 					switch (*cp) {
491 					case 'o':
492 						exflags |= MNT_EXRDONLY;
493 						break;
494 					case 'r':
495 						if (*++cp == '=')
496 							rootuid = atoi(++cp);
497 						break;
498 					default:
499 						syslog(LOG_WARNING,
500 						  "Bad -%c option in %s",
501 						  *cp, exname);
502 						break;
503 					};
504 				} else if (hp = gethostbyname(cp)) {
505 					grp = (struct grouplist *)
506 						malloc(sizeof(struct grouplist));
507 					if (grp == NULL)
508 						goto err;
509 					nhp = grp->gr_hp = (struct hostent *)
510 						malloc(sizeof(struct hostent));
511 					if (nhp == NULL)
512 						goto err;
513 					bcopy((caddr_t)hp, (caddr_t)nhp,
514 						sizeof(struct hostent));
515 					i = strlen(hp->h_name)+1;
516 					nhp->h_name = (char *)malloc(i);
517 					if (nhp->h_name == NULL)
518 						goto err;
519 					bcopy(hp->h_name, nhp->h_name, i);
520 					addrp = hp->h_addr_list;
521 					i = 1;
522 					while (*addrp++)
523 						i++;
524 					naddrp = nhp->h_addr_list = (char **)
525 						malloc(i*sizeof(char *));
526 					if (naddrp == NULL)
527 						goto err;
528 					addrp = hp->h_addr_list;
529 					while (*addrp) {
530 						*naddrp = (char *)
531 						    malloc(hp->h_length);
532 						bcopy(*addrp, *naddrp,
533 							hp->h_length);
534 						addrp++;
535 						naddrp++;
536 					}
537 					*naddrp = (char *)0;
538 					grp->gr_next = ep->ex_groups;
539 					ep->ex_groups = grp;
540 				}
541 			}
542 			cp = endcp;
543 			*cp = savedc;
544 			nextfield(&cp, &endcp);
545 			len = endcp-cp;
546 		}
547 		args.fspec = 0;
548 		args.exflags = exflags;
549 		args.exroot = rootuid;
550 		if (mount(MOUNT_UFS, ep->ex_dirp, MNT_UPDATE, &args) < 0) {
551 			syslog(LOG_WARNING, "Can't export %s", ep->ex_dirp);
552 			free((caddr_t)ep);
553 		} else {
554 			ep->ex_rootuid = rootuid;
555 			ep->ex_exflags = exflags;
556 			ep->ex_next = exphead.ex_next;
557 			ep->ex_prev = &exphead;
558 			if (ep->ex_next != NULL)
559 				ep->ex_next->ex_prev = ep;
560 			exphead.ex_next = ep;
561 		}
562 	}
563 	fclose(inf);
564 	return;
565 err:
566 	syslog(LOG_ERR, "Bad Exports File, mountd Failed");
567 	exit(2);
568 }
569 
570 /*
571  * Parse out the next white space separated field
572  */
573 nextfield(cp, endcp)
574 	char **cp;
575 	char **endcp;
576 {
577 	register char *p;
578 
579 	p = *cp;
580 	while (*p == ' ' || *p == '\t')
581 		p++;
582 	if (*p == '\n' || *p == '\0') {
583 		*cp = *endcp = p;
584 		return;
585 	}
586 	*cp = p++;
587 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
588 		p++;
589 	*endcp = p;
590 }
591 
592 /*
593  * Search the remote mounts file for a match.
594  * Iff found
595  *	- set file offset to record and return TRUE
596  * else
597  *	- set file offset to an unused record if found or eof otherwise
598  *	  and return FALSE
599  */
600 fnd_mnt(host, dirp)
601 	char *host;
602 	char *dirp;
603 {
604 	struct mountlist ml;
605 	off_t off, pos;
606 
607 	off = -1;
608 	pos = 0;
609 	lseek(mlfile, (off_t)0, L_SET);
610 	while (read(mlfile, (caddr_t)&ml, sizeof(ml)) == sizeof(ml)) {
611 		if (!strcmp(host, ml.ml_host) && !strcmp(dirp, ml.ml_dirp)) {
612 			lseek(mlfile, (off_t)-sizeof(ml), L_INCR);
613 			return (TRUE);
614 		} else if (ml.ml_host[0] == '\0') {
615 			off = pos;
616 		}
617 		pos += sizeof(ml);
618 	}
619 	if (off != -1)
620 		lseek(mlfile, off, L_SET);
621 	else
622 		lseek(mlfile, (off_t)0, L_XTND);
623 	return (FALSE);
624 }
625