2011-01-20 19:38:33 +03:00
/* procfs files for key database enumeration
2005-04-17 02:20:36 +04:00
*
* 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 .
*/
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/fs.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <asm/errno.h>
# include "internal.h"
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 ) ;
static void proc_keys_stop ( struct seq_file * p , void * v ) ;
static int proc_keys_show ( struct seq_file * m , void * v ) ;
2008-01-23 02:02:58 +03:00
static const struct seq_operations proc_keys_ops = {
2005-04-17 02:20:36 +04:00
. start = proc_keys_start ,
. next = proc_keys_next ,
. stop = proc_keys_stop ,
. show = proc_keys_show ,
} ;
static void * proc_key_users_start ( struct seq_file * p , loff_t * _pos ) ;
static void * proc_key_users_next ( struct seq_file * p , void * v , loff_t * _pos ) ;
static void proc_key_users_stop ( struct seq_file * p , void * v ) ;
static int proc_key_users_show ( struct seq_file * m , void * v ) ;
2008-01-23 02:02:58 +03:00
static const struct seq_operations proc_key_users_ops = {
2005-04-17 02:20:36 +04:00
. start = proc_key_users_start ,
. next = proc_key_users_next ,
. stop = proc_key_users_stop ,
. show = proc_key_users_show ,
} ;
/*
2011-01-20 19:38:33 +03:00
* Declare the / proc files .
2005-04-17 02:20:36 +04:00
*/
static int __init key_proc_init ( void )
{
struct proc_dir_entry * p ;
2018-04-13 20:44:18 +03:00
p = proc_create_seq ( " keys " , 0 , NULL , & proc_keys_ops ) ;
2005-04-17 02:20:36 +04:00
if ( ! p )
panic ( " Cannot create /proc/keys \n " ) ;
2018-04-13 20:44:18 +03:00
p = proc_create_seq ( " key-users " , 0 , NULL , & proc_key_users_ops ) ;
2005-04-17 02:20:36 +04:00
if ( ! p )
panic ( " Cannot create /proc/key-users \n " ) ;
return 0 ;
2011-01-20 19:38:27 +03:00
}
2005-04-17 02:20:36 +04:00
__initcall ( key_proc_init ) ;
/*
2011-01-20 19:38:33 +03:00
* Implement " /proc/keys " to provide a list of the keys on the system that
* grant View permission to the caller .
2005-04-17 02:20:36 +04:00
*/
2012-02-08 19:53:04 +04:00
static struct rb_node * key_serial_next ( struct seq_file * p , struct rb_node * n )
2009-02-27 03:28:04 +03:00
{
2012-02-08 19:53:04 +04:00
struct user_namespace * user_ns = seq_user_ns ( p ) ;
2009-09-02 12:14:05 +04:00
n = rb_next ( n ) ;
2009-02-27 03:28:04 +03:00
while ( n ) {
struct key * key = rb_entry ( n , struct key , serial_node ) ;
2012-02-08 19:53:04 +04:00
if ( kuid_has_mapping ( user_ns , key - > user - > uid ) )
2009-02-27 03:28:04 +03:00
break ;
n = rb_next ( n ) ;
}
return n ;
}
2012-02-08 19:53:04 +04:00
static struct key * find_ge_key ( struct seq_file * p , key_serial_t id )
2009-02-27 03:28:04 +03:00
{
2012-02-08 19:53:04 +04:00
struct user_namespace * user_ns = seq_user_ns ( p ) ;
2009-09-02 12:14:05 +04:00
struct rb_node * n = key_serial_tree . rb_node ;
struct key * minkey = NULL ;
2009-02-27 03:28:04 +03:00
2009-09-02 12:14:05 +04:00
while ( n ) {
struct key * key = rb_entry ( n , struct key , serial_node ) ;
if ( id < key - > serial ) {
if ( ! minkey | | minkey - > serial > key - > serial )
minkey = key ;
n = n - > rb_left ;
} else if ( id > key - > serial ) {
n = n - > rb_right ;
} else {
minkey = key ;
break ;
}
key = NULL ;
}
2005-04-17 02:20:36 +04:00
2009-09-02 12:14:05 +04:00
if ( ! minkey )
return NULL ;
for ( ; ; ) {
2012-02-08 19:53:04 +04:00
if ( kuid_has_mapping ( user_ns , minkey - > user - > uid ) )
2009-09-02 12:14:05 +04:00
return minkey ;
n = rb_next ( & minkey - > serial_node ) ;
if ( ! n )
return NULL ;
minkey = rb_entry ( n , struct key , serial_node ) ;
}
2005-04-17 02:20:36 +04:00
}
static void * proc_keys_start ( struct seq_file * p , loff_t * _pos )
2009-06-18 16:00:05 +04:00
__acquires ( key_serial_lock )
2005-04-17 02:20:36 +04:00
{
2009-09-02 12:14:05 +04:00
key_serial_t pos = * _pos ;
struct key * key ;
2005-04-17 02:20:36 +04:00
spin_lock ( & key_serial_lock ) ;
2009-09-02 12:14:05 +04:00
if ( * _pos > INT_MAX )
return NULL ;
2012-02-08 19:53:04 +04:00
key = find_ge_key ( p , pos ) ;
2009-09-02 12:14:05 +04:00
if ( ! key )
return NULL ;
* _pos = key - > serial ;
return & key - > serial_node ;
}
2005-04-17 02:20:36 +04:00
2009-09-02 12:14:05 +04:00
static inline key_serial_t key_node_serial ( struct rb_node * n )
{
struct key * key = rb_entry ( n , struct key , serial_node ) ;
return key - > serial ;
2005-04-17 02:20:36 +04:00
}
static void * proc_keys_next ( struct seq_file * p , void * v , loff_t * _pos )
{
2009-09-02 12:14:05 +04:00
struct rb_node * n ;
2005-04-17 02:20:36 +04:00
2012-02-08 19:53:04 +04:00
n = key_serial_next ( p , v ) ;
2009-09-02 12:14:05 +04:00
if ( n )
* _pos = key_node_serial ( n ) ;
return n ;
2005-04-17 02:20:36 +04:00
}
static void proc_keys_stop ( struct seq_file * p , void * v )
2009-06-18 16:00:05 +04:00
__releases ( key_serial_lock )
2005-04-17 02:20:36 +04:00
{
spin_unlock ( & key_serial_lock ) ;
}
static int proc_keys_show ( struct seq_file * m , void * v )
{
struct rb_node * _p = v ;
struct key * key = rb_entry ( _p , struct key , serial_node ) ;
2017-09-27 22:50:46 +03:00
unsigned long flags ;
2010-06-11 20:31:10 +04:00
key_ref_t key_ref , skey_ref ;
2017-11-15 19:38:45 +03:00
time64_t now , expiry ;
2016-10-26 17:01:54 +03:00
char xbuf [ 16 ] ;
2017-10-04 18:43:25 +03:00
short state ;
2017-11-15 19:38:45 +03:00
u64 timo ;
2006-06-26 11:24:56 +04:00
int rc ;
2013-09-24 13:35:15 +04:00
struct keyring_search_context ctx = {
KEYS: always initialize keyring_index_key::desc_len
syzbot hit the 'BUG_ON(index_key->desc_len == 0);' in __key_link_begin()
called from construct_alloc_key() during sys_request_key(), because the
length of the key description was never calculated.
The problem is that we rely on ->desc_len being initialized by
search_process_keyrings(), specifically by search_nested_keyrings().
But, if the process isn't subscribed to any keyrings that never happens.
Fix it by always initializing keyring_index_key::desc_len as soon as the
description is set, like we already do in some places.
The following program reproduces the BUG_ON() when it's run as root and
no session keyring has been installed. If it doesn't work, try removing
pam_keyinit.so from /etc/pam.d/login and rebooting.
#include <stdlib.h>
#include <unistd.h>
#include <keyutils.h>
int main(void)
{
int id = add_key("keyring", "syz", NULL, 0, KEY_SPEC_USER_KEYRING);
keyctl_setperm(id, KEY_OTH_WRITE);
setreuid(5000, 5000);
request_key("user", "desc", "", id);
}
Reported-by: syzbot+ec24e95ea483de0a24da@syzkaller.appspotmail.com
Fixes: b2a4df200d57 ("KEYS: Expand the capacity of a keyring")
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: James Morris <james.morris@microsoft.com>
2019-02-22 18:36:18 +03:00
. index_key = key - > index_key ,
2017-09-18 21:38:29 +03:00
. cred = m - > file - > f_cred ,
2014-09-16 20:36:02 +04:00
. match_data . cmp = lookup_user_key_possessed ,
. match_data . raw_data = key ,
. match_data . lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT ,
. flags = KEYRING_SEARCH_NO_STATE_CHECK ,
2013-09-24 13:35:15 +04:00
} ;
2010-06-11 20:31:10 +04:00
key_ref = make_key_ref ( key , 0 ) ;
/* determine if the key is possessed by this process (a test we can
* skip if the key does not indicate the possessor can view it
*/
if ( key - > perm & KEY_POS_VIEW ) {
2013-09-24 13:35:15 +04:00
skey_ref = search_my_process_keyrings ( & ctx ) ;
2010-06-11 20:31:10 +04:00
if ( ! IS_ERR ( skey_ref ) ) {
key_ref_put ( skey_ref ) ;
key_ref = make_key_ref ( key , 1 ) ;
}
}
2017-09-18 21:38:29 +03:00
/* check whether the current task is allowed to view the key */
2014-03-14 21:44:49 +04:00
rc = key_task_permission ( key_ref , ctx . cred , KEY_NEED_VIEW ) ;
2006-06-26 11:24:56 +04:00
if ( rc < 0 )
return 0 ;
2005-04-17 02:20:36 +04:00
2017-11-15 19:38:45 +03:00
now = ktime_get_real_seconds ( ) ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:00:49 +04:00
rcu_read_lock ( ) ;
2005-04-17 02:20:36 +04:00
/* come up with a suitable timeout value */
2017-09-27 22:50:46 +03:00
expiry = READ_ONCE ( key - > expiry ) ;
if ( expiry = = 0 ) {
2005-04-17 02:20:36 +04:00
memcpy ( xbuf , " perm " , 5 ) ;
2017-11-15 19:38:45 +03:00
} else if ( now > = expiry ) {
2005-04-17 02:20:36 +04:00
memcpy ( xbuf , " expd " , 5 ) ;
2009-09-02 12:14:11 +04:00
} else {
2017-11-15 19:38:45 +03:00
timo = expiry - now ;
2005-04-17 02:20:36 +04:00
if ( timo < 60 )
2017-11-15 19:38:45 +03:00
sprintf ( xbuf , " %llus " , timo ) ;
2005-04-17 02:20:36 +04:00
else if ( timo < 60 * 60 )
2017-11-15 19:38:45 +03:00
sprintf ( xbuf , " %llum " , div_u64 ( timo , 60 ) ) ;
2005-04-17 02:20:36 +04:00
else if ( timo < 60 * 60 * 24 )
2017-11-15 19:38:45 +03:00
sprintf ( xbuf , " %lluh " , div_u64 ( timo , 60 * 60 ) ) ;
2005-04-17 02:20:36 +04:00
else if ( timo < 60 * 60 * 24 * 7 )
2017-11-15 19:38:45 +03:00
sprintf ( xbuf , " %llud " , div_u64 ( timo , 60 * 60 * 24 ) ) ;
2005-04-17 02:20:36 +04:00
else
2017-11-15 19:38:45 +03:00
sprintf ( xbuf , " %lluw " , div_u64 ( timo , 60 * 60 * 24 * 7 ) ) ;
2005-04-17 02:20:36 +04:00
}
2017-10-04 18:43:25 +03:00
state = key_read_state ( key ) ;
2017-09-27 22:50:46 +03:00
# define showflag(FLAGS, LETTER, FLAG) \
( ( FLAGS & ( 1 < < FLAG ) ) ? LETTER : ' - ' )
2005-06-24 09:00:49 +04:00
2017-09-27 22:50:46 +03:00
flags = READ_ONCE ( key - > flags ) ;
2012-05-11 13:56:56 +04:00
seq_printf ( m , " %08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s " ,
2005-04-17 02:20:36 +04:00
key - > serial ,
2017-10-04 18:43:25 +03:00
state ! = KEY_IS_UNINSTANTIATED ? ' I ' : ' - ' ,
2017-09-27 22:50:46 +03:00
showflag ( flags , ' R ' , KEY_FLAG_REVOKED ) ,
showflag ( flags , ' D ' , KEY_FLAG_DEAD ) ,
showflag ( flags , ' Q ' , KEY_FLAG_IN_QUOTA ) ,
showflag ( flags , ' U ' , KEY_FLAG_USER_CONSTRUCT ) ,
2017-10-04 18:43:25 +03:00
state < 0 ? ' N ' : ' - ' ,
2017-09-27 22:50:46 +03:00
showflag ( flags , ' i ' , KEY_FLAG_INVALIDATED ) ,
2017-03-31 15:20:48 +03:00
refcount_read ( & key - > usage ) ,
2005-04-17 02:20:36 +04:00
xbuf ,
key - > perm ,
2012-02-08 19:53:04 +04:00
from_kuid_munged ( seq_user_ns ( m ) , key - > uid ) ,
from_kgid_munged ( seq_user_ns ( m ) , key - > gid ) ,
2005-04-17 02:20:36 +04:00
key - > type - > name ) ;
2005-06-24 09:00:49 +04:00
# undef showflag
2005-04-17 02:20:36 +04:00
if ( key - > type - > describe )
key - > type - > describe ( key , m ) ;
seq_putc ( m , ' \n ' ) ;
2005-06-24 09:00:49 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-02-08 19:53:04 +04:00
static struct rb_node * __key_user_next ( struct user_namespace * user_ns , struct rb_node * n )
2009-02-27 03:28:04 +03:00
{
while ( n ) {
struct key_user * user = rb_entry ( n , struct key_user , node ) ;
2012-02-08 19:53:04 +04:00
if ( kuid_has_mapping ( user_ns , user - > uid ) )
2009-02-27 03:28:04 +03:00
break ;
n = rb_next ( n ) ;
}
return n ;
}
2012-02-08 19:53:04 +04:00
static struct rb_node * key_user_next ( struct user_namespace * user_ns , struct rb_node * n )
2009-02-27 03:28:04 +03:00
{
2012-02-08 19:53:04 +04:00
return __key_user_next ( user_ns , rb_next ( n ) ) ;
2009-02-27 03:28:04 +03:00
}
2012-02-08 19:53:04 +04:00
static struct rb_node * key_user_first ( struct user_namespace * user_ns , struct rb_root * r )
2009-02-27 03:28:04 +03:00
{
struct rb_node * n = rb_first ( r ) ;
2012-02-08 19:53:04 +04:00
return __key_user_next ( user_ns , n ) ;
2009-02-27 03:28:04 +03:00
}
2009-09-02 12:14:11 +04:00
2005-04-17 02:20:36 +04:00
static void * proc_key_users_start ( struct seq_file * p , loff_t * _pos )
2009-06-18 16:00:05 +04:00
__acquires ( key_user_lock )
2005-04-17 02:20:36 +04:00
{
struct rb_node * _p ;
loff_t pos = * _pos ;
spin_lock ( & key_user_lock ) ;
2012-02-08 19:53:04 +04:00
_p = key_user_first ( seq_user_ns ( p ) , & key_user_tree ) ;
2005-04-17 02:20:36 +04:00
while ( pos > 0 & & _p ) {
pos - - ;
2012-02-08 19:53:04 +04:00
_p = key_user_next ( seq_user_ns ( p ) , _p ) ;
2005-04-17 02:20:36 +04:00
}
return _p ;
}
static void * proc_key_users_next ( struct seq_file * p , void * v , loff_t * _pos )
{
( * _pos ) + + ;
2012-02-08 19:53:04 +04:00
return key_user_next ( seq_user_ns ( p ) , ( struct rb_node * ) v ) ;
2005-04-17 02:20:36 +04:00
}
static void proc_key_users_stop ( struct seq_file * p , void * v )
2009-06-18 16:00:05 +04:00
__releases ( key_user_lock )
2005-04-17 02:20:36 +04:00
{
spin_unlock ( & key_user_lock ) ;
}
static int proc_key_users_show ( struct seq_file * m , void * v )
{
struct rb_node * _p = v ;
struct key_user * user = rb_entry ( _p , struct key_user , node ) ;
2012-02-08 19:53:04 +04:00
unsigned maxkeys = uid_eq ( user - > uid , GLOBAL_ROOT_UID ) ?
2008-04-29 12:01:32 +04:00
key_quota_root_maxkeys : key_quota_maxkeys ;
2012-02-08 19:53:04 +04:00
unsigned maxbytes = uid_eq ( user - > uid , GLOBAL_ROOT_UID ) ?
2008-04-29 12:01:32 +04:00
key_quota_root_maxbytes : key_quota_maxbytes ;
2005-04-17 02:20:36 +04:00
seq_printf ( m , " %5u: %5d %d/%d %d/%d %d/%d \n " ,
2012-02-08 19:53:04 +04:00
from_kuid_munged ( seq_user_ns ( m ) , user - > uid ) ,
2017-03-31 15:20:49 +03:00
refcount_read ( & user - > usage ) ,
2005-04-17 02:20:36 +04:00
atomic_read ( & user - > nkeys ) ,
atomic_read ( & user - > nikeys ) ,
user - > qnkeys ,
2008-04-29 12:01:32 +04:00
maxkeys ,
2005-04-17 02:20:36 +04:00
user - > qnbytes ,
2008-04-29 12:01:32 +04:00
maxbytes ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}