genrand.c: Improved filename based random seed generation.
[samba.git] / source / lib / genrand.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4
5    Functions to create reasonable random numbers for crypto use.
6
7    Copyright (C) Jeremy Allison 1998
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 extern int DEBUGLEVEL;
27 static uint32 counter = 0;
28
29 /****************************************************************
30  Try and get a seed by looking at the atimes of files in a given
31  directory. XOR them into the buf array.
32 *****************************************************************/
33
34 static void do_dirrand(char *name, unsigned char *buf, int buf_len)
35 {
36   void *dp = sys_opendir(name);
37   pstring fullname;
38   int len_left;
39   int fullname_len;
40   char *pos;
41
42   pstrcpy(fullname, name);
43   fullname_len = strlen(fullname);
44
45   if(fullname_len + 2 > sizeof(pstring))
46     return;
47
48   if(fullname[fullname_len] != '/') {
49     fullname[fullname_len] = '/';
50     fullname[fullname_len+1] = '\0';
51     fullname_len = strlen(fullname);
52   }
53
54   len_left = sizeof(pstring) - fullname_len - 1;
55   pos = &fullname[fullname_len];
56
57   if(dp != NULL) {
58     char *p;
59
60     while ((p = readdirname(dp))) {           
61       struct stat st;
62
63       if(strlen(p) <= len_left)
64         strcpy(pos, p);
65
66       if(sys_stat(fullname,&st) == 0) {
67         SIVAL(buf, ((counter * 4)%(buf_len-4)), 
68               IVAL(buf,((counter * 4)%(buf_len-4))) ^ st.st_atime);
69         counter++;
70         DEBUG(10,("do_dirrand: value from file %s.\n", fullname));
71       }
72     }
73     closedir(dp); 
74   }
75 }
76
77 /**************************************************************
78  Try and get a good random number seed. Try a number of
79  different factors. Firstly, try /dev/random and try and
80  read from this. If this fails iterate through /tmp and
81  XOR all the file timestamps. If this fails then just use
82  a combination of pid and time of day (yes I know this
83  sucks :-). Finally md4 the result.
84 **************************************************************/
85
86 static uint32 do_reseed(void)
87 {
88   unsigned char md4_outbuf[16];
89   unsigned char md4_inbuf[40];
90   BOOL got_random = False;
91   uint32 v1, v2, ret;
92   int fd;
93   struct timeval tval;
94   pid_t mypid;
95
96   memset(md4_inbuf, '\0', sizeof(md4_inbuf));
97
98   fd = open( "/dev/random", O_RDONLY);
99   if(fd >= 0) {
100     /* 
101      * We can use /dev/random !
102      */
103     if(read(fd, md4_inbuf, 40) == 40) {
104       got_random = True;
105       DEBUG(10,("do_reseed: got 40 bytes from /dev/random.\n"));
106     }
107     close(fd);
108   }
109
110   if(!got_random) {
111     /*
112      * /dev/random failed - try /tmp/ for timestamps.
113      */
114     do_dirrand("/tmp", md4_inbuf, sizeof(md4_inbuf));
115     do_dirrand("/dev", md4_inbuf, sizeof(md4_inbuf));
116   }
117
118   /*
119    * Finally add the counter, time of day, and pid.
120    */
121   GetTimeOfDay(&tval);
122   mypid = getpid();
123   v1 = (counter++) + mypid + tval.tv_sec;
124   v2 = (counter++) * mypid + tval.tv_usec;
125
126   SIVAL(md4_inbuf, 32, v1 ^ IVAL(md4_inbuf, 32));
127   SIVAL(md4_inbuf, 36, v1 ^ IVAL(md4_inbuf, 36));
128
129   mdfour(md4_outbuf, md4_inbuf, sizeof(md4_inbuf));
130
131   /* XOR everything togther in blocks of 4 bytes. */
132   ret = IVAL(md4_outbuf,0);
133   ret ^= IVAL(md4_outbuf,4);
134   ret ^= IVAL(md4_outbuf,8);
135   ret ^= IVAL(md4_outbuf,12);
136
137   DEBUG(10,("do_reseed: returning seed %lu\n", ret));
138
139   return ret;
140 }
141
142 /*******************************************************************
143  Interface to the (hopefully) good crypto random number generator.
144 ********************************************************************/
145
146 void generate_random_buffer( unsigned char *out, int len, BOOL re_seed)
147 {
148   static BOOL done_reseed = False;
149   unsigned char tmp_buf[64];
150   unsigned char md4_buf[16];
151   unsigned char *p;
152
153   if(!done_reseed || re_seed) {
154     srandom(do_reseed());
155     done_reseed = True;
156   }
157
158   /*
159    * Generate random numbers in chunks of 64 bytes,
160    * then md4 them & copy to the output buffer.
161    */
162
163   p = out;
164   while(len > 0) {
165     int i;
166     int copy_len = len > 16 ? 16 : len;
167     for( i = 0; i < 16; i++)
168       SIVAL(tmp_buf, i*4, random());
169     mdfour(md4_buf, tmp_buf, sizeof(tmp_buf));
170     memcpy(p, md4_buf, copy_len);
171     p += copy_len;
172     len -= copy_len;
173   }
174 }