[Yaffs] [PATCH] Add security namespace xattr support

Top Page
Attachments:
Message as email
+ (text/plain)
Delete this message
Reply to this message
Author: Liao Hua
Date:  
To: yaffs, cdhmanning
CC: wangfangpeng1, chenjie6
Subject: [Yaffs] [PATCH] Add security namespace xattr support
From: liaohua <>

This patch provides security namespace xattr support for Yaffs2.

Adjust the yaffs2 to use the generic xattr handler
by replace yaffs_setxattr with generic_setxattr in yaffs_file_inode_operations.
And we add yaffs_security_xattr_handler for security namespace xattr support.

We have tested the basic functions, for example, set and get the capabilities of some file like ping.
And a file named security_xattr_ping.sh is provided as a test script.

Thanks in advance for any advice you can provide.

Signed-off-by: liaohua <>
---
 Makefile                           |   1 +
 Makefile.kernel                    |   1 +
 linux-tests/security_xattr_ping.sh | 151 +++++++++++++++++++++++++++++
 yaffs_guts.h                       |   4 +
 yaffs_nameval.c                    |  39 ++++----
 yaffs_security.c                   |  87 +++++++++++++++++
 yaffs_vfs_multi.c                  | 119 ++++++++++++++++++-----
 yaffs_xattr.h                      |  40 ++++++++
 8 files changed, 398 insertions(+), 44 deletions(-)
 create mode 100644 linux-tests/security_xattr_ping.sh
 create mode 100644 yaffs_security.c
 create mode 100644 yaffs_xattr.h


diff --git a/Makefile b/Makefile
index 9318d4d..509c306 100644
--- a/Makefile
+++ b/Makefile
@@ -55,6 +55,7 @@ ifneq ($(KERNELRELEASE),)
     yaffs2multi-objs += yaffs_verify.o
     yaffs2multi-objs += yaffs_endian.o
     yaffs2multi-objs += yaffs_summary.o
+    yaffs2multi-objs += yaffs_security.o


 else
     KERNELDIR ?= /lib/modules/$(shell uname -r)/build
diff --git a/Makefile.kernel b/Makefile.kernel
index c052395..e842883 100644
--- a/Makefile.kernel
+++ b/Makefile.kernel
@@ -16,4 +16,5 @@ yaffs-y += yaffs_yaffs2.o
 yaffs-y += yaffs_bitmap.o
 yaffs-y += yaffs_summary.o
 yaffs-y += yaffs_verify.o
+yaffs-y += yaffs_security.o


diff --git a/linux-tests/security_xattr_ping.sh b/linux-tests/security_xattr_ping.sh
new file mode 100644
index 0000000..42a9fd1
--- /dev/null
+++ b/linux-tests/security_xattr_ping.sh
@@ -0,0 +1,151 @@
+#!/bin/bash
+
+pre_test() {
+    mount | grep yaffs2
+    if [ $? -ne 0 ]; then
+        echo "ERROR: no yaffs2 filesystem was mounted."
+        return 1
+    fi
+    yaffs_dir=$(mount | grep yaffs2 | awk -F " " '{print $3}')
+    echo $yaffs_dir
+
+    which ping
+    if [ $? -ne 0 ]; then
+        echo "ERROR: no ping exist."
+        return 1
+    fi
+    ping_dir=$(which ping)
+    echo $ping_dir
+}
+
+setcap_net_ping_test() {
+    cd $yaffs_dir
+    if [ $? -ne 0 ]; then
+        echo "Error: yaffs2 dir not fount"
+        return 1
+    fi
+
+    cp $ping_dir ./
+    chmod 755 ping
+    ls -al ping | grep rws
+    if [ $? -eq 0 ]; then
+        chmod -s ping
+    fi
+
+    ./ping 127.0.0.1 -c 3
+    if [ $? -ne 0 ]; then
+        echo "ERROR: ping 127.0.0.1 fail"
+        return 1
+    fi
+
+    # Add normal user;
+    useradd -m yaffs_test
+    echo "Yaffs2 xattr test: su yaffs_test and ping again."
+    su yaffs_test -c "cd $yaffs_dir && ./ping 127.0.0.1 -c 3"
+    if [ $? -eq 0 ]; then
+        echo "ERROR: yaffs_test ping 127.0.0.1 success, test fail"
+        return 1
+    fi
+
+    setcap 'cap_net_admin,cap_net_raw+ep' ./ping
+    getcap ./ping | grep "cap_net_admin,cap_net_raw+ep"
+    if [ $? -ne 0 ]; then
+        echo "ERROR: set cap fail. test fail"
+        return 1
+    fi
+
+    attr -l ./ping | grep '"capability" has a 20 byte'
+    if [ $? -ne 0 ]; then
+        echo "ERROR: setfattr cap fail. test fail"
+        return 1
+    fi
+
+    echo "Yaffs2 xattr test: setcap of ping, su yaffs_test and ping again."
+    su yaffs_test -c "cd $yaffs_dir && ./ping 127.0.0.1 -c 3"
+    if [ $? -ne 0 ]; then
+        echo "ERROR: yaffs_test ping 127.0.0.1 fail,add cap fail, test fail"
+        return 1
+    fi
+
+    rm ping
+    echo "Yaffs2 xattr test: setcap_net_ping_test success."
+}
+
+setfattr_net_ping_test() {
+    cd $yaffs_dir
+    if [ $? -ne 0 ]; then
+        echo "Error: yaffs2 dir not fount"
+        return 1
+    fi
+
+    cp $ping_dir ./
+    chmod 755 ping
+    ls -al ping | grep rws
+    if [ $? -eq 0 ]; then
+        chmod -s ping
+    fi
+
+    ./ping 127.0.0.1 -c 3
+    if [ $? -ne 0 ]; then
+        echo "ERROR: ping 127.0.0.1 fail"
+        return 1
+    fi
+
+    # set cap_net_admin,cap_net_raw+ep of ping by setfattr, check if getcap gets the attribute correctly.;
+    setfattr -n security.capability -v 0sAQAAAgAwAAAAAAAAAAAAAAAAAAA= ./ping
+    getfattr -n security.capability ./ping | grep "security.capability=0sAQAAAgAwAAAAAAAAAAAAAAAAAAA="
+    if [ $? -ne 0 ]; then
+        echo "ERROR: getfattr fail. test fail"
+        return 1
+    fi
+
+    getcap ./ping | grep "cap_net_admin,cap_net_raw+ep"
+    if [ $? -ne 0 ]; then
+        echo "ERROR: setfattr cap fail. test fail"
+        return 1
+    fi
+
+    rm ping
+    echo "Yaffs2 xattr test: setfattr_net_ping_test success."
+}
+
+getfattr_net_ping_test() {
+    cd $yaffs_dir
+    if [ $? -ne 0 ]; then
+        echo "Error: yaffs2 dir not fount"
+        return 1
+    fi
+
+    cp $ping_dir ./
+    chmod 755 ping
+    ls -al ping | grep rws
+    if [ $? -eq 0 ]; then
+        chmod -s ping
+    fi
+
+    ./ping 127.0.0.1 -c 3
+    if [ $? -ne 0 ]; then
+        echo "ERROR: ping 127.0.0.1 fail"
+        return 1
+    fi
+
+    # Set cap_net_admin,cap_net_raw+ep of ping, check if getfattr gets the attribute correctly.
+    setcap 'cap_net_admin,cap_net_raw+ep' ./ping
+    getcap ./ping | grep net_admin
+    if [ $? -ne 0 ]; then
+        echo "ERROR: set cap fail. test fail"
+    fi
+
+    getfattr -n security.capability ./ping | grep "security.capability=0sAQAAAgAwAAAAAAAAAAAAAAAAAAA="
+    if [ $? -ne 0 ]; then
+        echo "ERROR: getfattr fail. test fail"
+    fi
+
+    rm ping
+    echo "Yaffs2 xattr test: security_xattr_ping success."
+}
+
+pre_test || return 1
+setcap_net_ping_test || return 1
+setfattr_net_ping_test || return 1
+getfattr_net_ping_test || return 1
diff --git a/yaffs_guts.h b/yaffs_guts.h
index 124e4c9..e34ca27 100644
--- a/yaffs_guts.h
+++ b/yaffs_guts.h
@@ -15,6 +15,7 @@
 #ifndef __YAFFS_GUTS_H__
 #define __YAFFS_GUTS_H__


+#include <linux/xattr.h>
#include "yportenv.h"

 #define YAFFS_OK    1
@@ -956,6 +957,9 @@ int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name,
               const void *value, int size, int flags);
 int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value,
               int size);
+
+const struct xattr_handler *yaffs_xprefix_to_handler(const char *name);
+
 int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size);
 int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name);


diff --git a/yaffs_nameval.c b/yaffs_nameval.c
index b855365..862be08 100644
--- a/yaffs_nameval.c
+++ b/yaffs_nameval.c
@@ -171,7 +171,8 @@ int nval_get(struct yaffs_dev *dev,
             return size;


         if (size <= bsize) {
-            memcpy(buf, xb + pos, size);
+            if (buf)
+                memcpy(buf, xb + pos, size);
             return size;
         }
     }
@@ -187,38 +188,40 @@ int nval_list(struct yaffs_dev *dev, const char *xb, int xb_size, char *buf, int
     s32 size;
     int name_len;
     int ncopied = 0;
-    int filled = 0;
+    const struct xattr_handler *handler;
+    int onecopy;


     memcpy(&size, xb + pos, sizeof(size));
     yaffs_do_endian_s32(dev, &size);


     while (size > (int)(sizeof(size)) &&
         size <= xb_size &&
-        (pos + size) < xb_size &&
-        !filled) {
+        (pos + size) < xb_size) {
         pos += sizeof(size);
         size -= sizeof(size);
-        name_len = strnlen((YCHAR *) (xb + pos), size);
-        if (ncopied + name_len + 1 < bsize) {
-            memcpy(buf, xb + pos, name_len * sizeof(YCHAR));
-            buf += name_len;
-            *buf = '\0';
-            buf++;
-            if (sizeof(YCHAR) > 1) {
-                *buf = '\0';
-                buf++;
+        name_len = strnlen(xb + pos, size);
+
+        handler = yaffs_xprefix_to_handler(xb + pos);
+        if (handler) {
+            onecopy = handler->list(handler, NULL,
+                        buf, bsize,
+                        xb + pos, name_len);
+            /* check if it is a size query */
+            if (buf) {
+                if (onecopy + ncopied > bsize)
+                    return -ERANGE;
+                buf += onecopy;
             }
-            ncopied += (name_len + 1);
-        } else {
-            filled = 1;
+            ncopied += onecopy;
         }
+
         pos += size;
         if (pos < (int)(xb_size - sizeof(size))) {
             memcpy(&size, xb + pos, sizeof(size));
             yaffs_do_endian_s32(dev, &size);
-        }
-        else
+        } else {
             size = 0;
+        }
     }
     return ncopied;
 }
diff --git a/yaffs_security.c b/yaffs_security.c
new file mode 100644
index 0000000..8917d4b
--- /dev/null
+++ b/yaffs_security.c
@@ -0,0 +1,87 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (c) Huawei Technologies Co., Ltd. 2019-2020.
+ * Description: add security xattr for yaffs2
+ * Author: wanglei <>,chenjie <>
+ * Create: 2019-6-12
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_xattr.h"
+#include <linux/errno.h>
+
+static int yaffs_init_xattrs(struct inode *inode,
+                const struct xattr *xattr_array, void *dentry)
+{
+    const struct xattr *xattr;
+    int err = 0;
+    struct yaffs_obj *obj = yaffs_security_inode_to_obj(inode);
+
+    for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+        err = yaffs_set_xattrib(obj, xattr->name,
+                    xattr->value, xattr->value_len, 0);
+        if (err <= 0)
+            break;
+    }
+    return err;
+}
+
+int yaffs_inode_init_security(struct dentry *dentry, struct inode *inode,
+            struct inode *dir, const struct qstr *qstr)
+{
+    return security_inode_init_security(inode, dir, qstr,
+                        &yaffs_init_xattrs, dentry);
+}
+
+
+/* security xattr handler implementions */
+
+static int yaffs_security_get(const struct xattr_handler *handler,
+            struct dentry *dentry,
+            const char *name,
+            void *buffer, size_t size)
+{
+    if (strcmp(name, "") == 0)
+        return -EINVAL;
+    return yaffs_getxattr(dentry, name, buffer, size);
+}
+
+static int yaffs_security_set(const struct xattr_handler *handler,
+            struct dentry *dentry, const char *name,
+            const void *buffer, size_t size, int flags)
+{
+    if (strcmp(name, "") == 0)
+        return -EINVAL;
+    return yaffs_setxattr(dentry, name, buffer, size, flags);
+}
+
+/*
+ * If the list is not NULL, put xattr name with prefix in list
+ * and return the length, otherwise just return the length
+ */
+static size_t yaffs_security_list(const struct xattr_handler *handler,
+                struct dentry *dentry,
+                char *list, size_t list_len,
+                const char *name, size_t name_len)
+{
+    size_t total_len = XATTR_SECURITY_PREFIX_LEN + name_len + 1;
+
+    if (list && total_len <= list_len) {
+        memcpy(list, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN);
+        memcpy(list + XATTR_SECURITY_PREFIX_LEN, name, name_len);
+        list[total_len - 1] = '\0';
+    }
+    return total_len;
+}
+
+
+const struct xattr_handler yaffs_security_xattr_handler = {
+    .prefix = XATTR_SECURITY_PREFIX,
+    .get = yaffs_security_get,
+    .set = yaffs_security_set,
+    .list = yaffs_security_list,
+};
diff --git a/yaffs_vfs_multi.c b/yaffs_vfs_multi.c
index 3044db7..73afa6d 100644
--- a/yaffs_vfs_multi.c
+++ b/yaffs_vfs_multi.c
@@ -88,6 +88,7 @@
 #include <linux/interrupt.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
+#include "yaffs_xattr.h"


#if (YAFFS_NEW_FOLLOW_LINK == 1)
#include <linux/namei.h>
@@ -952,12 +953,56 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
}

 #ifdef YAFFS_USE_XATTR
+const struct xattr_handler *yaffs2_xattr_handlers[] = {
+    &yaffs_security_xattr_handler,
+    NULL
+};
+
+/*
+ * Check if the name xattr is supported.
+ * Currently, Only security xattr are supported on yaffs Now!
+ * Because of the restriction of yaffs2 xattr storage structure
+ *       xattr_entry bytes = sizeof(size) + name + value
+ * we consider the xattr name with out dot(.) is security xattr
+*/
+const struct xattr_handler *yaffs_xprefix_to_handler(const char *name)
+{
+    char *dot = strchr(name, '.');
+
+    if (!dot)
+        return &yaffs_security_xattr_handler;
+
+    return NULL;
+}
+
+struct yaffs_obj *yaffs_security_inode_to_obj(struct inode *inode)
+{
+    return yaffs_inode_to_obj(inode);
+}
+
+int yaffs_init_security(struct dentry *dentry, struct inode *inode,
+            struct inode *dir,
+            struct yaffs_obj *obj, struct yaffs_dev *dev)
+{
+    int err = 0;
+
+    yaffs_gross_lock(dev);
+    err = yaffs_inode_init_security(dentry, inode, dir, &dentry->d_name);
+    if (err) {
+        yaffs_del_obj(obj);
+        err = -ENOMEM;
+    }
+    yaffs_gross_unlock(dev);
+
+    return err;
+}
+
 #if (YAFFS_NEW_XATTR > 0)
