summary refs log tree commit diff
path: root/security
diff options
context:
space:
mode:
authorJames Morris <jmorris@namei.org>2009-01-07 09:58:22 +1100
committerJames Morris <jmorris@namei.org>2009-01-07 09:58:22 +1100
commitac8cc0fa5395fe2278e305a4cbed48e90d88d878 (patch)
tree515f577bfddd054ee4373228be7c974dfb8133af /security
parent238c6d54830c624f34ac9cf123ac04aebfca5013 (diff)
parent3699c53c485bf0168e6500d0ed18bf931584dd7c (diff)
downloadlinux-ac8cc0fa5395fe2278e305a4cbed48e90d88d878.tar.gz
Merge branch 'next' into for-linus
Diffstat (limited to 'security')
-rw-r--r--security/commoncap.c29
-rw-r--r--security/keys/keyctl.c2
-rw-r--r--security/security.c26
-rw-r--r--security/selinux/Kconfig27
-rw-r--r--security/selinux/avc.c16
-rw-r--r--security/selinux/hooks.c22
-rw-r--r--security/selinux/include/avc_ss.h4
-rw-r--r--security/selinux/selinuxfs.c16
-rw-r--r--security/selinux/ss/context.h2
-rw-r--r--security/smack/smack.h31
-rw-r--r--security/smack/smack_access.c28
-rw-r--r--security/smack/smack_lsm.c310
-rw-r--r--security/smack/smackfs.c369
13 files changed, 616 insertions, 266 deletions
diff --git a/security/commoncap.c b/security/commoncap.c
index 69fc9952650f..7cd61a5f5205 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -45,26 +45,22 @@ EXPORT_SYMBOL(cap_netlink_recv);
 /**
  * cap_capable - Determine whether a task has a particular effective capability
  * @tsk: The task to query
+ * @cred: The credentials to use
  * @cap: The capability to check for
  * @audit: Whether to write an audit message or not
  *
  * Determine whether the nominated task has the specified capability amongst
  * its effective set, returning 0 if it does, -ve if it does not.
  *
- * NOTE WELL: cap_capable() cannot be used like the kernel's capable()
- * function.  That is, it has the reverse semantics: cap_capable() returns 0
- * when a task has a capability, but the kernel's capable() returns 1 for this
- * case.
+ * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable()
+ * and has_capability() functions.  That is, it has the reverse semantics:
+ * cap_has_capability() returns 0 when a task has a capability, but the
+ * kernel's capable() and has_capability() returns 1 for this case.
  */
-int cap_capable(struct task_struct *tsk, int cap, int audit)
+int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap,
+		int audit)
 {
-	__u32 cap_raised;
-
-	/* Derived from include/linux/sched.h:capable. */
-	rcu_read_lock();
-	cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap);
-	rcu_read_unlock();
-	return cap_raised ? 0 : -EPERM;
+	return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
 }
 
 /**
@@ -160,7 +156,8 @@ static inline int cap_inh_is_capped(void)
 	/* they are so limited unless the current task has the CAP_SETPCAP
 	 * capability
 	 */
-	if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
+	if (cap_capable(current, current_cred(), CAP_SETPCAP,
+			SECURITY_CAP_AUDIT) == 0)
 		return 0;
 #endif
 	return 1;
@@ -869,7 +866,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
 		     & (new->securebits ^ arg2))			/*[1]*/
 		    || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))	/*[2]*/
 		    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))	/*[3]*/
