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