Loading Documentation/security/Smack.txt +26 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,10 @@ Smack kernels use the CIPSO IP option. Some network configurations are intolerant of IP options and can impede access to systems that use them as Smack does. Smack is used in the Tizen operating system. Please go to http://wiki.tizen.org for information about how Smack is used in Tizen. The current git repository for Smack user space is: git://github.com/smack-team/smack.git Loading Loading @@ -108,6 +112,8 @@ in the smackfs filesystem. This pseudo-filesystem is mounted on /sys/fs/smackfs. access Provided for backward compatibility. The access2 interface is preferred and should be used instead. This interface reports whether a subject with the specified Smack label has a particular access to an object with a specified Smack label. Write a fixed format access rule to Loading Loading @@ -136,6 +142,8 @@ change-rule those in the fourth string. If there is no such rule it will be created using the access specified in the third and the fourth strings. cipso Provided for backward compatibility. The cipso2 interface is preferred and should be used instead. This interface allows a specific CIPSO header to be assigned to a Smack label. The format accepted on write is: "%24s%4d%4d"["%4d"]... Loading @@ -157,7 +165,19 @@ direct doi This contains the CIPSO domain of interpretation used in network packets. ipv6host This interface allows specific IPv6 internet addresses to be treated as single label hosts. Packets are sent to single label hosts only from processes that have Smack write access to the host label. All packets received from single label hosts are given the specified label. The format accepted on write is: "%h:%h:%h:%h:%h:%h:%h:%h label" or "%h:%h:%h:%h:%h:%h:%h:%h/%d label". The "::" address shortcut is not supported. If label is "-DELETE" a matched entry will be deleted. load Provided for backward compatibility. The load2 interface is preferred and should be used instead. This interface allows access control rules in addition to the system defined rules to be specified. The format accepted on write is: Loading @@ -181,6 +201,8 @@ load2 permissions that are not allowed. The string "r-x--" would specify read and execute access. load-self Provided for backward compatibility. The load-self2 interface is preferred and should be used instead. This interface allows process specific access rules to be defined. These rules are only consulted if access would otherwise be permitted, and are intended to provide additional Loading @@ -205,6 +227,8 @@ netlabel received from single label hosts are given the specified label. The format accepted on write is: "%d.%d.%d.%d label" or "%d.%d.%d.%d/%d label". If the label specified is "-CIPSO" the address is treated as a host that supports CIPSO headers. onlycap This contains labels processes must have for CAP_MAC_ADMIN and CAP_MAC_OVERRIDE to be effective. If this file is empty Loading Loading @@ -232,7 +256,8 @@ unconfined is dangerous and can ruin the proper labeling of your system. It should never be used in production. You can add access rules in /etc/smack/accesses. They take the form: If you are using the smackload utility you can add access rules in /etc/smack/accesses. They take the form: subjectlabel objectlabel access Loading security/smack/smack.h +57 −9 Original line number Diff line number Diff line Loading @@ -17,11 +17,26 @@ #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. */ Loading Loading @@ -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. */ Loading @@ -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 */ Loading @@ -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" /* Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading security/smack/smack_access.c +6 −0 Original line number Diff line number Diff line Loading @@ -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; Loading security/smack/smack_lsm.c +390 −117 File changed.Preview size limit exceeded, changes collapsed. Show changes security/smack/smackfs.c +354 −82 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include <linux/magic.h> #include "smack.h" #define BEBITS (sizeof(__be32) * 8) /* * smackfs pseudo filesystem. */ Loading @@ -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 */ Loading @@ -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 */ }; /* Loading @@ -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. Loading Loading @@ -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. Loading @@ -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; Loading @@ -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. Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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); Loading @@ -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; } Loading @@ -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; } Loading @@ -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; } Loading @@ -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; Loading @@ -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); Loading @@ -1305,14 +1312,279 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, 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 Loading Loading @@ -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"); } /** Loading Loading @@ -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] = { Loading Loading @@ -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 */ {""} }; Loading Loading
Documentation/security/Smack.txt +26 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,10 @@ Smack kernels use the CIPSO IP option. Some network configurations are intolerant of IP options and can impede access to systems that use them as Smack does. Smack is used in the Tizen operating system. Please go to http://wiki.tizen.org for information about how Smack is used in Tizen. The current git repository for Smack user space is: git://github.com/smack-team/smack.git Loading Loading @@ -108,6 +112,8 @@ in the smackfs filesystem. This pseudo-filesystem is mounted on /sys/fs/smackfs. access Provided for backward compatibility. The access2 interface is preferred and should be used instead. This interface reports whether a subject with the specified Smack label has a particular access to an object with a specified Smack label. Write a fixed format access rule to Loading Loading @@ -136,6 +142,8 @@ change-rule those in the fourth string. If there is no such rule it will be created using the access specified in the third and the fourth strings. cipso Provided for backward compatibility. The cipso2 interface is preferred and should be used instead. This interface allows a specific CIPSO header to be assigned to a Smack label. The format accepted on write is: "%24s%4d%4d"["%4d"]... Loading @@ -157,7 +165,19 @@ direct doi This contains the CIPSO domain of interpretation used in network packets. ipv6host This interface allows specific IPv6 internet addresses to be treated as single label hosts. Packets are sent to single label hosts only from processes that have Smack write access to the host label. All packets received from single label hosts are given the specified label. The format accepted on write is: "%h:%h:%h:%h:%h:%h:%h:%h label" or "%h:%h:%h:%h:%h:%h:%h:%h/%d label". The "::" address shortcut is not supported. If label is "-DELETE" a matched entry will be deleted. load Provided for backward compatibility. The load2 interface is preferred and should be used instead. This interface allows access control rules in addition to the system defined rules to be specified. The format accepted on write is: Loading @@ -181,6 +201,8 @@ load2 permissions that are not allowed. The string "r-x--" would specify read and execute access. load-self Provided for backward compatibility. The load-self2 interface is preferred and should be used instead. This interface allows process specific access rules to be defined. These rules are only consulted if access would otherwise be permitted, and are intended to provide additional Loading @@ -205,6 +227,8 @@ netlabel received from single label hosts are given the specified label. The format accepted on write is: "%d.%d.%d.%d label" or "%d.%d.%d.%d/%d label". If the label specified is "-CIPSO" the address is treated as a host that supports CIPSO headers. onlycap This contains labels processes must have for CAP_MAC_ADMIN and CAP_MAC_OVERRIDE to be effective. If this file is empty Loading Loading @@ -232,7 +256,8 @@ unconfined is dangerous and can ruin the proper labeling of your system. It should never be used in production. You can add access rules in /etc/smack/accesses. They take the form: If you are using the smackload utility you can add access rules in /etc/smack/accesses. They take the form: subjectlabel objectlabel access Loading
security/smack/smack.h +57 −9 Original line number Diff line number Diff line Loading @@ -17,11 +17,26 @@ #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. */ Loading Loading @@ -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. */ Loading @@ -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 */ Loading @@ -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" /* Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading
security/smack/smack_access.c +6 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
security/smack/smack_lsm.c +390 −117 File changed.Preview size limit exceeded, changes collapsed. Show changes
security/smack/smackfs.c +354 −82 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include <linux/magic.h> #include "smack.h" #define BEBITS (sizeof(__be32) * 8) /* * smackfs pseudo filesystem. */ Loading @@ -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 */ Loading @@ -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 */ }; /* Loading @@ -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. Loading Loading @@ -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. Loading @@ -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; Loading @@ -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. Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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); Loading @@ -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; } Loading @@ -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; } Loading @@ -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; } Loading @@ -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; Loading @@ -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); Loading @@ -1305,14 +1312,279 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, 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 Loading Loading @@ -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"); } /** Loading Loading @@ -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] = { Loading Loading @@ -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 */ {""} }; Loading