rand_unix.c revision 238405
1109998Smarkm/* crypto/rand/rand_unix.c */
2109998Smarkm/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3109998Smarkm * All rights reserved.
4109998Smarkm *
5109998Smarkm * This package is an SSL implementation written
6109998Smarkm * by Eric Young (eay@cryptsoft.com).
7109998Smarkm * The implementation was written so as to conform with Netscapes SSL.
8109998Smarkm *
9109998Smarkm * This library is free for commercial and non-commercial use as long as
10109998Smarkm * the following conditions are aheared to.  The following conditions
11109998Smarkm * apply to all code found in this distribution, be it the RC4, RSA,
12109998Smarkm * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13109998Smarkm * included with this distribution is covered by the same copyright terms
14109998Smarkm * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15109998Smarkm *
16109998Smarkm * Copyright remains Eric Young's, and as such any Copyright notices in
17109998Smarkm * the code are not to be removed.
18109998Smarkm * If this package is used in a product, Eric Young should be given attribution
19109998Smarkm * as the author of the parts of the library used.
20109998Smarkm * This can be in the form of a textual message at program startup or
21109998Smarkm * in documentation (online or textual) provided with the package.
22109998Smarkm *
23109998Smarkm * Redistribution and use in source and binary forms, with or without
24109998Smarkm * modification, are permitted provided that the following conditions
25109998Smarkm * are met:
26109998Smarkm * 1. Redistributions of source code must retain the copyright
27109998Smarkm *    notice, this list of conditions and the following disclaimer.
28109998Smarkm * 2. Redistributions in binary form must reproduce the above copyright
29109998Smarkm *    notice, this list of conditions and the following disclaimer in the
30109998Smarkm *    documentation and/or other materials provided with the distribution.
31109998Smarkm * 3. All advertising materials mentioning features or use of this software
32109998Smarkm *    must display the following acknowledgement:
33109998Smarkm *    "This product includes cryptographic software written by
34109998Smarkm *     Eric Young (eay@cryptsoft.com)"
35109998Smarkm *    The word 'cryptographic' can be left out if the rouines from the library
36109998Smarkm *    being used are not cryptographic related :-).
37109998Smarkm * 4. If you include any Windows specific code (or a derivative thereof) from
38109998Smarkm *    the apps directory (application code) you must include an acknowledgement:
39109998Smarkm *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40109998Smarkm *
41109998Smarkm * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42109998Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43109998Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44109998Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45109998Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46109998Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47109998Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48109998Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49109998Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50109998Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51109998Smarkm * SUCH DAMAGE.
52109998Smarkm *
53109998Smarkm * The licence and distribution terms for any publically available version or
54109998Smarkm * derivative of this code cannot be changed.  i.e. this code cannot simply be
55109998Smarkm * copied and put under another distribution licence
56109998Smarkm * [including the GNU Public Licence.]
57109998Smarkm */
58109998Smarkm/* ====================================================================
59162911Ssimon * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
60109998Smarkm *
61109998Smarkm * Redistribution and use in source and binary forms, with or without
62109998Smarkm * modification, are permitted provided that the following conditions
63109998Smarkm * are met:
64109998Smarkm *
65109998Smarkm * 1. Redistributions of source code must retain the above copyright
66109998Smarkm *    notice, this list of conditions and the following disclaimer.
67109998Smarkm *
68109998Smarkm * 2. Redistributions in binary form must reproduce the above copyright
69109998Smarkm *    notice, this list of conditions and the following disclaimer in
70109998Smarkm *    the documentation and/or other materials provided with the
71109998Smarkm *    distribution.
72109998Smarkm *
73109998Smarkm * 3. All advertising materials mentioning features or use of this
74109998Smarkm *    software must display the following acknowledgment:
75109998Smarkm *    "This product includes software developed by the OpenSSL Project
76109998Smarkm *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
77109998Smarkm *
78109998Smarkm * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
79109998Smarkm *    endorse or promote products derived from this software without
80109998Smarkm *    prior written permission. For written permission, please contact
81109998Smarkm *    openssl-core@openssl.org.
82109998Smarkm *
83109998Smarkm * 5. Products derived from this software may not be called "OpenSSL"
84109998Smarkm *    nor may "OpenSSL" appear in their names without prior written
85109998Smarkm *    permission of the OpenSSL Project.
86109998Smarkm *
87109998Smarkm * 6. Redistributions of any form whatsoever must retain the following
88109998Smarkm *    acknowledgment:
89109998Smarkm *    "This product includes software developed by the OpenSSL Project
90109998Smarkm *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
91109998Smarkm *
92109998Smarkm * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
93109998Smarkm * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
94109998Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
95109998Smarkm * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
96109998Smarkm * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
97109998Smarkm * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
98109998Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
99109998Smarkm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
100109998Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
101109998Smarkm * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
102109998Smarkm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
103109998Smarkm * OF THE POSSIBILITY OF SUCH DAMAGE.
104109998Smarkm * ====================================================================
105109998Smarkm *
106109998Smarkm * This product includes cryptographic software written by Eric Young
107109998Smarkm * (eay@cryptsoft.com).  This product includes software written by Tim
108109998Smarkm * Hudson (tjh@cryptsoft.com).
109109998Smarkm *
110109998Smarkm */
111160814Ssimon#include <stdio.h>
112109998Smarkm
113109998Smarkm#define USE_SOCKETS
114109998Smarkm#include "e_os.h"
115109998Smarkm#include "cryptlib.h"
116109998Smarkm#include <openssl/rand.h>
117109998Smarkm#include "rand_lcl.h"
118109998Smarkm
119162911Ssimon#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_OS2) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_NETWARE))
120109998Smarkm
121109998Smarkm#include <sys/types.h>
122109998Smarkm#include <sys/time.h>
123109998Smarkm#include <sys/times.h>
124160814Ssimon#include <sys/stat.h>
125109998Smarkm#include <fcntl.h>
126109998Smarkm#include <unistd.h>
127109998Smarkm#include <time.h>
128162911Ssimon#if defined(OPENSSL_SYS_LINUX) /* should actually be available virtually everywhere */
129162911Ssimon# include <poll.h>
130162911Ssimon#endif
131162911Ssimon#include <limits.h>
132162911Ssimon#ifndef FD_SETSIZE
133162911Ssimon# define FD_SETSIZE (8*sizeof(fd_set))
134162911Ssimon#endif
135109998Smarkm
136238405Sjkim#if defined(OPENSSL_SYS_VOS)
137238405Sjkim
138238405Sjkim/* The following algorithm repeatedly samples the real-time clock
139238405Sjkim   (RTC) to generate a sequence of unpredictable data.  The algorithm
140238405Sjkim   relies upon the uneven execution speed of the code (due to factors
141238405Sjkim   such as cache misses, interrupts, bus activity, and scheduling) and
142238405Sjkim   upon the rather large relative difference between the speed of the
143238405Sjkim   clock and the rate at which it can be read.
144238405Sjkim
145238405Sjkim   If this code is ported to an environment where execution speed is
146238405Sjkim   more constant or where the RTC ticks at a much slower rate, or the
147238405Sjkim   clock can be read with fewer instructions, it is likely that the
148238405Sjkim   results would be far more predictable.
149238405Sjkim
150238405Sjkim   As a precaution, we generate 4 times the minimum required amount of
151238405Sjkim   seed data.  */
152238405Sjkim
153109998Smarkmint RAND_poll(void)
154109998Smarkm{
155238405Sjkim	short int code;
156238405Sjkim	gid_t curr_gid;
157238405Sjkim	pid_t curr_pid;
158238405Sjkim	uid_t curr_uid;
159238405Sjkim	int i, k;
160238405Sjkim	struct timespec ts;
161238405Sjkim	unsigned char v;
162238405Sjkim
163238405Sjkim#ifdef OPENSSL_SYS_VOS_HPPA
164238405Sjkim	long duration;
165238405Sjkim	extern void s$sleep (long *_duration, short int *_code);
166238405Sjkim#else
167238405Sjkim#ifdef OPENSSL_SYS_VOS_IA32
168238405Sjkim	long long duration;
169238405Sjkim	extern void s$sleep2 (long long *_duration, short int *_code);
170238405Sjkim#else
171238405Sjkim#error "Unsupported Platform."
172238405Sjkim#endif /* OPENSSL_SYS_VOS_IA32 */
173238405Sjkim#endif /* OPENSSL_SYS_VOS_HPPA */
174238405Sjkim
175238405Sjkim	/* Seed with the gid, pid, and uid, to ensure *some*
176238405Sjkim	   variation between different processes.  */
177238405Sjkim
178238405Sjkim	curr_gid = getgid();
179238405Sjkim	RAND_add (&curr_gid, sizeof curr_gid, 1);
180238405Sjkim	curr_gid = 0;
181238405Sjkim
182238405Sjkim	curr_pid = getpid();
183238405Sjkim	RAND_add (&curr_pid, sizeof curr_pid, 1);
184238405Sjkim	curr_pid = 0;
185238405Sjkim
186238405Sjkim	curr_uid = getuid();
187238405Sjkim	RAND_add (&curr_uid, sizeof curr_uid, 1);
188238405Sjkim	curr_uid = 0;
189238405Sjkim
190238405Sjkim	for (i=0; i<(ENTROPY_NEEDED*4); i++)
191238405Sjkim	{
192238405Sjkim		/* burn some cpu; hope for interrupts, cache
193238405Sjkim		   collisions, bus interference, etc.  */
194238405Sjkim		for (k=0; k<99; k++)
195238405Sjkim			ts.tv_nsec = random ();
196238405Sjkim
197238405Sjkim#ifdef OPENSSL_SYS_VOS_HPPA
198238405Sjkim		/* sleep for 1/1024 of a second (976 us).  */
199238405Sjkim		duration = 1;
200238405Sjkim		s$sleep (&duration, &code);
201238405Sjkim#else
202238405Sjkim#ifdef OPENSSL_SYS_VOS_IA32
203238405Sjkim		/* sleep for 1/65536 of a second (15 us).  */
204238405Sjkim		duration = 1;
205238405Sjkim		s$sleep2 (&duration, &code);
206238405Sjkim#endif /* OPENSSL_SYS_VOS_IA32 */
207238405Sjkim#endif /* OPENSSL_SYS_VOS_HPPA */
208238405Sjkim
209238405Sjkim		/* get wall clock time.  */
210238405Sjkim		clock_gettime (CLOCK_REALTIME, &ts);
211238405Sjkim
212238405Sjkim		/* take 8 bits */
213238405Sjkim		v = (unsigned char) (ts.tv_nsec % 256);
214238405Sjkim		RAND_add (&v, sizeof v, 1);
215238405Sjkim		v = 0;
216238405Sjkim	}
217238405Sjkim	return 1;
218238405Sjkim}
219238405Sjkim#elif defined __OpenBSD__
220238405Sjkimint RAND_poll(void)
221238405Sjkim{
222127128Snectar	u_int32_t rnd = 0, i;
223127128Snectar	unsigned char buf[ENTROPY_NEEDED];
224127128Snectar
225127128Snectar	for (i = 0; i < sizeof(buf); i++) {
226127128Snectar		if (i % 4 == 0)
227127128Snectar			rnd = arc4random();
228127128Snectar		buf[i] = rnd;
229127128Snectar		rnd >>= 8;
230127128Snectar	}
231127128Snectar	RAND_add(buf, sizeof(buf), ENTROPY_NEEDED);
232127128Snectar	memset(buf, 0, sizeof(buf));
233127128Snectar
234127128Snectar	return 1;
235127128Snectar}
236162911Ssimon#else /* !defined(__OpenBSD__) */
237127128Snectarint RAND_poll(void)
238127128Snectar{
239109998Smarkm	unsigned long l;
240109998Smarkm	pid_t curr_pid = getpid();
241109998Smarkm#if defined(DEVRANDOM) || defined(DEVRANDOM_EGD)
242109998Smarkm	unsigned char tmpbuf[ENTROPY_NEEDED];
243109998Smarkm	int n = 0;
244109998Smarkm#endif
245109998Smarkm#ifdef DEVRANDOM
246160814Ssimon	static const char *randomfiles[] = { DEVRANDOM };
247160814Ssimon	struct stat randomstats[sizeof(randomfiles)/sizeof(randomfiles[0])];
248109998Smarkm	int fd;
249238405Sjkim	unsigned int i;
250109998Smarkm#endif
251109998Smarkm#ifdef DEVRANDOM_EGD
252109998Smarkm	static const char *egdsockets[] = { DEVRANDOM_EGD, NULL };
253109998Smarkm	const char **egdsocket = NULL;
254109998Smarkm#endif
255109998Smarkm
256109998Smarkm#ifdef DEVRANDOM
257160814Ssimon	memset(randomstats,0,sizeof(randomstats));
258109998Smarkm	/* Use a random entropy pool device. Linux, FreeBSD and OpenBSD
259109998Smarkm	 * have this. Use /dev/urandom if you can as /dev/random may block
260109998Smarkm	 * if it runs out of random entries.  */
261109998Smarkm
262238405Sjkim	for (i = 0; (i < sizeof(randomfiles)/sizeof(randomfiles[0])) &&
263238405Sjkim			(n < ENTROPY_NEEDED); i++)
264109998Smarkm		{
265160814Ssimon		if ((fd = open(randomfiles[i], O_RDONLY
266160814Ssimon#ifdef O_NONBLOCK
267160814Ssimon			|O_NONBLOCK
268160814Ssimon#endif
269160814Ssimon#ifdef O_BINARY
270160814Ssimon			|O_BINARY
271160814Ssimon#endif
272109998Smarkm#ifdef O_NOCTTY /* If it happens to be a TTY (god forbid), do not make it
273109998Smarkm		   our controlling tty */
274109998Smarkm			|O_NOCTTY
275109998Smarkm#endif
276109998Smarkm			)) >= 0)
277109998Smarkm			{
278162911Ssimon			int usec = 10*1000; /* spend 10ms on each file */
279109998Smarkm			int r;
280238405Sjkim			unsigned int j;
281160814Ssimon			struct stat *st=&randomstats[i];
282109998Smarkm
283160814Ssimon			/* Avoid using same input... Used to be O_NOFOLLOW
284160814Ssimon			 * above, but it's not universally appropriate... */
285160814Ssimon			if (fstat(fd,st) != 0)	{ close(fd); continue; }
286160814Ssimon			for (j=0;j<i;j++)
287160814Ssimon				{
288160814Ssimon				if (randomstats[j].st_ino==st->st_ino &&
289160814Ssimon				    randomstats[j].st_dev==st->st_dev)
290160814Ssimon					break;
291160814Ssimon				}
292160814Ssimon			if (j<i)		{ close(fd); continue; }
293160814Ssimon
294109998Smarkm			do
295109998Smarkm				{
296162911Ssimon				int try_read = 0;
297109998Smarkm
298238405Sjkim#if defined(OPENSSL_SYS_BEOS_R5)
299238405Sjkim				/* select() is broken in BeOS R5, so we simply
300238405Sjkim				 *  try to read something and snooze if we couldn't */
301238405Sjkim				try_read = 1;
302238405Sjkim
303238405Sjkim#elif defined(OPENSSL_SYS_LINUX)
304162911Ssimon				/* use poll() */
305162911Ssimon				struct pollfd pset;
306162911Ssimon
307162911Ssimon				pset.fd = fd;
308162911Ssimon				pset.events = POLLIN;
309162911Ssimon				pset.revents = 0;
310162911Ssimon
311162911Ssimon				if (poll(&pset, 1, usec / 1000) < 0)
312162911Ssimon					usec = 0;
313162911Ssimon				else
314162911Ssimon					try_read = (pset.revents & POLLIN) != 0;
315162911Ssimon
316162911Ssimon#else
317162911Ssimon				/* use select() */
318162911Ssimon				fd_set fset;
319162911Ssimon				struct timeval t;
320162911Ssimon
321162911Ssimon				t.tv_sec = 0;
322162911Ssimon				t.tv_usec = usec;
323162911Ssimon
324194206Ssimon				if (FD_SETSIZE > 0 && (unsigned)fd >= FD_SETSIZE)
325109998Smarkm					{
326162911Ssimon					/* can't use select, so just try to read once anyway */
327162911Ssimon					try_read = 1;
328162911Ssimon					}
329162911Ssimon				else
330162911Ssimon					{
331162911Ssimon					FD_ZERO(&fset);
332162911Ssimon					FD_SET(fd, &fset);
333162911Ssimon
334162911Ssimon					if (select(fd+1,&fset,NULL,NULL,&t) >= 0)
335162911Ssimon						{
336162911Ssimon						usec = t.tv_usec;
337162911Ssimon						if (FD_ISSET(fd, &fset))
338162911Ssimon							try_read = 1;
339162911Ssimon						}
340162911Ssimon					else
341162911Ssimon						usec = 0;
342162911Ssimon					}
343162911Ssimon#endif
344162911Ssimon
345162911Ssimon				if (try_read)
346162911Ssimon					{
347162911Ssimon					r = read(fd,(unsigned char *)tmpbuf+n, ENTROPY_NEEDED-n);
348109998Smarkm					if (r > 0)
349109998Smarkm						n += r;
350238405Sjkim#if defined(OPENSSL_SYS_BEOS_R5)
351238405Sjkim					if (r == 0)
352238405Sjkim						snooze(t.tv_usec);
353238405Sjkim#endif
354109998Smarkm					}
355162911Ssimon				else
356162911Ssimon					r = -1;
357162911Ssimon
358162911Ssimon				/* Some Unixen will update t in select(), some
359162911Ssimon				   won't.  For those who won't, or if we
360162911Ssimon				   didn't use select() in the first place,
361162911Ssimon				   give up here, otherwise, we will do
362109998Smarkm				   this once again for the remaining
363109998Smarkm				   time. */
364162911Ssimon				if (usec == 10*1000)
365162911Ssimon					usec = 0;
366109998Smarkm				}
367162911Ssimon			while ((r > 0 ||
368162911Ssimon			       (errno == EINTR || errno == EAGAIN)) && usec != 0 && n < ENTROPY_NEEDED);
369109998Smarkm
370109998Smarkm			close(fd);
371109998Smarkm			}
372109998Smarkm		}
373162911Ssimon#endif /* defined(DEVRANDOM) */
374109998Smarkm
375109998Smarkm#ifdef DEVRANDOM_EGD
376109998Smarkm	/* Use an EGD socket to read entropy from an EGD or PRNGD entropy
377109998Smarkm	 * collecting daemon. */
378109998Smarkm
379109998Smarkm	for (egdsocket = egdsockets; *egdsocket && n < ENTROPY_NEEDED; egdsocket++)
380109998Smarkm		{
381109998Smarkm		int r;
382109998Smarkm
383109998Smarkm		r = RAND_query_egd_bytes(*egdsocket, (unsigned char *)tmpbuf+n,
384109998Smarkm					 ENTROPY_NEEDED-n);
385109998Smarkm		if (r > 0)
386109998Smarkm			n += r;
387109998Smarkm		}
388162911Ssimon#endif /* defined(DEVRANDOM_EGD) */
389109998Smarkm
390109998Smarkm#if defined(DEVRANDOM) || defined(DEVRANDOM_EGD)
391109998Smarkm	if (n > 0)
392109998Smarkm		{
393160814Ssimon		RAND_add(tmpbuf,sizeof tmpbuf,(double)n);
394109998Smarkm		OPENSSL_cleanse(tmpbuf,n);
395109998Smarkm		}
396109998Smarkm#endif
397109998Smarkm
398109998Smarkm	/* put in some default random data, we need more than just this */
399109998Smarkm	l=curr_pid;
400160814Ssimon	RAND_add(&l,sizeof(l),0.0);
401109998Smarkm	l=getuid();
402160814Ssimon	RAND_add(&l,sizeof(l),0.0);
403109998Smarkm
404109998Smarkm	l=time(NULL);
405160814Ssimon	RAND_add(&l,sizeof(l),0.0);
406109998Smarkm
407238405Sjkim#if defined(OPENSSL_SYS_BEOS)
408238405Sjkim	{
409238405Sjkim	system_info sysInfo;
410238405Sjkim	get_system_info(&sysInfo);
411238405Sjkim	RAND_add(&sysInfo,sizeof(sysInfo),0);
412238405Sjkim	}
413238405Sjkim#endif
414238405Sjkim
415109998Smarkm#if defined(DEVRANDOM) || defined(DEVRANDOM_EGD)
416109998Smarkm	return 1;
417109998Smarkm#else
418109998Smarkm	return 0;
419109998Smarkm#endif
420109998Smarkm}
421109998Smarkm
422162911Ssimon#endif /* defined(__OpenBSD__) */
423162911Ssimon#endif /* !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_OS2) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_NETWARE)) */
424109998Smarkm
425162911Ssimon
426109998Smarkm#if defined(OPENSSL_SYS_VXWORKS)
427109998Smarkmint RAND_poll(void)
428162911Ssimon	{
429162911Ssimon	return 0;
430162911Ssimon	}
431109998Smarkm#endif
432