From: David Howells (*) There are now five permissions instead of three, which makes things easier. Hopefully it also means that people will stop thinking keys have UNIX access permissions, which they do not. (*) The key "chmod" operation is now "setperm". (*) fsuid/fsgid are now used instead of euid/egid when using permissions. (*) Some stuff has been moved from the internal header file into include/linux/key-ui.h to make it available to keyfs. (*) /proc/keys is now disabled by default. It pretends keys don't exist unless you have at least one permission on them. Signed-off-by: Andrew Morton --- 25-akpm/Documentation/keys.txt | 115 ++++++++++++++++++++--------------- 25-akpm/include/linux/key-ui.h | 97 +++++++++++++++++++++++++++++ 25-akpm/include/linux/key.h | 41 ++++++++++-- 25-akpm/include/linux/prctl.h | 2 25-akpm/kernel/sys.c | 17 +++-- 25-akpm/security/Kconfig | 12 +++ 25-akpm/security/keys/internal.h | 35 ---------- 25-akpm/security/keys/key.c | 33 ++++------ 25-akpm/security/keys/keyctl.c | 67 ++++++++------------ 25-akpm/security/keys/keyring.c | 34 +++------- 25-akpm/security/keys/proc.c | 12 +++ 25-akpm/security/keys/process_keys.c | 113 ++++++++++++++++++++-------------- 25-akpm/security/keys/request_key.c | 11 +-- 13 files changed, 360 insertions(+), 229 deletions(-) diff -puN Documentation/keys.txt~implement-in-kernel-keys-keyring-management-update Documentation/keys.txt --- 25/Documentation/keys.txt~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/Documentation/keys.txt Wed Aug 18 17:23:37 2004 @@ -24,6 +24,7 @@ This document has the following sections - Kernel services - Defining a key type - Request-key callback service + - Key access filesystem ============ @@ -185,31 +186,43 @@ The key service provides a number of fea (*) There's a way for the a search done from the kernel to call back to userspace to request a key that can't be found in a process's keyrings. + (*) An optional filesystem is available through which the key database can be + viewed and manipulated. + ====================== KEY ACCESS PERMISSIONS ====================== -Keys have an owner user ID, a group access ID, and a permissions mask much -like a UNIX file does (three bits each for user, group and other access; each -of the group of three being read, write and execute). - -For most operations, the combination controls access. On ordinary keys the -bits in the mask map to privileges as follows: - - R Key payload can be viewed. - W Key can be updated or revoked. - X Key can be found by searching. - -And on a keyring it, they map thus: - - R Keyring subscriptions can be viewed. - W Keyring subscriptions can be added, removed or cleared. - X Keyring can be found by searching and its contents can be - searched. +Keys have an owner user ID, a group access ID, and a permissions mask. The +mask has up to eight bits each for user, group and other access. Only five of +each set of eight bits are defined. These permissions granted are: + + (*) View + + This permits a key or keyring's attributes to be viewed - including key + type and description. + + (*) Read + + This permits a key's payload to be viewed or a keyring's list of linked + keys. + + (*) Write -A key's description can be viewed only if a process qualifies for either read -permission or execute permission on a key. + This permits a key's payload to be instantiated or updated, or it allows + a link to be added to or removed from a keyring. + + (*) Search + + This permits keyrings to be searched and keys to be found. Searches can + only recurse into nested keyrings that have search permission set. + + (*) Link + + This permits a key or keyring to be linked to. To create a link from a + keyring to a key, a process must have Write permission on the keyring and + Link permission on the key. For changing the ownership, group ID or permissions mask, being the owner of the key or having the sysadmin capability is sufficient. @@ -228,14 +241,16 @@ about the status of the key service: type, description and permissions. The payload of the key is not available this way: - SERIAL FLAGS USAGE MODE UID GID TYPE DESCRIPTION: SUMMARY - 00000001 I---- 39 700 0 0 keyring _uid_ses.0: 2/4 - 00000002 I---- 2 700 0 0 keyring _uid.0: empty - 00000007 I---- 1 700 0 0 keyring _pid.1: empty - 000004d0 I---- 1 700 32 32 keyring _pid.887: empty - 000004d1 I--Q- 1 700 32 -1 keyring _uid.32: 1/4 - 000004d2 I--Q- 3 700 32 -1 keyring _uid_ses.32: empty - 00000892 ---QU 1 700 0 0 user test:wibble: 0 + SERIAL FLAGS USAGE EXPY PERM UID GID TYPE DESCRIPTION: SUMMARY + 00000001 I----- 39 perm 1f0000 0 0 keyring _uid_ses.0: 1/4 + 00000002 I----- 2 perm 1f0000 0 0 keyring _uid.0: empty + 00000007 I----- 1 perm 1f0000 0 0 keyring _pid.1: empty + 0000018d I----- 1 perm 1f0000 0 0 keyring _pid.412: empty + 000004d2 I--Q-- 1 perm 1f0000 32 -1 keyring _uid.32: 1/4 + 000004d3 I--Q-- 3 perm 1f0000 32 -1 keyring _uid_ses.32: empty + 00000892 I--QU- 1 perm 1f0000 0 0 user metal:copper: 0 + 00000893 I--Q-N 1 35s 1f0000 0 0 user metal:silver: 0 + 00000894 I--Q-- 1 10h 1f0000 0 0 user metal:gold: 0 The flags are: @@ -244,7 +259,10 @@ about the status of the key service: D Dead Q Contributes to user's quota U Under contruction by callback to userspace + N Negative key + This file must be enabled at kernel configuration time as it allows anyone + to list the keys database. (*) /proc/key-users @@ -314,8 +332,8 @@ The additional prctl syscall functions a is not permitted; otherwise a new keyring of that name is created and attached as the session keyring. - To attach to a named keyring, the keyring must have execute permission - for the process's ownership. + To attach to a named keyring, the keyring must have search permission for + the process's ownership. The ID of the new session keyring is returned if successful. @@ -333,7 +351,8 @@ The new keyctl syscall functions are: in the keyring, this will try to update it with the given payload, or it will return error EEXIST if that function is not supported by the key type. The process must also have permission to write to the key to be - able to update it. + able to update it. The new key will have all user permissions granted and + no group or third party permissions. Otherwise, this will attempt to create a new key of the specified type and description, and to instantiate it with the supplied payload and @@ -394,13 +413,13 @@ The new keyctl syscall functions are: (*) Change the permissions mask on a key: - long keyctl(KEYCTL_CHMOD, key_serial_t key, mode_t mode); + long keyctl(KEYCTL_SETPERM, key_serial_t key, key_perm_t perm); This function permits the owner of a key or the superuser to change the permissions mask on a key. - Only bits matching the mask 0777 are permitted. The bits are laid out - like standard UNIX file permissions. + Only bits the available bits are permitted; if any other bits are set, + error EINVAL will be returned. (*) Describe a key: @@ -416,16 +435,16 @@ The new keyctl syscall functions are: than requested to userspace. If the buffer pointer is NULL then no copy will take place. - A process must have either read or execute permission on the key for this - function to be successful. + A process must have view permission on the key for this function to be + successful. If successful, a string is placed in the buffer in the following format: - ;;;; + ;;;; - Where type and description are strings, uid and gid are decimal, and mode - is octal. A NUL character is included at the end of the string if the - buffer is sufficiently big. + Where type and description are strings, uid and gid are decimal, and perm + is hexadecimal. A NUL character is included at the end of the string if + the buffer is sufficiently big. This can be parsed with @@ -446,8 +465,8 @@ The new keyctl syscall functions are: long keyctl(KEYCTL_LINK, key_serial_t keyring, key_serial_t key); This function creates a link from the keyring to the key. The process - must have write permission on the keyring and must have read or execute - permission on the key. + must have write permission on the keyring and must have link permission + on the key. Should the keyring not be a keyring, error ENOTDIR will result; and if the keyring is full, error ENFILE will result. @@ -478,10 +497,10 @@ The new keyctl syscall functions are: key is found that matches the type and description criteria. Each keyring is checked for keys before recursion into its children occurs. - The process must have execute permission on the top level keyring, or - else error EACCES will result. Only keyrings that the process has execute + The process must have search permission on the top level keyring, or else + error EACCES will result. Only keyrings that the process has search permission on will be recursed into, and only keys and keyrings for which - a process has execute permission can be matched. If the specified keyring + a process has search permission can be matched. If the specified keyring is not a keyring, ENOTDIR will result. If the search succeeds, the function will attempt to link the found key @@ -537,7 +556,7 @@ The new keyctl syscall functions are: automatically. The process must have write access on the key to be able to instantiate - it. + it, and the key must be uninstantiated. If a keyring is specified (non-zero), the key will also be linked into that keyring, however all the constraints applying in KEYCTL_LINK apply @@ -554,7 +573,7 @@ The new keyctl syscall functions are: invoked process returns if it is unable to fulfil the request. The process must have write access on the key to be able to instantiate - it. + it, and the key must be uninstantiated. If a keyring is specified (non-zero), the key will also be linked into that keyring, however all the constraints applying in KEYCTL_LINK apply @@ -607,8 +626,8 @@ locked, or else the data may be changed const struct key_type *type, const char *description) - This searches the keyring specified for a matching key. Error ENOENT is - returned upon failure. If successful, the returned key will need to be + This searches the keyring tree specified for a matching key. Error ENOENT + is returned upon failure. If successful, the returned key will need to be released. diff -puN include/linux/key.h~implement-in-kernel-keys-keyring-management-update include/linux/key.h --- 25/include/linux/key.h~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/include/linux/key.h Wed Aug 18 17:23:37 2004 @@ -7,6 +7,9 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. + * + * + * See Documentation/keys.txt for information on keys/keyrings. */ #ifndef _LINUX_KEY_H @@ -21,10 +24,34 @@ #ifdef __KERNEL__ #ifdef CONFIG_KEYS -#define KEY_DEBUGGING +#undef KEY_DEBUGGING +/* key handle serial number */ typedef int32_t key_serial_t; +/* key handle permissions mask */ +typedef uint32_t key_perm_t; +#define KEY_USR_VIEW 0x00010000 /* user can view a key's attributes */ +#define KEY_USR_READ 0x00020000 /* user can read key payload / view keyring */ +#define KEY_USR_WRITE 0x00040000 /* user can update key payload / add link to keyring */ +#define KEY_USR_SEARCH 0x00080000 /* user can find a key in search / search a keyring */ +#define KEY_USR_LINK 0x00100000 /* user can create a link to a key/keyring */ +#define KEY_USR_ALL 0x001f0000 + +#define KEY_GRP_VIEW 0x00000100 /* group permissions... */ +#define KEY_GRP_READ 0x00000200 +#define KEY_GRP_WRITE 0x00000400 +#define KEY_GRP_SEARCH 0x00000800 +#define KEY_GRP_LINK 0x00001000 +#define KEY_GRP_ALL 0x00001f00 + +#define KEY_OTH_VIEW 0x00000001 /* third party permissions... */ +#define KEY_OTH_READ 0x00000002 +#define KEY_OTH_WRITE 0x00000004 +#define KEY_OTH_SEARCH 0x00000008 +#define KEY_OTH_LINK 0x00000010 +#define KEY_OTH_ALL 0x0000001f + struct seq_file; struct key; @@ -52,7 +79,7 @@ struct key { time_t expiry; /* time at which key expires (or 0) */ uid_t uid; gid_t gid; - umode_t mode; /* access mode - see Documentation/keys.txt */ + key_perm_t perm; /* access permissions */ unsigned short quotalen; /* length added to quota */ unsigned short datalen; /* payload data length */ unsigned short flags; /* status flags (change with lock writelocked) */ @@ -158,7 +185,7 @@ extern void unregister_key_type(struct k extern struct key *key_alloc(struct key_type *type, const char *desc, - uid_t uid, gid_t gid, mode_t mode, + uid_t uid, gid_t gid, key_perm_t perm, int not_in_quota); extern int key_payload_reserve(struct key *key, size_t datalen); extern int key_instantiate_and_link(struct key *key, @@ -221,10 +248,10 @@ extern int copy_keys(unsigned long clone extern void exit_keys(struct task_struct *tsk); extern int suid_keys(struct task_struct *tsk); extern int exec_keys(struct task_struct *tsk); -extern void key_euid_changed(struct task_struct *tsk); -extern void key_egid_changed(struct task_struct *tsk); +extern void key_fsuid_changed(struct task_struct *tsk); +extern void key_fsgid_changed(struct task_struct *tsk); extern long get_process_keyring_ID(key_serial_t id, int create); -extern long join_session_keyring(const char __user *name); +extern long join_session_keyring_user(const char __user *name); asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); @@ -241,7 +268,7 @@ asmlinkage long sys_keyctl(int option, u #define key_euid_changed(t) do { } while(0) #define key_egid_changed(t) do { } while(0) #define get_process_keyring_ID(s,c) (-EINVAL) -#define join_session_keyring(n) (-EINVAL) +#define join_session_keyring_user(n) (-EINVAL) #define sys_keyctl(o,b,c,d,e) (-EINVAL) #endif /* CONFIG_KEYS */ diff -puN /dev/null include/linux/key-ui.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/include/linux/key-ui.h Wed Aug 18 17:23:37 2004 @@ -0,0 +1,97 @@ +/* key-ui.h: key userspace interface stuff for use by keyfs + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_KEY_UI_H +#define _LINUX_KEY_UI_H + +#include + +/* the key tree */ +extern struct rb_root key_serial_tree; +extern spinlock_t key_serial_lock; + +/* required permissions */ +#define KEY_VIEW 0x01 /* require permission to view attributes */ +#define KEY_READ 0x02 /* require permission to read content */ +#define KEY_WRITE 0x04 /* require permission to update / modify */ +#define KEY_SEARCH 0x08 /* require permission to search (keyring) or find (key) */ +#define KEY_LINK 0x10 /* require permission to link */ +#define KEY_ALL 0x1f /* all the above permissions */ + +/* + * the keyring payload contains a list of the keys to which the keyring is + * subscribed + */ +struct keyring_list { + unsigned maxkeys; /* max keys this list can hold */ + unsigned nkeys; /* number of keys currently held */ + struct key *keys[0]; +}; + + +/* + * check to see whether permission is granted to use a key in the desired way + */ +static inline int key_permission(const struct key *key, key_perm_t perm) +{ + key_perm_t kperm; + + if (key->uid == current->fsuid) + kperm = key->perm >> 16; + else if (key->gid != -1 && + key->perm & KEY_GRP_ALL && + in_group_p(key->gid) + ) + kperm = key->perm >> 8; + else + kperm = key->perm; + + kperm = kperm & perm & KEY_ALL; + + return kperm == perm; +} + +/* + * check to see whether permission is granted to use a key in at least one of + * the desired ways + */ +static inline int key_any_permission(const struct key *key, key_perm_t perm) +{ + key_perm_t kperm; + + if (key->uid == current->fsuid) + kperm = key->perm >> 16; + else if (key->gid != -1 && + key->perm & KEY_GRP_ALL && + in_group_p(key->gid) + ) + kperm = key->perm >> 8; + else + kperm = key->perm; + + kperm = kperm & perm & KEY_ALL; + + return kperm != 0; +} + + +extern struct key *lookup_user_key(key_serial_t id, int create, int part, + key_perm_t perm); + +extern long join_session_keyring(const char *name); + +extern struct key_type *key_type_lookup(const char *type); +extern void key_type_put(struct key_type *ktype); + +#define key_negative_timeout 60 /* default timeout on a negative key's existence */ + + +#endif /* _LINUX_KEY_UI_H */ diff -puN include/linux/prctl.h~implement-in-kernel-keys-keyring-management-update include/linux/prctl.h --- 25/include/linux/prctl.h~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/include/linux/prctl.h Wed Aug 18 17:23:37 2004 @@ -63,7 +63,7 @@ #define KEYCTL_UPDATE 0x101 /* update a key */ #define KEYCTL_REVOKE 0x102 /* revoke a key */ #define KEYCTL_CHOWN 0x103 /* set ownership of a key */ -#define KEYCTL_CHMOD 0x104 /* set perms on a key */ +#define KEYCTL_SETPERM 0x104 /* set perms on a key */ #define KEYCTL_DESCRIBE 0x105 /* describe a key */ #define KEYCTL_CLEAR 0x106 /* clear contents of a keyring */ #define KEYCTL_LINK 0x107 /* link a key into a keyring */ diff -puN kernel/sys.c~implement-in-kernel-keys-keyring-management-update kernel/sys.c --- 25/kernel/sys.c~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/kernel/sys.c Wed Aug 18 17:23:37 2004 @@ -608,7 +608,7 @@ asmlinkage long sys_setregid(gid_t rgid, current->fsgid = new_egid; current->egid = new_egid; current->gid = new_rgid; - key_egid_changed(current); + key_fsgid_changed(current); return 0; } @@ -647,7 +647,7 @@ asmlinkage long sys_setgid(gid_t gid) else return -EPERM; - key_egid_changed(current); + key_fsgid_changed(current); return 0; } @@ -736,7 +736,7 @@ asmlinkage long sys_setreuid(uid_t ruid, current->suid = current->euid; current->fsuid = current->euid; - key_euid_changed(current); + key_fsuid_changed(current); return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE); } @@ -783,7 +783,7 @@ asmlinkage long sys_setuid(uid_t uid) current->fsuid = current->euid = uid; current->suid = new_suid; - key_euid_changed(current); + key_fsuid_changed(current); return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); } @@ -831,7 +831,7 @@ asmlinkage long sys_setresuid(uid_t ruid if (suid != (uid_t) -1) current->suid = suid; - key_euid_changed(current); + key_fsuid_changed(current); return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES); } @@ -883,7 +883,7 @@ asmlinkage long sys_setresgid(gid_t rgid if (sgid != (gid_t) -1) current->sgid = sgid; - key_egid_changed(current); + key_fsgid_changed(current); return 0; } @@ -925,6 +925,8 @@ asmlinkage long sys_setfsuid(uid_t uid) current->fsuid = uid; } + key_fsuid_changed(current); + security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); return old_fsuid; @@ -951,6 +953,7 @@ asmlinkage long sys_setfsgid(gid_t gid) wmb(); } current->fsgid = gid; + key_fsgid_changed(current); } return old_fsgid; } @@ -1691,7 +1694,7 @@ asmlinkage long sys_prctl(int option, un error = get_process_keyring_ID((key_serial_t) arg2, arg3); break; case PR_JOIN_SESSION_KEYRING: - error = join_session_keyring((char __user *) arg2); + error = join_session_keyring_user((const char __user *) arg2); break; case KEYCTL_NEW ... __KEYCTL_LAST: diff -puN security/Kconfig~implement-in-kernel-keys-keyring-management-update security/Kconfig --- 25/security/Kconfig~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/Kconfig Wed Aug 18 17:23:37 2004 @@ -21,6 +21,18 @@ config KEYS If you are unsure as to whether this is required, answer N. +config KEYS_DEBUG_PROC_KEYS + bool "Enable the /proc/keys file by which all keys may be viewed" + depends on KEYS + help + This option turns on support for the /proc/keys file through which + all the keys on the system can be listed. + + This option is a slight security risk in that it makes it possible + for anyone to see all the keys on the system. Normally the manager + pretends keys that are inaccessible to a process don't exist as far + as that process is concerned. + config SECURITY bool "Enable different security models" help diff -puN security/keys/internal.h~implement-in-kernel-keys-keyring-management-update security/keys/internal.h --- 25/security/keys/internal.h~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/keys/internal.h Wed Aug 18 17:23:37 2004 @@ -13,14 +13,11 @@ #define _INTERNAL_H #include +#include extern struct key_type key_type_dead; extern struct key_type key_type_user; -#define KEY_READ 04 /* require read permission */ -#define KEY_WRITE 02 /* require write permission */ -#define KEY_EXEC 01 /* require execute permission */ - /*****************************************************************************/ /* * keep track of keys for a user @@ -61,9 +58,6 @@ extern struct rw_semaphore key_construct extern wait_queue_head_t request_key_conswq; -extern struct key_type *key_type_lookup(const char *type); -extern void key_type_put(struct key_type *ktype); - extern void keyring_publish_name(struct key *keyring); extern int __key_link(struct key *keyring, struct key *key); @@ -71,39 +65,17 @@ extern int __key_link(struct key *keyrin extern struct key *__keyring_search_one(struct key *keyring, const struct key_type *type, const char *description, - umode_t mode); - -extern struct key *lookup_user_key(key_serial_t id, int create, int part, - mode_t mode); + key_perm_t perm); extern struct key *find_keyring_by_name(const char *name, key_serial_t bound); extern int install_thread_keyring(struct task_struct *tsk); -/* - * check to see whether permission is granted to use a key in the desired way - */ -static inline int key_permission(const struct key *key, mode_t mode) -{ - mode_t kmode; - - if (key->uid == current->euid) - kmode = key->mode >> 6; - else if (key->gid != -1 && - key->mode & S_IRWXG && - in_egroup_p(key->gid) - ) - kmode = key->mode >> 3; - else - kmode = key->mode; - - return (mode & kmode & 07) == mode; -} - /* * debugging key validation */ +#ifdef KEY_DEBUGGING static void __key_check(const struct key *key) { printk("__key_check: key %p {%08x} should be {%08x}\n", @@ -112,7 +84,6 @@ static void __key_check(const struct key } -#ifdef KEY_DEBUGGING static inline void key_check(const struct key *key) { if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC)) diff -puN security/keys/key.c~implement-in-kernel-keys-keyring-management-update security/keys/key.c --- 25/security/keys/key.c~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/keys/key.c Wed Aug 18 17:23:37 2004 @@ -238,7 +238,7 @@ static inline void key_alloc_serial(stru * instantiate the key or discard it before returning */ struct key *key_alloc(struct key_type *type, const char *desc, - uid_t uid, gid_t gid, mode_t mode, + uid_t uid, gid_t gid, key_perm_t perm, int not_in_quota) { struct key_user *user = NULL; @@ -293,7 +293,7 @@ struct key *key_alloc(struct key_type *t key->datalen = type->def_datalen; key->uid = uid; key->gid = gid; - key->mode = mode; + key->perm = perm; key->flags = 0; key->expiry = 0; key->payload.data = NULL; @@ -736,7 +736,7 @@ struct key *key_create_or_update(struct { struct key_type *ktype; struct key *key = NULL; - mode_t mode; + key_perm_t perm; int ret; key_check(keyring); @@ -768,16 +768,18 @@ struct key *key_create_or_update(struct if (!key_permission(keyring, KEY_WRITE)) goto error_3; - /* decide on the mode we want */ - mode = S_IXUSR; - if (ktype == &key_type_keyring || ktype == &key_type_user) - mode = S_IRWXU; - else if (ktype->update) - mode = S_IWUSR | S_IXUSR; + /* decide on the permissions we want */ + perm = KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK; + + if (ktype->read) + perm |= KEY_USR_READ; + + if (ktype == &key_type_keyring || ktype->update) + perm |= KEY_USR_WRITE; /* allocate a new key */ - key = key_alloc(ktype, description, current->euid, current->egid, mode, - not_in_quota); + key = key_alloc(ktype, description, current->fsuid, current->fsgid, + perm, not_in_quota); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error_3; @@ -870,14 +872,9 @@ struct key *key_duplicate(struct key *so if (!source->type->duplicate) goto error; - /* must be able to read the source key */ - ret = -EACCES; - if (!key_permission(source, KEY_READ)) - goto error; - /* allocate and instantiate a key */ - key = key_alloc(source->type, desc, current->euid, current->egid, - source->mode, 0); + key = key_alloc(source->type, desc, current->fsuid, current->fsgid, + source->perm, 0); if (IS_ERR(key)) goto error_k; diff -puN security/keys/keyctl.c~implement-in-kernel-keys-keyring-management-update security/keys/keyctl.c --- 25/security/keys/keyctl.c~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/keys/keyctl.c Wed Aug 18 17:23:37 2004 @@ -225,7 +225,7 @@ static long user_keyring_clear(key_seria /* * link a key into a keyring * - the keyring must be writable - * - the key must be readable or executable + * - the key must be linkable * - implements keyctl(KEYCTL_LINK) */ static long user_keyring_link(key_serial_t ringid, key_serial_t id) @@ -239,18 +239,11 @@ static long user_keyring_link(key_serial goto error; } - key = lookup_user_key(id, 1, 0, KEY_READ); + key = lookup_user_key(id, 1, 0, KEY_LINK); if (IS_ERR(key)) { - if (PTR_ERR(key) == -EACCES) { - key = lookup_user_key(id, 1, 0, KEY_EXEC); - if (!IS_ERR(key)) - goto okay; - } - ret = PTR_ERR(key); goto error2; } - okay: ret = key_link(keyring, key); @@ -299,12 +292,12 @@ static long user_keyring_unlink(key_seri /*****************************************************************************/ /* * describe a user key - * - the key must have read or execute permission + * - the key must have view permission * - if there's a buffer, we place up to buflen bytes of data into it * - unless there's an error, we return the amount of description available, * irrespective of how much we may have copied * - the description is formatted thus: - * type;uid;gid;mode;description + * type;uid;gid;perm;description * - implements keyctl(KEYCTL_DESCRIBE) */ static long user_describe_key(key_serial_t keyid, @@ -315,18 +308,11 @@ static long user_describe_key(key_serial char *tmpbuf; long ret; - key = lookup_user_key(keyid, 0, 1, KEY_READ); + key = lookup_user_key(keyid, 0, 1, KEY_VIEW); if (IS_ERR(key)) { - if (PTR_ERR(key) == -EACCES) { - key = lookup_user_key(keyid, 0, 1, KEY_EXEC); - if (!IS_ERR(key)) - goto okay; - } - ret = PTR_ERR(key); goto error; } - okay: /* calculate how much description we're going to return */ ret = -ENOMEM; @@ -335,11 +321,11 @@ static long user_describe_key(key_serial goto error2; ret = snprintf(tmpbuf, PAGE_SIZE - 1, - "%s;%d;%d;%o;%s", + "%s;%d;%d;%06x;%s", key->type->name, key->uid, key->gid, - key->mode, + key->perm, key->description ? key->description :"" ); @@ -369,8 +355,9 @@ static long user_describe_key(key_serial /*****************************************************************************/ /* * search the specified keyring for a matching key - * - the start keyring must be executable - * - nested keyrings may also be searched if they are executable + * - the start keyring must be searchable + * - nested keyrings may also be searched if they are searchable + * - only keys with search permission may be found * - if a key is found, it will be attached to the destination keyring if * there's one specified * - implements keyctl(KEYCTL_SEARCH) @@ -410,7 +397,7 @@ static long user_keyring_search(key_seri goto error2; /* get the keyring at which to begin the search */ - keyring = lookup_user_key(ringid, 0, 0, KEY_EXEC); + keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH); if (IS_ERR(keyring)) goto error2; @@ -440,8 +427,12 @@ static long user_keyring_search(key_seri goto error5; } - /* link the resulting key to the destination keyring */ + /* link the resulting key to the destination keyring if we can */ if (dest) { + ret = -EACCES; + if (!key_permission(key, KEY_LINK)) + goto error6; + ret = key_link(dest, key); if (ret < 0) goto error6; @@ -466,7 +457,7 @@ static long user_keyring_search(key_seri /*****************************************************************************/ /* - * read a user key + * read a user key's payload * - the keyring must be readable * - if there's a buffer, we place up to buflen bytes of data into it * - unless there's an error, we return the amount of data in the key, @@ -535,7 +526,7 @@ static long user_chown_key(key_serial_t /* only the sysadmin can set the key's GID to a group other * than one of those that the current process subscribes to */ - if (gid != (gid_t) -1 && gid != key->gid && !in_egroup_p(gid)) + if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) goto no_access; } @@ -565,15 +556,15 @@ static long user_chown_key(key_serial_t /* * change the permission mask on a key * - the keyring owned by the changer - * - implements keyctl(KEYCTL_CHMOD) + * - implements keyctl(KEYCTL_SETPERM) */ -static long user_chmod_key(key_serial_t id, mode_t mode) +static long user_setperm_key(key_serial_t id, key_perm_t perm) { struct key *key; long ret; ret = -EINVAL; - if (mode & ~S_IALLUGO) + if (perm & ~(KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) goto error; key = lookup_user_key(id, 1, 1, 0); @@ -590,11 +581,11 @@ static long user_chmod_key(key_serial_t /* if we're not the sysadmin, we can only chmod a key that we * own */ - if (!capable(CAP_SYS_ADMIN) && key->uid != current->euid) + if (!capable(CAP_SYS_ADMIN) && key->uid != current->fsuid) goto no_access; - /* changing the mode */ - key->mode = mode; + /* changing the permissions mask */ + key->perm = perm; ret = 0; no_access: @@ -604,12 +595,12 @@ static long user_chmod_key(key_serial_t error: return ret; -} /* end user_chmod_key() */ +} /* end user_serperm_key() */ /*****************************************************************************/ /* * search the process keyrings for a matching key - * - nested keyrings may also be searched if they are executable + * - nested keyrings may also be searched if they are searchable * - if a key is found, it will be attached to the destination keyring if * there's one specified * - implements keyctl(KEYCTL_REQUEST_KEY) @@ -855,9 +846,9 @@ asmlinkage long sys_keyctl(int option, u (uid_t) arg3, (gid_t) arg4); - case KEYCTL_CHMOD: - return user_chmod_key((key_serial_t) arg2, - (mode_t) arg3); + case KEYCTL_SETPERM: + return user_setperm_key((key_serial_t) arg2, + (key_perm_t) arg3); case KEYCTL_REQUEST_KEY: return user_request_key((const char __user *) arg2, diff -puN security/keys/keyring.c~implement-in-kernel-keys-keyring-management-update security/keys/keyring.c --- 25/security/keys/keyring.c~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/keys/keyring.c Wed Aug 18 17:23:37 2004 @@ -43,16 +43,6 @@ static inline unsigned keyring_hash(cons } /* - * the keyring payload contains a list of the keys to which the keyring is - * subscribed - */ -struct keyring_list { - unsigned maxkeys; /* max keys this list can hold */ - unsigned nkeys; /* number of keys currently held */ - struct key *keys[0]; -}; - -/* * the keyring type definition */ static int keyring_instantiate(struct key *keyring, @@ -299,7 +289,7 @@ struct key *keyring_alloc(const char *de int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, 0700, not_in_quota); + uid, gid, KEY_USR_ALL, not_in_quota); if (!IS_ERR(keyring)) { ret = key_instantiate_and_link(keyring, NULL, 0, dest); @@ -317,7 +307,7 @@ struct key *keyring_alloc(const char *de /* * search the supplied keyring tree for a key that matches the criterion * - perform a breadth-then-depth search up to the prescribed limit - * - we only find keys on which we have execute permission + * - we only find keys on which we have search permission * - we readlock the keyrings as we search down the tree * - we return -EAGAIN if we didn't find any matching key * - we return -ENOENT if we only found negative matching keys @@ -339,9 +329,9 @@ struct key *keyring_search(struct key *k key_check(keyring); - /* top keyring must have execute permission to begin the search */ + /* top keyring must have search permission to begin the search */ key = ERR_PTR(-EACCES); - if (!key_permission(keyring, KEY_EXEC)) + if (!key_permission(keyring, KEY_SEARCH)) goto error; key = ERR_PTR(-ENOTDIR); @@ -381,8 +371,8 @@ struct key *keyring_search(struct key *k if (!key->type->match(key, description)) continue; - /* key must have executable permissions */ - if (!key_permission(key, KEY_EXEC)) + /* key must have search permissions */ + if (!key_permission(key, KEY_SEARCH)) continue; /* we set a different error code if we find a negative key */ @@ -403,12 +393,12 @@ struct key *keyring_search(struct key *k goto next; /* recursively search nested keyrings - * - only search keyrings for which we have execute permission + * - only search keyrings for which we have search permission */ if (sp >= KEYRING_SEARCH_MAX_DEPTH) goto next; - if (!key_permission(key, KEY_EXEC)) + if (!key_permission(key, KEY_SEARCH)) goto next; /* evade loops in the keyring tree */ @@ -473,7 +463,7 @@ EXPORT_SYMBOL(keyring_search); struct key *__keyring_search_one(struct key *keyring, const struct key_type *ktype, const char *description, - umode_t mode) + key_perm_t perm) { struct keyring_list *klist; struct key *key; @@ -486,7 +476,7 @@ struct key *__keyring_search_one(struct if (key->type == ktype && key->type->match(key, description) && - key_permission(key, mode) && + key_permission(key, perm) && !(key->flags & KEY_FLAG_REVOKED) ) goto found; @@ -507,7 +497,7 @@ struct key *__keyring_search_one(struct /* * find a keyring with the specified name * - all named keyrings are searched - * - only find keyrings with execute permission for the process + * - only find keyrings with search permission for the process * - only find keyrings with a serial number greater than the one specified */ struct key *find_keyring_by_name(const char *name, key_serial_t bound) @@ -536,7 +526,7 @@ struct key *find_keyring_by_name(const c if (strcmp(keyring->description, name) != 0) continue; - if (!key_permission(keyring, KEY_EXEC)) + if (!key_permission(keyring, KEY_SEARCH)) continue; /* found a potential candidate, but we still need to diff -puN security/keys/proc.c~implement-in-kernel-keys-keyring-management-update security/keys/proc.c --- 25/security/keys/proc.c~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/keys/proc.c Wed Aug 18 17:23:37 2004 @@ -19,6 +19,7 @@ #include #include "internal.h" +#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS static int proc_keys_open(struct inode *inode, struct file *file); static void *proc_keys_start(struct seq_file *p, loff_t *_pos); static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); @@ -38,6 +39,7 @@ static struct file_operations proc_keys_ .llseek = seq_lseek, .release = seq_release, }; +#endif static int proc_key_users_open(struct inode *inode, struct file *file); static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); @@ -67,11 +69,13 @@ static int __init key_proc_init(void) { struct proc_dir_entry *p; +#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS p = create_proc_entry("keys", 0, NULL); if (!p) panic("Cannot create /proc/keys\n"); p->proc_fops = &proc_keys_fops; +#endif p = create_proc_entry("key-users", 0, NULL); if (!p) @@ -89,6 +93,8 @@ __initcall(key_proc_init); /* * implement "/proc/keys" to provides a list of the keys on the system */ +#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS + static int proc_keys_open(struct inode *inode, struct file *file) { return seq_open(file, &proc_keys_ops); @@ -158,7 +164,7 @@ static int proc_keys_show(struct seq_fil sprintf(xbuf, "%luw", timo / (60*60*24*7)); } - seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %03o %5d %5d %-9.9s ", + seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %06x %5d %5d %-9.9s ", key->serial, key->flags & KEY_FLAG_INSTANTIATED ? 'I' : '-', key->flags & KEY_FLAG_REVOKED ? 'R' : '-', @@ -168,7 +174,7 @@ static int proc_keys_show(struct seq_fil key->flags & KEY_FLAG_NEGATIVE ? 'N' : '-', atomic_read(&key->usage), xbuf, - key->mode, + key->perm, key->uid, key->gid, key->type->name); @@ -183,6 +189,8 @@ static int proc_keys_show(struct seq_fil } +#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ + /*****************************************************************************/ /* * implement "/proc/key-users" to provides a list of the key users diff -puN security/keys/process_keys.c~implement-in-kernel-keys-keyring-management-update security/keys/process_keys.c --- 25/security/keys/process_keys.c~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/keys/process_keys.c Wed Aug 18 17:24:28 2004 @@ -40,7 +40,7 @@ struct key root_user_keyring = { .user = &root_key_user, .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_user_keyring.sem), - .mode = S_IRWXU, + .perm = KEY_USR_ALL, .flags = KEY_FLAG_INSTANTIATED, .description = "_uid.0", #ifdef KEY_DEBUGGING @@ -56,7 +56,7 @@ struct key root_session_keyring = { .user = &root_key_user, .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_session_keyring.sem), - .mode = S_IRWXU, + .perm = KEY_USR_ALL, .flags = KEY_FLAG_INSTANTIATED, .description = "_uid_ses.0", #ifdef KEY_DEBUGGING @@ -310,15 +310,15 @@ int suid_keys(struct task_struct *tsk) /*****************************************************************************/ /* - * the effective user ID changed + * the filesystem user ID changed */ -void key_euid_changed(struct task_struct *tsk) +void key_fsuid_changed(struct task_struct *tsk) { /* update the ownership of the process keyring */ if (tsk->process_keyring) { down_write(&tsk->process_keyring->sem); write_lock(&tsk->process_keyring->lock); - tsk->process_keyring->uid = tsk->euid; + tsk->process_keyring->uid = tsk->fsuid; write_unlock(&tsk->process_keyring->lock); up_write(&tsk->process_keyring->sem); } @@ -327,24 +327,24 @@ void key_euid_changed(struct task_struct if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); write_lock(&tsk->thread_keyring->lock); - tsk->thread_keyring->uid = tsk->euid; + tsk->thread_keyring->uid = tsk->fsuid; write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } -} /* end key_euid_changed() */ +} /* end key_fsuid_changed() */ /*****************************************************************************/ /* - * the effective group ID changed + * the filesystem group ID changed */ -void key_egid_changed(struct task_struct *tsk) +void key_fsgid_changed(struct task_struct *tsk) { /* update the ownership of the process keyring */ if (tsk->process_keyring) { down_write(&tsk->process_keyring->sem); write_lock(&tsk->process_keyring->lock); - tsk->process_keyring->gid = tsk->egid; + tsk->process_keyring->gid = tsk->fsgid; write_unlock(&tsk->process_keyring->lock); up_write(&tsk->process_keyring->sem); } @@ -353,12 +353,12 @@ void key_egid_changed(struct task_struct if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); write_lock(&tsk->thread_keyring->lock); - tsk->thread_keyring->gid = tsk->egid; + tsk->thread_keyring->gid = tsk->fsgid; write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } -} /* end key_egid_changed() */ +} /* end key_fsgid_changed() */ /*****************************************************************************/ /* @@ -450,12 +450,12 @@ struct key *search_process_keyrings(stru /*****************************************************************************/ /* - * lookup a key given a key ID from userspace with a given access mode + * lookup a key given a key ID from userspace with a given permissions mask * - don't create special keyrings unless so requested * - partially constructed keys aren't found unless requested */ struct key *lookup_user_key(key_serial_t id, int create, int partial, - mode_t mode) + key_perm_t perm) { struct task_struct *tsk = current; struct key *key; @@ -537,7 +537,7 @@ struct key *lookup_user_key(key_serial_t } /* check the status and permissions */ - if (mode) { + if (perm) { ret = key_validate(key); if (ret < 0) goto invalid_key; @@ -548,7 +548,7 @@ struct key *lookup_user_key(key_serial_t goto invalid_key; ret = -EACCES; - if (!key_permission(key, mode)) + if (!key_permission(key, perm)) goto invalid_key; error: @@ -564,7 +564,7 @@ struct key *lookup_user_key(key_serial_t /*****************************************************************************/ /* * get the ID of the specified process keyring - * - the keyring must have execute permission to be found + * - the keyring must have search permission to be found * - implements prctl(PR_GET_KEYRING_ID) */ long get_process_keyring_ID(key_serial_t id, int create) @@ -572,7 +572,7 @@ long get_process_keyring_ID(key_serial_t struct key *key; long ret; - key = lookup_user_key(id, create, 0, KEY_EXEC); + key = lookup_user_key(id, create, 0, KEY_SEARCH); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -591,17 +591,15 @@ long get_process_keyring_ID(key_serial_t * create a new one of that name if not * - if the name is NULL, an empty anonymous keyring is installed instead * - named session keyring joining is done with a semaphore held - * - implements prctl(PR_JOIN_SESSION_KEYRING) */ -long join_session_keyring(const char __user *_name) +long join_session_keyring(const char *name) { struct task_struct *tsk = current; struct key *keyring; - char *name; - long nlen, ret; + long ret; /* if no name is provided, install an anonymous keyring */ - if (!_name) { + if (!name) { ret = install_session_keyring(tsk, NULL); if (ret < 0) goto error; @@ -610,25 +608,6 @@ long join_session_keyring(const char __u goto error; } - /* fetch the name from userspace */ - ret = -EFAULT; - nlen = strnlen_user(_name, PAGE_SIZE - 1); - if (nlen <= 0) - goto error; - - ret = -EINVAL; - if (nlen > PAGE_SIZE - 1) - goto error; - - ret = -ENOMEM; - name = kmalloc(nlen + 1, GFP_KERNEL); - if (!name) - goto error; - - ret = -EFAULT; - if (copy_from_user(name, _name, nlen + 1) != 0) - goto error_2; - /* allow the user to join or create a named keyring */ down(&key_session_sem); @@ -639,12 +618,12 @@ long join_session_keyring(const char __u keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); - goto error_2; + goto error; } } else if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); - goto error_3; + goto error2; } /* we've got a keyring - now to install it */ @@ -656,11 +635,51 @@ long join_session_keyring(const char __u ret = tsk->session_keyring->serial; - error_3: + error2: up(&key_session_sem); - error_2: - kfree(name); error: return ret; } /* end join_session_keyring() */ + +/*****************************************************************************/ +/* + * join the session keyring + * - implements prctl(PR_JOIN_SESSION_KEYRING) + */ +long join_session_keyring_user(const char __user *_name) +{ + char *name; + long nlen, ret; + + /* fetch the name from userspace */ + name = NULL; + if (_name) { + ret = -EFAULT; + nlen = strnlen_user(_name, PAGE_SIZE - 1); + if (nlen <= 0) + goto error; + + ret = -EINVAL; + if (nlen > PAGE_SIZE - 1) + goto error; + + ret = -ENOMEM; + name = kmalloc(nlen + 1, GFP_KERNEL); + if (!name) + goto error; + + ret = -EFAULT; + if (copy_from_user(name, _name, nlen + 1) != 0) + goto error2; + } + + /* join the session */ + ret = join_session_keyring(name); + + error2: + kfree(name); + error: + return ret; + +} /* end join_session_keyring_user() */ diff -puN security/keys/request_key.c~implement-in-kernel-keys-keyring-management-update security/keys/request_key.c --- 25/security/keys/request_key.c~implement-in-kernel-keys-keyring-management-update Wed Aug 18 17:23:37 2004 +++ 25-akpm/security/keys/request_key.c Wed Aug 18 17:23:37 2004 @@ -11,13 +11,10 @@ #include #include -#include #include #include #include "internal.h" -#define key_negative_timeout 60 /* timeout on a negative key's existence */ - struct key_construction { struct list_head link; /* link in construction queue */ struct key *key; /* key being constructed */ @@ -39,8 +36,8 @@ static int call_request_key(struct key * int i; /* record the UID and GID */ - sprintf(uid_str, "%d", current->euid); - sprintf(gid_str, "%d", current->egid); + sprintf(uid_str, "%d", current->fsuid); + sprintf(gid_str, "%d", current->fsgid); /* we say which key is under construction */ sprintf(key_str, "%d", key->serial); @@ -94,7 +91,7 @@ static struct key *__request_key_constru /* create a key and add it to the queue */ key = key_alloc(type, description, - current->euid, current->egid, 0700, 0); + current->fsuid, current->fsgid, KEY_USR_ALL, 0); if (IS_ERR(key)) goto alloc_failed; @@ -267,7 +264,7 @@ struct key *request_key(struct key_type goto error; /* - get hold of the user's construction queue */ - user = key_user_lookup(current->euid); + user = key_user_lookup(current->fsuid); if (IS_ERR(user)) { key = ERR_PTR(PTR_ERR(user)); goto error; _