History

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 ]

Viacheslav Dubeyko committed 9 months ago

Commit 7987cce1 parent bc8deda5 files · +43 −11Browse files

Unified · Split

5 files
  1. fs/ceph/mds_client.c +3 −2
  2. fs/ceph/mdsmap.c +19 −7
  3. fs/ceph/mdsmap.h +1 −0
  4. fs/ceph/super.h +14 −2
  5. include/linux/ceph/ceph_fs.h +6 −0
fs/ceph/mds_client.c+3−2
@@ -5671,7 +5671,7 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc, · 5670 unmodified lines
5671 u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);5671 u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);
5672 u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);5672 u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);
5673 struct ceph_client *cl = mdsc->fsc->client;5673 struct ceph_client *cl = mdsc->fsc->client;
5674 const char *fs_name = mdsc->fsc->mount_options->mds_namespace;5674 const char *fs_name = mdsc->mdsmap->m_fs_name;
5675 const char *spath = mdsc->fsc->mount_options->server_path;5675 const char *spath = mdsc->fsc->mount_options->server_path;
5676 bool gid_matched = false;5676 bool gid_matched = false;
5677 u32 gid, tlen, len;5677 u32 gid, tlen, len;
@@ -5679,7 +5679,8 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc, · 1 unmodified line
5679 5679
5680 doutc(cl, "fsname check fs_name=%s match.fs_name=%s\n",5680 doutc(cl, "fsname check fs_name=%s match.fs_name=%s\n",
5681 fs_name, auth->match.fs_name ? auth->match.fs_name : "");5681 fs_name, auth->match.fs_name ? auth->match.fs_name : "");
5682 if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) {5682
5683 if (!ceph_namespace_match(auth->match.fs_name, fs_name)) {
5683 /* fsname mismatch, try next one */5684 /* fsname mismatch, try next one */
5684 return 0;5685 return 0;
5685 }5686 }
fs/ceph/mdsmap.c+19−7
@@ -353,22 +353,33 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, · 352 unmodified lines
353 __decode_and_drop_type(p, end, u8, bad_ext);353 __decode_and_drop_type(p, end, u8, bad_ext);
354 }354 }
355 if (mdsmap_ev >= 8) {355 if (mdsmap_ev >= 8) {
356 u32 fsname_len;356 size_t fsname_len;
357
357 /* enabled */358 /* enabled */
358 ceph_decode_8_safe(p, end, m->m_enabled, bad_ext);359 ceph_decode_8_safe(p, end, m->m_enabled, bad_ext);
360
359 /* fs_name */361 /* fs_name */
360 ceph_decode_32_safe(p, end, fsname_len, bad_ext);362 m->m_fs_name = ceph_extract_encoded_string(p, end,
363 &fsname_len,
364 GFP_NOFS);
365 if (IS_ERR(m->m_fs_name)) {
366 m->m_fs_name = NULL;
367 goto nomem;
368 }
361 369
362 /* validate fsname against mds_namespace */370 /* validate fsname against mds_namespace */
363 if (!namespace_equals(mdsc->fsc->mount_options, *p,371 if (!namespace_equals(mdsc->fsc->mount_options, m->m_fs_name,
364 fsname_len)) {372 fsname_len)) {
365 pr_warn_client(cl, "fsname %*pE doesn't match mds_namespace %s\n",373 pr_warn_client(cl, "fsname %s doesn't match mds_namespace %s\n",
366 (int)fsname_len, (char *)*p,374 m->m_fs_name,
367 mdsc->fsc->mount_options->mds_namespace);375 mdsc->fsc->mount_options->mds_namespace);
368 goto bad;376 goto bad;
369 }377 }
370 /* skip fsname after validation */378 } else {
371 ceph_decode_skip_n(p, end, fsname_len, bad);379 m->m_enabled = false;
380 m->m_fs_name = kstrdup(CEPH_OLD_FS_NAME, GFP_NOFS);
381 if (!m->m_fs_name)
382 goto nomem;
372 }383 }
373 /* damaged */384 /* damaged */
374 if (mdsmap_ev >= 9) {385 if (mdsmap_ev >= 9) {
@@ -430,6 +441,7 @@ void ceph_mdsmap_destroy(struct ceph_mdsmap *m) · 55 unmodified lines
430 kfree(m->m_info);441 kfree(m->m_info);
431 }442 }
432 kfree(m->m_data_pg_pools);443 kfree(m->m_data_pg_pools);
444 kfree(m->m_fs_name);
433 kfree(m);445 kfree(m);
434}446}
435 447
fs/ceph/mdsmap.h+1−0
@@ -45,6 +45,7 @@ struct ceph_mdsmap { · 44 unmodified lines
45 bool m_enabled;45 bool m_enabled;
46 bool m_damaged;46 bool m_damaged;
47 int m_num_laggy;47 int m_num_laggy;
48 char *m_fs_name;
48};49};
49 50
50static inline struct ceph_entity_addr *51static inline struct ceph_entity_addr *
fs/ceph/super.h+14−2
@@ -104,14 +104,26 @@ struct ceph_mount_options { · 103 unmodified lines
104 struct fscrypt_dummy_policy dummy_enc_policy;104 struct fscrypt_dummy_policy dummy_enc_policy;
105};105};
106 106
107#define CEPH_NAMESPACE_WILDCARD "*"
108
109static inline bool ceph_namespace_match(const char *pattern,
110 const char *target)
111{
112 if (!pattern || !pattern[0] ||
113 !strcmp(pattern, CEPH_NAMESPACE_WILDCARD))
114 return true;
115
116 return !strcmp(pattern, target);
117}
118
107/*119/*
108 * Check if the mds namespace in ceph_mount_options matches120 * Check if the mds namespace in ceph_mount_options matches
109 * the passed in namespace string. First time match (when121 * the passed in namespace string. First time match (when
110 * ->mds_namespace is NULL) is treated specially, since122 * ->mds_namespace is NULL) is treated specially, since
111 * ->mds_namespace needs to be initialized by the caller.123 * ->mds_namespace needs to be initialized by the caller.
112 */124 */
113static inline int namespace_equals(struct ceph_mount_options *fsopt,125static inline bool namespace_equals(struct ceph_mount_options *fsopt,
114 const char *namespace, size_t len)126 const char *namespace, size_t len)
115{127{
116 return !(fsopt->mds_namespace &&128 return !(fsopt->mds_namespace &&
117 (strlen(fsopt->mds_namespace) != len ||129 (strlen(fsopt->mds_namespace) != len ||
include/linux/ceph/ceph_fs.h+6−0
@@ -31,6 +31,12 @@ · 30 unmodified lines
31#define CEPH_INO_CEPH 2 /* hidden .ceph dir */31#define CEPH_INO_CEPH 2 /* hidden .ceph dir */
32#define CEPH_INO_GLOBAL_SNAPREALM 3 /* global dummy snaprealm */32#define CEPH_INO_GLOBAL_SNAPREALM 3 /* global dummy snaprealm */
33 33
34/*
35 * name for "old" CephFS file systems,
36 * see ceph.git e2b151d009640114b2565c901d6f41f6cd5ec652
37 */
38#define CEPH_OLD_FS_NAME "cephfs"
39
34/* arbitrary limit on max # of monitors (cluster of 3 is typical) */40/* arbitrary limit on max # of monitors (cluster of 3 is typical) */
35#define CEPH_MAX_MON 3141#define CEPH_MAX_MON 31
36 42