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 247dc9ebbc7..bf107a389ac 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 */ {""}  |