nfs: add permission checking for mounting over WebNFS
Solaris 10 uses WebNFS and not the MOUNT protocol. All permission checks for allowing/denying clients to mount are done through the MNT handlers. These handlers will not give out a filehandle to the NFS-client when mounting is denied. This prevents clients from successful mounting. However, over WebNFS a well known 'root-filehandle' is used directly with the NFSv3 protocol. When WebNFS was used, no permission checks (the "nfs.export-dir" option) were applied. Now the WebNFS mount-handler in Gluster/NFS calls the mnt3_parse_dir_exports() function that takes care of the permission checking. BUG: 1468291 Change-Id: Ic9dfd092473ba9c1c7b5fa38401cf9c0aa8395bb Signed-off-by: Niels de Vos <ndevos@redhat.com> Reviewed-on: https://review.gluster.org/17718 Smoke: Gluster Build System <jenkins@build.gluster.org> Reviewed-by: soumya k <skoduri@redhat.com> CentOS-regression: Gluster Build System <jenkins@build.gluster.org> Reviewed-by: Kaleb KEITHLEY <kkeithle@redhat.com>
This commit is contained in:
parent
b81997264f
commit
e304f48fa2
@ -945,7 +945,7 @@ err:
|
||||
* we need to strip out the volume name first.
|
||||
*/
|
||||
char *
|
||||
__volume_subdir (char *dirpath, char **volname)
|
||||
mnt3_get_volume_subdir (char *dirpath, char **volname)
|
||||
{
|
||||
char *subdir = NULL;
|
||||
int volname_len = 0;
|
||||
@ -1024,10 +1024,6 @@ mnt3_resolve_subdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
|
||||
struct iatt *buf, dict_t *xattr,
|
||||
struct iatt *postparent);
|
||||
|
||||
int
|
||||
mnt3_parse_dir_exports (rpcsvc_request_t *req, struct mount3_state *ms,
|
||||
char *subdir);
|
||||
|
||||
int32_t
|
||||
mnt3_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
|
||||
int32_t op_ret, int32_t op_errno, const char *path,
|
||||
@ -1310,7 +1306,8 @@ mnt3_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
|
||||
/* After the resolving the symlink , parsing should be done
|
||||
* for the populated mount path
|
||||
*/
|
||||
ret = mnt3_parse_dir_exports (mres->req, mres->mstate, real_loc);
|
||||
ret = mnt3_parse_dir_exports (mres->req, mres->mstate, real_loc,
|
||||
_gf_true);
|
||||
|
||||
if (ret) {
|
||||
gf_msg (GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_RESOLVE_ERROR,
|
||||
@ -1518,7 +1515,8 @@ mnt3_verify_auth (struct sockaddr_in *client_addr, struct mnt3_export *export)
|
||||
|
||||
int
|
||||
mnt3_resolve_subdir (rpcsvc_request_t *req, struct mount3_state *ms,
|
||||
struct mnt3_export *exp, char *subdir)
|
||||
struct mnt3_export *exp, char *subdir,
|
||||
gf_boolean_t send_reply)
|
||||
{
|
||||
mnt3_resolve_t *mres = NULL;
|
||||
int ret = -EFAULT;
|
||||
@ -1541,6 +1539,10 @@ mnt3_resolve_subdir (rpcsvc_request_t *req, struct mount3_state *ms,
|
||||
}
|
||||
}
|
||||
|
||||
/* no reply is needed (WebNFS permissions checking), just return */
|
||||
if (!send_reply)
|
||||
return 0; /* no error, mnt3_verify_auth() allowed it */
|
||||
|
||||
mres = GF_CALLOC (1, sizeof (mnt3_resolve_t), gf_nfs_mt_mnt3_resolve);
|
||||
if (!mres) {
|
||||
gf_msg (GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
|
||||
@ -1585,11 +1587,11 @@ mnt3_resolve_export_subdir (rpcsvc_request_t *req, struct mount3_state *ms,
|
||||
if ((!req) || (!ms) || (!exp))
|
||||
return ret;
|
||||
|
||||
volume_subdir = __volume_subdir (exp->expname, NULL);
|
||||
volume_subdir = mnt3_get_volume_subdir (exp->expname, NULL);
|
||||
if (!volume_subdir)
|
||||
goto err;
|
||||
|
||||
ret = mnt3_resolve_subdir (req, ms, exp, volume_subdir);
|
||||
ret = mnt3_resolve_subdir (req, ms, exp, volume_subdir, _gf_true);
|
||||
if (ret < 0) {
|
||||
gf_msg (GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_RESOLVE_SUBDIR_FAIL,
|
||||
"Failed to resolve export dir: %s", exp->expname);
|
||||
@ -1686,7 +1688,7 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
mnt3_check_client_net_tcp (rpcsvc_request_t *req, char *volname)
|
||||
{
|
||||
rpcsvc_t *svc = NULL;
|
||||
@ -1772,25 +1774,41 @@ err:
|
||||
|
||||
int
|
||||
mnt3_parse_dir_exports (rpcsvc_request_t *req, struct mount3_state *ms,
|
||||
char *subdir)
|
||||
char *path, gf_boolean_t send_reply)
|
||||
{
|
||||
char volname[1024] = {0, };
|
||||
struct mnt3_export *exp = NULL;
|
||||
char *volname_ptr = NULL;
|
||||
char *subdir = NULL;
|
||||
int ret = -ENOENT;
|
||||
struct nfs_state *nfs = NULL;
|
||||
|
||||
if ((!ms) || (!subdir))
|
||||
if ((!ms) || (!path))
|
||||
return -1;
|
||||
|
||||
volname_ptr = volname;
|
||||
subdir = __volume_subdir (subdir, &volname_ptr);
|
||||
if (!subdir)
|
||||
subdir = mnt3_get_volume_subdir (path, &volname_ptr);
|
||||
if (!subdir) {
|
||||
gf_msg_trace (GF_MNT, 0, "Could not parse volname/subdir from "
|
||||
"%s", path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
exp = mnt3_mntpath_to_export (ms, volname, _gf_false);
|
||||
if (!exp)
|
||||
goto err;
|
||||
/* first try to match the full export/subdir */
|
||||
exp = mnt3_mntpath_to_export (ms, path, _gf_false);
|
||||
if (!exp) {
|
||||
gf_msg_trace (GF_MNT, 0, "Could not find exact matching export "
|
||||
"for path=%s", path);
|
||||
/* if no exact match is found, look for a fallback */
|
||||
exp = mnt3_mntpath_to_export (ms, volname, _gf_true);
|
||||
if (!exp) {
|
||||
gf_msg_trace (GF_MNT, 0, "Could not find export for "
|
||||
"volume %s", volname);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
gf_msg_trace (GF_MNT, 0, "volume %s and export %s will be used for "
|
||||
"path %s", exp->vol->name, exp->expname, path);
|
||||
|
||||
nfs = (struct nfs_state *)ms->nfsx->private;
|
||||
if (!nfs)
|
||||
@ -1809,7 +1827,7 @@ mnt3_parse_dir_exports (rpcsvc_request_t *req, struct mount3_state *ms,
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = mnt3_resolve_subdir (req, ms, exp, subdir);
|
||||
ret = mnt3_resolve_subdir (req, ms, exp, subdir, send_reply);
|
||||
if (ret < 0) {
|
||||
gf_msg (GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_RESOLVE_SUBDIR_FAIL,
|
||||
"Failed to resolve export dir: %s", subdir);
|
||||
@ -1852,7 +1870,7 @@ mnt3_find_export (rpcsvc_request_t *req, char *path, struct mnt3_export **e)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = mnt3_parse_dir_exports (req, ms, path);
|
||||
ret = mnt3_parse_dir_exports (req, ms, path, _gf_true);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
@ -2939,7 +2957,7 @@ nfs3_rootfh (struct svc_req *req, xlator_t *nfsx,
|
||||
* 3. If a subdir is exported using nfs.export-dir,
|
||||
* then the mount type would be MNT3_EXPTYPE_DIR,
|
||||
* so make sure to find the proper path to be
|
||||
* resolved using __volume_subdir()
|
||||
* resolved using mnt3_get_volume_subdir()
|
||||
* 3. Make sure subdir export is allowed.
|
||||
*/
|
||||
ms = __mnt3udp_get_mstate(nfsx);
|
||||
@ -2962,7 +2980,7 @@ nfs3_rootfh (struct svc_req *req, xlator_t *nfsx,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = __volume_subdir (path, &volptr);
|
||||
path = mnt3_get_volume_subdir (path, &volptr);
|
||||
if (exp == NULL)
|
||||
exp = mnt3_mntpath_to_export (ms, volname , _gf_false);
|
||||
}
|
||||
|
@ -178,4 +178,11 @@ struct mount3_resolve_state {
|
||||
|
||||
typedef struct mount3_resolve_state mnt3_resolve_t;
|
||||
|
||||
int
|
||||
mnt3_parse_dir_exports (rpcsvc_request_t *req, struct mount3_state *ms,
|
||||
char *subdir, gf_boolean_t send_reply);
|
||||
|
||||
char*
|
||||
mnt3_get_volume_subdir (char *path, char **volname);
|
||||
|
||||
#endif
|
||||
|
@ -73,8 +73,8 @@ nfs_xlator_to_xlid (xlator_list_t *cl, xlator_t *xl)
|
||||
xlator_t *
|
||||
nfs_mntpath_to_xlator (xlator_list_t *cl, char *path)
|
||||
{
|
||||
char *volname = NULL;
|
||||
char *volptr = NULL;
|
||||
char *volname = NULL; /* volume name only */
|
||||
char *volptr = NULL; /* ptr to original volname */
|
||||
size_t pathlen = -1;
|
||||
xlator_t *targetxl = NULL;
|
||||
int i = 0;
|
||||
@ -82,14 +82,16 @@ nfs_mntpath_to_xlator (xlator_list_t *cl, char *path)
|
||||
if ((!cl) || (!path))
|
||||
return NULL;
|
||||
|
||||
volname = strdupa (path);
|
||||
pathlen = strlen (volname);
|
||||
gf_msg_trace (GF_NFS, 0, "Subvolume search: %s", path);
|
||||
if (volname[0] == '/')
|
||||
volptr = &volname[1];
|
||||
else
|
||||
volptr = &volname[0];
|
||||
|
||||
volname = volptr = gf_strdup (path);
|
||||
if (!volname)
|
||||
return NULL;
|
||||
|
||||
if (volname[0] == '/')
|
||||
volname++;
|
||||
|
||||
pathlen = strlen (volname);
|
||||
for (i = 0; i < pathlen; i++) {
|
||||
if (volname[i] == '/') {
|
||||
volname[i] = '\0';
|
||||
@ -98,10 +100,10 @@ nfs_mntpath_to_xlator (xlator_list_t *cl, char *path)
|
||||
}
|
||||
|
||||
while (cl) {
|
||||
gf_msg_trace (GF_NFS, 0, "Volptr: %s and cl->xlator->name: %s",
|
||||
volptr, cl->xlator->name);
|
||||
gf_msg_trace (GF_NFS, 0, "Volname: %s and cl->xlator->name: %s",
|
||||
volname, cl->xlator->name);
|
||||
|
||||
if (strcmp (volptr, cl->xlator->name) == 0) {
|
||||
if (strcmp (volname, cl->xlator->name) == 0) {
|
||||
targetxl = cl->xlator;
|
||||
break;
|
||||
}
|
||||
@ -109,8 +111,9 @@ nfs_mntpath_to_xlator (xlator_list_t *cl, char *path)
|
||||
cl = cl->next;
|
||||
}
|
||||
|
||||
return targetxl;
|
||||
GF_FREE (volptr);
|
||||
|
||||
return targetxl;
|
||||
}
|
||||
|
||||
|
||||
|
@ -349,44 +349,51 @@ out:
|
||||
}
|
||||
|
||||
|
||||
static enum nfsstat3
|
||||
nfs3_funge_webnfs_zerolen_fh (struct nfs3_state *nfs3st, struct nfs3_fh *fhd,
|
||||
char *name)
|
||||
static int
|
||||
nfs3_funge_webnfs_zerolen_fh (rpcsvc_request_t *req, struct nfs3_state *nfs3st,
|
||||
struct nfs3_fh *fhd, char *name)
|
||||
{
|
||||
xlator_t *fungexl = NULL;
|
||||
glfs_t *fs = NULL;
|
||||
loc_t loc = { 0, };
|
||||
enum nfsstat3 nfsstat = NFS3ERR_SERVERFAULT;
|
||||
int ret = -1;
|
||||
size_t namelen = -1;
|
||||
xlator_t *fungexl = NULL;
|
||||
struct nfs_state *nfs = NULL;
|
||||
glfs_t *fs = NULL;
|
||||
loc_t loc = { 0, };
|
||||
int ret = -1;
|
||||
char *subdir = NULL;
|
||||
char volname[NAME_MAX] = { 0, };
|
||||
|
||||
fungexl = nfs_mntpath_to_xlator (nfs3st->exportslist, name);
|
||||
if (!fungexl) {
|
||||
nfsstat = NFS3ERR_NOENT;
|
||||
gf_msg_trace (GF_NFS3, 0, "failed to find xlator for volume");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
/* fungexl is valid, set for nfs3_request_xlator_deviceid() */
|
||||
rpcsvc_request_set_private (req, fungexl);
|
||||
|
||||
/* Permission checks are done through mnt3_parse_dir_exports(). The
|
||||
* "nfs.export-dir" option gets checked as well. */
|
||||
nfs = nfs_state (nfs3st->nfsx);
|
||||
ret = mnt3_parse_dir_exports (req, nfs->mstate, name, _gf_false);
|
||||
if (ret) {
|
||||
gf_msg_trace (GF_NFS3, -ret, "mounting not possible");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* glfs_resolve_at copied from UDP MNT support */
|
||||
fs = glfs_new_from_ctx (fungexl->ctx);
|
||||
if (!fs) {
|
||||
nfsstat = NFS3ERR_NOENT;
|
||||
gf_msg_trace (GF_NFS3, 0, "failed to create glfs instance");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* strip volname/ from 'name' */
|
||||
namelen = strlen(name);
|
||||
while (namelen != 0) {
|
||||
name++;
|
||||
if (name[0] == '/') {
|
||||
break;
|
||||
}
|
||||
namelen--;
|
||||
}
|
||||
gf_msg_debug (GF_NFS, 0, "NAME :%s ", name);
|
||||
/* split name "volname/sub/dir/s" into pieces */
|
||||
subdir = mnt3_get_volume_subdir (name, (char**) &volname);
|
||||
|
||||
ret = glfs_resolve_at (fs, fungexl, NULL, name, &loc, NULL, 1, 0);
|
||||
ret = glfs_resolve_at (fs, fungexl, NULL, subdir, &loc, NULL, 1, 0);
|
||||
if (ret != 0) {
|
||||
nfsstat = NFS3ERR_NOENT;
|
||||
gf_msg_trace (GF_NFS3, 0, "failed to resolve %s", subdir);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -399,17 +406,17 @@ nfs3_funge_webnfs_zerolen_fh (struct nfs3_state *nfs3st, struct nfs3_fh *fhd,
|
||||
fungexl);
|
||||
else {
|
||||
if (__nfs3_get_volume_id (nfs3st, fungexl, fhd->exportid) < 0) {
|
||||
nfsstat = NFS3ERR_STALE;
|
||||
ret = -ESTALE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
nfsstat = NFS3_OK;
|
||||
ret = 0;
|
||||
out:
|
||||
if (fs)
|
||||
glfs_free_from_ctx (fs);
|
||||
|
||||
return nfsstat;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -513,8 +520,10 @@ nfs3_solaris_zerolen_fh (struct nfs3_fh *fh, int fhlen)
|
||||
if (nfs3_fh_validate (fh))
|
||||
return 0;
|
||||
|
||||
if (fhlen == 0)
|
||||
if (fhlen == 0) {
|
||||
gf_msg_trace (GF_NFS3, 0, "received WebNFS request");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1593,8 +1602,8 @@ nfs3_lookup (rpcsvc_request_t *req, struct nfs3_fh *fh, int fhlen, char *name)
|
||||
name);
|
||||
nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret);
|
||||
if (nfs3_solaris_zerolen_fh (fh, fhlen)) {
|
||||
stat = nfs3_funge_webnfs_zerolen_fh (nfs3, fh, name);
|
||||
if (stat != NFS3_OK)
|
||||
ret = nfs3_funge_webnfs_zerolen_fh (req, nfs3, fh, name);
|
||||
if (ret < 0)
|
||||
goto nfs3err;
|
||||
|
||||
/* this fh means we're doing a mount, name is no more useful */
|
||||
@ -1614,11 +1623,11 @@ nfs3_lookup (rpcsvc_request_t *req, struct nfs3_fh *fh, int fhlen, char *name)
|
||||
gf_msg (GF_NFS, GF_LOG_ERROR, -ret,
|
||||
NFS_MSG_HARD_RESOLVE_FAIL,
|
||||
"failed to start hard resolve");
|
||||
stat = nfs3_errno_to_nfsstat3 (-ret);
|
||||
}
|
||||
|
||||
nfs3err:
|
||||
if (ret < 0) {
|
||||
stat = nfs3_errno_to_nfsstat3 (-ret);
|
||||
nfs3_log_common_res (rpcsvc_request_xid (req),
|
||||
NFS3_LOOKUP, stat, -ret,
|
||||
cs ? cs->resolvedloc.path : NULL);
|
||||
|
Loading…
x
Reference in New Issue
Block a user