Whamcloud - gitweb
LU-13791 mdt: allow using symbolic capability names 18/54118/3
authorAndreas Dilger <adilger@whamcloud.com>
Wed, 21 Feb 2024 00:59:25 +0000 (17:59 -0700)
committerOleg Drokin <green@whamcloud.com>
Tue, 21 May 2024 18:23:32 +0000 (18:23 +0000)
Allow "mdt.*.enable_cap_mask" param set and print symbolic names,
similar to the "debug" and "subsystem_debug" parameters.  The
allowed parameter names are in the capabilities(7) man page, in
either upper or lowercase, like cap_chown, cap_dac_read_search,
etc. along with "all" to enable all capabilities if clients are
trusted.  For example:

    lctl set_param -P mdt.lfs-*.enable_cap_mask=+cap_dac_read_search

Since kernel_cap_t is a 64-bit value, enhance cfs_str2mask() to
take u64 mask arguments.  The calling libcfs_debug_str2mask()
sticks with "int mask" for now.

Split the core out from libcfs_debug_mask2str() into a new helper
function cfs_mask2str() so it can be called directly.

Fixes: 54f677651b ("LU-13791 mdt: parameter to tune capabilities")
Signed-off-by: Andreas Dilger <adilger@whamcloud.com>
Change-Id: I3f71f61a17d4d3614e46a526c60e709d9eb825b3
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/54118
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Alexander Zarochentsev <alexander.zarochentsev@hpe.com>
Reviewed-by: Sebastien Buisson <sbuisson@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
libcfs/include/libcfs/libcfs_string.h
libcfs/libcfs/debug.c
libcfs/libcfs/libcfs_string.c
libcfs/libcfs/module.c
lustre/mdd/mdd_device.c
lustre/mdd/mdd_lproc.c
lustre/mdt/mdt_internal.h
lustre/mdt/mdt_lproc.c
lustre/tests/sanity-sec.sh

index 5bbb8dd..54bac97 100644 (file)
@@ -41,7 +41,9 @@
 /* libcfs_string.c */
 /* Convert a text string to a bitmask */
 int cfs_str2mask(const char *str, const char *(*bit2str)(int bit),
-                int *oldmask, int minmask, int allmask, int defmask);
+                u64 *oldmask, u64 minmask, u64 allmask, u64 defmask);
+int cfs_mask2str(char *str, int size, u64 mask, const char *(*bit2str)(int),
+                char sep);
 
 /*
  * Structure to represent \<range_expr\> token of the syntax.
index 9cc4c6a..66ca9d7 100644 (file)
@@ -283,78 +283,44 @@ static const char *libcfs_debug_dbg2str(int debug)
        return libcfs_debug_masks[debug];
 }
 
-int
-libcfs_debug_mask2str(char *str, int size, int mask, int is_subsys)
+int libcfs_debug_mask2str(char *str, int size, int mask, int is_subsys)
 {
-       const char *(*fn)(int bit) = is_subsys ? libcfs_debug_subsys2str :
-                                                libcfs_debug_dbg2str;
-       int len = 0;
-       const char *token;
-       int i;
-
-       if (mask == 0) {                        /* "0" */
-               if (size > 0)
-                       str[0] = '0';
-               len = 1;
-       } else {                                /* space-separated tokens */
-               for (i = 0; i < 32; i++) {
-                       if ((mask & BIT(i)) == 0)
-                               continue;
-
-                       token = fn(i);
-                       if (!token)     /* unused bit */
-                               continue;
-
-                       if (len > 0) {          /* separator? */
-                               if (len < size)
-                                       str[len] = ' ';
-                               len++;
-                       }
-
-                       while (*token != 0) {
-                               if (len < size)
-                                       str[len] = *token;
-                               token++;
-                               len++;
-                       }
-               }
-       }
-
-       /* terminate 'str' */
-       if (len < size)
-               str[len] = 0;
-       else
-               str[size - 1] = 0;
+       const char *(*bit2str)(int bit) = is_subsys ? libcfs_debug_subsys2str :
+                                                     libcfs_debug_dbg2str;
 
-       return len;
+       return cfs_mask2str(str, size, mask, bit2str, ' ');
 }
 
