2005-04-17 02:20:36 +04:00
/*
* linux / net / sunrpc / gss_krb5_mech . c
*
2010-03-17 20:02:51 +03:00
* Copyright ( c ) 2001 - 2008 The Regents of the University of Michigan .
2005-04-17 02:20:36 +04:00
* All rights reserved .
*
* Andy Adamson < andros @ umich . edu >
* 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 .
*
*/
2006-08-22 14:33:54 +04:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/sunrpc/auth.h>
# include <linux/sunrpc/gss_krb5.h>
# include <linux/sunrpc/xdr.h>
# include <linux/crypto.h>
# ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
# endif
2010-03-17 20:02:51 +03:00
static const struct gss_krb5_enctype supported_gss_krb5_enctypes [ ] = {
/*
* DES ( All DES enctypes are mapped to the same gss functionality )
*/
{
. etype = ENCTYPE_DES_CBC_RAW ,
. ctype = CKSUMTYPE_RSA_MD5 ,
. name = " des-cbc-crc " ,
. encrypt_name = " cbc(des) " ,
. cksum_name = " md5 " ,
. encrypt = krb5_encrypt ,
. decrypt = krb5_decrypt ,
. signalg = SGN_ALG_DES_MAC_MD5 ,
. sealalg = SEAL_ALG_DES ,
. keybytes = 7 ,
. keylength = 8 ,
. blocksize = 8 ,
. cksumlength = 8 ,
2010-03-17 20:02:52 +03:00
. keyed_cksum = 0 ,
2010-03-17 20:02:51 +03:00
} ,
} ;
static const int num_supported_enctypes =
ARRAY_SIZE ( supported_gss_krb5_enctypes ) ;
static int
supported_gss_krb5_enctype ( int etype )
{
int i ;
for ( i = 0 ; i < num_supported_enctypes ; i + + )
if ( supported_gss_krb5_enctypes [ i ] . etype = = etype )
return 1 ;
return 0 ;
}
static const struct gss_krb5_enctype *
get_gss_krb5_enctype ( int etype )
{
int i ;
for ( i = 0 ; i < num_supported_enctypes ; i + + )
if ( supported_gss_krb5_enctypes [ i ] . etype = = etype )
return & supported_gss_krb5_enctypes [ i ] ;
return NULL ;
}
2005-04-17 02:20:36 +04:00
static const void *
simple_get_bytes ( const void * p , const void * end , void * res , int 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 const void *
simple_get_netobj ( const void * p , const void * end , struct xdr_netobj * res )
{
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
res - > data = kmemdup ( p , len , GFP_NOFS ) ;
2005-04-17 02:20:36 +04:00
if ( unlikely ( res - > data = = NULL ) )
return ERR_PTR ( - ENOMEM ) ;
res - > len = len ;
return q ;
}
static inline const void *
2010-03-17 20:02:51 +03:00
get_key ( const void * p , const void * end ,
struct krb5_ctx * ctx , struct crypto_blkcipher * * res )
2005-04-17 02:20:36 +04:00
{
struct xdr_netobj key ;
2006-08-22 14:33:54 +04:00
int alg ;
2005-04-17 02:20:36 +04:00
p = simple_get_bytes ( p , end , & alg , sizeof ( alg ) ) ;
if ( IS_ERR ( p ) )
goto out_err ;
2010-03-17 20:02:51 +03:00
switch ( alg ) {
case ENCTYPE_DES_CBC_CRC :
case ENCTYPE_DES_CBC_MD4 :
case ENCTYPE_DES_CBC_MD5 :
/* Map all these key types to ENCTYPE_DES_CBC_RAW */
alg = ENCTYPE_DES_CBC_RAW ;
break ;
}
if ( ! supported_gss_krb5_enctype ( alg ) ) {
printk ( KERN_WARNING " gss_kerberos_mech: unsupported "
" encryption key algorithm %d \n " , alg ) ;
goto out_err ;
}
2005-04-17 02:20:36 +04:00
p = simple_get_netobj ( p , end , & key ) ;
if ( IS_ERR ( p ) )
goto out_err ;
2010-03-17 20:02:51 +03:00
* res = crypto_alloc_blkcipher ( ctx - > gk5e - > encrypt_name , 0 ,
CRYPTO_ALG_ASYNC ) ;
2006-08-22 14:33:54 +04:00
if ( IS_ERR ( * res ) ) {
2010-03-17 20:02:51 +03:00
printk ( KERN_WARNING " gss_kerberos_mech: unable to initialize "
" crypto algorithm %s \n " , ctx - > gk5e - > encrypt_name ) ;
2006-08-22 14:33:54 +04:00
* res = NULL ;
2005-04-17 02:20:36 +04:00
goto out_err_free_key ;
2006-01-03 11:56:01 +03:00
}
2006-08-22 14:33:54 +04:00
if ( crypto_blkcipher_setkey ( * res , key . data , key . len ) ) {
2010-03-17 20:02:51 +03:00
printk ( KERN_WARNING " gss_kerberos_mech: error setting key for "
" crypto algorithm %s \n " , ctx - > gk5e - > encrypt_name ) ;
2005-04-17 02:20:36 +04:00
goto out_err_free_tfm ;
2006-01-03 11:56:01 +03:00
}
2005-04-17 02:20:36 +04:00
kfree ( key . data ) ;
return p ;
out_err_free_tfm :
2006-08-22 14:33:54 +04:00
crypto_free_blkcipher ( * res ) ;
2005-04-17 02:20:36 +04:00
out_err_free_key :
kfree ( key . data ) ;
p = ERR_PTR ( - EINVAL ) ;
out_err :
return p ;
}
static int
2010-03-17 20:02:50 +03:00
gss_import_v1_context ( const void * p , const void * end , struct krb5_ctx * ctx )
2005-04-17 02:20:36 +04:00
{
2006-12-05 04:22:35 +03:00
int tmp ;
2005-04-17 02:20:36 +04:00
p = simple_get_bytes ( p , end , & ctx - > initiate , sizeof ( ctx - > initiate ) ) ;
if ( IS_ERR ( p ) )
2010-03-17 20:02:50 +03:00
goto out_err ;
/* Old format supports only DES! Any other enctype uses new format */
2010-03-17 20:02:49 +03:00
ctx - > enctype = ENCTYPE_DES_CBC_RAW ;
2010-03-17 20:02:50 +03:00
2010-03-17 20:02:51 +03:00
ctx - > gk5e = get_gss_krb5_enctype ( ctx - > enctype ) ;
if ( ctx - > gk5e = = NULL )
goto out_err ;
2006-12-05 04:22:41 +03:00
/* The downcall format was designed before we completely understood
* the uses of the context fields ; so it includes some stuff we
* just give some minimal sanity - checking , and some we ignore
* completely ( like the next twenty bytes ) : */
if ( unlikely ( p + 20 > end | | p + 20 < p ) )
2010-03-17 20:02:50 +03:00
goto out_err ;
2006-12-05 04:22:41 +03:00
p + = 20 ;
2006-12-05 04:22:35 +03:00
p = simple_get_bytes ( p , end , & tmp , sizeof ( tmp ) ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) )
2010-03-17 20:02:50 +03:00
goto out_err ;
2007-11-10 02:42:09 +03:00
if ( tmp ! = SGN_ALG_DES_MAC_MD5 ) {
p = ERR_PTR ( - ENOSYS ) ;
2010-03-17 20:02:50 +03:00
goto out_err ;
2007-11-10 02:42:09 +03:00
}
2006-12-05 04:22:40 +03:00
p = simple_get_bytes ( p , end , & tmp , sizeof ( tmp ) ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) )
2010-03-17 20:02:50 +03:00
goto out_err ;
2007-11-10 02:42:09 +03:00
if ( tmp ! = SEAL_ALG_DES ) {
p = ERR_PTR ( - ENOSYS ) ;
2010-03-17 20:02:50 +03:00
goto out_err ;
2007-11-10 02:42:09 +03:00
}
2005-04-17 02:20:36 +04:00
p = simple_get_bytes ( p , end , & ctx - > endtime , sizeof ( ctx - > endtime ) ) ;
if ( IS_ERR ( p ) )
2010-03-17 20:02:50 +03:00
goto out_err ;
2005-04-17 02:20:36 +04:00
p = simple_get_bytes ( p , end , & ctx - > seq_send , sizeof ( ctx - > seq_send ) ) ;
if ( IS_ERR ( p ) )
2010-03-17 20:02:50 +03:00
goto out_err ;
2005-04-17 02:20:36 +04:00
p = simple_get_netobj ( p , end , & ctx - > mech_used ) ;
if ( IS_ERR ( p ) )
2010-03-17 20:02:50 +03:00
goto out_err ;
2010-03-17 20:02:51 +03:00
p = get_key ( p , end , ctx , & ctx - > enc ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) )
goto out_err_free_mech ;
2010-03-17 20:02:51 +03:00
p = get_key ( p , end , ctx , & ctx - > seq ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) )
goto out_err_free_key1 ;
if ( p ! = end ) {
p = ERR_PTR ( - EFAULT ) ;
goto out_err_free_key2 ;
}
return 0 ;
out_err_free_key2 :
2006-08-22 14:33:54 +04:00
crypto_free_blkcipher ( ctx - > seq ) ;
2005-04-17 02:20:36 +04:00
out_err_free_key1 :
2006-08-22 14:33:54 +04:00
crypto_free_blkcipher ( ctx - > enc ) ;
2005-04-17 02:20:36 +04:00
out_err_free_mech :
kfree ( ctx - > mech_used . data ) ;
out_err :
return PTR_ERR ( p ) ;
}
2010-03-17 20:02:50 +03:00
static int
gss_import_sec_context_kerberos ( const void * p , size_t len ,
struct gss_ctx * ctx_id )
{
const void * end = ( const void * ) ( ( const char * ) p + len ) ;
struct krb5_ctx * ctx ;
int ret ;
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ctx = = NULL )
return - ENOMEM ;
if ( len = = 85 )
ret = gss_import_v1_context ( p , end , ctx ) ;
else
ret = - EINVAL ;
if ( ret = = 0 )
ctx_id - > internal_ctx_id = ctx ;
else
kfree ( ctx ) ;
dprintk ( " RPC: %s: returning %d \n " , __func__ , ret ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
static void
gss_delete_sec_context_kerberos ( void * internal_ctx ) {
struct krb5_ctx * kctx = internal_ctx ;
2006-08-22 14:33:54 +04:00
crypto_free_blkcipher ( kctx - > seq ) ;
crypto_free_blkcipher ( kctx - > enc ) ;
2005-09-02 04:44:29 +04:00
kfree ( kctx - > mech_used . data ) ;
2005-04-17 02:20:36 +04:00
kfree ( kctx ) ;
}
2007-06-24 04:17:58 +04:00
static const struct gss_api_ops gss_kerberos_ops = {
2005-04-17 02:20:36 +04:00
. gss_import_sec_context = gss_import_sec_context_kerberos ,
. gss_get_mic = gss_get_mic_kerberos ,
. gss_verify_mic = gss_verify_mic_kerberos ,
2005-10-14 00:55:13 +04:00
. gss_wrap = gss_wrap_kerberos ,
. gss_unwrap = gss_unwrap_kerberos ,
2005-04-17 02:20:36 +04:00
. gss_delete_sec_context = gss_delete_sec_context_kerberos ,
} ;
static struct pf_desc gss_kerberos_pfs [ ] = {
[ 0 ] = {
. pseudoflavor = RPC_AUTH_GSS_KRB5 ,
. service = RPC_GSS_SVC_NONE ,
. name = " krb5 " ,
} ,
[ 1 ] = {
. pseudoflavor = RPC_AUTH_GSS_KRB5I ,
. service = RPC_GSS_SVC_INTEGRITY ,
. name = " krb5i " ,
} ,
2005-10-14 00:55:13 +04:00
[ 2 ] = {
. pseudoflavor = RPC_AUTH_GSS_KRB5P ,
. service = RPC_GSS_SVC_PRIVACY ,
. name = " krb5p " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
static struct gss_api_mech gss_kerberos_mech = {
. gm_name = " krb5 " ,
. gm_owner = THIS_MODULE ,
2007-07-17 15:04:50 +04:00
. gm_oid = { 9 , ( void * ) " \x2a \x86 \x48 \x86 \xf7 \x12 \x01 \x02 \x02 " } ,
2005-04-17 02:20:36 +04:00
. gm_ops = & gss_kerberos_ops ,
. gm_pf_num = ARRAY_SIZE ( gss_kerberos_pfs ) ,
. gm_pfs = gss_kerberos_pfs ,
} ;
static int __init init_kerberos_module ( void )
{
int status ;
status = gss_mech_register ( & gss_kerberos_mech ) ;
if ( status )
printk ( " Failed to register kerberos gss mechanism! \n " ) ;
return status ;
}
static void __exit cleanup_kerberos_module ( void )
{
gss_mech_unregister ( & gss_kerberos_mech ) ;
}
MODULE_LICENSE ( " GPL " ) ;
module_init ( init_kerberos_module ) ;
module_exit ( cleanup_kerberos_module ) ;