Esempio n. 1
0
static share_t *
sharedup(share_t *sh)
{
	share_t *nsh;

	nsh = (share_t *)calloc(1, sizeof (*nsh));
	if (nsh == NULL)
		return (NULL);

	nsh->sh_path = strdup(sh->sh_path);
	if (nsh->sh_path == NULL)
		goto alloc_failed;
	nsh->sh_res = strdup(sh->sh_res);
	if (nsh->sh_res == NULL)
		goto alloc_failed;
	nsh->sh_fstype = strdup(sh->sh_fstype);
	if (nsh->sh_fstype == NULL)
		goto alloc_failed;
	nsh->sh_opts = strdup(sh->sh_opts);
	if (nsh->sh_opts == NULL)
		goto alloc_failed;
	nsh->sh_descr = strdup(sh->sh_descr);
	if (nsh->sh_descr == NULL)
		goto alloc_failed;
	return (nsh);

alloc_failed:
	sharefree(nsh);
	return (NULL);
}
Esempio n. 2
0
static void
sh_free(struct sh_list *shp)
{
	register struct sh_list *next;

	while (shp) {
		sharefree(shp->shl_sh);
		next = shp->shl_next;
		free(shp);
		shp = next;
	}
}
Esempio n. 3
0
static void
nfscmd_charmap_lookup(door_desc_t *dp, nfscmd_arg_t *args)
{
	nfscmd_res_t res;
	struct netbuf nb;
	struct sockaddr sa;
	struct share *sh = NULL;
	char *opts;
	char *name;

	memset(&res, '\0', sizeof (res));
	res.version = NFSCMD_VERS_1;
	res.cmd = NFSCMD_CHARMAP_LOOKUP;

	sh = findentry(args->arg.charmap.path);

	if (sh != NULL) {
		nb.len = nb.maxlen = sizeof (struct sockaddr);
		nb.buf = (char *)&sa;

		sa = args->arg.charmap.addr;

		name = charmap_search(&nb, sh->sh_opts);
		if (name != NULL) {
			strcpy(res.result.charmap.codeset, name);
			res.result.charmap.apply = B_TRUE;
			res.error = NFSCMD_ERR_SUCCESS;
			free(name);
		} else {
			res.result.charmap.apply = B_FALSE;
			res.error = NFSCMD_ERR_NOTFOUND;
		}
		sharefree(sh);
	} else {
		res.error = NFSCMD_ERR_NOTFOUND;
	}

	(void) door_return((char *)&res, sizeof (nfscmd_res_t), NULL, 0);
	(void) door_return(NULL, 0, NULL, 0);
	/* NOTREACHED */
}
Esempio n. 4
0
/*
 * 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);
}
Esempio n. 5
0
/*
 * Return only EACCES if client does not have access
 *  to this directory.
 * "If the server exports only /a/b, an attempt to
 *  mount a/b/c will fail with ENOENT if the directory
 *  does not exist"... However, if the client
 *  does not have access to /a/b, an attacker can
 *  determine whether the directory exists.
 * This routine checks either existence of the file or
 * existence of the file name entry in the mount table.
 * If the file exists and there is no file name entry,
 * the error returned should be EACCES.
 * If the file does not exist, it must be determined
 * whether the client has access to a parent
 * directory.  If the client has access to a parent
 * directory, the error returned should be ENOENT,
 * otherwise EACCES.
 */
static int
mount_enoent_error(char *path, char *rpath, struct nd_hostservlist *clnames,
    struct netbuf *nb, int *flavor_list)
{
	char *checkpath, *dp;
	struct share *sh = NULL;
	int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
	int flavor_count;

	checkpath = strdup(path);
	if (checkpath == NULL) {
		syslog(LOG_ERR, "mount_enoent: no memory");
		return (EACCES);
	}