-		    || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/
+		    || (cap_capable(current, current_cred(), CAP_SETPCAP,
+				    SECURITY_CAP_AUDIT) != 0)		/*[4]*/
 			/*
 			 * [1] no changing of bits that are locked
 			 * [2] no unlocking of locks
@@ -950,7 +948,8 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
 {
 	int cap_sys_admin = 0;
 
-	if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0)
+	if (cap_capable(current, current_cred(), CAP_SYS_ADMIN,
+			SECURITY_CAP_NOAUDIT) == 0)
 		cap_sys_admin = 1;
 	return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 6688765bd8b9..09796797d122 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1294,7 +1294,7 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
 
 	case KEYCTL_GET_SECURITY:
 		return keyctl_get_security((key_serial_t) arg2,
-					   (char *) arg3,
+					   (char __user *) arg3,
 					   (size_t) arg4);
 
 	default:
diff --git a/security/security.c b/security/security.c
index 678d4d07b852..c3586c0d97e2 100644
--- a/security/security.c
+++ b/security/security.c
@@ -154,14 +154,32 @@ int security_capset(struct cred *new, const struct cred *old,
 				    effective, inheritable, permitted);
 }
 
-int security_capable(struct task_struct *tsk, int cap)
+int security_capable(int cap)
 {
-	return security_ops->capable(tsk, cap, SECURITY_CAP_AUDIT);
+	return security_ops->capable(current, current_cred(), cap,
+				     SECURITY_CAP_AUDIT);
 }
 
-int security_capable_noaudit(struct task_struct *tsk, int cap)
+int security_real_capable(struct task_struct *tsk, int cap)
 {
-	return security_ops->capable(tsk, cap, SECURITY_CAP_NOAUDIT);
+	const struct cred *cred;
+	int ret;
+
+	cred = get_task_cred(tsk);
+	ret = security_ops->capable(tsk, cred, cap, SECURITY_CAP_AUDIT);
+	put_cred(cred);
+	return ret;
+}
+
+int security_real_capable_noaudit(struct task_struct *tsk, int cap)
+{
+	const struct cred *cred;
+	int ret;
+
+	cred = get_task_cred(tsk);
+	ret = security_ops->capable(tsk, cred, cap, SECURITY_CAP_NOAUDIT);
+	put_cred(cred);
+	return ret;
 }
 
 int security_acct(struct file *file)
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 26301dd651d3..bca1b74a4a2f 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -94,33 +94,6 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE
 
 	  If you are unsure how to answer this question, answer 1.
 
-config SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT
-	bool "NSA SELinux enable new secmark network controls by default"
-	depends on SECURITY_SELINUX
-	default n
-	help
-	  This option determines whether the new secmark-based network
-	  controls will be enabled by default.  If not, the old internal
-	  per-packet controls will be enabled by default, preserving
-	  old behavior.
-
-	  If you enable the new controls, you will need updated
-	  SELinux userspace libraries, tools and policy.  Typically,
-	  your distribution will provide these and enable the new controls
-	  in the kernel they also distribute.
-
-	  Note that this option can be overridden at boot with the
-	  selinux_compat_net parameter, and after boot via
-	  /selinux/compat_net.  See Documentation/kernel-parameters.txt
-	  for details on this parameter.
-
-	  If you enable the new network controls, you will likely
-	  also require the SECMARK and CONNSECMARK targets, as
-	  well as any conntrack helpers for protocols which you
-	  wish to control.
-
-	  If you are unsure what to do here, select N.
-
 config SECURITY_SELINUX_POLICYDB_VERSION_MAX
 	bool "NSA SELinux maximum supported policy format version"
 	depends on SECURITY_SELINUX
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index d43bd6baeeaa..eb41f43e2772 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -53,18 +53,20 @@ static const char *class_to_string[] = {
 #undef S_
 
 static const struct av_inherit av_inherit[] = {
-#define S_(c, i, b) { c, common_##i##_perm_to_string, b },
+#define S_(c, i, b) {	.tclass = c,\
+			.common_pts = common_##i##_perm_to_string,\
+			.common_base =  b },
 #include "av_inherit.h"
 #undef S_
 };
 
 const struct selinux_class_perm selinux_class_perm = {
-	av_perm_to_string,
-	ARRAY_SIZE(av_perm_to_string),
-	class_to_string,
-	ARRAY_SIZE(class_to_string),
-	av_inherit,
-	ARRAY_SIZE(av_inherit)
+	.av_perm_to_string = av_perm_to_string,
+	.av_pts_len = ARRAY_SIZE(av_perm_to_string),
+	.class_to_string = class_to_string,
+	.cts_len = ARRAY_SIZE(class_to_string),
+	.av_inherit = av_inherit,
+	.av_inherit_len = ARRAY_SIZE(av_inherit)
 };
 
 #define AVC_CACHE_SLOTS			512
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index dbeaa783b2a9..00815973d412 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1433,12 +1433,13 @@ static int current_has_perm(const struct task_struct *tsk,
 
 /* Check whether a task is allowed to use a capability. */
 static int task_has_capability(struct task_struct *tsk,
+			       const struct cred *cred,
 			       int cap, int audit)
 {
 	struct avc_audit_data ad;
 	struct av_decision avd;
 	u16 sclass;
-	u32 sid = task_sid(tsk);
+	u32 sid = cred_sid(cred);
 	u32 av = CAP_TO_MASK(cap);
 	int rc;
 
@@ -1865,15 +1866,16 @@ static int selinux_capset(struct cred *new, const struct cred *old,
 	return cred_has_perm(old, new, PROCESS__SETCAP);
 }
 
-static int selinux_capable(struct task_struct *tsk, int cap, int audit)
+static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
+			   int cap, int audit)
 {
 	int rc;
 
-	rc = secondary_ops->capable(tsk, cap, audit);
+	rc = secondary_ops->capable(tsk, cred, cap, audit);
 	if (rc)
 		return rc;
 
-	return task_has_capability(tsk, cap, audit);
+	return task_has_capability(tsk, cred, cap, audit);
 }
 
 static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
