From e304f48fa262e5cdbe181fb3fee5dfb9c893108c Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 6 Jul 2017 17:04:17 +0200 Subject: 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 Reviewed-on: https://review.gluster.org/17718 Smoke: Gluster Build System Reviewed-by: soumya k CentOS-regression: Gluster Build System Reviewed-by: Kaleb KEITHLEY --- xlators/nfs/server/src/mount3.c | 60 +++++++++++++++++++++----------- xlators/nfs/server/src/mount3.h | 7 ++++ xlators/nfs/server/src/nfs-common.c | 25 ++++++++------ xlators/nfs/server/src/nfs3.c | 69 +++++++++++++++++++++---------------- 4 files changed, 99 insertions(+), 62 deletions(-) (limited to 'xlators') diff --git a/xlators/nfs/server/src/mount3.c b/xlators/nfs/server/src/mount3.c index 12f52e4f5d0..2eaaea5c639 100644 --- a/xlators/nfs/server/src/mount3.c +++ b/xlators/nfs/server/src/mount3.c @@ -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); } diff --git a/xlators/nfs/server/src/mount3.h b/xlators/nfs/server/src/mount3.h index ce01a6c543d..8868aec787a 100644 --- a/xlators/nfs/server/src/mount3.h +++ b/xlators/nfs/server/src/mount3.h @@ -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 diff --git a/xlators/nfs/server/src/nfs-common.c b/xlators/nfs/server/src/nfs-common.c index 526918872d7..2f742d44366 100644 --- a/xlators/nfs/server/src/nfs-common.c +++ b/xlators/nfs/server/src/nfs-common.c @@ -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); + + volname = volptr = gf_strdup (path); + if (!volname) + return NULL; + if (volname[0] == '/') - volptr = &volname[1]; - else - volptr = &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; } diff --git a/xlators/nfs/server/src/nfs3.c b/xlators/nfs/server/src/nfs3.c index 7da412d0a35..836428f8111 100644 --- a/xlators/nfs/server/src/nfs3.c +++ b/xlators/nfs/server/src/nfs3.c @@ -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); -- cgit