Whamcloud - gitweb
LU-18756 sec: add generic nodemap resource id check 07/59207/5
authorMarc Vef <mvef@whamcloud.com>
Tue, 13 May 2025 11:13:50 +0000 (13:13 +0200)
committerOleg Drokin <green@whamcloud.com>
Thu, 12 Jun 2025 06:32:42 +0000 (06:32 +0000)
This patch represents the first patch in the series to check the OST
object and MDT inodes UID/GID against the nodemap offset range. This
patch adds the corresponding functions on the OST, MDT, and nodemap
sides for the resource ID check. A resource is defined as an MDT inode
or OST object. This patch does not yet connect the functions to the
relevant codepaths. The patch further adds the new "lctl set_param"
configurables, which are (for now) disabled by default:

- "lctl set_param mdt.*.enable_resource_id_check={0,1}" toggling the
  check on the MDT side.
- "lctl set_param obdfilter.*.enable_resource_id_check={0,1}" toggling
  the check on the OST side.

These configurables work individually but should be toggled together.

The ID check relies on the "nodemap_map_id()" functionality to
guarantee compatibility with the nodemap mapping functionality, e.g.,
covering both offset and mapping cases, among others. The ID check
therefore functions as follows:

If "nodemap_map_id()" returns the squashed value for both UID and GID
for a given client export, "fs_uid", and "fs_gid" stored on the MDT
inode and OST object, access is not permitted to the resource. It
does not rely on any IDs given by the client. The corresponding
permission bits or ACLs are not taken into consideration and are
only relevant later if access was permitted elsewhere.

Test-Parameters: trivial
Signed-off-by: Marc Vef <mvef@whamcloud.com>
Change-Id: I818c511cd37251843bcfa6b873ef8bdc05176980
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/59207
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Sebastien Buisson <sbuisson@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/include/lu_target.h
lustre/include/lustre_nodemap.h
lustre/mdt/mdt_internal.h
lustre/mdt/mdt_lib.c
lustre/mdt/mdt_lproc.c
lustre/ofd/lproc_ofd.c
lustre/ofd/ofd_internal.h
lustre/ofd/ofd_objects.c
lustre/ptlrpc/nodemap_handler.c
lustre/target/tgt_main.c

