1998-04-21 02:43:54 +04:00
/*
Unix SMB / Netbios implementation .
Version 1.9 .
Functions to create reasonable random numbers for crypto use .
Copyright ( C ) Jeremy Allison 1998
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"
extern int DEBUGLEVEL ;
1998-04-21 03:57:29 +04:00
static uint32 counter = 0 ;
1998-04-21 11:17:35 +04:00
/****************************************************************
get a 16 byte hash from the contents of a file
Note that the hash is not initialised .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void do_filehash ( char * fname , unsigned char * hash )
{
unsigned char buf [ 1011 ] ; /* deliberate weird size */
unsigned char tmp_md4 [ 16 ] ;
int fd , n ;
1998-11-17 23:50:07 +03:00
fd = sys_open ( fname , O_RDONLY , 0 ) ;
1998-04-21 11:17:35 +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 + + )
hash [ n ] ^ = tmp_md4 [ n ] ;
}
close ( fd ) ;
}
1998-04-21 03:57:29 +04:00
/****************************************************************
Try and get a seed by looking at the atimes of files in a given
directory . XOR them into the buf array .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void do_dirrand ( char * name , unsigned char * buf , int buf_len )
{
1998-11-26 00:17:20 +03:00
DIR * dp = opendir ( name ) ;
1998-04-21 03:57:29 +04:00
pstring fullname ;
1999-12-13 16:27:58 +03:00
int len_left ;
int fullname_len ;
1998-04-21 03:57:29 +04:00
char * pos ;
pstrcpy ( fullname , name ) ;
fullname_len = strlen ( fullname ) ;
if ( fullname_len + 2 > sizeof ( pstring ) )
return ;
if ( fullname [ fullname_len ] ! = ' / ' ) {
fullname [ fullname_len ] = ' / ' ;
fullname [ fullname_len + 1 ] = ' \0 ' ;
fullname_len = strlen ( fullname ) ;
}
len_left = sizeof ( pstring ) - fullname_len - 1 ;
pos = & fullname [ fullname_len ] ;
if ( dp ! = NULL ) {
char * p ;
while ( ( p = readdirname ( dp ) ) ) {
1998-09-02 00:11:54 +04:00
SMB_STRUCT_STAT st ;
1998-04-21 03:57:29 +04:00
if ( strlen ( p ) < = len_left )
1998-05-12 04:55:32 +04:00
pstrcpy ( pos , p ) ;
1998-04-21 03:57:29 +04:00
1998-11-26 00:17:20 +03:00
if ( sys_stat ( fullname , & st ) = = 0 ) {
1998-04-21 03:57:29 +04:00
SIVAL ( buf , ( ( counter * 4 ) % ( buf_len - 4 ) ) ,
IVAL ( buf , ( ( counter * 4 ) % ( buf_len - 4 ) ) ) ^ st . st_atime ) ;
counter + + ;
DEBUG ( 10 , ( " do_dirrand: value from file %s. \n " , fullname ) ) ;
}
}
closedir ( dp ) ;
}
}
1998-04-21 02:43:54 +04:00
/**************************************************************
Try and get a good random number seed . Try a number of
1999-04-28 06:00:38 +04:00
different factors . Firstly , try / dev / urandom and try and
1998-04-21 02:43:54 +04:00
read from this . If this fails iterate through / tmp and
1998-04-22 04:56:38 +04:00
/ dev and XOR all the file timestamps . Next add in
a hash of the contents of / etc / shadow and the smb passwd
file and a combination of pid and time of day ( yes I know this
1998-04-21 02:43:54 +04:00
sucks : - ) . Finally md4 the result .
1998-04-21 11:17:35 +04:00
1999-04-28 06:00:38 +04:00
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 .
1998-04-21 11:17:35 +04:00
The result goes in a 16 byte buffer passed from the caller
1998-04-21 02:43:54 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-04-22 04:56:38 +04:00
static uint32 do_reseed ( unsigned char * md4_outbuf )
1998-04-21 02:43:54 +04:00
{
unsigned char md4_inbuf [ 40 ] ;
BOOL got_random = False ;
1998-04-22 04:56:38 +04:00
uint32 v1 , v2 , ret ;
1998-04-21 02:43:54 +04:00
int fd ;
struct timeval tval ;
1998-04-21 03:57:29 +04:00
pid_t mypid ;
1998-04-21 11:26:15 +04:00
struct passwd * pw ;
1998-04-21 02:43:54 +04:00
memset ( md4_inbuf , ' \0 ' , sizeof ( md4_inbuf ) ) ;
1999-04-28 06:00:38 +04:00
fd = sys_open ( " /dev/urandom " , O_RDONLY , 0 ) ;
1998-04-21 02:43:54 +04:00
if ( fd > = 0 ) {
/*
1999-04-28 06:00:38 +04:00
* We can use / dev / urandom !
1998-04-21 02:43:54 +04:00
*/
if ( read ( fd , md4_inbuf , 40 ) = = 40 ) {
got_random = True ;
1999-04-28 06:00:38 +04:00
DEBUG ( 10 , ( " do_reseed: got 40 bytes from /dev/urandom. \n " ) ) ;
1998-04-21 02:43:54 +04:00
}
close ( fd ) ;
}
if ( ! got_random ) {
/*
1999-04-28 06:00:38 +04:00
* / dev / urandom failed - try / dev for timestamps .
1998-04-21 02:43:54 +04:00
*/
1998-04-21 03:57:29 +04:00
do_dirrand ( " /dev " , md4_inbuf , sizeof ( md4_inbuf ) ) ;
1998-04-21 02:43:54 +04:00
}
1998-04-21 11:17:35 +04:00
/* possibly add in some secret file contents */
do_filehash ( " /etc/shadow " , & md4_inbuf [ 0 ] ) ;
1998-04-23 22:54:57 +04:00
do_filehash ( lp_smb_passwd_file ( ) , & md4_inbuf [ 16 ] ) ;
1998-04-21 11:17:35 +04:00
1998-04-21 11:26:15 +04:00
/* add in the root encrypted password. On any system where security is taken
seriously this will be secret */
1999-12-13 16:27:58 +03:00
pw = sys_getpwnam ( " root " ) ;
1998-04-22 04:56:38 +04:00
if ( pw & & pw - > pw_passwd ) {
1998-04-21 11:26:15 +04:00
int i ;
unsigned char md4_tmp [ 16 ] ;
1998-05-05 23:24:32 +04:00
mdfour ( md4_tmp , ( unsigned char * ) pw - > pw_passwd , strlen ( pw - > pw_passwd ) ) ;
1998-04-21 11:26:15 +04:00
for ( i = 0 ; i < 16 ; i + + )
md4_inbuf [ 8 + i ] ^ = md4_tmp [ i ] ;
}
1998-04-21 02:43:54 +04:00
/*
* Finally add the counter , time of day , and pid .
*/
GetTimeOfDay ( & tval ) ;
2000-05-02 06:23:41 +04:00
mypid = sys_getpid ( ) ;
1998-04-21 03:57:29 +04:00
v1 = ( counter + + ) + mypid + tval . tv_sec ;
v2 = ( counter + + ) * mypid + tval . tv_usec ;
1998-04-21 02:43:54 +04:00
SIVAL ( md4_inbuf , 32 , v1 ^ IVAL ( md4_inbuf , 32 ) ) ;
1998-04-21 11:17:35 +04:00
SIVAL ( md4_inbuf , 36 , v2 ^ IVAL ( md4_inbuf , 36 ) ) ;
1998-04-21 02:43:54 +04:00
mdfour ( md4_outbuf , md4_inbuf , sizeof ( md4_inbuf ) ) ;
1998-04-22 04:56:38 +04:00
/*
* Return a 32 bit int created from XORing the
* 16 bit return buffer .
*/
ret = IVAL ( md4_outbuf , 0 ) ;
ret ^ = IVAL ( md4_outbuf , 4 ) ;
ret ^ = IVAL ( md4_outbuf , 8 ) ;
return ( ret ^ IVAL ( md4_outbuf , 12 ) ) ;
1998-04-21 02:43:54 +04:00
}
/*******************************************************************
Interface to the ( hopefully ) good crypto random number generator .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void generate_random_buffer ( unsigned char * out , int len , BOOL re_seed )
{
static BOOL done_reseed = False ;
1998-04-22 04:56:38 +04:00
static unsigned char md4_buf [ 16 ] ;
1998-04-21 11:17:35 +04:00
unsigned char tmp_buf [ 16 ] ;
1998-04-21 02:43:54 +04:00
unsigned char * p ;
if ( ! done_reseed | | re_seed ) {
1998-09-26 03:40:49 +04:00
sys_srandom ( do_reseed ( md4_buf ) ) ;
1998-04-21 11:17:35 +04:00
done_reseed = True ;
1998-04-21 02:43:54 +04:00
}
/*
* Generate random numbers in chunks of 64 bytes ,
* then md4 them & copy to the output buffer .
1998-04-22 04:56:38 +04:00
* Added XOR with output from random , seeded
* by the original md4_buf . This is to stop the
* output from this function being the previous
* md4_buf md4 ' ed . The output from this function
* is often output onto the wire , and so it should
* not be possible to guess the next output from
* this function based on the previous output .
* XORing in the output from random ( ) , seeded by
* the original md4 hash should stop this . JRA .
1998-04-21 02:43:54 +04:00
*/
p = out ;
while ( len > 0 ) {
1998-04-22 04:56:38 +04:00
int i ;
1998-04-21 02:43:54 +04:00
int copy_len = len > 16 ? 16 : len ;
1998-04-21 11:17:35 +04:00
mdfour ( tmp_buf , md4_buf , sizeof ( md4_buf ) ) ;
memcpy ( md4_buf , tmp_buf , sizeof ( md4_buf ) ) ;
1998-04-22 04:56:38 +04:00
/* XOR in output from random(). */
for ( i = 0 ; i < 4 ; i + + )
1998-09-26 03:40:49 +04:00
SIVAL ( tmp_buf , i * 4 , ( IVAL ( tmp_buf , i * 4 ) ^ ( uint32 ) sys_random ( ) ) ) ;
1998-04-21 11:17:35 +04:00
memcpy ( p , tmp_buf , copy_len ) ;
1998-04-21 02:43:54 +04:00
p + = copy_len ;
len - = copy_len ;
}
}
2001-04-12 11:00:08 +04:00
/*******************************************************************
Use the random number generator to generate a random string .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-04-12 11:20:15 +04:00
static char c_list [ ] = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#., " ;
2001-04-12 11:00:08 +04:00
char * generate_random_str ( size_t len )
{
static unsigned char retstr [ 256 ] ;
size_t i ;
memset ( retstr , ' \0 ' , sizeof ( retstr ) ) ;
if ( len > sizeof ( retstr ) - 1 )
len = sizeof ( retstr ) - 1 ;
generate_random_buffer ( retstr , len , False ) ;
for ( i = 0 ; i < len ; i + + )
retstr [ i ] = c_list [ retstr [ i ] % sizeof ( c_list ) ] ;
retstr [ i ] = ' \0 ' ;
return retstr ;
}