diff options
Diffstat (limited to 'security/selinux/ss/services.c')
| -rw-r--r-- | security/selinux/ss/services.c | 220 | 
1 files changed, 159 insertions, 61 deletions
| diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 3438d0130378..301633145040 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -67,6 +67,17 @@  #include "policycap_names.h"  #include "ima.h" +struct convert_context_args { +	struct selinux_state *state; +	struct policydb *oldp; +	struct policydb *newp; +}; + +struct selinux_policy_convert_data { +	struct convert_context_args args; +	struct sidtab_convert_params sidtab_params; +}; +  /* Forward declaration. */  static int context_struct_to_string(struct policydb *policydb,  				    struct context *context, @@ -1541,6 +1552,7 @@ static int security_context_to_sid_core(struct selinux_state *state,  		if (!str)  			goto out;  	} +retry:  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -1554,6 +1566,15 @@ static int security_context_to_sid_core(struct selinux_state *state,  	} else if (rc)  		goto out_unlock;  	rc = sidtab_context_to_sid(sidtab, &context, sid); +	if (rc == -ESTALE) { +		rcu_read_unlock(); +		if (context.str) { +			str = context.str; +			context.str = NULL; +		} +		context_destroy(&context); +		goto retry; +	}  	context_destroy(&context);  out_unlock:  	rcu_read_unlock(); @@ -1703,7 +1724,7 @@ static int security_compute_sid(struct selinux_state *state,  	struct selinux_policy *policy;  	struct policydb *policydb;  	struct sidtab *sidtab; -	struct class_datum *cladatum = NULL; +	struct class_datum *cladatum;  	struct context *scontext, *tcontext, newcontext;  	struct sidtab_entry *sentry, *tentry;  	struct avtab_key avkey; @@ -1725,6 +1746,8 @@ static int security_compute_sid(struct selinux_state *state,  		goto out;  	} +retry: +	cladatum = NULL;  	context_init(&newcontext);  	rcu_read_lock(); @@ -1869,6 +1892,11 @@ static int security_compute_sid(struct selinux_state *state,  	}  	/* Obtain the sid for the context. */  	rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); +	if (rc == -ESTALE) { +		rcu_read_unlock(); +		context_destroy(&newcontext); +		goto retry; +	}  out_unlock:  	rcu_read_unlock();  	context_destroy(&newcontext); @@ -1974,12 +2002,6 @@ static inline int convert_context_handle_invalid_context(  	return 0;  } -struct convert_context_args { -	struct selinux_state *state; -	struct policydb *oldp; -	struct policydb *newp; -}; -  /*   * Convert the values in the security context   * structure `oldc' from the values specified @@ -2159,7 +2181,7 @@ static void selinux_policy_cond_free(struct selinux_policy *policy)  }  void selinux_policy_cancel(struct selinux_state *state, -			struct selinux_policy *policy) +			   struct selinux_load_state *load_state)  {  	struct selinux_policy *oldpolicy; @@ -2167,7 +2189,8 @@ void selinux_policy_cancel(struct selinux_state *state,  					lockdep_is_held(&state->policy_mutex));  	sidtab_cancel_convert(oldpolicy->sidtab); -	selinux_policy_free(policy); +	selinux_policy_free(load_state->policy); +	kfree(load_state->convert_data);  }  static void selinux_notify_policy_change(struct selinux_state *state, @@ -2183,9 +2206,10 @@ static void selinux_notify_policy_change(struct selinux_state *state,  }  void selinux_policy_commit(struct selinux_state *state, -			struct selinux_policy *newpolicy) +			   struct selinux_load_state *load_state)  { -	struct selinux_policy *oldpolicy; +	struct selinux_policy *oldpolicy, *newpolicy = load_state->policy; +	unsigned long flags;  	u32 seqno;  	oldpolicy = rcu_dereference_protected(state->policy, @@ -2207,7 +2231,13 @@ void selinux_policy_commit(struct selinux_state *state,  	seqno = newpolicy->latest_granting;  	/* Install the new policy. */ -	rcu_assign_pointer(state->policy, newpolicy); +	if (oldpolicy) { +		sidtab_freeze_begin(oldpolicy->sidtab, &flags); +		rcu_assign_pointer(state->policy, newpolicy); +		sidtab_freeze_end(oldpolicy->sidtab, &flags); +	} else { +		rcu_assign_pointer(state->policy, newpolicy); +	}  	/* Load the policycaps from the new policy */  	security_load_policycaps(state, newpolicy); @@ -2225,6 +2255,7 @@ void selinux_policy_commit(struct selinux_state *state,  	/* Free the old policy */  	synchronize_rcu();  	selinux_policy_free(oldpolicy); +	kfree(load_state->convert_data);  	/* Notify others of the policy change */  	selinux_notify_policy_change(state, seqno); @@ -2241,11 +2272,10 @@ void selinux_policy_commit(struct selinux_state *state,   * loading the new policy.   */  int security_load_policy(struct selinux_state *state, void *data, size_t len, -			struct selinux_policy **newpolicyp) +			 struct selinux_load_state *load_state)  {  	struct selinux_policy *newpolicy, *oldpolicy; -	struct sidtab_convert_params convert_params; -	struct convert_context_args args; +	struct selinux_policy_convert_data *convert_data;  	int rc = 0;  	struct policy_file file = { data, len }, *fp = &file; @@ -2275,10 +2305,10 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,  		goto err_mapping;  	} -  	if (!selinux_initialized(state)) {  		/* First policy load, so no need to preserve state from old policy */ -		*newpolicyp = newpolicy; +		load_state->policy = newpolicy; +		load_state->convert_data = NULL;  		return 0;  	} @@ -2292,29 +2322,38 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,  		goto err_free_isids;  	} +	convert_data = kmalloc(sizeof(*convert_data), GFP_KERNEL); +	if (!convert_data) { +		rc = -ENOMEM; +		goto err_free_isids; +	} +  	/*  	 * Convert the internal representations of contexts  	 * in the new SID table.  	 */ -	args.state = state; -	args.oldp = &oldpolicy->policydb; -	args.newp = &newpolicy->policydb; +	convert_data->args.state = state; +	convert_data->args.oldp = &oldpolicy->policydb; +	convert_data->args.newp = &newpolicy->policydb; -	convert_params.func = convert_context; -	convert_params.args = &args; -	convert_params.target = newpolicy->sidtab; +	convert_data->sidtab_params.func = convert_context; +	convert_data->sidtab_params.args = &convert_data->args; +	convert_data->sidtab_params.target = newpolicy->sidtab; -	rc = sidtab_convert(oldpolicy->sidtab, &convert_params); +	rc = sidtab_convert(oldpolicy->sidtab, &convert_data->sidtab_params);  	if (rc) {  		pr_err("SELinux:  unable to convert the internal"  			" representation of contexts in the new SID"  			" table\n"); -		goto err_free_isids; +		goto err_free_convert_data;  	} -	*newpolicyp = newpolicy; +	load_state->policy = newpolicy; +	load_state->convert_data = convert_data;  	return 0; +err_free_convert_data: +	kfree(convert_data);  err_free_isids:  	sidtab_destroy(newpolicy->sidtab);  err_mapping: @@ -2342,13 +2381,15 @@ int security_port_sid(struct selinux_state *state,  	struct policydb *policydb;  	struct sidtab *sidtab;  	struct ocontext *c; -	int rc = 0; +	int rc;  	if (!selinux_initialized(state)) {  		*out_sid = SECINITSID_PORT;  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2367,6 +2408,10 @@ int security_port_sid(struct selinux_state *state,  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2393,13 +2438,15 @@ int security_ib_pkey_sid(struct selinux_state *state,  	struct policydb *policydb;  	struct sidtab *sidtab;  	struct ocontext *c; -	int rc = 0; +	int rc;  	if (!selinux_initialized(state)) {  		*out_sid = SECINITSID_UNLABELED;  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2420,6 +2467,10 @@ int security_ib_pkey_sid(struct selinux_state *state,  			rc = sidtab_context_to_sid(sidtab,  						   &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2445,13 +2496,15 @@ int security_ib_endport_sid(struct selinux_state *state,  	struct policydb *policydb;  	struct sidtab *sidtab;  	struct ocontext *c; -	int rc = 0; +	int rc;  	if (!selinux_initialized(state)) {  		*out_sid = SECINITSID_UNLABELED;  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2472,6 +2525,10 @@ int security_ib_endport_sid(struct selinux_state *state,  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2495,7 +2552,7 @@ int security_netif_sid(struct selinux_state *state,  	struct selinux_policy *policy;  	struct policydb *policydb;  	struct sidtab *sidtab; -	int rc = 0; +	int rc;  	struct ocontext *c;  	if (!selinux_initialized(state)) { @@ -2503,6 +2560,8 @@ int security_netif_sid(struct selinux_state *state,  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2519,10 +2578,18 @@ int security_netif_sid(struct selinux_state *state,  		if (!c->sid[0] || !c->sid[1]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  			rc = sidtab_context_to_sid(sidtab, &c->context[1],  						   &c->sid[1]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2572,6 +2639,7 @@ int security_node_sid(struct selinux_state *state,  		return 0;  	} +retry:  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2620,6 +2688,10 @@ int security_node_sid(struct selinux_state *state,  			rc = sidtab_context_to_sid(sidtab,  						   &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2661,18 +2733,24 @@ int security_get_user_sids(struct selinux_state *state,  	struct sidtab *sidtab;  	struct context *fromcon, usercon;  	u32 *mysids = NULL, *mysids2, sid; -	u32 mynel = 0, maxnel = SIDS_NEL; +	u32 i, j, mynel, maxnel = SIDS_NEL;  	struct user_datum *user;  	struct role_datum *role;  	struct ebitmap_node *rnode, *tnode; -	int rc = 0, i, j; +	int rc;  	*sids = NULL;  	*nel = 0;  	if (!selinux_initialized(state)) -		goto out; +		return 0; + +	mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL); +	if (!mysids) +		return -ENOMEM; +retry: +	mynel = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2692,11 +2770,6 @@ int security_get_user_sids(struct selinux_state *state,  	usercon.user = user->value; -	rc = -ENOMEM; -	mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC); -	if (!mysids) -		goto out_unlock; -  	ebitmap_for_each_positive_bit(&user->roles, rnode, i) {  		role = policydb->role_val_to_struct[i];  		usercon.role = i + 1; @@ -2708,6 +2781,10 @@ int security_get_user_sids(struct selinux_state *state,  				continue;  			rc = sidtab_context_to_sid(sidtab, &usercon, &sid); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out_unlock;  			if (mynel < maxnel) { @@ -2730,14 +2807,14 @@ out_unlock:  	rcu_read_unlock();  	if (rc || !mynel) {  		kfree(mysids); -		goto out; +		return rc;  	}  	rc = -ENOMEM;  	mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);  	if (!mysids2) {  		kfree(mysids); -		goto out; +		return rc;  	}  	for (i = 0, j = 0; i < mynel; i++) {  		struct av_decision dummy_avd; @@ -2750,12 +2827,10 @@ out_unlock:  			mysids2[j++] = mysids[i];  		cond_resched();  	} -	rc = 0;  	kfree(mysids);  	*sids = mysids2;  	*nel = j; -out: -	return rc; +	return 0;  }  /** @@ -2768,6 +2843,9 @@ out:   * Obtain a SID to use for a file in a filesystem that   * cannot support xattr or use a fixed labeling behavior like   * transition SIDs or task SIDs. + * + * WARNING: This function may return -ESTALE, indicating that the caller + * must retry the operation after re-acquiring the policy pointer!   */  static inline int __security_genfs_sid(struct selinux_policy *policy,  				       const char *fstype, @@ -2846,11 +2924,13 @@ int security_genfs_sid(struct selinux_state *state,  		return 0;  	} -	rcu_read_lock(); -	policy = rcu_dereference(state->policy); -	retval = __security_genfs_sid(policy, -				fstype, path, orig_sclass, sid); -	rcu_read_unlock(); +	do { +		rcu_read_lock(); +		policy = rcu_dereference(state->policy); +		retval = __security_genfs_sid(policy, fstype, path, +					      orig_sclass, sid); +		rcu_read_unlock(); +	} while (retval == -ESTALE);  	return retval;  } @@ -2873,7 +2953,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  	struct selinux_policy *policy;  	struct policydb *policydb;  	struct sidtab *sidtab; -	int rc = 0; +	int rc;  	struct ocontext *c;  	struct superblock_security_struct *sbsec = sb->s_security;  	const char *fstype = sb->s_type->name; @@ -2884,6 +2964,8 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2901,6 +2983,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2908,6 +2994,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  	} else {  		rc = __security_genfs_sid(policy, fstype, "/",  					SECCLASS_DIR, &sbsec->sid); +		if (rc == -ESTALE) { +			rcu_read_unlock(); +			goto retry; +		}  		if (rc) {  			sbsec->behavior = SECURITY_FS_USE_NONE;  			rc = 0; @@ -3117,12 +3207,13 @@ int security_sid_mls_copy(struct selinux_state *state,  	u32 len;  	int rc; -	rc = 0;  	if (!selinux_initialized(state)) {  		*new_sid = sid; -		goto out; +		return 0;  	} +retry: +	rc = 0;  	context_init(&newcon);  	rcu_read_lock(); @@ -3181,10 +3272,14 @@ int security_sid_mls_copy(struct selinux_state *state,  		}  	}  	rc = sidtab_context_to_sid(sidtab, &newcon, new_sid); +	if (rc == -ESTALE) { +		rcu_read_unlock(); +		context_destroy(&newcon); +		goto retry; +	}  out_unlock:  	rcu_read_unlock();  	context_destroy(&newcon); -out:  	return rc;  } @@ -3777,6 +3872,8 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -3803,23 +3900,24 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,  				goto out;  		}  		rc = -EIDRM; -		if (!mls_context_isvalid(policydb, &ctx_new)) -			goto out_free; +		if (!mls_context_isvalid(policydb, &ctx_new)) { +			ebitmap_destroy(&ctx_new.range.level[0].cat); +			goto out; +		}  		rc = sidtab_context_to_sid(sidtab, &ctx_new, sid); +		ebitmap_destroy(&ctx_new.range.level[0].cat); +		if (rc == -ESTALE) { +			rcu_read_unlock(); +			goto retry; +		}  		if (rc) -			goto out_free; +			goto out;  		security_netlbl_cache_add(secattr, *sid); - -		ebitmap_destroy(&ctx_new.range.level[0].cat);  	} else  		*sid = SECSID_NULL; -	rcu_read_unlock(); -	return 0; -out_free: -	ebitmap_destroy(&ctx_new.range.level[0].cat);  out:  	rcu_read_unlock();  	return rc; |