ada6d8
diff -up libselinux-2.0.77/include/selinux/avc.h.pre.create.cache libselinux-2.0.77/include/selinux/avc.h
ada6d8
--- libselinux-2.0.77/include/selinux/avc.h.pre.create.cache	2009-01-27 14:47:32.000000000 -0500
ada6d8
+++ libselinux-2.0.77/include/selinux/avc.h	2009-03-02 14:52:40.859167987 -0500
ada6d8
@@ -353,6 +353,7 @@ int avc_compute_member(security_id_t ssi
ada6d8
 #define AVC_CALLBACK_AUDITALLOW_DISABLE	32
ada6d8
 #define AVC_CALLBACK_AUDITDENY_ENABLE	64
ada6d8
 #define AVC_CALLBACK_AUDITDENY_DISABLE	128
ada6d8
+#define AVC_CALLBACK_ADD_CREATE		256
ada6d8
 
ada6d8
 /**
ada6d8
  * avc_add_callback - Register a callback for security events.
ada6d8
diff -up libselinux-2.0.77/src/avc.c.pre.create.cache libselinux-2.0.77/src/avc.c
ada6d8
--- libselinux-2.0.77/src/avc.c.pre.create.cache	2009-01-27 14:47:32.000000000 -0500
ada6d8
+++ libselinux-2.0.77/src/avc.c	2009-03-02 15:57:54.764288907 -0500
ada6d8
@@ -20,6 +20,8 @@ struct avc_entry {
ada6d8
 	security_id_t tsid;
ada6d8
 	security_class_t tclass;
ada6d8
 	struct av_decision avd;
ada6d8
+	security_id_t	create_sid;
ada6d8
+	unsigned	create_decided	:1;
ada6d8
 	int used;		/* used recently */
ada6d8
 };
ada6d8
 
ada6d8
@@ -58,6 +60,11 @@ static struct avc_cache_stats cache_stat
ada6d8
 static struct avc_callback_node *avc_callbacks = NULL;
ada6d8
 static struct sidtab avc_sidtab;
ada6d8
 
ada6d8
+/* forward declaration */
ada6d8
+static int avc_update_cache(uint32_t event, security_id_t ssid,
ada6d8
+			    security_id_t tsid, security_class_t tclass,
ada6d8
+			    access_vector_t perms, security_id_t create_sid);
ada6d8
+
ada6d8
 static inline int avc_hash(security_id_t ssid,
ada6d8
 			   security_id_t tsid, security_class_t tclass)
ada6d8
 {
ada6d8
@@ -340,6 +347,16 @@ static inline struct avc_node *avc_recla
ada6d8
 	return cur;
ada6d8
 }
ada6d8
 
ada6d8
+static inline void avc_clear_avc_entry(struct avc_entry *ae)
ada6d8
+{
ada6d8
+	ae->ssid = ae->tsid = ae->create_sid = NULL;
ada6d8
+	ae->tclass = 0;
ada6d8
+	ae->create_decided = 0;
ada6d8
+	ae->avd.allowed = ae->avd.decided = 0;
ada6d8
+	ae->avd.auditallow = ae->avd.auditdeny = 0;
ada6d8
+	ae->used = 0;
ada6d8
+}
ada6d8
+
ada6d8
 static inline struct avc_node *avc_claim_node(security_id_t ssid,
ada6d8
 					      security_id_t tsid,
ada6d8
 					      security_class_t tclass)
ada6d8
@@ -361,6 +378,7 @@ static inline struct avc_node *avc_claim
ada6d8
 	}
ada6d8
 
ada6d8
 	hvalue = avc_hash(ssid, tsid, tclass);
ada6d8
+	avc_clear_avc_entry(&new->ae);
ada6d8
 	new->ae.used = 1;
ada6d8
 	new->ae.ssid = ssid;
ada6d8
 	new->ae.tsid = tsid;