-int
-libcfs_debug_str2mask(int *mask, const char *str, int is_subsys)
+int libcfs_debug_str2mask(int *mask, const char *str, int is_subsys)
 {
-       const char *(*fn)(int bit) = is_subsys ? libcfs_debug_subsys2str :
-                                                libcfs_debug_dbg2str;
+       const char *(*bit2str)(int bit) = is_subsys ? libcfs_debug_subsys2str :
+                                                     libcfs_debug_dbg2str;
+       u64 newmask = *mask;
        int m = 0;
        int matched;
-       int n;
-       int t;
+       int n, t;
+       int rc;
 
        /* Allow a number for backwards compatibility */
        for (n = strlen(str); n > 0; n--)
-               if (!isspace(str[n-1]))
+               if (!isspace(str[n - 1]))
                        break;
        matched = n;
        t = sscanf(str, "%i%n", &m, &matched);
        if (t >= 1 && matched == n) {
                /* don't print warning for lctl set_param debug=0 or -1 */
                if (m != 0 && m != -1)
-                       CWARN("You are trying to use a numerical value for the mask - this will be deprecated in a future release.\n");
+                       CWARN("using a numerical debug mask is deprecated\n");
                *mask = m;
                return 0;
        }
 
-       return cfs_str2mask(str, fn, mask, is_subsys ? 0 : D_CANTMASK, ~0,
-                           is_subsys ? LIBCFS_S_DEFAULT : LIBCFS_D_DEFAULT);
+       rc = cfs_str2mask(str, bit2str, &newmask, is_subsys ? 0 : D_CANTMASK,
+                         ~0, is_subsys ? LIBCFS_S_DEFAULT : LIBCFS_D_DEFAULT);
+
+       *mask = newmask;
+
+       return rc;
 }
 
 char lnet_debug_log_upcall[1024] = "/usr/lib/lustre/lnet_debug_log_upcall";
index 0b1c621..cfa3c89 100644 (file)
 #include <libcfs/libcfs.h>
 #include <libcfs/libcfs_string.h>
 
