xref: /csrg-svn/sbin/mountd/mountd.c (revision 40368)
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.4 (Berkeley) 03/08/90";
29 #endif not lint
30 
31 #include <stdio.h>
32 #include <strings.h>
33 #include <syslog.h>
34 #include <signal.h>
35 #include <sys/param.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <sys/file.h>
39 #include <sys/mount.h>
40 #include <sys/socket.h>
41 #include <sys/errno.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 	if ((mlfile = open(_PATH_RMOUNTLIST, (O_RDWR|O_CREAT), 0600)) < 0) {
137 		syslog(LOG_ERR, "Can't open mountlist file");
138 		exit(1);
139 	}
140 	if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) {
141 		syslog(LOG_ERR, "Can't create socket");
142 		exit(1);
143 	}
144 	pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
145 	if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP)) {
146 		syslog(LOG_ERR, "Can't register mount");
147 		exit(1);
148 	}
149 	svc_run();
150 	syslog(LOG_ERR, "Mountd died");
151 }
152 
153 /*
154  * The mount rpc service
155  */
156 mntsrv(rqstp, transp)
157 	register struct svc_req *rqstp;
158 	register SVCXPRT *transp;
159 {
160 	register struct grouplist *grp;
161 	register u_long **addrp;
162 	register struct exportlist *ep;
163 	struct mountlist ml;
164 	nfsv2fh_t nfh;
165 	struct authunix_parms *ucr;
166 	struct stat stb;
167 	struct hostent *hp;
168 	u_long saddr;
169 	char dirpath[RPCMNT_PATHLEN+1];
170 	int ok = 0;
171 	int bad = ENOENT;
172 	int omask;
173 	uid_t uid = -2;
174 
175 	/* Get authorization */
176 	switch (rqstp->rq_cred.oa_flavor) {
177 	case AUTH_UNIX:
178 		ucr = (struct authunix_parms *)rqstp->rq_clntcred;
179 		uid = ucr->aup_uid;
180 		break;
181 	case AUTH_NULL:
182 	default:
183 		break;
184 	}
185 
186 	saddr = transp->xp_raddr.sin_addr.s_addr;
187 	hp = (struct hostent *)0;
188 	switch (rqstp->rq_proc) {
189 	case NULLPROC:
190 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
191 			syslog(LOG_ERR, "Can't send reply");
192 		return;
193 	case RPCMNT_MOUNT:
194 		if (uid != 0) {
195 			svcerr_weakauth(transp);
196 			return;
197 		}
198 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
199 			svcerr_decode(transp);
200 			return;
201 		}
202 
203 		/* Check to see if it's a valid dirpath */
204 		if (stat(dirpath, &stb) < 0 || (stb.st_mode&S_IFMT) !=
205 			S_IFDIR) {
206 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
207 				syslog(LOG_ERR, "Can't send reply");
208 			return;
209 		}
210 
211 		/* Check in the exports list */
212 		omask = sigblock(sigmask(SIGHUP));
213 		ep = exphead.ex_next;
214 		while (ep != NULL) {
215 			if (!strcmp(ep->ex_dirp, dirpath)) {
216 				grp = ep->ex_groups;
217 				if (grp == NULL)
218 					break;
219 
220 				/* Check for a host match */
221 				addrp = (u_long **)grp->gr_hp->h_addr_list;
222 				for (;;) {
223 					if (**addrp == saddr)
224 						break;
225 					if (*++addrp == NULL)
226 						if (grp = grp->gr_next) {
227 							addrp = (u_long **)
228 								grp->gr_hp->h_addr_list;
229 						} else {
230 							bad = EACCES;
231 							if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
232 								syslog(LOG_ERR, "Can't send reply");
233 							sigsetmask(omask);
234 							return;
235 						}
236 				}
237 				hp = grp->gr_hp;
238 				break;
239 			}
240 			ep = ep->ex_next;
241 		}
242 		sigsetmask(omask);
243 		if (ep == NULL) {
244 			bad = EACCES;
245 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
246 				syslog(LOG_ERR, "Can't send reply");
247 			return;
248 		}
249 
250 		/* Get the file handle */
251 		bzero((caddr_t)&nfh, sizeof(nfh));
252 		if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
253 			bad = errno;
254 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
255 				syslog(LOG_ERR, "Can't send reply");
256 			return;
257 		}
258 #ifdef notnow
259 nfh.fh_generic.fh_fid.fid_gen = htonl(nfh.fh_generic.fh_fid.fid_gen);
260 #endif
261 		if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
262 			syslog(LOG_ERR, "Can't send reply");
263 		if (hp == NULL)
264 			hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
265 		if (hp) {
266 			if (!fnd_mnt(hp->h_name, dirpath)) {
267 				strcpy(ml.ml_host, hp->h_name);
268 				strcpy(ml.ml_dirp, dirpath);
269 				write(mlfile, (caddr_t)&ml, sizeof(ml));
270 			}
271 		}
272 		return;
273 	case RPCMNT_DUMP:
274 		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0))
275 			syslog(LOG_ERR, "Can't send reply");
276 		return;
277 	case RPCMNT_UMOUNT:
278 		if (uid != 0) {
279 			svcerr_weakauth(transp);
280 			return;
281 		}
282 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
283 			svcerr_decode(transp);
284 			return;
285 		}
286 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
287 		if (hp && fnd_mnt(hp->h_name, dirpath)) {
288 			ml.ml_host[0] = '\0';
289 			write(mlfile, (caddr_t)&ml, sizeof(ml));
290 		}
291 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
292 			syslog(LOG_ERR, "Can't send reply");
293 		return;
294 	case RPCMNT_UMNTALL:
295 		if (uid != 0) {
296 			svcerr_weakauth(transp);
297 			return;
298 		}
299 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
300 		if (hp) {
301 			lseek(mlfile, (off_t)0, L_SET);
302 			while (read(mlfile, (caddr_t)&ml, sizeof(ml)) ==
303 				sizeof(ml)) {
304 				if (!strcmp(hp->h_name, ml.ml_host)) {
305 					lseek(mlfile, (off_t)-sizeof(ml),
306 						L_INCR);
307 					ml.ml_host[0] = '\0';
308 					write(mlfile, (caddr_t)&ml, sizeof(ml));
309 				}
310 			}
311 		}
312 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
313 			syslog(LOG_ERR, "Can't send reply");
314 		return;
315 	case RPCMNT_EXPORT:
316 		if (!svc_sendreply(transp, xdr_explist, (caddr_t)0))
317 			syslog(LOG_ERR, "Can't send reply");
318 		return;
319 	default:
320 		svcerr_noproc(transp);
321 		return;
322 	}
323 }
324 
325 /*
326  * Xdr conversion for a dirpath string
327  */
328 xdr_dir(xdrsp, dirp)
329 	XDR *xdrsp;
330 	char *dirp;
331 {
332 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
333 }
334 
335 /*
336  * Xdr routine to generate fhstatus
337  */
338 xdr_fhs(xdrsp, nfh)
339 	XDR *xdrsp;
340 	nfsv2fh_t *nfh;
341 {
342 	int ok = 0;
343 
344 	if (!xdr_long(xdrsp, &ok))
345 		return (0);
346 	return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
347 }
348 
349 xdr_mlist(xdrsp, cp)
350 	XDR *xdrsp;
351 	caddr_t cp;
352 {
353 	struct mountlist ml;
354 	int true = 1;
355 	int false = 0;
356 	char *strp;
357 
358 	lseek(mlfile, (off_t)0, L_SET);
359 	while (read(mlfile, (caddr_t)&ml, sizeof(ml)) == sizeof(ml)) {
360 		if (ml.ml_host[0] != '\0') {
361 			if (!xdr_bool(xdrsp, &true))
362 				return (0);
363 			strp = &ml.ml_host[0];
364 			if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
365 				return (0);
366 			strp = &ml.ml_dirp[0];
367 			if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
368 				return (0);
369 		}
370 	}
371 	if (!xdr_bool(xdrsp, &false))
372 		return (0);
373 	return (1);
374 }
375 
376 /*
377  * Xdr conversion for export list
378  */
379 xdr_explist(xdrsp, cp)
380 	XDR *xdrsp;
381 	caddr_t cp;
382 {
383 	register struct exportlist *ep;
384 	register struct grouplist *grp;
385 	int true = 1;
386 	int false = 0;
387 	char *strp;
388 	int omask;
389 
390 	omask = sigblock(sigmask(SIGHUP));
391 	ep = exphead.ex_next;
392 	while (ep != NULL) {
393 		if (!xdr_bool(xdrsp, &true))
394 			goto errout;
395 		strp = &ep->ex_dirp[0];
396 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
397 			goto errout;
398 		grp = ep->ex_groups;
399 		while (grp != NULL) {
400 			if (!xdr_bool(xdrsp, &true))
401 				goto errout;
402 			strp = grp->gr_hp->h_name;
403 			if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
404 				goto errout;
405 			grp = grp->gr_next;
406 		}
407 		if (!xdr_bool(xdrsp, &false))
408 			goto errout;
409 		ep = ep->ex_next;
410 	}
411 	sigsetmask(omask);
412 	if (!xdr_bool(xdrsp, &false))
413 		return (0);
414 	return (1);
415 errout:
416 	sigsetmask(omask);
417 	return (0);
418 }
419 
420 #define LINESIZ	10240
421 char line[LINESIZ];
422 
423 /*
424  * Get the export list
425  */
426 get_exportlist()
427 {
428 	register struct hostent *hp, *nhp;
429 	register char **addrp, **naddrp;
430 	register int i;
431 	register struct grouplist *grp, *grp2;
432 	register struct exportlist *ep, *ep2;
433 	struct ufs_args args;
434 	FILE *inf;
435 	char *cp, *endcp;
436 	char savedc;
437 	int len;
438 	int rootuid, exflags;
439 
440 	/*
441 	 * First, get rid of the old list
442 	 */
443 	ep = exphead.ex_next;
444 	while (ep != NULL) {
445 		grp = ep->ex_groups;
446 		while (grp != NULL) {
447 			addrp = grp->gr_hp->h_addr_list;
448 			while (*addrp)
449 				free(*addrp++);
450 			free((caddr_t)grp->gr_hp->h_addr_list);
451 			free(grp->gr_hp->h_name);
452 			free((caddr_t)grp->gr_hp);
453 			grp2 = grp;
454 			grp = grp->gr_next;
455 			free((caddr_t)grp2);
456 		}
457 		ep2 = ep;
458 		ep = ep->ex_next;
459 		free((caddr_t)ep2);
460 	}
461 
462 	/*
463 	 * Read in the exports file and build the list, calling
464 	 * exportfs() as we go along
465 	 */
466 	exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
467 	if ((inf = fopen(exname, "r")) == NULL) {
468 		syslog(LOG_ERR, "Can't open %s", exname);
469 		exit(2);
470 	}
471 	while (fgets(line, LINESIZ, inf)) {
472 		exflags = M_EXPORTED;
473 		rootuid = def_rootuid;
474 		cp = line;
475 		nextfield(&cp, &endcp);
476 		len = endcp-cp;
477 		if (len <= RPCMNT_PATHLEN && len > 0) {
478 			ep = (struct exportlist *)malloc(sizeof(*ep));
479 			ep->ex_next = ep->ex_prev = (struct exportlist *)0;
480 			ep->ex_groups = (struct grouplist *)0;
481 			bcopy(cp, ep->ex_dirp, len);
482 			ep->ex_dirp[len] = '\0';
483 		} else
484 			goto err;
485 		cp = endcp;
486 		nextfield(&cp, &endcp);
487 		len = endcp-cp;
488 		while (len > 0) {
489 			savedc = *endcp;
490 			*endcp = '\0';
491 			if (len <= RPCMNT_NAMELEN) {
492 				if (*cp == '-') {
493 					cp++;
494 					switch (*cp) {
495 					case 'o':
496 						exflags |= M_EXRDONLY;
497 						break;
498 					case 'r':
499 						if (*++cp == '=')
500 							rootuid = atoi(++cp);
501 						break;
502 					default:
503 						syslog(LOG_WARNING,
504 						  "Bad -%c option in %s",
505 						  *cp, exname);
506 						break;
507 					};
508 				} else if (hp = gethostbyname(cp)) {
509 					grp = (struct grouplist *)
510 						malloc(sizeof(struct grouplist));
511 					if (grp == NULL)
512 						goto err;
513 					nhp = grp->gr_hp = (struct hostent *)
514 						malloc(sizeof(struct hostent));
515 					if (nhp == NULL)
516 						goto err;
517 					bcopy((caddr_t)hp, (caddr_t)nhp,
518 						sizeof(struct hostent));
519 					i = strlen(hp->h_name)+1;
520 					nhp->h_name = (char *)malloc(i);
521 					if (nhp->h_name == NULL)
522 						goto err;
523 					bcopy(hp->h_name, nhp->h_name, i);
524 					addrp = hp->h_addr_list;
525 					i = 1;
526 					while (*addrp++)
527 						i++;
528 					naddrp = nhp->h_addr_list = (char **)
529 						malloc(i*sizeof(char *));
530 					if (naddrp == NULL)
531 						goto err;
532 					addrp = hp->h_addr_list;
533 					while (*addrp) {
534 						*naddrp = (char *)
535 						    malloc(hp->h_length);
536 						bcopy(*addrp, *naddrp,
537 							hp->h_length);
538 						addrp++;
539 						naddrp++;
540 					}
541 					*naddrp = (char *)0;
542 					grp->gr_next = ep->ex_groups;
543 					ep->ex_groups = grp;
544 				}
545 			}
546 			cp = endcp;
547 			*cp = savedc;
548 			nextfield(&cp, &endcp);
549 			len = endcp-cp;
550 		}
551 		args.fspec = 0;
552 		args.exflags = exflags;
553 		args.exroot = rootuid;
554 		if (mount(MOUNT_UFS, ep->ex_dirp, M_UPDATE, &args) < 0) {
555 			syslog(LOG_WARNING, "Can't export %s", ep->ex_dirp);
556 			free((caddr_t)ep);
557 		} else {
558 			ep->ex_rootuid = rootuid;
559 			ep->ex_exflags = exflags;
560 			ep->ex_next = exphead.ex_next;
561 			ep->ex_prev = &exphead;
562 			if (ep->ex_next != NULL)
563 				ep->ex_next->ex_prev = ep;
564 			exphead.ex_next = ep;
565 		}
566 	}
567 	fclose(inf);
568 	return;
569 err:
570 	syslog(LOG_ERR, "Bad Exports File, mountd Failed");
571 	exit(2);
572 }
573 
574 /*
575  * Parse out the next white space separated field
576  */
577 nextfield(cp, endcp)
578 	char **cp;
579 	char **endcp;
580 {
581 	register char *p;
582 
583 	p = *cp;
584 	while (*p == ' ' || *p == '\t')
585 		p++;
586 	if (*p == '\n' || *p == '\0') {
587 		*cp = *endcp = p;
588 		return;
589 	}
590 	*cp = p++;
591 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
592 		p++;
593 	*endcp = p;
594 }
595 
596 /*
597  * Search the remote mounts file for a match.
598  * Iff found
599  *	- set file offset to record and return TRUE
600  * else
601  *	- set file offset to an unused record if found or eof otherwise
602  *	  and return FALSE
603  */
604 fnd_mnt(host, dirp)
605 	char *host;
606 	char *dirp;
607 {
608 	struct mountlist ml;
609 	off_t off, pos;
610 
611 	off = -1;
612 	pos = 0;
613 	lseek(mlfile, (off_t)0, L_SET);
614 	while (read(mlfile, (caddr_t)&ml, sizeof(ml)) == sizeof(ml)) {
615 		if (!strcmp(host, ml.ml_host) && !strcmp(dirp, ml.ml_dirp)) {
616 			lseek(mlfile, (off_t)-sizeof(ml), L_INCR);
617 			return (TRUE);
618 		} else if (ml.ml_host[0] == '\0') {
619 			off = pos;
620 		}
621 		pos += sizeof(ml);
622 	}
623 	if (off != -1)
624 		lseek(mlfile, off, L_SET);
625 	else
626 		lseek(mlfile, (off_t)0, L_XTND);
627 	return (FALSE);
628 }
629