ada6d8
@@ -498,8 +516,8 @@ static int avc_insert(security_id_t ssid
ada6d8
  * avc_remove - Remove AVC and sidtab entries for SID.
ada6d8
  * @sid: security identifier to be removed
ada6d8
  *
ada6d8
- * Remove all AVC entries containing @sid as source
ada6d8
- * or target, and remove @sid from the SID table.
ada6d8
+ * Remove all AVC entries containing @sid as source, target, or
ada6d8
+ * create_sid, and remove @sid from the SID table.
ada6d8
  * Free the memory allocated for the structure corresponding
ada6d8
  * to @sid.  After this function has been called, @sid must
ada6d8
  * not be used until another call to avc_context_to_sid() has
ada6d8
@@ -514,19 +532,15 @@ static void avc_remove(security_id_t sid
ada6d8
 		cur = avc_cache.slots[i];
ada6d8
 		prev = NULL;
ada6d8
 		while (cur) {
ada6d8
-			if (sid == cur->ae.ssid || sid == cur->ae.tsid) {
ada6d8
+			if (sid == cur->ae.ssid || sid == cur->ae.tsid ||
ada6d8
+			    (cur->ae.create_decided && sid == cur->ae.create_sid)) {
ada6d8
 				if (prev)
ada6d8
 					prev->next = cur->next;
ada6d8
 				else
ada6d8
 					avc_cache.slots[i] = cur->next;
ada6d8
 				tmp = cur;
ada6d8
 				cur = cur->next;
ada6d8
-				tmp->ae.ssid = tmp->ae.tsid = NULL;
ada6d8
-				tmp->ae.tclass = 0;
ada6d8
-				tmp->ae.avd.allowed = tmp->ae.avd.decided = 0;
ada6d8
-				tmp->ae.avd.auditallow = tmp->ae.avd.auditdeny =
ada6d8
-				    0;
ada6d8
-				tmp->ae.used = 0;
ada6d8
+				avc_clear_avc_entry(&tmp->ae);
ada6d8
 				tmp->next = avc_node_freelist;
ada6d8
 				avc_node_freelist = tmp;
ada6d8
 				avc_cache.active_nodes--;
ada6d8
@@ -570,11 +584,7 @@ int avc_reset(void)
ada6d8
 		while (node) {
ada6d8
 			tmp = node;
ada6d8
 			node = node->next;
ada6d8
-			tmp->ae.ssid = tmp->ae.tsid = NULL;
ada6d8
-			tmp->ae.tclass = 0;
ada6d8
-			tmp->ae.avd.allowed = tmp->ae.avd.decided = 0;
ada6d8
-			tmp->ae.avd.auditallow = tmp->ae.avd.auditdeny = 0;
ada6d8
-			tmp->ae.used = 0;
ada6d8
+			avc_clear_avc_entry(&tmp->ae);
ada6d8
 			tmp->next = avc_node_freelist;
ada6d8
 			avc_node_freelist = tmp;
ada6d8
 			avc_cache.active_nodes--;
ada6d8
@@ -896,24 +906,52 @@ int avc_compute_create(security_id_t ssi
ada6d8
 		       security_class_t tclass, security_id_t *newsid)
ada6d8
 {
ada6d8
 	int rc;
ada6d8
+	struct avc_entry_ref aeref;
ada6d8
+	security_context_t ctx = NULL;
ada6d8
+
ada6d8
 	*newsid = NULL;
ada6d8
+
ada6d8
+	avc_entry_ref_init(&aeref);
ada6d8
+retry:
ada6d8
 	avc_get_lock(avc_lock);
ada6d8
-	if (ssid->refcnt > 0 && tsid->refcnt > 0) {
ada6d8
-		security_context_t ctx = NULL;
ada6d8
-		rc = security_compute_create_raw(ssid->ctx, tsid->ctx, tclass,
ada6d8
-						 &ctx);
ada6d8
-		if (rc)
ada6d8
-			goto out;
ada6d8
-		rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
ada6d8
-		if (!rc)
ada6d8
-			(*newsid)->refcnt++;
ada6d8
-		freecon(ctx);
ada6d8
-	} else {
ada6d8
+	if (ssid->refcnt <= 0 || tsid->refcnt <= 0) {
ada6d8
 		errno = EINVAL;	/* bad reference count */
ada6d8
 		rc = -1;
ada6d8
+		goto out;
ada6d8
+	}
ada6d8
+
ada6d8
+	rc = avc_lookup(ssid, tsid, tclass, 0, &aeref);
ada6d8
+	if (!rc) {
ada6d8
+		/* we found something in the avc */
ada6d8
+		if (aeref.ae->create_decided) {
ada6d8
+			*newsid = aeref.ae->create_sid;
ada6d8
+			goto out;
ada6d8
+		} else {
ada6d8
+			goto compute;
ada6d8
+		}
ada6d8
 	}
ada6d8
+	/* there is nothing in the avd for this tuple, so, lets get something */
ada6d8
+	avc_release_lock(avc_lock);
ada6d8
+	avc_has_perm_noaudit(ssid, tsid, tclass, 0, &aeref, NULL);
ada6d8
+	goto retry;
ada6d8
+
ada6d8
+compute:
ada6d8
+	rc = security_compute_create_raw(ssid->ctx, tsid->ctx, tclass,
ada6d8
+					 &ctx);
ada6d8
+	if (rc)
ada6d8
+		goto out;
ada6d8
+	rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
ada6d8
+	if (rc)
ada6d8
+		goto out;
ada6d8
+
ada6d8
+	avc_update_cache(AVC_CALLBACK_ADD_CREATE, ssid, tsid, tclass, 0,
ada6d8
+			 *newsid);
ada6d8
+
ada6d8
 out:
ada6d8
+	if (*newsid)
ada6d8
+		(*newsid)->refcnt++;
ada6d8
 	avc_release_lock(avc_lock);
ada6d8
+	freecon(ctx);
ada6d8
 	return rc;
ada6d8
 }
ada6d8
 
ada6d8
@@ -978,7 +1016,8 @@ static inline int avc_sidcmp(security_id
ada6d8
 }
ada6d8
 
ada6d8
 static inline void avc_update_node(uint32_t event, struct avc_node *node,
ada6d8
-				   access_vector_t perms)
ada6d8
+				   access_vector_t perms,
ada6d8
+				   security_id_t create_sid)
ada6d8
 {
ada6d8
 	switch (event) {
ada6d8
 	case AVC_CALLBACK_GRANT:
ada6d8
@@ -1000,12 +1039,16 @@ static inline void avc_update_node(uint3
ada6d8
 	case AVC_CALLBACK_AUDITDENY_DISABLE:
ada6d8
 		node->ae.avd.auditdeny &= ~perms;
ada6d8
 		break;
ada6d8
+	case AVC_CALLBACK_ADD_CREATE:
ada6d8
+		node->ae.create_sid = create_sid;
ada6d8
+		node->ae.create_decided = 1;
ada6d8
+		break;
ada6d8
 	}
ada6d8
 }
ada6d8
 
ada6d8
 static int avc_update_cache(uint32_t event, security_id_t ssid,
ada6d8
 			    security_id_t tsid, security_class_t tclass,
ada6d8
-			    access_vector_t perms)
ada6d8
+			    access_vector_t perms, security_id_t create_sid)
ada6d8
 {
ada6d8
 	struct avc_node *node;
ada6d8
 	int i;
ada6d8
@@ -1019,7 +1062,7 @@ static int avc_update_cache(uint32_t eve
ada6d8
 				if (avc_sidcmp(ssid, node->ae.ssid) &&
ada6d8
 				    avc_sidcmp(tsid, node->ae.tsid) &&
ada6d8
 				    tclass == node->ae.tclass) {
ada6d8
-					avc_update_node(event, node, perms);
ada6d8
+					avc_update_node(event, node, perms, create_sid);
ada6d8
 				}
ada6d8
 			}
ada6d8
 		}
ada6d8
@@ -1027,7 +1070,7 @@ static int avc_update_cache(uint32_t eve
ada6d8
 		/* apply to one node */
ada6d8
 		node = avc_search_node(ssid, tsid, tclass, 0);
ada6d8
 		if (node) {
ada6d8
-			avc_update_node(event, node, perms);
ada6d8
+			avc_update_node(event, node, perms, create_sid);
ada6d8
 		}
ada6d8
 	}
ada6d8
 
ada6d8
@@ -1058,7 +1101,7 @@ static int avc_control(uint32_t event, s
ada6d8
 	 * been invoked to update the cache state.
ada6d8
 	 */
ada6d8
 	if (event != AVC_CALLBACK_TRY_REVOKE)
ada6d8
-		avc_update_cache(event, ssid, tsid, tclass, perms);
ada6d8
+		avc_update_cache(event, ssid, tsid, tclass, perms, NULL);
ada6d8
 
ada6d8
 	for (c = avc_callbacks; c; c = c->next) {
ada6d8
 		if ((c->events & event) &&
ada6d8
@@ -1080,7 +1123,7 @@ static int avc_control(uint32_t event, s
ada6d8
 	if (event == AVC_CALLBACK_TRY_REVOKE) {
ada6d8
 		/* revoke any unretained permissions */
ada6d8
 		perms &= ~tretained;
ada6d8
-		avc_update_cache(event, ssid, tsid, tclass, perms);
ada6d8
+		avc_update_cache(event, ssid, tsid, tclass, perms, NULL);
ada6d8
 		*out_retained = tretained;
ada6d8
 	}
ada6d8
 
ada6d8