@@ -2037,7 +2039,8 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 {
 	int rc, cap_sys_admin = 0;
 
-	rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT);
+	rc = selinux_capable(current, current_cred(), CAP_SYS_ADMIN,
+			     SECURITY_CAP_NOAUDIT);
 	if (rc == 0)
 		cap_sys_admin = 1;
 
@@ -2880,7 +2883,8 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
 	 * and lack of permission just means that we fall back to the
 	 * in-core context value, not a denial.
 	 */
-	error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT);
+	error = selinux_capable(current, current_cred(), CAP_MAC_ADMIN,
+				SECURITY_CAP_NOAUDIT);
 	if (!error)
 		error = security_sid_to_context_force(isec->sid, &context,
 						      &size);
@@ -4185,7 +4189,7 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
 static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
 				       u16 family)
 {
-	int err;
+	int err = 0;
 	struct sk_security_struct *sksec = sk->sk_security;
 	u32 peer_sid;
 	u32 sk_sid = sksec->sid;
@@ -4202,7 +4206,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
 	if (selinux_compat_net)
 		err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad,
 							   family, addrp);
-	else
+	else if (selinux_secmark_enabled())
 		err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
 				   PACKET__RECV, &ad);
 	if (err)
@@ -4705,7 +4709,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
 		if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
 							 &ad, family, addrp))
 			return NF_DROP;
