@ -1,6 +1,6 @@
/* request_key.c: r equest a key from userspace
/* R equest a key from userspace
*
* Copyright ( C ) 2004 - 6 Red Hat , Inc . All Rights Reserved .
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
@ -18,27 +18,54 @@
# include <linux/keyctl.h>
# include "internal.h"
struct key_construction {
struct list_head link ; /* link in construction queue */
struct key * key ; /* key being constructed */
} ;
/*
* wait_on_bit ( ) sleep function for uninterruptible waiting
*/
static int key_wait_bit ( void * flags )
{
schedule ( ) ;
return 0 ;
}
/*
* wait_on_bit ( ) sleep function for interruptible waiting
*/
static int key_wait_bit_intr ( void * flags )
{
schedule ( ) ;
return signal_pending ( current ) ? - ERESTARTSYS : 0 ;
}
/*
* call to complete the construction of a key
*/
void complete_request_key ( struct key_construction * cons , int error )
{
kenter ( " {%d,%d},%d " , cons - > key - > serial , cons - > authkey - > serial , error ) ;
/* when waiting for someone else's keys, you get added to this */
DECLARE_WAIT_QUEUE_HEAD ( request_key_conswq ) ;
if ( error < 0 )
key_negate_and_link ( cons - > key , key_negative_timeout , NULL ,
cons - > authkey ) ;
else
key_revoke ( cons - > authkey ) ;
key_put ( cons - > key ) ;
key_put ( cons - > authkey ) ;
kfree ( cons ) ;
}
EXPORT_SYMBOL ( complete_request_key ) ;
/*****************************************************************************/
/*
* request userspace finish the construction of a key
* - execute " /sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> "
*/
static int call_sbin_request_key ( struct key * key ,
struct key * authkey ,
static int call_sbin_request_key ( struct key_construction * cons ,
const char * op ,
void * aux )
{
struct task_struct * tsk = current ;
key_serial_t prkey , sskey ;
struct key * keyring ;
struct key * key = cons - > key , * authkey = cons - > authkey , * key ring ;
char * argv [ 9 ] , * envp [ 3 ] , uid_str [ 12 ] , gid_str [ 12 ] ;
char key_str [ 12 ] , keyring_str [ 3 ] [ 12 ] ;
char desc [ 20 ] ;
@ -82,8 +109,7 @@ static int call_sbin_request_key(struct key *key,
rcu_read_lock ( ) ;
sskey = rcu_dereference ( tsk - > signal - > session_keyring ) - > serial ;
rcu_read_unlock ( ) ;
}
else {
} else {
sskey = tsk - > user - > session_keyring - > serial ;
}
@ -110,228 +136,77 @@ static int call_sbin_request_key(struct key *key,
/* do it */
ret = call_usermodehelper_keys ( argv [ 0 ] , argv , envp , keyring ,
UMH_WAIT_PROC ) ;
kdebug ( " usermode -> 0x%x " , ret ) ;
if ( ret > = 0 ) {
/* ret is the exit/wait code */
if ( test_bit ( KEY_FLAG_USER_CONSTRUCT , & key - > flags ) | |
key_validate ( key ) < 0 )
ret = - ENOKEY ;
else
/* ignore any errors from userspace if the key was
* instantiated */
ret = 0 ;
}
error_link :
key_put ( keyring ) ;
error_alloc :
kleave ( " = %d " , ret ) ;
complete_request_key ( cons , ret ) ;
return ret ;
}
} /* end call_sbin_request_key() */
/*****************************************************************************/
/*
* call out to userspace for the key
* - called with the construction sem held , but the sem is dropped here
* call out to userspace for key construction
* - we ignore program failure and go on key status instead
*/
static struct key * __request_key_construction ( struct key_type * type ,
const char * description ,
const char * callout_info ,
void * aux ,
unsigned long flags )
static int construct_key ( struct key * key , const char * callout_info , void * aux )
{
struct key_construction * cons ;
request_key_actor_t actor ;
struct key_construction cons ;
struct timespec now ;
struct key * key , * authkey ;
int ret , negated ;
struct key * authkey ;
int ret ;
kenter ( " %s,%s,%s,%lx " , type - > name , description , callout_info , flags ) ;
kenter ( " %d,%s,%p " , key - > serial , callout_info , aux ) ;
/* create a key and add it to the queue */
key = key_alloc ( type , description ,
current - > fsuid , current - > fsgid , current , KEY_POS_ALL ,
flags ) ;
if ( IS_ERR ( key ) )
goto alloc_failed ;
set_bit ( KEY_FLAG_USER_CONSTRUCT , & key - > flags ) ;
cons . key = key ;
list_add_tail ( & cons . link , & key - > user - > consq ) ;
/* we drop the construction sem here on behalf of the caller */
up_write ( & key_construction_sem ) ;
cons = kmalloc ( sizeof ( * cons ) , GFP_KERNEL ) ;
if ( ! cons )
return - ENOMEM ;
/* allocate an authorisation key */
authkey = request_key_auth_new ( key , callout_info ) ;
if ( IS_ERR ( authkey ) ) {
kfree ( cons ) ;
ret = PTR_ERR ( authkey ) ;
authkey = NULL ;
goto alloc_authkey_failed ;
}
/* make the call */
actor = call_sbin_request_key ;
if ( type - > request_key )
actor = type - > request_key ;
ret = actor ( key , authkey , " create " , aux ) ;
if ( ret < 0 )
goto request_failed ;
/* if the key wasn't instantiated, then we want to give an error */
ret = - ENOKEY ;
if ( ! test_bit ( KEY_FLAG_INSTANTIATED , & key - > flags ) )
goto request_failed ;
key_revoke ( authkey ) ;
key_put ( authkey ) ;
down_write ( & key_construction_sem ) ;
list_del ( & cons . link ) ;
up_write ( & key_construction_sem ) ;
/* also give an error if the key was negatively instantiated */
check_not_negative :
if ( test_bit ( KEY_FLAG_NEGATIVE , & key - > flags ) ) {
key_put ( key ) ;
key = ERR_PTR ( - ENOKEY ) ;
}
out :
kleave ( " = %p " , key ) ;
return key ;
request_failed :
key_revoke ( authkey ) ;
key_put ( authkey ) ;
alloc_authkey_failed :
/* it wasn't instantiated
* - remove from construction queue
* - mark the key as dead
*/
negated = 0 ;
down_write ( & key_construction_sem ) ;
list_del ( & cons . link ) ;
/* check it didn't get instantiated between the check and the down */
if ( ! test_bit ( KEY_FLAG_INSTANTIATED , & key - > flags ) ) {
set_bit ( KEY_FLAG_NEGATIVE , & key - > flags ) ;
set_bit ( KEY_FLAG_INSTANTIATED , & key - > flags ) ;
negated = 1 ;
}
clear_bit ( KEY_FLAG_USER_CONSTRUCT , & key - > flags ) ;
up_write ( & key_construction_sem ) ;
if ( ! negated )
goto check_not_negative ; /* surprisingly, the key got
* instantiated */
/* set the timeout and store in the session keyring if we can */
now = current_kernel_time ( ) ;
key - > expiry = now . tv_sec + key_negative_timeout ;
if ( current - > signal - > session_keyring ) {
struct key * keyring ;
rcu_read_lock ( ) ;
keyring = rcu_dereference ( current - > signal - > session_keyring ) ;
atomic_inc ( & keyring - > usage ) ;
rcu_read_unlock ( ) ;
key_link ( keyring , key ) ;
key_put ( keyring ) ;
}
key_put ( key ) ;
/* notify anyone who was waiting */
wake_up_all ( & request_key_conswq ) ;
key = ERR_PTR ( ret ) ;
goto out ;
alloc_failed :
up_write ( & key_construction_sem ) ;
goto out ;
} /* end __request_key_construction() */
/*****************************************************************************/
/*
* call out to userspace to request the key
* - we check the construction queue first to see if an appropriate key is
* already being constructed by userspace
*/
static struct key * request_key_construction ( struct key_type * type ,
const char * description ,
const char * callout_info ,
void * aux ,
struct key_user * user ,
unsigned long flags )
{
struct key_construction * pcons ;
struct key * key , * ckey ;
DECLARE_WAITQUEUE ( myself , current ) ;
kenter ( " %s,%s,{%d},%s,%lx " ,
type - > name , description , user - > uid , callout_info , flags ) ;
/* see if there's such a key under construction already */
down_write ( & key_construction_sem ) ;
list_for_each_entry ( pcons , & user - > consq , link ) {
ckey = pcons - > key ;
if ( ckey - > type ! = type )
continue ;
if ( type - > match ( ckey , description ) )
goto found_key_under_construction ;
} else {
cons - > authkey = key_get ( authkey ) ;
cons - > key = key_get ( key ) ;
/* make the call */
actor = call_sbin_request_key ;
if ( key - > type - > request_key )
actor = key - > type - > request_key ;
ret = actor ( cons , " create " , aux ) ;
/* check that the actor called complete_request_key() prior to
* returning an error */
WARN_ON ( ret < 0 & &
! test_bit ( KEY_FLAG_REVOKED , & authkey - > flags ) ) ;
key_put ( authkey ) ;
}
/* see about getting userspace to construct the key */
key = __request_key_construction ( type , description , callout_info , aux ,
flags ) ;
error :
kleave ( " = %p " , key ) ;
return key ;
/* someone else has the same key under construction
* - we want to keep an eye on their key
*/
found_key_under_construction :
atomic_inc ( & ckey - > usage ) ;
up_write ( & key_construction_sem ) ;
/* wait for the key to be completed one way or another */
add_wait_queue ( & request_key_conswq , & myself ) ;
for ( ; ; ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( ! test_bit ( KEY_FLAG_USER_CONSTRUCT , & ckey - > flags ) )
break ;
if ( signal_pending ( current ) )
break ;
schedule ( ) ;
}
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & request_key_conswq , & myself ) ;
/* we'll need to search this process's keyrings to see if the key is
* now there since we can ' t automatically assume it ' s also available
* there */
key_put ( ckey ) ;
ckey = NULL ;
key = NULL ; /* request a retry */
goto error ;
} /* end request_key_construction() */
kleave ( " = %d " , ret ) ;
return ret ;
}
/*****************************************************************************/
/*
* link a freshly minted key to an appropriate destination keyring
* link a key to the appropriate destination keyring
* - the caller must hold a write lock on the destination keyring
*/
static void request_key _link( struct key * key , struct key * dest_keyring )
static void construct_key_make_link ( struct key * key , struct key * dest_keyring )
{
struct task_struct * tsk = current ;
struct key * drop = NULL ;
@ -363,11 +238,11 @@ static void request_key_link(struct key *key, struct key *dest_keyring)
break ;
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING :
dest_keyring = curren t- > user - > session_keyring ;
dest_keyring = tsk - > user - > session_keyring ;
break ;
case KEY_REQKEY_DEFL_USER_KEYRING :
dest_keyring = curren t- > user - > uid_keyring ;
dest_keyring = tsk - > user - > uid_keyring ;
break ;
case KEY_REQKEY_DEFL_GROUP_KEYRING :
@ -377,15 +252,115 @@ static void request_key_link(struct key *key, struct key *dest_keyring)
}
/* and attach the key to it */
key_link ( dest_keyring , key ) ;
__key_link ( dest_keyring , key ) ;
key_put ( drop ) ;
kleave ( " " ) ;
}
} /* end request_key_link() */
/*
* allocate a new key in under - construction state and attempt to link it in to
* the requested place
* - may return a key that ' s already under construction instead
*/
static int construct_alloc_key ( struct key_type * type ,
const char * description ,
struct key * dest_keyring ,
unsigned long flags ,
struct key_user * user ,
struct key * * _key )
{
struct key * key ;
key_ref_t key_ref ;
kenter ( " %s,%s,,, " , type - > name , description ) ;
mutex_lock ( & user - > cons_lock ) ;
key = key_alloc ( type , description ,
current - > fsuid , current - > fsgid , current , KEY_POS_ALL ,
flags ) ;
if ( IS_ERR ( key ) )
goto alloc_failed ;
set_bit ( KEY_FLAG_USER_CONSTRUCT , & key - > flags ) ;
if ( dest_keyring )
down_write ( & dest_keyring - > sem ) ;
/* attach the key to the destination keyring under lock, but we do need
* to do another check just in case someone beat us to it whilst we
* waited for locks */
mutex_lock ( & key_construction_mutex ) ;
key_ref = search_process_keyrings ( type , description , type - > match ,
current ) ;
if ( ! IS_ERR ( key_ref ) )
goto key_already_present ;
if ( dest_keyring )
construct_key_make_link ( key , dest_keyring ) ;
mutex_unlock ( & key_construction_mutex ) ;
if ( dest_keyring )
up_write ( & dest_keyring - > sem ) ;
mutex_unlock ( & user - > cons_lock ) ;
* _key = key ;
kleave ( " = 0 [%d] " , key_serial ( key ) ) ;
return 0 ;
key_already_present :
mutex_unlock ( & key_construction_mutex ) ;
if ( dest_keyring )
up_write ( & dest_keyring - > sem ) ;
mutex_unlock ( & user - > cons_lock ) ;
key_put ( key ) ;
* _key = key = key_ref_to_ptr ( key_ref ) ;
kleave ( " = -EINPROGRESS [%d] " , key_serial ( key ) ) ;
return - EINPROGRESS ;
alloc_failed :
mutex_unlock ( & user - > cons_lock ) ;
* _key = NULL ;
kleave ( " = %ld " , PTR_ERR ( key ) ) ;
return PTR_ERR ( key ) ;
}
/*
* commence key construction
*/
static struct key * construct_key_and_link ( struct key_type * type ,
const char * description ,
const char * callout_info ,
void * aux ,
struct key * dest_keyring ,
unsigned long flags )
{
struct key_user * user ;
struct key * key ;
int ret ;
user = key_user_lookup ( current - > fsuid ) ;
if ( ! user )
return ERR_PTR ( - ENOMEM ) ;
ret = construct_alloc_key ( type , description , dest_keyring , flags , user ,
& key ) ;
key_user_put ( user ) ;
if ( ret = = 0 ) {
ret = construct_key ( key , callout_info , aux ) ;
if ( ret < 0 )
goto construction_failed ;
}
return key ;
construction_failed :
key_negate_and_link ( key , key_negative_timeout , NULL , NULL ) ;
key_put ( key ) ;
return ERR_PTR ( ret ) ;
}
/*****************************************************************************/
/*
* request a key
* - search the process ' s keyrings
@ -400,7 +375,6 @@ struct key *request_key_and_link(struct key_type *type,
struct key * dest_keyring ,
unsigned long flags )
{
struct key_user * user ;
struct key * key ;
key_ref_t key_ref ;
@ -412,112 +386,124 @@ struct key *request_key_and_link(struct key_type *type,
key_ref = search_process_keyrings ( type , description , type - > match ,
current ) ;
kdebug ( " search 1: %p " , key_ref ) ;
if ( ! IS_ERR ( key_ref ) ) {
key = key_ref_to_ptr ( key_ref ) ;
}
else if ( PTR_ERR ( key_ref ) ! = - EAGAIN ) {
} else if ( PTR_ERR ( key_ref ) ! = - EAGAIN ) {
key = ERR_PTR ( PTR_ERR ( key_ref ) ) ;
}
else {
} else {
/* the search failed, but the keyrings were searchable, so we
* should consult userspace if we can */
key = ERR_PTR ( - ENOKEY ) ;
if ( ! callout_info )
goto error ;
/* - get hold of the user's construction queue */
user = key_user_lookup ( current - > fsuid ) ;
if ( ! user )
goto nomem ;
for ( ; ; ) {
if ( signal_pending ( current ) )
goto interrupted ;
/* ask userspace (returns NULL if it waited on a key
* being constructed ) */
key = request_key_construction ( type , description ,
callout_info , aux ,
user , flags ) ;
if ( key )
break ;
/* someone else made the key we want, so we need to
* search again as it might now be available to us */
key_ref = search_process_keyrings ( type , description ,
type - > match ,
current ) ;
kdebug ( " search 2: %p " , key_ref ) ;
if ( ! IS_ERR ( key_ref ) ) {
key = key_ref_to_ptr ( key_ref ) ;
break ;
}
if ( PTR_ERR ( key_ref ) ! = - EAGAIN ) {
key = ERR_PTR ( PTR_ERR ( key_ref ) ) ;
break ;
}
}
key_user_put ( user ) ;
/* link the new key into the appropriate keyring */
if ( ! IS_ERR ( key ) )
request_key_link ( key , dest_keyring ) ;
key = construct_key_and_link ( type , description , callout_info ,
aux , dest_keyring , flags ) ;
}
error :
kleave ( " = %p " , key ) ;
return key ;
}
nomem :
key = ERR_PTR ( - ENOMEM ) ;
goto error ;
interrupted :
key_user_put ( user ) ;
key = ERR_PTR ( - EINTR ) ;
goto error ;
/*
* wait for construction of a key to complete
*/
int wait_for_key_construction ( struct key * key , bool intr )
{
int ret ;
} /* end request_key_and_link() */
ret = wait_on_bit ( & key - > flags , KEY_FLAG_USER_CONSTRUCT ,
intr ? key_wait_bit_intr : key_wait_bit ,
intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE ) ;
if ( ret < 0 )
return ret ;
return key_validate ( key ) ;
}
EXPORT_SYMBOL ( wait_for_key_construction ) ;
/*****************************************************************************/
/*
* request a key
* - search the process ' s keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
* - waits uninterruptible for creation to complete
*/
struct key * request_key ( struct key_type * type ,
const char * description ,
const char * callout_info )
{
return request_key_and_link ( type , description , callout_info , NULL ,
NULL , KEY_ALLOC_IN_QUOTA ) ;
} /* end request_key() */
struct key * key ;
int ret ;
key = request_key_and_link ( type , description , callout_info , NULL ,
NULL , KEY_ALLOC_IN_QUOTA ) ;
if ( ! IS_ERR ( key ) ) {
ret = wait_for_key_construction ( key , false ) ;
if ( ret < 0 ) {
key_put ( key ) ;
return ERR_PTR ( ret ) ;
}
}
return key ;
}
EXPORT_SYMBOL ( request_key ) ;
/*****************************************************************************/
/*
* request a key with auxiliary data for the upcaller
* - search the process ' s keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
* - waits uninterruptible for creation to complete
*/
struct key * request_key_with_auxdata ( struct key_type * type ,
const char * description ,
const char * callout_info ,
void * aux )
{
return request_key_and_link ( type , description , callout_info , aux ,
NULL , KEY_ALLOC_IN_QUOTA ) ;
struct key * key ;
int ret ;
key = request_key_and_link ( type , description , callout_info , aux ,
NULL , KEY_ALLOC_IN_QUOTA ) ;
if ( ! IS_ERR ( key ) ) {
ret = wait_for_key_construction ( key , false ) ;
if ( ret < 0 ) {
key_put ( key ) ;
return ERR_PTR ( ret ) ;
}
}
return key ;
}
EXPORT_SYMBOL ( request_key_with_auxdata ) ;
} /* end request_key_with_auxdata() */
/*
* request a key ( allow async construction )
* - search the process ' s keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
*/
struct key * request_key_async ( struct key_type * type ,
const char * description ,
const char * callout_info )
{
return request_key_and_link ( type , description , callout_info , NULL ,
NULL , KEY_ALLOC_IN_QUOTA ) ;
}
EXPORT_SYMBOL ( request_key_async ) ;
EXPORT_SYMBOL ( request_key_with_auxdata ) ;
/*
* request a key with auxiliary data for the upcaller ( allow async construction )
* - search the process ' s keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
*/
struct key * request_key_async_with_auxdata ( struct key_type * type ,
const char * description ,
const char * callout_info ,
void * aux )
{
return request_key_and_link ( type , description , callout_info , aux ,
NULL , KEY_ALLOC_IN_QUOTA ) ;
}
EXPORT_SYMBOL ( request_key_async_with_auxdata ) ;