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>
This commit is contained in:
parent
1d17de9534
commit
596afb0b89
@ -5603,6 +5603,170 @@ void send_flush_mdlog(struct ceph_mds_session *s)
|
||||
mutex_unlock(&s->s_mutex);
|
||||
}
|
||||
|
||||
static int ceph_mds_auth_match(struct ceph_mds_client *mdsc,
|
||||
struct ceph_mds_cap_auth *auth,
|
||||
char *tpath)
|
||||
{
|
||||
const struct cred *cred = get_current_cred();
|
||||
u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);
|
||||
u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);
|
||||
struct ceph_client *cl = mdsc->fsc->client;
|
||||
const char *spath = mdsc->fsc->mount_options->server_path;
|
||||
bool gid_matched = false;
|
||||
u32 gid, tlen, len;
|
||||
int i, j;
|
||||
|
||||
doutc(cl, "match.uid %lld\n", auth->match.uid);
|
||||
if (auth->match.uid != MDS_AUTH_UID_ANY) {
|
||||
if (auth->match.uid != caller_uid)
|
||||
return 0;
|
||||
if (auth->match.num_gids) {
|
||||
for (i = 0; i < auth->match.num_gids; i++) {
|
||||
if (caller_gid == auth->match.gids[i])
|
||||
gid_matched = true;
|
||||
}
|
||||
if (!gid_matched && cred->group_info->ngroups) {
|
||||
for (i = 0; i < cred->group_info->ngroups; i++) {
|
||||
gid = from_kgid(&init_user_ns,
|
||||
cred->group_info->gid[i]);
|
||||
for (j = 0; j < auth->match.num_gids; j++) {
|
||||
if (gid == auth->match.gids[j]) {
|
||||
gid_matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gid_matched)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!gid_matched)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* path match */
|
||||
if (auth->match.path) {
|
||||
if (!tpath)
|
||||
return 0;
|
||||
|
||||
tlen = strlen(tpath);
|
||||
len = strlen(auth->match.path);
|
||||
if (len) {
|
||||
char *_tpath = tpath;
|
||||
bool free_tpath = false;
|
||||
int m, n;
|
||||
|
||||
doutc(cl, "server path %s, tpath %s, match.path %s\n",
|
||||
spath, tpath, auth->match.path);
|
||||
if (spath && (m = strlen(spath)) != 1) {
|
||||
/* mount path + '/' + tpath + an extra space */
|
||||
n = m + 1 + tlen + 1;
|
||||
_tpath = kmalloc(n, GFP_NOFS);
|
||||
if (!_tpath)
|
||||
return -ENOMEM;
|
||||
/* remove the leading '/' */
|
||||
snprintf(_tpath, n, "%s/%s", spath + 1, tpath);
|
||||
free_tpath = true;
|
||||
tlen = strlen(_tpath);
|
||||
}
|
||||
|
||||
/*
|
||||
* Please note the tailing '/' for match.path has already
|
||||
* been removed when parsing.
|
||||
*
|
||||
* Remove the tailing '/' for the target path.
|
||||
*/
|
||||
while (tlen && _tpath[tlen - 1] == '/') {
|
||||
_tpath[tlen - 1] = '\0';
|
||||
tlen -= 1;
|
||||
}
|
||||
doutc(cl, "_tpath %s\n", _tpath);
|
||||
|
||||
/*
|
||||
* In case first == _tpath && tlen == len:
|
||||
* match.path=/foo --> /foo _path=/foo --> match
|
||||
* match.path=/foo/ --> /foo _path=/foo --> match
|
||||
*
|
||||
* In case first == _tmatch.path && tlen > len:
|
||||
* match.path=/foo/ --> /foo _path=/foo/ --> match
|
||||
* match.path=/foo --> /foo _path=/foo/ --> match
|
||||
* match.path=/foo/ --> /foo _path=/foo/d --> match
|
||||
* match.path=/foo --> /foo _path=/food --> mismatch
|
||||
*
|
||||
* All the other cases --> mismatch
|
||||
*/
|
||||
char *first = strstr(_tpath, auth->match.path);
|
||||
if (first != _tpath) {
|
||||
if (free_tpath)
|
||||
kfree(_tpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tlen > len && _tpath[len] != '/') {
|
||||
if (free_tpath)
|
||||
kfree(_tpath);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doutc(cl, "matched\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
|
||||
{
|
||||
const struct cred *cred = get_current_cred();
|
||||
u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);
|
||||
u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);
|
||||
struct ceph_mds_cap_auth *rw_perms_s = NULL;
|
||||
struct ceph_client *cl = mdsc->fsc->client;
|
||||
bool root_squash_perms = true;
|
||||
int i, err;
|
||||
|
||||
doutc(cl, "tpath '%s', mask %d, caller_uid %d, caller_gid %d\n",
|
||||
tpath, mask, caller_uid, caller_gid);
|
||||
|
||||
for (i = 0; i < mdsc->s_cap_auths_num; i++) {
|
||||
struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i];
|
||||
|
||||
err = ceph_mds_auth_match(mdsc, s, tpath);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
} else if (err > 0) {
|
||||
/* always follow the last auth caps' permision */
|
||||
root_squash_perms = true;
|
||||
rw_perms_s = NULL;
|
||||
if ((mask & MAY_WRITE) && s->writeable &&
|
||||
s->match.root_squash && (!caller_uid || !caller_gid))
|
||||
root_squash_perms = false;
|
||||
|
||||
if (((mask & MAY_WRITE) && !s->writeable) ||
|
||||
((mask & MAY_READ) && !s->readable))
|
||||
rw_perms_s = s;
|
||||
}
|
||||
}
|
||||
|
||||
doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms,
|
||||
rw_perms_s);
|
||||
if (root_squash_perms && rw_perms_s == NULL) {
|
||||
doutc(cl, "access allowed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!root_squash_perms) {
|
||||
doutc(cl, "root_squash is enabled and user(%d %d) isn't allowed to write",
|
||||
caller_uid, caller_gid);
|
||||
}
|
||||
if (rw_perms_s) {
|
||||
doutc(cl, "mds auth caps readable/writeable %d/%d while request r/w %d/%d",
|
||||
rw_perms_s->readable, rw_perms_s->writeable,
|
||||
!!(mask & MAY_READ), !!(mask & MAY_WRITE));
|
||||
}
|
||||
doutc(cl, "access denied\n");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* called before mount is ro, and before dentries are torn down.
|
||||
* (hmm, does this still race with new lookups?)
|
||||
|
@ -602,6 +602,9 @@ extern void ceph_queue_cap_unlink_work(struct ceph_mds_client *mdsc);
|
||||
extern int ceph_iterate_session_caps(struct ceph_mds_session *session,
|
||||
int (*cb)(struct inode *, int mds, void *),
|
||||
void *arg);
|
||||
extern int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath,
|
||||
int mask);
|
||||
|
||||
extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc);
|
||||
|
||||
static inline void ceph_mdsc_free_path(char *path, int len)
|
||||
|
Loading…
Reference in New Issue
Block a user