summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-08 19:56:57 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-08 19:56:57 -0700
commit0f75ef6a9cff49ff612f7ce0578bced9d0b38325 (patch)
treebdd2a6b7f35695b1d7ab1209efbb40187501fe7d /security
parentc84ca912b07901be528e5184fd254fca1dddf2ac (diff)
parent7a1ade847596dadc94b37e49f8c03f167fd71748 (diff)
Merge tag 'keys-acl-20190703' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
Pull keyring ACL support from David Howells: "This changes the permissions model used by keys and keyrings to be based on an internal ACL by the following means: - Replace the permissions mask internally with an ACL that contains a list of ACEs, each with a specific subject with a permissions mask. Potted default ACLs are available for new keys and keyrings. ACE subjects can be macroised to indicate the UID and GID specified on the key (which remain). Future commits will be able to add additional subject types, such as specific UIDs or domain tags/namespaces. Also split a number of permissions to give finer control. Examples include splitting the revocation permit from the change-attributes permit, thereby allowing someone to be granted permission to revoke a key without allowing them to change the owner; also the ability to join a keyring is split from the ability to link to it, thereby stopping a process accessing a keyring by joining it and thus acquiring use of possessor permits. - Provide a keyctl to allow the granting or denial of one or more permits to a specific subject. Direct access to the ACL is not granted, and the ACL cannot be viewed" * tag 'keys-acl-20190703' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: keys: Provide KEYCTL_GRANT_PERMISSION keys: Replace uid/gid/perm permissions checking with an ACL
Diffstat (limited to 'security')
-rw-r--r--security/integrity/digsig.c31
-rw-r--r--security/integrity/digsig_asymmetric.c2
-rw-r--r--security/integrity/evm/evm_crypto.c2
-rw-r--r--security/integrity/ima/ima_mok.c13
-rw-r--r--security/integrity/integrity.h6
-rw-r--r--security/integrity/platform_certs/platform_keyring.c14
-rw-r--r--security/keys/compat.c2
-rw-r--r--security/keys/encrypted-keys/encrypted.c2
-rw-r--r--security/keys/encrypted-keys/masterkey_trusted.c2
-rw-r--r--security/keys/gc.c2
-rw-r--r--security/keys/internal.h16
-rw-r--r--security/keys/key.c29
-rw-r--r--security/keys/keyctl.c104
-rw-r--r--security/keys/keyring.c27
-rw-r--r--security/keys/permission.c361
-rw-r--r--security/keys/persistent.c27
-rw-r--r--security/keys/proc.c22
-rw-r--r--security/keys/process_keys.c86
-rw-r--r--security/keys/request_key.c34
-rw-r--r--security/keys/request_key_auth.c15
-rw-r--r--security/selinux/hooks.c16
-rw-r--r--security/smack/smack_lsm.c3
22 files changed, 629 insertions, 187 deletions
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 4582bc26770a..ceb10553a6ba 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -47,7 +47,8 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
if (!keyring[id]) {
keyring[id] =
- request_key(&key_type_keyring, keyring_name[id], NULL);
+ request_key(&key_type_keyring, keyring_name[id],
+ NULL, NULL);
if (IS_ERR(keyring[id])) {
int err = PTR_ERR(keyring[id]);
pr_err("no %s keyring: %d\n", keyring_name[id], err);
@@ -69,14 +70,14 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
return -EOPNOTSUPP;
}
-static int __integrity_init_keyring(const unsigned int id, key_perm_t perm,
+static int __integrity_init_keyring(const unsigned int id, struct key_acl *acl,
struct key_restriction *restriction)
{
const struct cred *cred = current_cred();
int err = 0;
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
- KGIDT_INIT(0), cred, perm,
+ KGIDT_INIT(0), cred, acl,
KEY_ALLOC_NOT_IN_QUOTA, restriction, NULL);
if (IS_ERR(keyring[id])) {
err = PTR_ERR(keyring[id]);
@@ -94,10 +95,7 @@ static int __integrity_init_keyring(const unsigned int id, key_perm_t perm,
int __init integrity_init_keyring(const unsigned int id)
{
struct key_restriction *restriction;
- key_perm_t perm;
-
- perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW
- | KEY_USR_READ | KEY_USR_SEARCH;
+ struct key_acl *acl = &internal_keyring_acl;
if (id == INTEGRITY_KEYRING_PLATFORM) {
restriction = NULL;
@@ -112,14 +110,14 @@ int __init integrity_init_keyring(const unsigned int id)
return -ENOMEM;
restriction->check = restrict_link_to_ima;
- perm |= KEY_USR_WRITE;
+ acl = &internal_writable_keyring_acl;
out:
- return __integrity_init_keyring(id, perm, restriction);
+ return __integrity_init_keyring(id, acl, restriction);
}
-int __init integrity_add_key(const unsigned int id, const void *data,
- off_t size, key_perm_t perm)
+static int __init integrity_add_key(const unsigned int id, const void *data,
+ off_t size, struct key_acl *acl)
{
key_ref_t key;
int rc = 0;
@@ -128,7 +126,7 @@ int __init integrity_add_key(const unsigned int id, const void *data,
return -EINVAL;
key = key_create_or_update(make_key_ref(keyring[id], 1), "asymmetric",
- NULL, data, size, perm,
+ NULL, data, size, acl ?: &internal_key_acl,
KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key)) {
rc = PTR_ERR(key);
@@ -148,7 +146,6 @@ int __init integrity_load_x509(const unsigned int id, const char *path)
void *data;
loff_t size;
int rc;
- key_perm_t perm;
rc = kernel_read_file_from_path(path, &data, &size, 0,
READING_X509_CERTIFICATE);
@@ -157,21 +154,19 @@ int __init integrity_load_x509(const unsigned int id, const char *path)
return rc;
}
- perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ;
-
pr_info("Loading X.509 certificate: %s\n", path);
- rc = integrity_add_key(id, (const void *)data, size, perm);
+ rc = integrity_add_key(id, data, size, NULL);
vfree(data);
return rc;
}
int __init integrity_load_cert(const unsigned int id, const char *source,
- const void *data, size_t len, key_perm_t perm)
+ const void *data, size_t len, struct key_acl *acl)
{
if (!data)
return -EINVAL;
pr_info("Loading X.509 certificate: %s\n", source);
- return integrity_add_key(id, data, len, perm);
+ return integrity_add_key(id, data, len, acl);
}
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 55aec161d0e1..a29df775fdd8 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -53,7 +53,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
else
key = key_ref_to_ptr(kref);
} else {
- key = request_key(&key_type_asymmetric, name, NULL);
+ key = request_key(&key_type_asymmetric, name, NULL, NULL);
}
if (IS_ERR(key)) {
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index d485f6fc908e..466eebd3b4aa 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -356,7 +356,7 @@ int evm_init_key(void)
struct encrypted_key_payload *ekp;
int rc;
- evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
+ evm_key = request_key(&key_type_encrypted, EVMKEY, NULL, NULL);
if (IS_ERR(evm_key))
return -ENOENT;
diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c
index 36cadadbfba4..b52ae1476ec3 100644
--- a/security/integrity/ima/ima_mok.c
+++ b/security/integrity/ima/ima_mok.c
@@ -16,6 +16,15 @@
#include <keys/system_keyring.h>
+static struct key_acl integrity_blacklist_keyring_acl = {
+ .usage = REFCOUNT_INIT(1),
+ .nr_ace = 2,
+ .aces = {
+ KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+ KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_WRITE | KEY_ACE_SEARCH),
+ }
+};
+
struct key *ima_blacklist_keyring;
/*
@@ -35,9 +44,7 @@ __init int ima_mok_init(void)
ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
- (KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ |
- KEY_USR_WRITE | KEY_USR_SEARCH,
+ &integrity_blacklist_keyring_acl,
KEY_ALLOC_NOT_IN_QUOTA,
restriction, NULL);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 65377848fbc5..45f4aef83e29 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -12,6 +12,8 @@
#include <linux/key.h>
#include <linux/audit.h>
+struct key_acl;
+
/* iint action cache flags */
#define IMA_MEASURE 0x00000001
#define IMA_MEASURED 0x00000002
@@ -149,7 +151,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
int __init integrity_init_keyring(const unsigned int id);
int __init integrity_load_x509(const unsigned int id, const char *path);
int __init integrity_load_cert(const unsigned int id, const char *source,
- const void *data, size_t len, key_perm_t perm);
+ const void *data, size_t len, struct key_acl *acl);
#else
static inline int integrity_digsig_verify(const unsigned int id,
@@ -167,7 +169,7 @@ static inline int integrity_init_keyring(const unsigned int id)
static inline int __init integrity_load_cert(const unsigned int id,
const char *source,
const void *data, size_t len,
- key_perm_t perm)
+ struct key_acl *acl)
{
return 0;
}
diff --git a/security/integrity/platform_certs/platform_keyring.c b/security/integrity/platform_certs/platform_keyring.c
index bcafd7387729..7646e35f2d91 100644
--- a/security/integrity/platform_certs/platform_keyring.c
+++ b/security/integrity/platform_certs/platform_keyring.c
@@ -14,6 +14,15 @@
#include <linux/slab.h>
#include "../integrity.h"
+static struct key_acl platform_key_acl = {
+ .usage = REFCOUNT_INIT(1),
+ .nr_ace = 2,
+ .aces = {
+ KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_READ),
+ KEY_OWNER_ACE(KEY_ACE_VIEW),
+ }
+};
+
/**
* add_to_platform_keyring - Add to platform keyring without validation.
* @source: Source of key
@@ -26,13 +35,10 @@
void __init add_to_platform_keyring(const char *source, const void *data,
size_t len)
{
- key_perm_t perm;
int rc;
- perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW;
-
rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source, data, len,
- perm);
+ &platform_key_acl);
if (rc)
pr_info("Error adding keys to platform keyring %s\n", source);
}
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 9bcc404131aa..b0e59546e7bd 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -157,6 +157,8 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
case KEYCTL_MOVE:
return keyctl_keyring_move(arg2, arg3, arg4, arg5);
+ case KEYCTL_GRANT_PERMISSION:
+ return keyctl_grant_permission(arg2, arg3, arg4, arg5);
case KEYCTL_CAPABILITIES:
return keyctl_capabilities(compat_ptr(arg2), arg3);
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 60720f58cbe0..9df560e477c2 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -304,7 +304,7 @@ static struct key *request_user_key(const char *master_desc, const u8 **master_k
const struct user_key_payload *upayload;
struct key *ukey;
- ukey = request_key(&key_type_user, master_desc, NULL);
+ ukey = request_key(&key_type_user, master_desc, NULL, NULL);
if (IS_ERR(ukey))
goto error;
diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c
index c68528aa49c6..d649f2f29475 100644
--- a/security/keys/encrypted-keys/masterkey_trusted.c
+++ b/security/keys/encrypted-keys/masterkey_trusted.c
@@ -30,7 +30,7 @@ struct key *request_trusted_key(const char *trusted_desc,
struct trusted_key_payload *tpayload;
struct key *tkey;
- tkey = request_key(&key_type_trusted, trusted_desc, NULL);
+ tkey = request_key(&key_type_trusted, trusted_desc, NULL, NULL);
if (IS_ERR(tkey))
goto error;
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 671dd730ecfc..48c3e124c272 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -151,6 +151,7 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
key_user_put(key->user);
key_put_tag(key->domain_tag);
+ key_put_acl(rcu_access_pointer(key->acl));
kfree(key->description);
memzero_explicit(key, sizeof(*key));
@@ -220,7 +221,6 @@ continue_scanning:
if (key->type == key_gc_dead_keytype) {
gc_state |= KEY_GC_FOUND_DEAD_KEY;
set_bit(KEY_FLAG_DEAD, &key->flags);
- key->perm = 0;
goto skip_dead_key;
} else if (key->type == &key_type_keyring &&
key->restrict_link) {
diff --git a/security/keys/internal.h b/security/keys/internal.h
index c039373488bd..e0c5bb8b1685 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -84,8 +84,11 @@ extern struct rb_root key_serial_tree;
extern spinlock_t key_serial_lock;
extern struct mutex key_construction_mutex;
extern wait_queue_head_t request_key_conswq;
+extern struct key_acl default_key_acl;
+extern struct key_acl joinable_keyring_acl;
extern void key_set_index_key(struct keyring_index_key *index_key);
+
extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype);
@@ -156,6 +159,7 @@ extern struct key *request_key_and_link(struct key_type *type,
const void *callout_info,
size_t callout_len,
void *aux,
+ struct key_acl *acl,
struct key *dest_keyring,
unsigned long flags);
@@ -179,7 +183,10 @@ extern void key_gc_keytype(struct key_type *ktype);
extern int key_task_permission(const key_ref_t key_ref,
const struct cred *cred,
- key_perm_t perm);
+ u32 desired_perm);
+extern unsigned int key_acl_to_perm(const struct key_acl *acl);
+extern long key_set_acl(struct key *key, struct key_acl *acl);
+extern void key_put_acl(struct key_acl *acl);
/*
* Check to see whether permission is granted to use a key in the desired way.
@@ -226,7 +233,7 @@ extern long keyctl_keyring_search(key_serial_t, const char __user *,
const char __user *, key_serial_t);
extern long keyctl_read_key(key_serial_t, char __user *, size_t);
extern long keyctl_chown_key(key_serial_t, uid_t, gid_t);
-extern long keyctl_setperm_key(key_serial_t, key_perm_t);
+extern long keyctl_setperm_key(key_serial_t, unsigned int);
extern long keyctl_instantiate_key(key_serial_t, const void __user *,
size_t, key_serial_t);
extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
@@ -331,6 +338,11 @@ static inline long keyctl_pkey_e_d_s(int op,
extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen);
+extern long keyctl_grant_permission(key_serial_t keyid,
+ enum key_ace_subject_type type,
+ unsigned int subject,
+ unsigned int perm);
+
/*
* Debugging key validation
*/
diff --git a/security/keys/key.c b/security/keys/key.c
index 764f4c57913e..519211a996e7 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -195,7 +195,7 @@ serial_exists:
* @uid: The owner of the new key.
* @gid: The group ID for the new key's group permissions.
* @cred: The credentials specifying UID namespace.
- * @perm: The permissions mask of the new key.
+ * @acl: The ACL to attach to the new key.
* @flags: Flags specifying quota properties.
* @restrict_link: Optional link restriction for new keyrings.
*
@@ -223,7 +223,7 @@ serial_exists:
*/
struct key *key_alloc(struct key_type *type, const char *desc,
kuid_t uid, kgid_t gid, const struct cred *cred,
- key_perm_t perm, unsigned long flags,
+ struct key_acl *acl, unsigned long flags,
struct key_restriction *restrict_link)
{
struct key_user *user = NULL;
@@ -246,6 +246,9 @@ struct key *key_alloc(struct key_type *type, const char *desc,
desclen = strlen(desc);
quotalen = desclen + 1 + type->def_datalen;
+ if (!acl)
+ acl = &default_key_acl;
+
/* get hold of the key tracking for this user */
user = key_user_lookup(uid);
if (!user)
@@ -292,7 +295,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->datalen = type->def_datalen;
key->uid = uid;
key->gid = gid;
- key->perm = perm;
+ refcount_inc(&acl->usage);
+ rcu_assign_pointer(key->acl, acl);
key->restrict_link = restrict_link;
key->last_used_at = ktime_get_real_seconds();
@@ -787,7 +791,7 @@ error:
* @description: The searchable description for the key.
* @payload: The data to use to instantiate or update the key.
* @plen: The length of @payload.
- * @perm: The permissions mask for a new key.
+ * @acl: The ACL to attach if a key is created.
* @flags: The quota flags for a new key.
*
* Search the destination keyring for a key of the same description and if one
@@ -810,7 +814,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
const char *description,
const void *payload,
size_t plen,
- key_perm_t perm,
+ struct key_acl *acl,
unsigned long flags)
{
struct keyring_index_key index_key = {
@@ -907,22 +911,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
goto found_matching_key;
}
- /* 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;
-
- if (index_key.type->read)
- perm |= KEY_POS_READ;
-
- if (index_key.type == &key_type_keyring ||
- index_key.type->update)
- perm |= KEY_POS_WRITE;
- }
-
/* allocate a new key */
key = key_alloc(index_key.type, index_key.description,
- cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
+ cred->fsuid, cred->fsgid, cred, acl, flags, NULL);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
goto error_link_end;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 9b898c969558..c2dd66d556d4 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -37,7 +37,8 @@ static const unsigned char keyrings_capabilities[2] = {
KEYCTL_CAPS0_MOVE
),
[1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |
- KEYCTL_CAPS1_NS_KEY_TAG),
+ KEYCTL_CAPS1_NS_KEY_TAG |
+ KEYCTL_CAPS1_ACL_ALTERABLE),
};
static int key_get_type_from_user(char *type,
@@ -130,8 +131,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
/* create or update the requested key and add it to the target
* keyring */
key_ref = key_create_or_update(keyring_ref, type, description,
- payload, plen, KEY_PERM_UNDEF,
- KEY_ALLOC_IN_QUOTA);
+ payload, plen, NULL, KEY_ALLOC_IN_QUOTA);
if (!IS_ERR(key_ref)) {
ret = key_ref_to_ptr(key_ref)->serial;
key_ref_put(key_ref);
@@ -221,7 +221,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
/* do the search */
key = request_key_and_link(ktype, description, NULL, callout_info,
- callout_len, NULL, key_ref_to_ptr(dest_ref),
+ callout_len, NULL, NULL,
+ key_ref_to_ptr(dest_ref),
KEY_ALLOC_IN_QUOTA);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
@@ -383,16 +384,10 @@ long keyctl_revoke_key(key_serial_t id)
struct key *key;
long ret;
- key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);
+ key_ref = lookup_user_key(id, 0, KEY_NEED_REVOKE);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
- if (ret != -EACCES)
- goto error;
- key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
- if (IS_ERR(key_ref)) {
- ret = PTR_ERR(key_ref);
- goto error;
- }
+ goto error;
}
key = key_ref_to_ptr(key_ref);
@@ -426,7 +421,7 @@ long keyctl_invalidate_key(key_serial_t id)
kenter("%d", id);
- key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
+ key_ref = lookup_user_key(id, 0, KEY_NEED_INVAL);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
@@ -471,7 +466,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
struct key *keyring;
long ret;
- keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
+ keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_CLEAR);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
@@ -646,6 +641,7 @@ long keyctl_describe_key(key_serial_t keyid,
size_t buflen)
{
struct key *key, *instkey;
+ unsigned int perm;
key_ref_t key_ref;
char *infobuf;
long ret;
@@ -675,6 +671,10 @@ okay:
key = key_ref_to_ptr(key_ref);
desclen = strlen(key->description);
+ rcu_read_lock();
+ perm = key_acl_to_perm(rcu_dereference(key->acl));
+ rcu_read_unlock();
+
/* calculate how much information we're going to return */
ret = -ENOMEM;
infobuf = kasprintf(GFP_KERNEL,
@@ -682,7 +682,7 @@ okay:
key->type->name,
from_kuid_munged(current_user_ns(), key->uid),
from_kgid_munged(current_user_ns(), key->gid),
- key->perm);
+ perm);
if (!infobuf)
goto error2;
infolen = strlen(infobuf);
@@ -899,7 +899,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
goto error;
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
- KEY_NEED_SETATTR);
+ KEY_NEED_SETSEC);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -994,18 +994,25 @@ quota_overrun:
* the key need not be fully instantiated yet. If the caller does not have
* sysadmin capability, it may only change the permission on keys that it owns.
*/
-long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
+long keyctl_setperm_key(key_serial_t id, unsigned int perm)
{
+ struct key_acl *acl;
struct key *key;
key_ref_t key_ref;
long ret;
+ int nr, i, j;
- ret = -EINVAL;
if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
- goto error;
+ return -EINVAL;
+
+ nr = 0;
+ if (perm & KEY_POS_ALL) nr++;
+ if (perm & KEY_USR_ALL) nr++;
+ if (perm & KEY_GRP_ALL) nr++;
+ if (perm & KEY_OTH_ALL) nr++;
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
- KEY_NEED_SETATTR);
+ KEY_NEED_SETSEC);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -1013,17 +1020,45 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
key = key_ref_to_ptr(key_ref);
- /* make the changes with the locks held to prevent chown/chmod races */
- ret = -EACCES;
- down_write(&key->sem);
+ ret = -EOPNOTSUPP;
+ if (test_bit(KEY_FLAG_HAS_ACL, &key->flags))
+ goto error_key;
- /* if we're not the sysadmin, we can only change a key that we own */
- if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {
- key->perm = perm;
- ret = 0;
+ ret = -ENOMEM;
+ acl = kzalloc(struct_size(acl, aces, nr), GFP_KERNEL);
+ if (!acl)
+ goto error_key;
+
+ refcount_set(&acl->usage, 1);
+ acl->nr_ace = nr;
+ j = 0;
+ for (i = 0; i < 4; i++) {
+ struct key_ace *ace = &acl->aces[j];
+ unsigned int subset = (perm >> (i * 8)) & KEY_OTH_ALL;
+
+ if (!subset)
+ continue;
+ ace->type = KEY_ACE_SUBJ_STANDARD;
+ ace->subject_id = KEY_ACE_EVERYONE + i;
+ ace->perm = subset;
+ if (subset & (KEY_OTH_WRITE | KEY_OTH_SETATTR))
+ ace->perm |= KEY_ACE_REVOKE;
+ if (subset & KEY_OTH_SEARCH)
+ ace->perm |= KEY_ACE_INVAL;
+ if (key->type == &key_type_keyring) {
+ if (subset & KEY_OTH_SEARCH)
+ ace->perm |= KEY_ACE_JOIN;
+ if (subset & KEY_OTH_WRITE)
+ ace->perm |= KEY_ACE_CLEAR;
+ }
+ j++;
}
+ /* make the changes with the locks held to prevent chown/chmod races */
+ down_write(&key->sem);
+ ret = key_set_acl(key, acl);
up_write(&key->sem);
+error_key:
key_put(key);
error:
return ret;
@@ -1388,7 +1423,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
long ret;
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
- KEY_NEED_SETATTR);
+ KEY_NEED_SETSEC);
if (IS_ERR(key_ref)) {
/* setting the timeout on a key under construction is permitted
* if we have the authorisation token handy */
@@ -1539,7 +1574,7 @@ long keyctl_get_security(key_serial_t keyid,
* Attempt to install the calling process's session keyring on the process's
* parent process.
*
- * The keyring must exist and must grant the caller LINK permission, and the
+ * The keyring must exist and must grant the caller JOIN permission, and the
* parent process must be single-threaded and must have the same effective
* ownership as this process and mustn't be SUID/SGID.
*
@@ -1556,7 +1591,7 @@ long keyctl_session_to_parent(void)
struct cred *cred;
int ret;
- keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_LINK);
+ keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_JOIN);
if (IS_ERR(keyring_r))
return PTR_ERR(keyring_r);
@@ -1658,7 +1693,7 @@ long keyctl_restrict_keyring(key_serial_t id, const char __user *_type,
char *restriction = NULL;
long ret;
- key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
+ key_ref = lookup_user_key(id, 0, KEY_NEED_SETSEC);
if (IS_ERR(key_ref))
return PTR_ERR(key_ref);
@@ -1764,7 +1799,7 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_SETPERM:
return keyctl_setperm_key((key_serial_t) arg2,
- (key_perm_t) arg3);
+ (unsigned int)arg3);
case KEYCTL_INSTANTIATE:
return keyctl_instantiate_key((key_serial_t) arg2,
@@ -1853,6 +1888,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(key_serial_t)arg3,
(key_serial_t)arg4,
(unsigned int)arg5);
+ case KEYCTL_GRANT_PERMISSION:
+ return keyctl_grant_permission((key_serial_t)arg2,
+ (enum key_ace_subject_type)arg3,
+ (unsigned int)arg4,
+ (unsigned int)arg5);
case KEYCTL_CAPABILITIES:
return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index febf36c6ddc5..3b5458f23a95 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -515,11 +515,19 @@ static long keyring_read(const struct key *keyring,
return ret;
}
-/*
- * Allocate a keyring and link into the destination keyring.
+/**
+ * keyring_alloc - Allocate a keyring and link into the destination
+ * @description: The key description to allow the key to be searched out.
+ * @uid: The owner of the new key.
+ * @gid: The group ID for the new key's group permissions.
+ * @cred: The credentials specifying UID namespace.
+ * @acl: The ACL to attach to the new key.
+ * @flags: Flags specifying quota properties.
+ * @restrict_link: Optional link restriction for new keyrings.
+ * @dest: Destination keyring.
*/
struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
- const struct cred *cred, key_perm_t perm,
+ const struct cred *cred, struct key_acl *acl,
unsigned long flags,
struct key_restriction *restrict_link,
struct key *dest)
@@ -528,7 +536,7 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
int ret;
keyring = key_alloc(&key_type_keyring, description,
- uid, gid, cred, perm, flags, restrict_link);
+ uid, gid, cred, acl, flags, restrict_link);
if (!IS_ERR(keyring)) {
ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
if (ret < 0) {
@@ -1132,10 +1140,11 @@ found:
/*
* Find a keyring with the specified name.
*
- * Only keyrings that have nonzero refcount, are not revoked, and are owned by a
- * user in the current user namespace are considered. If @uid_keyring is %true,
- * the keyring additionally must have been allocated as a user or user session
- * keyring; otherwise, it must grant Search permission directly to the caller.
+ * Only keyrings that have nonzero refcount, are not revoked, and are owned by
+ * a user in the current user namespace are considered. If @uid_keyring is
+ * %true, the keyring additionally must have been allocated as a user or user
+ * session keyring; otherwise, it must grant JOIN permission directly to the
+ * caller (ie. not through possession).
*
* Returns a pointer to the keyring with the keyring's refcount having being
* incremented on success. -ENOKEY is returned if a key could not be found.
@@ -1169,7 +1178,7 @@ struct key *find_keyring_by_name(const char *name, bool uid_keyring)
continue;
} else {
if (key_permission(make_key_ref(keyring, 0),
- KEY_NEED_SEARCH) < 0)
+ KEY_NEED_JOIN) < 0)
continue;
}
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 085f907b64ac..fd8a5dc6910a 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -7,13 +7,67 @@
#include <linux/export.h>
#include <linux/security.h>
+#include <linux/user_namespace.h>
+#include <linux/uaccess.h>
#include "internal.h"
+struct key_acl default_key_acl = {
+ .usage = REFCOUNT_INIT(1),
+ .nr_ace = 2,
+ .possessor_viewable = true,
+ .aces = {
+ KEY_POSSESSOR_ACE(KEY_ACE__PERMS & ~KEY_ACE_JOIN),
+ KEY_OWNER_ACE(KEY_ACE_VIEW),
+ }
+};
+EXPORT_SYMBOL(default_key_acl);
+
+struct key_acl joinable_keyring_acl = {
+ .usage = REFCOUNT_INIT(1),
+ .nr_ace = 2,
+ .possessor_viewable = true,
+ .aces = {
+ KEY_POSSESSOR_ACE(KEY_ACE__PERMS & ~KEY_ACE_JOIN),
+ KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_LINK | KEY_ACE_JOIN),
+ }
+};
+EXPORT_SYMBOL(joinable_keyring_acl);
+
+struct key_acl internal_key_acl = {
+ .usage = REFCOUNT_INIT(1),
+ .nr_ace = 2,
+ .aces = {
+ KEY_POSSESSOR_ACE(KEY_ACE_SEARCH),
+ KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_SEARCH),
+ }
+};
+EXPORT_SYMBOL(internal_key_acl);
+
+struct key_acl internal_keyring_acl = {
+ .usage = REFCOUNT_INIT(1),
+ .nr_ace = 2,
+ .aces = {
+ KEY_POSSESSOR_ACE(KEY_ACE_SEARCH),
+ KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_SEA