summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
authorJames Morris <james.l.morris@oracle.com>2015-08-11 11:18:53 +1000
committerJames Morris <james.l.morris@oracle.com>2015-08-11 11:18:53 +1000
commit5ab16579020edb79e215b401399f88c7e07fdaf9 (patch)
tree5fdb2e49d543af7de6b129d6c87abb1967000a6b /security
parent459c15e53cf7e4e88a78ecfb109af5a267c5500a (diff)
parent41a2d5751616e38d1e293e3cb35a6e2bc7a03473 (diff)
Merge branch 'smack-for-4.3' of https://github.com/cschaufler/smack-next into next
Diffstat (limited to 'security')
-rw-r--r--security/smack/smack.h66
-rw-r--r--security/smack/smack_access.c6
-rw-r--r--security/smack/smack_lsm.c507
-rw-r--r--security/smack/smackfs.c436
4 files changed, 807 insertions, 208 deletions
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 244e035e5a99..fff0c612bbb7 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -17,12 +17,27 @@
#include <linux/spinlock.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
@@ -118,15 +133,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,12 +167,31 @@ 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_onlycap {
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
*/
@@ -152,6 +201,7 @@ struct smack_onlycap {
#define SMK_FSROOT "smackfsroot="
#define SMK_FSTRANS "smackfstransmute="
+#define SMACK_DELETE_OPTION "-DELETE"
#define SMACK_CIPSO_OPTION "-CIPSO"
/*
@@ -234,10 +284,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
@@ -267,7 +313,6 @@ 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;
@@ -279,7 +324,10 @@ 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 mutex smack_onlycap_lock;
extern struct list_head smack_onlycap_list;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 00f6b38bffbd..bc1053fb5d1d 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -639,6 +639,12 @@ int smack_privileged(int cap)
struct smack_known *skp = smk_of_current();
struct smack_onlycap *sop;
+ /*
+ * All kernel tasks are privileged
+ */
+ if (unlikely(current->flags & PF_KTHREAD))
+ return 1;
+
if (!capable(cap))
return 0;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index a143328f75eb..54fb3a1d8953 100644
--- a/security/smack/smack_lsm.c
+++ b/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,9 +51,9 @@
#define SMK_RECEIVING 1
#define SMK_SENDING 2
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_PORT_LABELING
LIST_HEAD(smk_ipv6_port_list);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
static struct kmem_cache *smack_inode_cache;
int smack_enabled;
@@ -64,6 +65,15 @@ static char *smk_bu_mess[] = {
"Unconfined Object", /* SMACK_UNCONFINED_OBJECT */
};
+static const match_table_t 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},
+};
+
static void smk_bu_mode(int mode, char *s)
{
int i = 0;
@@ -281,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;
@@ -577,76 +587,193 @@ 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, *fsfloor = NULL;
+ char *fshat = NULL, *fsroot = NULL, *fstransmute = NULL;
+ int rc = -ENOMEM, num_mnt_opts = 0;
+
+ opts->num_mnt_opts = 0;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+ substring_t args[MAX_OPT_ARGS];
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, 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);
+ 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_hat = skp;
- specified = 1;
-
- } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {
- op += strlen(SMK_FSFLOOR);
- skp = smk_import_entry(op, 0);
+ 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;
- specified = 1;
-
- } else if (strncmp(op, SMK_FSDEFAULT,
- strlen(SMK_FSDEFAULT)) == 0) {
- op += strlen(SMK_FSDEFAULT);
- skp = smk_import_entry(op, 0);
+ break;
+ case FSHAT_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
if (IS_ERR(skp))
return PTR_ERR(skp);
- 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);
+ 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;
- specified = 1;
-
- } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) {
- op += strlen(SMK_FSTRANS);
- skp = smk_import_entry(op, 0);
+ 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;
- specified = 1;
+ break;
+ default:
+ break;
}
}
@@ -654,7 +781,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.
@@ -663,6 +790,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.
*/
@@ -682,6 +810,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
*
@@ -2113,7 +2272,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
@@ -2124,30 +2283,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
@@ -2211,7 +2436,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;
@@ -2236,7 +2461,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
@@ -2320,48 +2580,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;
@@ -2371,22 +2626,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
@@ -2447,10 +2689,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;
}
@@ -2492,7 +2734,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
@@ -2506,14 +2748,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
@@ -2529,6 +2768,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;
@@ -2542,10 +2788,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;
@@ -3431,9 +3682,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;
/*
@@ -3447,9 +3702,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;
@@ -3663,10 +3924,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);
@@ -3677,9 +3940,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 */
}
@@ -3777,13 +4041,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)
@@ -3906,7 +4168,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)
@@ -4254,7 +4516,7 @@ static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
return 0;
}
-struct security_hook_list smack_hooks[] = {
+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),
@@ -4264,6 +4526,8 @@ struct security_hook_list smack_hooks[] = {
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),
@@ -4356,9 +4620,9 @@ struct security_hook_list smack_hooks[] = {
LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
-#ifndef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_PORT_LABELING
LSM_HOOK_INIT(socket_bind, smack_socket_bind),
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#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),
@@ -4453,7 +4717,16 @@ static __init int smack_init(void)
return -ENOMEM;
}
- printk(KERN_INFO "Smack: Initializing.\n");
+ 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.
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 2716d02119f3..c20b154a33f2 100644
--- a/security/smack/smackfs.c
+++ b/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,9 @@ 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 */
};
/*
@@ -64,7 +68,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.
@@ -118,7 +125,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.
@@ -129,7 +139,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;
@@ -140,11 +150,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.
@@ -1047,92 +1052,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_rcu(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);
-
- for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
+ struct smk_net4addr *skp =
+ list_entry_rcu(list, struct smk_net4addr, list);
+ char *kp = SMACK_CIPSO_OPTION;
- 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;
}
@@ -1141,28 +1144,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;
@@ -1200,7 +1204,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);
@@ -1209,8 +1213,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;
}
@@ -1225,16 +1230,16 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
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;
}
@@ -1245,14 +1250,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;
}
@@ -1265,17 +1269,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;
@@ -1287,15 +1294,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);
@@ -1305,14 +1312,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 (skp == NULL) {
+ rc = -EINVAL;
+ 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
@@ -2320,11 +2592,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");
}
/**
@@ -2519,8 +2787,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] = {
@@ -2552,6 +2820,10 @@ 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 */
/* last one */
{""}
};