-	} else {
+	} else if (selinux_secmark_enabled()) {
 		if (avc_has_perm(sksec->sid, skb->secmark,
 				 SECCLASS_PACKET, PACKET__SEND, &ad))
 			return NF_DROP;
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h
index c0d314d9f8e1..bb1ec801bdfe 100644
--- a/security/selinux/include/avc_ss.h
+++ b/security/selinux/include/avc_ss.h
@@ -17,16 +17,16 @@ struct av_perm_to_string {
 };
 
 struct av_inherit {
-	u16 tclass;
 	const char **common_pts;
 	u32 common_base;
+	u16 tclass;
 };
 
 struct selinux_class_perm {
 	const struct av_perm_to_string *av_perm_to_string;
 	u32 av_pts_len;
-	const char **class_to_string;
 	u32 cts_len;
+	const char **class_to_string;
 	const struct av_inherit *av_inherit;
 	u32 av_inherit_len;
 };
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 8f612c8becb5..01ec6d2c6b97 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -47,13 +47,7 @@ static char *policycap_names[] = {
 
 unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
 
-#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT
-#define SELINUX_COMPAT_NET_VALUE 0
-#else
-#define SELINUX_COMPAT_NET_VALUE 1
-#endif
-
-int selinux_compat_net = SELINUX_COMPAT_NET_VALUE;
+int selinux_compat_net = 0;
 
 static int __init checkreqprot_setup(char *str)
 {
@@ -494,7 +488,13 @@ static ssize_t sel_write_compat_net(struct file *file, const char __user *buf,
 	if (sscanf(page, "%d", &new_value) != 1)
 		goto out;
 
-	selinux_compat_net = new_value ? 1 : 0;
+	if (new_value) {
+		printk(KERN_NOTICE
+		       "SELinux: compat_net is deprecated, please use secmark"
+		       " instead\n");
+		selinux_compat_net = 1;
+	} else
+		selinux_compat_net = 0;
 	length = count;
 out:
 	free_page((unsigned long) page);
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index 658c2bd17da8..d9dd7a2f6a8a 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -27,9 +27,9 @@ struct context {
 	u32 user;
 	u32 role;
 	u32 type;
+	u32 len;        /* length of string in bytes */
 	struct mls_range range;
 	char *str;	/* string representation if context cannot be mapped. */
-	u32 len;        /* length of string in bytes */
 };
 
 static inline void mls_context_init(struct context *c)
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 31dce559595a..b79582e4fbfd 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -16,6 +16,7 @@
 #include <linux/capability.h>
 #include <linux/spinlock.h>
 #include <linux/security.h>
+#include <linux/in.h>
 #include <net/netlabel.h>
 
 /*
@@ -39,6 +40,7 @@ struct superblock_smack {
 struct socket_smack {
 	char		*smk_out;			/* outbound label */
 	char		*smk_in;			/* inbound label */
+	int		smk_labeled;			/* label scheme */
 	char		smk_packet[SMK_LABELLEN];	/* TCP peer label */
 };
 
@@ -80,6 +82,16 @@ struct smack_cipso {
 };
 
 /*
+ * An entry in the table identifying hosts.
+ */
+struct smk_netlbladdr {
+	struct smk_netlbladdr	*smk_next;
+	struct sockaddr_in	smk_host;	/* network address */
+	struct in_addr		smk_mask;	/* network mask */
+	char			*smk_label;	/* label */
+};
+
+/*
  * This is the repository for labels seen so that it is
  * not necessary to keep allocating tiny chuncks of memory
  * and so that they can be shared.
@@ -127,6 +139,20 @@ struct smack_known {
 #define XATTR_NAME_SMACKIPOUT	XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT
 
 /*
+ * How communications on this socket are treated.
+ * Usually it's determined by the underlying netlabel code
+ * but there are certain cases, including single label hosts
+ * and potentially single label interfaces for which the
+ * treatment can not be known in advance.
+ *
+ * The possibility of additional labeling schemes being
+ * introduced in the future exists as well.
+ */
+#define SMACK_UNLABELED_SOCKET	0
+#define SMACK_CIPSO_SOCKET	1
+
+/*
+ * smackfs magic number
  * smackfs macic number
  */
 #define SMACK_MAGIC	0x43415d53 /* "SMAC" */
@@ -141,6 +167,7 @@ struct smack_known {
  * CIPSO defaults.
  */
 #define SMACK_CIPSO_DOI_DEFAULT		3	/* Historical */
+#define SMACK_CIPSO_DOI_INVALID		-1	/* Not a DOI */
 #define SMACK_CIPSO_DIRECT_DEFAULT	250	/* Arbitrary */
 #define SMACK_CIPSO_MAXCATVAL		63	/* Bigger gets harder */
 #define SMACK_CIPSO_MAXLEVEL            255     /* CIPSO 2.2 standard */
@@ -176,7 +203,6 @@ u32 smack_to_secid(const char *);
  * Shared data.
  */
 extern int smack_cipso_direct;
-extern int smack_net_nltype;
 extern char *smack_net_ambient;
 extern char *smack_onlycap;
 
@@ -186,9 +212,10 @@ extern struct smack_known smack_known_hat;
 extern struct smack_known smack_known_huh;
 extern struct smack_known smack_known_invalid;
 extern struct smack_known smack_known_star;
-extern struct smack_known smack_known_unset;
+extern struct smack_known smack_known_web;
 
 extern struct smk_list_entry *smack_list;
+extern struct smk_netlbladdr *smack_netlbladdrs;
 extern struct security_operations smack_ops;
 
 /*
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 247cec3b5a43..2e0b83e77ffe 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -15,15 +15,8 @@
 #include <linux/sched.h>
 #include "smack.h"
 
-struct smack_known smack_known_unset = {
-	.smk_next	= NULL,
-	.smk_known	= "UNSET",
-	.smk_secid	= 1,
-	.smk_cipso	= NULL,
-};
-
 struct smack_known smack_known_huh = {
-	.smk_next	= &smack_known_unset,
+	.smk_next	= NULL,
 	.smk_known	= "?",
 	.smk_secid	= 2,
 	.smk_cipso	= NULL,
@@ -57,7 +50,14 @@ struct smack_known smack_known_invalid = {
 	.smk_cipso	= NULL,
 };
 
-struct smack_known *smack_known = &smack_known_invalid;
+struct smack_known smack_known_web = {
+	.smk_next	= &smack_known_invalid,
+	.smk_known	= "@",
+	.smk_secid	= 7,
+	.smk_cipso	= NULL,
+};
+
+struct smack_known *smack_known = &smack_known_web;
 
 /*
  * The initial value needs to be bigger than any of the
@@ -99,6 +99,16 @@ int smk_access(char *subject_label, char *object_label, int request)
 	    strcmp(subject_label, smack_known_star.smk_known) == 0)
 		return -EACCES;
 	/*
+	 * An internet object can be accessed by any subject.
+	 * Tasks cannot be assigned the internet label.
+	 * An internet subject can access any object.
+	 */
+	if (object_label == smack_known_web.smk_known ||
+	    subject_label == smack_known_web.smk_known ||
+	    strcmp(object_label, smack_known_web.smk_known) == 0 ||
+	    strcmp(subject_label, smack_known_web.smk_known) == 0)
+		return 0;
+	/*
 	 * A star object can be accessed by any subject.
 	 */
 	if (object_label == smack_known_star.smk_known ||
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 848212fd4845..0278bc083044 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1277,6 +1277,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
 
 	ssp->smk_in = csp;
 	ssp->smk_out = csp;
+	ssp->smk_labeled = SMACK_CIPSO_SOCKET;
 	ssp->smk_packet[0] = '\0';
 
 	sk->sk_security = ssp;
@@ -1341,45 +1342,69 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
 	struct smack_cipso cipso;
 	int rc;
 
-	switch (smack_net_nltype) {
-	case NETLBL_NLTYPE_CIPSOV4:
-		nlsp->domain = smack;
-		nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
+	nlsp->domain = smack;
+	nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
 
-		rc = smack_to_cipso(smack, &cipso);
-		if (rc == 0) {
-			nlsp->attr.mls.lvl = cipso.smk_level;
-			smack_set_catset(cipso.smk_catset, nlsp);
-		} else {
-			nlsp->attr.mls.lvl = smack_cipso_direct;
-			smack_set_catset(smack, nlsp);
-		}
-		break;
-	default:
-		break;
+	rc = smack_to_cipso(smack, &cipso);
+	if (rc == 0) {
+		nlsp->attr.mls.lvl = cipso.smk_level;
+		smack_set_catset(cipso.smk_catset, nlsp);
+	} else {
+		nlsp->attr.mls.lvl = smack_cipso_direct;
+		smack_set_catset(smack, nlsp);
 	}
 }
 
 /**
  * smack_netlabel - Set the secattr on a socket
  * @sk: the socket
+ * @labeled: socket label scheme
  *
  * Convert the outbound smack value (smk_out) to a
  * secattr and attach it to the socket.
  *
  * Returns 0 on success or an error code
  */
-static int smack_netlabel(struct sock *sk)
+static int smack_netlabel(struct sock *sk, int labeled)
 {
 	struct socket_smack *ssp;
 	struct netlbl_lsm_secattr secattr;
-	int rc;
+	int rc = 0;
 
 	ssp = sk->sk_security;
-	netlbl_secattr_init(&secattr);
-	smack_to_secattr(ssp->smk_out, &secattr);
-	rc = netlbl_sock_setattr(sk, &secattr);
-	netlbl_secattr_destroy(&secattr);
+	/*
+	 * Usually the netlabel code will handle changing the
+	 * packet labeling based on the label.
+	 * The case of a single label host is different, because
+	 * a single label host should never get a labeled packet
+	 * even though the label is usually associated with a packet
+	 * label.
+	 */
+	local_bh_disable();
+	bh_lock_sock_nested(sk);
+
+	if (ssp->smk_out == smack_net_ambient ||
+	    labeled == SMACK_UNLABELED_SOCKET)
+		netlbl_sock_delattr(sk);
+	else {
+		netlbl_secattr_init(&secattr);
+		smack_to_secattr(ssp->smk_out, &secattr);
+		rc = netlbl_sock_setattr(sk, &secattr);
+		netlbl_secattr_destroy(&secattr);
+	}
+
+	bh_unlock_sock(sk);
+	local_bh_enable();
+	/*
+	 * Remember the label scheme used so that it is not
+	 * necessary to do the netlabel setting if it has not
+	 * changed the next time through.
+	 *
+	 * The -EDESTADDRREQ case is an indication that there's
+	 * a single level host involved.
+	 */
+	if (rc == 0)
+		ssp->smk_labeled = labeled;
 
 	return rc;
 }
@@ -1432,7 +1457,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
 		ssp->smk_in = sp;
 	else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
 		ssp->smk_out = sp;
-		rc = smack_netlabel(sock->sk);
+		rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
 		if (rc != 0)
 			printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
 			       __func__, -rc);
@@ -1462,7 +1487,108 @@ static int smack_socket_post_create(struct socket *sock, int family,
 	/*
 	 * Set the outbound netlbl.
 	 */
-	return smack_netlabel(sock->sk);
+	return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
+}
+
+
+/**
+ * smack_host_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. Because of the masking
+ * it cannot shortcut out on the first match. There are
+ * numerious ways to address the problem, but none of them
+ * have been applied here.
+ *
+ * Returns the label of the far end or NULL if it's not special.
+ */
+static char *smack_host_label(struct sockaddr_in *sip)
+{
+	struct smk_netlbladdr *snp;
+	char *bestlabel = NULL;
+	struct in_addr *siap = &sip->sin_addr;
+	struct in_addr *liap;
+	struct in_addr *miap;
+	struct in_addr bestmask;
+
+	if (siap->s_addr == 0)
+		return NULL;
+
+	bestmask.s_addr = 0;
+
+	for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) {
+		liap = &snp->smk_host.sin_addr;
+		miap = &snp->smk_mask;
+		/*
+		 * If the addresses match after applying the list entry mask
+		 * the entry matches the address. If it doesn't move along to
+		 * the next entry.
+		 */
+		if ((liap->s_addr & miap->s_addr) !=
+		    (siap->s_addr & miap->s_addr))
+			continue;
+		/*
+		 * If the list entry mask identifies a single address
+		 * it can't get any more specific.
+		 */
+		if (miap->s_addr == 0xffffffff)
+			return snp->smk_label;
+		/*
+		 * If the list entry mask is less specific than the best
+		 * already found this entry is uninteresting.
+		 */
+		if ((miap->s_addr | bestmask.s_addr) == bestmask.s_addr)
+			continue;
+		/*
+		 * This is better than any entry found so far.
+		 */
+		bestmask.s_addr = miap->s_addr;
+		bestlabel = snp->smk_label;
+	}
+
+	return bestlabel;
+}
+
+/**
+ * smack_socket_connect - connect access check
+ * @sock: the socket
+ * @sap: the other end
+ * @addrlen: size of sap
+ *
+ * Verifies that a connection may be possible
+ *
+ * Returns 0 on success, and error code otherwise
+ */
+static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
+				int addrlen)
+{
+	struct socket_smack *ssp = sock->sk->sk_security;
+	char *hostsp;
+	int rc;
+
+	if (sock->sk == NULL || sock->sk->sk_family != PF_INET)
+		return 0;
+
+	if (addrlen < sizeof(struct sockaddr_in))
+		return -EINVAL;
+
+	hostsp = smack_host_label((struct sockaddr_in *)sap);
+	if (hostsp == NULL) {
+		if (ssp->smk_labeled != SMACK_CIPSO_SOCKET)
+			return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
+		return 0;
+	}
+
+	rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE);
+	if (rc != 0)
+		return rc;
+
+	if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET)
+		return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET);
+	return 0;
 }
 
 /**
@@ -2101,8 +2227,14 @@ static int smack_setprocattr(struct task_struct *p, char *name,
 	if (newsmack == NULL)
 		return -EINVAL;
 
+	/*
+	 * No process is ever allowed the web ("@") label.
+	 */
+	if (newsmack == smack_known_web.smk_known)
+		return -EPERM;
+
 	new = prepare_creds();
