/* * Copyright (c) 1987 Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAXRMTABLINELEN (MAXPATHLEN + MAXHOSTNAMELEN + 2) static char RMTAB[] = "/etc/rmtab"; static void mnt(struct svc_req *, SVCXPRT *); static void mnt3(struct svc_req *, SVCXPRT *); static void mntsgi(struct svc_req *, SVCXPRT *); static void usage(void); static void freeexports(struct exports *); static void *exmalloc(size_t); static char *exstrdup(const char *); static struct groups **newgroup(char *, struct groups **); static struct exports **newexport(char *, struct groups *, struct exports **); static void dostatvfs(struct svc_req *); static void log_cant_reply(SVCXPRT *); static int do_getfh(char *, fhandle_t *); static int issock(int); extern struct hostent *__getverfhostent(struct in_addr, int); extern int getfh(char *, fhandle_t *); extern int issubdir(char *, char *); static void mount(struct svc_req *); static void mount3(struct svc_req *); static void umount(struct svc_req *); static void umountall(struct svc_req *); static void export(struct svc_req *); /* * mountd's version of a "struct mountlist". It is the same except * for the added ml_pos field. */ struct mountdlist { /* same as XDR mountlist */ char *ml_name; char *ml_path; struct mountdlist *ml_nxt; /* private to mountd */ long ml_pos; /* position of mount entry in RMTAB */ int ml_active; /* stamps mount entry as valid or not */ }; static struct mountdlist *mountlist=NULL; static int nfs_portmon = 1; static int rflag = 0; /* Remove most likely corrupt rmtab entries */ static void rmtab_load(void); static void rmtab_delete(long); static void rmtab_valid(long, char); static long rmtab_insert(char *, char *); static void check_rmtab(int); static struct exportlist *getexportlist(void); static void exportlist(struct svc_req *); static struct timeval lifetime = { 60, 0 }; static FILE *f = NULL; extern int __svc_label_agile; int main(int argc, char **argv) { SVCXPRT *transp; int pid; register int i; int tcpsock, udpsock, tcpsock_sgi, udpsock_sgi; int tcpproto, udpproto; while ((i = getopt(argc, argv, "l:nr")) != EOF) { switch (i) { case 'l': lifetime.tv_sec = atoi(optarg); break; case 'n': nfs_portmon = 0; break; case 'r': rflag = 1; break; default: usage(); } } if (optind != argc) { usage(); } __svc_label_agile = (sysconf(_SC_IP_SECOPTS) > 0); if (issock(0)) { /* * Started from inetd -- don't register with portmapper. * Inetd allocates socket descriptors to protocols based * on lexicographic order of protocol name, so the tcp * socket is on descriptor 0, and udp on 1. */ tcpsock = 0; udpsock = 1; tcpsock_sgi = 2; udpsock_sgi = 3; tcpproto = udpproto = 0; } else { #ifndef DEBUG /* * Started from shell, background. */ pid = fork(); if (pid < 0) { perror("mountd: can't fork"); exit(1); } if (pid) { exit(0); } /* * Close existing file descriptors, open "/dev/null" as * standard input, output, and error, and detach from * controlling terminal. */ i = getdtablesize(); while (--i >= 0) (void)close(i); (void)open("/dev/null", O_RDONLY); (void)open("/dev/null", O_WRONLY); (void)dup(1); i = open("/dev/tty", O_RDWR); if (i >= 0) { ioctl(i, TIOCNOTTY, (char *)0); (void)close(i); } #endif /* !DEBUG */ lifetime.tv_sec = 0; /* command line mountd lives forever */ pmap_unset(MOUNTPROG, MOUNTVERS); pmap_unset(MOUNTPROG, MOUNTVERS3); pmap_unset(MOUNTPROG_SGI, MOUNTVERS_SGI_ORIG); tcpsock = udpsock = tcpsock_sgi = udpsock_sgi = RPC_ANYSOCK; tcpproto = IPPROTO_TCP; udpproto = IPPROTO_UDP; } /* 4.3 openlog */ openlog("mountd", LOG_PID, LOG_DAEMON); /* * Create UDP service */ if ((transp = svcudp_create(udpsock)) == NULL) { syslog(LOG_ERR, "couldn't create UDP transport"); exit(1); } if (!svc_register(transp, MOUNTPROG, MOUNTVERS, mnt, udpproto)) { syslog(LOG_ERR, "couldn't register mount as a UDP service"); exit(1); } if ((transp = svcudp_create(udpsock)) == NULL) { syslog(LOG_ERR, "couldn't create UDP transport"); exit(1); } if (!svc_register(transp, MOUNTPROG, MOUNTVERS3, mnt3, udpproto)) { syslog(LOG_ERR, "couldn't register mount as a UDP service"); exit(1); } if ((transp = svcudp_create(udpsock_sgi)) == NULL) { syslog(LOG_INFO, "couldn't create SGI's mount UDP transport"); } else { if (!svc_register(transp, MOUNTPROG_SGI, MOUNTVERS_SGI_ORIG, mntsgi, udpproto)) { syslog(LOG_INFO, "couldn't register SGI's mount as a UDP service"); } } /* * Create TCP service */ if ((transp = svctcp_create(tcpsock, 0, 0)) == NULL) { syslog(LOG_ERR, "couldn't create TCP transport"); exit(1); } if (!svc_register(transp, MOUNTPROG, MOUNTVERS, mnt, tcpproto)) { syslog(LOG_ERR, "couldn't register mount as a TCP service"); exit(1); } if ((transp = svctcp_create(tcpsock_sgi, 0, 0)) == NULL) { syslog(LOG_INFO, "couldn't create SGI's mount TCP transport"); } else { if (!svc_register(transp, MOUNTPROG_SGI, MOUNTVERS_SGI_ORIG, mntsgi, tcpproto)) { syslog(LOG_INFO, "couldn't register SGI's mount as a TCP service"); } } /* * Initalize the world */ _yellowup(1); /* * Start serving */ rmtab_load(); for (;;) { fd_set readfds; readfds = svc_fdset; switch (select(FD_SETSIZE, &readfds, 0, 0, lifetime.tv_sec ? &lifetime : NULL)) { case -1: if (errno == EINTR) break; syslog(LOG_ERR, "select: %m"); if (f) fclose(f); exit(1); case 0: if (f) fclose(f); exit(0); default: svc_getreqset(&readfds); } } } /* * Determine if a descriptor belongs to a socket or not */ static int issock(int fd) { struct stat st; if (fstat(fd, &st) < 0) { return (0); } /* * SunOS returns S_IFIFO for sockets, while 4.3 returns 0 and does not * even have an S_IFIFO mode. Since there is confusion about what the * mode is, we check for what it is not instead of what it is. */ switch (st.st_mode & S_IFMT) { case S_IFCHR: case S_IFREG: case S_IFLNK: case S_IFDIR: case S_IFBLK: return (0); default: return (1); } } /* * Server procedure switch routine */ static void mnt3(struct svc_req *rqstp, SVCXPRT *transp) { struct mountdlist *ml; struct mountdlist *ml_elem; struct mountdlist *mountlist_reply; struct mountdlist *last_ml; #ifdef MNT_TRACE syslog(LOG_INFO, "mnt3: %s requests %u", inet_ntoa(svc_getcaller(transp)->sin_addr), rqstp->rq_proc); #endif switch (rqstp->rq_proc) { case NULLPROC: errno = 0; if (!svc_sendreply(transp, xdr_void, (char *)0)) log_cant_reply(transp); break; case MOUNTPROC_MNT: mount3(rqstp); break; case MOUNTPROC_DUMP: errno = 0; mountlist_reply = NULL; /* Build a list of valid mounts */ for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) { if (ml->ml_active) { /* * Add this entry to the list sent on reply */ ml_elem = (struct mountdlist *) exmalloc(sizeof(struct mountdlist)); ml_elem->ml_path = (char *) exmalloc(strlen(ml->ml_path) + 1); (void)strcpy(ml_elem->ml_path, ml->ml_path); ml_elem->ml_name = (char *) exmalloc(strlen(ml->ml_name) + 1); (void)strcpy(ml_elem->ml_name, ml->ml_name); ml_elem->ml_nxt = NULL; if (mountlist_reply) { last_ml->ml_nxt = ml_elem; last_ml = last_ml->ml_nxt; } else mountlist_reply = last_ml = ml_elem; } } if (!svc_sendreply(transp, xdr_mountlist, (char *)&mountlist_reply)) log_cant_reply(transp); /* Free list sent on reply */ while (mountlist_reply) { ml_elem = mountlist_reply; mountlist_reply = mountlist_reply->ml_nxt; free(ml_elem->ml_path); free(ml_elem->ml_name); free((char *)ml_elem); } break; case MOUNTPROC_UMNT: umount(rqstp); break; case MOUNTPROC_UMNTALL: umountall(rqstp); break; case MOUNTPROC_EXPORT: case MOUNTPROC_EXPORTALL: export(rqstp); break; default: svcerr_noproc(transp); break; } return; } static void mnt(struct svc_req *rqstp, SVCXPRT *transp) { struct mountdlist *ml; struct mountdlist *ml_elem; struct mountdlist *mountlist_reply; struct mountdlist *last_ml; #ifdef MNT_TRACE syslog(LOG_INFO, "mnt: %s requests %u", inet_ntoa(svc_getcaller(transp)->sin_addr), rqstp->rq_proc); #endif switch (rqstp->rq_proc) { case NULLPROC: errno = 0; if (!svc_sendreply(transp, xdr_void, (char *)0)) log_cant_reply(transp); break; case MOUNTPROC_MNT: mount(rqstp); break; case MOUNTPROC_DUMP: errno = 0; mountlist_reply = NULL; /* Build a list of valid mounts */ for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) { if (ml->ml_active) { /* * Add this entry to the list sent on reply */ ml_elem = (struct mountdlist *) exmalloc(sizeof(struct mountdlist)); ml_elem->ml_path = (char *) exmalloc(strlen(ml->ml_path) + 1); (void)strcpy(ml_elem->ml_path, ml->ml_path); ml_elem->ml_name = (char *) exmalloc(strlen(ml->ml_name) + 1); (void)strcpy(ml_elem->ml_name, ml->ml_name); ml_elem->ml_nxt = NULL; if (mountlist_reply) { last_ml->ml_nxt = ml_elem; last_ml = last_ml->ml_nxt; } else mountlist_reply = last_ml = ml_elem; } } if (!svc_sendreply(transp, xdr_mountlist, (char *)&mountlist_reply)) log_cant_reply(transp); /* Free list sent on reply */ while (mountlist_reply) { ml_elem = mountlist_reply; mountlist_reply = mountlist_reply->ml_nxt; free(ml_elem->ml_path); free(ml_elem->ml_name); free((char *)ml_elem); } break; case MOUNTPROC_UMNT: umount(rqstp); break; case MOUNTPROC_UMNTALL: umountall(rqstp); break; case MOUNTPROC_EXPORT: case MOUNTPROC_EXPORTALL: export(rqstp); break; default: svcerr_noproc(transp); break; } return; } /* * Server procedure switch routine for SGI's mount program */ static void mntsgi(struct svc_req *rqstp, SVCXPRT *transp) { switch (rqstp->rq_proc) { case MOUNTPROC_EXPORTLIST: exportlist(rqstp); break; case MOUNTPROC_STATVFS: dostatvfs(rqstp); break; default: mnt(rqstp, transp); break; } return; } struct hostent * getclientsname(SVCXPRT *transp) { struct sockaddr_in actual; actual = *svc_getcaller(transp); if (nfs_portmon) { if (ntohs(actual.sin_port) >= IPPORT_RESERVED) { return (NULL); } } /* * Don't use the unix credentials to get the machine name, instead use * the source IP address. */ return (__getverfhostent(actual.sin_addr, 1)); } static void log_cant_reply(SVCXPRT *transp) { int saverrno; struct sockaddr_in actual; register struct hostent *hp; register char *name; saverrno = errno; /* save error code */ actual = *svc_getcaller(transp); /* * Don't use the unix credentials to get the machine name, instead use * the source IP address. */ hp = gethostbyaddr((char *)&actual.sin_addr, sizeof(actual.sin_addr), AF_INET); if (hp != NULL) name = hp->h_name; else name = inet_ntoa(actual.sin_addr); errno = saverrno; if (errno == 0) syslog(LOG_ERR, "couldn't send reply to %s", name); else syslog(LOG_ERR, "couldn't send reply to %s: %m", name); } /* * Check mount requests, add to mounted list if ok */ static void mount(struct svc_req *rqstp) { SVCXPRT *transp; fhandle_t fh; struct fhstatus fhs; char *path; struct mountdlist *ml; struct mountdlist *end; char *gr; char *grl; struct exportent *xent; struct exportlist *el; char *machine; char **aliases; struct hostent *client; char *clientname; cap_t ocap; cap_value_t cap_mac_read = CAP_MAC_READ; transp = rqstp->rq_xprt; path = NULL; if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { struct authunix_parms *aup; aup = (struct authunix_parms *)rqstp->rq_clntcred; clientname = aup->aup_machname; } else { clientname = ""; } if (!svc_getargs(transp, xdr_path, &path)) { svcerr_decode(transp); return; } if (do_getfh(path, &fh) < 0) { if (errno == EINVAL) { fhs.fhs_status = EACCES; } else { fhs.fhs_status = errno; } syslog(LOG_DEBUG, "%s mount request for %s: getfh failed: %m", clientname, path); goto fail; } else fhs.fhs_status = 0; el = getexportlist(); if (el == NULL) { fhs.fhs_status = EACCES; goto fail; } ocap = cap_acquire(1, &cap_mac_read); while (!issubdir(path, el->el_entry.ee_dirname)) { el = el->el_next; if (el == NULL) { cap_surrender(ocap); fhs.fhs_status = EACCES; goto fail; } } cap_surrender(ocap); xent = (struct exportent *) &el->el_entry; /* Check access list */ grl = getexportopt(xent, ACCESS_OPT); if (grl == NULL) { /* * Skip the address to name lookup if exported to * everybody. All this can do is waste time. */ client = NULL; goto hit; } client = getclientsname(transp); if (client == NULL) { clientname = ""; } else { clientname = client->h_name; } while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (strcasecmp(gr, clientname) == 0) goto hit; if (client == NULL) { continue; } for (aliases = client->h_aliases; *aliases != NULL; aliases++) { if (strcasecmp(gr, *aliases) == 0) goto hit; } } grl = getexportopt(xent, ACCESS_OPT); while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (innetgr(gr, clientname, (char *)NULL, _yp_domain)) goto hit; if (client == NULL) { continue; } for (aliases = client->h_aliases; *aliases != NULL; aliases++) { if (innetgr(gr, *aliases, (char *)NULL, _yp_domain)) goto hit; } } /* Check root and rw lists */ grl = getexportopt(xent, ROOT_OPT); if (grl != NULL) { while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (strcasecmp(gr, clientname) == 0) goto hit; } } grl = getexportopt(xent, RW_OPT); if (grl != NULL) { while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (strcasecmp(gr, clientname) == 0) goto hit; } } fhs.fhs_status = EACCES; goto fail; hit: fhs.fhs_fh = fh; machine = NULL; for (ml = mountlist; ml != NULL && machine == NULL; ml = ml->ml_nxt) { if (strcmp(ml->ml_path, path) == 0) { if (strcasecmp(ml->ml_name, clientname) == 0) { goto found; } if (client == NULL) { continue; } for (aliases = client->h_aliases; *aliases != NULL; aliases++) { if (strcasecmp(ml->ml_name, *aliases) == 0) { goto found; } } } } found: errno = 0; if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs)) log_cant_reply(transp); if (ml == NULL && strcmp(clientname, "")) { ml = (struct mountdlist *) exmalloc(sizeof(struct mountdlist)); ml->ml_path = exstrdup(path); ml->ml_name = exstrdup(clientname); #ifdef RMTAB_CHECKING /* * Add new entry at the tail of the list, this is required * for the check_rmtab() function to work. */ ml->ml_nxt = NULL; check_rmtab(__LINE__); ml->ml_pos = rmtab_insert(clientname, path); if (mountlist == NULL) { mountlist = ml; } else { for (end = mountlist; end->ml_nxt != NULL; end = end->ml_nxt); end->ml_nxt = ml; } check_rmtab(__LINE__); #else /* RMTAB_CHECKING */ /* * Add new entry at the head of the list */ ml->ml_nxt = mountlist; ml->ml_pos = rmtab_insert(clientname, path); mountlist = ml; #endif /* RMTAB_CHECKING */ } else if (ml != NULL && !ml->ml_active) { /* Validate line in /etc/rmtab */ rmtab_valid(ml->ml_pos, ml->ml_name[0]); ml->ml_active = 1; } svc_freeargs(transp, xdr_path, &path); return; fail: errno = 0; if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs)) log_cant_reply(transp); svc_freeargs(transp, xdr_path, &path); } /* * Check mount requests, add to mounted list if ok */ static void mount3(struct svc_req *rqstp) { SVCXPRT *transp; struct mountres3 mountres3; char fh3[FHSIZE3]; char *path; struct mountdlist *ml; struct mountdlist *end; char *gr; char *grl; struct exportent *xent; struct exportlist *el; char *machine; char **aliases; struct hostent *client; char *clientname; int flavor; cap_t ocap; cap_value_t cap_mac_read = CAP_MAC_READ; transp = rqstp->rq_xprt; path = NULL; if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { struct authunix_parms *aup; aup = (struct authunix_parms *)rqstp->rq_clntcred; clientname = aup->aup_machname; } else { clientname = ""; } if (!svc_getargs(transp, xdr_path, &path)) { svcerr_decode(transp); return; } mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = 32; mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh3; if (do_getfh(path, (fhandle_t *) fh3) < 0) { if (errno == EINVAL) { mountres3.fhs_status = (mountstat3) EACCES; } else { mountres3.fhs_status = (mountstat3) errno; } syslog(LOG_DEBUG, "%s mount3 request for %s: getfh failed: %m", clientname, path); goto fail; } else mountres3.fhs_status = MNT_OK; el = getexportlist(); if (el == NULL) { mountres3.fhs_status = (mountstat3) EACCES; goto fail; } ocap = cap_acquire(1, &cap_mac_read); while (!issubdir(path, el->el_entry.ee_dirname)) { el = el->el_next; if (el == NULL) { cap_surrender(ocap); mountres3.fhs_status = (mountstat3) EACCES; goto fail; } } cap_surrender(ocap); xent = (struct exportent *) &el->el_entry; /* Check access list */ grl = getexportopt(xent, ACCESS_OPT); if (grl == NULL) { /* * Skip the address to name lookup if exported to * everybody. All this can do is waste time. */ client = NULL; goto hit; } client = getclientsname(transp); if (client == NULL) { clientname = ""; } else { clientname = client->h_name; } while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (strcasecmp(gr, clientname) == 0) goto hit; if (client == NULL) { continue; } for (aliases = client->h_aliases; *aliases != NULL; aliases++) { if (strcasecmp(gr, *aliases) == 0) goto hit; } } grl = getexportopt(xent, ACCESS_OPT); while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (innetgr(gr, clientname, (char *)NULL, _yp_domain)) goto hit; if (client == NULL) { continue; } for (aliases = client->h_aliases; *aliases != NULL; aliases++) { if (innetgr(gr, *aliases, (char *)NULL, _yp_domain)) goto hit; } } /* Check root and rw lists */ grl = getexportopt(xent, ROOT_OPT); if (grl != NULL) { while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (strcasecmp(gr, clientname) == 0) goto hit; } } grl = getexportopt(xent, RW_OPT); if (grl != NULL) { while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; if (strcasecmp(gr, clientname) == 0) goto hit; } } mountres3.fhs_status = (mountstat3) EACCES; goto fail; hit: machine = NULL; for (ml = mountlist; ml != NULL && machine == NULL; ml = ml->ml_nxt) { if (strcmp(ml->ml_path, path) == 0) { if (strcasecmp(ml->ml_name, clientname) == 0) { goto found; } if (client == NULL) { continue; } for (aliases = client->h_aliases; *aliases != NULL; aliases++) { if (strcasecmp(ml->ml_name, *aliases) == 0) { goto found; } } } } found: flavor = AUTH_UNIX; mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &flavor; mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1; errno = 0; if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3)) log_cant_reply(transp); if (ml == NULL && strcmp(clientname, "")) { ml = (struct mountdlist *) exmalloc(sizeof(struct mountdlist)); ml->ml_path = exstrdup(path); ml->ml_name = exstrdup(clientname); #ifdef RMTAB_CHECKING /* * Add new entry at the tail of the list, this is required * for the check_rmtab() function to work. */ ml->ml_nxt = NULL; check_rmtab(__LINE__); ml->ml_pos = rmtab_insert(clientname, path); if (mountlist == NULL) { mountlist = ml; } else { for (end = mountlist; end->ml_nxt != NULL; end = end->ml_nxt); end->ml_nxt = ml; } check_rmtab(__LINE__); #else /* RMTAB_CHECKING */ /* * Add new entry at the head of the list */ ml->ml_nxt = mountlist; ml->ml_pos = rmtab_insert(clientname, path); mountlist = ml; #endif /* RMTAB_CHECKING */ } else if (ml != NULL && !ml->ml_active) { /* Validate line in /etc/rmtab */ rmtab_valid(ml->ml_pos, ml->ml_name[0]); ml->ml_active = 1; } svc_freeargs(transp, xdr_path, &path); return; fail: errno = 0; if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3)) log_cant_reply(transp); svc_freeargs(transp, xdr_path, &path); } /* * Helper to attempt a removal * Returns TRUE if we found it. */ int help_remove(char *name, char *path) { struct mountdlist *ml; int gotany = 0; for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) { if ((path == NULL || strcmp(ml->ml_path, path) == 0) && strcasecmp(ml->ml_name, name) == 0) { check_rmtab(__LINE__); rmtab_delete(ml->ml_pos); ml->ml_active = 0; check_rmtab(__LINE__); if (path) return (1); gotany = 1; } } return (gotany); } /* * Remove an entry from mounted list */ static void umount(struct svc_req *rqstp) { char *path; char *client; struct hostent *hp; SVCXPRT *transp; transp = rqstp->rq_xprt; path = NULL; if (!svc_getargs(transp, xdr_path, &path)) { svcerr_decode(transp); return; } if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { struct authunix_parms *aup; aup = (struct authunix_parms *)rqstp->rq_clntcred; client = aup->aup_machname; } else { client = ""; } if (!help_remove(client, path)) { hp = getclientsname(transp); if (hp) { (void) help_remove(hp->h_name, path); } } errno = 0; if (!svc_sendreply(transp, xdr_void, (char *)NULL)) log_cant_reply(transp); svc_freeargs(transp, xdr_path, &path); } /* * Remove all entries for one machine from mounted list */ static void umountall(struct svc_req *rqstp) { char *client; struct hostent *hp; SVCXPRT *transp; transp = rqstp->rq_xprt; if (!svc_getargs(transp, xdr_void, NULL)) { svcerr_decode(transp); return; } if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { struct authunix_parms *aup; aup = (struct authunix_parms *)rqstp->rq_clntcred; client = aup->aup_machname; } else { client = ""; } if (!help_remove(client, NULL)) { hp = getclientsname(transp); if (hp) { (void) help_remove(hp->h_name, NULL); } } /* * We assume that this call is asynchronous and made via the portmapper * callit routine. Therefore return control immediately. The error * causes the portmapper to remain silent, as opposed to every machine * on the net blasting the requester with a response. */ svcerr_systemerr(transp); } /* * send current export list */ static void export(struct svc_req *rqstp) { struct exportlist *el; struct exportent *xent; struct exports *ex; struct exports **tail; char *grl; char *gr; struct groups *groups; struct groups **grtail; SVCXPRT *transp; transp = rqstp->rq_xprt; if (!svc_getargs(transp, xdr_void, NULL)) { svcerr_decode(transp); return; } ex = NULL; tail = &ex; for (el = getexportlist(); el != NULL; el = el->el_next) { xent = (struct exportent *) &el->el_entry; grl = getexportopt(xent, ACCESS_OPT); groups = NULL; if (grl != NULL) { grtail = &groups; while ((gr = strtok(grl, ":")) != NULL) { grl = NULL; grtail = newgroup(gr, grtail); } } tail = newexport(xent->xent_dirname, groups, tail); } errno = 0; if (!svc_sendreply(transp, xdr_exports, (char *)&ex)) log_cant_reply(transp); freeexports(ex); } #if 0 static int str_to_uid(char *name, int *uidp) { struct passwd *pw; if (isdigit(*name)) { *uidp = atoi(name); return 1; } pw = getpwnam(name); if (pw) { *uidp = pw->pw_uid; return 1; } return 0; } #endif /* * Send export list as dirname/options string pairs. */ static void exportlist(struct svc_req *rqstp) { SVCXPRT *transp; struct exportlist *el; transp = rqstp->rq_xprt; if (!svc_getargs(transp, xdr_void, NULL)) { svcerr_decode(transp); return; } el = getexportlist(); errno = 0; if (!svc_sendreply(transp, xdr_exportlist, (char *)&el)) log_cant_reply(transp); } static void dostatvfs(struct svc_req *rqstp) { SVCXPRT *transp; char *path; struct statvfs lclstatvfs; struct mntrpc_statvfs rpcstatvfs; transp = rqstp->rq_xprt; path = NULL; if (!svc_getargs(transp, xdr_path, &path)) { svcerr_decode(transp); return; } errno = statvfs(path, &lclstatvfs); svc_freeargs(transp, xdr_path, &path); if (errno) { syslog(LOG_ERR,"rpc.mountd: error %d in statvfs(%s)\n", errno, path); svcerr_systemerr(transp); return; } rpcstatvfs.f_bsize = lclstatvfs.f_bsize; rpcstatvfs.f_frsize = lclstatvfs.f_frsize; rpcstatvfs.f_blocks = lclstatvfs.f_blocks; rpcstatvfs.f_bavail = lclstatvfs.f_bavail; rpcstatvfs.f_files = lclstatvfs.f_files; rpcstatvfs.f_ffree = lclstatvfs.f_ffree; rpcstatvfs.f_favail = lclstatvfs.f_favail; rpcstatvfs.f_fsid = lclstatvfs.f_fsid; strncpy(rpcstatvfs.f_basetype, lclstatvfs.f_basetype, sizeof rpcstatvfs.f_basetype); rpcstatvfs.f_flag = lclstatvfs.f_flag; strncpy(rpcstatvfs.f_fstr, lclstatvfs.f_fstr, sizeof rpcstatvfs.f_fstr); rpcstatvfs.f_namemax = lclstatvfs.f_namemax; errno = 0; if (!svc_sendreply(transp, xdr_statvfs, (char *)&rpcstatvfs)) log_cant_reply(transp); } #if 0 static void newgroups(struct exportent *xent, char *optname, struct groups **grp) { char *list, *element; list = getexportopt(xent, optname); if (list == NULL) return; while ((element = strtok(list, ":")) != NULL) { list = NULL; grp = newgroup(element, grp); } } static void freegroups(struct groups *gr) { while (gr) { struct groups *tmp; tmp = gr->g_next; free((char *) gr); gr = tmp; } } #endif static void freeexports(struct exports *ex) { struct groups *groups, *tmpgroups; struct exports *tmpex; while (ex) { groups = ex->ex_groups; while (groups) { tmpgroups = groups->g_next; free(groups->g_name); free((char *)groups); groups = tmpgroups; } tmpex = ex->ex_next; free(ex->ex_name); free((char *)ex); ex = tmpex; } } static struct groups ** newgroup(char *name, struct groups **tail) { struct groups *new; new = (struct groups *) exmalloc(sizeof(*new)); new->g_name = exstrdup(name); new->g_next = NULL; *tail = new; return (&new->g_next); } static struct exports ** newexport(char *name, struct groups *groups, struct exports **tail) { struct exports *new; new = (struct exports *) exmalloc(sizeof(*new)); new->ex_name = exstrdup(name); new->ex_groups = groups; new->ex_next = NULL; *tail = new; return (&new->ex_next); } static void * exmalloc(size_t size) { void *ret; if ((ret = malloc(size)) == (void *) 0) { syslog(LOG_ERR, "Out of memory"); if (f) fclose(f); exit(1); } return (ret); } static char * exstrdup(const char *s) { return strcpy(exmalloc(strlen(s)+1), s); } static void usage(void) { (void)fprintf(stderr, "usage: rpc.mountd [-l lifetime] [-n]\n"); exit(1); } /* * Old geth() took a file descriptor. New getfh() takes a pathname. * So the the mount daemon can run on both old and new kernels, we try * the old version of getfh() if the new one fails. */ static int do_getfh(char *path, fhandle_t *fh) { int fd; int res; int save; cap_t ocap; static const cap_value_t cap_mount_read[] = {CAP_MOUNT_MGT, CAP_MAC_READ}; ocap = cap_acquire (2, cap_mount_read); res = getfh(path, fh); cap_surrender(ocap); if (res < 0 && errno == EBADF) { /* * This kernel does not have the new-style getfh() */ ocap = cap_acquire (1, &cap_mount_read[1]); res = fd = open(path, 0, 0); cap_surrender(ocap); if (fd >= 0) { ocap = cap_acquire (1, &cap_mount_read[0]); res = getfh((char *)fd, fh); cap_surrender(ocap); save = errno; (void)close(fd); errno = (save == ENOENT) ? EACCES : save; } else if (errno == ENOENT) { errno = EACCES; } } else if (errno == ENOENT) { errno = EACCES; } return (res); } static void rmtab_load(void) { char *path; char *name; char *end; char *check_1; char *check_2; struct mountdlist *ml; char line[MAXRMTABLINELEN]; f = fopen(RMTAB, "r"); if (f != NULL) { while (fgets(line, sizeof(line), f) != NULL) { name = line; /* * If the name/path delimiter (colon) is found * and we are removing questionable entries, * perform further checking. */ if (((path = strchr(name, ':')) != NULL) && (rflag == 1)) { check_1 = strchr(path+1, ':'); check_2 = strchr(path, '#'); } else { check_1 = NULL; check_2 = NULL; } /* * If the host name start with a # it's an entry that * has been inactivated. * If we didn't find the colon it's not a valid entry * If we found a second colon it's not a valid entry * If we found a pound sign anywhere, but the begining * of a line it's not a valid entry */ if ((*name != '#') && (path != NULL) && (*(path+1) == '/') && (check_1 == NULL) && (check_2 == NULL)) { /* Null terminate name */ *path = 0; path++; end = strchr(path, '\n'); if (end != NULL) { /* Null terminate path */ *end = 0; } else { /* skip entry, no newline found */ continue; } /* * If this is a duplicate entry skip it. */ if ((rflag == 1) && dup_check( path, name)) { continue; /* skip duplicate entry */ } /* * Add this entry to the list */ ml = (struct mountdlist *) exmalloc(sizeof(struct mountdlist)); ml->ml_path = (char *) exmalloc(strlen(path) + 1); (void)strcpy(ml->ml_path, path); ml->ml_name = (char *) exmalloc(strlen(name) + 1); (void)strcpy(ml->ml_name, name); ml->ml_nxt = mountlist; mountlist = ml; } } fclose(f); (void)truncate(RMTAB, (off_t)0); } f = fopen(RMTAB, "w+"); if (f != NULL) { setvbuf(f, (char *) 0, _IOLBF, MAXRMTABLINELEN + 1); for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) { ml->ml_pos = rmtab_insert(ml->ml_name, ml->ml_path); } } } int dup_check( path, name) char *path; char *name; { struct mountdlist *ml; for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) { if ((strncmp( path, ml->ml_path, strlen(path)) == 0) && (strncmp( name, ml->ml_name, strlen(name)) == 0)) { return( 1); /* duplicate entry */ } } return( 0); /* no duplicate entry found */ } static long rmtab_insert(char *name, char *path) { long pos; if (f == NULL || fseek(f, 0L, 2) == -1) { return (-1); } pos = ftell(f); if (fprintf(f, "%s:%s\n", name, path) == EOF) { fflush(f); return (-1); } fflush(f); return (pos); } static void rmtab_delete(long pos) { if (f != NULL && pos != -1 && fseek(f, pos, 0) == 0) { fprintf(f, "#"); fflush(f); } } static void rmtab_valid(long pos, char first) { if (f != NULL && pos != -1 && fseek(f, pos, 0) == 0) { putc(first, f); fflush(f); } } static struct exportlist * getexportlist(void) { struct stat sb; struct exportlist **tail; struct exportlist *el; struct exportent *xent; static FILE *xtab; static time_t xtabmtime = -1; static struct exportlist *list; if (xtab == NULL) { xtab = setexportent(); if (xtab == NULL) return NULL; flock(fileno(xtab), LOCK_UN); } if (fstat(fileno(xtab), &sb) < 0) { endexportent(xtab); xtab = NULL; return NULL; } if (xtabmtime == sb.st_mtime) { /* cached list is still valid */ return list; } tail = &list; while ((el = *tail) != NULL) { *tail = el->el_next; free(el->el_entry.ee_dirname); if (el->el_entry.ee_options) free(el->el_entry.ee_options); free((char *) el); } rewind(xtab); flock(fileno(xtab), LOCK_SH); while ((xent = getexportent(xtab)) != NULL) { el = (struct exportlist *) exmalloc(sizeof *el); el->el_entry.ee_dirname = exstrdup(xent->xent_dirname); if (xent->xent_options) el->el_entry.ee_options = exstrdup(xent->xent_options); else el->el_entry.ee_options = NULL; el->el_next = NULL; *tail = el; tail = &el->el_next; } flock(fileno(xtab), LOCK_UN); xtabmtime = sb.st_mtime; return list; } static void check_rmtab(int from_line) { #ifdef RMTAB_CHECKING char *path; char *name; char *end; struct mountdlist *ml; char line[MAXRMTABLINELEN]; int error=0; /* Place the file pointer at the beginning of the file */ if (f == NULL || fseek(f, 0L, 0) == -1) { syslog(LOG_INFO, "Seek to beginning of rmtab failed"); return; } /* Start with the first active mount */ ml = mountlist; if (ml == NULL) return; /* Read each line in rmtab */ while (fgets(line, sizeof(line), f) != NULL) { /* Get the starting positions of name and path */ name = line; path = strchr(name, ':'); /* Make sure we have a path */ if (path == NULL) { error |= 0x1000; } /* Skip inactive or no path entires */ if ((*name != '#') && (path != NULL)) { /* Null terminate name */ *path = 0; /* Adjust to the true starting positions of path */ path++; /* Find end of path and terminate */ end = strchr(path, '\n'); if (end != NULL) { /* Null terminate path */ *end = 0; /* * Verify that the name and path we have * matches that read from the rmtab file. */ if (strcmp(ml->ml_name, name) != 0) { error |= 0x0030; } if (strcmp(ml->ml_path, path) != 0) { error |= 0x0004; } } else { /* no newline, skip entry */ error |= 0x0200; } /* Next active entry */ ml = ml->ml_nxt; } } if (error) { syslog(LOG_INFO, "rmtab detected corrupt of type 0x%x, on call from line %d", error, from_line); } #endif /* RMTAB_CHECKING */ }