diff options
Diffstat (limited to 'security')
| -rw-r--r-- | security/commoncap.c | 25 | ||||
| -rw-r--r-- | security/device_cgroup.c | 20 | ||||
| -rw-r--r-- | security/keys/key.c | 6 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 15 | ||||
| -rw-r--r-- | security/keys/keyring.c | 10 | ||||
| -rw-r--r-- | security/keys/process_keys.c | 94 | ||||
| -rw-r--r-- | security/keys/request_key.c | 21 | ||||
| -rw-r--r-- | security/selinux/nlmsgtab.c | 3 | ||||
| -rw-r--r-- | security/smack/Kconfig | 6 | ||||
| -rw-r--r-- | security/smack/smackfs.c | 17 | ||||
| -rw-r--r-- | security/yama/yama_lsm.c | 100 | 
11 files changed, 198 insertions, 119 deletions
| diff --git a/security/commoncap.c b/security/commoncap.c index 6dbae4650abe..7ee08c756d6b 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -76,24 +76,33 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb)  int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,  		int cap, int audit)  { -	for (;;) { -		/* The owner of the user namespace has all caps. */ -		if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, cred->euid)) -			return 0; +	struct user_namespace *ns = targ_ns; +	/* See if cred has the capability in the target user namespace +	 * by examining the target user namespace and all of the target +	 * user namespace's parents. +	 */ +	for (;;) {  		/* Do we have the necessary capabilities? */ -		if (targ_ns == cred->user_ns) +		if (ns == cred->user_ns)  			return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;  		/* Have we tried all of the parent namespaces? */ -		if (targ_ns == &init_user_ns) +		if (ns == &init_user_ns)  			return -EPERM; +		/*  +		 * The owner of the user namespace in the parent of the +		 * user namespace has all caps. +		 */ +		if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid)) +			return 0; +  		/* -		 *If you have a capability in a parent user ns, then you have +		 * If you have a capability in a parent user ns, then you have  		 * it over all children user namespaces as well.  		 */ -		targ_ns = targ_ns->parent; +		ns = ns->parent;  	}  	/* We never get here */ diff --git a/security/device_cgroup.c b/security/device_cgroup.c index b08d20c66c2e..19ecc8de9e6b 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -82,6 +82,8 @@ static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)  {  	struct dev_exception_item *ex, *tmp, *new; +	lockdep_assert_held(&devcgroup_mutex); +  	list_for_each_entry(ex, orig, list) {  		new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);  		if (!new) @@ -107,6 +109,8 @@ static int dev_exception_add(struct dev_cgroup *dev_cgroup,  {  	struct dev_exception_item *excopy, *walk; +	lockdep_assert_held(&devcgroup_mutex); +  	excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);  	if (!excopy)  		return -ENOMEM; @@ -137,6 +141,8 @@ static void dev_exception_rm(struct dev_cgroup *dev_cgroup,  {  	struct dev_exception_item *walk, *tmp; +	lockdep_assert_held(&devcgroup_mutex); +  	list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {  		if (walk->type != ex->type)  			continue; @@ -163,6 +169,8 @@ static void dev_exception_clean(struct dev_cgroup *dev_cgroup)  {  	struct dev_exception_item *ex, *tmp; +	lockdep_assert_held(&devcgroup_mutex); +  	list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {  		list_del_rcu(&ex->list);  		kfree_rcu(ex, rcu); @@ -172,7 +180,7 @@ static void dev_exception_clean(struct dev_cgroup *dev_cgroup)  /*   * called from kernel/cgroup.c with cgroup_lock() held.   */ -static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup) +static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup)  {  	struct dev_cgroup *dev_cgroup, *parent_dev_cgroup;  	struct cgroup *parent_cgroup; @@ -202,7 +210,7 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup)  	return &dev_cgroup->css;  } -static void devcgroup_destroy(struct cgroup *cgroup) +static void devcgroup_css_free(struct cgroup *cgroup)  {  	struct dev_cgroup *dev_cgroup; @@ -298,6 +306,10 @@ static int may_access(struct dev_cgroup *dev_cgroup,  	struct dev_exception_item *ex;  	bool match = false; +	rcu_lockdep_assert(rcu_read_lock_held() || +			   lockdep_is_held(&devcgroup_mutex), +			   "device_cgroup::may_access() called without proper synchronization"); +  	list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {  		if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))  			continue; @@ -552,8 +564,8 @@ static struct cftype dev_cgroup_files[] = {  struct cgroup_subsys devices_subsys = {  	.name = "devices",  	.can_attach = devcgroup_can_attach, -	.create = devcgroup_create, -	.destroy = devcgroup_destroy, +	.css_alloc = devcgroup_css_alloc, +	.css_free = devcgroup_css_free,  	.subsys_id = devices_subsys_id,  	.base_cftypes = dev_cgroup_files, diff --git a/security/keys/key.c b/security/keys/key.c index a15c9da8f971..8fb7c7bd4657 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -854,13 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	/* if the client doesn't provide, decide on the permissions we want */  	if (perm == KEY_PERM_UNDEF) {  		perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR; -		perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK | KEY_USR_SETATTR; +		perm |= KEY_USR_VIEW;  		if (ktype->read) -			perm |= KEY_POS_READ | KEY_USR_READ; +			perm |= KEY_POS_READ;  		if (ktype == &key_type_keyring || ktype->update) -			perm |= KEY_USR_WRITE; +			perm |= KEY_POS_WRITE;  	}  	/* allocate a new key */ diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 5d34b4e827d6..4b5c948eb414 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1132,12 +1132,12 @@ long keyctl_instantiate_key_iov(key_serial_t id,  	ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc,  				    ARRAY_SIZE(iovstack), iovstack, &iov);  	if (ret < 0) -		return ret; +		goto err;  	if (ret == 0)  		goto no_payload_free;  	ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid); - +err:  	if (iov != iovstack)  		kfree(iov);  	return ret; @@ -1495,7 +1495,8 @@ long keyctl_session_to_parent(void)  		goto error_keyring;  	newwork = &cred->rcu; -	cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); +	cred->session_keyring = key_ref_to_ptr(keyring_r); +	keyring_r = NULL;  	init_task_work(newwork, key_change_session_keyring);  	me = current; @@ -1519,7 +1520,7 @@ long keyctl_session_to_parent(void)  	mycred = current_cred();  	pcred = __task_cred(parent);  	if (mycred == pcred || -	    mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) { +	    mycred->session_keyring == pcred->session_keyring) {  		ret = 0;  		goto unlock;  	} @@ -1535,9 +1536,9 @@ long keyctl_session_to_parent(void)  		goto unlock;  	/* the keyrings must have the same UID */ -	if ((pcred->tgcred->session_keyring && -	     !uid_eq(pcred->tgcred->session_keyring->uid, mycred->euid)) || -	    !uid_eq(mycred->tgcred->session_keyring->uid, mycred->euid)) +	if ((pcred->session_keyring && +	     !uid_eq(pcred->session_keyring->uid, mycred->euid)) || +	    !uid_eq(mycred->session_keyring->uid, mycred->euid))  		goto unlock;  	/* cancel an already pending keyring replacement */ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 6e42df15a24c..6ece7f2e5707 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -257,17 +257,14 @@ error:   * Allocate a keyring and link into the destination keyring.   */  struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, -			  const struct cred *cred, unsigned long flags, -			  struct key *dest) +			  const struct cred *cred, key_perm_t perm, +			  unsigned long flags, struct key *dest)  {  	struct key *keyring;  	int ret;  	keyring = key_alloc(&key_type_keyring, description, -			    uid, gid, cred, -			    (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, -			    flags); - +			    uid, gid, cred, perm, flags);  	if (!IS_ERR(keyring)) {  		ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);  		if (ret < 0) { @@ -278,6 +275,7 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,  	return keyring;  } +EXPORT_SYMBOL(keyring_alloc);  /**   * keyring_search_aux - Search a keyring tree for a key matching some criteria diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index a58f712605d8..58dfe0890947 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -45,10 +45,12 @@ int install_user_keyrings(void)  	struct user_struct *user;  	const struct cred *cred;  	struct key *uid_keyring, *session_keyring; +	key_perm_t user_keyring_perm;  	char buf[20];  	int ret;  	uid_t uid; +	user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;  	cred = current_cred();  	user = cred->user;  	uid = from_kuid(cred->user_ns, user->uid); @@ -73,8 +75,8 @@ int install_user_keyrings(void)  		uid_keyring = find_keyring_by_name(buf, true);  		if (IS_ERR(uid_keyring)) {  			uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, -						    cred, KEY_ALLOC_IN_QUOTA, -						    NULL); +						    cred, user_keyring_perm, +						    KEY_ALLOC_IN_QUOTA, NULL);  			if (IS_ERR(uid_keyring)) {  				ret = PTR_ERR(uid_keyring);  				goto error; @@ -89,7 +91,8 @@ int install_user_keyrings(void)  		if (IS_ERR(session_keyring)) {  			session_keyring =  				keyring_alloc(buf, user->uid, INVALID_GID, -					      cred, KEY_ALLOC_IN_QUOTA, NULL); +					      cred, user_keyring_perm, +					      KEY_ALLOC_IN_QUOTA, NULL);  			if (IS_ERR(session_keyring)) {  				ret = PTR_ERR(session_keyring);  				goto error_release; @@ -130,6 +133,7 @@ int install_thread_keyring_to_cred(struct cred *new)  	struct key *keyring;  	keyring = keyring_alloc("_tid", new->uid, new->gid, new, +				KEY_POS_ALL | KEY_USR_VIEW,  				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	if (IS_ERR(keyring))  		return PTR_ERR(keyring); @@ -170,27 +174,18 @@ static int install_thread_keyring(void)  int install_process_keyring_to_cred(struct cred *new)  {  	struct key *keyring; -	int ret; -	if (new->tgcred->process_keyring) +	if (new->process_keyring)  		return -EEXIST; -	keyring = keyring_alloc("_pid", new->uid, new->gid, -				new, KEY_ALLOC_QUOTA_OVERRUN, NULL); +	keyring = keyring_alloc("_pid", new->uid, new->gid, new, +				KEY_POS_ALL | KEY_USR_VIEW, +				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	if (IS_ERR(keyring))  		return PTR_ERR(keyring); -	spin_lock_irq(&new->tgcred->lock); -	if (!new->tgcred->process_keyring) { -		new->tgcred->process_keyring = keyring; -		keyring = NULL; -		ret = 0; -	} else { -		ret = -EEXIST; -	} -	spin_unlock_irq(&new->tgcred->lock); -	key_put(keyring); -	return ret; +	new->process_keyring = keyring; +	return 0;  }  /* @@ -231,11 +226,12 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  	/* create an empty session keyring */  	if (!keyring) {  		flags = KEY_ALLOC_QUOTA_OVERRUN; -		if (cred->tgcred->session_keyring) +		if (cred->session_keyring)  			flags = KEY_ALLOC_IN_QUOTA; -		keyring = keyring_alloc("_ses", cred->uid, cred->gid, -					cred, flags, NULL); +		keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, +					KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, +					flags, NULL);  		if (IS_ERR(keyring))  			return PTR_ERR(keyring);  	} else { @@ -243,17 +239,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  	}  	/* install the keyring */ -	spin_lock_irq(&cred->tgcred->lock); -	old = cred->tgcred->session_keyring; -	rcu_assign_pointer(cred->tgcred->session_keyring, keyring); -	spin_unlock_irq(&cred->tgcred->lock); - -	/* we're using RCU on the pointer, but there's no point synchronising -	 * on it if it didn't previously point to anything */ -	if (old) { -		synchronize_rcu(); +	old = cred->session_keyring; +	rcu_assign_pointer(cred->session_keyring, keyring); + +	if (old)  		key_put(old); -	}  	return 0;  } @@ -358,8 +348,6 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  		switch (PTR_ERR(key_ref)) {  		case -EAGAIN: /* no key */ -			if (ret) -				break;  		case -ENOKEY: /* negative key */  			ret = key_ref;  			break; @@ -370,9 +358,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the process keyring second */ -	if (cred->tgcred->process_keyring) { +	if (cred->process_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->tgcred->process_keyring, 1), +			make_key_ref(cred->process_keyring, 1),  			cred, type, description, match, no_state_check);  		if (!IS_ERR(key_ref))  			goto found; @@ -391,12 +379,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the session keyring */ -	if (cred->tgcred->session_keyring) { +	if (cred->session_keyring) {  		rcu_read_lock();  		key_ref = keyring_search_aux( -			make_key_ref(rcu_dereference( -					     cred->tgcred->session_keyring), -				     1), +			make_key_ref(rcu_dereference(cred->session_keyring), 1),  			cred, type, description, match, no_state_check);  		rcu_read_unlock(); @@ -566,7 +552,7 @@ try_again:  		break;  	case KEY_SPEC_PROCESS_KEYRING: -		if (!cred->tgcred->process_keyring) { +		if (!cred->process_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -578,13 +564,13 @@ try_again:  			goto reget_creds;  		} -		key = cred->tgcred->process_keyring; +		key = cred->process_keyring;  		atomic_inc(&key->usage);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_SESSION_KEYRING: -		if (!cred->tgcred->session_keyring) { +		if (!cred->session_keyring) {  			/* always install a session keyring upon access if one  			 * doesn't exist yet */  			ret = install_user_keyrings(); @@ -599,7 +585,7 @@ try_again:  			if (ret < 0)  				goto error;  			goto reget_creds; -		} else if (cred->tgcred->session_keyring == +		} else if (cred->session_keyring ==  			   cred->user->session_keyring &&  			   lflags & KEY_LOOKUP_CREATE) {  			ret = join_session_keyring(NULL); @@ -609,7 +595,7 @@ try_again:  		}  		rcu_read_lock(); -		key = rcu_dereference(cred->tgcred->session_keyring); +		key = rcu_dereference(cred->session_keyring);  		atomic_inc(&key->usage);  		rcu_read_unlock();  		key_ref = make_key_ref(key, 1); @@ -769,12 +755,6 @@ long join_session_keyring(const char *name)  	struct key *keyring;  	long ret, serial; -	/* only permit this if there's a single thread in the thread group - -	 * this avoids us having to adjust the creds on all threads and risking -	 * ENOMEM */ -	if (!current_is_single_threaded()) -		return -EMLINK; -  	new = prepare_creds();  	if (!new)  		return -ENOMEM; @@ -786,7 +766,7 @@ long join_session_keyring(const char *name)  		if (ret < 0)  			goto error; -		serial = new->tgcred->session_keyring->serial; +		serial = new->session_keyring->serial;  		ret = commit_creds(new);  		if (ret == 0)  			ret = serial; @@ -800,8 +780,10 @@ long join_session_keyring(const char *name)  	keyring = find_keyring_by_name(name, false);  	if (PTR_ERR(keyring) == -ENOKEY) {  		/* not found - try and create a new one */ -		keyring = keyring_alloc(name, old->uid, old->gid, old, -					KEY_ALLOC_IN_QUOTA, NULL); +		keyring = keyring_alloc( +			name, old->uid, old->gid, old, +			KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, +			KEY_ALLOC_IN_QUOTA, NULL);  		if (IS_ERR(keyring)) {  			ret = PTR_ERR(keyring);  			goto error2; @@ -809,6 +791,9 @@ long join_session_keyring(const char *name)  	} else if (IS_ERR(keyring)) {  		ret = PTR_ERR(keyring);  		goto error2; +	} else if (keyring == new->session_keyring) { +		ret = 0; +		goto error2;  	}  	/* we've got a keyring - now to install it */ @@ -865,8 +850,7 @@ void key_change_session_keyring(struct callback_head *twork)  	new->jit_keyring	= old->jit_keyring;  	new->thread_keyring	= key_get(old->thread_keyring); -	new->tgcred->tgid	= old->tgcred->tgid; -	new->tgcred->process_keyring = key_get(old->tgcred->process_keyring); +	new->process_keyring	= key_get(old->process_keyring);  	security_transfer_creds(new, old); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 66e21184b559..4bd6bdb74193 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -126,6 +126,7 @@ static int call_sbin_request_key(struct key_construction *cons,  	cred = get_current_cred();  	keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, +				KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,  				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	put_cred(cred);  	if (IS_ERR(keyring)) { @@ -150,12 +151,12 @@ static int call_sbin_request_key(struct key_construction *cons,  		cred->thread_keyring ? cred->thread_keyring->serial : 0);  	prkey = 0; -	if (cred->tgcred->process_keyring) -		prkey = cred->tgcred->process_keyring->serial; +	if (cred->process_keyring) +		prkey = cred->process_keyring->serial;  	sprintf(keyring_str[1], "%d", prkey);  	rcu_read_lock(); -	session = rcu_dereference(cred->tgcred->session_keyring); +	session = rcu_dereference(cred->session_keyring);  	if (!session)  		session = cred->user->session_keyring;  	sskey = session->serial; @@ -297,14 +298,14 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  				break;  		case KEY_REQKEY_DEFL_PROCESS_KEYRING: -			dest_keyring = key_get(cred->tgcred->process_keyring); +			dest_keyring = key_get(cred->process_keyring);  			if (dest_keyring)  				break;  		case KEY_REQKEY_DEFL_SESSION_KEYRING:  			rcu_read_lock();  			dest_keyring = key_get( -				rcu_dereference(cred->tgcred->session_keyring)); +				rcu_dereference(cred->session_keyring));  			rcu_read_unlock();  			if (dest_keyring) @@ -347,6 +348,7 @@ static int construct_alloc_key(struct key_type *type,  	const struct cred *cred = current_cred();  	unsigned long prealloc;  	struct key *key; +	key_perm_t perm;  	key_ref_t key_ref;  	int ret; @@ -355,8 +357,15 @@ static int construct_alloc_key(struct key_type *type,  	*_key = NULL;  	mutex_lock(&user->cons_lock); +	perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR; +	perm |= KEY_USR_VIEW; +	if (type->read) +		perm |= KEY_POS_READ; +	if (type == &key_type_keyring || type->update) +		perm |= KEY_POS_WRITE; +  	key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred, -			KEY_POS_ALL, flags); +			perm, flags);  	if (IS_ERR(key))  		goto alloc_failed; diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index d309e7f472d8..370a6468b3ba 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -67,6 +67,9 @@ static struct nlmsg_perm nlmsg_route_perms[] =  	{ RTM_GETADDRLABEL,	NETLINK_ROUTE_SOCKET__NLMSG_READ  },  	{ RTM_GETDCB,		NETLINK_ROUTE_SOCKET__NLMSG_READ  },  	{ RTM_SETDCB,		NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, +	{ RTM_NEWNETCONF,	NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, +	{ RTM_GETNETCONF,	NETLINK_ROUTE_SOCKET__NLMSG_READ  }, +	{ RTM_GETMDB,		NETLINK_ROUTE_SOCKET__NLMSG_READ  },  };  static struct nlmsg_perm nlmsg_tcpdiag_perms[] = diff --git a/security/smack/Kconfig b/security/smack/Kconfig index 603b08784341..e69de9c642b7 100644 --- a/security/smack/Kconfig +++ b/security/smack/Kconfig @@ -1,6 +1,10 @@  config SECURITY_SMACK  	bool "Simplified Mandatory Access Control Kernel Support" -	depends on NETLABEL && SECURITY_NETWORK +	depends on NET +	depends on INET +	depends on SECURITY +	select NETLABEL +	select SECURITY_NETWORK  	default n  	help  	  This selects the Simplified Mandatory Access Control Kernel. diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 99929a50093a..76a5dca46404 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -2063,6 +2063,19 @@ static const struct file_operations smk_revoke_subj_ops = {  	.llseek		= generic_file_llseek,  }; +static struct kset *smackfs_kset; +/** + * smk_init_sysfs - initialize /sys/fs/smackfs + * + */ +static int smk_init_sysfs(void) +{ +	smackfs_kset = kset_create_and_add("smackfs", NULL, fs_kobj); +	if (!smackfs_kset) +		return -ENOMEM; +	return 0; +} +  /**   * smk_fill_super - fill the /smackfs superblock   * @sb: the empty superblock @@ -2183,6 +2196,10 @@ static int __init init_smk_fs(void)  	if (!security_module_enable(&smack_ops))  		return 0; +	err = smk_init_sysfs(); +	if (err) +		printk(KERN_ERR "smackfs: sysfs mountpoint problem.\n"); +  	err = register_filesystem(&smk_fs_type);  	if (!err) {  		smackfs_mount = kern_mount(&smk_fs_type); diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index b4c29848b49d..23414b93771f 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -17,6 +17,7 @@  #include <linux/ptrace.h>  #include <linux/prctl.h>  #include <linux/ratelimit.h> +#include <linux/workqueue.h>  #define YAMA_SCOPE_DISABLED	0  #define YAMA_SCOPE_RELATIONAL	1 @@ -29,12 +30,37 @@ static int ptrace_scope = YAMA_SCOPE_RELATIONAL;  struct ptrace_relation {  	struct task_struct *tracer;  	struct task_struct *tracee; +	bool invalid;  	struct list_head node; +	struct rcu_head rcu;  };  static LIST_HEAD(ptracer_relations);  static DEFINE_SPINLOCK(ptracer_relations_lock); +static void yama_relation_cleanup(struct work_struct *work); +static DECLARE_WORK(yama_relation_work, yama_relation_cleanup); + +/** + * yama_relation_cleanup - remove invalid entries from the relation list + * + */ +static void yama_relation_cleanup(struct work_struct *work) +{ +	struct ptrace_relation *relation; + +	spin_lock(&ptracer_relations_lock); +	rcu_read_lock(); +	list_for_each_entry_rcu(relation, &ptracer_relations, node) { +		if (relation->invalid) { +			list_del_rcu(&relation->node); +			kfree_rcu(relation, rcu); +		} +	} +	rcu_read_unlock(); +	spin_unlock(&ptracer_relations_lock); +} +  /**   * yama_ptracer_add - add/replace an exception for this tracer/tracee pair   * @tracer: the task_struct of the process doing the ptrace @@ -48,32 +74,34 @@ static DEFINE_SPINLOCK(ptracer_relations_lock);  static int yama_ptracer_add(struct task_struct *tracer,  			    struct task_struct *tracee)  { -	int rc = 0; -	struct ptrace_relation *added; -	struct ptrace_relation *entry, *relation = NULL; +	struct ptrace_relation *relation, *added;  	added = kmalloc(sizeof(*added), GFP_KERNEL);  	if (!added)  		return -ENOMEM; -	spin_lock_bh(&ptracer_relations_lock); -	list_for_each_entry(entry, &ptracer_relations, node) -		if (entry->tracee == tracee) { -			relation = entry; -			break; +	added->tracee = tracee; +	added->tracer = tracer; +	added->invalid = false; + +	spin_lock(&ptracer_relations_lock); +	rcu_read_lock(); +	list_for_each_entry_rcu(relation, &ptracer_relations, node) { +		if (relation->invalid) +			continue; +		if (relation->tracee == tracee) { +			list_replace_rcu(&relation->node, &added->node); +			kfree_rcu(relation, rcu); +			goto out;  		} -	if (!relation) { -		relation = added; -		relation->tracee = tracee; -		list_add(&relation->node, &ptracer_relations);  	} -	relation->tracer = tracer; -	spin_unlock_bh(&ptracer_relations_lock); -	if (added != relation) -		kfree(added); +	list_add_rcu(&added->node, &ptracer_relations); -	return rc; +out: +	rcu_read_unlock(); +	spin_unlock(&ptracer_relations_lock); +	return 0;  }  /** @@ -84,16 +112,23 @@ static int yama_ptracer_add(struct task_struct *tracer,  static void yama_ptracer_del(struct task_struct *tracer,  			     struct task_struct *tracee)  { -	struct ptrace_relation *relation, *safe; +	struct ptrace_relation *relation; +	bool marked = false; -	spin_lock_bh(&ptracer_relations_lock); -	list_for_each_entry_safe(relation, safe, &ptracer_relations, node) +	rcu_read_lock(); +	list_for_each_entry_rcu(relation, &ptracer_relations, node) { +		if (relation->invalid) +			continue;  		if (relation->tracee == tracee ||  		    (tracer && relation->tracer == tracer)) { -			list_del(&relation->node); -			kfree(relation); +			relation->invalid = true; +			marked = true;  		} -	spin_unlock_bh(&ptracer_relations_lock); +	} +	rcu_read_unlock(); + +	if (marked) +		schedule_work(&yama_relation_work);  }  /** @@ -217,21 +252,22 @@ static int ptracer_exception_found(struct task_struct *tracer,  	struct task_struct *parent = NULL;  	bool found = false; -	spin_lock_bh(&ptracer_relations_lock);  	rcu_read_lock();  	if (!thread_group_leader(tracee))  		tracee = rcu_dereference(tracee->group_leader); -	list_for_each_entry(relation, &ptracer_relations, node) +	list_for_each_entry_rcu(relation, &ptracer_relations, node) { +		if (relation->invalid) +			continue;  		if (relation->tracee == tracee) {  			parent = relation->tracer;  			found = true;  			break;  		} +	}  	if (found && (parent == NULL || task_is_descendant(parent, tracer)))  		rc = 1;  	rcu_read_unlock(); -	spin_unlock_bh(&ptracer_relations_lock);  	return rc;  } @@ -262,14 +298,18 @@ int yama_ptrace_access_check(struct task_struct *child,  			/* No additional restrictions. */  			break;  		case YAMA_SCOPE_RELATIONAL: +			rcu_read_lock();  			if (!task_is_descendant(current, child) &&  			    !ptracer_exception_found(current, child) && -			    !ns_capable(task_user_ns(child), CAP_SYS_PTRACE)) +			    !ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE))  				rc = -EPERM; +			rcu_read_unlock();  			break;  		case YAMA_SCOPE_CAPABILITY: -			if (!ns_capable(task_user_ns(child), CAP_SYS_PTRACE)) +			rcu_read_lock(); +			if (!ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE))  				rc = -EPERM; +			rcu_read_unlock();  			break;  		case YAMA_SCOPE_NO_ATTACH:  		default: @@ -307,8 +347,10 @@ int yama_ptrace_traceme(struct task_struct *parent)  	/* Only disallow PTRACE_TRACEME on more aggressive settings. */  	switch (ptrace_scope) {  	case YAMA_SCOPE_CAPABILITY: -		if (!ns_capable(task_user_ns(parent), CAP_SYS_PTRACE)) +		rcu_read_lock(); +		if (!ns_capable(__task_cred(parent)->user_ns, CAP_SYS_PTRACE))  			rc = -EPERM; +		rcu_read_unlock();  		break;  	case YAMA_SCOPE_NO_ATTACH:  		rc = -EPERM; |