	/* CONSTCOND */
	while (1) {
		if (sh) {
			sharefree(sh);
			sh = NULL;
		}
		if ((sh = findentry(rpath)) == NULL &&
			(sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
			/*
			 * There is no file name entry.
			 * If the file (with symbolic links resolved) exists,
			 * the error returned should be EACCES.
			 */
			if (realpath_error == 0)
				break;
		} else if (checkrootmount(sh, rpath) == 0) {
			/*
			 * This is a "nosub" only export, in which case,
			 * mounting subdirectories isn't allowed.
			 * If the file (with symbolic links resolved) exists,
			 * the error returned should be EACCES.
			 */
			if (realpath_error == 0)
				break;
		} else {
			/*
			 * Check permissions in mount table.
			 */
			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) {
				/*
				 * Found entry in table and
				 * client has correct permissions.
				 */
				reply_error = ENOENT;
				break;
			}
		}
		/*
		 * Check all parent directories.
		 */
		dp = strrchr(checkpath, '/');
		if (dp == NULL)
			break;
		*dp = '\0';
		if (strlen(checkpath) == 0)
			break;
		/*
		 * Get the real path (no symbolic links in it)
		 */
		if (realpath(checkpath, rpath) == NULL) {
			if (errno != ENOENT)
				break;
		} else {
			realpath_error = 0;
		}
	}

	if (sh)
		sharefree(sh);
	free(checkpath);
	return (reply_error);
}
Esempio n. 6
0
/*
 * find_lofsentry() searches for the real path which this requested LOFS path
 * (rpath) shadows. If found, it will return the sharetab entry of
 * the real path that corresponds to the LOFS path.
 * We first search mnttab to see if the requested path is an automounted
 * path. If it is an automounted path, it will trigger the mount by stat()ing
 * the requested path. Note that it is important to check that this path is
 * actually an automounted path, otherwise we would stat() a path which may
 * turn out to be NFS and block indefinitely on a dead server. The automounter
 * times-out if the server is dead, so there's no risk of hanging this
 * thread waiting for stat().
 * After the mount has been triggered (if necessary), we look for a
 * mountpoint of type LOFS (by searching /etc/mnttab again) which
 * is a substring of the rpath. If found, we construct a new path by
 * concatenating the mnt_special and the remaining of rpath, call findentry()
 * to make sure the 'real path' is shared.
 */