+
+/* convert a binary mask to a string of bit names */
+int cfs_mask2str(char *str, int size, u64 mask, const char *(*bit2str)(int bit),
+                char sep)
+{
+       int len = 0;
+       const char *token;
+       int i;
+
+       if (mask == 0) {                        /* "0" */
+               if (size > 0)
+                       str[0] = '0';
+               len = 1;
+       } else {                                /* space-separated tokens */
+               for (i = 0; i < 64; i++) {
+                       if ((mask & BIT(i)) == 0)
+                               continue;
+
+                       token = bit2str(i);
+                       if (!token)             /* unused bit */
+                               continue;
+
+                       if (len > 0) {          /* separator? */
+                               if (len < size)
+                                       str[len] = sep;
+                               len++;
+                       }
+
+                       while (*token != 0) {
+                               if (len < size)
+                                       str[len] = *token;
+                               token++;
+                               len++;
+                       }
+               }
+       }
+
+       /* terminate 'str' */
+       if (len < size)
+               str[len++] = '\n';
+       if (len < size)
+               str[len] = '\0';
+       else
+               str[size - 1] = '\0';
+
+       return len;
+}
+EXPORT_SYMBOL(cfs_mask2str);
+
 /* Convert a text string to a bitmask */
 int cfs_str2mask(const char *str, const char *(*bit2str)(int bit),
-                int *oldmask, int minmask, int allmask, int defmask)
+                u64 *oldmask, u64 minmask, u64 allmask, u64 defmask)
 {
        const char *debugstr;
-       char op = 0;
-       int newmask = minmask, i, len, found = 0;
+       u64 newmask = minmask, found = 0;
 
        ENTRY;
        /* <str> must be a list of tokens separated by whitespace or comma,
@@ -55,6 +103,9 @@ int cfs_str2mask(const char *str, const char *(*bit2str)(int bit),
         * applies to all following tokens up to the next operator.
         */
        while (*str != 0) {
+               int i, len;
+               char op = 0;
+
                while (isspace(*str) || *str == ',')
                        str++;
                if (*str == 0)
index 4e0440b..e65c1c7 100644 (file)
@@ -124,7 +124,7 @@ static int proc_dobitmasks(struct ctl_table *table, int write,
                        rc = 0;
                } else {
                        rc = cfs_trace_copyout_string(buffer, nob,
-                                                     tmpstr + pos, "\n");
+                                                     tmpstr + pos, NULL);
                }
        } else {
                tmpstr = memdup_user_nul(buffer, nob);
index d39fbef..9068a17 100644 (file)
@@ -1798,13 +1798,15 @@ static int mdd_changelog_user_register(const struct lu_env *env,
        spin_unlock(&mdd->mdd_cl.mc_lock);
 
        if (mask) {
+               u64 newmask = CHANGELOG_DEFMASK;
+
                /* if user will use relative mask apply it on default one */
-               rec->cur_mask = CHANGELOG_DEFMASK;
-               rc = cfs_str2mask(mask, changelog_type2str, &rec->cur_mask,
+               rc = cfs_str2mask(mask, changelog_type2str, &newmask,
                                  CHANGELOG_MINMASK, CHANGELOG_ALLMASK,
                                  CHANGELOG_DEFMASK);
                if (rc)
                        GOTO(out_users, rc);
+               rec->cur_mask = newmask;
        } else if (mdd->mdd_cl.mc_proc_mask == CHANGELOG_MINMASK) {
                /* a maskless users means default mask but only if server has
                 * no specific mask set
index a8746a6..427eb66 100644 (file)
@@ -121,7 +121,7 @@ mdd_changelog_mask_seq_write(struct file *file, const char __user *buffer,
        char *kernbuf;
        int rc;
        int oldmask = mdd->mdd_cl.mc_proc_mask;
-       int newmask = oldmask;
+       u64 newmask = oldmask;
 
        ENTRY;
 
index 631952d..34f748e 100644 (file)
@@ -1479,6 +1479,37 @@ static inline bool mdt_changelog_allow(struct mdt_thread_info *info)
        return is_admin;
 }
 
+/* convert a capability into an integer to print or manage more easily */
+static inline u64 mdt_cap2num(kernel_cap_t cap)
+{
+#ifdef CAP_FOR_EACH_U32
+       /* kernels before v6.2-13111-gf122a08b197d had a more complex
+        * kernel_cap_t structure with an array of __u32 values, but this
+        * was then fixed to have a single __u64 value.  There are accessor
+        * functions for the old kernel_cap_t but since that is now dead code
+        * it isn't worthwhile to jump through hoops for compatibility for it.
+        */
+       return ((u64)cap.cap[1] << 32) | cap.cap[0];
+#else
+       return cap.val;
+#endif
+}
+
+/* convert an integer into a capabilityt */
+static inline kernel_cap_t mdt_num2cap(u64 num)
+{
+       kernel_cap_t cap;
+
+#ifdef CAP_FOR_EACH_U32
+       cap.cap[0] = num;
+       cap.cap[1] = (num >> 32);
+#else
+       cap.val = num;
+#endif
+
+       return cap;
+}
+
 /* We forbid operations from encryption-unaware clients if they try to
  * manipulate encrypted files/directories.
  */
index f92e1e2..e848540 100644 (file)
@@ -589,23 +589,64 @@ mdt_nosquash_nids_seq_write(struct file *file, const char __user *buffer,
 }
 LPROC_SEQ_FOPS(mdt_nosquash_nids);
 
+static const char *mdt_cap2str(int cap)
+{
+       /* We don't allow using all capabilities, but the fields must exist.
+        * The supported capabilities are CAP_FS_SET and CAP_NFSD_SET, plus
+        * CAP_SYS_ADMIN for a bunch of HSM operations (that should be fixed).
+        */
+       static const char *const capability_names[] = {
+               "cap_chown",                    /*  0 */
+               "cap_dac_override",             /*  1 */
+               "cap_dac_read_search",          /*  2 */
+               "cap_fowner",                   /*  3 */
+               "cap_fsetid",                   /*  4 */
+               NULL,                           /*  5 */
+               NULL,                           /*  6 */
+               NULL,                           /*  7 */
+               NULL,                           /*  8 */
+               "cap_linux_immutable",          /*  9 */
+               NULL,                           /* 10 */
+               NULL,                           /* 11 */
+               NULL,                           /* 12 */
+               NULL,                           /* 13 */
+               NULL,                           /* 14 */
+               NULL,                           /* 15 */
+               NULL,                           /* 16 */
+               NULL,                           /* 17 */
+               NULL,                           /* 18 */
+               NULL,                           /* 19 */
+               NULL,                           /* 20 */
+               /* we should use more precise capabilities than this */
+               "cap_sys_admin",                /* 21 */
+               NULL,                           /* 22 */
+               NULL,                           /* 23 */
+               "cap_sys_resource",             /* 24 */
+               NULL,                           /* 25 */
+               NULL,                           /* 26 */
+               "cap_mknod",                    /* 27 */
+               NULL,                           /* 28 */
+               NULL,                           /* 29 */
+               NULL,                           /* 30 */
+               NULL,                           /* 31 */
+               "cap_mac_override",             /* 32 */
+       };
+
+       if (cap >= ARRAY_SIZE(capability_names))
+               return NULL;
+
+       return capability_names[cap];
+}
+
 static ssize_t enable_cap_mask_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);
-       u64 cap;
-
-       BUILD_BUG_ON(_KERNEL_CAP_T_SIZE != sizeof(u64));
+       u64 mask = mdt_cap2num(mdt->mdt_enable_cap_mask);
 
-#ifdef CAP_FOR_EACH_U32 /* kernels before v6.2-13111-gf122a08b197d */
-       cap = ((u64)mdt->mdt_enable_cap_mask.cap[1] << 32) |
-              mdt->mdt_enable_cap_mask.cap[0];
-#else
-       cap = mdt->mdt_enable_cap_mask.val;
-#endif
-       return scnprintf(buf, PAGE_SIZE, "%#0llx\n", cap);
+       return cfs_mask2str(buf, PAGE_SIZE, mask, mdt_cap2str, ',');
 }
 
 static ssize_t enable_cap_mask_store(struct kobject *kobj,
@@ -615,24 +656,28 @@ static ssize_t enable_cap_mask_store(struct kobject *kobj,
        struct obd_device *obd = container_of(kobj, struct obd_device,
                                              obd_kset.kobj);
        struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+       static kernel_cap_t allowed_cap = CAP_EMPTY_SET;
        unsigned long long val;
        int rc;
 
        rc = kstrtoull(buffer, 0, &val);
+       if (rc == -EINVAL) {
+               u64 cap = mdt_cap2num(mdt->mdt_enable_cap_mask);
+
+               /* the "allmask" is filtered by allowed_mask below */
+               rc = cfs_str2mask(buffer, mdt_cap2str, &cap, 0, ~0ULL, 0);
+               val = cap;
+       }
        if (rc)
-               /* should also accept symbolic names via cfs_str2mask() */
                return rc;
 
-#ifdef CAP_FOR_EACH_U32
-       mdt->mdt_enable_cap_mask.cap[0] = val &
-               (CAP_FS_MASK_B0 | CAP_TO_MASK(CAP_SYS_RESOURCE) |
-                CAP_TO_MASK(CAP_LINUX_IMMUTABLE));
-       mdt->mdt_enable_cap_mask.cap[1] = (val >> 32) & CAP_FS_MASK_B1;
-#else
-       mdt->mdt_enable_cap_mask.val = val &
-               (CAP_FS_MASK | BIT_ULL(CAP_SYS_RESOURCE) |
-                BIT_ULL(CAP_LINUX_IMMUTABLE));
-#endif
+       /* All of the capabilities that we currently allow/check */
+       if (unlikely(cap_isclear(allowed_cap))) {
+               allowed_cap = CAP_FS_SET;
+               cap_raise(allowed_cap, CAP_SYS_RESOURCE);
+       }
+
+       mdt->mdt_enable_cap_mask = cap_intersect(mdt_num2cap(val), allowed_cap);
 
        return count;
 }
index ff65865..659a110 100755 (executable)
@@ -4391,7 +4391,10 @@ test_51() {
 
        old_cap=($(do_nodes $mdts $LCTL get_param -n $cap_param 2>/dev/null))
        if [[ -n "$old_cap" ]]; then
-               do_nodes $mdts $LCTL set_param $cap_param=0xf
+               local new_cap="+cap_chown+cap_fowner+cap_dac_override+cap_dac_read_search"
+               (( $MDS1_VERSION > $(version_code 2.14.0.135) )) || new_cap=0xf
+               echo "old_cap: $old_cap new_cap: $new_cap"
+               do_nodes $mdts $LCTL set_param $cap_param=$new_cap
                stack_trap "do_nodes $mdts $LCTL set_param $cap_param=$old_cap"
        fi