|
@@ -20,6 +20,7 @@
|
|
|
#include <linux/vmalloc.h>
|
|
|
#include <linux/security.h>
|
|
|
#include <linux/mutex.h>
|
|
|
+#include <net/net_namespace.h>
|
|
|
#include <net/netlabel.h>
|
|
|
#include <net/cipso_ipv4.h>
|
|
|
#include <linux/seq_file.h>
|
|
@@ -38,7 +39,7 @@ enum smk_inos {
|
|
|
SMK_DOI = 5, /* CIPSO DOI */
|
|
|
SMK_DIRECT = 6, /* CIPSO level indicating direct label */
|
|
|
SMK_AMBIENT = 7, /* internet ambient label */
|
|
|
- SMK_NLTYPE = 8, /* label scheme to use by default */
|
|
|
+ SMK_NETLBLADDR = 8, /* single label hosts */
|
|
|
SMK_ONLYCAP = 9, /* the only "capable" label */
|
|
|
};
|
|
|
|
|
@@ -48,6 +49,7 @@ enum smk_inos {
|
|
|
static DEFINE_MUTEX(smack_list_lock);
|
|
|
static DEFINE_MUTEX(smack_cipso_lock);
|
|
|
static DEFINE_MUTEX(smack_ambient_lock);
|
|
|
+static DEFINE_MUTEX(smk_netlbladdr_lock);
|
|
|
|
|
|
/*
|
|
|
* This is the "ambient" label for network traffic.
|
|
@@ -56,12 +58,6 @@ static DEFINE_MUTEX(smack_ambient_lock);
|
|
|
*/
|
|
|
char *smack_net_ambient = smack_known_floor.smk_known;
|
|
|
|
|
|
-/*
|
|
|
- * This is the default packet marking scheme for network traffic.
|
|
|
- * It can be reset via smackfs/nltype
|
|
|
- */
|
|
|
-int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4;
|
|
|
-
|
|
|
/*
|
|
|
* This is the level in a CIPSO header that indicates a
|
|
|
* smack label is contained directly in the category set.
|
|
@@ -79,6 +75,13 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
|
|
|
*/
|
|
|
char *smack_onlycap;
|
|
|
|
|
|
+/*
|
|
|
+ * Certain IP addresses may be designated as single label hosts.
|
|
|
+ * Packets are sent there unlabeled, but only from tasks that
|
|
|
+ * can write to the specified label.
|
|
|
+ */
|
|
|
+struct smk_netlbladdr *smack_netlbladdrs;
|
|
|
+
|
|
|
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
|
|
|
struct smk_list_entry *smack_list;
|
|
|
|
|
@@ -104,6 +107,24 @@ struct smk_list_entry *smack_list;
|
|
|
#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
|
|
|
#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
|
|
|
|
|
|
+/**
|
|
|
+ * smk_netlabel_audit_set - fill a netlbl_audit struct
|
|
|
+ * @nap: structure to fill
|
|
|
+ */
|
|
|
+static void smk_netlabel_audit_set(struct netlbl_audit *nap)
|
|
|
+{
|
|
|
+ nap->loginuid = audit_get_loginuid(current);
|
|
|
+ nap->sessionid = audit_get_sessionid(current);
|
|
|
+ nap->secid = smack_to_secid(current_security());
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Values for parsing single label host rules
|
|
|
+ * "1.2.3.4 X"
|
|
|
+ * "192.168.138.129/32 abcdefghijklmnopqrstuvw"
|
|
|
+ */
|
|
|
+#define SMK_NETLBLADDRMIN 9
|
|
|
+#define SMK_NETLBLADDRMAX 42
|
|
|
|
|
|
/*
|
|
|
* Seq_file read operations for /smack/load
|
|
@@ -344,13 +365,11 @@ static void smk_cipso_doi(void)
|
|
|
{
|
|
|
int rc;
|
|
|
struct cipso_v4_doi *doip;
|
|
|
- struct netlbl_audit audit_info;
|
|
|
+ struct netlbl_audit nai;
|
|
|
|
|
|
- audit_info.loginuid = audit_get_loginuid(current);
|
|
|
- audit_info.sessionid = audit_get_sessionid(current);
|
|
|
- audit_info.secid = smack_to_secid(current_security());
|
|
|
+ smk_netlabel_audit_set(&nai);
|
|
|
|
|
|
- rc = netlbl_cfg_map_del(NULL, &audit_info);
|
|
|
+ rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai);
|
|
|
if (rc != 0)
|
|
|
printk(KERN_WARNING "%s:%d remove rc = %d\n",
|
|
|
__func__, __LINE__, rc);
|
|
@@ -365,11 +384,19 @@ static void smk_cipso_doi(void)
|
|
|
for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)
|
|
|
doip->tags[rc] = CIPSO_V4_TAG_INVALID;
|
|
|
|
|
|
- rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info);
|
|
|
+ rc = netlbl_cfg_cipsov4_add(doip, &nai);
|
|
|
if (rc != 0) {
|
|
|
- printk(KERN_WARNING "%s:%d add rc = %d\n",
|
|
|
+ printk(KERN_WARNING "%s:%d cipso add rc = %d\n",
|
|
|
__func__, __LINE__, rc);
|
|
|
kfree(doip);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ rc = netlbl_cfg_cipsov4_map_add(doip->doi, NULL, NULL, NULL, &nai);
|
|
|
+ if (rc != 0) {
|
|
|
+ printk(KERN_WARNING "%s:%d map add rc = %d\n",
|
|
|
+ __func__, __LINE__, rc);
|
|
|
+ kfree(doip);
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -379,20 +406,19 @@ static void smk_cipso_doi(void)
|
|
|
static void smk_unlbl_ambient(char *oldambient)
|
|
|
{
|
|
|
int rc;
|
|
|
- struct netlbl_audit audit_info;
|
|
|
+ struct netlbl_audit nai;
|
|
|
|
|
|
- audit_info.loginuid = audit_get_loginuid(current);
|
|
|
- audit_info.sessionid = audit_get_sessionid(current);
|
|
|
- audit_info.secid = smack_to_secid(current_security());
|
|
|
+ smk_netlabel_audit_set(&nai);
|
|
|
|
|
|
if (oldambient != NULL) {
|
|
|
- rc = netlbl_cfg_map_del(oldambient, &audit_info);
|
|
|
+ rc = netlbl_cfg_map_del(oldambient, PF_INET, NULL, NULL, &nai);
|
|
|
if (rc != 0)
|
|
|
printk(KERN_WARNING "%s:%d remove rc = %d\n",
|
|
|
__func__, __LINE__, rc);
|
|
|
}
|
|
|
|
|
|
- rc = netlbl_cfg_unlbl_add_map(smack_net_ambient, &audit_info);
|
|
|
+ rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET,
|
|
|
+ NULL, NULL, &nai);
|
|
|
if (rc != 0)
|
|
|
printk(KERN_WARNING "%s:%d add rc = %d\n",
|
|
|
__func__, __LINE__, rc);
|
|
@@ -603,6 +629,201 @@ static const struct file_operations smk_cipso_ops = {
|
|
|
.release = seq_release,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * Seq_file read operations for /smack/netlabel
|
|
|
+ */
|
|
|
+
|
|
|
+static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
+{
|
|
|
+ if (*pos == SEQ_READ_FINISHED)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ return smack_netlbladdrs;
|
|
|
+}
|
|
|
+
|
|
|
+static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
+{
|
|
|
+ struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next;
|
|
|
+
|
|
|
+ if (skp == NULL)
|
|
|
+ *pos = SEQ_READ_FINISHED;
|
|
|
+
|
|
|
+ return skp;
|
|
|
+}
|
|
|
+/*
|
|
|
+#define BEMASK 0x80000000
|
|
|
+*/
|
|
|
+#define BEMASK 0x00000001
|
|
|
+#define BEBITS (sizeof(__be32) * 8)
|
|
|
+
|
|
|
+/*
|
|
|
+ * Print host/label pairs
|
|
|
+ */
|
|
|
+static int netlbladdr_seq_show(struct seq_file *s, void *v)
|
|
|
+{
|
|
|
+ struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v;
|
|
|
+ unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
|
|
|
+ __be32 bebits;
|
|
|
+ int maskn = 0;
|
|
|
+
|
|
|
+ for (bebits = BEMASK; bebits != 0; maskn++, bebits <<= 1)
|
|
|
+ if ((skp->smk_mask.s_addr & bebits) == 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ seq_printf(s, "%u.%u.%u.%u/%d %s\n",
|
|
|
+ hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void netlbladdr_seq_stop(struct seq_file *s, void *v)
|
|
|
+{
|
|
|
+ /* No-op */
|
|
|
+}
|
|
|
+
|
|
|
+static struct seq_operations netlbladdr_seq_ops = {
|
|
|
+ .start = netlbladdr_seq_start,
|
|
|
+ .stop = netlbladdr_seq_stop,
|
|
|
+ .next = netlbladdr_seq_next,
|
|
|
+ .show = netlbladdr_seq_show,
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * smk_open_netlbladdr - open() for /smack/netlabel
|
|
|
+ * @inode: inode structure representing file
|
|
|
+ * @file: "netlabel" file pointer
|
|
|
+ *
|
|
|
+ * Connect our netlbladdr_seq_* operations with /smack/netlabel
|
|
|
+ * file_operations
|
|
|
+ */
|
|
|
+static int smk_open_netlbladdr(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ return seq_open(file, &netlbladdr_seq_ops);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * smk_write_netlbladdr - write() for /smack/netlabel
|
|
|
+ * @filp: 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.
|
|
|
+ * Returns number of bytes written or error code, as appropriate
|
|
|
+ */
|
|
|
+static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct smk_netlbladdr *skp;
|
|
|
+ struct sockaddr_in newname;
|
|
|
+ char smack[SMK_LABELLEN];
|
|
|
+ char *sp;
|
|
|
+ char data[SMK_NETLBLADDRMAX];
|
|
|
+ char *host = (char *)&newname.sin_addr.s_addr;
|
|
|
+ int rc;
|
|
|
+ struct netlbl_audit audit_info;
|
|
|
+ struct in_addr mask;
|
|
|
+ unsigned int m;
|
|
|
+ __be32 bebits = BEMASK;
|
|
|
+ __be32 nsa;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Must have privilege.
|
|
|
+ * No partial writes.
|
|
|
+ * Enough data must be present.
|
|
|
+ * "<addr/mask, as a.b.c.d/e><space><label>"
|
|
|
+ * "<addr, as a.b.c.d><space><label>"
|
|
|
+ */
|
|
|
+ if (!capable(CAP_MAC_ADMIN))
|
|
|
+ return -EPERM;
|
|
|
+ if (*ppos != 0)
|
|
|
+ return -EINVAL;
|
|
|
+ if (count < SMK_NETLBLADDRMIN || count > SMK_NETLBLADDRMAX)
|
|
|
+ return -EINVAL;
|
|
|
+ if (copy_from_user(data, buf, count) != 0)
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ data[count] = '\0';
|
|
|
+
|
|
|
+ rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%d %s",
|
|
|
+ &host[0], &host[1], &host[2], &host[3], &m, smack);
|
|
|
+ if (rc != 6) {
|
|
|
+ rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
|
|
|
+ &host[0], &host[1], &host[2], &host[3], smack);
|
|
|
+ if (rc != 5)
|
|
|
+ return -EINVAL;
|
|
|
+ m = BEBITS;
|
|
|
+ }
|
|
|
+ if (m > BEBITS)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ sp = smk_import(smack, 0);
|
|
|
+ if (sp == NULL)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ for (mask.s_addr = 0; m > 0; m--) {
|
|
|
+ mask.s_addr |= bebits;
|
|
|
+ bebits <<= 1;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Only allow one writer at a time. Writes should be
|
|
|
+ * quite rare and small in any case.
|
|
|
+ */
|
|
|
+ mutex_lock(&smk_netlbladdr_lock);
|
|
|
+
|
|
|
+ nsa = newname.sin_addr.s_addr;
|
|
|
+ for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next)
|
|
|
+ if (skp->smk_host.sin_addr.s_addr == nsa &&
|
|
|
+ skp->smk_mask.s_addr == mask.s_addr)
|
|
|
+ break;
|
|
|
+
|
|
|
+ smk_netlabel_audit_set(&audit_info);
|
|
|
+
|
|
|
+ if (skp == NULL) {
|
|
|
+ skp = kzalloc(sizeof(*skp), GFP_KERNEL);
|
|
|
+ if (skp == NULL)
|
|
|
+ rc = -ENOMEM;
|
|
|
+ else {
|
|
|
+ rc = 0;
|
|
|
+ skp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
|
|
|
+ skp->smk_mask.s_addr = mask.s_addr;
|
|
|
+ skp->smk_next = smack_netlbladdrs;
|
|
|
+ skp->smk_label = sp;
|
|
|
+ smack_netlbladdrs = skp;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
|
|
|
+ &skp->smk_host.sin_addr, &skp->smk_mask,
|
|
|
+ PF_INET, &audit_info);
|
|
|
+ skp->smk_label = sp;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Now tell netlabel about the single label nature of
|
|
|
+ * this host so that incoming packets get labeled.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (rc == 0)
|
|
|
+ rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
|
|
|
+ &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET,
|
|
|
+ smack_to_secid(skp->smk_label), &audit_info);
|
|
|
+
|
|
|
+ if (rc == 0)
|
|
|
+ rc = count;
|
|
|
+
|
|
|
+ mutex_unlock(&smk_netlbladdr_lock);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations smk_netlbladdr_ops = {
|
|
|
+ .open = smk_open_netlbladdr,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .write = smk_write_netlbladdr,
|
|
|
+ .release = seq_release,
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* smk_read_doi - read() for /smack/doi
|
|
|
* @filp: file pointer, not actually used
|
|
@@ -891,110 +1112,6 @@ static const struct file_operations smk_onlycap_ops = {
|
|
|
.write = smk_write_onlycap,
|
|
|
};
|
|
|
|
|
|
-struct option_names {
|
|
|
- int o_number;
|
|
|
- char *o_name;
|
|
|
- char *o_alias;
|
|
|
-};
|
|
|
-
|
|
|
-static struct option_names netlbl_choices[] = {
|
|
|
- { NETLBL_NLTYPE_RIPSO,
|
|
|
- NETLBL_NLTYPE_RIPSO_NAME, "ripso" },
|
|
|
- { NETLBL_NLTYPE_CIPSOV4,
|
|
|
- NETLBL_NLTYPE_CIPSOV4_NAME, "cipsov4" },
|
|
|
- { NETLBL_NLTYPE_CIPSOV4,
|
|
|
- NETLBL_NLTYPE_CIPSOV4_NAME, "cipso" },
|
|
|
- { NETLBL_NLTYPE_CIPSOV6,
|
|
|
- NETLBL_NLTYPE_CIPSOV6_NAME, "cipsov6" },
|
|
|
- { NETLBL_NLTYPE_UNLABELED,
|
|
|
- NETLBL_NLTYPE_UNLABELED_NAME, "unlabeled" },
|
|
|
-};
|
|
|
-
|
|
|
-/**
|
|
|
- * smk_read_nltype - read() for /smack/nltype
|
|
|
- * @filp: file pointer, not actually used
|
|
|
- * @buf: where to put the result
|
|
|
- * @count: maximum to send along
|
|
|
- * @ppos: where to start
|
|
|
- *
|
|
|
- * Returns number of bytes read or error code, as appropriate
|
|
|
- */
|
|
|
-static ssize_t smk_read_nltype(struct file *filp, char __user *buf,
|
|
|
- size_t count, loff_t *ppos)
|
|
|
-{
|
|
|
- char bound[40];
|
|
|
- ssize_t rc;
|
|
|
- int i;
|
|
|
-
|
|
|
- if (count < SMK_LABELLEN)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (*ppos != 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
- sprintf(bound, "unknown");
|
|
|
-
|
|
|
- for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
|
|
|
- if (smack_net_nltype == netlbl_choices[i].o_number) {
|
|
|
- sprintf(bound, "%s", netlbl_choices[i].o_name);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound));
|
|
|
-
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * smk_write_nltype - write() for /smack/nltype
|
|
|
- * @filp: file pointer, not actually used
|
|
|
- * @buf: where to get the data from
|
|
|
- * @count: bytes sent
|
|
|
- * @ppos: where to start
|
|
|
- *
|
|
|
- * Returns number of bytes written or error code, as appropriate
|
|
|
- */
|
|
|
-static ssize_t smk_write_nltype(struct file *file, const char __user *buf,
|
|
|
- size_t count, loff_t *ppos)
|
|
|
-{
|
|
|
- char bound[40];
|
|
|
- char *cp;
|
|
|
- int i;
|
|
|
-
|
|
|
- if (!capable(CAP_MAC_ADMIN))
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- if (count >= 40)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (copy_from_user(bound, buf, count) != 0)
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
- bound[count] = '\0';
|
|
|
- cp = strchr(bound, ' ');
|
|
|
- if (cp != NULL)
|
|
|
- *cp = '\0';
|
|
|
- cp = strchr(bound, '\n');
|
|
|
- if (cp != NULL)
|
|
|
- *cp = '\0';
|
|
|
-
|
|
|
- for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
|
|
|
- if (strcmp(bound, netlbl_choices[i].o_name) == 0 ||
|
|
|
- strcmp(bound, netlbl_choices[i].o_alias) == 0) {
|
|
|
- smack_net_nltype = netlbl_choices[i].o_number;
|
|
|
- return count;
|
|
|
- }
|
|
|
- /*
|
|
|
- * Not a valid choice.
|
|
|
- */
|
|
|
- return -EINVAL;
|
|
|
-}
|
|
|
-
|
|
|
-static const struct file_operations smk_nltype_ops = {
|
|
|
- .read = smk_read_nltype,
|
|
|
- .write = smk_write_nltype,
|
|
|
-};
|
|
|
-
|
|
|
/**
|
|
|
* smk_fill_super - fill the /smackfs superblock
|
|
|
* @sb: the empty superblock
|
|
@@ -1021,8 +1138,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_NLTYPE] =
|
|
|
- {"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR},
|
|
|
+ [SMK_NETLBLADDR] =
|
|
|
+ {"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
|
|
|
[SMK_ONLYCAP] =
|
|
|
{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
|
|
|
/* last one */ {""}
|