summaryrefslogtreecommitdiff
path: root/security/keys/keyctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/keys/keyctl.c')
-rw-r--r--security/keys/keyctl.c104
1 files changed, 72 insertions, 32 deletions
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);