2005-04-16 15:20:36 -07:00
/*
* linux / net / sunrpc / gss_mech_switch . c
*
* Copyright ( c ) 2001 The Regents of the University of Michigan .
* All rights reserved .
*
* J . Bruce Fields < bfields @ 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/types.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/sunrpc/msg_prot.h>
# include <linux/sunrpc/gss_asn1.h>
# include <linux/sunrpc/auth_gss.h>
# include <linux/sunrpc/svcauth_gss.h>
# include <linux/sunrpc/gss_err.h>
# include <linux/sunrpc/sched.h>
# include <linux/sunrpc/gss_api.h>
# include <linux/sunrpc/clnt.h>
# ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
# endif
static LIST_HEAD ( registered_mechs ) ;
static DEFINE_SPINLOCK ( registered_mechs_lock ) ;
static void
gss_mech_free ( struct gss_api_mech * gm )
{
struct pf_desc * pf ;
int i ;
for ( i = 0 ; i < gm - > gm_pf_num ; i + + ) {
pf = & gm - > gm_pfs [ i ] ;
2005-11-08 09:41:34 -08:00
kfree ( pf - > auth_domain_name ) ;
2005-04-16 15:20:36 -07:00
pf - > auth_domain_name = NULL ;
}
}
static inline char *
make_auth_domain_name ( char * name )
{
static char * prefix = " gss/ " ;
char * new ;
new = kmalloc ( strlen ( name ) + strlen ( prefix ) + 1 , GFP_KERNEL ) ;
if ( new ) {
strcpy ( new , prefix ) ;
strcat ( new , name ) ;
}
return new ;
}
static int
gss_mech_svc_setup ( struct gss_api_mech * gm )
{
struct pf_desc * pf ;
int i , status ;
for ( i = 0 ; i < gm - > gm_pf_num ; i + + ) {
pf = & gm - > gm_pfs [ i ] ;
pf - > auth_domain_name = make_auth_domain_name ( pf - > name ) ;
status = - ENOMEM ;
if ( pf - > auth_domain_name = = NULL )
goto out ;
status = svcauth_gss_register_pseudoflavor ( pf - > pseudoflavor ,
pf - > auth_domain_name ) ;
if ( status )
goto out ;
}
return 0 ;
out :
gss_mech_free ( gm ) ;
return status ;
}
int
gss_mech_register ( struct gss_api_mech * gm )
{
int status ;
status = gss_mech_svc_setup ( gm ) ;
if ( status )
return status ;
spin_lock ( & registered_mechs_lock ) ;
list_add ( & gm - > gm_list , & registered_mechs ) ;
spin_unlock ( & registered_mechs_lock ) ;
dprintk ( " RPC: registered gss mechanism %s \n " , gm - > gm_name ) ;
return 0 ;
}
EXPORT_SYMBOL ( gss_mech_register ) ;
void
gss_mech_unregister ( struct gss_api_mech * gm )
{
spin_lock ( & registered_mechs_lock ) ;
list_del ( & gm - > gm_list ) ;
spin_unlock ( & registered_mechs_lock ) ;
dprintk ( " RPC: unregistered gss mechanism %s \n " , gm - > gm_name ) ;
gss_mech_free ( gm ) ;
}
EXPORT_SYMBOL ( gss_mech_unregister ) ;
struct gss_api_mech *
gss_mech_get ( struct gss_api_mech * gm )
{
__module_get ( gm - > gm_owner ) ;
return gm ;
}
EXPORT_SYMBOL ( gss_mech_get ) ;
struct gss_api_mech *
gss_mech_get_by_name ( const char * name )
{
struct gss_api_mech * pos , * gm = NULL ;
spin_lock ( & registered_mechs_lock ) ;
list_for_each_entry ( pos , & registered_mechs , gm_list ) {
if ( 0 = = strcmp ( name , pos - > gm_name ) ) {
if ( try_module_get ( pos - > gm_owner ) )
gm = pos ;
break ;
}
}
spin_unlock ( & registered_mechs_lock ) ;
return gm ;
}
EXPORT_SYMBOL ( gss_mech_get_by_name ) ;
static inline int
mech_supports_pseudoflavor ( struct gss_api_mech * gm , u32 pseudoflavor )
{
int i ;
for ( i = 0 ; i < gm - > gm_pf_num ; i + + ) {
if ( gm - > gm_pfs [ i ] . pseudoflavor = = pseudoflavor )
return 1 ;
}
return 0 ;
}
struct gss_api_mech *
gss_mech_get_by_pseudoflavor ( u32 pseudoflavor )
{
struct gss_api_mech * pos , * gm = NULL ;
spin_lock ( & registered_mechs_lock ) ;
list_for_each_entry ( pos , & registered_mechs , gm_list ) {
if ( ! mech_supports_pseudoflavor ( pos , pseudoflavor ) ) {
module_put ( pos - > gm_owner ) ;
continue ;
}
if ( try_module_get ( pos - > gm_owner ) )
gm = pos ;
break ;
}
spin_unlock ( & registered_mechs_lock ) ;
return gm ;
}
EXPORT_SYMBOL ( gss_mech_get_by_pseudoflavor ) ;
u32
gss_pseudoflavor_to_service ( struct gss_api_mech * gm , u32 pseudoflavor )
{
int i ;
for ( i = 0 ; i < gm - > gm_pf_num ; i + + ) {
if ( gm - > gm_pfs [ i ] . pseudoflavor = = pseudoflavor )
return gm - > gm_pfs [ i ] . service ;
}
return 0 ;
}
EXPORT_SYMBOL ( gss_pseudoflavor_to_service ) ;
char *
gss_service_to_auth_domain_name ( struct gss_api_mech * gm , u32 service )
{
int i ;
for ( i = 0 ; i < gm - > gm_pf_num ; i + + ) {
if ( gm - > gm_pfs [ i ] . service = = service )
return gm - > gm_pfs [ i ] . auth_domain_name ;
}
return NULL ;
}
EXPORT_SYMBOL ( gss_service_to_auth_domain_name ) ;
void
gss_mech_put ( struct gss_api_mech * gm )
{
2006-06-30 01:56:16 -07:00
if ( gm )
module_put ( gm - > gm_owner ) ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( gss_mech_put ) ;
/* The mech could probably be determined from the token instead, but it's just
* as easy for now to pass it in . */
int
gss_import_sec_context ( const void * input_token , size_t bufsize ,
struct gss_api_mech * mech ,
struct gss_ctx * * ctx_id )
{
if ( ! ( * ctx_id = kmalloc ( sizeof ( * * ctx_id ) , GFP_KERNEL ) ) )
return GSS_S_FAILURE ;
memset ( * ctx_id , 0 , sizeof ( * * ctx_id ) ) ;
( * ctx_id ) - > mech_type = gss_mech_get ( mech ) ;
return mech - > gm_ops
- > gss_import_sec_context ( input_token , bufsize , * ctx_id ) ;
}
/* gss_get_mic: compute a mic over message and return mic_token. */
u32
gss_get_mic ( struct gss_ctx * context_handle ,
struct xdr_buf * message ,
struct xdr_netobj * mic_token )
{
return context_handle - > mech_type - > gm_ops
- > gss_get_mic ( context_handle ,
message ,
mic_token ) ;
}
/* gss_verify_mic: check whether the provided mic_token verifies message. */
u32
gss_verify_mic ( struct gss_ctx * context_handle ,
struct xdr_buf * message ,
2005-10-13 16:55:18 -04:00
struct xdr_netobj * mic_token )
2005-04-16 15:20:36 -07:00
{
return context_handle - > mech_type - > gm_ops
- > gss_verify_mic ( context_handle ,
message ,
2005-10-13 16:55:18 -04:00
mic_token ) ;
2005-04-16 15:20:36 -07:00
}
2005-10-13 16:54:37 -04:00
u32
gss_wrap ( struct gss_ctx * ctx_id ,
int offset ,
struct xdr_buf * buf ,
struct page * * inpages )
{
return ctx_id - > mech_type - > gm_ops
2005-10-13 16:55:18 -04:00
- > gss_wrap ( ctx_id , offset , buf , inpages ) ;
2005-10-13 16:54:37 -04:00
}
u32
gss_unwrap ( struct gss_ctx * ctx_id ,
int offset ,
struct xdr_buf * buf )
{
return ctx_id - > mech_type - > gm_ops
2005-10-13 16:55:18 -04:00
- > gss_unwrap ( ctx_id , offset , buf ) ;
2005-10-13 16:54:37 -04:00
}
2005-04-16 15:20:36 -07:00
/* gss_delete_sec_context: free all resources associated with context_handle.
* Note this differs from the RFC 2744 - specified prototype in that we don ' t
* bother returning an output token , since it would never be used anyway . */
u32
gss_delete_sec_context ( struct gss_ctx * * context_handle )
{
dprintk ( " RPC: gss_delete_sec_context deleting %p \n " ,
* context_handle ) ;
if ( ! * context_handle )
return ( GSS_S_NO_CONTEXT ) ;
if ( ( * context_handle ) - > internal_ctx_id ! = 0 )
( * context_handle ) - > mech_type - > gm_ops
- > gss_delete_sec_context ( ( * context_handle )
- > internal_ctx_id ) ;
2006-06-30 01:56:16 -07:00
gss_mech_put ( ( * context_handle ) - > mech_type ) ;
2005-04-16 15:20:36 -07:00
kfree ( * context_handle ) ;
* context_handle = NULL ;
return GSS_S_COMPLETE ;
}