-	if (!new)
+	if (new == NULL)
 		return -ENOMEM;
 	new->security = newsmack;
 	commit_creds(new);
@@ -2144,6 +2276,49 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
 }
 
 /**
+ * smack_socket_sendmsg - Smack check based on destination host
+ * @sock: the socket
+ * @msghdr: the message
+ * @size: the size of the message
+ *
+ * Return 0 if the current subject can write to the destination
+ * host. This is only a question if the destination is a single
+ * label host.
+ */
+static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+				int size)
+{
+	struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
+	struct socket_smack *ssp = sock->sk->sk_security;
+	char *hostsp;
+	int rc;
+
+	/*
+	 * Perfectly reasonable for this to be NULL
+	 */
+	if (sip == NULL || sip->sin_family != PF_INET)
+		return 0;
+
+	hostsp = smack_host_label(sip);
+	if (hostsp == NULL) {
+		if (ssp->smk_labeled != SMACK_CIPSO_SOCKET)
+			return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
+		return 0;
+	}
+
+	rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE);
+	if (rc != 0)
+		return rc;
+
+	if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET)
+		return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET);
+
+	return 0;
+
+}
+
+
+/**
  * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
  * 	pair to smack
  * @sap: netlabel secattr
@@ -2154,44 +2329,66 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
 static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
 {
 	char smack[SMK_LABELLEN];
+	char *sp;
 	int pcat;
 
-	if ((sap->flags & NETLBL_SECATTR_MLS_LVL) == 0) {
+	if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
 		/*
+		 * Looks like a CIPSO packet.
 		 * If there are flags but no level netlabel isn't
 		 * behaving the way we expect it to.
 		 *
+		 * Get the categories, if any
 		 * Without guidance regarding the smack value
 		 * for the packet fall back on the network
 		 * ambient value.
 		 */
