ceph: fix NULL pointer dereference in ceph_mds_auth_match()
The CephFS kernel client has regression starting from 6.18-rc1.
We have issue in ceph_mds_auth_match() if fs_name == NULL:
const char fs_name = mdsc->fsc->mount_options->mds_namespace;
...
if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) {
/ fsname mismatch, try next one */
return 0;
}
Patrick Donnelly suggested that: In summary, we should definitely start
decoding `fs_name` from the MDSMap and do strict authorizations checks
against it. Note that the `-o mds_namespace=foo` should only be used for
selecting the file system to mount and nothing else. It's possible
no mds_namespace is specified but the kernel will mount the only
file system that exists which may have name "foo".
This patch reworks ceph_mdsmap_decode() and namespace_equals() with
the goal of supporting the suggested concept. Now struct ceph_mdsmap
contains m_fs_name field that receives copy of extracted FS name
by ceph_extract_encoded_string(). For the case of "old" CephFS file
systems, it is used "cephfs" name.
[ idryomov: replace redundant %*pE with %s in ceph_mdsmap_decode(),
get rid of a series of strlen() calls in ceph_namespace_match(),
drop changes to namespace_equals() body to avoid treating empty
mds_namespace as equal, drop changes to ceph_mdsc_handle_fsmap()
as namespace_equals() isn't an equivalent substitution there ]
Cc: stable@vger.kernel.org
Fixes: 22c73d52a6 ("ceph: fix multifs mds auth caps issue")
Link: https://tracker.ceph.com/issues/73886
Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Reviewed-by: Patrick Donnelly <pdonnell@ibm.com>
Tested-by: Patrick Donnelly <pdonnell@ibm.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
master
parent
bc8dedae02
commit
7987cce375
|
|
@ -5671,7 +5671,7 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc,
|
|||
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 *fs_name = mdsc->fsc->mount_options->mds_namespace;
|
||||
const char *fs_name = mdsc->mdsmap->m_fs_name;
|
||||
const char *spath = mdsc->fsc->mount_options->server_path;
|
||||
bool gid_matched = false;
|
||||
u32 gid, tlen, len;
|
||||
|
|
@ -5679,7 +5679,8 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc,
|
|||
|
||||
doutc(cl, "fsname check fs_name=%s match.fs_name=%s\n",
|
||||
fs_name, auth->match.fs_name ? auth->match.fs_name : "");
|
||||
if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) {
|
||||
|
||||
if (!ceph_namespace_match(auth->match.fs_name, fs_name)) {
|
||||
/* fsname mismatch, try next one */
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,22 +353,33 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
|
|||
__decode_and_drop_type(p, end, u8, bad_ext);
|
||||
}
|
||||
if (mdsmap_ev >= 8) {
|
||||
u32 fsname_len;
|
||||
size_t fsname_len;
|
||||
|
||||
/* enabled */
|
||||
ceph_decode_8_safe(p, end, m->m_enabled, bad_ext);
|
||||
|
||||
/* fs_name */
|
||||
ceph_decode_32_safe(p, end, fsname_len, bad_ext);
|
||||
m->m_fs_name = ceph_extract_encoded_string(p, end,
|
||||
&fsname_len,
|
||||
GFP_NOFS);
|
||||
if (IS_ERR(m->m_fs_name)) {
|
||||
m->m_fs_name = NULL;
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
/* validate fsname against mds_namespace */
|
||||
if (!namespace_equals(mdsc->fsc->mount_options, *p,
|
||||
if (!namespace_equals(mdsc->fsc->mount_options, m->m_fs_name,
|
||||
fsname_len)) {
|
||||
pr_warn_client(cl, "fsname %*pE doesn't match mds_namespace %s\n",
|
||||
(int)fsname_len, (char *)*p,
|
||||
pr_warn_client(cl, "fsname %s doesn't match mds_namespace %s\n",
|
||||
m->m_fs_name,
|
||||
mdsc->fsc->mount_options->mds_namespace);
|
||||
goto bad;
|
||||
}
|
||||
/* skip fsname after validation */
|
||||
ceph_decode_skip_n(p, end, fsname_len, bad);
|
||||
} else {
|
||||
m->m_enabled = false;
|
||||
m->m_fs_name = kstrdup(CEPH_OLD_FS_NAME, GFP_NOFS);
|
||||
if (!m->m_fs_name)
|
||||
goto nomem;
|
||||
}
|
||||
/* damaged */
|
||||
if (mdsmap_ev >= 9) {
|
||||
|
|
@ -430,6 +441,7 @@ void ceph_mdsmap_destroy(struct ceph_mdsmap *m)
|
|||
kfree(m->m_info);
|
||||
}
|
||||
kfree(m->m_data_pg_pools);
|
||||
kfree(m->m_fs_name);
|
||||
kfree(m);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ struct ceph_mdsmap {
|
|||
bool m_enabled;
|
||||
bool m_damaged;
|
||||
int m_num_laggy;
|
||||
char *m_fs_name;
|
||||
};
|
||||
|
||||
static inline struct ceph_entity_addr *
|
||||
|
|
|
|||
|
|
@ -104,14 +104,26 @@ struct ceph_mount_options {
|
|||
struct fscrypt_dummy_policy dummy_enc_policy;
|
||||
};
|
||||
|
||||
#define CEPH_NAMESPACE_WILDCARD "*"
|
||||
|
||||
static inline bool ceph_namespace_match(const char *pattern,
|
||||
const char *target)
|
||||
{
|
||||
if (!pattern || !pattern[0] ||
|
||||
!strcmp(pattern, CEPH_NAMESPACE_WILDCARD))
|
||||
return true;
|
||||
|
||||
return !strcmp(pattern, target);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the mds namespace in ceph_mount_options matches
|
||||
* the passed in namespace string. First time match (when
|
||||
* ->mds_namespace is NULL) is treated specially, since
|
||||
* ->mds_namespace needs to be initialized by the caller.
|
||||
*/
|
||||
static inline int namespace_equals(struct ceph_mount_options *fsopt,
|
||||
const char *namespace, size_t len)
|
||||
static inline bool namespace_equals(struct ceph_mount_options *fsopt,
|
||||
const char *namespace, size_t len)
|
||||
{
|
||||
return !(fsopt->mds_namespace &&
|
||||
(strlen(fsopt->mds_namespace) != len ||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@
|
|||
#define CEPH_INO_CEPH 2 /* hidden .ceph dir */
|
||||
#define CEPH_INO_GLOBAL_SNAPREALM 3 /* global dummy snaprealm */
|
||||
|
||||
/*
|
||||
* name for "old" CephFS file systems,
|
||||
* see ceph.git e2b151d009640114b2565c901d6f41f6cd5ec652
|
||||
*/
|
||||
#define CEPH_OLD_FS_NAME "cephfs"
|
||||
|
||||
/* arbitrary limit on max # of monitors (cluster of 3 is typical) */
|
||||
#define CEPH_MAX_MON 31
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue