lkml.org 
[lkml]   [2019]   [Jul]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v5 02/12] S.A.R.A.: create framework
    Date
    Initial S.A.R.A. framework setup.
    Creation of a simplified interface to securityfs API to store and retrieve
    configurations and flags from user-space.
    Creation of some generic functions and macros to handle concurrent access
    to configurations, memory allocation and path resolution.

    Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
    ---
    security/Kconfig | 11 +-
    security/Makefile | 2 +
    security/sara/Kconfig | 40 +++
    security/sara/Makefile | 3 +
    security/sara/include/sara.h | 29 ++
    security/sara/include/securityfs.h | 61 ++++
    security/sara/include/utils.h | 80 ++++++
    security/sara/main.c | 115 ++++++++
    security/sara/securityfs.c | 565 +++++++++++++++++++++++++++++++++++++
    security/sara/utils.c | 92 ++++++
    10 files changed, 993 insertions(+), 5 deletions(-)
    create mode 100644 security/sara/Kconfig
    create mode 100644 security/sara/Makefile
    create mode 100644 security/sara/include/sara.h
    create mode 100644 security/sara/include/securityfs.h
    create mode 100644 security/sara/include/utils.h
    create mode 100644 security/sara/main.c
    create mode 100644 security/sara/securityfs.c
    create mode 100644 security/sara/utils.c

    diff --git a/security/Kconfig b/security/Kconfig
    index 466cc1f..4cae0ec 100644
    --- a/security/Kconfig
    +++ b/security/Kconfig
    @@ -237,6 +237,7 @@ source "security/apparmor/Kconfig"
    source "security/loadpin/Kconfig"
    source "security/yama/Kconfig"
    source "security/safesetid/Kconfig"
    +source "security/sara/Kconfig"

    source "security/integrity/Kconfig"

    @@ -276,11 +277,11 @@ endchoice

    config LSM
    string "Ordered list of enabled LSMs"
    - default "yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK
    - default "yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR
    - default "yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO
    - default "yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC
    - default "yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor"
    + default "yama,loadpin,safesetid,integrity,sara,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK
    + default "yama,loadpin,safesetid,integrity,sara,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR
    + default "yama,loadpin,safesetid,integrity,sara,tomoyo" if DEFAULT_SECURITY_TOMOYO
    + default "yama,loadpin,safesetid,integrity,sara" if DEFAULT_SECURITY_DAC
    + default "yama,loadpin,safesetid,integrity,sara,selinux,smack,tomoyo,apparmor"
    help
    A comma-separated list of LSMs, in initialization order.
    Any LSMs left off this list will be ignored. This can be
    diff --git a/security/Makefile b/security/Makefile
    index c598b90..4b0fd11 100644
    --- a/security/Makefile
    +++ b/security/Makefile
    @@ -11,6 +11,7 @@ subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
    subdir-$(CONFIG_SECURITY_YAMA) += yama
    subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
    subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
    +subdir-$(CONFIG_SECURITY_SARA) += sara

    # always enable default capabilities
    obj-y += commoncap.o
    @@ -27,6 +28,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
    obj-$(CONFIG_SECURITY_YAMA) += yama/
    obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
    obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
    +obj-$(CONFIG_SECURITY_SARA) += sara/
    obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o

    # Object integrity file lists
    diff --git a/security/sara/Kconfig b/security/sara/Kconfig
    new file mode 100644
    index 0000000..0456220
    --- /dev/null
    +++ b/security/sara/Kconfig
    @@ -0,0 +1,40 @@
    +menuconfig SECURITY_SARA
    + bool "S.A.R.A."
    + depends on SECURITY
    + select SECURITYFS
    + default n
    + help
    + This selects S.A.R.A. LSM which aims to collect heterogeneous
    + security measures providing a common interface to manage them.
    + This LSM will always be stacked with the selected primary LSM and
    + other stacked LSMs.
    + Further information can be found in
    + Documentation/admin-guide/LSM/SARA.rst.
    +
    + If unsure, answer N.
    +
    +config SECURITY_SARA_DEFAULT_DISABLED
    + bool "S.A.R.A. will be disabled at boot."
    + depends on SECURITY_SARA
    + default n
    + help
    + If you say Y here, S.A.R.A. will not be enabled at startup. You can
    + override this option at boot time via "sara.enabled=[1|0]" kernel
    + parameter or via user-space utilities.
    + This option is useful for distro kernels.
    +
    + If unsure, answer N.
    +
    +config SECURITY_SARA_NO_RUNTIME_ENABLE
    + bool "S.A.R.A. can be turn on only at boot time."
    + depends on SECURITY_SARA_DEFAULT_DISABLED
    + default y
    + help
    + By enabling this option it won't be possible to turn on S.A.R.A.
    + at runtime via user-space utilities. However it can still be
    + turned on at boot time via the "sara.enabled=1" kernel parameter.
    + This option is functionally equivalent to "sara.enabled=0" kernel
    + parameter. This option is useful for distro kernels.
    +
    + If unsure, answer Y.
    +
    diff --git a/security/sara/Makefile b/security/sara/Makefile
    new file mode 100644
    index 0000000..8acd291
    --- /dev/null
    +++ b/security/sara/Makefile
    @@ -0,0 +1,3 @@
    +obj-$(CONFIG_SECURITY_SARA) := sara.o
    +
    +sara-y := main.o securityfs.o utils.o
    diff --git a/security/sara/include/sara.h b/security/sara/include/sara.h
    new file mode 100644
    index 0000000..cd12f52
    --- /dev/null
    +++ b/security/sara/include/sara.h
    @@ -0,0 +1,29 @@
    +/* SPDX-License-Identifier: GPL-2.0 */
    +
    +/*
    + * S.A.R.A. Linux Security Module
    + *
    + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
    + *
    + * 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.
    + *
    + */
    +
    +#ifndef __SARA_H
    +#define __SARA_H
    +
    +#include <linux/types.h>
    +#include <uapi/linux/limits.h>
    +
    +#define SARA_VERSION 0
    +#define SARA_PATH_MAX PATH_MAX
    +
    +#undef pr_fmt
    +#define pr_fmt(fmt) "SARA: " fmt
    +
    +extern int sara_config_locked __read_mostly;
    +extern int sara_enabled __read_mostly;
    +
    +#endif /* __SARA_H */
    diff --git a/security/sara/include/securityfs.h b/security/sara/include/securityfs.h
    new file mode 100644
    index 0000000..92d6180
    --- /dev/null
    +++ b/security/sara/include/securityfs.h
    @@ -0,0 +1,61 @@
    +/* SPDX-License-Identifier: GPL-2.0 */
    +
    +/*
    + * S.A.R.A. Linux Security Module
    + *
    + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
    + *
    + * 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.
    + *
    + */
    +
    +#ifndef __SARA_SECURITYFS_H
    +#define __SARA_SECURITYFS_H
    +
    +#include <linux/init.h>
    +
    +#define SARA_SUBTREE_NN_LEN 24
    +#define SARA_CONFIG_HASH_LEN 20
    +
    +struct sara_secfs_node;
    +
    +int sara_secfs_init(void) __init;
    +int sara_secfs_subtree_register(const char *subtree_name,
    + const struct sara_secfs_node *nodes,
    + size_t size) __init;
    +
    +enum sara_secfs_node_type {
    + SARA_SECFS_BOOL,
    + SARA_SECFS_READONLY_INT,
    + SARA_SECFS_CONFIG_LOAD,
    + SARA_SECFS_CONFIG_DUMP,
    + SARA_SECFS_CONFIG_HASH,
    +};
    +
    +struct sara_secfs_node {
    + const enum sara_secfs_node_type type;
    + void *const data;
    + const size_t dir_contents_len;
    + const char name[SARA_SUBTREE_NN_LEN];
    +};
    +
    +struct sara_secfs_fptrs {
    + int (*const load)(const char *, size_t);
    + ssize_t (*const dump)(char **);
    + int (*const hash)(char **);
    +};
    +
    +struct sara_secfs_bool_flag {
    + const char notice_line[SARA_SUBTREE_NN_LEN];
    + int *const flag;
    +};
    +
    +#define DEFINE_SARA_SECFS_BOOL_FLAG(NAME, VAR) \
    +const struct sara_secfs_bool_flag NAME = { \
    + .notice_line = #VAR, \
    + .flag = &(VAR), \
    +}
    +
    +#endif /* __SARA_SECURITYFS_H */
    diff --git a/security/sara/include/utils.h b/security/sara/include/utils.h
    new file mode 100644
    index 0000000..ce9d5fb
    --- /dev/null
    +++ b/security/sara/include/utils.h
    @@ -0,0 +1,80 @@
    +/* SPDX-License-Identifier: GPL-2.0 */
    +
    +/*
    + * S.A.R.A. Linux Security Module
    + *
    + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
    + *
    + * 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.
    + *
    + */
    +
    +#ifndef __SARA_UTILS_H
    +#define __SARA_UTILS_H
    +
    +#include <linux/kref.h>
    +#include <linux/rcupdate.h>
    +#include <linux/spinlock.h>
    +
    +char *get_absolute_path(const struct path *spath, char **buf);
    +char *get_current_path(char **buf);
    +
    +static inline void release_entry(struct kref *ref)
    +{
    + /* All work is done after the return from kref_put(). */
    +}
    +
    +
    +/*
    + * The following macros must be used to access S.A.R.A. configuration
    + * structures.
    + * They are thread-safe under the assumption that a configuration
    + * won't ever be deleted but just replaced using SARA_CONFIG_REPLACE,
    + * possibly using an empty configuration.
    + * i.e. every call to SARA_CONFIG_PUT *must* be preceded by a matching
    + * SARA_CONFIG_GET invocation.
    + */
    +
    +#define SARA_CONFIG_GET_RCU(DEST, CONFIG) do { \
    + rcu_read_lock(); \
    + (DEST) = rcu_dereference(CONFIG); \
    +} while (0)
    +
    +#define SARA_CONFIG_PUT_RCU(DATA) do { \
    + rcu_read_unlock(); \
    + (DATA) = NULL; \
    +} while (0)
    +
    +#define SARA_CONFIG_GET(DEST, CONFIG) do { \
    + rcu_read_lock(); \
    + do { \
    + (DEST) = rcu_dereference(CONFIG); \
    + } while ((DEST) && !kref_get_unless_zero(&(DEST)->refcount)); \
    + rcu_read_unlock(); \
    +} while (0)
    +
    +#define SARA_CONFIG_PUT(DATA, FREE) do { \
    + if (kref_put(&(DATA)->refcount, release_entry)) { \
    + synchronize_rcu(); \
    + (FREE)(DATA); \
    + } \
    + (DATA) = NULL; \
    +} while (0)
    +
    +#define SARA_CONFIG_REPLACE(CONFIG, NEW, FREE, LOCK) do { \
    + typeof(NEW) tmp; \
    + spin_lock(LOCK); \
    + tmp = rcu_dereference_protected(CONFIG, \
    + lockdep_is_held(LOCK)); \
    + rcu_assign_pointer(CONFIG, NEW); \
    + if (kref_put(&tmp->refcount, release_entry)) { \
    + spin_unlock(LOCK); \
    + synchronize_rcu(); \
    + FREE(tmp); \
    + } else \
    + spin_unlock(LOCK); \
    +} while (0)
    +
    +#endif /* __SARA_UTILS_H */
    diff --git a/security/sara/main.c b/security/sara/main.c
    new file mode 100644
    index 0000000..52e6d18
    --- /dev/null
    +++ b/security/sara/main.c
    @@ -0,0 +1,115 @@
    +// SPDX-License-Identifier: GPL-2.0
    +
    +/*
    + * S.A.R.A. Linux Security Module
    + *
    + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
    + *
    + * 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 <linux/bug.h>
    +#include <linux/kernel.h>
    +#include <linux/lsm_hooks.h>
    +#include <linux/module.h>
    +#include <linux/printk.h>
    +
    +#include "include/sara.h"
    +#include "include/securityfs.h"
    +
    +static const int sara_version = SARA_VERSION;
    +
    +#ifdef CONFIG_SECURITY_SARA_NO_RUNTIME_ENABLE
    +int sara_config_locked __read_mostly = true;
    +#else
    +int sara_config_locked __read_mostly;
    +#endif
    +
    +#ifdef CONFIG_SECURITY_SARA_DEFAULT_DISABLED
    +int sara_enabled __read_mostly;
    +#else
    +int sara_enabled __read_mostly = true;
    +#endif
    +
    +static DEFINE_SARA_SECFS_BOOL_FLAG(sara_enabled_data, sara_enabled);
    +static DEFINE_SARA_SECFS_BOOL_FLAG(sara_config_locked_data, sara_config_locked);
    +
    +static int param_set_senabled(const char *val, const struct kernel_param *kp)
    +{
    + if (!val)
    + return 0;
    + if (strtobool(val, kp->arg))
    + return -EINVAL;
    + /* config must by locked when S.A.R.A. is disabled at boot
    + * and unlocked when it's enabled
    + */
    + sara_config_locked = !(*(int *) kp->arg);
    + return 0;
    +}
    +
    +static struct kernel_param_ops param_ops_senabled = {
    + .set = param_set_senabled,
    +};
    +
    +#define param_check_senabled(name, p) __param_check(name, p, int)
    +
    +module_param_named(enabled, sara_enabled, senabled, 0000);
    +MODULE_PARM_DESC(enabled, "Disable or enable S.A.R.A. at boot time. If disabled this way S.A.R.A. can't be enabled again.");
    +
    +static const struct sara_secfs_node main_fs[] __initconst = {
    + {
    + .name = "enabled",
    + .type = SARA_SECFS_BOOL,
    + .data = (void *) &sara_enabled_data,
    + },
    + {
    + .name = "locked",
    + .type = SARA_SECFS_BOOL,
    + .data = (void *) &sara_config_locked_data,
    + },
    + {
    + .name = "version",
    + .type = SARA_SECFS_READONLY_INT,
    + .data = (int *) &sara_version,
    + },
    +};
    +
    +static int __init sara_init(void)
    +{
    + if (!sara_enabled && sara_config_locked) {
    + pr_notice("permanently disabled.\n");
    + return 0;
    + }
    +
    + pr_debug("initializing...\n");
    +
    + if (sara_secfs_subtree_register("main",
    + main_fs,
    + ARRAY_SIZE(main_fs))) {
    + pr_crit("impossible to register main fs.\n");
    + goto error;
    + }
    +
    + pr_debug("initialized.\n");
    +
    + if (sara_enabled)
    + pr_info("enabled\n");
    + else
    + pr_notice("disabled\n");
    + return 0;
    +
    +error:
    + sara_enabled = false;
    + sara_config_locked = true;
    + pr_crit("permanently disabled.\n");
    + return 1;
    +}
    +
    +DEFINE_LSM(sara) = {
    + .name = "sara",
    + .enabled = &sara_enabled,
    + .init = sara_init,
    +};
    diff --git a/security/sara/securityfs.c b/security/sara/securityfs.c
    new file mode 100644
    index 0000000..f6b152c
    --- /dev/null
    +++ b/security/sara/securityfs.c
    @@ -0,0 +1,565 @@
    +// SPDX-License-Identifier: GPL-2.0
    +
    +/*
    + * S.A.R.A. Linux Security Module
    + *
    + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
    + *
    + * 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 <linux/capability.h>
    +#include <linux/ctype.h>
    +#include <linux/mm.h>
    +#include <linux/printk.h>
    +#include <linux/security.h>
    +#include <linux/seq_file.h>
    +#include <linux/spinlock.h>
    +#include <linux/uaccess.h>
    +
    +#include "include/sara.h"
    +#include "include/utils.h"
    +#include "include/securityfs.h"
    +
    +#define __SARA_STR_HELPER(x) #x
    +#define SARA_STR(x) __SARA_STR_HELPER(x)
    +
    +static struct dentry *fs_root;
    +
    +static inline bool check_config_write_access(void)
    +{
    + if (unlikely(sara_config_locked)) {
    + pr_warn("config write access blocked.\n");
    + return false;
    + }
    + return true;
    +}
    +
    +static bool check_config_access(const struct file *file)
    +{
    + if (!capable(CAP_MAC_ADMIN))
    + return false;
    + if (file->f_flags & O_WRONLY || file->f_flags & O_RDWR)
    + if (unlikely(!check_config_write_access()))
    + return false;
    + return true;
    +}
    +
    +static int file_flag_show(struct seq_file *seq, void *v)
    +{
    + int *flag = ((struct sara_secfs_bool_flag *)seq->private)->flag;
    +
    + seq_printf(seq, "%d\n", *flag);
    + return 0;
    +}
    +
    +static ssize_t file_flag_write(struct file *file,
    + const char __user *ubuf,
    + size_t buf_size,
    + loff_t *offset)
    +{
    + struct sara_secfs_bool_flag *bool_flag =
    + ((struct seq_file *) file->private_data)->private;
    + char kbuf[2] = {'A', '\n'};
    + int nf;
    +
    + if (unlikely(*offset != 0))
    + return -ESPIPE;
    +
    + if (unlikely(buf_size != 1 && buf_size != 2))
    + return -EPERM;
    +
    + if (unlikely(copy_from_user(kbuf, ubuf, buf_size)))
    + return -EFAULT;
    +
    + if (unlikely(kbuf[1] != '\n'))
    + return -EPERM;
    +
    + switch (kbuf[0]) {
    + case '0':
    + nf = false;
    + break;
    + case '1':
    + nf = true;
    + break;
    + default:
    + return -EPERM;
    + }
    +
    + *bool_flag->flag = nf;
    +
    + if (strlen(bool_flag->notice_line) > 0)
    + pr_notice("flag \"%s\" set to %d\n",
    + bool_flag->notice_line,
    + nf);
    +
    + return buf_size;
    +}
    +
    +static int file_flag_open(struct inode *inode, struct file *file)
    +{
    + if (unlikely(!check_config_access(file)))
    + return -EACCES;
    + return single_open(file, file_flag_show, inode->i_private);
    +}
    +
    +static const struct file_operations file_flag = {
    + .owner = THIS_MODULE,
    + .open = file_flag_open,
    + .write = file_flag_write,
    + .read = seq_read,
    + .release = single_release,
    +};
    +
    +static int file_readonly_int_show(struct seq_file *seq, void *v)
    +{
    + int *flag = seq->private;
    +
    + seq_printf(seq, "%d\n", *flag);
    + return 0;
    +}
    +
    +static int file_readonly_int_open(struct inode *inode, struct file *file)
    +{
    + if (unlikely(!check_config_access(file)))
    + return -EACCES;
    + return single_open(file, file_readonly_int_show, inode->i_private);
    +}
    +
    +static const struct file_operations file_readonly_int = {
    + .owner = THIS_MODULE,
    + .open = file_readonly_int_open,
    + .read = seq_read,
    + .release = single_release,
    +};
    +
    +static ssize_t file_config_loader_write(struct file *file,
    + const char __user *ubuf,
    + size_t buf_size,
    + loff_t *offset)
    +{
    + const struct sara_secfs_fptrs *fptrs = file->private_data;
    + char *kbuf = NULL;
    + ssize_t ret;
    +
    + ret = -ESPIPE;
    + if (unlikely(*offset != 0))
    + goto out;
    +
    + ret = -ENOMEM;
    + kbuf = kvmalloc(buf_size, GFP_KERNEL_ACCOUNT);
    + if (unlikely(kbuf == NULL))
    + goto out;
    +
    + ret = -EFAULT;
    + if (unlikely(copy_from_user(kbuf, ubuf, buf_size)))
    + goto out;
    +
    + ret = fptrs->load(kbuf, buf_size);
    +
    + if (unlikely(ret))
    + goto out;
    +
    + ret = buf_size;
    +
    +out:
    + kvfree(kbuf);
    + return ret;
    +}
    +
    +static int file_config_loader_open(struct inode *inode, struct file *file)
    +{
    + if (unlikely(!check_config_access(file)))
    + return -EACCES;
    + file->private_data = inode->i_private;
    + return 0;
    +}
    +
    +static const struct file_operations file_config_loader = {
    + .owner = THIS_MODULE,
    + .open = file_config_loader_open,
    + .write = file_config_loader_write,
    +};
    +
    +static int file_config_show(struct seq_file *seq, void *v)
    +{
    + const struct sara_secfs_fptrs *fptrs = seq->private;
    + char *buf = NULL;
    + ssize_t ret;
    +
    + ret = fptrs->dump(&buf);
    + if (unlikely(ret <= 0))
    + goto out;
    + seq_write(seq, buf, ret);
    + kvfree(buf);
    + ret = 0;
    +out:
    + return ret;
    +}
    +
    +static int file_dumper_open(struct inode *inode, struct file *file)
    +{
    + if (unlikely(!check_config_access(file)))
    + return -EACCES;
    + return single_open(file, file_config_show, inode->i_private);
    +}
    +
    +static const struct file_operations file_config_dumper = {
    + .owner = THIS_MODULE,
    + .open = file_dumper_open,
    + .read = seq_read,
    + .release = single_release,
    +};
    +
    +static int file_hash_show(struct seq_file *seq, void *v)
    +{
    + const struct sara_secfs_fptrs *fptrs = seq->private;
    + char *buf = NULL;
    + int ret;
    +
    + ret = fptrs->hash(&buf);
    + if (unlikely(ret))
    + goto out;
    + seq_printf(seq, "%" SARA_STR(SARA_CONFIG_HASH_LEN) "phN\n", buf);
    + kvfree(buf);
    + ret = 0;
    +out:
    + return ret;
    +}
    +
    +static int file_hash_open(struct inode *inode, struct file *file)
    +{
    + if (unlikely(!check_config_access(file)))
    + return -EACCES;
    + return single_open(file, file_hash_show, inode->i_private);
    +}
    +
    +static const struct file_operations file_hash = {
    + .owner = THIS_MODULE,
    + .open = file_hash_open,
    + .read = seq_read,
    + .release = single_release,
    +};
    +
    +static int mk_dir(struct dentry *parent,
    + const char *dir_name,
    + struct dentry **dir_out)
    +{
    + int ret = 0;
    +
    + *dir_out = securityfs_create_dir(dir_name, parent);
    + if (IS_ERR(*dir_out)) {
    + ret = -PTR_ERR(*dir_out);
    + *dir_out = NULL;
    + }
    + return ret;
    +}
    +
    +static int mk_bool_flag(struct dentry *parent,
    + const char *file_name,
    + struct dentry **dir_out,
    + void *flag)
    +{
    + int ret = 0;
    +
    + *dir_out = securityfs_create_file(file_name,
    + 0600,
    + parent,
    + flag,
    + &file_flag);
    + if (IS_ERR(*dir_out)) {
    + ret = -PTR_ERR(*dir_out);
    + *dir_out = NULL;
    + }
    + return ret;
    +}
    +
    +static int mk_readonly_int(struct dentry *parent,
    + const char *file_name,
    + struct dentry **dir_out,
    + void *readonly_int)
    +{
    + int ret = 0;
    +
    + *dir_out = securityfs_create_file(file_name,
    + 0400,
    + parent,
    + readonly_int,
    + &file_readonly_int);
    + if (IS_ERR(*dir_out)) {
    + ret = -PTR_ERR(*dir_out);
    + *dir_out = NULL;
    + }
    + return ret;
    +}
    +
    +static int mk_config_loader(struct dentry *parent,
    + const char *file_name,
    + struct dentry **dir_out,
    + void *fptrs)
    +{
    + int ret = 0;
    +
    + *dir_out = securityfs_create_file(file_name,
    + 0200,
    + parent,
    + fptrs,
    + &file_config_loader);
    + if (IS_ERR(*dir_out)) {
    + ret = -PTR_ERR(*dir_out);
    + *dir_out = NULL;
    + }
    + return ret;
    +}
    +
    +static int mk_config_dumper(struct dentry *parent,
    + const char *file_name,
    + struct dentry **dir_out,
    + void *fptrs)
    +{
    + int ret = 0;
    +
    + *dir_out = securityfs_create_file(file_name,
    + 0400,
    + parent,
    + fptrs,
    + &file_config_dumper);
    + if (IS_ERR(*dir_out)) {
    + ret = -PTR_ERR(*dir_out);
    + *dir_out = NULL;
    + }
    + return ret;
    +}
    +
    +static int mk_config_hash(struct dentry *parent,
    + const char *file_name,
    + struct dentry **dir_out,
    + void *fptrs)
    +{
    + int ret = 0;
    +
    + *dir_out = securityfs_create_file(file_name,
    + 0400,
    + parent,
    + fptrs,
    + &file_hash);
    + if (IS_ERR(*dir_out)) {
    + ret = -PTR_ERR(*dir_out);
    + *dir_out = NULL;
    + }
    + return ret;
    +}
    +
    +struct sara_secfs_subtree {
    + char name[SARA_SUBTREE_NN_LEN];
    + size_t size;
    + struct dentry **nodes;
    + const struct sara_secfs_node *nodes_description;
    + struct list_head subtree_list;
    +};
    +
    +static LIST_HEAD(subtree_list);
    +
    +int __init sara_secfs_subtree_register(const char *subtree_name,
    + const struct sara_secfs_node *nodes,
    + size_t size)
    +{
    + int ret;
    + struct sara_secfs_subtree *subtree = NULL;
    +
    + ret = -EINVAL;
    + if (unlikely(size < 1))
    + goto error;
    + ret = -ENOMEM;
    + subtree = kmalloc(sizeof(*subtree), GFP_KERNEL);
    + if (unlikely(subtree == NULL))
    + goto error;
    + strncpy(subtree->name,
    + subtree_name,
    + sizeof(subtree->name));
    + subtree->name[sizeof(subtree->name)-1] = '\0';
    + subtree->size = size+1;
    + subtree->nodes = kcalloc(subtree->size,
    + sizeof(*subtree->nodes),
    + GFP_KERNEL);
    + if (unlikely(subtree->nodes == NULL))
    + goto error;
    + subtree->nodes_description = nodes;
    + INIT_LIST_HEAD(&subtree->subtree_list);
    + list_add(&subtree->subtree_list, &subtree_list);
    + return 0;
    +
    +error:
    + kfree(subtree);
    + pr_warn("SECFS: Impossible to register '%s' (%d).\n",
    + subtree_name, ret);
    + return ret;
    +}
    +
    +static inline int __init create_node(enum sara_secfs_node_type type,
    + struct dentry *parent,
    + const char *name,
    + struct dentry **output,
    + void *data)
    +{
    + switch (type) {
    + case SARA_SECFS_BOOL:
    + return mk_bool_flag(parent, name, output, data);
    + case SARA_SECFS_READONLY_INT:
    + return mk_readonly_int(parent, name, output, data);
    + case SARA_SECFS_CONFIG_LOAD:
    + return mk_config_loader(parent, name, output, data);
    + case SARA_SECFS_CONFIG_DUMP:
    + return mk_config_dumper(parent, name, output, data);
    + case SARA_SECFS_CONFIG_HASH:
    + return mk_config_hash(parent, name, output, data);
    + default:
    + return -EINVAL;
    + }
    +}
    +
    +static void subtree_unplug(struct sara_secfs_subtree *subtree)
    +{
    + int i;
    +
    + for (i = 0; i < subtree->size; ++i) {
    + if (subtree->nodes[i] != NULL) {
    + securityfs_remove(subtree->nodes[i]);
    + subtree->nodes[i] = NULL;
    + }
    + }
    +}
    +
    +static int __init subtree_plug(struct sara_secfs_subtree *subtree)
    +{
    + int ret;
    + int i;
    + const struct sara_secfs_node *nodes = subtree->nodes_description;
    +
    + ret = -EINVAL;
    + if (unlikely(fs_root == NULL))
    + goto out;
    + ret = mk_dir(fs_root,
    + subtree->name,
    + &subtree->nodes[subtree->size-1]);
    + if (unlikely(ret))
    + goto out_unplug;
    + for (i = 0; i < subtree->size-1; ++i) {
    + ret = create_node(nodes[i].type,
    + subtree->nodes[subtree->size-1],
    + nodes[i].name,
    + &subtree->nodes[i],
    + nodes[i].data);
    + if (unlikely(ret))
    + goto out_unplug;
    + }
    + return 0;
    +
    +out_unplug:
    + subtree_unplug(subtree);
    +out:
    + pr_warn("SECFS: Impossible to plug '%s' (%d).\n", subtree->name, ret);
    + return ret;
    +}
    +
    +static int __init subtree_plug_all(void)
    +{
    + int ret;
    + struct list_head *position;
    + struct sara_secfs_subtree *subtree;
    +
    + ret = -EINVAL;
    + if (unlikely(fs_root == NULL))
    + goto out;
    + ret = 0;
    + list_for_each(position, &subtree_list) {
    + subtree = list_entry(position,
    + struct sara_secfs_subtree,
    + subtree_list);
    + if (subtree->nodes[0] == NULL) {
    + ret = subtree_plug(subtree);
    + if (unlikely(ret))
    + goto out;
    + }
    + }
    +out:
    + if (unlikely(ret))
    + pr_warn("SECFS: Impossible to plug subtrees (%d).\n", ret);
    + return ret;
    +}
    +
    +static void __init subtree_free_all(bool unplug)
    +{
    + struct list_head *position;
    + struct list_head *next;
    + struct sara_secfs_subtree *subtree;
    +
    + list_for_each_safe(position, next, &subtree_list) {
    + subtree = list_entry(position,
    + struct sara_secfs_subtree,
    + subtree_list);
    + list_del(position);
    + if (unplug)
    + subtree_unplug(subtree);
    + kfree(subtree->nodes);
    + kfree(subtree);
    + }
    +}
    +
    +static int mk_root(void)
    +{
    + int ret = -1;
    +
    + if (fs_root == NULL)
    + ret = mk_dir(NULL, "sara", &fs_root);
    + if (unlikely(ret || fs_root == NULL))
    + pr_warn("SECFS: Impossible to create root (%d).\n", ret);
    + return ret;
    +}
    +
    +static inline void rm_root(void)
    +{
    + if (likely(fs_root != NULL)) {
    + securityfs_remove(fs_root);
    + fs_root = NULL;
    + }
    +}
    +
    +static inline void __init sara_secfs_destroy(void)
    +{
    + subtree_free_all(true);
    + rm_root();
    +}
    +
    +int __init sara_secfs_init(void)
    +{
    + int ret;
    +
    + if (!sara_enabled && sara_config_locked)
    + return 0;
    +
    + fs_root = NULL;
    +
    + ret = mk_root();
    + if (unlikely(ret))
    + goto error;
    +
    + ret = subtree_plug_all();
    + if (unlikely(ret))
    + goto error;
    +
    + subtree_free_all(false);
    +
    + pr_debug("securityfs initilaized.\n");
    + return 0;
    +
    +error:
    + sara_secfs_destroy();
    + pr_crit("impossible to build securityfs.\n");
    + return ret;
    +}
    +
    +fs_initcall(sara_secfs_init);
    diff --git a/security/sara/utils.c b/security/sara/utils.c
    new file mode 100644
    index 0000000..d63febb
    --- /dev/null
    +++ b/security/sara/utils.c
    @@ -0,0 +1,92 @@
    +// SPDX-License-Identifier: GPL-2.0
    +
    +/*
    + * S.A.R.A. Linux Security Module
    + *
    + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
    + *
    + * 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 <linux/dcache.h>
    +#include <linux/file.h>
    +#include <linux/fs.h>
    +#include <linux/mm.h>
    +#include <linux/sched.h>
    +#include <linux/slab.h>
    +#include <linux/vmalloc.h>
    +
    +#include "include/sara.h"
    +#include "include/utils.h"
    +
    +/**
    + * get_absolute_path - return the absolute path for a struct path
    + * @spath: the struct path to report
    + * @buf: double pointer where the newly allocated buffer will be placed
    + *
    + * Returns a pointer into @buf or an error code.
    + *
    + * The caller MUST kvfree @buf when finished using it.
    + */
    +char *get_absolute_path(const struct path *spath, char **buf)
    +{
    + size_t size = 128;
    + char *work_buf = NULL;
    + char *path = NULL;
    +
    + do {
    + kvfree(work_buf);
    + work_buf = NULL;
    + if (size > SARA_PATH_MAX) {
    + path = ERR_PTR(-ENAMETOOLONG);
    + goto error;
    + }
    + work_buf = kvmalloc(size, GFP_KERNEL);
    + if (unlikely(work_buf == NULL)) {
    + path = ERR_PTR(-ENOMEM);
    + goto error;
    + }
    + path = d_absolute_path(spath, work_buf, size);
    + size *= 2;
    + } while (PTR_ERR(path) == -ENAMETOOLONG);
    + if (!IS_ERR(path))
    + goto out;
    +
    +error:
    + kvfree(work_buf);
    + work_buf = NULL;
    +out:
    + *buf = work_buf;
    + return path;
    +}
    +
    +/**
    + * get_current_path - return the absolute path for the exe_file
    + * in the current task_struct, falling back
    + * to the contents of the comm field.
    + * @buf: double pointer where the newly allocated buffer will be placed
    + *
    + * Returns a pointer into @buf or an error code.
    + *
    + * The caller MUST kvfree @buf when finished using it.
    + */
    +char *get_current_path(char **buf)
    +{
    + struct file *exe_file;
    + char *path = NULL;
    +
    + exe_file = get_task_exe_file(current);
    + if (exe_file) {
    + path = get_absolute_path(&exe_file->f_path, buf);
    + fput(exe_file);
    + }
    + if (IS_ERR_OR_NULL(path)) {
    + *buf = kzalloc(TASK_COMM_LEN, GFP_KERNEL);
    + __get_task_comm(*buf, TASK_COMM_LEN, current);
    + path = *buf;
    + }
    + return path;
    +}
    --
    1.9.1
    \
     
     \ /
      Last update: 2019-07-06 12:56    [W:4.681 / U:0.476 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site