Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem fixes from James Morris:
 "Two fixes for the security subsystem:

   - keys: split both rcu_dereference_key() and user_key_payload() into
     versions which can be called with or without holding the key
     semaphore.

   - SELinux: fix Android init(8) breakage due to new cgroup security
     labeling support when using older policy"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  selinux: wrap cgroup seclabel support with its own policy capability
  KEYS: Differentiate uses of rcu_dereference_key() and user_key_payload()
This commit is contained in:
Linus Torvalds 2017-03-02 13:22:18 -08:00
commit 0f221a3102
19 changed files with 55 additions and 26 deletions

View File

@ -1151,8 +1151,21 @@ access the data:
usage. This is called key->payload.rcu_data0. The following accessors
wrap the RCU calls to this element:
(a) Set or change the first payload pointer:
rcu_assign_keypointer(struct key *key, void *data);
void *rcu_dereference_key(struct key *key);
(b) Read the first payload pointer with the key semaphore held:
[const] void *dereference_key_locked([const] struct key *key);
Note that the return value will inherit its constness from the key
parameter. Static analysis will give an error if it things the lock
isn't held.
(c) Read the first payload pointer with the RCU read lock held:
const void *dereference_key_rcu(const struct key *key);
===================

View File

@ -1536,7 +1536,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string
down_read(&key->sem);
ukp = user_key_payload(key);
ukp = user_key_payload_locked(key);
if (!ukp) {
up_read(&key->sem);
key_put(key);

View File

@ -2455,7 +2455,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
}
down_read(&key->sem);
upayload = user_key_payload(key);
upayload = user_key_payload_locked(key);
if (IS_ERR_OR_NULL(upayload)) {
rc = upayload ? PTR_ERR(upayload) : -EINVAL;
goto out_key_put;

View File

@ -103,7 +103,7 @@ static int validate_user_key(struct fscrypt_info *crypt_info,
goto out;
}
down_read(&keyring_key->sem);
ukp = user_key_payload(keyring_key);
ukp = user_key_payload_locked(keyring_key);
if (ukp->datalen != sizeof(struct fscrypt_key)) {
res = -EINVAL;
up_read(&keyring_key->sem);

View File

@ -117,7 +117,7 @@ ecryptfs_get_key_payload_data(struct key *key)
auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
if (!auth_tok)
return (struct ecryptfs_auth_tok *)user_key_payload(key)->data;
return (struct ecryptfs_auth_tok *)user_key_payload_locked(key)->data;
else
return auth_tok;
}

View File

@ -329,7 +329,7 @@ static void fscache_objlist_config(struct fscache_objlist_data *data)
config = 0;
rcu_read_lock();
confkey = user_key_payload(key);
confkey = user_key_payload_rcu(key);
buf = confkey->data;
for (len = confkey->datalen - 1; len >= 0; len--) {

View File

@ -316,7 +316,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
if (ret < 0)
goto out_up;
payload = user_key_payload(rkey);
payload = user_key_payload_rcu(rkey);
if (IS_ERR_OR_NULL(payload)) {
ret = PTR_ERR(payload);
goto out_up;

View File

@ -48,9 +48,14 @@ extern void user_describe(const struct key *user, struct seq_file *m);
extern long user_read(const struct key *key,
char __user *buffer, size_t buflen);
static inline const struct user_key_payload *user_key_payload(const struct key *key)
static inline const struct user_key_payload *user_key_payload_rcu(const struct key *key)
{
return (struct user_key_payload *)rcu_dereference_key(key);
return (struct user_key_payload *)dereference_key_rcu(key);
}
static inline struct user_key_payload *user_key_payload_locked(const struct key *key)
{
return (struct user_key_payload *)dereference_key_locked((struct key *)key);
}
#endif /* CONFIG_KEYS */

View File

@ -354,7 +354,10 @@ static inline bool key_is_instantiated(const struct key *key)
!test_bit(KEY_FLAG_NEGATIVE, &key->flags);
}
#define rcu_dereference_key(KEY) \
#define dereference_key_rcu(KEY) \
(rcu_dereference((KEY)->payload.rcu_data0))
#define dereference_key_locked(KEY) \
(rcu_dereference_protected((KEY)->payload.rcu_data0, \
rwsem_is_locked(&((struct key *)(KEY))->sem)))

View File

@ -85,7 +85,7 @@ static int digsig_verify_rsa(struct key *key,
struct pubkey_hdr *pkh;
down_read(&key->sem);
ukp = user_key_payload(key);
ukp = user_key_payload_locked(key);
if (ukp->datalen < sizeof(*pkh))
goto err1;

View File

@ -70,7 +70,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
const char *options, char **_result, time64_t *_expiry)
{
struct key *rkey;
const struct user_key_payload *upayload;
struct user_key_payload *upayload;
const struct cred *saved_cred;
size_t typelen, desclen;
char *desc, *cp;
@ -141,7 +141,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
if (ret)
goto put;
upayload = user_key_payload(rkey);
upayload = user_key_payload_locked(rkey);
len = upayload->datalen;
ret = -ENOMEM;

View File

@ -55,7 +55,7 @@ static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
if (status == 0) {
const struct user_key_payload *payload;
payload = user_key_payload(key);
payload = user_key_payload_locked(key);
if (maxlen == 0) {
*mpi = NULL;

View File

@ -314,7 +314,7 @@ static struct key *request_user_key(const char *master_desc, const u8 **master_k
goto error;
down_read(&ukey->sem);
upayload = user_key_payload(ukey);
upayload = user_key_payload_locked(ukey);
*master_key = upayload->data;
*master_keylen = upayload->datalen;
error:
@ -926,7 +926,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
size_t asciiblob_len;
int ret;
epayload = rcu_dereference_key(key);
epayload = dereference_key_locked(key);
/* returns the hex encoded iv, encrypted-data, and hmac as ascii */
asciiblob_len = epayload->datablob_len + ivsize + 1

View File

@ -1140,12 +1140,12 @@ out:
static long trusted_read(const struct key *key, char __user *buffer,
size_t buflen)
{
struct trusted_key_payload *p;
const struct trusted_key_payload *p;
char *ascii_buf;
char *bufp;
int i;
p = rcu_dereference_key(key);
p = dereference_key_locked(key);
if (!p)
return -EINVAL;
if (!buffer || buflen <= 0)

View File

@ -107,7 +107,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
/* attach the new data, displacing the old */
key->expiry = prep->expiry;
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
zap = rcu_dereference_key(key);
zap = dereference_key_locked(key);
rcu_assign_keypointer(key, prep->payload.data[0]);
prep->payload.data[0] = NULL;
@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(user_update);
*/
void user_revoke(struct key *key)
{
struct user_key_payload *upayload = key->payload.data[0];
struct user_key_payload *upayload = user_key_payload_locked(key);
/* clear the quota */
key_payload_reserve(key, 0);
@ -169,7 +169,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
const struct user_key_payload *upayload;
long ret;
upayload = user_key_payload(key);
upayload = user_key_payload_locked(key);
ret = upayload->datalen;
/* we can return the data as is */

View File

@ -480,12 +480,13 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
sbsec->behavior == SECURITY_FS_USE_NATIVE ||
/* Special handling. Genfs but also in-core setxattr handler */
!strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "cgroup") ||
!strcmp(sb->s_type->name, "cgroup2") ||
!strcmp(sb->s_type->name, "pstore") ||
!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "rootfs");
!strcmp(sb->s_type->name, "rootfs") ||
(selinux_policycap_cgroupseclabel &&
(!strcmp(sb->s_type->name, "cgroup") ||
!strcmp(sb->s_type->name, "cgroup2")));
}
static int sb_finish_set_opts(struct super_block *sb)

View File

@ -71,6 +71,7 @@ enum {
POLICYDB_CAPABILITY_OPENPERM,
POLICYDB_CAPABILITY_EXTSOCKCLASS,
POLICYDB_CAPABILITY_ALWAYSNETWORK,
POLICYDB_CAPABILITY_CGROUPSECLABEL,
__POLICYDB_CAPABILITY_MAX
};
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
@ -79,6 +80,7 @@ extern int selinux_policycap_netpeer;
extern int selinux_policycap_openperm;
extern int selinux_policycap_extsockclass;
extern int selinux_policycap_alwaysnetwork;
extern int selinux_policycap_cgroupseclabel;
/*
* type_datum properties

View File

@ -46,7 +46,8 @@ static char *policycap_names[] = {
"network_peer_controls",
"open_perms",
"extended_socket_class",
"always_check_network"
"always_check_network",
"cgroup_seclabel"
};
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;

View File

@ -74,6 +74,7 @@ int selinux_policycap_netpeer;
int selinux_policycap_openperm;
int selinux_policycap_extsockclass;
int selinux_policycap_alwaysnetwork;
int selinux_policycap_cgroupseclabel;
static DEFINE_RWLOCK(policy_rwlock);
@ -1993,6 +1994,9 @@ static void security_load_policycaps(void)
POLICYDB_CAPABILITY_EXTSOCKCLASS);
selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_ALWAYSNETWORK);
selinux_policycap_cgroupseclabel =
ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_CGROUPSECLABEL);
}
static int security_preserve_bools(struct policydb *p);