2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* linux / net / sunrpc / auth_gss / auth_gss . c
2005-04-17 02:20:36 +04:00
*
* RPCSEC_GSS client authentication .
2007-02-10 02:38:13 +03:00
*
2005-04-17 02:20:36 +04:00
* Copyright ( c ) 2000 The Regents of the University of Michigan .
* All rights reserved .
*
* Dug Song < dugsong @ monkey . org >
* Andy Adamson < andros @ umich . edu >
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
* BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/sched.h>
2005-10-14 00:54:58 +04:00
# include <linux/pagemap.h>
2005-04-17 02:20:36 +04:00
# include <linux/sunrpc/clnt.h>
# include <linux/sunrpc/auth.h>
# include <linux/sunrpc/auth_gss.h>
# include <linux/sunrpc/svcauth_gss.h>
# include <linux/sunrpc/gss_err.h>
# include <linux/workqueue.h>
# include <linux/sunrpc/rpc_pipe_fs.h>
# include <linux/sunrpc/gss_api.h>
# include <asm/uaccess.h>
2013-05-15 22:28:54 +04:00
# include "../netns.h"
2007-06-24 04:17:58 +04:00
static const struct rpc_authops authgss_ops ;
2005-04-17 02:20:36 +04:00
2007-06-24 04:17:58 +04:00
static const struct rpc_credops gss_credops ;
2007-06-27 01:04:57 +04:00
static const struct rpc_credops gss_nullops ;
2005-04-17 02:20:36 +04:00
2010-05-13 20:55:38 +04:00
# define GSS_RETRY_EXPIRED 5
static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED ;
2005-04-17 02:20:36 +04:00
# ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
# endif
2010-03-17 20:02:46 +03:00
# define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2)
2005-04-17 02:20:36 +04:00
/* length of a krb5 verifier (48), plus data added before arguments when
* using integrity ( two 4 - byte integers ) : */
2006-12-05 04:22:34 +03:00
# define GSS_VERF_SLACK 100
2005-04-17 02:20:36 +04:00
struct gss_auth {
2007-06-27 22:29:12 +04:00
struct kref kref ;
2005-04-17 02:20:36 +04:00
struct rpc_auth rpc_auth ;
struct gss_api_mech * mech ;
enum rpc_gss_svc service ;
struct rpc_clnt * client ;
2008-12-24 00:16:37 +03:00
/*
* There are two upcall pipes ; dentry [ 1 ] , named " gssd " , is used
* for the new text - based upcall ; dentry [ 0 ] is named after the
* mechanism ( for example , " krb5 " ) and exists for
* backwards - compatibility with older gssd ' s .
*/
2011-12-26 16:44:06 +04:00
struct rpc_pipe * pipe [ 2 ] ;
2005-04-17 02:20:36 +04:00
} ;
2008-12-24 00:10:52 +03:00
/* pipe_version >= 0 if and only if someone has a pipe open. */
static DEFINE_SPINLOCK ( pipe_version_lock ) ;
static struct rpc_wait_queue pipe_version_rpc_waitqueue ;
static DECLARE_WAIT_QUEUE_HEAD ( pipe_version_waitqueue ) ;
2008-12-24 00:10:19 +03:00
2007-06-27 03:18:38 +04:00
static void gss_free_ctx ( struct gss_cl_ctx * ) ;
2009-08-09 23:14:15 +04:00
static const struct rpc_pipe_ops gss_upcall_ops_v0 ;
static const struct rpc_pipe_ops gss_upcall_ops_v1 ;
2005-04-17 02:20:36 +04:00
static inline struct gss_cl_ctx *
gss_get_ctx ( struct gss_cl_ctx * ctx )
{
atomic_inc ( & ctx - > count ) ;
return ctx ;
}
static inline void
gss_put_ctx ( struct gss_cl_ctx * ctx )
{
if ( atomic_dec_and_test ( & ctx - > count ) )
2007-06-27 03:18:38 +04:00
gss_free_ctx ( ctx ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-27 03:18:38 +04:00
/* gss_cred_set_ctx:
* called by gss_upcall_callback and gss_create_upcall in order
* to set the gss context . The actual exchange of an old context
2011-12-26 16:43:57 +04:00
* and a new one is protected by the pipe - > lock .
2007-06-27 03:18:38 +04:00
*/
2005-04-17 02:20:36 +04:00
static void
gss_cred_set_ctx ( struct rpc_cred * cred , struct gss_cl_ctx * ctx )
{
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred , gc_base ) ;
2007-06-27 03:18:38 +04:00
2008-04-18 01:03:58 +04:00
if ( ! test_bit ( RPCAUTH_CRED_NEW , & cred - > cr_flags ) )
return ;
2008-04-18 00:53:01 +04:00
gss_get_ctx ( ctx ) ;
2012-01-12 08:41:32 +04:00
rcu_assign_pointer ( gss_cred - > gc_ctx , ctx ) ;
2007-06-25 18:15:15 +04:00
set_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ;
2008-04-18 01:03:58 +04:00
smp_mb__before_clear_bit ( ) ;
2007-06-25 18:15:15 +04:00
clear_bit ( RPCAUTH_CRED_NEW , & cred - > cr_flags ) ;
2005-04-17 02:20:36 +04:00
}
static const void *
simple_get_bytes ( const void * p , const void * end , void * res , size_t len )
{
const void * q = ( const void * ) ( ( const char * ) p + len ) ;
if ( unlikely ( q > end | | q < p ) )
return ERR_PTR ( - EFAULT ) ;
memcpy ( res , p , len ) ;
return q ;
}
static inline const void *
simple_get_netobj ( const void * p , const void * end , struct xdr_netobj * dest )
{
const void * q ;
unsigned int len ;
p = simple_get_bytes ( p , end , & len , sizeof ( len ) ) ;
if ( IS_ERR ( p ) )
return p ;
q = ( const void * ) ( ( const char * ) p + len ) ;
if ( unlikely ( q > end | | q < p ) )
return ERR_PTR ( - EFAULT ) ;
2008-06-11 02:31:01 +04:00
dest - > data = kmemdup ( p , len , GFP_NOFS ) ;
2005-04-17 02:20:36 +04:00
if ( unlikely ( dest - > data = = NULL ) )
return ERR_PTR ( - ENOMEM ) ;
dest - > len = len ;
return q ;
}
static struct gss_cl_ctx *
gss_cred_get_ctx ( struct rpc_cred * cred )
{
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred , gc_base ) ;
struct gss_cl_ctx * ctx = NULL ;
2007-06-27 03:18:38 +04:00
rcu_read_lock ( ) ;
2005-04-17 02:20:36 +04:00
if ( gss_cred - > gc_ctx )
ctx = gss_get_ctx ( gss_cred - > gc_ctx ) ;
2007-06-27 03:18:38 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return ctx ;
}
static struct gss_cl_ctx *
gss_alloc_context ( void )
{
struct gss_cl_ctx * ctx ;
2008-06-11 02:31:01 +04:00
ctx = kzalloc ( sizeof ( * ctx ) , GFP_NOFS ) ;
2005-04-17 02:20:36 +04:00
if ( ctx ! = NULL ) {
ctx - > gc_proc = RPC_GSS_PROC_DATA ;
ctx - > gc_seq = 1 ; /* NetApp 6.4R1 doesn't accept seq. no. 0 */
spin_lock_init ( & ctx - > gc_seq_lock ) ;
atomic_set ( & ctx - > count , 1 ) ;
}
return ctx ;
}
# define GSSD_MIN_TIMEOUT (60 * 60)
static const void *
gss_fill_context ( const void * p , const void * end , struct gss_cl_ctx * ctx , struct gss_api_mech * gm )
{
const void * q ;
unsigned int seclen ;
unsigned int timeout ;
2012-11-27 19:34:20 +04:00
unsigned long now = jiffies ;
2005-04-17 02:20:36 +04:00
u32 window_size ;
int ret ;
2012-11-27 19:34:20 +04:00
/* First unsigned int gives the remaining lifetime in seconds of the
* credential - e . g . the remaining TGT lifetime for Kerberos or
* the - t value passed to GSSD .
*/
2005-04-17 02:20:36 +04:00
p = simple_get_bytes ( p , end , & timeout , sizeof ( timeout ) ) ;
if ( IS_ERR ( p ) )
goto err ;
if ( timeout = = 0 )
timeout = GSSD_MIN_TIMEOUT ;
2012-11-27 19:34:20 +04:00
ctx - > gc_expiry = now + ( ( unsigned long ) timeout * HZ ) ;
/* Sequence number window. Determines the maximum number of
* simultaneous requests
*/
2005-04-17 02:20:36 +04:00
p = simple_get_bytes ( p , end , & window_size , sizeof ( window_size ) ) ;
if ( IS_ERR ( p ) )
goto err ;
ctx - > gc_win = window_size ;
/* gssd signals an error by passing ctx->gc_win = 0: */
if ( ctx - > gc_win = = 0 ) {
2010-01-07 17:42:02 +03:00
/*
* in which case , p points to an error code . Anything other
* than - EKEYEXPIRED gets converted to - EACCES .
*/
p = simple_get_bytes ( p , end , & ret , sizeof ( ret ) ) ;
if ( ! IS_ERR ( p ) )
p = ( ret = = - EKEYEXPIRED ) ? ERR_PTR ( - EKEYEXPIRED ) :
ERR_PTR ( - EACCES ) ;
2005-04-17 02:20:36 +04:00
goto err ;
}
/* copy the opaque wire context */
p = simple_get_netobj ( p , end , & ctx - > gc_wire_ctx ) ;
if ( IS_ERR ( p ) )
goto err ;
/* import the opaque security context */
p = simple_get_bytes ( p , end , & seclen , sizeof ( seclen ) ) ;
if ( IS_ERR ( p ) )
goto err ;
q = ( const void * ) ( ( const char * ) p + seclen ) ;
if ( unlikely ( q > end | | q < p ) ) {
p = ERR_PTR ( - EFAULT ) ;
goto err ;
}
2012-05-26 02:09:53 +04:00
ret = gss_import_sec_context ( p , seclen , gm , & ctx - > gc_gss_ctx , NULL , GFP_NOFS ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 ) {
p = ERR_PTR ( ret ) ;
goto err ;
}
2012-11-27 19:34:20 +04:00
dprintk ( " RPC: %s Success. gc_expiry %lu now %lu timeout %u \n " ,
__func__ , ctx - > gc_expiry , now , timeout ) ;
2005-04-17 02:20:36 +04:00
return q ;
err :
2013-02-07 19:29:06 +04:00
dprintk ( " RPC: %s returns error %ld \n " , __func__ , - PTR_ERR ( p ) ) ;
2005-04-17 02:20:36 +04:00
return p ;
}
2008-12-24 00:16:37 +03:00
# define UPCALL_BUF_LEN 128
2005-04-17 02:20:36 +04:00
struct gss_upcall_msg {
atomic_t count ;
2013-02-02 04:31:17 +04:00
kuid_t uid ;
2005-04-17 02:20:36 +04:00
struct rpc_pipe_msg msg ;
struct list_head list ;
struct gss_auth * auth ;
2011-12-26 16:43:57 +04:00
struct rpc_pipe * pipe ;
2005-04-17 02:20:36 +04:00
struct rpc_wait_queue rpc_waitqueue ;
wait_queue_head_t waitqueue ;
struct gss_cl_ctx * ctx ;
2008-12-24 00:16:37 +03:00
char databuf [ UPCALL_BUF_LEN ] ;
2005-04-17 02:20:36 +04:00
} ;
2013-05-15 21:27:32 +04:00
static int get_pipe_version ( struct net * net )
2008-12-24 00:10:52 +03:00
{
2013-05-15 21:27:32 +04:00
struct sunrpc_net * sn = net_generic ( net , sunrpc_net_id ) ;
2008-12-24 00:10:52 +03:00
int ret ;
spin_lock ( & pipe_version_lock ) ;
2013-05-15 21:27:32 +04:00
if ( sn - > pipe_version > = 0 ) {
atomic_inc ( & sn - > pipe_users ) ;
ret = sn - > pipe_version ;
2008-12-24 00:10:52 +03:00
} else
ret = - EAGAIN ;
spin_unlock ( & pipe_version_lock ) ;
return ret ;
}
2013-05-15 21:27:32 +04:00
static void put_pipe_version ( struct net * net )
2008-12-24 00:10:52 +03:00
{
2013-05-15 21:27:32 +04:00
struct sunrpc_net * sn = net_generic ( net , sunrpc_net_id ) ;
if ( atomic_dec_and_lock ( & sn - > pipe_users , & pipe_version_lock ) ) {
sn - > pipe_version = - 1 ;
2008-12-24 00:10:52 +03:00
spin_unlock ( & pipe_version_lock ) ;
}
}
2005-04-17 02:20:36 +04:00
static void
gss_release_msg ( struct gss_upcall_msg * gss_msg )
{
2013-05-15 21:27:32 +04:00
struct net * net = rpc_net_ns ( gss_msg - > auth - > client ) ;
2005-04-17 02:20:36 +04:00
if ( ! atomic_dec_and_test ( & gss_msg - > count ) )
return ;
2013-05-15 21:27:32 +04:00
put_pipe_version ( net ) ;
2005-04-17 02:20:36 +04:00
BUG_ON ( ! list_empty ( & gss_msg - > list ) ) ;
if ( gss_msg - > ctx ! = NULL )
gss_put_ctx ( gss_msg - > ctx ) ;
2008-02-23 01:06:55 +03:00
rpc_destroy_wait_queue ( & gss_msg - > rpc_waitqueue ) ;
2005-04-17 02:20:36 +04:00
kfree ( gss_msg ) ;
}
static struct gss_upcall_msg *
2013-02-02 04:31:17 +04:00
__gss_find_upcall ( struct rpc_pipe * pipe , kuid_t uid )
2005-04-17 02:20:36 +04:00
{
struct gss_upcall_msg * pos ;
2011-12-26 16:43:57 +04:00
list_for_each_entry ( pos , & pipe - > in_downcall , list ) {
2013-02-02 04:39:32 +04:00
if ( ! uid_eq ( pos - > uid , uid ) )
2005-04-17 02:20:36 +04:00
continue ;
atomic_inc ( & pos - > count ) ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s found msg %p \n " , __func__ , pos ) ;
2005-04-17 02:20:36 +04:00
return pos ;
}
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s found nothing \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2008-06-10 00:51:33 +04:00
/* Try to add an upcall to the pipefs queue.
2005-04-17 02:20:36 +04:00
* If an upcall owned by our uid already exists , then we return a reference
* to that upcall instead of adding the new upcall .
*/
static inline struct gss_upcall_msg *
2009-12-09 20:45:22 +03:00
gss_add_msg ( struct gss_upcall_msg * gss_msg )
2005-04-17 02:20:36 +04:00
{
2011-12-26 16:43:57 +04:00
struct rpc_pipe * pipe = gss_msg - > pipe ;
2005-04-17 02:20:36 +04:00
struct gss_upcall_msg * old ;
2011-12-26 16:43:57 +04:00
spin_lock ( & pipe - > lock ) ;
old = __gss_find_upcall ( pipe , gss_msg - > uid ) ;
2005-04-17 02:20:36 +04:00
if ( old = = NULL ) {
atomic_inc ( & gss_msg - > count ) ;
2011-12-26 16:43:57 +04:00
list_add ( & gss_msg - > list , & pipe - > in_downcall ) ;
2005-04-17 02:20:36 +04:00
} else
gss_msg = old ;
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2005-04-17 02:20:36 +04:00
return gss_msg ;
}
static void
__gss_unhash_msg ( struct gss_upcall_msg * gss_msg )
{
list_del_init ( & gss_msg - > list ) ;
rpc_wake_up_status ( & gss_msg - > rpc_waitqueue , gss_msg - > msg . errno ) ;
wake_up_all ( & gss_msg - > waitqueue ) ;
atomic_dec ( & gss_msg - > count ) ;
}
static void
gss_unhash_msg ( struct gss_upcall_msg * gss_msg )
{
2011-12-26 16:43:57 +04:00
struct rpc_pipe * pipe = gss_msg - > pipe ;
2005-04-17 02:20:36 +04:00
2007-06-07 18:14:15 +04:00
if ( list_empty ( & gss_msg - > list ) )
return ;
2011-12-26 16:43:57 +04:00
spin_lock ( & pipe - > lock ) ;
2007-06-07 18:14:15 +04:00
if ( ! list_empty ( & gss_msg - > list ) )
__gss_unhash_msg ( gss_msg ) ;
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2010-05-13 20:55:38 +04:00
static void
gss_handle_downcall_result ( struct gss_cred * gss_cred , struct gss_upcall_msg * gss_msg )
{
switch ( gss_msg - > msg . errno ) {
case 0 :
if ( gss_msg - > ctx = = NULL )
break ;
clear_bit ( RPCAUTH_CRED_NEGATIVE , & gss_cred - > gc_base . cr_flags ) ;
gss_cred_set_ctx ( & gss_cred - > gc_base , gss_msg - > ctx ) ;
break ;
case - EKEYEXPIRED :
set_bit ( RPCAUTH_CRED_NEGATIVE , & gss_cred - > gc_base . cr_flags ) ;
}
gss_cred - > gc_upcall_timestamp = jiffies ;
gss_cred - > gc_upcall = NULL ;
rpc_wake_up_status ( & gss_msg - > rpc_waitqueue , gss_msg - > msg . errno ) ;
}
2005-04-17 02:20:36 +04:00
static void
gss_upcall_callback ( struct rpc_task * task )
{
2010-07-31 22:29:08 +04:00
struct gss_cred * gss_cred = container_of ( task - > tk_rqstp - > rq_cred ,
2005-04-17 02:20:36 +04:00
struct gss_cred , gc_base ) ;
struct gss_upcall_msg * gss_msg = gss_cred - > gc_upcall ;
2011-12-26 16:43:57 +04:00
struct rpc_pipe * pipe = gss_msg - > pipe ;
2005-04-17 02:20:36 +04:00
2011-12-26 16:43:57 +04:00
spin_lock ( & pipe - > lock ) ;
2010-05-13 20:55:38 +04:00
gss_handle_downcall_result ( gss_cred , gss_msg ) ;
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2010-05-13 20:55:38 +04:00
task - > tk_status = gss_msg - > msg . errno ;
2005-04-17 02:20:36 +04:00
gss_release_msg ( gss_msg ) ;
}
2008-12-24 00:16:37 +03:00
static void gss_encode_v0_msg ( struct gss_upcall_msg * gss_msg )
{
2013-02-02 12:25:43 +04:00
uid_t uid = from_kuid ( & init_user_ns , gss_msg - > uid ) ;
memcpy ( gss_msg - > databuf , & uid , sizeof ( uid ) ) ;
gss_msg - > msg . data = gss_msg - > databuf ;
gss_msg - > msg . len = sizeof ( uid ) ;
BUG_ON ( sizeof ( uid ) > UPCALL_BUF_LEN ) ;
2008-12-24 00:16:37 +03:00
}
2008-12-24 00:19:26 +03:00
static void gss_encode_v1_msg ( struct gss_upcall_msg * gss_msg ,
2012-01-03 22:22:46 +04:00
struct rpc_clnt * clnt ,
const char * service_name )
2008-12-24 00:16:37 +03:00
{
2010-04-08 22:09:58 +04:00
struct gss_api_mech * mech = gss_msg - > auth - > mech ;
2008-12-24 00:19:26 +03:00
char * p = gss_msg - > databuf ;
int len = 0 ;
gss_msg - > msg . len = sprintf ( gss_msg - > databuf , " mech=%s uid=%d " ,
2010-04-08 22:09:58 +04:00
mech - > gm_name ,
2013-02-02 12:25:43 +04:00
from_kuid ( & init_user_ns , gss_msg - > uid ) ) ;
2008-12-24 00:19:26 +03:00
p + = gss_msg - > msg . len ;
if ( clnt - > cl_principal ) {
len = sprintf ( p , " target=%s " , clnt - > cl_principal ) ;
p + = len ;
gss_msg - > msg . len + = len ;
}
2012-01-03 22:22:46 +04:00
if ( service_name ! = NULL ) {
len = sprintf ( p , " service=%s " , service_name ) ;
2008-12-24 00:19:56 +03:00
p + = len ;
gss_msg - > msg . len + = len ;
}
2010-04-08 22:09:58 +04:00
if ( mech - > gm_upcall_enctypes ) {
2011-03-03 03:51:41 +03:00
len = sprintf ( p , " enctypes=%s " , mech - > gm_upcall_enctypes ) ;
2010-04-08 22:09:58 +04:00
p + = len ;
gss_msg - > msg . len + = len ;
}
2008-12-24 00:19:26 +03:00
len = sprintf ( p , " \n " ) ;
gss_msg - > msg . len + = len ;
2008-12-24 00:16:37 +03:00
gss_msg - > msg . data = gss_msg - > databuf ;
BUG_ON ( gss_msg - > msg . len > UPCALL_BUF_LEN ) ;
}
2008-12-24 00:19:26 +03:00
static void gss_encode_msg ( struct gss_upcall_msg * gss_msg ,
2012-01-03 22:22:46 +04:00
struct rpc_clnt * clnt ,
const char * service_name )
2008-12-24 00:16:37 +03:00
{
2013-05-15 21:27:32 +04:00
struct net * net = rpc_net_ns ( clnt ) ;
struct sunrpc_net * sn = net_generic ( net , sunrpc_net_id ) ;
if ( sn - > pipe_version = = 0 )
2008-12-24 00:16:37 +03:00
gss_encode_v0_msg ( gss_msg ) ;
else /* pipe_version == 1 */
2012-01-03 22:22:46 +04:00
gss_encode_v1_msg ( gss_msg , clnt , service_name ) ;
2008-12-24 00:16:37 +03:00
}
2012-01-03 22:22:46 +04:00
static struct gss_upcall_msg *
gss_alloc_msg ( struct gss_auth * gss_auth , struct rpc_clnt * clnt ,
2013-02-02 04:31:17 +04:00
kuid_t uid , const char * service_name )
2005-04-17 02:20:36 +04:00
{
struct gss_upcall_msg * gss_msg ;
2008-12-24 00:10:52 +03:00
int vers ;
2005-04-17 02:20:36 +04:00
2008-06-11 02:31:01 +04:00
gss_msg = kzalloc ( sizeof ( * gss_msg ) , GFP_NOFS ) ;
2008-12-24 00:07:13 +03:00
if ( gss_msg = = NULL )
return ERR_PTR ( - ENOMEM ) ;
2013-05-15 21:27:32 +04:00
vers = get_pipe_version ( rpc_net_ns ( clnt ) ) ;
2008-12-24 00:10:52 +03:00
if ( vers < 0 ) {
kfree ( gss_msg ) ;
return ERR_PTR ( vers ) ;
}
2011-12-26 16:44:06 +04:00
gss_msg - > pipe = gss_auth - > pipe [ vers ] ;
2008-12-24 00:07:13 +03:00
INIT_LIST_HEAD ( & gss_msg - > list ) ;
rpc_init_wait_queue ( & gss_msg - > rpc_waitqueue , " RPCSEC_GSS upcall waitq " ) ;
init_waitqueue_head ( & gss_msg - > waitqueue ) ;
atomic_set ( & gss_msg - > count , 1 ) ;
gss_msg - > uid = uid ;
gss_msg - > auth = gss_auth ;
2012-01-03 22:22:46 +04:00
gss_encode_msg ( gss_msg , clnt , service_name ) ;
2005-04-17 02:20:36 +04:00
return gss_msg ;
}
static struct gss_upcall_msg *
gss_setup_upcall ( struct rpc_clnt * clnt , struct gss_auth * gss_auth , struct rpc_cred * cred )
{
2008-04-08 04:50:11 +04:00
struct gss_cred * gss_cred = container_of ( cred ,
struct gss_cred , gc_base ) ;
2005-04-17 02:20:36 +04:00
struct gss_upcall_msg * gss_new , * gss_msg ;
2013-02-02 04:31:17 +04:00
kuid_t uid = cred - > cr_uid ;
2005-04-17 02:20:36 +04:00
2012-01-03 22:22:46 +04:00
gss_new = gss_alloc_msg ( gss_auth , clnt , uid , gss_cred - > gc_principal ) ;
2008-12-24 00:07:13 +03:00
if ( IS_ERR ( gss_new ) )
return gss_new ;
2009-12-09 20:45:22 +03:00
gss_msg = gss_add_msg ( gss_new ) ;
2005-04-17 02:20:36 +04:00
if ( gss_msg = = gss_new ) {
2011-12-26 16:43:57 +04:00
int res = rpc_queue_upcall ( gss_new - > pipe , & gss_new - > msg ) ;
2005-04-17 02:20:36 +04:00
if ( res ) {
gss_unhash_msg ( gss_new ) ;
gss_msg = ERR_PTR ( res ) ;
}
} else
gss_release_msg ( gss_new ) ;
return gss_msg ;
}
2008-12-24 00:06:55 +03:00
static void warn_gssd ( void )
{
static unsigned long ratelimit ;
unsigned long now = jiffies ;
if ( time_after ( now , ratelimit ) ) {
printk ( KERN_WARNING " RPC: AUTH_GSS upcall timed out. \n "
" Please check user daemon is running. \n " ) ;
ratelimit = now + 15 * HZ ;
}
}
2005-04-17 02:20:36 +04:00
static inline int
gss_refresh_upcall ( struct rpc_task * task )
{
2010-07-31 22:29:08 +04:00
struct rpc_cred * cred = task - > tk_rqstp - > rq_cred ;
2007-06-07 18:14:14 +04:00
struct gss_auth * gss_auth = container_of ( cred - > cr_auth ,
2005-04-17 02:20:36 +04:00
struct gss_auth , rpc_auth ) ;
struct gss_cred * gss_cred = container_of ( cred ,
struct gss_cred , gc_base ) ;
struct gss_upcall_msg * gss_msg ;
2011-12-26 16:43:57 +04:00
struct rpc_pipe * pipe ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s for uid %u \n " ,
2013-02-02 05:10:52 +04:00
task - > tk_pid , __func__ , from_kuid ( & init_user_ns , cred - > cr_uid ) ) ;
2005-04-17 02:20:36 +04:00
gss_msg = gss_setup_upcall ( task - > tk_client , gss_auth , cred ) ;
2009-12-08 21:13:03 +03:00
if ( PTR_ERR ( gss_msg ) = = - EAGAIN ) {
2008-12-24 00:10:52 +03:00
/* XXX: warning on the first, under the assumption we
* shouldn ' t normally hit this case on a refresh . */
warn_gssd ( ) ;
task - > tk_timeout = 15 * HZ ;
rpc_sleep_on ( & pipe_version_rpc_waitqueue , task , NULL ) ;
2011-04-13 22:31:28 +04:00
return - EAGAIN ;
2008-12-24 00:10:52 +03:00
}
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( gss_msg ) ) {
err = PTR_ERR ( gss_msg ) ;
goto out ;
}
2011-12-26 16:43:57 +04:00
pipe = gss_msg - > pipe ;
spin_lock ( & pipe - > lock ) ;
2005-04-17 02:20:36 +04:00
if ( gss_cred - > gc_upcall ! = NULL )
2008-02-23 00:34:17 +03:00
rpc_sleep_on ( & gss_cred - > gc_upcall - > rpc_waitqueue , task , NULL ) ;
2010-05-13 20:55:38 +04:00
else if ( gss_msg - > ctx = = NULL & & gss_msg - > msg . errno > = 0 ) {
2005-04-17 02:20:36 +04:00
task - > tk_timeout = 0 ;
gss_cred - > gc_upcall = gss_msg ;
/* gss_upcall_callback will release the reference to gss_upcall_msg */
atomic_inc ( & gss_msg - > count ) ;
2008-02-23 00:34:17 +03:00
rpc_sleep_on ( & gss_msg - > rpc_waitqueue , task , gss_upcall_callback ) ;
2010-05-13 20:55:38 +04:00
} else {
gss_handle_downcall_result ( gss_cred , gss_msg ) ;
2005-04-17 02:20:36 +04:00
err = gss_msg - > msg . errno ;
2010-05-13 20:55:38 +04:00
}
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2005-04-17 02:20:36 +04:00
gss_release_msg ( gss_msg ) ;
out :
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s for uid %u result %d \n " ,
2013-02-02 05:10:52 +04:00
task - > tk_pid , __func__ ,
from_kuid ( & init_user_ns , cred - > cr_uid ) , err ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static inline int
gss_create_upcall ( struct gss_auth * gss_auth , struct gss_cred * gss_cred )
{
2013-05-15 22:28:54 +04:00
struct net * net = rpc_net_ns ( gss_auth - > client ) ;
struct sunrpc_net * sn = net_generic ( net , sunrpc_net_id ) ;
2011-12-26 16:43:57 +04:00
struct rpc_pipe * pipe ;
2005-04-17 02:20:36 +04:00
struct rpc_cred * cred = & gss_cred - > gc_base ;
struct gss_upcall_msg * gss_msg ;
2013-05-15 22:28:54 +04:00
unsigned long timeout ;
2005-04-17 02:20:36 +04:00
DEFINE_WAIT ( wait ) ;
2013-05-15 21:46:33 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2013-02-02 05:10:52 +04:00
dprintk ( " RPC: %s for uid %u \n " ,
__func__ , from_kuid ( & init_user_ns , cred - > cr_uid ) ) ;
2008-12-24 00:10:52 +03:00
retry :
2013-05-15 21:46:33 +04:00
err = 0 ;
2013-05-15 22:28:54 +04:00
/* Default timeout is 15s unless we know that gssd is not running */
timeout = 15 * HZ ;
if ( ! sn - > gssd_running )
timeout = HZ > > 2 ;
2005-04-17 02:20:36 +04:00
gss_msg = gss_setup_upcall ( gss_auth - > client , gss_auth , cred ) ;
2008-12-24 00:10:52 +03:00
if ( PTR_ERR ( gss_msg ) = = - EAGAIN ) {
err = wait_event_interruptible_timeout ( pipe_version_waitqueue ,
2013-05-15 21:27:32 +04:00
sn - > pipe_version > = 0 , timeout ) ;
if ( sn - > pipe_version < 0 ) {
2013-05-15 22:28:54 +04:00
if ( err = = 0 )
sn - > gssd_running = 0 ;
2011-04-13 22:31:28 +04:00
warn_gssd ( ) ;
err = - EACCES ;
}
2013-05-15 21:46:33 +04:00
if ( err < 0 )
2008-12-24 00:10:52 +03:00
goto out ;
goto retry ;
}
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( gss_msg ) ) {
err = PTR_ERR ( gss_msg ) ;
goto out ;
}
2011-12-26 16:43:57 +04:00
pipe = gss_msg - > pipe ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
2011-06-17 18:14:59 +04:00
prepare_to_wait ( & gss_msg - > waitqueue , & wait , TASK_KILLABLE ) ;
2011-12-26 16:43:57 +04:00
spin_lock ( & pipe - > lock ) ;
2005-04-17 02:20:36 +04:00
if ( gss_msg - > ctx ! = NULL | | gss_msg - > msg . errno < 0 ) {
break ;
}
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2011-06-17 18:14:59 +04:00
if ( fatal_signal_pending ( current ) ) {
2005-04-17 02:20:36 +04:00
err = - ERESTARTSYS ;
goto out_intr ;
}
schedule ( ) ;
}
if ( gss_msg - > ctx )
2008-04-18 00:53:01 +04:00
gss_cred_set_ctx ( cred , gss_msg - > ctx ) ;
2005-04-17 02:20:36 +04:00
else
err = gss_msg - > msg . errno ;
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2005-04-17 02:20:36 +04:00
out_intr :
finish_wait ( & gss_msg - > waitqueue , & wait ) ;
gss_release_msg ( gss_msg ) ;
out :
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s for uid %u result %d \n " ,
2013-02-02 05:10:52 +04:00
__func__ , from_kuid ( & init_user_ns , cred - > cr_uid ) , err ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
# define MSG_BUF_MAXSIZE 1024
static ssize_t
gss_pipe_downcall ( struct file * filp , const char __user * src , size_t mlen )
{
const void * p , * end ;
void * buf ;
struct gss_upcall_msg * gss_msg ;
2013-01-24 02:07:38 +04:00
struct rpc_pipe * pipe = RPC_I ( file_inode ( filp ) ) - > pipe ;
2005-04-17 02:20:36 +04:00
struct gss_cl_ctx * ctx ;
2013-02-02 12:25:43 +04:00
uid_t id ;
kuid_t uid ;
2007-06-07 18:14:15 +04:00
ssize_t err = - EFBIG ;
2005-04-17 02:20:36 +04:00
if ( mlen > MSG_BUF_MAXSIZE )
goto out ;
err = - ENOMEM ;
2008-06-11 02:31:01 +04:00
buf = kmalloc ( mlen , GFP_NOFS ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf )
goto out ;
err = - EFAULT ;
if ( copy_from_user ( buf , src , mlen ) )
goto err ;
end = ( const void * ) ( ( char * ) buf + mlen ) ;
2013-02-02 12:25:43 +04:00
p = simple_get_bytes ( buf , end , & id , sizeof ( id ) ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) ) {
err = PTR_ERR ( p ) ;
goto err ;
}
2013-02-02 12:25:43 +04:00
uid = make_kuid ( & init_user_ns , id ) ;
if ( ! uid_valid ( uid ) ) {
err = - EINVAL ;
goto err ;
}
2005-04-17 02:20:36 +04:00
err = - ENOMEM ;
ctx = gss_alloc_context ( ) ;
if ( ctx = = NULL )
goto err ;
2007-06-07 18:14:15 +04:00
err = - ENOENT ;
/* Find a matching upcall */
2011-12-26 16:43:57 +04:00
spin_lock ( & pipe - > lock ) ;
gss_msg = __gss_find_upcall ( pipe , uid ) ;
2007-06-07 18:14:15 +04:00
if ( gss_msg = = NULL ) {
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2007-06-07 18:14:15 +04:00
goto err_put_ctx ;
}
list_del_init ( & gss_msg - > list ) ;
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2007-06-07 18:14:15 +04:00
2007-06-07 23:31:36 +04:00
p = gss_fill_context ( p , end , ctx , gss_msg - > auth - > mech ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) ) {
err = PTR_ERR ( p ) ;
2009-12-19 00:28:20 +03:00
switch ( err ) {
case - EACCES :
2010-01-07 17:42:02 +03:00
case - EKEYEXPIRED :
2009-12-19 00:28:20 +03:00
gss_msg - > msg . errno = err ;
err = mlen ;
break ;
case - EFAULT :
case - ENOMEM :
case - EINVAL :
case - ENOSYS :
gss_msg - > msg . errno = - EAGAIN ;
break ;
default :
printk ( KERN_CRIT " %s: bad return from "
2010-01-07 01:26:27 +03:00
" gss_fill_context: %zd \n " , __func__ , err ) ;
2009-12-19 00:28:20 +03:00
BUG ( ) ;
}
2007-06-07 18:14:15 +04:00
goto err_release_msg ;
2005-04-17 02:20:36 +04:00
}
2007-06-07 18:14:15 +04:00
gss_msg - > ctx = gss_get_ctx ( ctx ) ;
err = mlen ;
err_release_msg :
2011-12-26 16:43:57 +04:00
spin_lock ( & pipe - > lock ) ;
2007-06-07 18:14:15 +04:00
__gss_unhash_msg ( gss_msg ) ;
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2007-06-07 18:14:15 +04:00
gss_release_msg ( gss_msg ) ;
2005-04-17 02:20:36 +04:00
err_put_ctx :
gss_put_ctx ( ctx ) ;
err :
kfree ( buf ) ;
out :
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s returning %Zd \n " , __func__ , err ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2008-12-24 00:16:37 +03:00
static int gss_pipe_open ( struct inode * inode , int new_version )
2008-12-24 00:10:19 +03:00
{
2013-05-15 21:27:32 +04:00
struct net * net = inode - > i_sb - > s_fs_info ;
struct sunrpc_net * sn = net_generic ( net , sunrpc_net_id ) ;
2008-12-24 00:16:37 +03:00
int ret = 0 ;
2008-12-24 00:10:52 +03:00
spin_lock ( & pipe_version_lock ) ;
2013-05-15 21:27:32 +04:00
if ( sn - > pipe_version < 0 ) {
2008-12-24 00:16:37 +03:00
/* First open of any gss pipe determines the version: */
2013-05-15 21:27:32 +04:00
sn - > pipe_version = new_version ;
2008-12-24 00:10:52 +03:00
rpc_wake_up ( & pipe_version_rpc_waitqueue ) ;
wake_up ( & pipe_version_waitqueue ) ;
2013-05-15 21:27:32 +04:00
} else if ( sn - > pipe_version ! = new_version ) {
2008-12-24 00:16:37 +03:00
/* Trying to open a pipe of a different version */
ret = - EBUSY ;
goto out ;
2008-12-24 00:10:52 +03:00
}
2013-05-15 21:27:32 +04:00
atomic_inc ( & sn - > pipe_users ) ;
2008-12-24 00:16:37 +03:00
out :
2008-12-24 00:10:52 +03:00
spin_unlock ( & pipe_version_lock ) ;
2008-12-24 00:16:37 +03:00
return ret ;
}
static int gss_pipe_open_v0 ( struct inode * inode )
{
return gss_pipe_open ( inode , 0 ) ;
}
static int gss_pipe_open_v1 ( struct inode * inode )
{
return gss_pipe_open ( inode , 1 ) ;
2008-12-24 00:10:19 +03:00
}
2005-04-17 02:20:36 +04:00
static void
gss_pipe_release ( struct inode * inode )
{
2013-05-15 21:27:32 +04:00
struct net * net = inode - > i_sb - > s_fs_info ;
2011-12-26 16:43:57 +04:00
struct rpc_pipe * pipe = RPC_I ( inode ) - > pipe ;
2007-06-07 23:31:36 +04:00
struct gss_upcall_msg * gss_msg ;
2005-04-17 02:20:36 +04:00
2010-09-13 03:55:25 +04:00
restart :
2011-12-26 16:43:57 +04:00
spin_lock ( & pipe - > lock ) ;
list_for_each_entry ( gss_msg , & pipe - > in_downcall , list ) {
2005-04-17 02:20:36 +04:00
2010-09-13 03:55:25 +04:00
if ( ! list_empty ( & gss_msg - > msg . list ) )
continue ;
2005-04-17 02:20:36 +04:00
gss_msg - > msg . errno = - EPIPE ;
atomic_inc ( & gss_msg - > count ) ;
__gss_unhash_msg ( gss_msg ) ;
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2005-04-17 02:20:36 +04:00
gss_release_msg ( gss_msg ) ;
2010-09-13 03:55:25 +04:00
goto restart ;
2005-04-17 02:20:36 +04:00
}
2011-12-26 16:43:57 +04:00
spin_unlock ( & pipe - > lock ) ;
2008-12-24 00:10:19 +03:00
2013-05-15 21:27:32 +04:00
put_pipe_version ( net ) ;
2005-04-17 02:20:36 +04:00
}
static void
gss_pipe_destroy_msg ( struct rpc_pipe_msg * msg )
{
struct gss_upcall_msg * gss_msg = container_of ( msg , struct gss_upcall_msg , msg ) ;
if ( msg - > errno < 0 ) {
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s releasing msg %p \n " ,
__func__ , gss_msg ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & gss_msg - > count ) ;
gss_unhash_msg ( gss_msg ) ;
2008-12-24 00:06:55 +03:00
if ( msg - > errno = = - ETIMEDOUT )
warn_gssd ( ) ;
2005-04-17 02:20:36 +04:00
gss_release_msg ( gss_msg ) ;
}
}
2012-01-11 19:18:09 +04:00
static void gss_pipes_dentries_destroy ( struct rpc_auth * auth )
{
struct gss_auth * gss_auth ;
gss_auth = container_of ( auth , struct gss_auth , rpc_auth ) ;
2012-01-11 19:18:17 +04:00
if ( gss_auth - > pipe [ 0 ] - > dentry )
rpc_unlink ( gss_auth - > pipe [ 0 ] - > dentry ) ;
if ( gss_auth - > pipe [ 1 ] - > dentry )
rpc_unlink ( gss_auth - > pipe [ 1 ] - > dentry ) ;
2012-01-11 19:18:09 +04:00
}
static int gss_pipes_dentries_create ( struct rpc_auth * auth )
{
int err ;
struct gss_auth * gss_auth ;
struct rpc_clnt * clnt ;
gss_auth = container_of ( auth , struct gss_auth , rpc_auth ) ;
clnt = gss_auth - > client ;
2012-01-11 19:18:42 +04:00
gss_auth - > pipe [ 1 ] - > dentry = rpc_mkpipe_dentry ( clnt - > cl_dentry ,
2012-01-11 19:18:09 +04:00
" gssd " ,
clnt , gss_auth - > pipe [ 1 ] ) ;
if ( IS_ERR ( gss_auth - > pipe [ 1 ] - > dentry ) )
return PTR_ERR ( gss_auth - > pipe [ 1 ] - > dentry ) ;
2012-01-11 19:18:42 +04:00
gss_auth - > pipe [ 0 ] - > dentry = rpc_mkpipe_dentry ( clnt - > cl_dentry ,
2012-01-11 19:18:09 +04:00
gss_auth - > mech - > gm_name ,
clnt , gss_auth - > pipe [ 0 ] ) ;
if ( IS_ERR ( gss_auth - > pipe [ 0 ] - > dentry ) ) {
err = PTR_ERR ( gss_auth - > pipe [ 0 ] - > dentry ) ;
goto err_unlink_pipe_1 ;
}
return 0 ;
err_unlink_pipe_1 :
rpc_unlink ( gss_auth - > pipe [ 1 ] - > dentry ) ;
return err ;
}
static void gss_pipes_dentries_destroy_net ( struct rpc_clnt * clnt ,
struct rpc_auth * auth )
{
2012-03-02 02:00:56 +04:00
struct net * net = rpc_net_ns ( clnt ) ;
2012-01-11 19:18:09 +04:00
struct super_block * sb ;
sb = rpc_get_sb_net ( net ) ;
if ( sb ) {
2012-01-11 19:18:42 +04:00
if ( clnt - > cl_dentry )
2012-01-11 19:18:09 +04:00
gss_pipes_dentries_destroy ( auth ) ;
rpc_put_sb_net ( net ) ;
}
}
static int gss_pipes_dentries_create_net ( struct rpc_clnt * clnt ,
struct rpc_auth * auth )
{
2012-03-02 02:00:56 +04:00
struct net * net = rpc_net_ns ( clnt ) ;
2012-01-11 19:18:09 +04:00
struct super_block * sb ;
int err = 0 ;
sb = rpc_get_sb_net ( net ) ;
if ( sb ) {
2012-01-11 19:18:42 +04:00
if ( clnt - > cl_dentry )
2012-01-11 19:18:09 +04:00
err = gss_pipes_dentries_create ( auth ) ;
rpc_put_sb_net ( net ) ;
}
return err ;
}
2007-02-10 02:38:13 +03:00
/*
* NOTE : we have the opportunity to use different
2005-04-17 02:20:36 +04:00
* parameters based on the input flavor ( which must be a pseudoflavor )
*/
static struct rpc_auth *
gss_create ( struct rpc_clnt * clnt , rpc_authflavor_t flavor )
{
struct gss_auth * gss_auth ;
struct rpc_auth * auth ;
2005-06-22 21:16:23 +04:00
int err = - ENOMEM ; /* XXX? */
2005-04-17 02:20:36 +04:00
2007-01-31 20:14:05 +03:00
dprintk ( " RPC: creating GSS authenticator for client %p \n " , clnt ) ;
2005-04-17 02:20:36 +04:00
if ( ! try_module_get ( THIS_MODULE ) )
2005-06-22 21:16:23 +04:00
return ERR_PTR ( err ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( gss_auth = kmalloc ( sizeof ( * gss_auth ) , GFP_KERNEL ) ) )
goto out_dec ;
gss_auth - > client = clnt ;
2005-06-22 21:16:23 +04:00
err = - EINVAL ;
2005-04-17 02:20:36 +04:00
gss_auth - > mech = gss_mech_get_by_pseudoflavor ( flavor ) ;
if ( ! gss_auth - > mech ) {
2013-05-01 17:17:50 +04:00
dprintk ( " RPC: Pseudoflavor %d not found! \n " , flavor ) ;
2005-04-17 02:20:36 +04:00
goto err_free ;
}
gss_auth - > service = gss_pseudoflavor_to_service ( gss_auth - > mech , flavor ) ;
2005-06-22 21:16:23 +04:00
if ( gss_auth - > service = = 0 )
goto err_put_mech ;
2005-04-17 02:20:36 +04:00
auth = & gss_auth - > rpc_auth ;
auth - > au_cslack = GSS_CRED_SLACK > > 2 ;
auth - > au_rslack = GSS_VERF_SLACK > > 2 ;
auth - > au_ops = & authgss_ops ;
auth - > au_flavor = flavor ;
atomic_set ( & auth - > au_count , 1 ) ;
2007-06-27 22:29:12 +04:00
kref_init ( & gss_auth - > kref ) ;
2005-04-17 02:20:36 +04:00
2008-12-24 00:16:37 +03:00
/*
* Note : if we created the old pipe first , then someone who
* examined the directory at the right moment might conclude
* that we supported only the old pipe . So we instead create
* the new pipe first .
*/
2011-12-26 16:44:06 +04:00
gss_auth - > pipe [ 1 ] = rpc_mkpipe_data ( & gss_upcall_ops_v1 ,
RPC_PIPE_WAIT_FOR_OPEN ) ;
if ( IS_ERR ( gss_auth - > pipe [ 1 ] ) ) {
err = PTR_ERR ( gss_auth - > pipe [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
goto err_put_mech ;
2005-06-22 21:16:23 +04:00
}
2005-04-17 02:20:36 +04:00
2011-12-26 16:44:06 +04:00
gss_auth - > pipe [ 0 ] = rpc_mkpipe_data ( & gss_upcall_ops_v0 ,
RPC_PIPE_WAIT_FOR_OPEN ) ;
if ( IS_ERR ( gss_auth - > pipe [ 0 ] ) ) {
err = PTR_ERR ( gss_auth - > pipe [ 0 ] ) ;
goto err_destroy_pipe_1 ;
}
2012-01-11 19:18:09 +04:00
err = gss_pipes_dentries_create_net ( clnt , auth ) ;
if ( err )
2011-12-26 16:44:06 +04:00
goto err_destroy_pipe_0 ;
2007-06-26 01:11:20 +04:00
err = rpcauth_init_credcache ( auth ) ;
2007-06-09 23:42:01 +04:00
if ( err )
2012-01-11 19:18:09 +04:00
goto err_unlink_pipes ;
2007-06-09 23:42:01 +04:00
2005-04-17 02:20:36 +04:00
return auth ;
2012-01-11 19:18:09 +04:00
err_unlink_pipes :
gss_pipes_dentries_destroy_net ( clnt , auth ) ;
2011-12-26 16:44:06 +04:00
err_destroy_pipe_0 :
rpc_destroy_pipe_data ( gss_auth - > pipe [ 0 ] ) ;
err_destroy_pipe_1 :
rpc_destroy_pipe_data ( gss_auth - > pipe [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
err_put_mech :
gss_mech_put ( gss_auth - > mech ) ;
err_free :
kfree ( gss_auth ) ;
out_dec :
module_put ( THIS_MODULE ) ;
2005-06-22 21:16:23 +04:00
return ERR_PTR ( err ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-27 22:29:12 +04:00
static void
gss_free ( struct gss_auth * gss_auth )
{
2012-01-11 19:18:09 +04:00
gss_pipes_dentries_destroy_net ( gss_auth - > client , & gss_auth - > rpc_auth ) ;
2011-12-26 16:44:06 +04:00
rpc_destroy_pipe_data ( gss_auth - > pipe [ 0 ] ) ;
rpc_destroy_pipe_data ( gss_auth - > pipe [ 1 ] ) ;
2007-06-27 22:29:12 +04:00
gss_mech_put ( gss_auth - > mech ) ;
kfree ( gss_auth ) ;
module_put ( THIS_MODULE ) ;
}
static void
gss_free_callback ( struct kref * kref )
{
struct gss_auth * gss_auth = container_of ( kref , struct gss_auth , kref ) ;
gss_free ( gss_auth ) ;
}
2005-04-17 02:20:36 +04:00
static void
gss_destroy ( struct rpc_auth * auth )
{
struct gss_auth * gss_auth ;
2007-01-31 20:14:05 +03:00
dprintk ( " RPC: destroying GSS authenticator %p flavor %d \n " ,
auth , auth - > au_flavor ) ;
2005-04-17 02:20:36 +04:00
2007-06-09 23:41:42 +04:00
rpcauth_destroy_credcache ( auth ) ;
2005-04-17 02:20:36 +04:00
gss_auth = container_of ( auth , struct gss_auth , rpc_auth ) ;
2007-06-27 22:29:12 +04:00
kref_put ( & gss_auth - > kref , gss_free_callback ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-27 01:04:57 +04:00
/*
* gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call
* to the server with the GSS control procedure field set to
* RPC_GSS_PROC_DESTROY . This should normally cause the server to release
* all RPCSEC_GSS state associated with that context .
*/
static int
gss_destroying_context ( struct rpc_cred * cred )
{
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred , gc_base ) ;
struct gss_auth * gss_auth = container_of ( cred - > cr_auth , struct gss_auth , rpc_auth ) ;
struct rpc_task * task ;
if ( gss_cred - > gc_ctx = = NULL | |
2008-12-23 23:21:57 +03:00
test_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) = = 0 )
2007-06-27 01:04:57 +04:00
return 0 ;
gss_cred - > gc_ctx - > gc_proc = RPC_GSS_PROC_DESTROY ;
cred - > cr_ops = & gss_nullops ;
/* Take a reference to ensure the cred will be destroyed either
* by the RPC call or by the put_rpccred ( ) below */
get_rpccred ( cred ) ;
2008-04-19 22:22:31 +04:00
task = rpc_call_null ( gss_auth - > client , cred , RPC_TASK_ASYNC | RPC_TASK_SOFT ) ;
2007-06-27 01:04:57 +04:00
if ( ! IS_ERR ( task ) )
rpc_put_task ( task ) ;
put_rpccred ( cred ) ;
return 1 ;
}
/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure
2005-04-17 02:20:36 +04:00
* to create a new cred or context , so they check that things have been
* allocated before freeing them . */
static void
2007-06-27 03:18:38 +04:00
gss_do_free_ctx ( struct gss_cl_ctx * ctx )
2005-04-17 02:20:36 +04:00
{
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2010-08-02 04:14:55 +04:00
gss_delete_sec_context ( & ctx - > gc_gss_ctx ) ;
2005-04-17 02:20:36 +04:00
kfree ( ctx - > gc_wire_ctx . data ) ;
kfree ( ctx ) ;
}
2007-06-27 03:18:38 +04:00
static void
gss_free_ctx_callback ( struct rcu_head * head )
{
struct gss_cl_ctx * ctx = container_of ( head , struct gss_cl_ctx , gc_rcu ) ;
gss_do_free_ctx ( ctx ) ;
}
static void
gss_free_ctx ( struct gss_cl_ctx * ctx )
{
call_rcu ( & ctx - > gc_rcu , gss_free_ctx_callback ) ;
}
2005-04-17 02:20:36 +04:00
static void
2007-06-24 23:55:26 +04:00
gss_free_cred ( struct gss_cred * gss_cred )
2005-04-17 02:20:36 +04:00
{
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s cred=%p \n " , __func__ , gss_cred ) ;
2007-06-24 23:55:26 +04:00
kfree ( gss_cred ) ;
}
2005-04-17 02:20:36 +04:00
2007-06-24 23:55:26 +04:00
static void
gss_free_cred_callback ( struct rcu_head * head )
{
struct gss_cred * gss_cred = container_of ( head , struct gss_cred , gc_base . cr_rcu ) ;
gss_free_cred ( gss_cred ) ;
}
2005-04-17 02:20:36 +04:00
2007-06-24 23:55:26 +04:00
static void
2008-12-23 23:21:57 +03:00
gss_destroy_nullcred ( struct rpc_cred * cred )
2007-06-24 23:55:26 +04:00
{
2007-06-27 03:18:38 +04:00
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred , gc_base ) ;
2007-06-27 22:29:12 +04:00
struct gss_auth * gss_auth = container_of ( cred - > cr_auth , struct gss_auth , rpc_auth ) ;
2007-06-27 03:18:38 +04:00
struct gss_cl_ctx * ctx = gss_cred - > gc_ctx ;
2011-08-01 20:19:00 +04:00
RCU_INIT_POINTER ( gss_cred - > gc_ctx , NULL ) ;
2007-06-24 23:55:26 +04:00
call_rcu ( & cred - > cr_rcu , gss_free_cred_callback ) ;
2007-06-27 03:18:38 +04:00
if ( ctx )
gss_put_ctx ( ctx ) ;
2007-06-27 22:29:12 +04:00
kref_put ( & gss_auth - > kref , gss_free_callback ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-23 23:21:57 +03:00
static void
gss_destroy_cred ( struct rpc_cred * cred )
{
if ( gss_destroying_context ( cred ) )
return ;
gss_destroy_nullcred ( cred ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Lookup RPCSEC_GSS cred for the current process
*/
static struct rpc_cred *
2006-02-01 20:18:36 +03:00
gss_lookup_cred ( struct rpc_auth * auth , struct auth_cred * acred , int flags )
2005-04-17 02:20:36 +04:00
{
2006-02-01 20:18:36 +03:00
return rpcauth_lookup_credcache ( auth , acred , flags ) ;
2005-04-17 02:20:36 +04:00
}
static struct rpc_cred *
2006-02-01 20:18:36 +03:00
gss_create_cred ( struct rpc_auth * auth , struct auth_cred * acred , int flags )
2005-04-17 02:20:36 +04:00
{
struct gss_auth * gss_auth = container_of ( auth , struct gss_auth , rpc_auth ) ;
struct gss_cred * cred = NULL ;
int err = - ENOMEM ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s for uid %d, flavor %d \n " ,
2013-02-02 05:10:52 +04:00
__func__ , from_kuid ( & init_user_ns , acred - > uid ) ,
auth - > au_flavor ) ;
2005-04-17 02:20:36 +04:00
2008-06-11 02:31:01 +04:00
if ( ! ( cred = kzalloc ( sizeof ( * cred ) , GFP_NOFS ) ) )
2005-04-17 02:20:36 +04:00
goto out_err ;
2007-06-24 03:55:31 +04:00
rpcauth_init_cred ( & cred - > gc_base , acred , auth , & gss_credops ) ;
2005-04-17 02:20:36 +04:00
/*
* Note : in order to force a call to call_refresh ( ) , we deliberately
* fail to flag the credential as RPCAUTH_CRED_UPTODATE .
*/
2007-06-25 18:15:15 +04:00
cred - > gc_base . cr_flags = 1UL < < RPCAUTH_CRED_NEW ;
2005-04-17 02:20:36 +04:00
cred - > gc_service = gss_auth - > service ;
2012-01-03 22:22:46 +04:00
cred - > gc_principal = NULL ;
if ( acred - > machine_cred )
cred - > gc_principal = acred - > principal ;
2007-06-27 22:29:12 +04:00
kref_get ( & gss_auth - > kref ) ;
2005-04-17 02:20:36 +04:00
return & cred - > gc_base ;
out_err :
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %s failed with error %d \n " , __func__ , err ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( err ) ;
}
2006-02-01 20:19:27 +03:00
static int
gss_cred_init ( struct rpc_auth * auth , struct rpc_cred * cred )
{
struct gss_auth * gss_auth = container_of ( auth , struct gss_auth , rpc_auth ) ;
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred , gc_base ) ;
int err ;
do {
err = gss_create_upcall ( gss_auth , gss_cred ) ;
} while ( err = = - EAGAIN ) ;
return err ;
}
2005-04-17 02:20:36 +04:00
static int
2006-02-01 20:18:36 +03:00
gss_match ( struct auth_cred * acred , struct rpc_cred * rc , int flags )
2005-04-17 02:20:36 +04:00
{
struct gss_cred * gss_cred = container_of ( rc , struct gss_cred , gc_base ) ;
2008-04-18 01:03:58 +04:00
if ( test_bit ( RPCAUTH_CRED_NEW , & rc - > cr_flags ) )
2006-02-01 20:18:36 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
/* Don't match with creds that have expired. */
2008-04-18 01:03:58 +04:00
if ( time_after ( jiffies , gss_cred - > gc_ctx - > gc_expiry ) )
return 0 ;
if ( ! test_bit ( RPCAUTH_CRED_UPTODATE , & rc - > cr_flags ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2006-02-01 20:18:36 +03:00
out :
2012-01-03 22:22:46 +04:00
if ( acred - > principal ! = NULL ) {
if ( gss_cred - > gc_principal = = NULL )
return 0 ;
return strcmp ( acred - > principal , gss_cred - > gc_principal ) = = 0 ;
}
if ( gss_cred - > gc_principal ! = NULL )
2008-04-08 04:50:11 +04:00
return 0 ;
2013-02-02 04:39:32 +04:00
return uid_eq ( rc - > cr_uid , acred - > uid ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Marshal credentials .
* Maybe we should keep a cached credential for performance reasons .
*/
2006-09-27 09:29:38 +04:00
static __be32 *
gss_marshal ( struct rpc_task * task , __be32 * p )
2005-04-17 02:20:36 +04:00
{
2010-07-31 22:29:08 +04:00
struct rpc_rqst * req = task - > tk_rqstp ;
struct rpc_cred * cred = req - > rq_cred ;
2005-04-17 02:20:36 +04:00
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred ,
gc_base ) ;
struct gss_cl_ctx * ctx = gss_cred_get_ctx ( cred ) ;
2006-09-27 09:29:38 +04:00
__be32 * cred_len ;
2005-04-17 02:20:36 +04:00
u32 maj_stat = 0 ;
struct xdr_netobj mic ;
struct kvec iov ;
struct xdr_buf verf_buf ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s \n " , task - > tk_pid , __func__ ) ;
2005-04-17 02:20:36 +04:00
* p + + = htonl ( RPC_AUTH_GSS ) ;
cred_len = p + + ;
spin_lock ( & ctx - > gc_seq_lock ) ;
req - > rq_seqno = ctx - > gc_seq + + ;
spin_unlock ( & ctx - > gc_seq_lock ) ;
* p + + = htonl ( ( u32 ) RPC_GSS_VERSION ) ;
* p + + = htonl ( ( u32 ) ctx - > gc_proc ) ;
* p + + = htonl ( ( u32 ) req - > rq_seqno ) ;
* p + + = htonl ( ( u32 ) gss_cred - > gc_service ) ;
p = xdr_encode_netobj ( p , & ctx - > gc_wire_ctx ) ;
* cred_len = htonl ( ( p - ( cred_len + 1 ) ) < < 2 ) ;
/* We compute the checksum for the verifier over the xdr-encoded bytes
* starting with the xid and ending at the end of the credential : */
2013-01-08 18:10:21 +04:00
iov . iov_base = xprt_skip_transport_header ( req - > rq_xprt ,
2005-08-26 03:25:49 +04:00
req - > rq_snd_buf . head [ 0 ] . iov_base ) ;
2005-04-17 02:20:36 +04:00
iov . iov_len = ( u8 * ) p - ( u8 * ) iov . iov_base ;
xdr_buf_from_iov ( & iov , & verf_buf ) ;
/* set verifier flavor*/
* p + + = htonl ( RPC_AUTH_GSS ) ;
mic . data = ( u8 * ) ( p + 1 ) ;
2005-10-14 00:55:18 +04:00
maj_stat = gss_get_mic ( ctx - > gc_gss_ctx , & verf_buf , & mic ) ;
2005-04-17 02:20:36 +04:00
if ( maj_stat = = GSS_S_CONTEXT_EXPIRED ) {
2007-06-25 18:15:15 +04:00
clear_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ;
2005-04-17 02:20:36 +04:00
} else if ( maj_stat ! = 0 ) {
printk ( " gss_marshal: gss_get_mic FAILED (%d) \n " , maj_stat ) ;
goto out_put_ctx ;
}
p = xdr_encode_opaque ( p , NULL , mic . len ) ;
gss_put_ctx ( ctx ) ;
return p ;
out_put_ctx :
gss_put_ctx ( ctx ) ;
return NULL ;
}
2008-04-18 01:03:58 +04:00
static int gss_renew_cred ( struct rpc_task * task )
{
2010-07-31 22:29:08 +04:00
struct rpc_cred * oldcred = task - > tk_rqstp - > rq_cred ;
2008-04-18 01:03:58 +04:00
struct gss_cred * gss_cred = container_of ( oldcred ,
struct gss_cred ,
gc_base ) ;
struct rpc_auth * auth = oldcred - > cr_auth ;
struct auth_cred acred = {
. uid = oldcred - > cr_uid ,
2012-01-03 22:22:46 +04:00
. principal = gss_cred - > gc_principal ,
. machine_cred = ( gss_cred - > gc_principal ! = NULL ? 1 : 0 ) ,
2008-04-18 01:03:58 +04:00
} ;
struct rpc_cred * new ;
new = gss_lookup_cred ( auth , & acred , RPCAUTH_LOOKUP_NEW ) ;
if ( IS_ERR ( new ) )
return PTR_ERR ( new ) ;
2010-07-31 22:29:08 +04:00
task - > tk_rqstp - > rq_cred = new ;
2008-04-18 01:03:58 +04:00
put_rpccred ( oldcred ) ;
return 0 ;
}
2010-05-13 20:55:38 +04:00
static int gss_cred_is_negative_entry ( struct rpc_cred * cred )
{
if ( test_bit ( RPCAUTH_CRED_NEGATIVE , & cred - > cr_flags ) ) {
unsigned long now = jiffies ;
unsigned long begin , expire ;
struct gss_cred * gss_cred ;
gss_cred = container_of ( cred , struct gss_cred , gc_base ) ;
begin = gss_cred - > gc_upcall_timestamp ;
expire = begin + gss_expired_cred_retry_delay * HZ ;
if ( time_in_range_open ( now , begin , expire ) )
return 1 ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Refresh credentials . XXX - finish
*/
static int
gss_refresh ( struct rpc_task * task )
{
2010-07-31 22:29:08 +04:00
struct rpc_cred * cred = task - > tk_rqstp - > rq_cred ;
2008-04-18 01:03:58 +04:00
int ret = 0 ;
2010-05-13 20:55:38 +04:00
if ( gss_cred_is_negative_entry ( cred ) )
return - EKEYEXPIRED ;
2008-04-18 01:03:58 +04:00
if ( ! test_bit ( RPCAUTH_CRED_NEW , & cred - > cr_flags ) & &
! test_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ) {
ret = gss_renew_cred ( task ) ;
if ( ret < 0 )
goto out ;
2010-07-31 22:29:08 +04:00
cred = task - > tk_rqstp - > rq_cred ;
2008-04-18 01:03:58 +04:00
}
2005-04-17 02:20:36 +04:00
2008-04-18 01:03:58 +04:00
if ( test_bit ( RPCAUTH_CRED_NEW , & cred - > cr_flags ) )
ret = gss_refresh_upcall ( task ) ;
out :
return ret ;
2005-04-17 02:20:36 +04:00
}
2007-06-27 01:04:57 +04:00
/* Dummy refresh routine: used only when destroying the context */
static int
gss_refresh_null ( struct rpc_task * task )
{
return - EACCES ;
}
2006-09-27 09:29:38 +04:00
static __be32 *
gss_validate ( struct rpc_task * task , __be32 * p )
2005-04-17 02:20:36 +04:00
{
2010-07-31 22:29:08 +04:00
struct rpc_cred * cred = task - > tk_rqstp - > rq_cred ;
2005-04-17 02:20:36 +04:00
struct gss_cl_ctx * ctx = gss_cred_get_ctx ( cred ) ;
2006-09-27 09:29:38 +04:00
__be32 seq ;
2005-04-17 02:20:36 +04:00
struct kvec iov ;
struct xdr_buf verf_buf ;
struct xdr_netobj mic ;
u32 flav , len ;
u32 maj_stat ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s \n " , task - > tk_pid , __func__ ) ;
2005-04-17 02:20:36 +04:00
flav = ntohl ( * p + + ) ;
if ( ( len = ntohl ( * p + + ) ) > RPC_MAX_AUTH_SIZE )
2007-02-10 02:38:13 +03:00
goto out_bad ;
2005-04-17 02:20:36 +04:00
if ( flav ! = RPC_AUTH_GSS )
goto out_bad ;
seq = htonl ( task - > tk_rqstp - > rq_seqno ) ;
iov . iov_base = & seq ;
iov . iov_len = sizeof ( seq ) ;
xdr_buf_from_iov ( & iov , & verf_buf ) ;
mic . data = ( u8 * ) p ;
mic . len = len ;
2005-10-14 00:55:18 +04:00
maj_stat = gss_verify_mic ( ctx - > gc_gss_ctx , & verf_buf , & mic ) ;
2005-04-17 02:20:36 +04:00
if ( maj_stat = = GSS_S_CONTEXT_EXPIRED )
2007-06-25 18:15:15 +04:00
clear_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ;
2007-06-27 01:04:57 +04:00
if ( maj_stat ) {
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s: gss_verify_mic returned error 0x%08x \n " ,
task - > tk_pid , __func__ , maj_stat ) ;
2005-04-17 02:20:36 +04:00
goto out_bad ;
2007-06-27 01:04:57 +04:00
}
2005-10-14 00:54:53 +04:00
/* We leave it to unwrap to calculate au_rslack. For now we just
* calculate the length of the verifier : */
2007-06-27 22:29:04 +04:00
cred - > cr_auth - > au_verfsize = XDR_QUADLEN ( len ) + 2 ;
2005-04-17 02:20:36 +04:00
gss_put_ctx ( ctx ) ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s: gss_verify_mic succeeded. \n " ,
task - > tk_pid , __func__ ) ;
2005-04-17 02:20:36 +04:00
return p + XDR_QUADLEN ( len ) ;
out_bad :
gss_put_ctx ( ctx ) ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s failed. \n " , task - > tk_pid , __func__ ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2010-12-14 17:59:18 +03:00
static void gss_wrap_req_encode ( kxdreproc_t encode , struct rpc_rqst * rqstp ,
__be32 * p , void * obj )
{
struct xdr_stream xdr ;
xdr_init_encode ( & xdr , & rqstp - > rq_snd_buf , p ) ;
encode ( rqstp , & xdr , obj ) ;
}
2005-04-17 02:20:36 +04:00
static inline int
gss_wrap_req_integ ( struct rpc_cred * cred , struct gss_cl_ctx * ctx ,
2010-12-14 17:59:18 +03:00
kxdreproc_t encode , struct rpc_rqst * rqstp ,
__be32 * p , void * obj )
2005-04-17 02:20:36 +04:00
{
struct xdr_buf * snd_buf = & rqstp - > rq_snd_buf ;
struct xdr_buf integ_buf ;
2006-09-27 09:29:38 +04:00
__be32 * integ_len = NULL ;
2005-04-17 02:20:36 +04:00
struct xdr_netobj mic ;
2006-09-27 09:29:38 +04:00
u32 offset ;
__be32 * q ;
2005-04-17 02:20:36 +04:00
struct kvec * iov ;
u32 maj_stat = 0 ;
int status = - EIO ;
integ_len = p + + ;
offset = ( u8 * ) p - ( u8 * ) snd_buf - > head [ 0 ] . iov_base ;
* p + + = htonl ( rqstp - > rq_seqno ) ;
2010-12-14 17:59:18 +03:00
gss_wrap_req_encode ( encode , rqstp , p , obj ) ;
2005-04-17 02:20:36 +04:00
if ( xdr_buf_subsegment ( snd_buf , & integ_buf ,
offset , snd_buf - > len - offset ) )
return status ;
* integ_len = htonl ( integ_buf . len ) ;
/* guess whether we're in the head or the tail: */
2007-02-10 02:38:13 +03:00
if ( snd_buf - > page_len | | snd_buf - > tail [ 0 ] . iov_len )
2005-04-17 02:20:36 +04:00
iov = snd_buf - > tail ;
else
iov = snd_buf - > head ;
p = iov - > iov_base + iov - > iov_len ;
mic . data = ( u8 * ) ( p + 1 ) ;
2005-10-14 00:55:18 +04:00
maj_stat = gss_get_mic ( ctx - > gc_gss_ctx , & integ_buf , & mic ) ;
2005-04-17 02:20:36 +04:00
status = - EIO ; /* XXX? */
if ( maj_stat = = GSS_S_CONTEXT_EXPIRED )
2007-06-25 18:15:15 +04:00
clear_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ;
2005-04-17 02:20:36 +04:00
else if ( maj_stat )
return status ;
q = xdr_encode_opaque ( p , NULL , mic . len ) ;
offset = ( u8 * ) q - ( u8 * ) p ;
iov - > iov_len + = offset ;
snd_buf - > len + = offset ;
return 0 ;
}
2005-10-14 00:54:58 +04:00
static void
priv_release_snd_buf ( struct rpc_rqst * rqstp )
{
int i ;
for ( i = 0 ; i < rqstp - > rq_enc_pages_num ; i + + )
__free_page ( rqstp - > rq_enc_pages [ i ] ) ;
kfree ( rqstp - > rq_enc_pages ) ;
}
static int
alloc_enc_pages ( struct rpc_rqst * rqstp )
{
struct xdr_buf * snd_buf = & rqstp - > rq_snd_buf ;
int first , last , i ;
if ( snd_buf - > page_len = = 0 ) {
rqstp - > rq_enc_pages_num = 0 ;
return 0 ;
}
first = snd_buf - > page_base > > PAGE_CACHE_SHIFT ;
last = ( snd_buf - > page_base + snd_buf - > page_len - 1 ) > > PAGE_CACHE_SHIFT ;
rqstp - > rq_enc_pages_num = last - first + 1 + 1 ;
rqstp - > rq_enc_pages
= kmalloc ( rqstp - > rq_enc_pages_num * sizeof ( struct page * ) ,
GFP_NOFS ) ;
if ( ! rqstp - > rq_enc_pages )
goto out ;
for ( i = 0 ; i < rqstp - > rq_enc_pages_num ; i + + ) {
rqstp - > rq_enc_pages [ i ] = alloc_page ( GFP_NOFS ) ;
if ( rqstp - > rq_enc_pages [ i ] = = NULL )
goto out_free ;
}
rqstp - > rq_release_snd_buf = priv_release_snd_buf ;
return 0 ;
out_free :
2010-03-19 22:36:22 +03:00
rqstp - > rq_enc_pages_num = i ;
priv_release_snd_buf ( rqstp ) ;
2005-10-14 00:54:58 +04:00
out :
return - EAGAIN ;
}
static inline int
gss_wrap_req_priv ( struct rpc_cred * cred , struct gss_cl_ctx * ctx ,
2010-12-14 17:59:18 +03:00
kxdreproc_t encode , struct rpc_rqst * rqstp ,
__be32 * p , void * obj )
2005-10-14 00:54:58 +04:00
{
struct xdr_buf * snd_buf = & rqstp - > rq_snd_buf ;
u32 offset ;
u32 maj_stat ;
int status ;
2006-09-27 09:29:38 +04:00
__be32 * opaque_len ;
2005-10-14 00:54:58 +04:00
struct page * * inpages ;
int first ;
int pad ;
struct kvec * iov ;
char * tmp ;
opaque_len = p + + ;
offset = ( u8 * ) p - ( u8 * ) snd_buf - > head [ 0 ] . iov_base ;
* p + + = htonl ( rqstp - > rq_seqno ) ;
2010-12-14 17:59:18 +03:00
gss_wrap_req_encode ( encode , rqstp , p , obj ) ;
2005-10-14 00:54:58 +04:00
status = alloc_enc_pages ( rqstp ) ;
if ( status )
return status ;
first = snd_buf - > page_base > > PAGE_CACHE_SHIFT ;
inpages = snd_buf - > pages + first ;
snd_buf - > pages = rqstp - > rq_enc_pages ;
snd_buf - > page_base - = first < < PAGE_CACHE_SHIFT ;
2010-03-17 20:02:47 +03:00
/*
* Give the tail its own page , in case we need extra space in the
* head when wrapping :
*
* call_allocate ( ) allocates twice the slack space required
* by the authentication flavor to rq_callsize .
* For GSS , slack is GSS_CRED_SLACK .
*/
2005-10-14 00:54:58 +04:00
if ( snd_buf - > page_len | | snd_buf - > tail [ 0 ] . iov_len ) {
tmp = page_address ( rqstp - > rq_enc_pages [ rqstp - > rq_enc_pages_num - 1 ] ) ;
memcpy ( tmp , snd_buf - > tail [ 0 ] . iov_base , snd_buf - > tail [ 0 ] . iov_len ) ;
snd_buf - > tail [ 0 ] . iov_base = tmp ;
}
2005-10-14 00:55:18 +04:00
maj_stat = gss_wrap ( ctx - > gc_gss_ctx , offset , snd_buf , inpages ) ;
2010-03-17 20:02:47 +03:00
/* slack space should prevent this ever happening: */
2005-10-14 00:54:58 +04:00
BUG_ON ( snd_buf - > len > snd_buf - > buflen ) ;
2007-02-10 02:38:13 +03:00
status = - EIO ;
2005-10-14 00:54:58 +04:00
/* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
* done anyway , so it ' s safe to put the request on the wire : */
if ( maj_stat = = GSS_S_CONTEXT_EXPIRED )
2007-06-25 18:15:15 +04:00
clear_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ;
2005-10-14 00:54:58 +04:00
else if ( maj_stat )
return status ;
* opaque_len = htonl ( snd_buf - > len - offset ) ;
/* guess whether we're in the head or the tail: */
if ( snd_buf - > page_len | | snd_buf - > tail [ 0 ] . iov_len )
iov = snd_buf - > tail ;
else
iov = snd_buf - > head ;
p = iov - > iov_base + iov - > iov_len ;
pad = 3 - ( ( snd_buf - > len - offset - 1 ) & 3 ) ;
memset ( p , 0 , pad ) ;
iov - > iov_len + = pad ;
snd_buf - > len + = pad ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int
gss_wrap_req ( struct rpc_task * task ,
2010-12-14 17:59:18 +03:00
kxdreproc_t encode , void * rqstp , __be32 * p , void * obj )
2005-04-17 02:20:36 +04:00
{
2010-07-31 22:29:08 +04:00
struct rpc_cred * cred = task - > tk_rqstp - > rq_cred ;
2005-04-17 02:20:36 +04:00
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred ,
gc_base ) ;
struct gss_cl_ctx * ctx = gss_cred_get_ctx ( cred ) ;
int status = - EIO ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s \n " , task - > tk_pid , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( ctx - > gc_proc ! = RPC_GSS_PROC_DATA ) {
/* The spec seems a little ambiguous here, but I think that not
* wrapping context destruction requests makes the most sense .
*/
2010-12-14 17:59:18 +03:00
gss_wrap_req_encode ( encode , rqstp , p , obj ) ;
status = 0 ;
2005-04-17 02:20:36 +04:00
goto out ;
}
switch ( gss_cred - > gc_service ) {
2011-07-01 13:43:12 +04:00
case RPC_GSS_SVC_NONE :
gss_wrap_req_encode ( encode , rqstp , p , obj ) ;
status = 0 ;
break ;
case RPC_GSS_SVC_INTEGRITY :
status = gss_wrap_req_integ ( cred , ctx , encode , rqstp , p , obj ) ;
break ;
case RPC_GSS_SVC_PRIVACY :
status = gss_wrap_req_priv ( cred , ctx , encode , rqstp , p , obj ) ;
break ;
2005-04-17 02:20:36 +04:00
}
out :
gss_put_ctx ( ctx ) ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s returning %d \n " , task - > tk_pid , __func__ , status ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
static inline int
gss_unwrap_resp_integ ( struct rpc_cred * cred , struct gss_cl_ctx * ctx ,
2006-09-27 09:29:38 +04:00
struct rpc_rqst * rqstp , __be32 * * p )
2005-04-17 02:20:36 +04:00
{
struct xdr_buf * rcv_buf = & rqstp - > rq_rcv_buf ;
struct xdr_buf integ_buf ;
struct xdr_netobj mic ;
u32 data_offset , mic_offset ;
u32 integ_len ;
u32 maj_stat ;
int status = - EIO ;
integ_len = ntohl ( * ( * p ) + + ) ;
if ( integ_len & 3 )
return status ;
data_offset = ( u8 * ) ( * p ) - ( u8 * ) rcv_buf - > head [ 0 ] . iov_base ;
mic_offset = integ_len + data_offset ;
if ( mic_offset > rcv_buf - > len )
return status ;
if ( ntohl ( * ( * p ) + + ) ! = rqstp - > rq_seqno )
return status ;
if ( xdr_buf_subsegment ( rcv_buf , & integ_buf , data_offset ,
mic_offset - data_offset ) )
return status ;
if ( xdr_buf_read_netobj ( rcv_buf , & mic , mic_offset ) )
return status ;
2005-10-14 00:55:18 +04:00
maj_stat = gss_verify_mic ( ctx - > gc_gss_ctx , & integ_buf , & mic ) ;
2005-04-17 02:20:36 +04:00
if ( maj_stat = = GSS_S_CONTEXT_EXPIRED )
2007-06-25 18:15:15 +04:00
clear_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ;
2005-04-17 02:20:36 +04:00
if ( maj_stat ! = GSS_S_COMPLETE )
return status ;
return 0 ;
}
2005-10-14 00:54:58 +04:00
static inline int
gss_unwrap_resp_priv ( struct rpc_cred * cred , struct gss_cl_ctx * ctx ,
2006-09-27 09:29:38 +04:00
struct rpc_rqst * rqstp , __be32 * * p )
2005-10-14 00:54:58 +04:00
{
struct xdr_buf * rcv_buf = & rqstp - > rq_rcv_buf ;
u32 offset ;
u32 opaque_len ;
u32 maj_stat ;
int status = - EIO ;
opaque_len = ntohl ( * ( * p ) + + ) ;
offset = ( u8 * ) ( * p ) - ( u8 * ) rcv_buf - > head [ 0 ] . iov_base ;
if ( offset + opaque_len > rcv_buf - > len )
return status ;
/* remove padding: */
rcv_buf - > len = offset + opaque_len ;
2005-10-14 00:55:18 +04:00
maj_stat = gss_unwrap ( ctx - > gc_gss_ctx , offset , rcv_buf ) ;
2005-10-14 00:54:58 +04:00
if ( maj_stat = = GSS_S_CONTEXT_EXPIRED )
2007-06-25 18:15:15 +04:00
clear_bit ( RPCAUTH_CRED_UPTODATE , & cred - > cr_flags ) ;
2005-10-14 00:54:58 +04:00
if ( maj_stat ! = GSS_S_COMPLETE )
return status ;
if ( ntohl ( * ( * p ) + + ) ! = rqstp - > rq_seqno )
return status ;
return 0 ;
}
2010-12-14 17:59:29 +03:00
static int
gss_unwrap_req_decode ( kxdrdproc_t decode , struct rpc_rqst * rqstp ,
__be32 * p , void * obj )
{
struct xdr_stream xdr ;
xdr_init_decode ( & xdr , & rqstp - > rq_rcv_buf , p ) ;
return decode ( rqstp , & xdr , obj ) ;
}
2005-10-14 00:54:58 +04:00
2005-04-17 02:20:36 +04:00
static int
gss_unwrap_resp ( struct rpc_task * task ,
2010-12-14 17:59:29 +03:00
kxdrdproc_t decode , void * rqstp , __be32 * p , void * obj )
2005-04-17 02:20:36 +04:00
{
2010-07-31 22:29:08 +04:00
struct rpc_cred * cred = task - > tk_rqstp - > rq_cred ;
2005-04-17 02:20:36 +04:00
struct gss_cred * gss_cred = container_of ( cred , struct gss_cred ,
gc_base ) ;
struct gss_cl_ctx * ctx = gss_cred_get_ctx ( cred ) ;
2006-09-27 09:29:38 +04:00
__be32 * savedp = p ;
2005-10-14 00:54:58 +04:00
struct kvec * head = ( ( struct rpc_rqst * ) rqstp ) - > rq_rcv_buf . head ;
int savedlen = head - > iov_len ;
2005-04-17 02:20:36 +04:00
int status = - EIO ;
if ( ctx - > gc_proc ! = RPC_GSS_PROC_DATA )
goto out_decode ;
switch ( gss_cred - > gc_service ) {
2011-07-01 13:43:12 +04:00
case RPC_GSS_SVC_NONE :
break ;
case RPC_GSS_SVC_INTEGRITY :
status = gss_unwrap_resp_integ ( cred , ctx , rqstp , & p ) ;
if ( status )
goto out ;
break ;
case RPC_GSS_SVC_PRIVACY :
status = gss_unwrap_resp_priv ( cred , ctx , rqstp , & p ) ;
if ( status )
goto out ;
break ;
2005-04-17 02:20:36 +04:00
}
2005-10-14 00:54:53 +04:00
/* take into account extra slack for integrity and privacy cases: */
2007-06-27 22:29:04 +04:00
cred - > cr_auth - > au_rslack = cred - > cr_auth - > au_verfsize + ( p - savedp )
2005-10-14 00:54:58 +04:00
+ ( savedlen - head - > iov_len ) ;
2005-04-17 02:20:36 +04:00
out_decode :
2010-12-14 17:59:29 +03:00
status = gss_unwrap_req_decode ( decode , rqstp , p , obj ) ;
2005-04-17 02:20:36 +04:00
out :
gss_put_ctx ( ctx ) ;
2012-09-15 01:23:43 +04:00
dprintk ( " RPC: %5u %s returning %d \n " ,
task - > tk_pid , __func__ , status ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
2007-02-10 02:38:13 +03:00
2007-06-24 04:17:58 +04:00
static const struct rpc_authops authgss_ops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. au_flavor = RPC_AUTH_GSS ,
. au_name = " RPCSEC_GSS " ,
. create = gss_create ,
. destroy = gss_destroy ,
. lookup_cred = gss_lookup_cred ,
2012-01-11 19:18:17 +04:00
. crcreate = gss_create_cred ,
. pipes_create = gss_pipes_dentries_create ,
. pipes_destroy = gss_pipes_dentries_destroy ,
2012-07-12 00:31:08 +04:00
. list_pseudoflavors = gss_mech_list_pseudoflavors ,
2013-03-16 23:54:43 +04:00
. info2flavor = gss_mech_info2flavor ,
2013-03-16 23:55:10 +04:00
. flavor2info = gss_mech_flavor2info ,
2005-04-17 02:20:36 +04:00
} ;
2007-06-24 04:17:58 +04:00
static const struct rpc_credops gss_credops = {
2005-04-17 02:20:36 +04:00
. cr_name = " AUTH_GSS " ,
. crdestroy = gss_destroy_cred ,
2006-02-01 20:19:27 +03:00
. cr_init = gss_cred_init ,
2008-03-12 23:21:07 +03:00
. crbind = rpcauth_generic_bind_cred ,
2005-04-17 02:20:36 +04:00
. crmatch = gss_match ,
. crmarshal = gss_marshal ,
. crrefresh = gss_refresh ,
. crvalidate = gss_validate ,
. crwrap_req = gss_wrap_req ,
. crunwrap_resp = gss_unwrap_resp ,
} ;
2007-06-27 01:04:57 +04:00
static const struct rpc_credops gss_nullops = {
. cr_name = " AUTH_GSS " ,
2008-12-23 23:21:57 +03:00
. crdestroy = gss_destroy_nullcred ,
2008-03-12 23:21:07 +03:00
. crbind = rpcauth_generic_bind_cred ,
2007-06-27 01:04:57 +04:00
. crmatch = gss_match ,
. crmarshal = gss_marshal ,
. crrefresh = gss_refresh_null ,
. crvalidate = gss_validate ,
. crwrap_req = gss_wrap_req ,
. crunwrap_resp = gss_unwrap_resp ,
} ;
2009-08-09 23:14:15 +04:00
static const struct rpc_pipe_ops gss_upcall_ops_v0 = {
2011-09-23 05:50:10 +04:00
. upcall = rpc_pipe_generic_upcall ,
2008-12-24 00:16:37 +03:00
. downcall = gss_pipe_downcall ,
. destroy_msg = gss_pipe_destroy_msg ,
. open_pipe = gss_pipe_open_v0 ,
. release_pipe = gss_pipe_release ,
} ;
2009-08-09 23:14:15 +04:00
static const struct rpc_pipe_ops gss_upcall_ops_v1 = {
2011-09-23 05:50:10 +04:00
. upcall = rpc_pipe_generic_upcall ,
2005-04-17 02:20:36 +04:00
. downcall = gss_pipe_downcall ,
. destroy_msg = gss_pipe_destroy_msg ,
2008-12-24 00:16:37 +03:00
. open_pipe = gss_pipe_open_v1 ,
2005-04-17 02:20:36 +04:00
. release_pipe = gss_pipe_release ,
} ;
2012-01-19 21:42:37 +04:00
static __net_init int rpcsec_gss_init_net ( struct net * net )
{
return gss_svc_init_net ( net ) ;
}
static __net_exit void rpcsec_gss_exit_net ( struct net * net )
{
gss_svc_shutdown_net ( net ) ;
}
static struct pernet_operations rpcsec_gss_net_ops = {
. init = rpcsec_gss_init_net ,
. exit = rpcsec_gss_exit_net ,
} ;
2005-04-17 02:20:36 +04:00
/*
* Initialize RPCSEC_GSS module
*/
static int __init init_rpcsec_gss ( void )
{
int err = 0 ;
err = rpcauth_register ( & authgss_ops ) ;
if ( err )
goto out ;
err = gss_svc_init ( ) ;
if ( err )
goto out_unregister ;
2012-01-19 21:42:37 +04:00
err = register_pernet_subsys ( & rpcsec_gss_net_ops ) ;
if ( err )
goto out_svc_exit ;
2008-12-24 00:10:52 +03:00
rpc_init_wait_queue ( & pipe_version_rpc_waitqueue , " gss pipe version " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2012-01-19 21:42:37 +04:00
out_svc_exit :
gss_svc_shutdown ( ) ;
2005-04-17 02:20:36 +04:00
out_unregister :
rpcauth_unregister ( & authgss_ops ) ;
out :
return err ;
}
static void __exit exit_rpcsec_gss ( void )
{
2012-01-19 21:42:37 +04:00
unregister_pernet_subsys ( & rpcsec_gss_net_ops ) ;
2005-04-17 02:20:36 +04:00
gss_svc_shutdown ( ) ;
rpcauth_unregister ( & authgss_ops ) ;
2009-06-08 07:11:48 +04:00
rcu_barrier ( ) ; /* Wait for completion of call_rcu()'s */
2005-04-17 02:20:36 +04:00
}
SUNRPC: Missing module alias for auth_rpcgss.ko
Commit f344f6df "SUNRPC: Auto-load RPC authentication kernel
modules", Mon Mar 20 13:44:08 2006, adds a request_module() call
in rpcauth_create() to auto-load RPC security modules when a ULP
tries to create a credential of that flavor.
In rpcauth_create(), the name of the module to load is built like
this:
request_module("rpc-auth-%u", flavor);
This means that for, say, RPC_AUTH_GSS, request_module() is looking
for a module or alias called "rpc-auth-6".
The GSS module is named "auth_rpcgss", and commit f344f6df does not
add any new module aliases. There is also no such alias provided in
/etc/modprobe.d on my system (Fedora 16). Without this alias, the
GSS module is not loaded on demand.
This is used by rpcauth_create(). The pseudoflavor_to_flavor() call
can return RPC_AUTH_GSS, which is passed to request_module().
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-03-16 23:54:16 +04:00
MODULE_ALIAS ( " rpc-auth-6 " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2010-05-13 20:55:38 +04:00
module_param_named ( expired_cred_retry_delay ,
gss_expired_cred_retry_delay ,
uint , 0644 ) ;
MODULE_PARM_DESC ( expired_cred_retry_delay , " Timeout (in seconds) until "
" the RPC engine retries an expired credential " ) ;
2005-04-17 02:20:36 +04:00
module_init ( init_rpcsec_gss )
module_exit ( exit_rpcsec_gss )