-		strncpy(sip, smack_net_ambient, SMK_MAXLEN);
+		memset(smack, '\0', SMK_LABELLEN);
+		if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
+			for (pcat = -1;;) {
+				pcat = netlbl_secattr_catmap_walk(
+					sap->attr.mls.cat, pcat + 1);
+				if (pcat < 0)
+					break;
+				smack_catset_bit(pcat, smack);
+			}
+		/*
+		 * If it is CIPSO using smack direct mapping
+		 * we are already done. WeeHee.
+		 */
+		if (sap->attr.mls.lvl == smack_cipso_direct) {
+			memcpy(sip, smack, SMK_MAXLEN);
+			return;
+		}
+		/*
+		 * Look it up in the supplied table if it is not
+		 * a direct mapping.
+		 */
+		smack_from_cipso(sap->attr.mls.lvl, smack, sip);
 		return;
 	}
-	/*
-	 * Get the categories, if any
-	 */
-	memset(smack, '\0', SMK_LABELLEN);
-	if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
-		for (pcat = -1;;) {
-			pcat = netlbl_secattr_catmap_walk(sap->attr.mls.cat,
-							  pcat + 1);
-			if (pcat < 0)
-				break;
-			smack_catset_bit(pcat, smack);
-		}
-	/*
-	 * If it is CIPSO using smack direct mapping
-	 * we are already done. WeeHee.
-	 */
-	if (sap->attr.mls.lvl == smack_cipso_direct) {
-		memcpy(sip, smack, SMK_MAXLEN);
+	if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
+		/*
+		 * Looks like a fallback, which gives us a secid.
+		 */
+		sp = smack_from_secid(sap->attr.secid);
+		/*
+		 * This has got to be a bug because it is
+		 * impossible to specify a fallback without
+		 * specifying the label, which will ensure
+		 * it has a secid, and the only way to get a
+		 * secid is from a fallback.
+		 */
+		BUG_ON(sp == NULL);
+		strncpy(sip, sp, SMK_MAXLEN);
 		return;
 	}
 	/*
-	 * Look it up in the supplied table if it is not a direct mapping.
+	 * Without guidance regarding the smack value
+	 * for the packet fall back on the network
+	 * ambient value.
 	 */
