staging: lustre: llite: fix inconsistencies of root squash feature
authorGregoire Pichon <gregoire.pichon@bull.net>
Tue, 16 Aug 2016 20:18:56 +0000 (16:18 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 21 Aug 2016 13:57:36 +0000 (15:57 +0200)
Root squash exhibits inconsistent behaviour on a client when
enabled. If a file is not cached on the client, then root will get
a permission denied error when accessing the file. When
the file has recently been accessed by a regular user and is
still in cache, root will be able to access the file without error
because the permission check is only done by the client that
isn't aware of root squash.

While the only real security benefit from root squash is to deny
clients access to files owned by root itself, it also makes sense
to treat file access on the client in a consistent manner
regardless of whether the file is in cache or not.

This patch adds root squash settings to llite so that client is able
to apply root squashing when it is relevant.

Configuration of MDT root squash settings will automatically be
applied to llite config log as well.

Update cfs_str2num_check() routine by removing any modification
of the specified string parameter. Since string can come from ls_str
field of a lstr structure, this avoids inconsistent ls_len field.

Signed-off-by: Gregoire Pichon <gregoire.pichon@bull.net>
Intel-bug-id: https://jira.hpdd.intel.com/browse/LU-1778
Reviewed-on: http://review.whamcloud.com/5700
Reviewed-by: Andreas Dilger <andreas.dilger@intel.com>
Reviewed-by: Niu Yawei <yawei.niu@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
Signed-off-by: James Simmons <jsimmons@infradead.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/lustre/lnet/libcfs/libcfs_string.c
drivers/staging/lustre/lustre/include/lprocfs_status.h
drivers/staging/lustre/lustre/include/obd_class.h
drivers/staging/lustre/lustre/llite/file.c
drivers/staging/lustre/lustre/llite/llite_internal.h
drivers/staging/lustre/lustre/llite/llite_lib.c
drivers/staging/lustre/lustre/llite/lproc_llite.c
drivers/staging/lustre/lustre/obdclass/lprocfs_status.c

index fc697cdfcdaf678ab55704acf263375486da840f..56a614d7713b1d81afd12974034b3f62caa8120e 100644 (file)
@@ -229,8 +229,6 @@ cfs_str2num_check(char *str, int nob, unsigned *num,
        char *endp, cache;
        int rc;
 
-       str = cfs_trimwhite(str);
-
        /**
         * kstrouint can only handle strings composed
         * of only numbers. We need to scan the string
index d68e60e7fef73250993ead48bec64201e38b4075..ff35e6302d05bf1d9e2bf2ba8cb06a8b9e33c20e 100644 (file)
@@ -681,6 +681,12 @@ static struct lustre_attr lustre_attr_##name = __ATTR(name, mode, show, store)
 
 extern const struct sysfs_ops lustre_sysfs_ops;
 
+struct root_squash_info;
+int lprocfs_wr_root_squash(const char *buffer, unsigned long count,
+                          struct root_squash_info *squash, char *name);
+int lprocfs_wr_nosquash_nids(const char *buffer, unsigned long count,
+                            struct root_squash_info *squash, char *name);
+
 /* all quota proc functions */
 int lprocfs_quota_rd_bunit(char *page, char **start,
                           loff_t off, int count,
index a288995d46e4cd83bdad5bca9a344346f674b7b0..e86961c40928cab83bacbb2aaca12ab0a90f6232 100644 (file)
@@ -1759,4 +1759,13 @@ extern spinlock_t obd_types_lock;
 /* prng.c */
 #define ll_generate_random_uuid(uuid_out) cfs_get_random_bytes(uuid_out, sizeof(class_uuid_t))
 
+/* root squash info */
+struct rw_semaphore;
+struct root_squash_info {
+       uid_t                   rsi_uid;
+       gid_t                   rsi_gid;
+       struct list_head        rsi_nosquash_nids;
+       struct rw_semaphore     rsi_sem;
+};
+
 #endif /* __LINUX_OBD_CLASS_H */
index 519db53564f36114ba8a51b98ffa7401c2bb9cb4..90a717096c7056627f57a19c293eb4fdd3ffe769 100644 (file)
@@ -41,6 +41,7 @@
 #include "../include/lustre_lite.h"
 #include <linux/pagemap.h>
 #include <linux/file.h>
+#include <linux/sched.h>
 #include <linux/mount.h>
 #include "llite_internal.h"
 #include "../include/lustre/ll_fiemap.h"
@@ -3289,6 +3290,12 @@ struct posix_acl *ll_get_acl(struct inode *inode, int type)
 
 int ll_inode_permission(struct inode *inode, int mask)
 {
+       struct ll_sb_info *sbi;
+       struct root_squash_info *squash;
+       const struct cred *old_cred = NULL;
+       struct cred *cred = NULL;
+       bool squash_id = false;
+       cfs_cap_t cap;
        int rc = 0;
 
        if (mask & MAY_NOT_BLOCK)
@@ -3308,9 +3315,46 @@ int ll_inode_permission(struct inode *inode, int mask)
        CDEBUG(D_VFSTRACE, "VFS Op:inode="DFID"(%p), inode mode %x mask %o\n",
               PFID(ll_inode2fid(inode)), inode, inode->i_mode, mask);
 
+       /* squash fsuid/fsgid if needed */
+       sbi = ll_i2sbi(inode);
+       squash = &sbi->ll_squash;
+       if (unlikely(squash->rsi_uid &&
+                    uid_eq(current_fsuid(), GLOBAL_ROOT_UID) &&
+                    !(sbi->ll_flags & LL_SBI_NOROOTSQUASH))) {
+               squash_id = true;
+       }
+
+       if (squash_id) {
+               CDEBUG(D_OTHER, "squash creds (%d:%d)=>(%d:%d)\n",
+                      __kuid_val(current_fsuid()), __kgid_val(current_fsgid()),
+                      squash->rsi_uid, squash->rsi_gid);
+
+               /*
+                * update current process's credentials
+                * and FS capability
+                */
+               cred = prepare_creds();
+               if (!cred)
+                       return -ENOMEM;
+
+               cred->fsuid = make_kuid(&init_user_ns, squash->rsi_uid);
+               cred->fsgid = make_kgid(&init_user_ns, squash->rsi_gid);
+               for (cap = 0; cap < sizeof(cfs_cap_t) * 8; cap++) {
+                       if ((1 << cap) & CFS_CAP_FS_MASK)
+                               cap_lower(cred->cap_effective, cap);
+               }
+               old_cred = override_creds(cred);
+       }
+
        ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_INODE_PERM, 1);
        rc = generic_permission(inode, mask);
 
+       /* restore current process's credentials and FS capability */
+       if (squash_id) {
+               revert_creds(old_cred);
+               put_cred(cred);
+       }
+
        return rc;
 }
 
index e101dd83d9092c28582f642ad8090e1ac05fa9a6..500b5ec2d932ac39876cfc6016416501a1369035 100644 (file)
@@ -412,6 +412,7 @@ enum stats_track_type {
 #define LL_SBI_LAYOUT_LOCK    0x20000 /* layout lock support */
 #define LL_SBI_USER_FID2PATH  0x40000 /* allow fid2path by unprivileged users */
 #define LL_SBI_XATTR_CACHE    0x80000 /* support for xattr cache */
+#define LL_SBI_NOROOTSQUASH    0x100000 /* do not apply root squash */
 
 #define LL_SBI_FLAGS { \
        "nolck",        \
@@ -434,6 +435,7 @@ enum stats_track_type {
        "layout",       \
        "user_fid2path",\
        "xattr",        \
+       "norootsquash", \
 }
 
 struct ll_sb_info {
@@ -500,6 +502,9 @@ struct ll_sb_info {
        dev_t                     ll_sdev_orig; /* save s_dev before assign for
                                                 * clustered nfs
                                                 */
+       /* root squash */
+       struct root_squash_info   ll_squash;
+
        __kernel_fsid_t           ll_fsid;
        struct kobject           ll_kobj; /* sysfs object */
        struct super_block      *ll_sb; /* struct super_block (for sysfs code)*/
@@ -798,6 +803,7 @@ struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data,
 void ll_finish_md_op_data(struct md_op_data *op_data);
 int ll_get_obd_name(struct inode *inode, unsigned int cmd, unsigned long arg);
 char *ll_get_fsname(struct super_block *sb, char *buf, int buflen);
+void ll_compute_rootsquash_state(struct ll_sb_info *sbi);
 void ll_open_cleanup(struct super_block *sb, struct ptlrpc_request *open_req);
 
 /* llite/llite_nfs.c */
index a3b4c9767e876f602506f500716969369b2eb4b8..0a289252cd504c9da4875b25b419639dc7d6a3c4 100644 (file)
@@ -119,6 +119,12 @@ static struct ll_sb_info *ll_init_sbi(struct super_block *sb)
        atomic_set(&sbi->ll_agl_total, 0);
        sbi->ll_flags |= LL_SBI_AGL_ENABLED;
 
+       /* root squash */
+       sbi->ll_squash.rsi_uid = 0;
+       sbi->ll_squash.rsi_gid = 0;
+       INIT_LIST_HEAD(&sbi->ll_squash.rsi_nosquash_nids);
+       init_rwsem(&sbi->ll_squash.rsi_sem);
+
        sbi->ll_sb = sb;
 
        return sbi;
@@ -129,6 +135,8 @@ static void ll_free_sbi(struct super_block *sb)
        struct ll_sb_info *sbi = ll_s2sbi(sb);
 
        if (sbi->ll_cache) {
+               if (!list_empty(&sbi->ll_squash.rsi_nosquash_nids))
+                       cfs_free_nidlist(&sbi->ll_squash.rsi_nosquash_nids);
                cl_cache_decref(sbi->ll_cache);
                sbi->ll_cache = NULL;
        }
@@ -2496,3 +2504,42 @@ void ll_dirty_page_discard_warn(struct page *page, int ioret)
        if (buf)
                free_page((unsigned long)buf);
 }
+
+/*
+ * Compute llite root squash state after a change of root squash
+ * configuration setting or add/remove of a lnet nid
+ */
+void ll_compute_rootsquash_state(struct ll_sb_info *sbi)
+{
+       struct root_squash_info *squash = &sbi->ll_squash;
+       lnet_process_id_t id;
+       bool matched;
+       int i;
+
+       /* Update norootsquash flag */
+       down_write(&squash->rsi_sem);
+       if (list_empty(&squash->rsi_nosquash_nids)) {
+               sbi->ll_flags &= ~LL_SBI_NOROOTSQUASH;
+       } else {
+               /*
+                * Do not apply root squash as soon as one of our NIDs is
+                * in the nosquash_nids list
+                */
+               matched = false;
+               i = 0;
+
+               while (LNetGetId(i++, &id) != -ENOENT) {
+                       if (LNET_NETTYP(LNET_NIDNET(id.nid)) == LOLND)
+                               continue;
+                       if (cfs_match_nid(id.nid, &squash->rsi_nosquash_nids)) {
+                               matched = true;
+                               break;
+                       }
+               }
+               if (matched)
+                       sbi->ll_flags |= LL_SBI_NOROOTSQUASH;
+               else
+                       sbi->ll_flags &= ~LL_SBI_NOROOTSQUASH;
+       }
+       up_write(&squash->rsi_sem);
+}
index e86bf3c53be3722e076a67c9314994902e280456..2f1f3897d8e1abea5da21e4227e2194622c9cd64 100644 (file)
@@ -833,6 +833,71 @@ static ssize_t unstable_stats_show(struct kobject *kobj,
 }
 LUSTRE_RO_ATTR(unstable_stats);
 
+static ssize_t root_squash_show(struct kobject *kobj, struct attribute *attr,
+                               char *buf)
+{
+       struct ll_sb_info *sbi = container_of(kobj, struct ll_sb_info,
+                                             ll_kobj);
+       struct root_squash_info *squash = &sbi->ll_squash;
+
+       return sprintf(buf, "%u:%u\n", squash->rsi_uid, squash->rsi_gid);
+}
+
+static ssize_t root_squash_store(struct kobject *kobj, struct attribute *attr,
+                                const char *buffer, size_t count)
+{
+       struct ll_sb_info *sbi = container_of(kobj, struct ll_sb_info,
+                                             ll_kobj);
+       struct root_squash_info *squash = &sbi->ll_squash;
+
+       return lprocfs_wr_root_squash(buffer, count, squash,
+                                     ll_get_fsname(sbi->ll_sb, NULL, 0));
+}
+LUSTRE_RW_ATTR(root_squash);
+
+static int ll_nosquash_nids_seq_show(struct seq_file *m, void *v)
+{
+       struct super_block *sb = m->private;
+       struct ll_sb_info *sbi = ll_s2sbi(sb);
+       struct root_squash_info *squash = &sbi->ll_squash;
+       int len;
+
+       down_read(&squash->rsi_sem);
+       if (!list_empty(&squash->rsi_nosquash_nids)) {
+               len = cfs_print_nidlist(m->buf + m->count, m->size - m->count,
+                                       &squash->rsi_nosquash_nids);
+               m->count += len;
+               seq_puts(m, "\n");
+       } else {
+               seq_puts(m, "NONE\n");
+       }
+       up_read(&squash->rsi_sem);
+
+       return 0;
+}
+
+static ssize_t ll_nosquash_nids_seq_write(struct file *file,
+                                         const char __user *buffer,
+                                         size_t count, loff_t *off)
+{
+       struct seq_file *m = file->private_data;
+       struct super_block *sb = m->private;
+       struct ll_sb_info *sbi = ll_s2sbi(sb);
+       struct root_squash_info *squash = &sbi->ll_squash;
+       int rc;
+
+       rc = lprocfs_wr_nosquash_nids(buffer, count, squash,
+                                     ll_get_fsname(sb, NULL, 0));
+       if (rc < 0)
+               return rc;
+
+       ll_compute_rootsquash_state(sbi);
+
+       return rc;
+}
+
+LPROC_SEQ_FOPS(ll_nosquash_nids);
+
 static struct lprocfs_vars lprocfs_llite_obd_vars[] = {
        /* { "mntpt_path",   ll_rd_path,             0, 0 }, */
        { "site",         &ll_site_stats_fops,    NULL, 0 },
@@ -840,6 +905,8 @@ static struct lprocfs_vars lprocfs_llite_obd_vars[] = {
        { "max_cached_mb",    &ll_max_cached_mb_fops, NULL },
        { "statahead_stats",  &ll_statahead_stats_fops, NULL, 0 },
        { "sbi_flags",        &ll_sbi_flags_fops, NULL, 0 },
+       { .name =               "nosquash_nids",
+         .fops =               &ll_nosquash_nids_fops          },
        { NULL }
 };
 
@@ -869,6 +936,7 @@ static struct attribute *llite_attrs[] = {
        &lustre_attr_default_easize.attr,
        &lustre_attr_xattr_cache.attr,
        &lustre_attr_unstable_stats.attr,
+       &lustre_attr_root_squash.attr,
        NULL,
 };
 
index 279b625f1afe468d2396d2aa1f04c0d16a1487d0..c83d28e7e763c154c5713df7fb186422898dec89 100644 (file)
@@ -1547,6 +1547,146 @@ void lprocfs_oh_clear(struct obd_histogram *oh)
 }
 EXPORT_SYMBOL(lprocfs_oh_clear);
 
+int lprocfs_wr_root_squash(const char __user *buffer, unsigned long count,
+                          struct root_squash_info *squash, char *name)
+{
+       char kernbuf[64], *tmp, *errmsg;
+       unsigned long uid, gid;
+       int rc;
+
+       if (count >= sizeof(kernbuf)) {
+               errmsg = "string too long";
+               rc = -EINVAL;
+               goto failed_noprint;
+       }
+       if (copy_from_user(kernbuf, buffer, count)) {
+               errmsg = "bad address";
+               rc = -EFAULT;
+               goto failed_noprint;
+       }
+       kernbuf[count] = '\0';
+
+       /* look for uid gid separator */
+       tmp = strchr(kernbuf, ':');
+       if (!tmp) {
+               errmsg = "needs uid:gid format";
+               rc = -EINVAL;
+               goto failed;
+       }
+       *tmp = '\0';
+       tmp++;
+
+       /* parse uid */
+       if (kstrtoul(kernbuf, 0, &uid) != 0) {
+               errmsg = "bad uid";
+               rc = -EINVAL;
+               goto failed;
+       }
+       /* parse gid */
+       if (kstrtoul(tmp, 0, &gid) != 0) {
+               errmsg = "bad gid";
+               rc = -EINVAL;
+               goto failed;
+       }
+
+       squash->rsi_uid = uid;
+       squash->rsi_gid = gid;
+
+       LCONSOLE_INFO("%s: root_squash is set to %u:%u\n",
+                     name, squash->rsi_uid, squash->rsi_gid);
+       return count;
+
+failed:
+       if (tmp) {
+               tmp--;
+               *tmp = ':';
+       }
+       CWARN("%s: failed to set root_squash to \"%s\", %s, rc = %d\n",
+             name, kernbuf, errmsg, rc);
+       return rc;
+failed_noprint:
+       CWARN("%s: failed to set root_squash due to %s, rc = %d\n",
+             name, errmsg, rc);
+       return rc;
+}
+EXPORT_SYMBOL(lprocfs_wr_root_squash);
+
+int lprocfs_wr_nosquash_nids(const char __user *buffer, unsigned long count,
+                            struct root_squash_info *squash, char *name)
+{
+       char *kernbuf = NULL, *errmsg;
+       struct list_head tmp;
+       int len = count;
+       int rc;
+
+       if (count > 4096) {
+               errmsg = "string too long";
+               rc = -EINVAL;
+               goto failed;
+       }
+
+       kernbuf = kzalloc(count + 1, GFP_NOFS);
+       if (!kernbuf) {
+               errmsg = "no memory";
+               rc = -ENOMEM;
+               goto failed;
+       }
+
+       if (copy_from_user(kernbuf, buffer, count)) {
+               errmsg = "bad address";
+               rc = -EFAULT;
+               goto failed;
+       }
+       kernbuf[count] = '\0';
+
+       if (count > 0 && kernbuf[count - 1] == '\n')
+               len = count - 1;
+
+       if ((len == 4 && !strncmp(kernbuf, "NONE", len)) ||
+           (len == 5 && !strncmp(kernbuf, "clear", len))) {
+               /* empty string is special case */
+               down_write(&squash->rsi_sem);
+               if (!list_empty(&squash->rsi_nosquash_nids))
+                       cfs_free_nidlist(&squash->rsi_nosquash_nids);
+               up_write(&squash->rsi_sem);
+               LCONSOLE_INFO("%s: nosquash_nids is cleared\n", name);
+               kfree(kernbuf);
+               return count;
+       }
+
+       INIT_LIST_HEAD(&tmp);
+       if (cfs_parse_nidlist(kernbuf, count, &tmp) <= 0) {
+               errmsg = "can't parse";
+               rc = -EINVAL;
+               goto failed;
+       }
+       LCONSOLE_INFO("%s: nosquash_nids set to %s\n",
+                     name, kernbuf);
+       kfree(kernbuf);
+       kernbuf = NULL;
+
+       down_write(&squash->rsi_sem);
+       if (!list_empty(&squash->rsi_nosquash_nids))
+               cfs_free_nidlist(&squash->rsi_nosquash_nids);
+       list_splice(&tmp, &squash->rsi_nosquash_nids);
+       up_write(&squash->rsi_sem);
+
+       return count;
+
+failed:
+       if (kernbuf) {
+               CWARN("%s: failed to set nosquash_nids to \"%s\", %s rc = %d\n",
+                     name, kernbuf, errmsg, rc);
+               kfree(kernbuf);
+               kernbuf = NULL;
+       } else {
+               CWARN("%s: failed to set nosquash_nids due to %s rc = %d\n",
+                     name, errmsg, rc);
+       }
+       return rc;
+}
+EXPORT_SYMBOL(lprocfs_wr_nosquash_nids);
+
 static ssize_t lustre_attr_show(struct kobject *kobj,
                                struct attribute *attr, char *buf)
 {