index ff631fa..a437eaf 100644 (file)
@@ -157,7 +157,11 @@ struct lu_target {
                                 /* enforce recovery for local clients */
                                 lut_local_recovery:1,
                                 lut_cksum_t10pi_enforce:1,
-                                lut_no_create:1;
+                                lut_no_create:1,
+                                /* if enabled, MDT inodes UID/GID are checked
+                                 * against the nodemap mapping rules.
+                                 */
+                                lut_enable_resource_id_check:1;
        /* checksum types supported on this node */
        enum cksum_types         lut_cksum_types_supported;
        /** last_rcvd file */
index c986e57..dd64dc6 100644 (file)
@@ -219,6 +219,8 @@ __u32 nodemap_map_id(struct lu_nodemap *nodemap,
 ssize_t nodemap_map_acl(struct lu_nodemap *nodemap, void *buf, size_t size,
                        enum nodemap_tree_type tree_type);
 int nodemap_map_suppgid(struct lu_nodemap *nodemap, int suppgid);
+int nodemap_check_resource_ids(struct obd_export *exp, __u32 fs_uid,
+                              __u32 fs_gid);
 #ifdef HAVE_SERVER_SUPPORT
 void nodemap_test_nid(struct lnet_nid *nid, char *name_buf, size_t name_len);
 #else
index c3801ae..993ae31 100644 (file)
@@ -989,6 +989,8 @@ int mdt_check_ucred(struct mdt_thread_info *info);
 int mdt_init_ucred(struct mdt_thread_info *info, struct mdt_body *body);
 int mdt_init_ucred_reint(struct mdt_thread_info *info);
 void mdt_exit_ucred(struct mdt_thread_info *info);
+int mdt_check_resource_ids(struct mdt_thread_info *info,
+                          struct mdt_object *obj);
 int mdt_version_get_check(struct mdt_thread_info *info, struct mdt_object *mto,
                          int idx);
 void mdt_version_get_save(struct mdt_thread_info *info, struct mdt_object *mto,
index 9621568..b6ce437 100644 (file)
@@ -750,6 +750,46 @@ int mdt_init_ucred_reint(struct mdt_thread_info *info)
                return new_init_ucred(info, REC_INIT, NULL);
 }
 
+/**
+ * mdt_check_resource_id() - check client access to resource via nodemap
+ *
+ * @info: mdt thread environment
+ * @obj: mdt object to check
+ *
+ * Check whether the client is allowed to access the resource by consulting
+ * the nodemap with the client's export and the MDT inode's UID/GID attributes.
+ *
+ * Return:
+ * * %0 on success (access is allowed)
+ * * %-ECHRNG if access is denied
+ */
+int mdt_check_resource_ids(struct mdt_thread_info *info, struct mdt_object *obj)
+{
+       struct dt_object *dt;
+       struct lu_attr la = { 0 };
+
+       ENTRY;
+
+       if (info->mti_mdt->mdt_lut.lut_enable_resource_id_check == 0)
+               RETURN(0);
+
+       dt = mdt_obj2dt(obj);
+
+       /* Get attributes from MDT inode */
+       if (dt && dt->do_ops && dt->do_ops->do_attr_get) {
+               dt_attr_get(info->mti_env, mdt_obj2dt(obj), &la);
+       } else {
+               /* log this case but don't return err code */
+               CERROR("%s: no dt object for " DFID ": rc = %d\n",
+                      mdt_obd_name(info->mti_mdt), PFID(mdt_object_fid(obj)),
+                      -ENOENT);
+               RETURN(0);
+       }
+
+       RETURN(nodemap_check_resource_ids(mdt_info_req(info)->rq_export,
+                                         la.la_uid, la.la_gid));
+}
+
 /* copied from lov/lov_ea.c, just for debugging, will be removed later */
 void mdt_dump_lmm(int level, const struct lov_mds_md *lmm, __u64 valid)
 {
index f77a5e2..e416f4c 100644 (file)
@@ -805,6 +805,68 @@ MDT_BOOL_RW_ATTR(enable_dmv_implicit_inherit);
 MDT_BOOL_RW_ATTR(enable_dmv_xattr);
 MDT_BOOL_RW_ATTR(enable_rename_trylock);
 
+/**
+ * enable_resource_id_check_show() - Show if resource ID checking is enabled
+ * on the MDT.
+ *
+ * @kobj: kobject for the MDT device
+ * @attr: attribute for the MDT device
+ * @buf: buffer to write the value to
+ *
+ * When enabled, MDT inodes UID/GID are checked against
+ * the nodemap mapping rules.
+ *
+ * Return:
+ * * %0 on success
+ * * %negative on failure
+ */
+static ssize_t enable_resource_id_check_show(struct kobject *kobj,
+                                            struct attribute *attr, char *buf)
+{
+       struct obd_device *obd =
+               container_of(kobj, struct obd_device, obd_kset.kobj);
+       struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n",
+                        mdt->mdt_lut.lut_enable_resource_id_check);
+}
+
+/**
+ * enable_resource_id_check_store() - Enable or disable resource ID checking
+ * on the MDT.
+ *
+ * @kobj: kobject for the MDT device
+ * @attr: attribute for the MDT device
+ * @buffer: buffer containing the value to set
+ * @count: length of the buffer
+ *
+ * This is used to interface to userspace administrative tools to enable
+ * or disable resource ID checking on the MDT.
+ *
+ * Return:
+ * * %0 on success
+ * * %negative on failure
+ */
+static ssize_t enable_resource_id_check_store(struct kobject *kobj,
+                                             struct attribute *attr,
+                                             const char *buffer, size_t count)
+{
+       struct obd_device *obd =
+               container_of(kobj, struct obd_device, obd_kset.kobj);
+       struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+       bool val;
+       int rc;
+
+       rc = kstrtobool(buffer, &val);
+       if (rc)
+               return rc;
+
+       mdt->mdt_lut.lut_enable_resource_id_check = val;
+
+       return count;
+}
+LUSTRE_RW_ATTR(enable_resource_id_check);
+
 static ssize_t enable_pin_gid_show(struct kobject *kobj,
                                   struct attribute *attr, char *buf)
 {
@@ -1436,6 +1498,8 @@ static struct attribute *mdt_attrs[] = {
        &lustre_attr_enable_remote_dir_gid.attr,
        &lustre_attr_enable_remote_rename.attr,
        &lustre_attr_enable_remote_subdir_mount.attr,
+       &lustre_attr_enable_rename_trylock.attr,
+       &lustre_attr_enable_resource_id_check.attr,
        &lustre_attr_enable_strict_som.attr,
        &lustre_attr_enable_striped_dir.attr,
        &lustre_attr_commit_on_sharing.attr,
@@ -1455,7 +1519,6 @@ static struct attribute *mdt_attrs[] = {
        &lustre_attr_dir_restripe_nsonly.attr,
        &lustre_attr_checksum_t10pi_enforce.attr,
        &lustre_attr_max_mod_rpcs_in_flight.attr,
-       &lustre_attr_enable_rename_trylock.attr,
        NULL,
 };
 
index 48fd75e..e00abd5 100644 (file)
@@ -271,6 +271,70 @@ static ssize_t degraded_store(struct kobject *kobj, struct attribute *attr,
 LUSTRE_RW_ATTR(degraded);
 
 /**
+ * enable_resource_id_check_show() - Show if resource ID checking is enabled
+ * on the OST.
+ *
+ * @kobj: kobject for the OFD device
+ * @attr: attribute for the OFD device
+ * @buf: buffer to write the value to
+ *
+ * When enabled, an OST object's UID and GID are checked against
+ * the nodemap mapping rules.
+ *
+ * Return:
+ * * %0 on success
+ * * %negative on failure
+ */
+static ssize_t enable_resource_id_check_show(struct kobject *kobj,
+                                            struct attribute *attr, char *buf)
+{
+       struct obd_device *obd =
+               container_of(kobj, struct obd_device, obd_kset.kobj);
+       struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n",
+                        ofd->ofd_lut.lut_enable_resource_id_check);
+}
+
+/**
+ * enable_resource_id_check_store() - Enable or disable resource ID checking
+ * on the OST.
+ *
+ * @kobj: kobject for the OFD device
+ * @attr: attribute for the OFD device
+ * @buffer: buffer containing the value to set
+ * @count: length of the buffer
+ *
+ * This is used to interface to userspace administrative tools to enable
+ * or disable resource ID checking on the OST.
+ *
+ * Return:
+ * * %0 on success
+ * * %negative on failure
+ */
+static ssize_t enable_resource_id_check_store(struct kobject *kobj,
+                                             struct attribute *attr,
+                                             const char *buffer, size_t count)
+{
+       struct obd_device *obd =
+               container_of(kobj, struct obd_device, obd_kset.kobj);
+       struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+       bool val;
+       int rc;
+
+       rc = kstrtobool(buffer, &val);
+       if (rc)
+               return rc;
+
+       spin_lock(&ofd->ofd_flags_lock);
+       ofd->ofd_lut.lut_enable_resource_id_check = val;
+       spin_unlock(&ofd->ofd_flags_lock);
+
+       return count;
+}
+LUSTRE_RW_ATTR(enable_resource_id_check);
+
+/**
  * Show if the OFD is in no precreate mode.
  *
  * This means OFD has been adminstratively disabled at the OST to prevent
@@ -981,11 +1045,15 @@ static struct attribute *ofd_attrs[] = {
        &lustre_attr_access_log_mask.attr,
        &lustre_attr_access_log_size.attr,
        &lustre_attr_atime_diff.attr,
+       &lustre_attr_at_history.attr,
+       &lustre_attr_at_max.attr,
+       &lustre_attr_at_min.attr,
        &lustre_attr_brw_size.attr,
        &lustre_attr_checksum_dump.attr,
        &lustre_attr_checksum_t10pi_enforce.attr,
        &lustre_attr_checksum_type.attr,
        &lustre_attr_degraded.attr,
+       &lustre_attr_enable_resource_id_check.attr,
        &lustre_attr_evict_client.attr,
        &lustre_attr_eviction_count.attr,
        &lustre_attr_fstype.attr,
@@ -997,12 +1065,12 @@ static struct attribute *ofd_attrs[] = {
        &lustre_attr_job_cleanup_interval.attr,
        &lustre_attr_lfsck_speed_limit.attr,
        &lustre_attr_no_create.attr,
-       &lustre_attr_readonly.attr,
 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 20, 53, 0)
        &lustre_attr_no_precreate.attr,
 #endif
        &lustre_attr_num_exports.attr,
        &lustre_attr_precreate_batch.attr,
+       &lustre_attr_readonly.attr,
        &lustre_attr_recovery_time_hard.attr,
        &lustre_attr_recovery_time_soft.attr,
        &lustre_attr_seqs_allocated.attr,
@@ -1011,9 +1079,6 @@ static struct attribute *ofd_attrs[] = {
        &lustre_attr_tot_pending.attr,
        &lustre_attr_soft_sync_limit.attr,
        &lustre_attr_sync_journal.attr,
-       &lustre_attr_at_min.attr,
-       &lustre_attr_at_max.attr,
-       &lustre_attr_at_history.attr,
        NULL,
 };
 
index b99a150..d17b1aa 100644 (file)
@@ -40,6 +40,9 @@
  */
 #define OFD_DEF_ATIME_DIFF     0 /* disabled */
 
+/* Special mode value for OST objects with unset attributes */
+#define OFD_UNSET_ATTRS_MODE (S_IFREG | S_ISUID | S_ISGID | S_ISVTX | 0666)
+
 /* request stats */
 enum {
        LPROC_OFD_STATS_READ_BYTES = 0,
@@ -394,6 +397,7 @@ int ofd_attr_get(const struct lu_env *env, struct ofd_object *fo,
                 struct lu_attr *la);
 int ofd_attr_handle_id(const struct lu_env *env, struct ofd_object *fo,
                         struct lu_attr *la, int is_setattr);
+int ofd_check_resource_ids(const struct lu_env *env, struct obd_export *exp);
 
 static inline
 struct ofd_object *ofd_object_find_exists(const struct lu_env *env,
index 19ea68a..5a5a6d8 100644 (file)
@@ -22,6 +22,7 @@
 #include <dt_object.h>
 #include <lustre_lfsck.h>
 #include <lustre_export.h>
+#include <lustre_nodemap.h>
 
 #include "ofd_internal.h"
 
@@ -290,7 +291,7 @@ int ofd_precreate_objects(const struct lu_env *env, struct ofd_device *ofd,
                RETURN(-ENOMEM);
 
        info->fti_attr.la_valid = LA_TYPE | LA_MODE;
-       info->fti_attr.la_mode = S_IFREG | S_ISUID | S_ISGID | S_ISVTX | 0666;
+       info->fti_attr.la_mode = OFD_UNSET_ATTRS_MODE;
        info->fti_dof.dof_type = dt_mode_to_dft(S_IFREG);
 
        info->fti_attr.la_valid |= LA_ATIME | LA_MTIME | LA_CTIME;
@@ -1121,3 +1122,53 @@ int ofd_attr_get(const struct lu_env *env, struct ofd_object *fo,
        }
        RETURN(rc);
 }
+
+/**
+ * ofd_check_resource_id() - check client access to resource via nodemap
+ *
+ * @env: execution environment
+ * @exp: OBD export of client
+ *
+ * Check whether the client is allowed to access the resource by consulting
+ * the nodemap with the client's export and the OST objects's UID/GID attr.
+ *
+ * Return:
+ * * %0 on success (access is allowed)
+ * * %-ECHRNG if access is denied
+ */
+int ofd_check_resource_ids(const struct lu_env *env, struct obd_export *exp)
+{
+       struct ofd_object *fo;
+       struct lu_attr la = { 0 };
+       int rc = 0;
+
+       ENTRY;
+
+       if (ofd_exp(exp)->ofd_lut.lut_enable_resource_id_check == 0)
+               RETURN(0);
+
+       fo = ofd_info(env)->fti_obj;
+
+       rc = dt_attr_get(env, ofd_object_child(fo), &la);
+       if (rc) {
+               /* log this case but don't return err code */
+               CERROR("%s: failed to get attr for obj " DFID ": rc = %d\n",
+                      ofd_name(ofd_exp(exp)),
+                      PFID(lu_object_fid(&fo->ofo_obj.do_lu)), rc);
+               RETURN(0);
+       }
+
+       /* Objects with OFD_UNSET_ATTRS_MODE have no ID associated with them
+        * yet. Therefore, we can't verify that the stored IDs are valid.
+        * TODO Instead, the object will be repaired for future accesses based
+        * on the IDs set on the corresponding MDT inode.
+        */
+       if (la.la_mode == OFD_UNSET_ATTRS_MODE) {
+               CDEBUG(D_SEC,
+                      "OST object " DFID " has unset attributes (mode=0%o), skipping ID check\n",
+                      PFID(lu_object_fid(&fo->ofo_obj.do_lu)), la.la_mode);
+               RETURN(0);
+       }
+
+       RETURN(nodemap_check_resource_ids(exp, la.la_uid, la.la_gid));
+}
index 3bf0a20..46607f0 100644 (file)
@@ -1058,12 +1058,15 @@ ssize_t nodemap_map_acl(struct lu_nodemap *nodemap, void *buf, size_t size,
 EXPORT_SYMBOL(nodemap_map_acl);
 
 /**
- * Map supplementary groups received from client.
+ * nodemap_map_supplementary_groups() - map supplementary groups received
+ * from the client
  *
- * \param      lu_nodemap      nodemap
- * \param      id              id to map
+ * @nodemap: nodemap
+ * @suppgid: id to map
  *
- * \retval     mapped id or -1 for invalid suppgid
+ * Return:
+ * * mapped id on success
+ * * %-1 for invalid suppgid
  */
 int nodemap_map_suppgid(struct lu_nodemap *nodemap, int suppgid)
 {
@@ -1073,6 +1076,62 @@ int nodemap_map_suppgid(struct lu_nodemap *nodemap, int suppgid)
 }
 EXPORT_SYMBOL(nodemap_map_suppgid);
 
+/**
+ * nodemap_check_resource_id() - check if export can access a resource
+ *
+ * @exp: export to check
+ * @fs_uid: uid of the resource
+ * @fs_gid: gid of the resource
+ *
+ * Checks whether an export should be able to access a resource. This is called,
+ * e.g., for an MDT inode or OST object. If both UID and GID are squashed,
+ * the export should not be able to access the object since it is from outside
+ * the nodemap ID range.
+ *
+ * Return:
+ * * %0 on success (access is allowed)
+ * * %-ECHRNG if access is denied
+ */
+int nodemap_check_resource_ids(struct obd_export *exp, __u32 fs_uid,
+                              __u32 fs_gid)
+{
+       struct lu_nodemap *nodemap;
+       __u32 client_uid, client_gid;
+       __u32 client_squashed_uid, client_squashed_gid;
+       int rc = 0;
+
+       ENTRY;
+
+       nodemap = nodemap_get_from_exp(exp);
+       if (IS_ERR_OR_NULL(nodemap))
+               RETURN(0);
+
+       client_uid = nodemap_map_id(nodemap, NODEMAP_UID, NODEMAP_FS_TO_CLIENT,
+                                   fs_uid);
+       client_gid = nodemap_map_id(nodemap, NODEMAP_GID, NODEMAP_FS_TO_CLIENT,
+                                   fs_gid);
+       client_squashed_uid = nodemap_map_id(nodemap, NODEMAP_UID,
+                                            NODEMAP_FS_TO_CLIENT,
+                                            nodemap->nm_squash_uid);
+       client_squashed_gid = nodemap_map_id(nodemap, NODEMAP_GID,
+                                            NODEMAP_FS_TO_CLIENT,
+                                            nodemap->nm_squash_gid);
+
+       if (client_uid == client_squashed_uid &&
+           client_gid == client_squashed_gid) {
+               CDEBUG(D_SEC,
+                      "Nodemap %s: access denied for export %s (at %s) fs_uid=%u fs_gid=%u\n",
+                      nodemap->nm_name, obd_uuid2str(&exp->exp_client_uuid),
+                      obd_export_nid2str(exp), fs_uid, fs_gid);
+               GOTO(out, rc = -ECHRNG);
+       }
+
+out:
+       nodemap_putref(nodemap);
+       RETURN(rc);
+}
+EXPORT_SYMBOL(nodemap_check_resource_ids);
+
 static int nodemap_inherit_properties(struct lu_nodemap *dst,
                                      struct lu_nodemap *src)
 {
index 4505c5f..12aa073 100644 (file)
@@ -492,6 +492,7 @@ int tgt_init(const struct lu_env *env, struct lu_target *lut,
        lut->lut_cksum_t10pi_enforce = 0;
        lut->lut_cksum_types_supported =
                obd_cksum_types_supported_server(obd->obd_name);
+       lut->lut_enable_resource_id_check = 0;
 
        spin_lock_init(&lut->lut_slc_locks_guard);
        INIT_LIST_HEAD(&lut->lut_slc_locks);