-	smack_from_cipso(sap->attr.mls.lvl, smack, sip);
+	strncpy(sip, smack_net_ambient, SMK_MAXLEN);
 	return;
 }
 
@@ -2207,6 +2404,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 	struct netlbl_lsm_secattr secattr;
 	struct socket_smack *ssp = sk->sk_security;
 	char smack[SMK_LABELLEN];
+	char *csp;
 	int rc;
 
 	if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
@@ -2215,21 +2413,24 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 	/*
 	 * Translate what netlabel gave us.
 	 */
-	memset(smack, '\0', SMK_LABELLEN);
 	netlbl_secattr_init(&secattr);
+
 	rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
-	if (rc == 0)
+	if (rc == 0) {
 		smack_from_secattr(&secattr, smack);
-	else
-		strncpy(smack, smack_net_ambient, SMK_MAXLEN);
+		csp = smack;
+	} else
+		csp = smack_net_ambient;
+
 	netlbl_secattr_destroy(&secattr);
+
 	/*
 	 * Receiving a packet requires that the other end
 	 * be able to write here. Read access is not required.
 	 * This is the simplist possible security model
 	 * for networking.
 	 */
-	rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
+	rc = smk_access(csp, ssp->smk_in, MAY_WRITE);
 	if (rc != 0)
 		netlbl_skbuff_err(skb, rc, 0);
 	return rc;