-static int yaffs_setxattr(struct dentry *dentry, struct inode *inode,
+int yaffs_setxattr(struct dentry *dentry, struct inode *inode,
         const char *name, const void *value, size_t size, int flags)
 {
 #else
-static int yaffs_setxattr(struct dentry *dentry, const char *name,
+int yaffs_setxattr(struct dentry *dentry, const char *name,
            const void *value, size_t size, int flags)
 {
     struct inode *inode = dentry->d_inode;
@@ -986,11 +1031,11 @@ static int yaffs_setxattr(struct dentry *dentry, const char *name,
 }


 #if (YAFFS_NEW_XATTR > 0)
-static ssize_t yaffs_getxattr(struct dentry * dentry, struct inode *inode,
-    const char *name, void *buff, size_t size)
+ssize_t yaffs_getxattr(struct dentry *dentry, struct inode *inode,
+            const char *name, void *buff, size_t size)
 {
 #else
-static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name,
+ssize_t yaffs_getxattr(struct dentry *dentry, const char *name,
             void *buff, size_t size)
 {
     struct inode *inode = dentry->d_inode;
@@ -1042,40 +1087,38 @@ static int yaffs_removexattr(struct dentry *dentry, const char *name)


     return error;
 }
-#endif


-static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size)
+static ssize_t yaffs_listxattr(struct dentry *dentry, char *buff, size_t size)
 {
     struct inode *inode = dentry->d_inode;
-    int error = 0;
+    int error;
     struct yaffs_dev *dev;
     struct yaffs_obj *obj = yaffs_inode_to_obj(inode);


     yaffs_trace(YAFFS_TRACE_OS,
         "yaffs_listxattr of object %d", obj->obj_id);


-    if (error == 0) {
-        dev = obj->my_dev;
-        yaffs_gross_lock(dev);
-        error = yaffs_list_xattrib(obj, buff, size);
-        yaffs_gross_unlock(dev);
+    dev = obj->my_dev;
+    yaffs_gross_lock(dev);
+    error = yaffs_list_xattrib(obj, buff, size);
+    yaffs_gross_unlock(dev);


-    }
     yaffs_trace(YAFFS_TRACE_OS,
         "yaffs_listxattr done returning %d", error);


     return error;
 }


+#endif /* YAFFS_USE_XATTR */

 static const struct inode_operations yaffs_file_inode_operations = {
     .setattr = yaffs_setattr,
 #ifdef YAFFS_USE_XATTR
-    .setxattr = yaffs_setxattr,
-    .getxattr = yaffs_getxattr,
-    .removexattr = yaffs_removexattr,
-#endif
+    .setxattr = generic_setxattr,
+    .getxattr = generic_getxattr,
+    .removexattr = generic_removexattr,
     .listxattr = yaffs_listxattr,
+#endif
 };



@@ -1200,11 +1243,11 @@ static const struct inode_operations yaffs_symlink_inode_operations = {
 #endif
     .setattr = yaffs_setattr,
 #ifdef YAFFS_USE_XATTR
-    .setxattr = yaffs_setxattr,
-    .getxattr = yaffs_getxattr,
-    .removexattr = yaffs_removexattr,
-#endif
+    .setxattr = generic_setxattr,
+    .getxattr = generic_getxattr,
+    .removexattr = generic_removexattr,
     .listxattr = yaffs_listxattr,
+#endif
 };


#ifdef YAFFS_USE_OWN_IGET
@@ -1407,6 +1450,15 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,

     if (obj) {
         inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
+
+#ifdef YAFFS_USE_XATTR
+        yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: init security");
+        if (yaffs_init_security(dentry, inode, dir, obj, dev) < 0) {
+            yaffs_trace(YAFFS_TRACE_OS,
+                "yaffs_mknod: init inode security failed");
+            return -ENOMEM;
+        }
+#endif
         d_instantiate(dentry, inode);
         update_dir_time(dir);
         yaffs_trace(YAFFS_TRACE_OS,
@@ -1570,6 +1622,15 @@ static int yaffs_symlink(struct inode *dir, struct dentry *dentry,
         struct inode *inode;


         inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj);
+
+#ifdef YAFFS_USE_XATTR
+        yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink: init security");
+        if (yaffs_init_security(dentry, inode, dir, obj, dev) < 0) {
+            yaffs_trace(YAFFS_TRACE_OS,
+                "yaffs_symlink: init inode security failed");
+            return -ENOMEM;
+        }
+#endif
         d_instantiate(dentry, inode);
         update_dir_time(dir);
         yaffs_trace(YAFFS_TRACE_OS, "symlink created OK");
@@ -1684,11 +1745,11 @@ static const struct inode_operations yaffs_dir_inode_operations = {
     .mknod = yaffs_mknod,
     .rename = yaffs_rename,
     .setattr = yaffs_setattr,
-    .listxattr = yaffs_listxattr,
 #ifdef YAFFS_USE_XATTR
-    .setxattr = yaffs_setxattr,
-    .getxattr = yaffs_getxattr,
-    .removexattr = yaffs_removexattr,
+    .setxattr = generic_setxattr,
+    .getxattr = generic_getxattr,
+    .removexattr = generic_removexattr,
+    .listxattr = yaffs_listxattr,
 #endif
 };


@@ -3025,6 +3086,12 @@ static struct super_block *yaffs_internal_read_super(int yaffs_version,

     dev->read_only = read_only;


+#ifdef YAFFS_USE_XATTR
+    yaffs_trace(YAFFS_TRACE_ALWAYS,
+        "yaffs_read_super: set s_xattr handlers for security xattr.");
+    sb->s_xattr = yaffs2_xattr_handlers;
+#endif
+
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
     sb->s_fs_info = dev;
 #else
diff --git a/yaffs_xattr.h b/yaffs_xattr.h
new file mode 100644
index 0000000..40dde54
--- /dev/null
+++ b/yaffs_xattr.h
@@ -0,0 +1,40 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (c) Huawei Technologies Co., Ltd. 2019-2020.
+ * Description: head of namespace xattr
+ * Author: wanglei <>,chenjie <>
+ * Create: 2019-6-12
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_XATTR_H__
+#define __YAFFS_XATTR_H__
+
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/mtd/mtd.h>
+#include <linux/security.h>
+#include "yaffs_guts.h"
+
+
+struct yaffs_obj *yaffs_security_inode_to_obj(struct inode *inode);
+
+ssize_t yaffs_getxattr(struct dentry *dentry, const char *name,
+            void *buff, size_t size);
+
+int yaffs_setxattr(struct dentry *dentry, const char *name,
+        const void *value, size_t size, int flags);
+
+int yaffs_inode_init_security(struct dentry *dentry, struct inode *inode,
+            struct inode *dir, const struct qstr *qstr);
+
+extern const struct xattr_handler yaffs_security_xattr_handler;
+
+#endif /* __YAFFS_XATTR_H__ */
+
-- 
2.26.2.windows.1