void mntlist_send(SVCXPRT *transp) { (void) rw_rdlock(&rmtab_lock); errno = 0; if (!svc_sendreply(transp, xdr_mntlistencode, (char *)&mntlist)) log_cant_reply(transp); (void) rw_unlock(&rmtab_lock); }
/* * Remove an entry from mounted list */ static void umount(struct svc_req *rqstp) { char *host, *path, *remove_path; char rpath[MAXPATHLEN]; struct nd_hostservlist *clnames = NULL; SVCXPRT *transp; struct netbuf *nb; transp = rqstp->rq_xprt; path = NULL; if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) { svcerr_decode(transp); return; } errno = 0; if (!svc_sendreply(transp, xdr_void, (char *)NULL)) log_cant_reply(transp); getclientsnames(transp, &nb, &clnames); if (clnames == NULL) { /* * Without the hostname we can't do audit or delete * this host from the mount entries. */ svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); return; } host = clnames->h_hostservs[0].h_host; if (verbose) syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path); audit_mountd_umount(host, path); remove_path = rpath; /* assume we will use the cannonical path */ if (realpath(path, rpath) == NULL) { if (verbose) syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path); remove_path = path; /* use path provided instead */ } mntlist_delete(host, remove_path); /* remove from mount list */ svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); netdir_free(clnames, ND_HOSTSERVLIST); }
/* * Server procedure switch routine */ void mnt(struct svc_req *rqstp, SVCXPRT *transp) { switch (rqstp->rq_proc) { case NULLPROC: errno = 0; if (!svc_sendreply(transp, xdr_void, (char *)0)) log_cant_reply(transp); return; case MOUNTPROC_MNT: mount(rqstp); return; case MOUNTPROC_DUMP: mntlist_send(transp); return; case MOUNTPROC_UMNT: umount(rqstp); return; case MOUNTPROC_UMNTALL: umountall(rqstp); return; case MOUNTPROC_EXPORT: case MOUNTPROC_EXPORTALL: export(rqstp); return; case MOUNTPROC_PATHCONF: if (rqstp->rq_vers == MOUNTVERS_POSIX) mnt_pathconf(rqstp); else svcerr_noproc(transp); return; default: svcerr_noproc(transp); return; } }
/* * Check mount requests, add to mounted list if ok */ static void mount(struct svc_req *rqstp) { SVCXPRT *transp; int version, vers; struct fhstatus fhs; struct mountres3 mountres3; char fh[FHSIZE3]; int len = FHSIZE3; char *path, rpath[MAXPATHLEN]; struct share *sh = NULL; struct nd_hostservlist *clnames = NULL; char *host = NULL; int error = 0, lofs_tried = 0; int flavor_list[MAX_FLAVORS]; int flavor_count; struct netbuf *nb; transp = rqstp->rq_xprt; version = rqstp->rq_vers; path = NULL; if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) { svcerr_decode(transp); return; } getclientsnames(transp, &nb, &clnames); if (clnames == NULL || nb == NULL) { /* * We failed to get a name for the client, even 'anon', * probably because we ran out of memory. In this situation * it doesn't make sense to allow the mount to succeed. */ error = EACCES; goto reply; } host = clnames->h_hostservs[0].h_host; /* * If the version being used is less than the minimum version, * the filehandle translation should not be provided to the * client. */ if (rejecting || version < mount_vers_min) { if (verbose) syslog(LOG_NOTICE, "Rejected mount: %s for %s", host, path); error = EACCES; goto reply; } /* * Trusted Extension doesn't support older versions of nfs(v2, v3). * To prevent circumventing TX label policy via using an older * version of nfs client, reject the mount request and log an * error. */ if (is_system_labeled()) { syslog(LOG_ERR, "mount rejected: Solaris TX only supports nfs4 clients"); error = EACCES; goto reply; } /* * Get the real path (no symbolic links in it) */ if (realpath(path, rpath) == NULL) { error = errno; if (verbose) syslog(LOG_ERR, "mount request: realpath: %s: %m", path); if (error == ENOENT) error = mount_enoent_error(path, rpath, clnames, nb, flavor_list); goto reply; } if ((sh = findentry(rpath)) == NULL && (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) { error = EACCES; goto reply; } /* * Check if this is a "nosub" only export, in which case, mounting * subdirectories isn't allowed. Bug 1184573. */ if (checkrootmount(sh, rpath) == 0) { error = EACCES; goto reply; } if (newopts(sh->sh_opts)) flavor_count = getclientsflavors_new(sh, nb, clnames, flavor_list); else flavor_count = getclientsflavors_old(sh, nb, clnames, flavor_list); if (flavor_count == 0) { error = EACCES; goto reply; } /* * Now get the filehandle. * * NFS V2 clients get a 32 byte filehandle. * NFS V3 clients get a 32 or 64 byte filehandle, depending on * the embedded FIDs. */ vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION; /* LINTED pointer alignment */ while (nfs_getfh(rpath, vers, &len, fh) < 0) { if (errno == EINVAL && (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) { errno = 0; continue; } error = errno == EINVAL ? EACCES : errno; syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m", path); break; } if (version == MOUNTVERS3) { mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len; mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh; } else { bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE); } reply: switch (version) { case MOUNTVERS: case MOUNTVERS_POSIX: if (error == EINVAL) fhs.fhs_status = NFSERR_ACCES; else if (error == EREMOTE) fhs.fhs_status = NFSERR_REMOTE; else fhs.fhs_status = error; if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs)) log_cant_reply(transp); audit_mountd_mount(host, path, fhs.fhs_status); /* BSM */ break; case MOUNTVERS3: if (!error) { mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = flavor_list; mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = flavor_count; } else if (error == ENAMETOOLONG) error = MNT3ERR_NAMETOOLONG; mountres3.fhs_status = error; if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3)) log_cant_reply(transp); audit_mountd_mount(host, path, mountres3.fhs_status); /* BSM */ break; } if (verbose) syslog(LOG_NOTICE, "MOUNT: %s %s %s", (host == NULL) ? "unknown host" : host, error ? "denied" : "mounted", path); if (path != NULL) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); if (!error) mntlist_new(host, rpath); /* add entry to mount list */ done: if (sh) sharefree(sh); netdir_free(clnames, ND_HOSTSERVLIST); }
/* * Answer pathconf questions for the mount point fs */ static void mnt_pathconf(struct svc_req *rqstp) { SVCXPRT *transp; struct pathcnf p; char *path, rpath[MAXPATHLEN]; struct stat st; transp = rqstp->rq_xprt; path = NULL; (void) memset((caddr_t)&p, 0, sizeof (p)); if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) { svcerr_decode(transp); return; } if (lstat(path, &st) < 0) { _PC_SET(_PC_ERROR, p.pc_mask); goto done; } /* * Get a path without symbolic links. */ if (realpath(path, rpath) == NULL) { syslog(LOG_DEBUG, "mount request: realpath failed on %s: %m", path); _PC_SET(_PC_ERROR, p.pc_mask); goto done; } (void) memset((caddr_t)&p, 0, sizeof (p)); /* * can't ask about devices over NFS */ _PC_SET(_PC_MAX_CANON, p.pc_mask); _PC_SET(_PC_MAX_INPUT, p.pc_mask); _PC_SET(_PC_PIPE_BUF, p.pc_mask); _PC_SET(_PC_VDISABLE, p.pc_mask); errno = 0; p.pc_link_max = pathconf(rpath, _PC_LINK_MAX); if (errno) _PC_SET(_PC_LINK_MAX, p.pc_mask); p.pc_name_max = pathconf(rpath, _PC_NAME_MAX); if (errno) _PC_SET(_PC_NAME_MAX, p.pc_mask); p.pc_path_max = pathconf(rpath, _PC_PATH_MAX); if (errno) _PC_SET(_PC_PATH_MAX, p.pc_mask); if (pathconf(rpath, _PC_NO_TRUNC) == 1) _PC_SET(_PC_NO_TRUNC, p.pc_mask); if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1) _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask); done: errno = 0; if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p)) log_cant_reply(transp); if (path != NULL) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); }