@@ -2298,7 +2499,6 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
 	/*
 	 * Translate what netlabel gave us.
 	 */
-	memset(smack, '\0', SMK_LABELLEN);
 	netlbl_secattr_init(&secattr);
 	rc = netlbl_skbuff_getattr(skb, family, &secattr);
 	if (rc == 0)
@@ -2341,7 +2541,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
 	ssp->smk_in = ssp->smk_out = current_security();
 	ssp->smk_packet[0] = '\0';
 
-	rc = smack_netlabel(sk);
+	rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET);
 	if (rc != 0)
 		printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
 		       __func__, -rc);
@@ -2367,7 +2567,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	if (skb == NULL)
 		return -EACCES;
 
-	memset(smack, '\0', SMK_LABELLEN);
 	netlbl_secattr_init(&skb_secattr);
 	rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr);
 	if (rc == 0)
@@ -2732,6 +2931,8 @@ struct security_operations smack_ops = {
 	.unix_may_send = 		smack_unix_may_send,
 
 	.socket_post_create = 		smack_socket_post_create,
+	.socket_connect =		smack_socket_connect,
+	.socket_sendmsg =		smack_socket_sendmsg,
 	.socket_sock_rcv_skb = 		smack_socket_sock_rcv_skb,
 	.socket_getpeersec_stream =	smack_socket_getpeersec_stream,
 	.socket_getpeersec_dgram =	smack_socket_getpeersec_dgram,
@@ -2783,7 +2984,6 @@ static __init int smack_init(void)
 	/*
 	 * Initialize locks
 	 */
-	spin_lock_init(&smack_known_unset.smk_cipsolock);
 	spin_lock_init(&smack_known_huh.smk_cipsolock);
 	spin_lock_init(&smack_known_hat.smk_cipsolock);
 	spin_lock_init(&smack_known_star.smk_cipsolock);
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 247dc9ebbc71..bf107a389ac1 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 */ {""}