diff options
Diffstat (limited to 'security/smack/smackfs.c')
-rw-r--r-- | security/smack/smackfs.c | 369 |
1 files changed, 243 insertions, 126 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 247dc9e..bf107a3 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -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. @@ -57,12 +59,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. * It can be reset via smackfs/direct @@ -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 */ {""} |