smack: Add a new '-CIPSO' option to the network address label configuration

This patch adds a new special option '-CIPSO' to the Smack subsystem. When used
in the netlabel list, it means "use CIPSO networking". A use case is when your
local network speaks CIPSO and you want also to connect to the unlabeled
Internet. This patch also add some documentation describing that. The patch
also corrects an oops when setting a '' SMACK64 xattr to a file.

Signed-off-by: Etienne Basset <etienne.basset@numericable.fr>
Signed-off-by: Paul Moore <paul.moore@hp.com>
Acked-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: James Morris <jmorris@namei.org>
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 5e5a3bc..42ef313 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -132,6 +132,8 @@
 #define XATTR_NAME_SMACKIPIN	XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN
 #define XATTR_NAME_SMACKIPOUT	XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT
 
+#define SMACK_CIPSO_OPTION 	"-CIPSO"
+
 /*
  * How communications on this socket are treated.
  * Usually it's determined by the underlying netlabel code
@@ -199,6 +201,7 @@
 extern int smack_cipso_direct;
 extern char *smack_net_ambient;
 extern char *smack_onlycap;
+extern const char *smack_cipso_option;
 
 extern struct smack_known smack_known_floor;
 extern struct smack_known smack_known_hat;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 5856419..ac0a270 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -261,6 +261,9 @@
 {
 	struct smack_known *skp;
 
+	/* labels cannot begin with a '-' */
+	if (string[0] == '-')
+		return NULL;
 	skp = smk_import_entry(string, len);
 	if (skp == NULL)
 		return NULL;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 8ed502c..9215149 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -609,6 +609,9 @@
 	    strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
 		if (!capable(CAP_MAC_ADMIN))
 			rc = -EPERM;
+		/* a label cannot be void and cannot begin with '-' */
+		if (size == 0 || (size > 0 && ((char *)value)[0] == '-'))
+			rc = -EINVAL;
 	} else
 		rc = cap_inode_setxattr(dentry, name, value, size, flags);
 
@@ -1323,8 +1326,12 @@
 		* so we have found the most specific match
 		*/
 		if ((&snp->smk_host.sin_addr)->s_addr ==
-		    (siap->s_addr & (&snp->smk_mask)->s_addr))
+		    (siap->s_addr & (&snp->smk_mask)->s_addr)) {
+			/* we have found the special CIPSO option */
+			if (snp->smk_label == smack_cipso_option)
+				return NULL;
 			return snp->smk_label;
+		}
 
 	return NULL;
 }
@@ -1486,7 +1493,7 @@
 	struct socket *sock;
 	int rc = 0;
 
-	if (value == NULL || size > SMK_LABELLEN)
+	if (value == NULL || size > SMK_LABELLEN || size == 0)
 		return -EACCES;
 
 	sp = smk_import(value, size);
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 856c8a2..e03a7e1 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -86,6 +86,9 @@
 
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 
+const char *smack_cipso_option = SMACK_CIPSO_OPTION;
+
+
 #define	SEQ_READ_FINISHED	1
 
 /*
@@ -565,6 +568,11 @@
 		goto unlockedout;
 	}
 
+	/* labels cannot begin with a '-' */
+	if (data[0] == '-') {
+		rc = -EINVAL;
+		goto unlockedout;
+	}
 	data[count] = '\0';
 	rule = data;
 	/*
@@ -808,9 +816,18 @@
 	if (m > BEBITS)
 		return -EINVAL;
 
-	sp = smk_import(smack, 0);
-	if (sp == NULL)
-		return -EINVAL;
+	/* if smack begins with '-', its an option, don't import it */
+	if (smack[0] != '-') {
+		sp = smk_import(smack, 0);
+		if (sp == NULL)
+			return -EINVAL;
+	} else {
+		/* check known options */
+		if (strcmp(smack, smack_cipso_option) == 0)
+			sp = (char *)smack_cipso_option;
+		else
+			return -EINVAL;
+	}
 
 	for (temp_mask = 0; m > 0; m--) {
 		temp_mask |= mask_bits;
@@ -849,18 +866,23 @@
 			smk_netlbladdr_insert(skp);
 		}
 	} else {
-		rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
-			&skp->smk_host.sin_addr, &skp->smk_mask,
-			PF_INET, &audit_info);
+		/* we delete the unlabeled entry, only if the previous label
+		 * wasnt the special CIPSO option */
+		if (skp->smk_label != smack_cipso_option)
+			rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
+					&skp->smk_host.sin_addr, &skp->smk_mask,
+					PF_INET, &audit_info);
+		else
+			rc = 0;
 		skp->smk_label = sp;
 	}
 
 	/*
 	 * Now tell netlabel about the single label nature of
 	 * this host so that incoming packets get labeled.
+	 * but only if we didn't get the special CIPSO option
 	 */
-
-	if (rc == 0)
+	if (rc == 0 && sp != smack_cipso_option)
 		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);