Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 596afb0

Browse files
lxbszidryomov
authored andcommitted
ceph: add ceph_mds_check_access() helper
This will help check the mds auth access in client side. Always insert the server path in front of the target path when matching the paths. [ idryomov: use u32 instead of uint32_t ] Link: https://tracker.ceph.com/issues/61333 Signed-off-by: Xiubo Li <xiubli@redhat.com> Reviewed-by: Milind Changire <mchangir@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
1 parent 1d17de9 commit 596afb0

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

fs/ceph/mds_client.c

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5603,6 +5603,170 @@ void send_flush_mdlog(struct ceph_mds_session *s)
56035603
mutex_unlock(&s->s_mutex);
56045604
}
56055605

5606+
static int ceph_mds_auth_match(struct ceph_mds_client *mdsc,
5607+
struct ceph_mds_cap_auth *auth,
5608+
char *tpath)
5609+
{
5610+
const struct cred *cred = get_current_cred();
5611+
u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);
5612+
u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);
5613+
struct ceph_client *cl = mdsc->fsc->client;
5614+
const char *spath = mdsc->fsc->mount_options->server_path;
5615+
bool gid_matched = false;
5616+
u32 gid, tlen, len;
5617+
int i, j;
5618+
5619+
doutc(cl, "match.uid %lld\n", auth->match.uid);
5620+
if (auth->match.uid != MDS_AUTH_UID_ANY) {
5621+
if (auth->match.uid != caller_uid)
5622+
return 0;
5623+
if (auth->match.num_gids) {
5624+
for (i = 0; i < auth->match.num_gids; i++) {
5625+
if (caller_gid == auth->match.gids[i])
5626+
gid_matched = true;
5627+
}
5628+
if (!gid_matched && cred->group_info->ngroups) {
5629+
for (i = 0; i < cred->group_info->ngroups; i++) {
5630+
gid = from_kgid(&init_user_ns,
5631+
cred->group_info->gid[i]);
5632+
for (j = 0; j < auth->match.num_gids; j++) {
5633+
if (gid == auth->match.gids[j]) {
5634+
gid_matched = true;
5635+
break;
5636+
}
5637+
}
5638+
if (gid_matched)
5639+
break;
5640+
}
5641+
}
5642+
if (!gid_matched)
5643+
return 0;
5644+
}
5645+
}
5646+
5647+
/* path match */
5648+
if (auth->match.path) {
5649+
if (!tpath)
5650+
return 0;
5651+
5652+
tlen = strlen(tpath);
5653+
len = strlen(auth->match.path);
5654+
if (len) {
5655+
char *_tpath = tpath;
5656+
bool free_tpath = false;
5657+
int m, n;
5658+
5659+
doutc(cl, "server path %s, tpath %s, match.path %s\n",
5660+
spath, tpath, auth->match.path);
5661+
if (spath && (m = strlen(spath)) != 1) {
5662+
/* mount path + '/' + tpath + an extra space */
5663+
n = m + 1 + tlen + 1;
5664+
_tpath = kmalloc(n, GFP_NOFS);
5665+
if (!_tpath)
5666+
return -ENOMEM;
5667+
/* remove the leading '/' */
5668+
snprintf(_tpath, n, "%s/%s", spath + 1, tpath);
5669+
free_tpath = true;
5670+
tlen = strlen(_tpath);
5671+
}
5672+
5673+
/*
5674+
* Please note the tailing '/' for match.path has already
5675+
* been removed when parsing.
5676+
*
5677+
* Remove the tailing '/' for the target path.
5678+
*/
5679+
while (tlen && _tpath[tlen - 1] == '/') {
5680+
_tpath[tlen - 1] = '\0';
5681+
tlen -= 1;
5682+
}
5683+
doutc(cl, "_tpath %s\n", _tpath);
5684+
5685+
/*
5686+
* In case first == _tpath && tlen == len:
5687+
* match.path=/foo --> /foo _path=/foo --> match
5688+
* match.path=/foo/ --> /foo _path=/foo --> match
5689+
*
5690+
* In case first == _tmatch.path && tlen > len:
5691+
* match.path=/foo/ --> /foo _path=/foo/ --> match
5692+
* match.path=/foo --> /foo _path=/foo/ --> match
5693+
* match.path=/foo/ --> /foo _path=/foo/d --> match
5694+
* match.path=/foo --> /foo _path=/food --> mismatch
5695+
*
5696+
* All the other cases --> mismatch
5697+
*/
5698+
char *first = strstr(_tpath, auth->match.path);
5699+
if (first != _tpath) {
5700+
if (free_tpath)
5701+
kfree(_tpath);
5702+
return 0;
5703+
}
5704+
5705+
if (tlen > len && _tpath[len] != '/') {
5706+
if (free_tpath)
5707+
kfree(_tpath);
5708+
return 0;
5709+
}
5710+
}
5711+
}
5712+
5713+
doutc(cl, "matched\n");
5714+
return 1;
5715+
}
5716+
5717+
int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
5718+
{
5719+
const struct cred *cred = get_current_cred();
5720+
u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);
5721+
u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);
5722+
struct ceph_mds_cap_auth *rw_perms_s = NULL;
5723+
struct ceph_client *cl = mdsc->fsc->client;
5724+
bool root_squash_perms = true;
5725+
int i, err;
5726+
5727+
doutc(cl, "tpath '%s', mask %d, caller_uid %d, caller_gid %d\n",
5728+
tpath, mask, caller_uid, caller_gid);
5729+
5730+
for (i = 0; i < mdsc->s_cap_auths_num; i++) {
5731+
struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i];
5732+
5733+
err = ceph_mds_auth_match(mdsc, s, tpath);
5734+
if (err < 0) {
5735+
return err;
5736+
} else if (err > 0) {
5737+
/* always follow the last auth caps' permision */
5738+
root_squash_perms = true;
5739+
rw_perms_s = NULL;
5740+
if ((mask & MAY_WRITE) && s->writeable &&
5741+
s->match.root_squash && (!caller_uid || !caller_gid))
5742+
root_squash_perms = false;
5743+
5744+
if (((mask & MAY_WRITE) && !s->writeable) ||
5745+
((mask & MAY_READ) && !s->readable))
5746+
rw_perms_s = s;
5747+
}
5748+
}
5749+
5750+
doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms,
5751+
rw_perms_s);
5752+
if (root_squash_perms && rw_perms_s == NULL) {
5753+
doutc(cl, "access allowed\n");
5754+
return 0;
5755+
}
5756+
5757+
if (!root_squash_perms) {
5758+
doutc(cl, "root_squash is enabled and user(%d %d) isn't allowed to write",
5759+
caller_uid, caller_gid);
5760+
}
5761+
if (rw_perms_s) {
5762+
doutc(cl, "mds auth caps readable/writeable %d/%d while request r/w %d/%d",
5763+
rw_perms_s->readable, rw_perms_s->writeable,
5764+
!!(mask & MAY_READ), !!(mask & MAY_WRITE));
5765+
}
5766+
doutc(cl, "access denied\n");
5767+
return -EACCES;
5768+
}
5769+
56065770
/*
56075771
* called before mount is ro, and before dentries are torn down.
56085772
* (hmm, does this still race with new lookups?)

fs/ceph/mds_client.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,9 @@ extern void ceph_queue_cap_unlink_work(struct ceph_mds_client *mdsc);
602602
extern int ceph_iterate_session_caps(struct ceph_mds_session *session,
603603
int (*cb)(struct inode *, int mds, void *),
604604
void *arg);
605+
extern int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath,
606+
int mask);
607+
605608
extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc);
606609

607610
static inline void ceph_mdsc_free_path(char *path, int len)

0 commit comments

Comments
 (0)