static struct share *
find_lofsentry(char *rpath, int *done_flag)
{
	struct stat r_stbuf;
	mntlist_t *ml, *mntl, *mntpnt = NULL;
	struct share *retcode = NULL;
	char tmp_path[MAXPATHLEN];
	int mntpnt_len = 0, tmp;
	char *p1, *p2;

	if ((*done_flag)++)
		return (retcode);

	/*
	 * While fsgetmntlist() uses lockf() to
	 * lock the mnttab before reading it in,
	 * the lock ignores threads in the same process.
	 * Read in the mnttab with the protection of a mutex.
	 */
	(void) mutex_lock(&mnttab_lock);
	mntl = fsgetmntlist();
	(void) mutex_unlock(&mnttab_lock);

	/*
	 * Obtain the mountpoint for the requested path.
	 */
	for (ml = mntl; ml; ml = ml->mntl_next) {
		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
			*p1 == *p2 && *p1; p1++, p2++);
		if (is_substring(&p1, &p2) &&
		    (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
			mntpnt = ml;
			mntpnt_len = tmp;
		}
	}

	/*
	 * If the path needs to be autoFS mounted, trigger the mount by
	 * stat()ing it. This is determined by checking whether the
	 * mountpoint we just found is of type autofs.
	 */
	if (mntpnt != NULL &&
	    strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
		/*
		 * The requested path is a substring of an autoFS filesystem.
		 * Trigger the mount.
		 */
		if (stat(rpath, &r_stbuf) < 0) {
			if (verbose)
				syslog(LOG_NOTICE, "%s: %m", rpath);
			goto done;
		}
		if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
			/*
			 * The requested path is a directory, stat(2) it
			 * again with a trailing '.' to force the autoFS
			 * module to trigger the mount of indirect
			 * automount entries, such as /net/jurassic/.
			 */
			if (strlen(rpath) + 2 > MAXPATHLEN) {
				if (verbose) {
					syslog(LOG_NOTICE,
						"%s/.: exceeds MAXPATHLEN %d",
						rpath, MAXPATHLEN);
				}
				goto done;
			}
			(void) strcpy(tmp_path, rpath);
			(void) strcat(tmp_path, "/.");

			if (stat(tmp_path, &r_stbuf) < 0) {
				if (verbose)
					syslog(LOG_NOTICE, "%s: %m", tmp_path);
				goto done;
			}
		}
		/*
		 * The mount has been triggered, re-read mnttab to pick up
		 * the changes made by autoFS.
		 */
		fsfreemntlist(mntl);
		(void) mutex_lock(&mnttab_lock);
		mntl = fsgetmntlist();
		(void) mutex_unlock(&mnttab_lock);
	}

	/*
	 * The autoFS mountpoint has been triggered if necessary,
	 * now search mnttab again to determine if the requested path
	 * is an LOFS mount of a shared path.
	 */
	mntpnt_len = 0;
	for (ml = mntl; ml; ml = ml->mntl_next) {
		if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
			continue;

		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
				*p1 == *p2 && *p1; p1++, p2++);

		if (is_substring(&p1, &p2) &&
		    ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
			mntpnt_len = tmp;

			if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
			    MAXPATHLEN) {
				if (verbose) {
					syslog(LOG_NOTICE, "%s%s: exceeds %d",
						ml->mntl_mnt->mnt_special, p2,
						MAXPATHLEN);
				}
				if (retcode)
					sharefree(retcode);
				retcode = NULL;
				goto done;
			}

			(void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
			(void) strcat(tmp_path, p2);
			if (retcode)
				sharefree(retcode);
			retcode = findentry(tmp_path);
		}
	}

	if (retcode) {
		assert(strlen(tmp_path) > 0);
		(void) strcpy(rpath, tmp_path);
	}

done:
	fsfreemntlist(mntl);
	return (retcode);
}
Esempio n. 7
0
static void
nfsauth_access(auth_req *argp, auth_res *result)
{
	struct netconfig *nconf;
	struct nd_hostservlist *clnames = NULL;
	struct netbuf nbuf;
	struct share *sh;
	char tmp[MAXIPADDRLEN];
	char *host = NULL;

	result->auth_perm = NFSAUTH_DENIED;

	/*
	 * Convert the client's address to a hostname
	 */
	nconf = getnetconfigent(argp->req_netid);
	if (nconf == NULL) {
		syslog(LOG_ERR, "No netconfig entry for %s", argp->req_netid);
		return;
	}

	nbuf.len = argp->req_client.n_len;
	nbuf.buf = argp->req_client.n_bytes;

	if (netdir_getbyaddr(nconf, &clnames, &nbuf)) {
		host = &tmp[0];
		if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
			struct sockaddr_in *sa;

			/* LINTED pointer alignment */
			sa = (struct sockaddr_in *)nbuf.buf;
			(void) inet_ntoa_r(sa->sin_addr, tmp);
		} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
			struct sockaddr_in6 *sa;
			/* LINTED pointer */
			sa = (struct sockaddr_in6 *)nbuf.buf;
			(void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr,
				    tmp, INET6_ADDRSTRLEN);
		}
		clnames = anon_client(host);
	}

	/*
	 * Now find the export
	 */
	sh = findentry(argp->req_path);
	if (sh == NULL) {
		syslog(LOG_ERR, "%s not exported", argp->req_path);
		goto done;
	}

	result->auth_perm = check_client(sh, &nbuf, clnames, argp->req_flavor);

	sharefree(sh);

	if (result->auth_perm == NFSAUTH_DENIED) {
		syslog(LOG_ERR, "%s denied access to %s",
			clnames->h_hostservs[0].h_host, argp->req_path);
	}

done:
	freenetconfigent(nconf);
	if (clnames)
		netdir_free(clnames, ND_HOSTSERVLIST);
}