2005-11-08 20:40:47 +03:00
/*
* ppp_mppe . c - interface MPPE to the PPP code .
* This version is for use with Linux kernel 2.6 .14 +
*
* By Frank Cusack < fcusack @ fcusack . com > .
* Copyright ( c ) 2002 , 2003 , 2004 Google , Inc .
* All rights reserved .
*
* License :
* Permission to use , copy , modify , and distribute this software and its
* documentation is hereby granted , provided that the above copyright
* notice appears in all copies . This software is provided without any
* warranty , express or implied .
*
* ALTERNATIVELY , provided that this notice is retained in full , this product
* may be distributed under the terms of the GNU General Public License ( GPL ) ,
* in which case the provisions of the GPL apply INSTEAD OF those given above .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*
* Changelog :
* 08 / 12 / 05 - Matt Domsch < Matt_Domsch @ dell . com >
* Only need extra skb padding on transmit , not receive .
* 06 / 18 / 04 - Matt Domsch < Matt_Domsch @ dell . com > , Oleg Makarenko < mole @ quadra . ru >
* Use Linux kernel 2.6 arc4 and sha1 routines rather than
* providing our own .
* 2 / 15 / 04 - TS : added # include < version . h > and testing for Kernel
* version before using
* MOD_DEC_USAGE_COUNT / MOD_INC_USAGE_COUNT which are
* deprecated in 2.6
*/
2006-08-22 14:36:13 +04:00
# include <linux/err.h>
2005-11-08 20:40:47 +03:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/crypto.h>
# include <linux/mm.h>
# include <linux/ppp_defs.h>
# include <linux/ppp-comp.h>
2007-10-22 23:19:53 +04:00
# include <linux/scatterlist.h>
2011-01-06 16:37:36 +03:00
# include <asm/unaligned.h>
2005-11-08 20:40:47 +03:00
# include "ppp_mppe.h"
MODULE_AUTHOR ( " Frank Cusack <fcusack@fcusack.com> " ) ;
MODULE_DESCRIPTION ( " Point-to-Point Protocol Microsoft Point-to-Point Encryption support " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_ALIAS ( " ppp-compress- " __stringify ( CI_MPPE ) ) ;
MODULE_VERSION ( " 1.0.2 " ) ;
2006-08-24 13:10:20 +04:00
static unsigned int
2005-11-08 20:40:47 +03:00
setup_sg ( struct scatterlist * sg , const void * address , unsigned int length )
{
2007-10-27 11:52:07 +04:00
sg_set_buf ( sg , address , length ) ;
2006-08-24 13:10:20 +04:00
return length ;
2005-11-08 20:40:47 +03:00
}
# define SHA1_PAD_SIZE 40
/*
* kernel crypto API needs its arguments to be in kmalloc ' d memory , not in the module
* static data area . That means sha_pad needs to be kmalloc ' d .
*/
struct sha_pad {
unsigned char sha_pad1 [ SHA1_PAD_SIZE ] ;
unsigned char sha_pad2 [ SHA1_PAD_SIZE ] ;
} ;
static struct sha_pad * sha_pad ;
static inline void sha_pad_init ( struct sha_pad * shapad )
{
memset ( shapad - > sha_pad1 , 0x00 , sizeof ( shapad - > sha_pad1 ) ) ;
memset ( shapad - > sha_pad2 , 0xF2 , sizeof ( shapad - > sha_pad2 ) ) ;
}
/*
* State for an MPPE ( de ) compressor .
*/
struct ppp_mppe_state {
2006-08-22 14:36:13 +04:00
struct crypto_blkcipher * arc4 ;
2006-08-24 13:10:20 +04:00
struct crypto_hash * sha1 ;
2005-11-08 20:40:47 +03:00
unsigned char * sha1_digest ;
unsigned char master_key [ MPPE_MAX_KEY_LEN ] ;
unsigned char session_key [ MPPE_MAX_KEY_LEN ] ;
unsigned keylen ; /* key length in bytes */
/* NB: 128-bit == 16, 40-bit == 8! */
/* If we want to support 56-bit, */
/* the unit has to change to bits */
unsigned char bits ; /* MPPE control bits */
unsigned ccount ; /* 12-bit coherency count (seqno) */
unsigned stateful ; /* stateful mode flag */
int discard ; /* stateful mode packet loss flag */
int sanity_errors ; /* take down LCP if too many */
int unit ;
int debug ;
struct compstat stats ;
} ;
/* struct ppp_mppe_state.bits definitions */
# define MPPE_BIT_A 0x80 /* Encryption table were (re)inititalized */
# define MPPE_BIT_B 0x40 /* MPPC only (not implemented) */
# define MPPE_BIT_C 0x20 /* MPPC only (not implemented) */
# define MPPE_BIT_D 0x10 /* This is an encrypted frame */
# define MPPE_BIT_FLUSHED MPPE_BIT_A
# define MPPE_BIT_ENCRYPTED MPPE_BIT_D
# define MPPE_BITS(p) ((p)[4] & 0xf0)
# define MPPE_CCOUNT(p) ((((p)[4] & 0x0f) << 8) + (p)[5])
# define MPPE_CCOUNT_SPACE 0x1000 /* The size of the ccount space */
# define MPPE_OVHD 2 /* MPPE overhead/packet */
# define SANITY_MAX 1600 /* Max bogon factor we will tolerate */
/*
* Key Derivation , from RFC 3078 , RFC 3079.
* Equivalent to Get_Key ( ) for MS - CHAP as described in RFC 3079.
*/
2007-09-26 10:05:39 +04:00
static void get_new_key_from_sha ( struct ppp_mppe_state * state )
2005-11-08 20:40:47 +03:00
{
2006-08-24 13:10:20 +04:00
struct hash_desc desc ;
2005-11-08 20:40:47 +03:00
struct scatterlist sg [ 4 ] ;
2006-08-24 13:10:20 +04:00
unsigned int nbytes ;
2005-11-08 20:40:47 +03:00
2007-10-27 11:52:07 +04:00
sg_init_table ( sg , 4 ) ;
2006-08-24 13:10:20 +04:00
nbytes = setup_sg ( & sg [ 0 ] , state - > master_key , state - > keylen ) ;
nbytes + = setup_sg ( & sg [ 1 ] , sha_pad - > sha_pad1 ,
sizeof ( sha_pad - > sha_pad1 ) ) ;
nbytes + = setup_sg ( & sg [ 2 ] , state - > session_key , state - > keylen ) ;
nbytes + = setup_sg ( & sg [ 3 ] , sha_pad - > sha_pad2 ,
sizeof ( sha_pad - > sha_pad2 ) ) ;
2005-11-08 20:40:47 +03:00
2006-08-24 13:10:20 +04:00
desc . tfm = state - > sha1 ;
desc . flags = 0 ;
crypto_hash_digest ( & desc , sg , nbytes , state - > sha1_digest ) ;
2005-11-08 20:40:47 +03:00
}
/*
* Perform the MPPE rekey algorithm , from RFC 3078 , sec . 7.3 .
* Well , not what ' s written there , but rather what they meant .
*/
static void mppe_rekey ( struct ppp_mppe_state * state , int initial_key )
{
struct scatterlist sg_in [ 1 ] , sg_out [ 1 ] ;
2006-08-22 14:36:13 +04:00
struct blkcipher_desc desc = { . tfm = state - > arc4 } ;
2005-11-08 20:40:47 +03:00
2007-09-26 10:05:39 +04:00
get_new_key_from_sha ( state ) ;
2005-11-08 20:40:47 +03:00
if ( ! initial_key ) {
2007-09-26 10:05:39 +04:00
crypto_blkcipher_setkey ( state - > arc4 , state - > sha1_digest ,
state - > keylen ) ;
2007-10-27 11:52:07 +04:00
sg_init_table ( sg_in , 1 ) ;
sg_init_table ( sg_out , 1 ) ;
2007-09-26 10:05:39 +04:00
setup_sg ( sg_in , state - > sha1_digest , state - > keylen ) ;
2005-11-08 20:40:47 +03:00
setup_sg ( sg_out , state - > session_key , state - > keylen ) ;
2006-08-22 14:36:13 +04:00
if ( crypto_blkcipher_encrypt ( & desc , sg_out , sg_in ,
state - > keylen ) ! = 0 ) {
2005-11-08 20:40:47 +03:00
printk ( KERN_WARNING " mppe_rekey: cipher_encrypt failed \n " ) ;
}
} else {
2007-09-26 10:05:39 +04:00
memcpy ( state - > session_key , state - > sha1_digest , state - > keylen ) ;
2005-11-08 20:40:47 +03:00
}
if ( state - > keylen = = 8 ) {
/* See RFC 3078 */
state - > session_key [ 0 ] = 0xd1 ;
state - > session_key [ 1 ] = 0x26 ;
state - > session_key [ 2 ] = 0x9e ;
}
2006-08-22 14:36:13 +04:00
crypto_blkcipher_setkey ( state - > arc4 , state - > session_key , state - > keylen ) ;
2005-11-08 20:40:47 +03:00
}
/*
* Allocate space for a ( de ) compressor .
*/
static void * mppe_alloc ( unsigned char * options , int optlen )
{
struct ppp_mppe_state * state ;
unsigned int digestsize ;
2009-12-03 10:58:21 +03:00
if ( optlen ! = CILEN_MPPE + sizeof ( state - > master_key ) | |
options [ 0 ] ! = CI_MPPE | | options [ 1 ] ! = CILEN_MPPE )
2005-11-08 20:40:47 +03:00
goto out ;
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 12:49:03 +04:00
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
2005-11-08 20:40:47 +03:00
if ( state = = NULL )
goto out ;
2006-08-22 14:36:13 +04:00
state - > arc4 = crypto_alloc_blkcipher ( " ecb(arc4) " , 0 , CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( state - > arc4 ) ) {
state - > arc4 = NULL ;
2005-11-08 20:40:47 +03:00
goto out_free ;
2006-08-22 14:36:13 +04:00
}
2005-11-08 20:40:47 +03:00
2006-08-24 13:10:20 +04:00
state - > sha1 = crypto_alloc_hash ( " sha1 " , 0 , CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( state - > sha1 ) ) {
state - > sha1 = NULL ;
2005-11-08 20:40:47 +03:00
goto out_free ;
2006-08-24 13:10:20 +04:00
}
2005-11-08 20:40:47 +03:00
2006-08-24 13:10:20 +04:00
digestsize = crypto_hash_digestsize ( state - > sha1 ) ;
2005-11-08 20:40:47 +03:00
if ( digestsize < MPPE_MAX_KEY_LEN )
goto out_free ;
state - > sha1_digest = kmalloc ( digestsize , GFP_KERNEL ) ;
if ( ! state - > sha1_digest )
goto out_free ;
/* Save keys. */
memcpy ( state - > master_key , & options [ CILEN_MPPE ] ,
sizeof ( state - > master_key ) ) ;
memcpy ( state - > session_key , state - > master_key ,
sizeof ( state - > master_key ) ) ;
/*
* We defer initial key generation until mppe_init ( ) , as mppe_alloc ( )
* is called frequently during negotiation .
*/
return ( void * ) state ;
out_free :
if ( state - > sha1_digest )
kfree ( state - > sha1_digest ) ;
if ( state - > sha1 )
2006-08-24 13:10:20 +04:00
crypto_free_hash ( state - > sha1 ) ;
2005-11-08 20:40:47 +03:00
if ( state - > arc4 )
2006-08-22 14:36:13 +04:00
crypto_free_blkcipher ( state - > arc4 ) ;
2005-11-08 20:40:47 +03:00
kfree ( state ) ;
out :
return NULL ;
}
/*
* Deallocate space for a ( de ) compressor .
*/
static void mppe_free ( void * arg )
{
struct ppp_mppe_state * state = ( struct ppp_mppe_state * ) arg ;
if ( state ) {
if ( state - > sha1_digest )
kfree ( state - > sha1_digest ) ;
if ( state - > sha1 )
2006-08-24 13:10:20 +04:00
crypto_free_hash ( state - > sha1 ) ;
2005-11-08 20:40:47 +03:00
if ( state - > arc4 )
2006-08-22 14:36:13 +04:00
crypto_free_blkcipher ( state - > arc4 ) ;
2005-11-08 20:40:47 +03:00
kfree ( state ) ;
}
}
/*
* Initialize ( de ) compressor state .
*/
static int
mppe_init ( void * arg , unsigned char * options , int optlen , int unit , int debug ,
const char * debugstr )
{
struct ppp_mppe_state * state = ( struct ppp_mppe_state * ) arg ;
unsigned char mppe_opts ;
2009-12-03 10:58:21 +03:00
if ( optlen ! = CILEN_MPPE | |
options [ 0 ] ! = CI_MPPE | | options [ 1 ] ! = CILEN_MPPE )
2005-11-08 20:40:47 +03:00
return 0 ;
MPPE_CI_TO_OPTS ( & options [ 2 ] , mppe_opts ) ;
if ( mppe_opts & MPPE_OPT_128 )
state - > keylen = 16 ;
else if ( mppe_opts & MPPE_OPT_40 )
state - > keylen = 8 ;
else {
printk ( KERN_WARNING " %s[%d]: unknown key length \n " , debugstr ,
unit ) ;
return 0 ;
}
if ( mppe_opts & MPPE_OPT_STATEFUL )
state - > stateful = 1 ;
/* Generate the initial session key. */
mppe_rekey ( state , 1 ) ;
if ( debug ) {
int i ;
char mkey [ sizeof ( state - > master_key ) * 2 + 1 ] ;
char skey [ sizeof ( state - > session_key ) * 2 + 1 ] ;
printk ( KERN_DEBUG " %s[%d]: initialized with %d-bit %s mode \n " ,
debugstr , unit , ( state - > keylen = = 16 ) ? 128 : 40 ,
( state - > stateful ) ? " stateful " : " stateless " ) ;
for ( i = 0 ; i < sizeof ( state - > master_key ) ; i + + )
sprintf ( mkey + i * 2 , " %02x " , state - > master_key [ i ] ) ;
for ( i = 0 ; i < sizeof ( state - > session_key ) ; i + + )
sprintf ( skey + i * 2 , " %02x " , state - > session_key [ i ] ) ;
printk ( KERN_DEBUG
" %s[%d]: keys: master: %s initial session: %s \n " ,
debugstr , unit , mkey , skey ) ;
}
/*
* Initialize the coherency count . The initial value is not specified
* in RFC 3078 , but we can make a reasonable assumption that it will
* start at 0. Setting it to the max here makes the comp / decomp code
* do the right thing ( determined through experiment ) .
*/
state - > ccount = MPPE_CCOUNT_SPACE - 1 ;
/*
* Note that even though we have initialized the key table , we don ' t
* set the FLUSHED bit . This is contrary to RFC 3078 , sec . 3.1 .
*/
state - > bits = MPPE_BIT_ENCRYPTED ;
state - > unit = unit ;
state - > debug = debug ;
return 1 ;
}
static int
mppe_comp_init ( void * arg , unsigned char * options , int optlen , int unit ,
int hdrlen , int debug )
{
/* ARGSUSED */
return mppe_init ( arg , options , optlen , unit , debug , " mppe_comp_init " ) ;
}
/*
* We received a CCP Reset - Request ( actually , we are sending a Reset - Ack ) ,
* tell the compressor to rekey . Note that we MUST NOT rekey for
* every CCP Reset - Request ; we only rekey on the next xmit packet .
* We might get multiple CCP Reset - Requests if our CCP Reset - Ack is lost .
* So , rekeying for every CCP Reset - Request is broken as the peer will not
* know how many times we ' ve rekeyed . ( If we rekey and THEN get another
* CCP Reset - Request , we must rekey again . )
*/
static void mppe_comp_reset ( void * arg )
{
struct ppp_mppe_state * state = ( struct ppp_mppe_state * ) arg ;
state - > bits | = MPPE_BIT_FLUSHED ;
}
/*
* Compress ( encrypt ) a packet .
* It ' s strange to call this a compressor , since the output is always
* MPPE_OVHD + 2 bytes larger than the input .
*/
static int
mppe_compress ( void * arg , unsigned char * ibuf , unsigned char * obuf ,
int isize , int osize )
{
struct ppp_mppe_state * state = ( struct ppp_mppe_state * ) arg ;
2006-08-22 14:36:13 +04:00
struct blkcipher_desc desc = { . tfm = state - > arc4 } ;
2005-11-08 20:40:47 +03:00
int proto ;
struct scatterlist sg_in [ 1 ] , sg_out [ 1 ] ;
/*
* Check that the protocol is in the range we handle .
*/
proto = PPP_PROTOCOL ( ibuf ) ;
if ( proto < 0x0021 | | proto > 0x00fa )
return 0 ;
/* Make sure we have enough room to generate an encrypted packet. */
if ( osize < isize + MPPE_OVHD + 2 ) {
/* Drop the packet if we should encrypt it, but can't. */
printk ( KERN_DEBUG " mppe_compress[%d]: osize too small! "
" (have: %d need: %d) \n " , state - > unit ,
osize , osize + MPPE_OVHD + 2 ) ;
return - 1 ;
}
osize = isize + MPPE_OVHD + 2 ;
/*
* Copy over the PPP header and set control bits .
*/
obuf [ 0 ] = PPP_ADDRESS ( ibuf ) ;
obuf [ 1 ] = PPP_CONTROL ( ibuf ) ;
2011-01-06 16:37:36 +03:00
put_unaligned_be16 ( PPP_COMP , obuf + 2 ) ;
2005-11-08 20:40:47 +03:00
obuf + = PPP_HDRLEN ;
state - > ccount = ( state - > ccount + 1 ) % MPPE_CCOUNT_SPACE ;
if ( state - > debug > = 7 )
printk ( KERN_DEBUG " mppe_compress[%d]: ccount %d \n " , state - > unit ,
state - > ccount ) ;
2011-01-06 16:37:36 +03:00
put_unaligned_be16 ( state - > ccount , obuf ) ;
2005-11-08 20:40:47 +03:00
if ( ! state - > stateful | | /* stateless mode */
( ( state - > ccount & 0xff ) = = 0xff ) | | /* "flag" packet */
( state - > bits & MPPE_BIT_FLUSHED ) ) { /* CCP Reset-Request */
/* We must rekey */
if ( state - > debug & & state - > stateful )
printk ( KERN_DEBUG " mppe_compress[%d]: rekeying \n " ,
state - > unit ) ;
mppe_rekey ( state , 0 ) ;
state - > bits | = MPPE_BIT_FLUSHED ;
}
obuf [ 0 ] | = state - > bits ;
state - > bits & = ~ MPPE_BIT_FLUSHED ; /* reset for next xmit */
obuf + = MPPE_OVHD ;
ibuf + = 2 ; /* skip to proto field */
isize - = 2 ;
/* Encrypt packet */
2007-10-27 11:52:07 +04:00
sg_init_table ( sg_in , 1 ) ;
sg_init_table ( sg_out , 1 ) ;
2005-11-08 20:40:47 +03:00
setup_sg ( sg_in , ibuf , isize ) ;
setup_sg ( sg_out , obuf , osize ) ;
2006-08-22 14:36:13 +04:00
if ( crypto_blkcipher_encrypt ( & desc , sg_out , sg_in , isize ) ! = 0 ) {
2005-11-08 20:40:47 +03:00
printk ( KERN_DEBUG " crypto_cypher_encrypt failed \n " ) ;
return - 1 ;
}
state - > stats . unc_bytes + = isize ;
state - > stats . unc_packets + + ;
state - > stats . comp_bytes + = osize ;
state - > stats . comp_packets + + ;
return osize ;
}
/*
* Since every frame grows by MPPE_OVHD + 2 bytes , this is always going
* to look bad . . . and the longer the link is up the worse it will get .
*/
static void mppe_comp_stats ( void * arg , struct compstat * stats )
{
struct ppp_mppe_state * state = ( struct ppp_mppe_state * ) arg ;
* stats = state - > stats ;
}
static int
mppe_decomp_init ( void * arg , unsigned char * options , int optlen , int unit ,
int hdrlen , int mru , int debug )
{
/* ARGSUSED */
return mppe_init ( arg , options , optlen , unit , debug , " mppe_decomp_init " ) ;
}
/*
* We received a CCP Reset - Ack . Just ignore it .
*/
static void mppe_decomp_reset ( void * arg )
{
/* ARGSUSED */
return ;
}
/*
* Decompress ( decrypt ) an MPPE packet .
*/
static int
mppe_decompress ( void * arg , unsigned char * ibuf , int isize , unsigned char * obuf ,
int osize )
{
struct ppp_mppe_state * state = ( struct ppp_mppe_state * ) arg ;
2006-08-22 14:36:13 +04:00
struct blkcipher_desc desc = { . tfm = state - > arc4 } ;
2005-11-08 20:40:47 +03:00
unsigned ccount ;
int flushed = MPPE_BITS ( ibuf ) & MPPE_BIT_FLUSHED ;
int sanity = 0 ;
struct scatterlist sg_in [ 1 ] , sg_out [ 1 ] ;
if ( isize < = PPP_HDRLEN + MPPE_OVHD ) {
if ( state - > debug )
printk ( KERN_DEBUG
" mppe_decompress[%d]: short pkt (%d) \n " ,
state - > unit , isize ) ;
return DECOMP_ERROR ;
}
/*
* Make sure we have enough room to decrypt the packet .
2007-06-24 10:04:11 +04:00
* Note that for our test we only subtract 1 byte whereas in
* mppe_compress ( ) we added 2 bytes ( + MPPE_OVHD ) ;
* this is to account for possible PFC .
2005-11-08 20:40:47 +03:00
*/
2007-06-24 10:04:11 +04:00
if ( osize < isize - MPPE_OVHD - 1 ) {
2005-11-08 20:40:47 +03:00
printk ( KERN_DEBUG " mppe_decompress[%d]: osize too small! "
" (have: %d need: %d) \n " , state - > unit ,
2007-06-24 10:04:11 +04:00
osize , isize - MPPE_OVHD - 1 ) ;
2005-11-08 20:40:47 +03:00
return DECOMP_ERROR ;
}
osize = isize - MPPE_OVHD - 2 ; /* assume no PFC */
ccount = MPPE_CCOUNT ( ibuf ) ;
if ( state - > debug > = 7 )
printk ( KERN_DEBUG " mppe_decompress[%d]: ccount %d \n " ,
state - > unit , ccount ) ;
/* sanity checks -- terminate with extreme prejudice */
if ( ! ( MPPE_BITS ( ibuf ) & MPPE_BIT_ENCRYPTED ) ) {
printk ( KERN_DEBUG
" mppe_decompress[%d]: ENCRYPTED bit not set! \n " ,
state - > unit ) ;
state - > sanity_errors + = 100 ;
sanity = 1 ;
}
if ( ! state - > stateful & & ! flushed ) {
printk ( KERN_DEBUG " mppe_decompress[%d]: FLUSHED bit not set in "
" stateless mode! \n " , state - > unit ) ;
state - > sanity_errors + = 100 ;
sanity = 1 ;
}
if ( state - > stateful & & ( ( ccount & 0xff ) = = 0xff ) & & ! flushed ) {
printk ( KERN_DEBUG " mppe_decompress[%d]: FLUSHED bit not set on "
" flag packet! \n " , state - > unit ) ;
state - > sanity_errors + = 100 ;
sanity = 1 ;
}
if ( sanity ) {
if ( state - > sanity_errors < SANITY_MAX )
return DECOMP_ERROR ;
else
/*
* Take LCP down if the peer is sending too many bogons .
* We don ' t want to do this for a single or just a few
* instances since it could just be due to packet corruption .
*/
return DECOMP_FATALERROR ;
}
/*
* Check the coherency count .
*/
if ( ! state - > stateful ) {
/* RFC 3078, sec 8.1. Rekey for every packet. */
while ( state - > ccount ! = ccount ) {
mppe_rekey ( state , 0 ) ;
state - > ccount = ( state - > ccount + 1 ) % MPPE_CCOUNT_SPACE ;
}
} else {
/* RFC 3078, sec 8.2. */
if ( ! state - > discard ) {
/* normal state */
state - > ccount = ( state - > ccount + 1 ) % MPPE_CCOUNT_SPACE ;
if ( ccount ! = state - > ccount ) {
/*
* ( ccount > state - > ccount )
* Packet loss detected , enter the discard state .
* Signal the peer to rekey ( by sending a CCP Reset - Request ) .
*/
state - > discard = 1 ;
return DECOMP_ERROR ;
}
} else {
/* discard state */
if ( ! flushed ) {
/* ccp.c will be silent (no additional CCP Reset-Requests). */
return DECOMP_ERROR ;
} else {
/* Rekey for every missed "flag" packet. */
while ( ( ccount & ~ 0xff ) ! =
( state - > ccount & ~ 0xff ) ) {
mppe_rekey ( state , 0 ) ;
state - > ccount =
( state - > ccount +
256 ) % MPPE_CCOUNT_SPACE ;
}
/* reset */
state - > discard = 0 ;
state - > ccount = ccount ;
/*
* Another problem with RFC 3078 here . It implies that the
* peer need not send a Reset - Ack packet . But RFC 1962
* requires it . Hopefully , M $ does send a Reset - Ack ; even
* though it isn ' t required for MPPE synchronization , it is
* required to reset CCP state .
*/
}
}
if ( flushed )
mppe_rekey ( state , 0 ) ;
}
/*
* Fill in the first part of the PPP header . The protocol field
* comes from the decrypted data .
*/
obuf [ 0 ] = PPP_ADDRESS ( ibuf ) ; /* +1 */
obuf [ 1 ] = PPP_CONTROL ( ibuf ) ; /* +1 */
obuf + = 2 ;
ibuf + = PPP_HDRLEN + MPPE_OVHD ;
isize - = PPP_HDRLEN + MPPE_OVHD ; /* -6 */
/* net osize: isize-4 */
/*
* Decrypt the first byte in order to check if it is
* a compressed or uncompressed protocol field .
*/
2007-10-27 11:52:07 +04:00
sg_init_table ( sg_in , 1 ) ;
sg_init_table ( sg_out , 1 ) ;
2005-11-08 20:40:47 +03:00
setup_sg ( sg_in , ibuf , 1 ) ;
setup_sg ( sg_out , obuf , 1 ) ;
2006-08-22 14:36:13 +04:00
if ( crypto_blkcipher_decrypt ( & desc , sg_out , sg_in , 1 ) ! = 0 ) {
2005-11-08 20:40:47 +03:00
printk ( KERN_DEBUG " crypto_cypher_decrypt failed \n " ) ;
return DECOMP_ERROR ;
}
/*
* Do PFC decompression .
* This would be nicer if we were given the actual sk_buff
* instead of a char * .
*/
if ( ( obuf [ 0 ] & 0x01 ) ! = 0 ) {
obuf [ 1 ] = obuf [ 0 ] ;
obuf [ 0 ] = 0 ;
obuf + + ;
osize + + ;
}
/* And finally, decrypt the rest of the packet. */
setup_sg ( sg_in , ibuf + 1 , isize - 1 ) ;
setup_sg ( sg_out , obuf + 1 , osize - 1 ) ;
2006-08-22 14:36:13 +04:00
if ( crypto_blkcipher_decrypt ( & desc , sg_out , sg_in , isize - 1 ) ) {
2005-11-08 20:40:47 +03:00
printk ( KERN_DEBUG " crypto_cypher_decrypt failed \n " ) ;
return DECOMP_ERROR ;
}
state - > stats . unc_bytes + = osize ;
state - > stats . unc_packets + + ;
state - > stats . comp_bytes + = isize ;
state - > stats . comp_packets + + ;
/* good packet credit */
state - > sanity_errors > > = 1 ;
return osize ;
}
/*
* Incompressible data has arrived ( this should never happen ! ) .
* We should probably drop the link if the protocol is in the range
* of what should be encrypted . At the least , we should drop this
* packet . ( How to do this ? )
*/
static void mppe_incomp ( void * arg , unsigned char * ibuf , int icnt )
{
struct ppp_mppe_state * state = ( struct ppp_mppe_state * ) arg ;
if ( state - > debug & &
( PPP_PROTOCOL ( ibuf ) > = 0x0021 & & PPP_PROTOCOL ( ibuf ) < = 0x00fa ) )
printk ( KERN_DEBUG
" mppe_incomp[%d]: incompressible (unencrypted) data! "
" (proto %04x) \n " , state - > unit , PPP_PROTOCOL ( ibuf ) ) ;
state - > stats . inc_bytes + = icnt ;
state - > stats . inc_packets + + ;
state - > stats . unc_bytes + = icnt ;
state - > stats . unc_packets + + ;
}
/*************************************************************
* Module interface table
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Procedures exported to if_ppp . c .
*/
static struct compressor ppp_mppe = {
. compress_proto = CI_MPPE ,
. comp_alloc = mppe_alloc ,
. comp_free = mppe_free ,
. comp_init = mppe_comp_init ,
. comp_reset = mppe_comp_reset ,
. compress = mppe_compress ,
. comp_stat = mppe_comp_stats ,
. decomp_alloc = mppe_alloc ,
. decomp_free = mppe_free ,
. decomp_init = mppe_decomp_init ,
. decomp_reset = mppe_decomp_reset ,
. decompress = mppe_decompress ,
. incomp = mppe_incomp ,
. decomp_stat = mppe_comp_stats ,
. owner = THIS_MODULE ,
. comp_extra = MPPE_PAD ,
} ;
/*
* ppp_mppe_init ( )
*
* Prior to allowing load , try to load the arc4 and sha1 crypto
* libraries . The actual use will be allocated later , but
* this way the module will fail to insmod if they aren ' t available .
*/
static int __init ppp_mppe_init ( void )
{
int answer ;
2006-08-26 12:12:40 +04:00
if ( ! ( crypto_has_blkcipher ( " ecb(arc4) " , 0 , CRYPTO_ALG_ASYNC ) & &
crypto_has_hash ( " sha1 " , 0 , CRYPTO_ALG_ASYNC ) ) )
2005-11-08 20:40:47 +03:00
return - ENODEV ;
sha_pad = kmalloc ( sizeof ( struct sha_pad ) , GFP_KERNEL ) ;
if ( ! sha_pad )
return - ENOMEM ;
sha_pad_init ( sha_pad ) ;
answer = ppp_register_compressor ( & ppp_mppe ) ;
if ( answer = = 0 )
printk ( KERN_INFO " PPP MPPE Compression module registered \n " ) ;
else
kfree ( sha_pad ) ;
return answer ;
}
static void __exit ppp_mppe_cleanup ( void )
{
ppp_unregister_compressor ( & ppp_mppe ) ;
kfree ( sha_pad ) ;
}
module_init ( ppp_mppe_init ) ;
module_exit ( ppp_mppe_cleanup ) ;