1/*
2   Unix SMB/CIFS implementation.
3
4   Functions to create reasonable random numbers for crypto use.
5
6   Copyright (C) Jeremy Allison 2001
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
23#include "includes.h"
24
25static unsigned char hash[258];
26static uint32 counter;
27
28static BOOL done_reseed = False;
29static void (*reseed_callback)(int *newseed);
30
31/****************************************************************
32 Copy any user given reseed data.
33*****************************************************************/
34
35void set_rand_reseed_callback(void (*fn)(int *))
36{
37	reseed_callback = fn;
38	set_need_random_reseed();
39}
40
41void set_need_random_reseed(void)
42{
43	done_reseed = False;
44}
45
46static void get_rand_reseed_data(int *reseed_data)
47{
48	if (reseed_callback) {
49		reseed_callback(reseed_data);
50	} else {
51		*reseed_data = 0;
52	}
53}
54
55/****************************************************************
56 Setup the seed.
57*****************************************************************/
58
59static void seed_random_stream(unsigned char *seedval, size_t seedlen)
60{
61	unsigned char j = 0;
62	size_t ind;
63
64	for (ind = 0; ind < 256; ind++)
65		hash[ind] = (unsigned char)ind;
66
67	for( ind = 0; ind < 256; ind++) {
68		unsigned char tc;
69
70		j += (hash[ind] + seedval[ind%seedlen]);
71
72		tc = hash[ind];
73		hash[ind] = hash[j];
74		hash[j] = tc;
75	}
76
77	hash[256] = 0;
78	hash[257] = 0;
79}
80
81/****************************************************************
82 Get datasize bytes worth of random data.
83*****************************************************************/
84
85static void get_random_stream(unsigned char *data, size_t datasize)
86{
87	unsigned char index_i = hash[256];
88	unsigned char index_j = hash[257];
89	size_t ind;
90
91	for( ind = 0; ind < datasize; ind++) {
92		unsigned char tc;
93		unsigned char t;
94
95		index_i++;
96		index_j += hash[index_i];
97
98		tc = hash[index_i];
99		hash[index_i] = hash[index_j];
100		hash[index_j] = tc;
101
102		t = hash[index_i] + hash[index_j];
103		data[ind] = hash[t];
104	}
105
106	hash[256] = index_i;
107	hash[257] = index_j;
108}
109
110/****************************************************************
111 Get a 16 byte hash from the contents of a file.
112 Note that the hash is not initialised.
113*****************************************************************/
114
115static void do_filehash(const char *fname, unsigned char *the_hash)
116{
117	unsigned char buf[1011]; /* deliberate weird size */
118	unsigned char tmp_md4[16];
119	int fd, n;
120
121	fd = sys_open(fname,O_RDONLY,0);
122	if (fd == -1)
123		return;
124
125	while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
126		mdfour(tmp_md4, buf, n);
127		for (n=0;n<16;n++)
128			the_hash[n] ^= tmp_md4[n];
129	}
130	close(fd);
131}
132
133/**************************************************************
134 Try and get a good random number seed. Try a number of
135 different factors. Firstly, try /dev/urandom - use if exists.
136
137 We use /dev/urandom as a read of /dev/random can block if
138 the entropy pool dries up. This leads clients to timeout
139 or be very slow on connect.
140
141 If we can't use /dev/urandom then seed the stream random generator
142 above...
143**************************************************************/
144
145static int do_reseed(BOOL use_fd, int fd)
146{
147	unsigned char seed_inbuf[40];
148	uint32 v1, v2; struct timeval tval; pid_t mypid;
149	struct passwd *pw;
150	int reseed_data = 0;
151
152	if (use_fd) {
153		if (fd != -1)
154			return fd;
155
156		fd = sys_open( "/dev/urandom", O_RDONLY,0);
157		if(fd >= 0)
158			return fd;
159	}
160
161	/* Add in some secret file contents */
162
163	do_filehash("/etc/shadow", &seed_inbuf[0]);
164	do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]);
165
166	/*
167	 * Add in the root encrypted password.
168	 * On any system where security is taken
169	 * seriously this will be secret.
170	 */
171
172	pw = getpwnam_alloc("root");
173	if (pw && pw->pw_passwd) {
174		size_t i;
175		unsigned char md4_tmp[16];
176		mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd));
177		for (i=0;i<16;i++)
178			seed_inbuf[8+i] ^= md4_tmp[i];
179		passwd_free(&pw);
180	}
181
182	/*
183	 * Add the counter, time of day, and pid.
184	 */
185
186	GetTimeOfDay(&tval);
187	mypid = sys_getpid();
188	v1 = (counter++) + mypid + tval.tv_sec;
189	v2 = (counter++) * mypid + tval.tv_usec;
190
191	SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
192	SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
193
194	/*
195	 * Add any user-given reseed data.
196	 */
197
198	get_rand_reseed_data(&reseed_data);
199	if (reseed_data) {
200		size_t i;
201		for (i = 0; i < sizeof(seed_inbuf); i++)
202			seed_inbuf[i] ^= ((char *)(&reseed_data))[i % sizeof(reseed_data)];
203	}
204
205	seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
206
207	return -1;
208}
209
210/*******************************************************************
211 Interface to the (hopefully) good crypto random number generator.
212********************************************************************/
213
214void generate_random_buffer( unsigned char *out, int len)
215{
216	static int urand_fd = -1;
217	unsigned char md4_buf[64];
218	unsigned char tmp_buf[16];
219	unsigned char *p;
220
221	if(!done_reseed) {
222		urand_fd = do_reseed(True, urand_fd);
223		done_reseed = True;
224	}
225
226	if (urand_fd != -1 && len > 0) {
227
228		if (read(urand_fd, out, len) == len)
229			return; /* len bytes of random data read from urandom. */
230
231		/* Read of urand error, drop back to non urand method. */
232		close(urand_fd);
233		urand_fd = -1;
234		do_reseed(False, -1);
235		done_reseed = True;
236	}
237
238	/*
239	 * Generate random numbers in chunks of 64 bytes,
240	 * then md4 them & copy to the output buffer.
241	 * This way the raw state of the stream is never externally
242	 * seen.
243	 */
244
245	p = out;
246	while(len > 0) {
247		int copy_len = len > 16 ? 16 : len;
248
249		get_random_stream(md4_buf, sizeof(md4_buf));
250		mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
251		memcpy(p, tmp_buf, copy_len);
252		p += copy_len;
253		len -= copy_len;
254	}
255}
256
257/*******************************************************************
258 Use the random number generator to generate a random string.
259********************************************************************/
260
261static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
262
263char *generate_random_str(size_t len)
264{
265	static unsigned char retstr[256];
266	size_t i;
267
268	memset(retstr, '\0', sizeof(retstr));
269
270	if (len > sizeof(retstr)-1)
271		len = sizeof(retstr) -1;
272	generate_random_buffer( retstr, len);
273	for (i = 0; i < len; i++)
274		retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ];
275
276	retstr[i] = '\0';
277
278	return (char *)retstr;
279}
280