2003-08-13 05:53:07 +04:00
/*
Unix SMB / CIFS implementation .
Functions to create reasonable random numbers for crypto use .
Copyright ( C ) Jeremy Allison 2001
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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2004-11-02 04:42:45 +03:00
# include "system/iconv.h"
2003-08-13 05:53:07 +04:00
2004-07-14 16:14:07 +04:00
static unsigned char hash [ 258 ] ;
static uint32 counter ;
static BOOL done_reseed = False ;
static void ( * reseed_callback ) ( int * newseed ) ;
2003-08-13 05:53:07 +04:00
/****************************************************************
Copy any user given reseed data .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-07-14 16:14:07 +04:00
void set_rand_reseed_callback ( void ( * fn ) ( int * ) )
{
reseed_callback = fn ;
set_need_random_reseed ( ) ;
}
void set_need_random_reseed ( void )
2003-08-13 05:53:07 +04:00
{
2004-07-14 16:14:07 +04:00
done_reseed = False ;
}
2003-08-13 05:53:07 +04:00
2004-07-14 16:14:07 +04:00
static void get_rand_reseed_data ( int * reseed_data )
{
if ( reseed_callback ) {
reseed_callback ( reseed_data ) ;
} else {
* reseed_data = 0 ;
}
2003-08-13 05:53:07 +04:00
}
/****************************************************************
Setup the seed .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-07-14 16:14:07 +04:00
static void seed_random_stream ( unsigned char * seedval , size_t seedlen )
2003-08-13 05:53:07 +04:00
{
2004-07-14 16:14:07 +04:00
unsigned char j = 0 ;
2003-08-13 05:53:07 +04:00
size_t ind ;
for ( ind = 0 ; ind < 256 ; ind + + )
2004-07-14 16:14:07 +04:00
hash [ ind ] = ( unsigned char ) ind ;
2003-08-13 05:53:07 +04:00
for ( ind = 0 ; ind < 256 ; ind + + ) {
2004-07-14 16:14:07 +04:00
unsigned char tc ;
2003-08-13 05:53:07 +04:00
j + = ( hash [ ind ] + seedval [ ind % seedlen ] ) ;
tc = hash [ ind ] ;
hash [ ind ] = hash [ j ] ;
hash [ j ] = tc ;
}
hash [ 256 ] = 0 ;
hash [ 257 ] = 0 ;
}
/****************************************************************
Get datasize bytes worth of random data .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-07-14 16:14:07 +04:00
static void get_random_stream ( unsigned char * data , size_t datasize )
2003-08-13 05:53:07 +04:00
{
2004-07-14 16:14:07 +04:00
unsigned char index_i = hash [ 256 ] ;
unsigned char index_j = hash [ 257 ] ;
2003-08-13 05:53:07 +04:00
size_t ind ;
for ( ind = 0 ; ind < datasize ; ind + + ) {
2004-07-14 16:14:07 +04:00
unsigned char tc ;
unsigned char t ;
2003-08-13 05:53:07 +04:00
index_i + + ;
index_j + = hash [ index_i ] ;
tc = hash [ index_i ] ;
hash [ index_i ] = hash [ index_j ] ;
hash [ index_j ] = tc ;
t = hash [ index_i ] + hash [ index_j ] ;
data [ ind ] = hash [ t ] ;
}
hash [ 256 ] = index_i ;
hash [ 257 ] = index_j ;
}
/****************************************************************
Get a 16 byte hash from the contents of a file .
Note that the hash is not initialised .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-07-14 16:14:07 +04:00
static void do_filehash ( const char * fname , unsigned char * the_hash )
2003-08-13 05:53:07 +04:00
{
2004-07-14 16:14:07 +04:00
unsigned char buf [ 1011 ] ; /* deliberate weird size */
unsigned char tmp_md4 [ 16 ] ;
2003-08-13 05:53:07 +04:00
int fd , n ;
2004-11-01 23:21:54 +03:00
fd = open ( fname , O_RDONLY , 0 ) ;
2003-08-13 05:53:07 +04:00
if ( fd = = - 1 )
return ;
while ( ( n = read ( fd , ( char * ) buf , sizeof ( buf ) ) ) > 0 ) {
mdfour ( tmp_md4 , buf , n ) ;
for ( n = 0 ; n < 16 ; n + + )
the_hash [ n ] ^ = tmp_md4 [ n ] ;
}
close ( fd ) ;
}
/**************************************************************
Try and get a good random number seed . Try a number of
different factors . Firstly , try / dev / urandom - use if exists .
We use / dev / urandom as a read of / dev / random can block if
the entropy pool dries up . This leads clients to timeout
or be very slow on connect .
If we can ' t use / dev / urandom then seed the stream random generator
above . . .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int do_reseed ( BOOL use_fd , int fd )
{
2004-07-14 16:14:07 +04:00
unsigned char seed_inbuf [ 40 ] ;
uint32 v1 , v2 ; struct timeval tval ; pid_t mypid ;
int reseed_data = 0 ;
2003-08-13 05:53:07 +04:00
if ( use_fd ) {
if ( fd ! = - 1 )
return fd ;
2004-11-01 23:21:54 +03:00
fd = open ( " /dev/urandom " , O_RDONLY , 0 ) ;
2003-08-13 05:53:07 +04:00
if ( fd > = 0 )
return fd ;
}
/* Add in some secret file contents */
do_filehash ( " /etc/shadow " , & seed_inbuf [ 0 ] ) ;
do_filehash ( lp_smb_passwd_file ( ) , & seed_inbuf [ 16 ] ) ;
/*
* Add the counter , time of day , and pid .
*/
GetTimeOfDay ( & tval ) ;
mypid = getpid ( ) ;
v1 = ( counter + + ) + mypid + tval . tv_sec ;
v2 = ( counter + + ) * mypid + tval . tv_usec ;
SIVAL ( seed_inbuf , 32 , v1 ^ IVAL ( seed_inbuf , 32 ) ) ;
SIVAL ( seed_inbuf , 36 , v2 ^ IVAL ( seed_inbuf , 36 ) ) ;
/*
* Add any user - given reseed data .
*/
2004-07-14 16:14:07 +04:00
get_rand_reseed_data ( & reseed_data ) ;
2003-08-13 05:53:07 +04:00
if ( reseed_data ) {
size_t i ;
for ( i = 0 ; i < sizeof ( seed_inbuf ) ; i + + )
2004-07-14 16:14:07 +04:00
seed_inbuf [ i ] ^ = ( ( char * ) ( & reseed_data ) ) [ i % sizeof ( reseed_data ) ] ;
2003-08-13 05:53:07 +04:00
}
seed_random_stream ( seed_inbuf , sizeof ( seed_inbuf ) ) ;
return - 1 ;
}
/*******************************************************************
Interface to the ( hopefully ) good crypto random number generator .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-07-14 16:14:07 +04:00
void generate_random_buffer ( unsigned char * out , int len )
2003-08-13 05:53:07 +04:00
{
static int urand_fd = - 1 ;
2004-07-14 16:14:07 +04:00
unsigned char md4_buf [ 64 ] ;
unsigned char tmp_buf [ 16 ] ;
unsigned char * p ;
2003-08-13 05:53:07 +04:00
2004-07-14 16:14:07 +04:00
if ( ! done_reseed ) {
2003-08-13 05:53:07 +04:00
urand_fd = do_reseed ( True , urand_fd ) ;
done_reseed = True ;
}
if ( urand_fd ! = - 1 & & len > 0 ) {
if ( read ( urand_fd , out , len ) = = len )
return ; /* len bytes of random data read from urandom. */
/* Read of urand error, drop back to non urand method. */
close ( urand_fd ) ;
urand_fd = - 1 ;
do_reseed ( False , - 1 ) ;
done_reseed = True ;
}
/*
* Generate random numbers in chunks of 64 bytes ,
* then md4 them & copy to the output buffer .
* This way the raw state of the stream is never externally
* seen .
*/
p = out ;
while ( len > 0 ) {
int copy_len = len > 16 ? 16 : len ;
get_random_stream ( md4_buf , sizeof ( md4_buf ) ) ;
mdfour ( tmp_buf , md4_buf , sizeof ( md4_buf ) ) ;
memcpy ( p , tmp_buf , copy_len ) ;
p + = copy_len ;
len - = copy_len ;
}
}
2004-04-22 10:17:50 +04:00
/*
very basic password quality checker
*/
2004-05-10 15:23:50 +04:00
BOOL check_password_quality ( const char * s )
2004-04-22 10:17:50 +04:00
{
int has_digit = 0 , has_capital = 0 , has_lower = 0 ;
while ( * s ) {
if ( isdigit ( * s ) ) {
has_digit + + ;
} else if ( isupper ( * s ) ) {
has_capital + + ;
} else if ( islower ( * s ) ) {
has_lower + + ;
}
s + + ;
}
return has_digit & & has_lower & & has_capital ;
}
2003-08-13 05:53:07 +04:00
/*******************************************************************
Use the random number generator to generate a random string .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-10-26 17:16:50 +04:00
char * generate_random_str_list ( TALLOC_CTX * mem_ctx , size_t len , const char * list )
2003-08-13 05:53:07 +04:00
{
size_t i ;
2004-10-26 17:16:50 +04:00
size_t list_len = strlen ( list ) ;
2003-08-13 05:53:07 +04:00
2004-05-15 11:51:38 +04:00
char * retstr = talloc ( mem_ctx , len + 1 ) ;
2004-10-26 17:16:50 +04:00
if ( ! retstr ) return NULL ;
2003-08-13 05:53:07 +04:00
2004-07-14 16:14:07 +04:00
generate_random_buffer ( retstr , len ) ;
2004-10-26 17:16:50 +04:00
for ( i = 0 ; i < len ; i + + ) {
retstr [ i ] = list [ retstr [ i ] % list_len ] ;
}
2003-08-13 05:53:07 +04:00
retstr [ i ] = ' \0 ' ;
2004-10-26 17:16:50 +04:00
return retstr ;
}
char * generate_random_str ( TALLOC_CTX * mem_ctx , size_t len )
{
char * retstr ;
const char * c_list = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#., " ;
again :
retstr = generate_random_str_list ( mem_ctx , len , c_list ) ;
if ( ! retstr ) return NULL ;
2004-04-22 10:17:50 +04:00
/* we need to make sure the random string passes basic quality tests
or it might be rejected by windows as a password */
if ( len > = 7 & & ! check_password_quality ( retstr ) ) {
2004-10-26 17:16:50 +04:00
talloc_free ( retstr ) ;
2004-04-22 10:17:50 +04:00
goto again ;
}
2004-05-15 11:51:38 +04:00
return retstr ;
2003-08-13 05:53:07 +04:00
}