summaryrefslogtreecommitdiffstats
path: root/kernel/security/smack
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/security/smack
parentf93b97fd65072de626c074dbe099a1fff05ce060 (diff)
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page. During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/security/smack')
-rw-r--r--kernel/security/smack/smack.h95
-rw-r--r--kernel/security/smack/smack_access.c74
-rw-r--r--kernel/security/smack/smack_lsm.c965
-rw-r--r--kernel/security/smack/smack_netfilter.c17
-rw-r--r--kernel/security/smack/smackfs.c893
5 files changed, 1466 insertions, 578 deletions
diff --git a/kernel/security/smack/smack.h b/kernel/security/smack/smack.h
index 49eada626..6c91156ae 100644
--- a/kernel/security/smack/smack.h
+++ b/kernel/security/smack/smack.h
@@ -15,14 +15,29 @@
#include <linux/capability.h>
#include <linux/spinlock.h>
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/in.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/in6.h>
+#endif /* CONFIG_IPV6 */
#include <net/netlabel.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/lsm_audit.h>
/*
+ * Use IPv6 port labeling if IPv6 is enabled and secmarks
+ * are not being used.
+ */
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#define SMACK_IPV6_PORT_LABELING 1
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6) && defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#define SMACK_IPV6_SECMARK_LABELING 1
+#endif
+
+/*
* Smack labels were limited to 23 characters for a long time.
*/
#define SMK_LABELLEN 24
@@ -100,6 +115,7 @@ struct task_smack {
struct smack_known *smk_forked; /* label when forked */
struct list_head smk_rules; /* per task access rules */
struct mutex smk_rules_lock; /* lock for the rules */
+ struct list_head smk_relabel; /* transit allowed labels */
};
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
@@ -118,15 +134,30 @@ struct smack_rule {
};
/*
- * An entry in the table identifying hosts.
+ * An entry in the table identifying IPv4 hosts.
*/
-struct smk_netlbladdr {
+struct smk_net4addr {
struct list_head list;
- struct sockaddr_in smk_host; /* network address */
+ struct in_addr smk_host; /* network address */
struct in_addr smk_mask; /* network mask */
+ int smk_masks; /* mask size */
struct smack_known *smk_label; /* label */
};
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * An entry in the table identifying IPv6 hosts.
+ */
+struct smk_net6addr {
+ struct list_head list;
+ struct in6_addr smk_host; /* network address */
+ struct in6_addr smk_mask; /* network mask */
+ int smk_masks; /* mask size */
+ struct smack_known *smk_label; /* label */
+};
+#endif /* CONFIG_IPV6 */
+
+#ifdef SMACK_IPV6_PORT_LABELING
/*
* An entry in the table identifying ports.
*/
@@ -137,6 +168,30 @@ struct smk_port_label {
struct smack_known *smk_in; /* inbound label */
struct smack_known *smk_out; /* outgoing label */
};
+#endif /* SMACK_IPV6_PORT_LABELING */
+
+struct smack_known_list_elem {
+ struct list_head list;
+ struct smack_known *smk_label;
+};
+
+/* Super block security struct flags for mount options */
+#define FSDEFAULT_MNT 0x01
+#define FSFLOOR_MNT 0x02
+#define FSHAT_MNT 0x04
+#define FSROOT_MNT 0x08
+#define FSTRANS_MNT 0x10
+
+#define NUM_SMK_MNT_OPTS 5
+
+enum {
+ Opt_error = -1,
+ Opt_fsdefault = 1,
+ Opt_fsfloor = 2,
+ Opt_fshat = 3,
+ Opt_fsroot = 4,
+ Opt_fstransmute = 5,
+};
/*
* Mount options
@@ -147,6 +202,7 @@ struct smk_port_label {
#define SMK_FSROOT "smackfsroot="
#define SMK_FSTRANS "smackfstransmute="
+#define SMACK_DELETE_OPTION "-DELETE"
#define SMACK_CIPSO_OPTION "-CIPSO"
/*
@@ -229,10 +285,6 @@ struct smk_audit_info {
struct smack_audit_data sad;
#endif
};
-/*
- * These functions are in smack_lsm.c
- */
-struct inode_smack *new_inode_smack(struct smack_known *);
/*
* These functions are in smack_access.c
@@ -249,6 +301,8 @@ int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
struct smack_known *smk_import_entry(const char *, int);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
+int smack_privileged(int cap);
+void smk_destroy_label_list(struct list_head *list);
/*
* Shared data.
@@ -257,12 +311,10 @@ extern int smack_enabled;
extern int smack_cipso_direct;
extern int smack_cipso_mapped;
extern struct smack_known *smack_net_ambient;
-extern struct smack_known *smack_onlycap;
extern struct smack_known *smack_syslog_label;
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
extern struct smack_known *smack_unconfined;
#endif
-extern struct smack_known smack_cipso_option;
extern int smack_ptrace_rule;
extern struct smack_known smack_known_floor;
@@ -274,9 +326,13 @@ extern struct smack_known smack_known_web;
extern struct mutex smack_known_lock;
extern struct list_head smack_known_list;
-extern struct list_head smk_netlbladdr_list;
+extern struct list_head smk_net4addr_list;
+#if IS_ENABLED(CONFIG_IPV6)
+extern struct list_head smk_net6addr_list;
+#endif /* CONFIG_IPV6 */
-extern struct security_operations smack_ops;
+extern struct mutex smack_onlycap_lock;
+extern struct list_head smack_onlycap_list;
#define SMACK_HASH_SLOTS 16
extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
@@ -334,21 +390,6 @@ static inline struct smack_known *smk_of_current(void)
}
/*
- * Is the task privileged and allowed to be privileged
- * by the onlycap rule.
- */
-static inline int smack_privileged(int cap)
-{
- struct smack_known *skp = smk_of_current();
-
- if (!capable(cap))
- return 0;
- if (smack_onlycap == NULL || smack_onlycap == skp)
- return 1;
- return 0;
-}
-
-/*
* logging functions
*/
#define SMACK_AUDIT_DENIED 0x1
diff --git a/kernel/security/smack/smack_access.c b/kernel/security/smack/smack_access.c
index 0f410fc56..a283f9e79 100644
--- a/kernel/security/smack/smack_access.c
+++ b/kernel/security/smack/smack_access.c
@@ -425,7 +425,7 @@ void smk_insert_entry(struct smack_known *skp)
* @string: a text string that might be a Smack label
*
* Returns a pointer to the entry in the label list that
- * matches the passed string.
+ * matches the passed string or NULL if not found.
*/
struct smack_known *smk_find_entry(const char *string)
{
@@ -448,7 +448,7 @@ struct smack_known *smk_find_entry(const char *string)
* @string: a text string that might contain a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
*
- * Returns a pointer to the clean label, or NULL
+ * Returns a pointer to the clean label or an error code.
*/
char *smk_parse_smack(const char *string, int len)
{
@@ -464,7 +464,7 @@ char *smk_parse_smack(const char *string, int len)
* including /smack/cipso and /smack/cipso2
*/
if (string[0] == '-')
- return NULL;
+ return ERR_PTR(-EINVAL);
for (i = 0; i < len; i++)
if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
@@ -472,11 +472,13 @@ char *smk_parse_smack(const char *string, int len)
break;
if (i == 0 || i >= SMK_LONGLABEL)
- return NULL;
+ return ERR_PTR(-EINVAL);
smack = kzalloc(i + 1, GFP_KERNEL);
- if (smack != NULL)
- strncpy(smack, string, i);
+ if (smack == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ strncpy(smack, string, i);
return smack;
}
@@ -523,7 +525,8 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
* @len: the maximum size, or zero if it is NULL terminated.
*
* Returns a pointer to the entry in the label list that
- * matches the passed string, adding it if necessary.
+ * matches the passed string, adding it if necessary,
+ * or an error code.
*/
struct smack_known *smk_import_entry(const char *string, int len)
{
@@ -533,8 +536,8 @@ struct smack_known *smk_import_entry(const char *string, int len)
int rc;
smack = smk_parse_smack(string, len);
- if (smack == NULL)
- return NULL;
+ if (IS_ERR(smack))
+ return ERR_CAST(smack);
mutex_lock(&smack_known_lock);
@@ -543,8 +546,10 @@ struct smack_known *smk_import_entry(const char *string, int len)
goto freeout;
skp = kzalloc(sizeof(*skp), GFP_KERNEL);
- if (skp == NULL)
+ if (skp == NULL) {
+ skp = ERR_PTR(-ENOMEM);
goto freeout;
+ }
skp->smk_known = smack;
skp->smk_secid = smack_next_secid++;
@@ -577,7 +582,7 @@ struct smack_known *smk_import_entry(const char *string, int len)
* smk_netlbl_mls failed.
*/
kfree(skp);
- skp = NULL;
+ skp = ERR_PTR(rc);
freeout:
kfree(smack);
unlockout:
@@ -612,3 +617,50 @@ struct smack_known *smack_from_secid(const u32 secid)
rcu_read_unlock();
return &smack_known_invalid;
}
+
+/*
+ * Unless a process is running with one of these labels
+ * even having CAP_MAC_OVERRIDE isn't enough to grant
+ * privilege to violate MAC policy. If no labels are
+ * designated (the empty list case) capabilities apply to
+ * everyone.
+ */
+LIST_HEAD(smack_onlycap_list);
+DEFINE_MUTEX(smack_onlycap_lock);
+
+/*
+ * Is the task privileged and allowed to be privileged
+ * by the onlycap rule.
+ *
+ * Returns 1 if the task is allowed to be privileged, 0 if it's not.
+ */
+int smack_privileged(int cap)
+{
+ struct smack_known *skp = smk_of_current();
+ struct smack_known_list_elem *sklep;
+
+ /*
+ * All kernel tasks are privileged
+ */
+ if (unlikely(current->flags & PF_KTHREAD))
+ return 1;
+
+ if (!capable(cap))
+ return 0;
+
+ rcu_read_lock();
+ if (list_empty(&smack_onlycap_list)) {
+ rcu_read_unlock();
+ return 1;
+ }
+
+ list_for_each_entry_rcu(sklep, &smack_onlycap_list, list) {
+ if (sklep->smk_label == skp) {
+ rcu_read_unlock();
+ return 1;
+ }
+ }
+ rcu_read_unlock();
+
+ return 0;
+}
diff --git a/kernel/security/smack/smack_lsm.c b/kernel/security/smack/smack_lsm.c
index b64475788..7c57c7fcf 100644
--- a/kernel/security/smack/smack_lsm.c
+++ b/kernel/security/smack/smack_lsm.c
@@ -41,6 +41,7 @@
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/binfmts.h>
+#include <linux/parser.h>
#include "smack.h"
#define TRANS_TRUE "TRUE"
@@ -50,12 +51,21 @@
#define SMK_RECEIVING 1
#define SMK_SENDING 2
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
-LIST_HEAD(smk_ipv6_port_list);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#ifdef SMACK_IPV6_PORT_LABELING
+static LIST_HEAD(smk_ipv6_port_list);
+#endif
static struct kmem_cache *smack_inode_cache;
int smack_enabled;
+static const match_table_t smk_mount_tokens = {
+ {Opt_fsdefault, SMK_FSDEFAULT "%s"},
+ {Opt_fsfloor, SMK_FSFLOOR "%s"},
+ {Opt_fshat, SMK_FSHAT "%s"},
+ {Opt_fsroot, SMK_FSROOT "%s"},
+ {Opt_fstransmute, SMK_FSTRANS "%s"},
+ {Opt_error, NULL},
+};
+
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static char *smk_bu_mess[] = {
"Bringup Error", /* Unused */
@@ -245,8 +255,8 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file,
* @ip: a pointer to the inode
* @dp: a pointer to the dentry
*
- * Returns a pointer to the master list entry for the Smack label
- * or NULL if there was no label to fetch.
+ * Returns a pointer to the master list entry for the Smack label,
+ * NULL if there was no label to fetch, or an error code.
*/
static struct smack_known *smk_fetch(const char *name, struct inode *ip,
struct dentry *dp)
@@ -256,14 +266,18 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
struct smack_known *skp = NULL;
if (ip->i_op->getxattr == NULL)
- return NULL;
+ return ERR_PTR(-EOPNOTSUPP);
buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL);
if (buffer == NULL)
- return NULL;
+ return ERR_PTR(-ENOMEM);
rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL);
- if (rc > 0)
+ if (rc < 0)
+ skp = ERR_PTR(rc);
+ else if (rc == 0)
+ skp = NULL;
+ else
skp = smk_import_entry(buffer, rc);
kfree(buffer);
@@ -277,7 +291,7 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
*
* Returns the new blob or NULL if there's no memory available
*/
-struct inode_smack *new_inode_smack(struct smack_known *skp)
+static struct inode_smack *new_inode_smack(struct smack_known *skp)
{
struct inode_smack *isp;
@@ -312,6 +326,7 @@ static struct task_smack *new_task_smack(struct smack_known *task,
tsp->smk_task = task;
tsp->smk_forked = forked;
INIT_LIST_HEAD(&tsp->smk_rules);
+ INIT_LIST_HEAD(&tsp->smk_relabel);
mutex_init(&tsp->smk_rules_lock);
return tsp;
@@ -347,6 +362,35 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
}
/**
+ * smk_copy_relabel - copy smk_relabel labels list
+ * @nhead: new rules header pointer
+ * @ohead: old rules header pointer
+ * @gfp: type of the memory for the allocation
+ *
+ * Returns 0 on success, -ENOMEM on error
+ */
+static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead,
+ gfp_t gfp)
+{
+ struct smack_known_list_elem *nklep;
+ struct smack_known_list_elem *oklep;
+
+ INIT_LIST_HEAD(nhead);
+
+ list_for_each_entry(oklep, ohead, list) {
+ nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp);
+ if (nklep == NULL) {
+ smk_destroy_label_list(nhead);
+ return -ENOMEM;
+ }
+ nklep->smk_label = oklep->smk_label;
+ list_add(&nklep->list, nhead);
+ }
+
+ return 0;
+}
+
+/**
* smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_*
* @mode - input mode in form of PTRACE_MODE_*
*
@@ -354,12 +398,10 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
*/
static inline unsigned int smk_ptrace_mode(unsigned int mode)
{
- switch (mode) {
- case PTRACE_MODE_READ:
- return MAY_READ;
- case PTRACE_MODE_ATTACH:
+ if (mode & PTRACE_MODE_ATTACH)
return MAY_READWRITE;
- }
+ if (mode & PTRACE_MODE_READ)
+ return MAY_READ;
return 0;
}
@@ -436,17 +478,11 @@ static int smk_ptrace_rule_check(struct task_struct *tracer,
*/
static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
{
- int rc;
struct smack_known *skp;
- rc = cap_ptrace_access_check(ctp, mode);
- if (rc != 0)
- return rc;
-
skp = smk_of_task_struct(ctp);
- rc = smk_ptrace_rule_check(current, skp, mode, __func__);
- return rc;
+ return smk_ptrace_rule_check(current, skp, mode, __func__);
}
/**
@@ -462,10 +498,6 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
int rc;
struct smack_known *skp;
- rc = cap_ptrace_traceme(ptp);
- if (rc != 0)
- return rc;
-
skp = smk_of_task(current_security());
rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__);
@@ -583,72 +615,197 @@ static int smack_sb_copy_data(char *orig, char *smackopts)
}
/**
- * smack_sb_kern_mount - Smack specific mount processing
+ * smack_parse_opts_str - parse Smack specific mount options
+ * @options: mount options string
+ * @opts: where to store converted mount opts
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ *
+ * converts Smack specific mount options to generic security option format
+ */
+static int smack_parse_opts_str(char *options,
+ struct security_mnt_opts *opts)
+{
+ char *p;
+ char *fsdefault = NULL;
+ char *fsfloor = NULL;
+ char *fshat = NULL;
+ char *fsroot = NULL;
+ char *fstransmute = NULL;
+ int rc = -ENOMEM;
+ int num_mnt_opts = 0;
+ int token;
+
+ opts->num_mnt_opts = 0;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ substring_t args[MAX_OPT_ARGS];
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, smk_mount_tokens, args);
+
+ switch (token) {
+ case Opt_fsdefault:
+ if (fsdefault)
+ goto out_opt_err;
+ fsdefault = match_strdup(&args[0]);
+ if (!fsdefault)
+ goto out_err;
+ break;
+ case Opt_fsfloor:
+ if (fsfloor)
+ goto out_opt_err;
+ fsfloor = match_strdup(&args[0]);
+ if (!fsfloor)
+ goto out_err;
+ break;
+ case Opt_fshat:
+ if (fshat)
+ goto out_opt_err;
+ fshat = match_strdup(&args[0]);
+ if (!fshat)
+ goto out_err;
+ break;
+ case Opt_fsroot:
+ if (fsroot)
+ goto out_opt_err;
+ fsroot = match_strdup(&args[0]);
+ if (!fsroot)
+ goto out_err;
+ break;
+ case Opt_fstransmute:
+ if (fstransmute)
+ goto out_opt_err;
+ fstransmute = match_strdup(&args[0]);
+ if (!fstransmute)
+ goto out_err;
+ break;
+ default:
+ rc = -EINVAL;
+ pr_warn("Smack: unknown mount option\n");
+ goto out_err;
+ }
+ }
+
+ opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
+ if (!opts->mnt_opts)
+ goto out_err;
+
+ opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
+ GFP_ATOMIC);
+ if (!opts->mnt_opts_flags) {
+ kfree(opts->mnt_opts);
+ goto out_err;
+ }
+
+ if (fsdefault) {
+ opts->mnt_opts[num_mnt_opts] = fsdefault;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
+ }
+ if (fsfloor) {
+ opts->mnt_opts[num_mnt_opts] = fsfloor;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
+ }
+ if (fshat) {
+ opts->mnt_opts[num_mnt_opts] = fshat;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
+ }
+ if (fsroot) {
+ opts->mnt_opts[num_mnt_opts] = fsroot;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
+ }
+ if (fstransmute) {
+ opts->mnt_opts[num_mnt_opts] = fstransmute;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
+ }
+
+ opts->num_mnt_opts = num_mnt_opts;
+ return 0;
+
+out_opt_err:
+ rc = -EINVAL;
+ pr_warn("Smack: duplicate mount options\n");
+
+out_err:
+ kfree(fsdefault);
+ kfree(fsfloor);
+ kfree(fshat);
+ kfree(fsroot);
+ kfree(fstransmute);
+ return rc;
+}
+
+/**
+ * smack_set_mnt_opts - set Smack specific mount options
* @sb: the file system superblock
- * @flags: the mount flags
- * @data: the smack mount options
+ * @opts: Smack mount options
+ * @kern_flags: mount option from kernel space or user space
+ * @set_kern_flags: where to store converted mount opts
*
* Returns 0 on success, an error code on failure
+ *
+ * Allow filesystems with binary mount data to explicitly set Smack mount
+ * labels.
*/
-static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
+static int smack_set_mnt_opts(struct super_block *sb,
+ struct security_mnt_opts *opts,
+ unsigned long kern_flags,
+ unsigned long *set_kern_flags)
{
struct dentry *root = sb->s_root;
struct inode *inode = d_backing_inode(root);
struct superblock_smack *sp = sb->s_security;
struct inode_smack *isp;
struct smack_known *skp;
- char *op;
- char *commap;
+ int i;
+ int num_opts = opts->num_mnt_opts;
int transmute = 0;
- int specified = 0;
if (sp->smk_initialized)
return 0;
sp->smk_initialized = 1;
- for (op = data; op != NULL; op = commap) {
- commap = strchr(op, ',');
- if (commap != NULL)
- *commap++ = '\0';
-
- if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) {
- op += strlen(SMK_FSHAT);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_hat = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {
- op += strlen(SMK_FSFLOOR);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_floor = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSDEFAULT,
- strlen(SMK_FSDEFAULT)) == 0) {
- op += strlen(SMK_FSDEFAULT);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_default = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) {
- op += strlen(SMK_FSROOT);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_root = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) {
- op += strlen(SMK_FSTRANS);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_root = skp;
- transmute = 1;
- specified = 1;
- }
+ for (i = 0; i < num_opts; i++) {
+ switch (opts->mnt_opts_flags[i]) {
+ case FSDEFAULT_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_default = skp;
+ break;
+ case FSFLOOR_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_floor = skp;
+ break;
+ case FSHAT_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_hat = skp;
+ break;
+ case FSROOT_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_root = skp;
+ break;
+ case FSTRANS_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_root = skp;
+ transmute = 1;
+ break;
+ default:
+ break;
}
}
@@ -656,7 +813,7 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
/*
* Unprivileged mounts don't get to specify Smack values.
*/
- if (specified)
+ if (num_opts)
return -EPERM;
/*
* Unprivileged mounts get root and default from the caller.
@@ -665,6 +822,7 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
sp->smk_root = skp;
sp->smk_default = skp;
}
+
/*
* Initialize the root inode.
*/
@@ -684,6 +842,37 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
}
/**
+ * smack_sb_kern_mount - Smack specific mount processing
+ * @sb: the file system superblock
+ * @flags: the mount flags
+ * @data: the smack mount options
+ *
+ * Returns 0 on success, an error code on failure
+ */
+static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
+{
+ int rc = 0;
+ char *options = data;
+ struct security_mnt_opts opts;
+
+ security_init_mnt_opts(&opts);
+
+ if (!options)
+ goto out;
+
+ rc = smack_parse_opts_str(options, &opts);
+ if (rc)
+ goto out_err;
+
+out:
+ rc = smack_set_mnt_opts(sb, &opts, 0, NULL);
+
+out_err:
+ security_free_mnt_opts(&opts);
+ return rc;
+}
+
+/**
* smack_sb_statfs - Smack check on statfs
* @dentry: identifies the file system in question
*
@@ -721,10 +910,6 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
struct inode_smack *isp;
int rc;
- rc = cap_bprm_set_creds(bprm);
- if (rc != 0)
- return rc;
-
if (bprm->cred_prepared)
return 0;
@@ -779,12 +964,11 @@ static void smack_bprm_committing_creds(struct linux_binprm *bprm)
static int smack_bprm_secureexec(struct linux_binprm *bprm)
{
struct task_smack *tsp = current_security();
- int ret = cap_bprm_secureexec(bprm);
- if (!ret && (tsp->smk_task != tsp->smk_forked))
- ret = 1;
+ if (tsp->smk_task != tsp->smk_forked)
+ return 1;
- return ret;
+ return 0;
}
/*
@@ -1133,7 +1317,9 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
if (rc == 0 && check_import) {
skp = size ? smk_import_entry(value, size) : NULL;
- if (skp == NULL || (check_star &&
+ if (IS_ERR(skp))
+ rc = PTR_ERR(skp);
+ else if (skp == NULL || (check_star &&
(skp == &smack_known_star || skp == &smack_known_web)))
rc = -EINVAL;
}
@@ -1173,19 +1359,19 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
skp = smk_import_entry(value, size);
- if (skp != NULL)
+ if (!IS_ERR(skp))
isp->smk_inode = skp;
else
isp->smk_inode = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
skp = smk_import_entry(value, size);
- if (skp != NULL)
+ if (!IS_ERR(skp))
isp->smk_task = skp;
else
isp->smk_task = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
skp = smk_import_entry(value, size);
- if (skp != NULL)
+ if (!IS_ERR(skp))
isp->smk_mmap = skp;
else
isp->smk_mmap = &smack_known_invalid;
@@ -1673,6 +1859,9 @@ static int smack_file_receive(struct file *file)
struct smk_audit_info ad;
struct inode *inode = file_inode(file);
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path);
/*
@@ -1761,6 +1950,8 @@ static void smack_cred_free(struct cred *cred)
return;
cred->security = NULL;
+ smk_destroy_label_list(&tsp->smk_relabel);
+
list_for_each_safe(l, n, &tsp->smk_rules) {
rp = list_entry(l, struct smack_rule, list);
list_del(&rp->list);
@@ -1792,6 +1983,11 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
if (rc != 0)
return rc;
+ rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
+ gfp);
+ if (rc != 0)
+ return rc;
+
new->security = new_tsp;
return 0;
}
@@ -1934,12 +2130,7 @@ static void smack_task_getsecid(struct task_struct *p, u32 *secid)
*/
static int smack_task_setnice(struct task_struct *p, int nice)
{
- int rc;
-
- rc = cap_task_setnice(p, nice);
- if (rc == 0)
- rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
- return rc;
+ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/**
@@ -1951,12 +2142,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
*/
static int smack_task_setioprio(struct task_struct *p, int ioprio)
{
- int rc;
-
- rc = cap_task_setioprio(p, ioprio);
- if (rc == 0)
- rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
- return rc;
+ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/**
@@ -1980,12 +2166,7 @@ static int smack_task_getioprio(struct task_struct *p)
*/
static int smack_task_setscheduler(struct task_struct *p)
{
- int rc;
-
- rc = cap_task_setscheduler(p);
- if (rc == 0)
- rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
- return rc;
+ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/**
@@ -2130,7 +2311,7 @@ static void smack_sk_free_security(struct sock *sk)
}
/**
-* smack_host_label - check host based restrictions
+* smack_ipv4host_label - check host based restrictions
* @sip: the object end
*
* looks for host based access restrictions
@@ -2141,30 +2322,96 @@ static void smack_sk_free_security(struct sock *sk)
*
* Returns the label of the far end or NULL if it's not special.
*/
-static struct smack_known *smack_host_label(struct sockaddr_in *sip)
+static struct smack_known *smack_ipv4host_label(struct sockaddr_in *sip)
{
- struct smk_netlbladdr *snp;
+ struct smk_net4addr *snp;
struct in_addr *siap = &sip->sin_addr;
if (siap->s_addr == 0)
return NULL;
- list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list)
+ list_for_each_entry_rcu(snp, &smk_net4addr_list, list)
+ /*
+ * we break after finding the first match because
+ * the list is sorted from longest to shortest mask
+ * so we have found the most specific match
+ */
+ if (snp->smk_host.s_addr ==
+ (siap->s_addr & snp->smk_mask.s_addr))
+ return snp->smk_label;
+
+ return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * smk_ipv6_localhost - Check for local ipv6 host address
+ * @sip: the address
+ *
+ * Returns boolean true if this is the localhost address
+ */
+static bool smk_ipv6_localhost(struct sockaddr_in6 *sip)
+{
+ __be16 *be16p = (__be16 *)&sip->sin6_addr;
+ __be32 *be32p = (__be32 *)&sip->sin6_addr;
+
+ if (be32p[0] == 0 && be32p[1] == 0 && be32p[2] == 0 && be16p[6] == 0 &&
+ ntohs(be16p[7]) == 1)
+ return true;
+ return false;
+}
+
+/**
+* smack_ipv6host_label - check host based restrictions
+* @sip: the object end
+*
+* looks for host based access restrictions
+*
+* This version will only be appropriate for really small sets of single label
+* hosts. The caller is responsible for ensuring that the RCU read lock is
+* taken before calling this function.
+*
+* Returns the label of the far end or NULL if it's not special.
+*/
+static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
+{
+ struct smk_net6addr *snp;
+ struct in6_addr *sap = &sip->sin6_addr;
+ int i;
+ int found = 0;
+
+ /*
+ * It's local. Don't look for a host label.
+ */
+ if (smk_ipv6_localhost(sip))
+ return NULL;
+
+ list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
/*
* we break after finding the first match because
* the list is sorted from longest to shortest mask
* so we have found the most specific match
*/
- if ((&snp->smk_host.sin_addr)->s_addr ==
- (siap->s_addr & (&snp->smk_mask)->s_addr)) {
- /* we have found the special CIPSO option */
- if (snp->smk_label == &smack_cipso_option)
- return NULL;
- return snp->smk_label;
+ for (found = 1, i = 0; i < 8; i++) {
+ /*
+ * If the label is NULL the entry has
+ * been renounced. Ignore it.
+ */
+ if (snp->smk_label == NULL)
+ continue;
+ if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
+ snp->smk_host.s6_addr16[i]) {
+ found = 0;
+ break;
+ }
}
+ if (found)
+ return snp->smk_label;
+ }
return NULL;
}
+#endif /* CONFIG_IPV6 */
/**
* smack_netlabel - Set the secattr on a socket
@@ -2228,7 +2475,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
struct smk_audit_info ad;
rcu_read_lock();
- hkp = smack_host_label(sap);
+ hkp = smack_ipv4host_label(sap);
if (hkp != NULL) {
#ifdef CONFIG_AUDIT
struct lsm_network_audit net;
@@ -2253,7 +2500,42 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
return smack_netlabel(sk, sk_lbl);
}
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * smk_ipv6_check - check Smack access
+ * @subject: subject Smack label
+ * @object: object Smack label
+ * @address: address
+ * @act: the action being taken
+ *
+ * Check an IPv6 access
+ */
+static int smk_ipv6_check(struct smack_known *subject,
+ struct smack_known *object,
+ struct sockaddr_in6 *address, int act)
+{
+#ifdef CONFIG_AUDIT
+ struct lsm_network_audit net;
+#endif
+ struct smk_audit_info ad;
+ int rc;
+
+#ifdef CONFIG_AUDIT
+ smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
+ ad.a.u.net->family = PF_INET6;
+ ad.a.u.net->dport = ntohs(address->sin6_port);
+ if (act == SMK_RECEIVING)
+ ad.a.u.net->v6info.saddr = address->sin6_addr;
+ else
+ ad.a.u.net->v6info.daddr = address->sin6_addr;
+#endif
+ rc = smk_access(subject, object, MAY_WRITE, &ad);
+ rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc);
+ return rc;
+}
+#endif /* CONFIG_IPV6 */
+
+#ifdef SMACK_IPV6_PORT_LABELING
/**
* smk_ipv6_port_label - Smack port access table management
* @sock: socket
@@ -2337,48 +2619,43 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
int act)
{
- __be16 *bep;
- __be32 *be32p;
struct smk_port_label *spp;
struct socket_smack *ssp = sk->sk_security;
- struct smack_known *skp;
- unsigned short port = 0;
+ struct smack_known *skp = NULL;
+ unsigned short port;
struct smack_known *object;
- struct smk_audit_info ad;
- int rc;
-#ifdef CONFIG_AUDIT
- struct lsm_network_audit net;
-#endif
if (act == SMK_RECEIVING) {
- skp = smack_net_ambient;
+ skp = smack_ipv6host_label(address);
object = ssp->smk_in;
} else {
skp = ssp->smk_out;
- object = smack_net_ambient;
+ object = smack_ipv6host_label(address);
}
/*
- * Get the IP address and port from the address.
+ * The other end is a single label host.
*/
- port = ntohs(address->sin6_port);
- bep = (__be16 *)(&address->sin6_addr);
- be32p = (__be32 *)(&address->sin6_addr);
+ if (skp != NULL && object != NULL)
+ return smk_ipv6_check(skp, object, address, act);
+ if (skp == NULL)
+ skp = smack_net_ambient;
+ if (object == NULL)
+ object = smack_net_ambient;
/*
* It's remote, so port lookup does no good.
*/
- if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1)
- goto auditout;
+ if (!smk_ipv6_localhost(address))
+ return smk_ipv6_check(skp, object, address, act);
/*
* It's local so the send check has to have passed.
*/
- if (act == SMK_RECEIVING) {
- skp = &smack_known_web;
- goto auditout;
- }
+ if (act == SMK_RECEIVING)
+ return 0;
+ port = ntohs(address->sin6_port);
list_for_each_entry(spp, &smk_ipv6_port_list, list) {
if (spp->smk_port != port)
continue;
@@ -2388,22 +2665,9 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
break;
}
-auditout:
-
-#ifdef CONFIG_AUDIT
- smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
- ad.a.u.net->family = sk->sk_family;
- ad.a.u.net->dport = port;
- if (act == SMK_RECEIVING)
- ad.a.u.net->v6info.saddr = address->sin6_addr;
- else
- ad.a.u.net->v6info.daddr = address->sin6_addr;
-#endif
- rc = smk_access(skp, object, MAY_WRITE, &ad);
- rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc);
- return rc;
+ return smk_ipv6_check(skp, object, address, act);
}
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
/**
* smack_inode_setsecurity - set smack xattrs
@@ -2430,8 +2694,8 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
return -EINVAL;
skp = smk_import_entry(value, size);
- if (skp == NULL)
- return -EINVAL;
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
nsp->smk_inode = skp;
@@ -2464,10 +2728,10 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
} else
return -EOPNOTSUPP;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_PORT_LABELING
if (sock->sk->sk_family == PF_INET6)
smk_ipv6_port_label(sock, NULL);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
return 0;
}
@@ -2509,7 +2773,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
}
-#ifndef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_PORT_LABELING
/**
* smack_socket_bind - record port binding information.
* @sock: the socket
@@ -2523,14 +2787,11 @@ static int smack_socket_post_create(struct socket *sock, int family,
static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
int addrlen)
{
-#if IS_ENABLED(CONFIG_IPV6)
if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
smk_ipv6_port_label(sock, address);
-#endif
-
return 0;
}
-#endif /* !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
/**
* smack_socket_connect - connect access check
@@ -2546,6 +2807,13 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
int addrlen)
{
int rc = 0;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ struct smack_known *rsp;
+ struct socket_smack *ssp = sock->sk->sk_security;
+#endif
if (sock->sk == NULL)
return 0;
@@ -2559,10 +2827,15 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
case PF_INET6:
if (addrlen < sizeof(struct sockaddr_in6))
return -EINVAL;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
- rc = smk_ipv6_port_check(sock->sk, (struct sockaddr_in6 *)sap,
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ rsp = smack_ipv6host_label(sip);
+ if (rsp != NULL)
+ rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
SMK_CONNECTING);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
+ rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING);
+#endif
break;
}
return rc;
@@ -3116,6 +3389,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
isp->smk_inode = smk_of_current();
break;
+ case PIPEFS_MAGIC:
+ isp->smk_inode = smk_of_current();
+ break;
default:
isp->smk_inode = sbsp->smk_root;
break;
@@ -3204,7 +3480,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
dp = dget(opt_dentry);
skp = smk_fetch(XATTR_NAME_SMACK, inode, dp);
- if (skp != NULL)
+ if (!IS_ERR_OR_NULL(skp))
final = skp;
/*
@@ -3241,11 +3517,14 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* Don't let the exec or mmap label be "*" or "@".
*/
skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
- if (skp == &smack_known_star || skp == &smack_known_web)
+ if (IS_ERR(skp) || skp == &smack_known_star ||
+ skp == &smack_known_web)
skp = NULL;
isp->smk_task = skp;
+
skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
- if (skp == &smack_known_star || skp == &smack_known_web)
+ if (IS_ERR(skp) || skp == &smack_known_star ||
+ skp == &smack_known_web)
skp = NULL;
isp->smk_mmap = skp;
@@ -3308,9 +3587,11 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
static int smack_setprocattr(struct task_struct *p, char *name,
void *value, size_t size)
{
- struct task_smack *tsp;
+ struct task_smack *tsp = current_security();
struct cred *new;
struct smack_known *skp;
+ struct smack_known_list_elem *sklep;
+ int rc;
/*
* Changing another process' Smack value is too dangerous
@@ -3319,7 +3600,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (p != current)
return -EPERM;
- if (!smack_privileged(CAP_MAC_ADMIN))
+ if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
return -EPERM;
if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
@@ -3329,8 +3610,8 @@ static int smack_setprocattr(struct task_struct *p, char *name,
return -EINVAL;
skp = smk_import_entry(value, size);
- if (skp == NULL)
- return -EINVAL;
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
/*
* No process is ever allowed the web ("@") label.
@@ -3338,12 +3619,27 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (skp == &smack_known_web)
return -EPERM;
+ if (!smack_privileged(CAP_MAC_ADMIN)) {
+ rc = -EPERM;
+ list_for_each_entry(sklep, &tsp->smk_relabel, list)
+ if (sklep->smk_label == skp) {
+ rc = 0;
+ break;
+ }
+ if (rc)
+ return rc;
+ }
+
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
tsp = new->security;
tsp->smk_task = skp;
+ /*
+ * process can change its label only once
+ */
+ smk_destroy_label_list(&tsp->smk_relabel);
commit_creds(new);
return size;
@@ -3445,9 +3741,13 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ struct socket_smack *ssp = sock->sk->sk_security;
+ struct smack_known *rsp;
+#endif
int rc = 0;
/*
@@ -3461,9 +3761,15 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
rc = smack_netlabel_send(sock->sk, sip);
break;
case AF_INET6:
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ rsp = smack_ipv6host_label(sap);
+ if (rsp != NULL)
+ rc = smk_ipv6_check(ssp->smk_out, rsp, sap,
+ SMK_CONNECTING);
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
break;
}
return rc;
@@ -3677,10 +3983,12 @@ access_check:
proto = smk_skb_to_addr_ipv6(skb, &sadd);
if (proto != IPPROTO_UDP && proto != IPPROTO_TCP)
break;
-#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_SECMARK_LABELING
if (skb && skb->secmark != 0)
skp = smack_from_secid(skb->secmark);
else
+ skp = smack_ipv6host_label(&sadd);
+ if (skp == NULL)
skp = smack_net_ambient;
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
@@ -3691,9 +3999,10 @@ access_check:
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in,
MAY_WRITE, rc);
-#else /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_SECMARK_LABELING */
+#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
break;
#endif /* CONFIG_IPV6 */
}
@@ -3791,13 +4100,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
}
netlbl_secattr_destroy(&secattr);
break;
-#if IS_ENABLED(CONFIG_IPV6)
case PF_INET6:
-#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_SECMARK_LABELING
s = skb->secmark;
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
break;
-#endif /* CONFIG_IPV6 */
}
*secid = s;
if (s == 0)
@@ -3920,7 +4227,7 @@ access_check:
hdr = ip_hdr(skb);
addr.sin_addr.s_addr = hdr->saddr;
rcu_read_lock();
- hskp = smack_host_label(&addr);
+ hskp = smack_ipv4host_label(&addr);
rcu_read_unlock();
if (hskp == NULL)
@@ -4105,8 +4412,10 @@ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
return -EINVAL;
skp = smk_import_entry(rulestr, 0);
- if (skp)
- *rule = skp->smk_known;
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+
+ *rule = skp->smk_known;
return 0;
}
@@ -4266,147 +4575,147 @@ static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
return 0;
}
-struct security_operations smack_ops = {
- .name = "smack",
-
- .ptrace_access_check = smack_ptrace_access_check,
- .ptrace_traceme = smack_ptrace_traceme,
- .syslog = smack_syslog,
-
- .sb_alloc_security = smack_sb_alloc_security,
- .sb_free_security = smack_sb_free_security,
- .sb_copy_data = smack_sb_copy_data,
- .sb_kern_mount = smack_sb_kern_mount,
- .sb_statfs = smack_sb_statfs,
-
- .bprm_set_creds = smack_bprm_set_creds,
- .bprm_committing_creds = smack_bprm_committing_creds,
- .bprm_secureexec = smack_bprm_secureexec,
-
- .inode_alloc_security = smack_inode_alloc_security,
- .inode_free_security = smack_inode_free_security,
- .inode_init_security = smack_inode_init_security,
- .inode_link = smack_inode_link,
- .inode_unlink = smack_inode_unlink,
- .inode_rmdir = smack_inode_rmdir,
- .inode_rename = smack_inode_rename,
- .inode_permission = smack_inode_permission,
- .inode_setattr = smack_inode_setattr,
- .inode_getattr = smack_inode_getattr,
- .inode_setxattr = smack_inode_setxattr,
- .inode_post_setxattr = smack_inode_post_setxattr,
- .inode_getxattr = smack_inode_getxattr,
- .inode_removexattr = smack_inode_removexattr,
- .inode_getsecurity = smack_inode_getsecurity,
- .inode_setsecurity = smack_inode_setsecurity,
- .inode_listsecurity = smack_inode_listsecurity,
- .inode_getsecid = smack_inode_getsecid,
-
- .file_permission = smack_file_permission,
- .file_alloc_security = smack_file_alloc_security,
- .file_free_security = smack_file_free_security,
- .file_ioctl = smack_file_ioctl,
- .file_lock = smack_file_lock,
- .file_fcntl = smack_file_fcntl,
- .mmap_file = smack_mmap_file,
- .mmap_addr = cap_mmap_addr,
- .file_set_fowner = smack_file_set_fowner,
- .file_send_sigiotask = smack_file_send_sigiotask,
- .file_receive = smack_file_receive,
-
- .file_open = smack_file_open,
-
- .cred_alloc_blank = smack_cred_alloc_blank,
- .cred_free = smack_cred_free,
- .cred_prepare = smack_cred_prepare,
- .cred_transfer = smack_cred_transfer,
- .kernel_act_as = smack_kernel_act_as,
- .kernel_create_files_as = smack_kernel_create_files_as,
- .task_setpgid = smack_task_setpgid,
- .task_getpgid = smack_task_getpgid,
- .task_getsid = smack_task_getsid,
- .task_getsecid = smack_task_getsecid,
- .task_setnice = smack_task_setnice,
- .task_setioprio = smack_task_setioprio,
- .task_getioprio = smack_task_getioprio,
- .task_setscheduler = smack_task_setscheduler,
- .task_getscheduler = smack_task_getscheduler,
- .task_movememory = smack_task_movememory,
- .task_kill = smack_task_kill,
- .task_wait = smack_task_wait,
- .task_to_inode = smack_task_to_inode,
-
- .ipc_permission = smack_ipc_permission,
- .ipc_getsecid = smack_ipc_getsecid,
-
- .msg_msg_alloc_security = smack_msg_msg_alloc_security,
- .msg_msg_free_security = smack_msg_msg_free_security,
-
- .msg_queue_alloc_security = smack_msg_queue_alloc_security,
- .msg_queue_free_security = smack_msg_queue_free_security,
- .msg_queue_associate = smack_msg_queue_associate,
- .msg_queue_msgctl = smack_msg_queue_msgctl,
- .msg_queue_msgsnd = smack_msg_queue_msgsnd,
- .msg_queue_msgrcv = smack_msg_queue_msgrcv,
-
- .shm_alloc_security = smack_shm_alloc_security,
- .shm_free_security = smack_shm_free_security,
- .shm_associate = smack_shm_associate,
- .shm_shmctl = smack_shm_shmctl,
- .shm_shmat = smack_shm_shmat,
-
- .sem_alloc_security = smack_sem_alloc_security,
- .sem_free_security = smack_sem_free_security,
- .sem_associate = smack_sem_associate,
- .sem_semctl = smack_sem_semctl,
- .sem_semop = smack_sem_semop,
-
- .d_instantiate = smack_d_instantiate,
-
- .getprocattr = smack_getprocattr,
- .setprocattr = smack_setprocattr,
-
- .unix_stream_connect = smack_unix_stream_connect,
- .unix_may_send = smack_unix_may_send,
-
- .socket_post_create = smack_socket_post_create,
-#ifndef CONFIG_SECURITY_SMACK_NETFILTER
- .socket_bind = smack_socket_bind,
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
- .socket_connect = smack_socket_connect,
- .socket_sendmsg = smack_socket_sendmsg,
- .socket_sock_rcv_skb = smack_socket_sock_rcv_skb,
- .socket_getpeersec_stream = smack_socket_getpeersec_stream,
- .socket_getpeersec_dgram = smack_socket_getpeersec_dgram,
- .sk_alloc_security = smack_sk_alloc_security,
- .sk_free_security = smack_sk_free_security,
- .sock_graft = smack_sock_graft,
- .inet_conn_request = smack_inet_conn_request,
- .inet_csk_clone = smack_inet_csk_clone,
+static struct security_hook_list smack_hooks[] = {
+ LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
+ LSM_HOOK_INIT(syslog, smack_syslog),
+
+ LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
+ LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
+ LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
+ LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
+ LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
+ LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
+ LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
+
+ LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
+ LSM_HOOK_INIT(bprm_committing_creds, smack_bprm_committing_creds),
+ LSM_HOOK_INIT(bprm_secureexec, smack_bprm_secureexec),
+
+ LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
+ LSM_HOOK_INIT(inode_free_security, smack_inode_free_security),
+ LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
+ LSM_HOOK_INIT(inode_link, smack_inode_link),
+ LSM_HOOK_INIT(inode_unlink, smack_inode_unlink),
+ LSM_HOOK_INIT(inode_rmdir, smack_inode_rmdir),
+ LSM_HOOK_INIT(inode_rename, smack_inode_rename),
+ LSM_HOOK_INIT(inode_permission, smack_inode_permission),
+ LSM_HOOK_INIT(inode_setattr, smack_inode_setattr),
+ LSM_HOOK_INIT(inode_getattr, smack_inode_getattr),
+ LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
+ LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
+ LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),
+ LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr),
+ LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity),
+ LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity),
+ LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity),
+ LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid),
+
+ LSM_HOOK_INIT(file_permission, smack_file_permission),
+ LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
+ LSM_HOOK_INIT(file_free_security, smack_file_free_security),
+ LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
+ LSM_HOOK_INIT(file_lock, smack_file_lock),
+ LSM_HOOK_INIT(file_fcntl, smack_file_fcntl),
+ LSM_HOOK_INIT(mmap_file, smack_mmap_file),
+ LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
+ LSM_HOOK_INIT(file_set_fowner, smack_file_set_fowner),
+ LSM_HOOK_INIT(file_send_sigiotask, smack_file_send_sigiotask),
+ LSM_HOOK_INIT(file_receive, smack_file_receive),
+
+ LSM_HOOK_INIT(file_open, smack_file_open),
+
+ LSM_HOOK_INIT(cred_alloc_blank, smack_cred_alloc_blank),
+ LSM_HOOK_INIT(cred_free, smack_cred_free),
+ LSM_HOOK_INIT(cred_prepare, smack_cred_prepare),
+ LSM_HOOK_INIT(cred_transfer, smack_cred_transfer),
+ LSM_HOOK_INIT(kernel_act_as, smack_kernel_act_as),
+ LSM_HOOK_INIT(kernel_create_files_as, smack_kernel_create_files_as),
+ LSM_HOOK_INIT(task_setpgid, smack_task_setpgid),
+ LSM_HOOK_INIT(task_getpgid, smack_task_getpgid),
+ LSM_HOOK_INIT(task_getsid, smack_task_getsid),
+ LSM_HOOK_INIT(task_getsecid, smack_task_getsecid),
+ LSM_HOOK_INIT(task_setnice, smack_task_setnice),
+ LSM_HOOK_INIT(task_setioprio, smack_task_setioprio),
+ LSM_HOOK_INIT(task_getioprio, smack_task_getioprio),
+ LSM_HOOK_INIT(task_setscheduler, smack_task_setscheduler),
+ LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler),
+ LSM_HOOK_INIT(task_movememory, smack_task_movememory),
+ LSM_HOOK_INIT(task_kill, smack_task_kill),
+ LSM_HOOK_INIT(task_wait, smack_task_wait),
+ LSM_HOOK_INIT(task_to_inode, smack_task_to_inode),
+
+ LSM_HOOK_INIT(ipc_permission, smack_ipc_permission),
+ LSM_HOOK_INIT(ipc_getsecid, smack_ipc_getsecid),
+
+ LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security),
+ LSM_HOOK_INIT(msg_msg_free_security, smack_msg_msg_free_security),
+
+ LSM_HOOK_INIT(msg_queue_alloc_security, smack_msg_queue_alloc_security),
+ LSM_HOOK_INIT(msg_queue_free_security, smack_msg_queue_free_security),
+ LSM_HOOK_INIT(msg_queue_associate, smack_msg_queue_associate),
+ LSM_HOOK_INIT(msg_queue_msgctl, smack_msg_queue_msgctl),
+ LSM_HOOK_INIT(msg_queue_msgsnd, smack_msg_queue_msgsnd),
+ LSM_HOOK_INIT(msg_queue_msgrcv, smack_msg_queue_msgrcv),
+
+ LSM_HOOK_INIT(shm_alloc_security, smack_shm_alloc_security),
+ LSM_HOOK_INIT(shm_free_security, smack_shm_free_security),
+ LSM_HOOK_INIT(shm_associate, smack_shm_associate),
+ LSM_HOOK_INIT(shm_shmctl, smack_shm_shmctl),
+ LSM_HOOK_INIT(shm_shmat, smack_shm_shmat),
+
+ LSM_HOOK_INIT(sem_alloc_security, smack_sem_alloc_security),
+ LSM_HOOK_INIT(sem_free_security, smack_sem_free_security),
+ LSM_HOOK_INIT(sem_associate, smack_sem_associate),
+ LSM_HOOK_INIT(sem_semctl, smack_sem_semctl),
+ LSM_HOOK_INIT(sem_semop, smack_sem_semop),
+
+ LSM_HOOK_INIT(d_instantiate, smack_d_instantiate),
+
+ LSM_HOOK_INIT(getprocattr, smack_getprocattr),
+ LSM_HOOK_INIT(setprocattr, smack_setprocattr),
+
+ LSM_HOOK_INIT(unix_stream_connect, smack_unix_stream_connect),
+ LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
+
+ LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
+#ifdef SMACK_IPV6_PORT_LABELING
+ LSM_HOOK_INIT(socket_bind, smack_socket_bind),
+#endif
+ LSM_HOOK_INIT(socket_connect, smack_socket_connect),
+ LSM_HOOK_INIT(socket_sendmsg, smack_socket_sendmsg),
+ LSM_HOOK_INIT(socket_sock_rcv_skb, smack_socket_sock_rcv_skb),
+ LSM_HOOK_INIT(socket_getpeersec_stream, smack_socket_getpeersec_stream),
+ LSM_HOOK_INIT(socket_getpeersec_dgram, smack_socket_getpeersec_dgram),
+ LSM_HOOK_INIT(sk_alloc_security, smack_sk_alloc_security),
+ LSM_HOOK_INIT(sk_free_security, smack_sk_free_security),
+ LSM_HOOK_INIT(sock_graft, smack_sock_graft),
+ LSM_HOOK_INIT(inet_conn_request, smack_inet_conn_request),
+ LSM_HOOK_INIT(inet_csk_clone, smack_inet_csk_clone),
/* key management security hooks */
#ifdef CONFIG_KEYS
- .key_alloc = smack_key_alloc,
- .key_free = smack_key_free,
- .key_permission = smack_key_permission,
- .key_getsecurity = smack_key_getsecurity,
+ LSM_HOOK_INIT(key_alloc, smack_key_alloc),
+ LSM_HOOK_INIT(key_free, smack_key_free),
+ LSM_HOOK_INIT(key_permission, smack_key_permission),
+ LSM_HOOK_INIT(key_getsecurity, smack_key_getsecurity),
#endif /* CONFIG_KEYS */
/* Audit hooks */
#ifdef CONFIG_AUDIT
- .audit_rule_init = smack_audit_rule_init,
- .audit_rule_known = smack_audit_rule_known,
- .audit_rule_match = smack_audit_rule_match,
- .audit_rule_free = smack_audit_rule_free,
+ LSM_HOOK_INIT(audit_rule_init, smack_audit_rule_init),
+ LSM_HOOK_INIT(audit_rule_known, smack_audit_rule_known),
+ LSM_HOOK_INIT(audit_rule_match, smack_audit_rule_match),
+ LSM_HOOK_INIT(audit_rule_free, smack_audit_rule_free),
#endif /* CONFIG_AUDIT */
- .ismaclabel = smack_ismaclabel,
- .secid_to_secctx = smack_secid_to_secctx,
- .secctx_to_secid = smack_secctx_to_secid,
- .release_secctx = smack_release_secctx,
- .inode_notifysecctx = smack_inode_notifysecctx,
- .inode_setsecctx = smack_inode_setsecctx,
- .inode_getsecctx = smack_inode_getsecctx,
+ LSM_HOOK_INIT(ismaclabel, smack_ismaclabel),
+ LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx),
+ LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid),
+ LSM_HOOK_INIT(release_secctx, smack_release_secctx),
+ LSM_HOOK_INIT(inode_notifysecctx, smack_inode_notifysecctx),
+ LSM_HOOK_INIT(inode_setsecctx, smack_inode_setsecctx),
+ LSM_HOOK_INIT(inode_getsecctx, smack_inode_getsecctx),
};
@@ -4451,11 +4760,9 @@ static __init int smack_init(void)
struct cred *cred;
struct task_smack *tsp;
- if (!security_module_enable(&smack_ops))
+ if (!security_module_enable("smack"))
return 0;
- smack_enabled = 1;
-
smack_inode_cache = KMEM_CACHE(inode_smack, 0);
if (!smack_inode_cache)
return -ENOMEM;
@@ -4467,7 +4774,18 @@ static __init int smack_init(void)
return -ENOMEM;
}
- printk(KERN_INFO "Smack: Initializing.\n");
+ smack_enabled = 1;
+
+ pr_info("Smack: Initializing.\n");
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+ pr_info("Smack: Netfilter enabled.\n");
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
+ pr_info("Smack: IPv6 port labeling enabled.\n");
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ pr_info("Smack: IPv6 Netfilter enabled.\n");
+#endif
/*
* Set the security state for the initial task.
@@ -4481,8 +4799,7 @@ static __init int smack_init(void)
/*
* Register with LSM
*/
- if (register_security(&smack_ops))
- panic("smack: Unable to register with kernel.\n");
+ security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
return 0;
}
diff --git a/kernel/security/smack/smack_netfilter.c b/kernel/security/smack/smack_netfilter.c
index a455cfc9e..aa6bf1b22 100644
--- a/kernel/security/smack/smack_netfilter.c
+++ b/kernel/security/smack/smack_netfilter.c
@@ -17,19 +17,21 @@
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netdevice.h>
+#include <net/inet_sock.h>
#include "smack.h"
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
+static unsigned int smack_ipv6_output(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
+ struct sock *sk = skb_to_full_sk(skb);
struct socket_smack *ssp;
struct smack_known *skp;
- if (skb && skb->sk && skb->sk->sk_security) {
- ssp = skb->sk->sk_security;
+ if (sk && sk->sk_security) {
+ ssp = sk->sk_security;
skp = ssp->smk_out;
skb->secmark = skp->smk_secid;
}
@@ -38,15 +40,16 @@ static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
}
#endif /* IPV6 */
-static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
+static unsigned int smack_ipv4_output(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
+ struct sock *sk = skb_to_full_sk(skb);
struct socket_smack *ssp;
struct smack_known *skp;
- if (skb && skb->sk && skb->sk->sk_security) {
- ssp = skb->sk->sk_security;
+ if (sk && sk->sk_security) {
+ ssp = sk->sk_security;
skp = ssp->smk_out;
skb->secmark = skp->smk_secid;
}
@@ -57,7 +60,6 @@ static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
static struct nf_hook_ops smack_nf_ops[] = {
{
.hook = smack_ipv4_output,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SELINUX_FIRST,
@@ -65,7 +67,6 @@ static struct nf_hook_ops smack_nf_ops[] = {
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
{
.hook = smack_ipv6_output,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_SELINUX_FIRST,
diff --git a/kernel/security/smack/smackfs.c b/kernel/security/smack/smackfs.c
index ac4cac7c6..94bd9e41c 100644
--- a/kernel/security/smack/smackfs.c
+++ b/kernel/security/smack/smackfs.c
@@ -29,6 +29,7 @@
#include <linux/magic.h>
#include "smack.h"
+#define BEBITS (sizeof(__be32) * 8)
/*
* smackfs pseudo filesystem.
*/
@@ -40,7 +41,7 @@ enum smk_inos {
SMK_DOI = 5, /* CIPSO DOI */
SMK_DIRECT = 6, /* CIPSO level indicating direct label */
SMK_AMBIENT = 7, /* internet ambient label */
- SMK_NETLBLADDR = 8, /* single label hosts */
+ SMK_NET4ADDR = 8, /* single label hosts */
SMK_ONLYCAP = 9, /* the only "capable" label */
SMK_LOGGING = 10, /* logging */
SMK_LOAD_SELF = 11, /* task specific rules */
@@ -57,6 +58,10 @@ enum smk_inos {
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
SMK_UNCONFINED = 22, /* define an unconfined label */
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ SMK_NET6ADDR = 23, /* single label IPv6 hosts */
+#endif /* CONFIG_IPV6 */
+ SMK_RELABEL_SELF = 24, /* relabel possible without CAP_MAC_ADMIN */
};
/*
@@ -64,7 +69,10 @@ enum smk_inos {
*/
static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock);
-static DEFINE_MUTEX(smk_netlbladdr_lock);
+static DEFINE_MUTEX(smk_net4addr_lock);
+#if IS_ENABLED(CONFIG_IPV6)
+static DEFINE_MUTEX(smk_net6addr_lock);
+#endif /* CONFIG_IPV6 */
/*
* This is the "ambient" label for network traffic.
@@ -87,16 +95,6 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
*/
int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
-/*
- * Unless a process is running with this label even
- * having CAP_MAC_OVERRIDE isn't enough to grant
- * privilege to violate MAC policy. If no label is
- * designated (the NULL case) capabilities apply to
- * everyone. It is expected that the hat (^) label
- * will be used if any label is used.
- */
-struct smack_known *smack_onlycap;
-
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
/*
* Allow one label to be unconfined. This is for
@@ -128,7 +126,10 @@ int smack_ptrace_rule = SMACK_PTRACE_DEFAULT;
* can write to the specified label.
*/
-LIST_HEAD(smk_netlbladdr_list);
+LIST_HEAD(smk_net4addr_list);
+#if IS_ENABLED(CONFIG_IPV6)
+LIST_HEAD(smk_net6addr_list);
+#endif /* CONFIG_IPV6 */
/*
* Rule lists are maintained for each label.
@@ -139,7 +140,7 @@ struct smack_master_list {
struct smack_rule *smk_rule;
};
-LIST_HEAD(smack_rule_list);
+static LIST_HEAD(smack_rule_list);
struct smack_parsed_rule {
struct smack_known *smk_subject;
@@ -150,11 +151,6 @@ struct smack_parsed_rule {
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
-struct smack_known smack_cipso_option = {
- .smk_known = SMACK_CIPSO_OPTION,
- .smk_secid = 0,
-};
-
/*
* Values for parsing cipso rules
* SMK_DIGITLEN: Length of a digit field in a rule.
@@ -338,8 +334,7 @@ static int smk_perm_from_str(const char *string)
* @import: if non-zero, import labels
* @len: label length limit
*
- * Returns 0 on success, -EINVAL on failure and -ENOENT when either subject
- * or object is missing.
+ * Returns 0 on success, appropriate error code on failure.
*/
static int smk_fill_rule(const char *subject, const char *object,
const char *access1, const char *access2,
@@ -351,16 +346,16 @@ static int smk_fill_rule(const char *subject, const char *object,
if (import) {
rule->smk_subject = smk_import_entry(subject, len);
- if (rule->smk_subject == NULL)
- return -EINVAL;
+ if (IS_ERR(rule->smk_subject))
+ return PTR_ERR(rule->smk_subject);
rule->smk_object = smk_import_entry(object, len);
- if (rule->smk_object == NULL)
- return -EINVAL;
+ if (IS_ERR(rule->smk_object))
+ return PTR_ERR(rule->smk_object);
} else {
cp = smk_parse_smack(subject, len);
- if (cp == NULL)
- return -EINVAL;
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
skp = smk_find_entry(cp);
kfree(cp);
if (skp == NULL)
@@ -368,8 +363,8 @@ static int smk_fill_rule(const char *subject, const char *object,
rule->smk_subject = skp;
cp = smk_parse_smack(object, len);
- if (cp == NULL)
- return -EINVAL;
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
skp = smk_find_entry(cp);
kfree(cp);
if (skp == NULL)
@@ -412,7 +407,7 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule,
* @import: if non-zero, import labels
* @tokens: numer of substrings expected in data
*
- * Returns number of processed bytes on success, -1 on failure.
+ * Returns number of processed bytes on success, -ERRNO on failure.
*/
static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
int import, int tokens)
@@ -431,7 +426,7 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
if (data[cnt] == '\0')
/* Unexpected end of data */
- return -1;
+ return -EINVAL;
tok[i] = data + cnt;
@@ -529,14 +524,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
while (cnt < count) {
if (format == SMK_FIXED24_FMT) {
rc = smk_parse_rule(data, &rule, 1);
- if (rc != 0) {
- rc = -EINVAL;
+ if (rc < 0)
goto out;
- }
cnt = count;
} else {
rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens);
- if (rc <= 0) {
+ if (rc < 0)
+ goto out;
+ if (rc == 0) {
rc = -EINVAL;
goto out;
}
@@ -567,23 +562,17 @@ static void *smk_seq_start(struct seq_file *s, loff_t *pos,
struct list_head *head)
{
struct list_head *list;
+ int i = *pos;
+
+ rcu_read_lock();
+ for (list = rcu_dereference(list_next_rcu(head));
+ list != head;
+ list = rcu_dereference(list_next_rcu(list))) {
+ if (i-- == 0)
+ return list;
+ }
- /*
- * This is 0 the first time through.
- */
- if (s->index == 0)
- s->private = head;
-
- if (s->private == NULL)
- return NULL;
-
- list = s->private;
- if (list_empty(list))
- return NULL;
-
- if (s->index == 0)
- return list->next;
- return list;
+ return NULL;
}
static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
@@ -591,17 +580,15 @@ static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
{
struct list_head *list = v;
- if (list_is_last(list, head)) {
- s->private = NULL;
- return NULL;
- }
- s->private = list->next;
- return list->next;
+ ++*pos;
+ list = rcu_dereference(list_next_rcu(list));
+
+ return (list == head) ? NULL : list;
}
static void smk_seq_stop(struct seq_file *s, void *v)
{
- /* No-op */
+ rcu_read_unlock();
}
static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
@@ -661,7 +648,7 @@ static int load_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_master_list *smlp =
- list_entry(list, struct smack_master_list, list);
+ list_entry_rcu(list, struct smack_master_list, list);
smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN);
@@ -809,7 +796,7 @@ static int cipso_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_known *skp =
- list_entry(list, struct smack_known, list);
+ list_entry_rcu(list, struct smack_known, list);
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
char sep = '/';
int i;
@@ -915,8 +902,10 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
mutex_lock(&smack_cipso_lock);
skp = smk_import_entry(rule, 0);
- if (skp == NULL)
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
goto out;
+ }
if (format == SMK_FIXED24_FMT)
rule += SMK_LABELLEN;
@@ -998,7 +987,7 @@ static int cipso2_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_known *skp =
- list_entry(list, struct smack_known, list);
+ list_entry_rcu(list, struct smack_known, list);
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
char sep = '/';
int i;
@@ -1064,92 +1053,90 @@ static const struct file_operations smk_cipso2_ops = {
* Seq_file read operations for /smack/netlabel
*/
-static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
+static void *net4addr_seq_start(struct seq_file *s, loff_t *pos)
{
- return smk_seq_start(s, pos, &smk_netlbladdr_list);
+ return smk_seq_start(s, pos, &smk_net4addr_list);
}
-static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
+static void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- return smk_seq_next(s, v, pos, &smk_netlbladdr_list);
+ return smk_seq_next(s, v, pos, &smk_net4addr_list);
}
-#define BEBITS (sizeof(__be32) * 8)
/*
* Print host/label pairs
*/
-static int netlbladdr_seq_show(struct seq_file *s, void *v)
+static int net4addr_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
- struct smk_netlbladdr *skp =
- list_entry(list, struct smk_netlbladdr, list);
- unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
- int maskn;
- u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
+ struct smk_net4addr *skp =
+ list_entry_rcu(list, struct smk_net4addr, list);
+ char *kp = SMACK_CIPSO_OPTION;
- for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
-
- seq_printf(s, "%u.%u.%u.%u/%d %s\n",
- hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known);
+ if (skp->smk_label != NULL)
+ kp = skp->smk_label->smk_known;
+ seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr,
+ skp->smk_masks, kp);
return 0;
}
-static const struct seq_operations netlbladdr_seq_ops = {
- .start = netlbladdr_seq_start,
- .next = netlbladdr_seq_next,
- .show = netlbladdr_seq_show,
+static const struct seq_operations net4addr_seq_ops = {
+ .start = net4addr_seq_start,
+ .next = net4addr_seq_next,
+ .show = net4addr_seq_show,
.stop = smk_seq_stop,
};
/**
- * smk_open_netlbladdr - open() for /smack/netlabel
+ * smk_open_net4addr - open() for /smack/netlabel
* @inode: inode structure representing file
* @file: "netlabel" file pointer
*
- * Connect our netlbladdr_seq_* operations with /smack/netlabel
+ * Connect our net4addr_seq_* operations with /smack/netlabel
* file_operations
*/
-static int smk_open_netlbladdr(struct inode *inode, struct file *file)
+static int smk_open_net4addr(struct inode *inode, struct file *file)
{
- return seq_open(file, &netlbladdr_seq_ops);
+ return seq_open(file, &net4addr_seq_ops);
}
/**
- * smk_netlbladdr_insert
+ * smk_net4addr_insert
* @new : netlabel to insert
*
- * This helper insert netlabel in the smack_netlbladdrs list
+ * This helper insert netlabel in the smack_net4addrs list
* sorted by netmask length (longest to smallest)
- * locked by &smk_netlbladdr_lock in smk_write_netlbladdr
+ * locked by &smk_net4addr_lock in smk_write_net4addr
*
*/
-static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
+static void smk_net4addr_insert(struct smk_net4addr *new)
{
- struct smk_netlbladdr *m, *m_next;
+ struct smk_net4addr *m;
+ struct smk_net4addr *m_next;
- if (list_empty(&smk_netlbladdr_list)) {
- list_add_rcu(&new->list, &smk_netlbladdr_list);
+ if (list_empty(&smk_net4addr_list)) {
+ list_add_rcu(&new->list, &smk_net4addr_list);
return;
}
- m = list_entry_rcu(smk_netlbladdr_list.next,
- struct smk_netlbladdr, list);
+ m = list_entry_rcu(smk_net4addr_list.next,
+ struct smk_net4addr, list);
/* the comparison '>' is a bit hacky, but works */
- if (new->smk_mask.s_addr > m->smk_mask.s_addr) {
- list_add_rcu(&new->list, &smk_netlbladdr_list);
+ if (new->smk_masks > m->smk_masks) {
+ list_add_rcu(&new->list, &smk_net4addr_list);
return;
}
- list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) {
- if (list_is_last(&m->list, &smk_netlbladdr_list)) {
+ list_for_each_entry_rcu(m, &smk_net4addr_list, list) {
+ if (list_is_last(&m->list, &smk_net4addr_list)) {
list_add_rcu(&new->list, &m->list);
return;
}
m_next = list_entry_rcu(m->list.next,
- struct smk_netlbladdr, list);
- if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) {
+ struct smk_net4addr, list);
+ if (new->smk_masks > m_next->smk_masks) {
list_add_rcu(&new->list, &m->list);
return;
}
@@ -1158,28 +1145,29 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
/**
- * smk_write_netlbladdr - write() for /smack/netlabel
+ * smk_write_net4addr - write() for /smack/netlabel
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
- * Accepts only one netlbladdr per write call.
+ * Accepts only one net4addr per write call.
* Returns number of bytes written or error code, as appropriate
*/
-static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
+static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct smk_netlbladdr *snp;
+ struct smk_net4addr *snp;
struct sockaddr_in newname;
char *smack;
- struct smack_known *skp;
+ struct smack_known *skp = NULL;
char *data;
char *host = (char *)&newname.sin_addr.s_addr;
int rc;
struct netlbl_audit audit_info;
struct in_addr mask;
unsigned int m;
+ unsigned int masks;
int found;
u32 mask_bits = (1<<31);
__be32 nsa;
@@ -1217,7 +1205,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
data[count] = '\0';
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",
- &host[0], &host[1], &host[2], &host[3], &m, smack);
+ &host[0], &host[1], &host[2], &host[3], &masks, smack);
if (rc != 6) {
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
&host[0], &host[1], &host[2], &host[3], smack);
@@ -1226,8 +1214,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
goto free_out;
}
m = BEBITS;
+ masks = 32;
}
- if (m > BEBITS) {
+ if (masks > BEBITS) {
rc = -EINVAL;
goto free_out;
}
@@ -1237,21 +1226,21 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
*/
if (smack[0] != '-') {
skp = smk_import_entry(smack, 0);
- if (skp == NULL) {
- rc = -EINVAL;
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
goto free_out;
}
} else {
- /* check known options */
- if (strcmp(smack, smack_cipso_option.smk_known) == 0)
- skp = &smack_cipso_option;
- else {
+ /*
+ * Only the -CIPSO option is supported for IPv4
+ */
+ if (strcmp(smack, SMACK_CIPSO_OPTION) != 0) {
rc = -EINVAL;
goto free_out;
}
}
- for (temp_mask = 0; m > 0; m--) {
+ for (m = masks, temp_mask = 0; m > 0; m--) {
temp_mask |= mask_bits;
mask_bits >>= 1;
}
@@ -1262,14 +1251,13 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* Only allow one writer at a time. Writes should be
* quite rare and small in any case.
*/
- mutex_lock(&smk_netlbladdr_lock);
+ mutex_lock(&smk_net4addr_lock);
nsa = newname.sin_addr.s_addr;
/* try to find if the prefix is already in the list */
found = 0;
- list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) {
- if (snp->smk_host.sin_addr.s_addr == nsa &&
- snp->smk_mask.s_addr == mask.s_addr) {
+ list_for_each_entry_rcu(snp, &smk_net4addr_list, list) {
+ if (snp->smk_host.s_addr == nsa && snp->smk_masks == masks) {
found = 1;
break;
}
@@ -1282,17 +1270,20 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
rc = -ENOMEM;
else {
rc = 0;
- snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
+ snp->smk_host.s_addr = newname.sin_addr.s_addr;
snp->smk_mask.s_addr = mask.s_addr;
snp->smk_label = skp;
- smk_netlbladdr_insert(snp);
+ snp->smk_masks = masks;
+ smk_net4addr_insert(snp);
}
} else {
- /* we delete the unlabeled entry, only if the previous label
- * wasn't the special CIPSO option */
- if (snp->smk_label != &smack_cipso_option)
+ /*
+ * Delete the unlabeled entry, only if the previous label
+ * wasn't the special CIPSO option
+ */
+ if (snp->smk_label != NULL)
rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
- &snp->smk_host.sin_addr, &snp->smk_mask,
+ &snp->smk_host, &snp->smk_mask,
PF_INET, &audit_info);
else
rc = 0;
@@ -1304,15 +1295,15 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* this host so that incoming packets get labeled.
* but only if we didn't get the special CIPSO option
*/
- if (rc == 0 && skp != &smack_cipso_option)
+ if (rc == 0 && skp != NULL)
rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
- &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET,
+ &snp->smk_host, &snp->smk_mask, PF_INET,
snp->smk_label->smk_secid, &audit_info);
if (rc == 0)
rc = count;
- mutex_unlock(&smk_netlbladdr_lock);
+ mutex_unlock(&smk_net4addr_lock);
free_out:
kfree(smack);
@@ -1322,14 +1313,279 @@ free_data_out:
return rc;
}
-static const struct file_operations smk_netlbladdr_ops = {
- .open = smk_open_netlbladdr,
+static const struct file_operations smk_net4addr_ops = {
+ .open = smk_open_net4addr,
.read = seq_read,
.llseek = seq_lseek,
- .write = smk_write_netlbladdr,
+ .write = smk_write_net4addr,
.release = seq_release,
};
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * Seq_file read operations for /smack/netlabel6
+ */
+
+static void *net6addr_seq_start(struct seq_file *s, loff_t *pos)
+{
+ return smk_seq_start(s, pos, &smk_net6addr_list);
+}
+
+static void *net6addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return smk_seq_next(s, v, pos, &smk_net6addr_list);
+}
+
+/*
+ * Print host/label pairs
+ */
+static int net6addr_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smk_net6addr *skp =
+ list_entry(list, struct smk_net6addr, list);
+
+ if (skp->smk_label != NULL)
+ seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks,
+ skp->smk_label->smk_known);
+
+ return 0;
+}
+
+static const struct seq_operations net6addr_seq_ops = {
+ .start = net6addr_seq_start,
+ .next = net6addr_seq_next,
+ .show = net6addr_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_net6addr - open() for /smack/netlabel
+ * @inode: inode structure representing file
+ * @file: "netlabel" file pointer
+ *
+ * Connect our net6addr_seq_* operations with /smack/netlabel
+ * file_operations
+ */
+static int smk_open_net6addr(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &net6addr_seq_ops);
+}
+
+/**
+ * smk_net6addr_insert
+ * @new : entry to insert
+ *
+ * This inserts an entry in the smack_net6addrs list
+ * sorted by netmask length (longest to smallest)
+ * locked by &smk_net6addr_lock in smk_write_net6addr
+ *
+ */
+static void smk_net6addr_insert(struct smk_net6addr *new)
+{
+ struct smk_net6addr *m_next;
+ struct smk_net6addr *m;
+
+ if (list_empty(&smk_net6addr_list)) {
+ list_add_rcu(&new->list, &smk_net6addr_list);
+ return;
+ }
+
+ m = list_entry_rcu(smk_net6addr_list.next,
+ struct smk_net6addr, list);
+
+ if (new->smk_masks > m->smk_masks) {
+ list_add_rcu(&new->list, &smk_net6addr_list);
+ return;
+ }
+
+ list_for_each_entry_rcu(m, &smk_net6addr_list, list) {
+ if (list_is_last(&m->list, &smk_net6addr_list)) {
+ list_add_rcu(&new->list, &m->list);
+ return;
+ }
+ m_next = list_entry_rcu(m->list.next,
+ struct smk_net6addr, list);
+ if (new->smk_masks > m_next->smk_masks) {
+ list_add_rcu(&new->list, &m->list);
+ return;
+ }
+ }
+}
+
+
+/**
+ * smk_write_net6addr - write() for /smack/netlabel
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Accepts only one net6addr per write call.
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smk_net6addr *snp;
+ struct in6_addr newname;
+ struct in6_addr fullmask;
+ struct smack_known *skp = NULL;
+ char *smack;
+ char *data;
+ int rc = 0;
+ int found = 0;
+ int i;
+ unsigned int scanned[8];
+ unsigned int m;
+ unsigned int mask = 128;
+
+ /*
+ * Must have privilege.
+ * No partial writes.
+ * Enough data must be present.
+ * "<addr/mask, as a:b:c:d:e:f:g:h/e><space><label>"
+ * "<addr, as a:b:c:d:e:f:g:h><space><label>"
+ */
+ if (!smack_privileged(CAP_MAC_ADMIN))
+ return -EPERM;
+ if (*ppos != 0)
+ return -EINVAL;
+ if (count < SMK_NETLBLADDRMIN)
+ return -EINVAL;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto free_data_out;
+ }
+
+ smack = kzalloc(count + 1, GFP_KERNEL);
+ if (smack == NULL) {
+ rc = -ENOMEM;
+ goto free_data_out;
+ }
+
+ data[count] = '\0';
+
+ i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s",
+ &scanned[0], &scanned[1], &scanned[2], &scanned[3],
+ &scanned[4], &scanned[5], &scanned[6], &scanned[7],
+ &mask, smack);
+ if (i != 10) {
+ i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x %s",
+ &scanned[0], &scanned[1], &scanned[2],
+ &scanned[3], &scanned[4], &scanned[5],
+ &scanned[6], &scanned[7], smack);
+ if (i != 9) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ }
+ if (mask > 128) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ for (i = 0; i < 8; i++) {
+ if (scanned[i] > 0xffff) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ newname.s6_addr16[i] = htons(scanned[i]);
+ }
+
+ /*
+ * If smack begins with '-', it is an option, don't import it
+ */
+ if (smack[0] != '-') {
+ skp = smk_import_entry(smack, 0);
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
+ goto free_out;
+ }
+ } else {
+ /*
+ * Only -DELETE is supported for IPv6
+ */
+ if (strcmp(smack, SMACK_DELETE_OPTION) != 0) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ }
+
+ for (i = 0, m = mask; i < 8; i++) {
+ if (m >= 16) {
+ fullmask.s6_addr16[i] = 0xffff;
+ m -= 16;
+ } else if (m > 0) {
+ fullmask.s6_addr16[i] = (1 << m) - 1;
+ m = 0;
+ } else
+ fullmask.s6_addr16[i] = 0;
+ newname.s6_addr16[i] &= fullmask.s6_addr16[i];
+ }
+
+ /*
+ * Only allow one writer at a time. Writes should be
+ * quite rare and small in any case.
+ */
+ mutex_lock(&smk_net6addr_lock);
+ /*
+ * Try to find the prefix in the list
+ */
+ list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
+ if (mask != snp->smk_masks)
+ continue;
+ for (found = 1, i = 0; i < 8; i++) {
+ if (newname.s6_addr16[i] !=
+ snp->smk_host.s6_addr16[i]) {
+ found = 0;
+ break;
+ }
+ }
+ if (found == 1)
+ break;
+ }
+ if (found == 0) {
+ snp = kzalloc(sizeof(*snp), GFP_KERNEL);
+ if (snp == NULL)
+ rc = -ENOMEM;
+ else {
+ snp->smk_host = newname;
+ snp->smk_mask = fullmask;
+ snp->smk_masks = mask;
+ snp->smk_label = skp;
+ smk_net6addr_insert(snp);
+ }
+ } else {
+ snp->smk_label = skp;
+ }
+
+ if (rc == 0)
+ rc = count;
+
+ mutex_unlock(&smk_net6addr_lock);
+
+free_out:
+ kfree(smack);
+free_data_out:
+ kfree(data);
+
+ return rc;
+}
+
+static const struct file_operations smk_net6addr_ops = {
+ .open = smk_open_net6addr,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_net6addr,
+ .release = seq_release,
+};
+#endif /* CONFIG_IPV6 */
+
/**
* smk_read_doi - read() for /smack/doi
* @filp: file pointer, not actually used
@@ -1619,8 +1875,8 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
}
skp = smk_import_entry(data, count);
- if (skp == NULL) {
- rc = -EINVAL;
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
goto out;
}
@@ -1643,34 +1899,127 @@ static const struct file_operations smk_ambient_ops = {
.llseek = default_llseek,
};
+/*
+ * Seq_file operations for /smack/onlycap
+ */
+static void *onlycap_seq_start(struct seq_file *s, loff_t *pos)
+{
+ return smk_seq_start(s, pos, &smack_onlycap_list);
+}
+
+static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return smk_seq_next(s, v, pos, &smack_onlycap_list);
+}
+
+static int onlycap_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_known_list_elem *sklep =
+ list_entry_rcu(list, struct smack_known_list_elem, list);
+
+ seq_puts(s, sklep->smk_label->smk_known);
+ seq_putc(s, ' ');
+
+ return 0;
+}
+
+static const struct seq_operations onlycap_seq_ops = {
+ .start = onlycap_seq_start,
+ .next = onlycap_seq_next,
+ .show = onlycap_seq_show,
+ .stop = smk_seq_stop,
+};
+
+static int smk_open_onlycap(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &onlycap_seq_ops);
+}
+
/**
- * smk_read_onlycap - read() for smackfs/onlycap
- * @filp: file pointer, not actually used
- * @buf: where to put the result
- * @cn: maximum to send along
- * @ppos: where to start
+ * smk_list_swap_rcu - swap public list with a private one in RCU-safe way
+ * The caller must hold appropriate mutex to prevent concurrent modifications
+ * to the public list.
+ * Private list is assumed to be not accessible to other threads yet.
*
- * Returns number of bytes read or error code, as appropriate
+ * @public: public list
+ * @private: private list
*/
-static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
- size_t cn, loff_t *ppos)
+static void smk_list_swap_rcu(struct list_head *public,
+ struct list_head *private)
{
- char *smack = "";
- ssize_t rc = -EINVAL;
- int asize;
+ struct list_head *first, *last;
- if (*ppos != 0)
- return 0;
+ if (list_empty(public)) {
+ list_splice_init_rcu(private, public, synchronize_rcu);
+ } else {
+ /* Remember public list before replacing it */
+ first = public->next;
+ last = public->prev;
+
+ /* Publish private list in place of public in RCU-safe way */
+ private->prev->next = public;
+ private->next->prev = public;
+ rcu_assign_pointer(public->next, private->next);
+ public->prev = private->prev;
+
+ synchronize_rcu();
+
+ /* When all readers are done with the old public list,
+ * attach it in place of private */
+ private->next = first;
+ private->prev = last;
+ first->prev = private;
+ last->next = private;
+ }
+}
+
+/**
+ * smk_parse_label_list - parse list of Smack labels, separated by spaces
+ *
+ * @data: the string to parse
+ * @private: destination list
+ *
+ * Returns zero on success or error code, as appropriate
+ */
+static int smk_parse_label_list(char *data, struct list_head *list)
+{
+ char *tok;
+ struct smack_known *skp;
+ struct smack_known_list_elem *sklep;
- if (smack_onlycap != NULL)
- smack = smack_onlycap->smk_known;
+ while ((tok = strsep(&data, " ")) != NULL) {
+ if (!*tok)
+ continue;
- asize = strlen(smack) + 1;
+ skp = smk_import_entry(tok, 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
- if (cn >= asize)
- rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
+ sklep = kzalloc(sizeof(*sklep), GFP_KERNEL);
+ if (sklep == NULL)
+ return -ENOMEM;
- return rc;
+ sklep->smk_label = skp;
+ list_add(&sklep->list, list);
+ }
+
+ return 0;
+}
+
+/**
+ * smk_destroy_label_list - destroy a list of smack_known_list_elem
+ * @head: header pointer of the list to destroy
+ */
+void smk_destroy_label_list(struct list_head *list)
+{
+ struct smack_known_list_elem *sklep;
+ struct smack_known_list_elem *sklep2;
+
+ list_for_each_entry_safe(sklep, sklep2, list, list)
+ kfree(sklep);
+
+ INIT_LIST_HEAD(list);
}
/**
@@ -1686,47 +2035,52 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *data;
- struct smack_known *skp = smk_of_task(current->cred->security);
- int rc = count;
+ LIST_HEAD(list_tmp);
+ int rc;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- /*
- * This can be done using smk_access() but is done
- * explicitly for clarity. The smk_access() implementation
- * would use smk_access(smack_onlycap, MAY_WRITE)
- */
- if (smack_onlycap != NULL && smack_onlycap != skp)
- return -EPERM;
-
data = kzalloc(count + 1, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ rc = smk_parse_label_list(data, &list_tmp);
+ kfree(data);
+
/*
- * Should the null string be passed in unset the onlycap value.
- * This seems like something to be careful with as usually
- * smk_import only expects to return NULL for errors. It
- * is usually the case that a nullstring or "\n" would be
- * bad to pass to smk_import but in fact this is useful here.
+ * Clear the smack_onlycap on invalid label errors. This means
+ * that we can pass a null string to unset the onlycap value.
*
- * smk_import will also reject a label beginning with '-',
+ * Importing will also reject a label beginning with '-',
* so "-usecapabilities" will also work.
+ *
+ * But do so only on invalid label, not on system errors.
+ * The invalid label must be first to count as clearing attempt.
*/
- if (copy_from_user(data, buf, count) != 0)
- rc = -EFAULT;
- else
- smack_onlycap = smk_import_entry(data, count);
+ if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
+ mutex_lock(&smack_onlycap_lock);
+ smk_list_swap_rcu(&smack_onlycap_list, &list_tmp);
+ mutex_unlock(&smack_onlycap_lock);
+ rc = count;
+ }
+
+ smk_destroy_label_list(&list_tmp);
- kfree(data);
return rc;
}
static const struct file_operations smk_onlycap_ops = {
- .read = smk_read_onlycap,
+ .open = smk_open_onlycap,
+ .read = seq_read,
.write = smk_write_onlycap,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = seq_release,
};
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
@@ -1773,6 +2127,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *data;
+ struct smack_known *skp;
int rc = count;
if (!smack_privileged(CAP_MAC_ADMIN))
@@ -1782,21 +2137,31 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
if (data == NULL)
return -ENOMEM;
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto freeout;
+ }
+
/*
- * Should the null string be passed in unset the unconfined value.
- * This seems like something to be careful with as usually
- * smk_import only expects to return NULL for errors. It
- * is usually the case that a nullstring or "\n" would be
- * bad to pass to smk_import but in fact this is useful here.
+ * Clear the smack_unconfined on invalid label errors. This means
+ * that we can pass a null string to unset the unconfined value.
*
- * smk_import will also reject a label beginning with '-',
+ * Importing will also reject a label beginning with '-',
* so "-confine" will also work.
+ *
+ * But do so only on invalid label, not on system errors.
*/
- if (copy_from_user(data, buf, count) != 0)
- rc = -EFAULT;
- else
- smack_unconfined = smk_import_entry(data, count);
+ skp = smk_import_entry(data, count);
+ if (PTR_ERR(skp) == -EINVAL)
+ skp = NULL;
+ else if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
+ goto freeout;
+ }
+
+ smack_unconfined = skp;
+freeout:
kfree(data);
return rc;
}
@@ -1895,7 +2260,7 @@ static int load_self_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_rule *srp =
- list_entry(list, struct smack_rule, list);
+ list_entry_rcu(list, struct smack_rule, list);
smk_rule_show(s, srp, SMK_LABELLEN);
@@ -1980,7 +2345,7 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
res = smk_access(rule.smk_subject, rule.smk_object,
rule.smk_access1, NULL);
else if (res != -ENOENT)
- return -EINVAL;
+ return res;
/*
* smk_access() can return a value > 0 in the "bringup" case.
@@ -2024,7 +2389,7 @@ static int load2_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_master_list *smlp =
- list_entry(list, struct smack_master_list, list);
+ list_entry_rcu(list, struct smack_master_list, list);
smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL);
@@ -2101,7 +2466,7 @@ static int load_self2_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_rule *srp =
- list_entry(list, struct smack_rule, list);
+ list_entry_rcu(list, struct smack_rule, list);
smk_rule_show(s, srp, SMK_LONGLABEL);
@@ -2182,8 +2547,8 @@ static const struct file_operations smk_access2_ops = {
static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *data = NULL;
- const char *cp = NULL;
+ char *data;
+ const char *cp;
struct smack_known *skp;
struct smack_rule *sp;
struct list_head *rule_list;
@@ -2205,18 +2570,18 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
if (copy_from_user(data, buf, count) != 0) {
rc = -EFAULT;
- goto free_out;
+ goto out_data;
}
cp = smk_parse_smack(data, count);
- if (cp == NULL) {
- rc = -EINVAL;
- goto free_out;
+ if (IS_ERR(cp)) {
+ rc = PTR_ERR(cp);
+ goto out_data;
}
skp = smk_find_entry(cp);
if (skp == NULL)
- goto free_out;
+ goto out_cp;
rule_list = &skp->smk_rules;
rule_lock = &skp->smk_rules_lock;
@@ -2228,9 +2593,11 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
mutex_unlock(rule_lock);
-free_out:
- kfree(data);
+out_cp:
kfree(cp);
+out_data:
+ kfree(data);
+
return rc;
}
@@ -2247,11 +2614,7 @@ static const struct file_operations smk_revoke_subj_ops = {
*/
static int smk_init_sysfs(void)
{
- int err;
- err = sysfs_create_mount_point(fs_kobj, "smackfs");
- if (err)
- return err;
- return 0;
+ return sysfs_create_mount_point(fs_kobj, "smackfs");
}
/**
@@ -2341,10 +2704,10 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf,
rc = -EFAULT;
else {
skp = smk_import_entry(data, count);
- if (skp == NULL)
- rc = -EINVAL;
+ if (IS_ERR(skp))
+ rc = PTR_ERR(skp);
else
- smack_syslog_label = smk_import_entry(data, count);
+ smack_syslog_label = skp;
}
kfree(data);
@@ -2357,6 +2720,113 @@ static const struct file_operations smk_syslog_ops = {
.llseek = default_llseek,
};
+/*
+ * Seq_file read operations for /smack/relabel-self
+ */
+
+static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct task_smack *tsp = current_security();
+
+ return smk_seq_start(s, pos, &tsp->smk_relabel);
+}
+
+static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct task_smack *tsp = current_security();
+
+ return smk_seq_next(s, v, pos, &tsp->smk_relabel);
+}
+
+static int relabel_self_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_known_list_elem *sklep =
+ list_entry(list, struct smack_known_list_elem, list);
+
+ seq_puts(s, sklep->smk_label->smk_known);
+ seq_putc(s, ' ');
+
+ return 0;
+}
+
+static const struct seq_operations relabel_self_seq_ops = {
+ .start = relabel_self_seq_start,
+ .next = relabel_self_seq_next,
+ .show = relabel_self_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_relabel_self - open() for /smack/relabel-self
+ * @inode: inode structure representing file
+ * @file: "relabel-self" file pointer
+ *
+ * Connect our relabel_self_seq_* operations with /smack/relabel-self
+ * file_operations
+ */
+static int smk_open_relabel_self(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &relabel_self_seq_ops);
+}
+
+/**
+ * smk_write_relabel_self - write() for /smack/relabel-self
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ */
+static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_smack *tsp = current_security();
+ char *data;
+ int rc;
+ LIST_HEAD(list_tmp);
+
+ /*
+ * Must have privilege.
+ */
+ if (!smack_privileged(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ /*
+ * Enough data must be present.
+ */
+ if (*ppos != 0)
+ return -EINVAL;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ rc = smk_parse_label_list(data, &list_tmp);
+ kfree(data);
+
+ if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
+ smk_destroy_label_list(&tsp->smk_relabel);
+ list_splice(&list_tmp, &tsp->smk_relabel);
+ return count;
+ }
+
+ smk_destroy_label_list(&list_tmp);
+ return rc;
+}
+
+static const struct file_operations smk_relabel_self_ops = {
+ .open = smk_open_relabel_self,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_relabel_self,
+ .release = seq_release,
+};
/**
* smk_read_ptrace - read() for /smack/ptrace
@@ -2446,8 +2916,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
[SMK_AMBIENT] = {
"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
- [SMK_NETLBLADDR] = {
- "netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
+ [SMK_NET4ADDR] = {
+ "netlabel", &smk_net4addr_ops, S_IRUGO|S_IWUSR},
[SMK_ONLYCAP] = {
"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
[SMK_LOGGING] = {
@@ -2479,6 +2949,13 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
[SMK_UNCONFINED] = {
"unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ [SMK_NET6ADDR] = {
+ "ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
+#endif /* CONFIG_IPV6 */
+ [SMK_RELABEL_SELF] = {
+ "relabel-self", &smk_relabel_self_ops,
+ S_IRUGO|S_IWUGO},
/* last one */
{""}
};
@@ -2547,7 +3024,7 @@ static int __init init_smk_fs(void)
int err;
int rc;
- if (!security_module_enable(&smack_ops))
+ if (smack_enabled == 0)
return 0;
err = smk_init_sysfs();