xref: /minix3/external/bsd/bind/dist/lib/isc/unix/entropy.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1*00b67f09SDavid van Moolenbroek /*	$NetBSD: entropy.c,v 1.5 2014/12/10 04:38:01 christos Exp $	*/
2*00b67f09SDavid van Moolenbroek 
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek  * Copyright (C) 2004-2008, 2012  Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek  * Copyright (C) 2000-2003  Internet Software Consortium.
6*00b67f09SDavid van Moolenbroek  *
7*00b67f09SDavid van Moolenbroek  * Permission to use, copy, modify, and/or distribute this software for any
8*00b67f09SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
9*00b67f09SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
10*00b67f09SDavid van Moolenbroek  *
11*00b67f09SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12*00b67f09SDavid van Moolenbroek  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13*00b67f09SDavid van Moolenbroek  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14*00b67f09SDavid van Moolenbroek  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15*00b67f09SDavid van Moolenbroek  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16*00b67f09SDavid van Moolenbroek  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*00b67f09SDavid van Moolenbroek  * PERFORMANCE OF THIS SOFTWARE.
18*00b67f09SDavid van Moolenbroek  */
19*00b67f09SDavid van Moolenbroek 
20*00b67f09SDavid van Moolenbroek /* Id: entropy.c,v 1.82 2008/12/01 23:47:45 tbox Exp  */
21*00b67f09SDavid van Moolenbroek 
22*00b67f09SDavid van Moolenbroek /* \file unix/entropy.c
23*00b67f09SDavid van Moolenbroek  * \brief
24*00b67f09SDavid van Moolenbroek  * This is the system dependent part of the ISC entropy API.
25*00b67f09SDavid van Moolenbroek  */
26*00b67f09SDavid van Moolenbroek 
27*00b67f09SDavid van Moolenbroek #include <config.h>
28*00b67f09SDavid van Moolenbroek 
29*00b67f09SDavid van Moolenbroek #include <sys/param.h>	/* Openserver 5.0.6A and FD_SETSIZE */
30*00b67f09SDavid van Moolenbroek #include <sys/types.h>
31*00b67f09SDavid van Moolenbroek #include <sys/time.h>
32*00b67f09SDavid van Moolenbroek #include <sys/stat.h>
33*00b67f09SDavid van Moolenbroek #include <sys/socket.h>
34*00b67f09SDavid van Moolenbroek #include <sys/un.h>
35*00b67f09SDavid van Moolenbroek 
36*00b67f09SDavid van Moolenbroek #ifdef HAVE_NANOSLEEP
37*00b67f09SDavid van Moolenbroek #include <time.h>
38*00b67f09SDavid van Moolenbroek #endif
39*00b67f09SDavid van Moolenbroek #include <unistd.h>
40*00b67f09SDavid van Moolenbroek 
41*00b67f09SDavid van Moolenbroek #include <isc/platform.h>
42*00b67f09SDavid van Moolenbroek #include <isc/strerror.h>
43*00b67f09SDavid van Moolenbroek 
44*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_NEEDSYSSELECTH
45*00b67f09SDavid van Moolenbroek #include <sys/select.h>
46*00b67f09SDavid van Moolenbroek #endif
47*00b67f09SDavid van Moolenbroek 
48*00b67f09SDavid van Moolenbroek #include "errno2result.h"
49*00b67f09SDavid van Moolenbroek 
50*00b67f09SDavid van Moolenbroek /*%
51*00b67f09SDavid van Moolenbroek  * There is only one variable in the entropy data structures that is not
52*00b67f09SDavid van Moolenbroek  * system independent, but pulling the structure that uses it into this file
53*00b67f09SDavid van Moolenbroek  * ultimately means pulling several other independent structures here also to
54*00b67f09SDavid van Moolenbroek  * resolve their interdependencies.  Thus only the problem variable's type
55*00b67f09SDavid van Moolenbroek  * is defined here.
56*00b67f09SDavid van Moolenbroek  */
57*00b67f09SDavid van Moolenbroek #define FILESOURCE_HANDLE_TYPE	int
58*00b67f09SDavid van Moolenbroek 
59*00b67f09SDavid van Moolenbroek typedef struct {
60*00b67f09SDavid van Moolenbroek 	int	handle;
61*00b67f09SDavid van Moolenbroek 	enum	{
62*00b67f09SDavid van Moolenbroek 		isc_usocketsource_disconnected,
63*00b67f09SDavid van Moolenbroek 		isc_usocketsource_connecting,
64*00b67f09SDavid van Moolenbroek 		isc_usocketsource_connected,
65*00b67f09SDavid van Moolenbroek 		isc_usocketsource_ndesired,
66*00b67f09SDavid van Moolenbroek 		isc_usocketsource_wrote,
67*00b67f09SDavid van Moolenbroek 		isc_usocketsource_reading
68*00b67f09SDavid van Moolenbroek 	} status;
69*00b67f09SDavid van Moolenbroek 	size_t	sz_to_recv;
70*00b67f09SDavid van Moolenbroek } isc_entropyusocketsource_t;
71*00b67f09SDavid van Moolenbroek 
72*00b67f09SDavid van Moolenbroek #include "../entropy.c"
73*00b67f09SDavid van Moolenbroek 
74*00b67f09SDavid van Moolenbroek static unsigned int
get_from_filesource(isc_entropysource_t * source,isc_uint32_t desired)75*00b67f09SDavid van Moolenbroek get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
76*00b67f09SDavid van Moolenbroek 	isc_entropy_t *ent = source->ent;
77*00b67f09SDavid van Moolenbroek 	unsigned char buf[128];
78*00b67f09SDavid van Moolenbroek 	int fd = source->sources.file.handle;
79*00b67f09SDavid van Moolenbroek 	ssize_t n, ndesired;
80*00b67f09SDavid van Moolenbroek 	unsigned int added;
81*00b67f09SDavid van Moolenbroek 
82*00b67f09SDavid van Moolenbroek 	if (source->bad)
83*00b67f09SDavid van Moolenbroek 		return (0);
84*00b67f09SDavid van Moolenbroek 
85*00b67f09SDavid van Moolenbroek 	desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
86*00b67f09SDavid van Moolenbroek 
87*00b67f09SDavid van Moolenbroek 	added = 0;
88*00b67f09SDavid van Moolenbroek 	while (desired > 0) {
89*00b67f09SDavid van Moolenbroek 		ndesired = ISC_MIN(desired, sizeof(buf));
90*00b67f09SDavid van Moolenbroek 		n = read(fd, buf, ndesired);
91*00b67f09SDavid van Moolenbroek 		if (n < 0) {
92*00b67f09SDavid van Moolenbroek 			if (errno == EAGAIN || errno == EINTR)
93*00b67f09SDavid van Moolenbroek 				goto out;
94*00b67f09SDavid van Moolenbroek 			goto err;
95*00b67f09SDavid van Moolenbroek 		}
96*00b67f09SDavid van Moolenbroek 		if (n == 0)
97*00b67f09SDavid van Moolenbroek 			goto err;
98*00b67f09SDavid van Moolenbroek 
99*00b67f09SDavid van Moolenbroek 		entropypool_adddata(ent, buf, n, n * 8);
100*00b67f09SDavid van Moolenbroek 		added += n * 8;
101*00b67f09SDavid van Moolenbroek 		desired -= n;
102*00b67f09SDavid van Moolenbroek 	}
103*00b67f09SDavid van Moolenbroek 	goto out;
104*00b67f09SDavid van Moolenbroek 
105*00b67f09SDavid van Moolenbroek  err:
106*00b67f09SDavid van Moolenbroek 	(void)close(fd);
107*00b67f09SDavid van Moolenbroek 	source->sources.file.handle = -1;
108*00b67f09SDavid van Moolenbroek 	source->bad = ISC_TRUE;
109*00b67f09SDavid van Moolenbroek 
110*00b67f09SDavid van Moolenbroek  out:
111*00b67f09SDavid van Moolenbroek 	return (added);
112*00b67f09SDavid van Moolenbroek }
113*00b67f09SDavid van Moolenbroek 
114*00b67f09SDavid van Moolenbroek static unsigned int
get_from_usocketsource(isc_entropysource_t * source,isc_uint32_t desired)115*00b67f09SDavid van Moolenbroek get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
116*00b67f09SDavid van Moolenbroek 	isc_entropy_t *ent = source->ent;
117*00b67f09SDavid van Moolenbroek 	unsigned char buf[128];
118*00b67f09SDavid van Moolenbroek 	int fd = source->sources.usocket.handle;
119*00b67f09SDavid van Moolenbroek 	ssize_t n = 0, ndesired;
120*00b67f09SDavid van Moolenbroek 	unsigned int added;
121*00b67f09SDavid van Moolenbroek 	size_t sz_to_recv = source->sources.usocket.sz_to_recv;
122*00b67f09SDavid van Moolenbroek 
123*00b67f09SDavid van Moolenbroek 	if (source->bad)
124*00b67f09SDavid van Moolenbroek 		return (0);
125*00b67f09SDavid van Moolenbroek 
126*00b67f09SDavid van Moolenbroek 	desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
127*00b67f09SDavid van Moolenbroek 
128*00b67f09SDavid van Moolenbroek 	added = 0;
129*00b67f09SDavid van Moolenbroek 	while (desired > 0) {
130*00b67f09SDavid van Moolenbroek 		ndesired = ISC_MIN(desired, sizeof(buf));
131*00b67f09SDavid van Moolenbroek  eagain_loop:
132*00b67f09SDavid van Moolenbroek 
133*00b67f09SDavid van Moolenbroek 		switch ( source->sources.usocket.status ) {
134*00b67f09SDavid van Moolenbroek 		case isc_usocketsource_ndesired:
135*00b67f09SDavid van Moolenbroek 			buf[0] = ndesired;
136*00b67f09SDavid van Moolenbroek 			if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
137*00b67f09SDavid van Moolenbroek 				if (errno == EWOULDBLOCK || errno == EINTR ||
138*00b67f09SDavid van Moolenbroek 				    errno == ECONNRESET)
139*00b67f09SDavid van Moolenbroek 					goto out;
140*00b67f09SDavid van Moolenbroek 				goto err;
141*00b67f09SDavid van Moolenbroek 			}
142*00b67f09SDavid van Moolenbroek 			INSIST(n == 1);
143*00b67f09SDavid van Moolenbroek 			source->sources.usocket.status =
144*00b67f09SDavid van Moolenbroek 						isc_usocketsource_wrote;
145*00b67f09SDavid van Moolenbroek 			goto eagain_loop;
146*00b67f09SDavid van Moolenbroek 
147*00b67f09SDavid van Moolenbroek 		case isc_usocketsource_connecting:
148*00b67f09SDavid van Moolenbroek 		case isc_usocketsource_connected:
149*00b67f09SDavid van Moolenbroek 			buf[0] = 1;
150*00b67f09SDavid van Moolenbroek 			buf[1] = ndesired;
151*00b67f09SDavid van Moolenbroek 			if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
152*00b67f09SDavid van Moolenbroek 				if (errno == EWOULDBLOCK || errno == EINTR ||
153*00b67f09SDavid van Moolenbroek 				    errno == ECONNRESET)
154*00b67f09SDavid van Moolenbroek 					goto out;
155*00b67f09SDavid van Moolenbroek 				goto err;
156*00b67f09SDavid van Moolenbroek 			}
157*00b67f09SDavid van Moolenbroek 			if (n == 1) {
158*00b67f09SDavid van Moolenbroek 				source->sources.usocket.status =
159*00b67f09SDavid van Moolenbroek 					isc_usocketsource_ndesired;
160*00b67f09SDavid van Moolenbroek 				goto eagain_loop;
161*00b67f09SDavid van Moolenbroek 			}
162*00b67f09SDavid van Moolenbroek 			INSIST(n == 2);
163*00b67f09SDavid van Moolenbroek 			source->sources.usocket.status =
164*00b67f09SDavid van Moolenbroek 						isc_usocketsource_wrote;
165*00b67f09SDavid van Moolenbroek 			/*FALLTHROUGH*/
166*00b67f09SDavid van Moolenbroek 
167*00b67f09SDavid van Moolenbroek 		case isc_usocketsource_wrote:
168*00b67f09SDavid van Moolenbroek 			if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
169*00b67f09SDavid van Moolenbroek 				if (errno == EAGAIN) {
170*00b67f09SDavid van Moolenbroek 					/*
171*00b67f09SDavid van Moolenbroek 					 * The problem of EAGAIN (try again
172*00b67f09SDavid van Moolenbroek 					 * later) is a major issue on HP-UX.
173*00b67f09SDavid van Moolenbroek 					 * Solaris actually tries the recvfrom
174*00b67f09SDavid van Moolenbroek 					 * call again, while HP-UX just dies.
175*00b67f09SDavid van Moolenbroek 					 * This code is an attempt to let the
176*00b67f09SDavid van Moolenbroek 					 * entropy pool fill back up (at least
177*00b67f09SDavid van Moolenbroek 					 * that's what I think the problem is.)
178*00b67f09SDavid van Moolenbroek 					 * We go to eagain_loop because if we
179*00b67f09SDavid van Moolenbroek 					 * just "break", then the "desired"
180*00b67f09SDavid van Moolenbroek 					 * amount gets borked.
181*00b67f09SDavid van Moolenbroek 					 */
182*00b67f09SDavid van Moolenbroek #ifdef HAVE_NANOSLEEP
183*00b67f09SDavid van Moolenbroek 					struct timespec ts;
184*00b67f09SDavid van Moolenbroek 
185*00b67f09SDavid van Moolenbroek 					ts.tv_sec = 0;
186*00b67f09SDavid van Moolenbroek 					ts.tv_nsec = 1000000;
187*00b67f09SDavid van Moolenbroek 					nanosleep(&ts, NULL);
188*00b67f09SDavid van Moolenbroek #else
189*00b67f09SDavid van Moolenbroek 					usleep(1000);
190*00b67f09SDavid van Moolenbroek #endif
191*00b67f09SDavid van Moolenbroek 					goto eagain_loop;
192*00b67f09SDavid van Moolenbroek 				}
193*00b67f09SDavid van Moolenbroek 				if (errno == EWOULDBLOCK || errno == EINTR)
194*00b67f09SDavid van Moolenbroek 					goto out;
195*00b67f09SDavid van Moolenbroek 				goto err;
196*00b67f09SDavid van Moolenbroek 			}
197*00b67f09SDavid van Moolenbroek 			source->sources.usocket.status =
198*00b67f09SDavid van Moolenbroek 					isc_usocketsource_reading;
199*00b67f09SDavid van Moolenbroek 			sz_to_recv = buf[0];
200*00b67f09SDavid van Moolenbroek 			source->sources.usocket.sz_to_recv = sz_to_recv;
201*00b67f09SDavid van Moolenbroek 			if (sz_to_recv > sizeof(buf))
202*00b67f09SDavid van Moolenbroek 				goto err;
203*00b67f09SDavid van Moolenbroek 			/*FALLTHROUGH*/
204*00b67f09SDavid van Moolenbroek 
205*00b67f09SDavid van Moolenbroek 		case isc_usocketsource_reading:
206*00b67f09SDavid van Moolenbroek 			if (sz_to_recv != 0U) {
207*00b67f09SDavid van Moolenbroek 				n = recv(fd, buf, sz_to_recv, 0);
208*00b67f09SDavid van Moolenbroek 				if (n < 0) {
209*00b67f09SDavid van Moolenbroek 					if (errno == EWOULDBLOCK ||
210*00b67f09SDavid van Moolenbroek 					    errno == EINTR)
211*00b67f09SDavid van Moolenbroek 						goto out;
212*00b67f09SDavid van Moolenbroek 					goto err;
213*00b67f09SDavid van Moolenbroek 				}
214*00b67f09SDavid van Moolenbroek 			} else
215*00b67f09SDavid van Moolenbroek 				n = 0;
216*00b67f09SDavid van Moolenbroek 			break;
217*00b67f09SDavid van Moolenbroek 
218*00b67f09SDavid van Moolenbroek 		default:
219*00b67f09SDavid van Moolenbroek 			goto err;
220*00b67f09SDavid van Moolenbroek 		}
221*00b67f09SDavid van Moolenbroek 
222*00b67f09SDavid van Moolenbroek 		if ((size_t)n != sz_to_recv)
223*00b67f09SDavid van Moolenbroek 			source->sources.usocket.sz_to_recv -= n;
224*00b67f09SDavid van Moolenbroek 		else
225*00b67f09SDavid van Moolenbroek 			source->sources.usocket.status =
226*00b67f09SDavid van Moolenbroek 				isc_usocketsource_connected;
227*00b67f09SDavid van Moolenbroek 
228*00b67f09SDavid van Moolenbroek 		if (n == 0)
229*00b67f09SDavid van Moolenbroek 			goto out;
230*00b67f09SDavid van Moolenbroek 
231*00b67f09SDavid van Moolenbroek 		entropypool_adddata(ent, buf, n, n * 8);
232*00b67f09SDavid van Moolenbroek 		added += n * 8;
233*00b67f09SDavid van Moolenbroek 		desired -= n;
234*00b67f09SDavid van Moolenbroek 	}
235*00b67f09SDavid van Moolenbroek 	goto out;
236*00b67f09SDavid van Moolenbroek 
237*00b67f09SDavid van Moolenbroek  err:
238*00b67f09SDavid van Moolenbroek 	close(fd);
239*00b67f09SDavid van Moolenbroek 	source->bad = ISC_TRUE;
240*00b67f09SDavid van Moolenbroek 	source->sources.usocket.status = isc_usocketsource_disconnected;
241*00b67f09SDavid van Moolenbroek 	source->sources.usocket.handle = -1;
242*00b67f09SDavid van Moolenbroek 
243*00b67f09SDavid van Moolenbroek  out:
244*00b67f09SDavid van Moolenbroek 	return (added);
245*00b67f09SDavid van Moolenbroek }
246*00b67f09SDavid van Moolenbroek 
247*00b67f09SDavid van Moolenbroek /*
248*00b67f09SDavid van Moolenbroek  * Poll each source, trying to get data from it to stuff into the entropy
249*00b67f09SDavid van Moolenbroek  * pool.
250*00b67f09SDavid van Moolenbroek  */
251*00b67f09SDavid van Moolenbroek static void
fillpool(isc_entropy_t * ent,unsigned int desired,isc_boolean_t blocking)252*00b67f09SDavid van Moolenbroek fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
253*00b67f09SDavid van Moolenbroek 	unsigned int added;
254*00b67f09SDavid van Moolenbroek 	unsigned int remaining;
255*00b67f09SDavid van Moolenbroek 	unsigned int needed;
256*00b67f09SDavid van Moolenbroek 	unsigned int nsource;
257*00b67f09SDavid van Moolenbroek 	isc_entropysource_t *source;
258*00b67f09SDavid van Moolenbroek 
259*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_ENTROPY(ent));
260*00b67f09SDavid van Moolenbroek 
261*00b67f09SDavid van Moolenbroek 	needed = desired;
262*00b67f09SDavid van Moolenbroek 
263*00b67f09SDavid van Moolenbroek 	/*
264*00b67f09SDavid van Moolenbroek 	 * This logic is a little strange, so an explanation is in order.
265*00b67f09SDavid van Moolenbroek 	 *
266*00b67f09SDavid van Moolenbroek 	 * If needed is 0, it means we are being asked to "fill to whatever
267*00b67f09SDavid van Moolenbroek 	 * we think is best."  This means that if we have at least a
268*00b67f09SDavid van Moolenbroek 	 * partially full pool (say, > 1/4th of the pool) we probably don't
269*00b67f09SDavid van Moolenbroek 	 * need to add anything.
270*00b67f09SDavid van Moolenbroek 	 *
271*00b67f09SDavid van Moolenbroek 	 * Also, we will check to see if the "pseudo" count is too high.
272*00b67f09SDavid van Moolenbroek 	 * If it is, try to mix in better data.  Too high is currently
273*00b67f09SDavid van Moolenbroek 	 * defined as 1/4th of the pool.
274*00b67f09SDavid van Moolenbroek 	 *
275*00b67f09SDavid van Moolenbroek 	 * Next, if we are asked to add a specific bit of entropy, make
276*00b67f09SDavid van Moolenbroek 	 * certain that we will do so.  Clamp how much we try to add to
277*00b67f09SDavid van Moolenbroek 	 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
278*00b67f09SDavid van Moolenbroek 	 *
279*00b67f09SDavid van Moolenbroek 	 * Note that if we are in a blocking mode, we will only try to
280*00b67f09SDavid van Moolenbroek 	 * get as much data as we need, not as much as we might want
281*00b67f09SDavid van Moolenbroek 	 * to build up.
282*00b67f09SDavid van Moolenbroek 	 */
283*00b67f09SDavid van Moolenbroek 	if (needed == 0) {
284*00b67f09SDavid van Moolenbroek 		REQUIRE(!blocking);
285*00b67f09SDavid van Moolenbroek 
286*00b67f09SDavid van Moolenbroek 		if ((ent->pool.entropy >= RND_POOLBITS / 4)
287*00b67f09SDavid van Moolenbroek 		    && (ent->pool.pseudo <= RND_POOLBITS / 4))
288*00b67f09SDavid van Moolenbroek 			return;
289*00b67f09SDavid van Moolenbroek 
290*00b67f09SDavid van Moolenbroek 		needed = THRESHOLD_BITS * 4;
291*00b67f09SDavid van Moolenbroek 	} else {
292*00b67f09SDavid van Moolenbroek 		needed = ISC_MAX(needed, THRESHOLD_BITS);
293*00b67f09SDavid van Moolenbroek 		needed = ISC_MIN(needed, RND_POOLBITS);
294*00b67f09SDavid van Moolenbroek 	}
295*00b67f09SDavid van Moolenbroek 
296*00b67f09SDavid van Moolenbroek 	/*
297*00b67f09SDavid van Moolenbroek 	 * In any case, clamp how much we need to how much we can add.
298*00b67f09SDavid van Moolenbroek 	 */
299*00b67f09SDavid van Moolenbroek 	needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
300*00b67f09SDavid van Moolenbroek 
301*00b67f09SDavid van Moolenbroek 	/*
302*00b67f09SDavid van Moolenbroek 	 * But wait!  If we're not yet initialized, we need at least
303*00b67f09SDavid van Moolenbroek 	 *	THRESHOLD_BITS
304*00b67f09SDavid van Moolenbroek 	 * of randomness.
305*00b67f09SDavid van Moolenbroek 	 */
306*00b67f09SDavid van Moolenbroek 	if (ent->initialized < THRESHOLD_BITS)
307*00b67f09SDavid van Moolenbroek 		needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
308*00b67f09SDavid van Moolenbroek 
309*00b67f09SDavid van Moolenbroek 	/*
310*00b67f09SDavid van Moolenbroek 	 * Poll each file source to see if we can read anything useful from
311*00b67f09SDavid van Moolenbroek 	 * it.  XXXMLG When where are multiple sources, we should keep a
312*00b67f09SDavid van Moolenbroek 	 * record of which one we last used so we can start from it (or the
313*00b67f09SDavid van Moolenbroek 	 * next one) to avoid letting some sources build up entropy while
314*00b67f09SDavid van Moolenbroek 	 * others are always drained.
315*00b67f09SDavid van Moolenbroek 	 */
316*00b67f09SDavid van Moolenbroek 
317*00b67f09SDavid van Moolenbroek 	added = 0;
318*00b67f09SDavid van Moolenbroek 	remaining = needed;
319*00b67f09SDavid van Moolenbroek 	if (ent->nextsource == NULL) {
320*00b67f09SDavid van Moolenbroek 		ent->nextsource = ISC_LIST_HEAD(ent->sources);
321*00b67f09SDavid van Moolenbroek 		if (ent->nextsource == NULL)
322*00b67f09SDavid van Moolenbroek 			return;
323*00b67f09SDavid van Moolenbroek 	}
324*00b67f09SDavid van Moolenbroek 	source = ent->nextsource;
325*00b67f09SDavid van Moolenbroek  again_file:
326*00b67f09SDavid van Moolenbroek 	for (nsource = 0; nsource < ent->nsources; nsource++) {
327*00b67f09SDavid van Moolenbroek 		unsigned int got;
328*00b67f09SDavid van Moolenbroek 
329*00b67f09SDavid van Moolenbroek 		if (remaining == 0)
330*00b67f09SDavid van Moolenbroek 			break;
331*00b67f09SDavid van Moolenbroek 
332*00b67f09SDavid van Moolenbroek 		got = 0;
333*00b67f09SDavid van Moolenbroek 
334*00b67f09SDavid van Moolenbroek 		switch ( source->type ) {
335*00b67f09SDavid van Moolenbroek 		case ENTROPY_SOURCETYPE_FILE:
336*00b67f09SDavid van Moolenbroek 			got = get_from_filesource(source, remaining);
337*00b67f09SDavid van Moolenbroek 			break;
338*00b67f09SDavid van Moolenbroek 
339*00b67f09SDavid van Moolenbroek 		case ENTROPY_SOURCETYPE_USOCKET:
340*00b67f09SDavid van Moolenbroek 			got = get_from_usocketsource(source, remaining);
341*00b67f09SDavid van Moolenbroek 			break;
342*00b67f09SDavid van Moolenbroek 		}
343*00b67f09SDavid van Moolenbroek 
344*00b67f09SDavid van Moolenbroek 		added += got;
345*00b67f09SDavid van Moolenbroek 
346*00b67f09SDavid van Moolenbroek 		remaining -= ISC_MIN(remaining, got);
347*00b67f09SDavid van Moolenbroek 
348*00b67f09SDavid van Moolenbroek 		source = ISC_LIST_NEXT(source, link);
349*00b67f09SDavid van Moolenbroek 		if (source == NULL)
350*00b67f09SDavid van Moolenbroek 			source = ISC_LIST_HEAD(ent->sources);
351*00b67f09SDavid van Moolenbroek 	}
352*00b67f09SDavid van Moolenbroek 	ent->nextsource = source;
353*00b67f09SDavid van Moolenbroek 
354*00b67f09SDavid van Moolenbroek 	if (blocking && remaining != 0) {
355*00b67f09SDavid van Moolenbroek 		int fds;
356*00b67f09SDavid van Moolenbroek 
357*00b67f09SDavid van Moolenbroek 		fds = wait_for_sources(ent);
358*00b67f09SDavid van Moolenbroek 		if (fds > 0)
359*00b67f09SDavid van Moolenbroek 			goto again_file;
360*00b67f09SDavid van Moolenbroek 	}
361*00b67f09SDavid van Moolenbroek 
362*00b67f09SDavid van Moolenbroek 	/*
363*00b67f09SDavid van Moolenbroek 	 * Here, if there are bits remaining to be had and we can block,
364*00b67f09SDavid van Moolenbroek 	 * check to see if we have a callback source.  If so, call them.
365*00b67f09SDavid van Moolenbroek 	 */
366*00b67f09SDavid van Moolenbroek 	source = ISC_LIST_HEAD(ent->sources);
367*00b67f09SDavid van Moolenbroek 	while ((remaining != 0) && (source != NULL)) {
368*00b67f09SDavid van Moolenbroek 		unsigned int got;
369*00b67f09SDavid van Moolenbroek 
370*00b67f09SDavid van Moolenbroek 		got = 0;
371*00b67f09SDavid van Moolenbroek 
372*00b67f09SDavid van Moolenbroek 		if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
373*00b67f09SDavid van Moolenbroek 			got = get_from_callback(source, remaining, blocking);
374*00b67f09SDavid van Moolenbroek 
375*00b67f09SDavid van Moolenbroek 		added += got;
376*00b67f09SDavid van Moolenbroek 		remaining -= ISC_MIN(remaining, got);
377*00b67f09SDavid van Moolenbroek 
378*00b67f09SDavid van Moolenbroek 		if (added >= needed)
379*00b67f09SDavid van Moolenbroek 			break;
380*00b67f09SDavid van Moolenbroek 
381*00b67f09SDavid van Moolenbroek 		source = ISC_LIST_NEXT(source, link);
382*00b67f09SDavid van Moolenbroek 	}
383*00b67f09SDavid van Moolenbroek 
384*00b67f09SDavid van Moolenbroek 	/*
385*00b67f09SDavid van Moolenbroek 	 * Mark as initialized if we've added enough data.
386*00b67f09SDavid van Moolenbroek 	 */
387*00b67f09SDavid van Moolenbroek 	if (ent->initialized < THRESHOLD_BITS)
388*00b67f09SDavid van Moolenbroek 		ent->initialized += added;
389*00b67f09SDavid van Moolenbroek }
390*00b67f09SDavid van Moolenbroek 
391*00b67f09SDavid van Moolenbroek static int
wait_for_sources(isc_entropy_t * ent)392*00b67f09SDavid van Moolenbroek wait_for_sources(isc_entropy_t *ent) {
393*00b67f09SDavid van Moolenbroek 	isc_entropysource_t *source;
394*00b67f09SDavid van Moolenbroek 	int maxfd, fd;
395*00b67f09SDavid van Moolenbroek 	int cc;
396*00b67f09SDavid van Moolenbroek 	fd_set reads;
397*00b67f09SDavid van Moolenbroek 	fd_set writes;
398*00b67f09SDavid van Moolenbroek 
399*00b67f09SDavid van Moolenbroek 	maxfd = -1;
400*00b67f09SDavid van Moolenbroek 	FD_ZERO(&reads);
401*00b67f09SDavid van Moolenbroek 	FD_ZERO(&writes);
402*00b67f09SDavid van Moolenbroek 
403*00b67f09SDavid van Moolenbroek 	source = ISC_LIST_HEAD(ent->sources);
404*00b67f09SDavid van Moolenbroek 	while (source != NULL) {
405*00b67f09SDavid van Moolenbroek 		if (source->type == ENTROPY_SOURCETYPE_FILE) {
406*00b67f09SDavid van Moolenbroek 			fd = source->sources.file.handle;
407*00b67f09SDavid van Moolenbroek 			if (fd >= 0) {
408*00b67f09SDavid van Moolenbroek 				maxfd = ISC_MAX(maxfd, fd);
409*00b67f09SDavid van Moolenbroek 				FD_SET(fd, &reads);
410*00b67f09SDavid van Moolenbroek 			}
411*00b67f09SDavid van Moolenbroek 		}
412*00b67f09SDavid van Moolenbroek 		if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
413*00b67f09SDavid van Moolenbroek 			fd = source->sources.usocket.handle;
414*00b67f09SDavid van Moolenbroek 			if (fd >= 0) {
415*00b67f09SDavid van Moolenbroek 				switch (source->sources.usocket.status) {
416*00b67f09SDavid van Moolenbroek 				case isc_usocketsource_disconnected:
417*00b67f09SDavid van Moolenbroek 					break;
418*00b67f09SDavid van Moolenbroek 				case isc_usocketsource_connecting:
419*00b67f09SDavid van Moolenbroek 				case isc_usocketsource_connected:
420*00b67f09SDavid van Moolenbroek 				case isc_usocketsource_ndesired:
421*00b67f09SDavid van Moolenbroek 					maxfd = ISC_MAX(maxfd, fd);
422*00b67f09SDavid van Moolenbroek 					FD_SET(fd, &writes);
423*00b67f09SDavid van Moolenbroek 					break;
424*00b67f09SDavid van Moolenbroek 				case isc_usocketsource_wrote:
425*00b67f09SDavid van Moolenbroek 				case isc_usocketsource_reading:
426*00b67f09SDavid van Moolenbroek 					maxfd = ISC_MAX(maxfd, fd);
427*00b67f09SDavid van Moolenbroek 					FD_SET(fd, &reads);
428*00b67f09SDavid van Moolenbroek 					break;
429*00b67f09SDavid van Moolenbroek 				}
430*00b67f09SDavid van Moolenbroek 			}
431*00b67f09SDavid van Moolenbroek 		}
432*00b67f09SDavid van Moolenbroek 		source = ISC_LIST_NEXT(source, link);
433*00b67f09SDavid van Moolenbroek 	}
434*00b67f09SDavid van Moolenbroek 
435*00b67f09SDavid van Moolenbroek 	if (maxfd < 0)
436*00b67f09SDavid van Moolenbroek 		return (-1);
437*00b67f09SDavid van Moolenbroek 
438*00b67f09SDavid van Moolenbroek 	cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
439*00b67f09SDavid van Moolenbroek 	if (cc < 0)
440*00b67f09SDavid van Moolenbroek 		return (-1);
441*00b67f09SDavid van Moolenbroek 
442*00b67f09SDavid van Moolenbroek 	return (cc);
443*00b67f09SDavid van Moolenbroek }
444*00b67f09SDavid van Moolenbroek 
445*00b67f09SDavid van Moolenbroek static void
destroyfilesource(isc_entropyfilesource_t * source)446*00b67f09SDavid van Moolenbroek destroyfilesource(isc_entropyfilesource_t *source) {
447*00b67f09SDavid van Moolenbroek 	(void)close(source->handle);
448*00b67f09SDavid van Moolenbroek }
449*00b67f09SDavid van Moolenbroek 
450*00b67f09SDavid van Moolenbroek static void
destroyusocketsource(isc_entropyusocketsource_t * source)451*00b67f09SDavid van Moolenbroek destroyusocketsource(isc_entropyusocketsource_t *source) {
452*00b67f09SDavid van Moolenbroek 	close(source->handle);
453*00b67f09SDavid van Moolenbroek }
454*00b67f09SDavid van Moolenbroek 
455*00b67f09SDavid van Moolenbroek /*
456*00b67f09SDavid van Moolenbroek  * Make a fd non-blocking
457*00b67f09SDavid van Moolenbroek  */
458*00b67f09SDavid van Moolenbroek static isc_result_t
make_nonblock(int fd)459*00b67f09SDavid van Moolenbroek make_nonblock(int fd) {
460*00b67f09SDavid van Moolenbroek 	int ret;
461*00b67f09SDavid van Moolenbroek 	int flags;
462*00b67f09SDavid van Moolenbroek 	char strbuf[ISC_STRERRORSIZE];
463*00b67f09SDavid van Moolenbroek #ifdef USE_FIONBIO_IOCTL
464*00b67f09SDavid van Moolenbroek 	int on = 1;
465*00b67f09SDavid van Moolenbroek 
466*00b67f09SDavid van Moolenbroek 	ret = ioctl(fd, FIONBIO, (char *)&on);
467*00b67f09SDavid van Moolenbroek #else
468*00b67f09SDavid van Moolenbroek 	flags = fcntl(fd, F_GETFL, 0);
469*00b67f09SDavid van Moolenbroek 	flags |= PORT_NONBLOCK;
470*00b67f09SDavid van Moolenbroek 	ret = fcntl(fd, F_SETFL, flags);
471*00b67f09SDavid van Moolenbroek #endif
472*00b67f09SDavid van Moolenbroek 
473*00b67f09SDavid van Moolenbroek 	if (ret == -1) {
474*00b67f09SDavid van Moolenbroek 		isc__strerror(errno, strbuf, sizeof(strbuf));
475*00b67f09SDavid van Moolenbroek 		UNEXPECTED_ERROR(__FILE__, __LINE__,
476*00b67f09SDavid van Moolenbroek #ifdef USE_FIONBIO_IOCTL
477*00b67f09SDavid van Moolenbroek 				 "ioctl(%d, FIONBIO, &on): %s", fd,
478*00b67f09SDavid van Moolenbroek #else
479*00b67f09SDavid van Moolenbroek 				 "fcntl(%d, F_SETFL, %d): %s", fd, flags,
480*00b67f09SDavid van Moolenbroek #endif
481*00b67f09SDavid van Moolenbroek 				 strbuf);
482*00b67f09SDavid van Moolenbroek 
483*00b67f09SDavid van Moolenbroek 		return (ISC_R_UNEXPECTED);
484*00b67f09SDavid van Moolenbroek 	}
485*00b67f09SDavid van Moolenbroek 
486*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
487*00b67f09SDavid van Moolenbroek }
488*00b67f09SDavid van Moolenbroek 
489*00b67f09SDavid van Moolenbroek isc_result_t
isc_entropy_createfilesource(isc_entropy_t * ent,const char * fname)490*00b67f09SDavid van Moolenbroek isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
491*00b67f09SDavid van Moolenbroek 	int fd;
492*00b67f09SDavid van Moolenbroek 	struct stat _stat;
493*00b67f09SDavid van Moolenbroek 	isc_boolean_t is_usocket = ISC_FALSE;
494*00b67f09SDavid van Moolenbroek 	isc_boolean_t is_connected = ISC_FALSE;
495*00b67f09SDavid van Moolenbroek 	isc_result_t ret;
496*00b67f09SDavid van Moolenbroek 	isc_entropysource_t *source;
497*00b67f09SDavid van Moolenbroek 
498*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_ENTROPY(ent));
499*00b67f09SDavid van Moolenbroek 	REQUIRE(fname != NULL);
500*00b67f09SDavid van Moolenbroek 
501*00b67f09SDavid van Moolenbroek 	LOCK(&ent->lock);
502*00b67f09SDavid van Moolenbroek 
503*00b67f09SDavid van Moolenbroek 	if (stat(fname, &_stat) < 0) {
504*00b67f09SDavid van Moolenbroek 		ret = isc__errno2result(errno);
505*00b67f09SDavid van Moolenbroek 		goto errout;
506*00b67f09SDavid van Moolenbroek 	}
507*00b67f09SDavid van Moolenbroek 	/*
508*00b67f09SDavid van Moolenbroek 	 * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
509*00b67f09SDavid van Moolenbroek 	 * but it does return type S_IFIFO (the OS believes that
510*00b67f09SDavid van Moolenbroek 	 * the socket is a fifo).  This may be an issue if we tell
511*00b67f09SDavid van Moolenbroek 	 * the program to look at an actual FIFO as its source of
512*00b67f09SDavid van Moolenbroek 	 * entropy.
513*00b67f09SDavid van Moolenbroek 	 */
514*00b67f09SDavid van Moolenbroek #if defined(S_ISSOCK)
515*00b67f09SDavid van Moolenbroek 	if (S_ISSOCK(_stat.st_mode))
516*00b67f09SDavid van Moolenbroek 		is_usocket = ISC_TRUE;
517*00b67f09SDavid van Moolenbroek #endif
518*00b67f09SDavid van Moolenbroek #if defined(S_ISFIFO) && defined(sun)
519*00b67f09SDavid van Moolenbroek 	if (S_ISFIFO(_stat.st_mode))
520*00b67f09SDavid van Moolenbroek 		is_usocket = ISC_TRUE;
521*00b67f09SDavid van Moolenbroek #endif
522*00b67f09SDavid van Moolenbroek 	if (is_usocket)
523*00b67f09SDavid van Moolenbroek 		fd = socket(PF_UNIX, SOCK_STREAM, 0);
524*00b67f09SDavid van Moolenbroek 	else
525*00b67f09SDavid van Moolenbroek 		fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
526*00b67f09SDavid van Moolenbroek 
527*00b67f09SDavid van Moolenbroek 	if (fd < 0) {
528*00b67f09SDavid van Moolenbroek 		ret = isc__errno2result(errno);
529*00b67f09SDavid van Moolenbroek 		goto errout;
530*00b67f09SDavid van Moolenbroek 	}
531*00b67f09SDavid van Moolenbroek 
532*00b67f09SDavid van Moolenbroek 	ret = make_nonblock(fd);
533*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
534*00b67f09SDavid van Moolenbroek 		goto closefd;
535*00b67f09SDavid van Moolenbroek 
536*00b67f09SDavid van Moolenbroek 	if (is_usocket) {
537*00b67f09SDavid van Moolenbroek 		struct sockaddr_un sname;
538*00b67f09SDavid van Moolenbroek 
539*00b67f09SDavid van Moolenbroek 		memset(&sname, 0, sizeof(sname));
540*00b67f09SDavid van Moolenbroek 		sname.sun_family = AF_UNIX;
541*00b67f09SDavid van Moolenbroek 		strlcpy(sname.sun_path, fname, sizeof(sname.sun_path));
542*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_HAVESALEN
543*00b67f09SDavid van Moolenbroek #if !defined(SUN_LEN)
544*00b67f09SDavid van Moolenbroek #define SUN_LEN(su) \
545*00b67f09SDavid van Moolenbroek 	(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
546*00b67f09SDavid van Moolenbroek #endif
547*00b67f09SDavid van Moolenbroek 		sname.sun_len = SUN_LEN(&sname);
548*00b67f09SDavid van Moolenbroek #endif
549*00b67f09SDavid van Moolenbroek 
550*00b67f09SDavid van Moolenbroek 		if (connect(fd, (struct sockaddr *) &sname,
551*00b67f09SDavid van Moolenbroek 			    sizeof(struct sockaddr_un)) < 0) {
552*00b67f09SDavid van Moolenbroek 			if (errno != EINPROGRESS) {
553*00b67f09SDavid van Moolenbroek 				ret = isc__errno2result(errno);
554*00b67f09SDavid van Moolenbroek 				goto closefd;
555*00b67f09SDavid van Moolenbroek 			}
556*00b67f09SDavid van Moolenbroek 		} else
557*00b67f09SDavid van Moolenbroek 			is_connected = ISC_TRUE;
558*00b67f09SDavid van Moolenbroek 	}
559*00b67f09SDavid van Moolenbroek 
560*00b67f09SDavid van Moolenbroek 	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
561*00b67f09SDavid van Moolenbroek 	if (source == NULL) {
562*00b67f09SDavid van Moolenbroek 		ret = ISC_R_NOMEMORY;
563*00b67f09SDavid van Moolenbroek 		goto closefd;
564*00b67f09SDavid van Moolenbroek 	}
565*00b67f09SDavid van Moolenbroek 
566*00b67f09SDavid van Moolenbroek 	/*
567*00b67f09SDavid van Moolenbroek 	 * From here down, no failures can occur.
568*00b67f09SDavid van Moolenbroek 	 */
569*00b67f09SDavid van Moolenbroek 	source->magic = SOURCE_MAGIC;
570*00b67f09SDavid van Moolenbroek 	source->ent = ent;
571*00b67f09SDavid van Moolenbroek 	source->total = 0;
572*00b67f09SDavid van Moolenbroek 	source->bad = ISC_FALSE;
573*00b67f09SDavid van Moolenbroek 	memset(source->name, 0, sizeof(source->name));
574*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(source, link);
575*00b67f09SDavid van Moolenbroek 	if (is_usocket) {
576*00b67f09SDavid van Moolenbroek 		source->sources.usocket.handle = fd;
577*00b67f09SDavid van Moolenbroek 		if (is_connected)
578*00b67f09SDavid van Moolenbroek 			source->sources.usocket.status =
579*00b67f09SDavid van Moolenbroek 					isc_usocketsource_connected;
580*00b67f09SDavid van Moolenbroek 		else
581*00b67f09SDavid van Moolenbroek 			source->sources.usocket.status =
582*00b67f09SDavid van Moolenbroek 					isc_usocketsource_connecting;
583*00b67f09SDavid van Moolenbroek 		source->sources.usocket.sz_to_recv = 0;
584*00b67f09SDavid van Moolenbroek 		source->type = ENTROPY_SOURCETYPE_USOCKET;
585*00b67f09SDavid van Moolenbroek 	} else {
586*00b67f09SDavid van Moolenbroek 		source->sources.file.handle = fd;
587*00b67f09SDavid van Moolenbroek 		source->type = ENTROPY_SOURCETYPE_FILE;
588*00b67f09SDavid van Moolenbroek 	}
589*00b67f09SDavid van Moolenbroek 
590*00b67f09SDavid van Moolenbroek 	/*
591*00b67f09SDavid van Moolenbroek 	 * Hook it into the entropy system.
592*00b67f09SDavid van Moolenbroek 	 */
593*00b67f09SDavid van Moolenbroek 	ISC_LIST_APPEND(ent->sources, source, link);
594*00b67f09SDavid van Moolenbroek 	ent->nsources++;
595*00b67f09SDavid van Moolenbroek 
596*00b67f09SDavid van Moolenbroek 	UNLOCK(&ent->lock);
597*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
598*00b67f09SDavid van Moolenbroek 
599*00b67f09SDavid van Moolenbroek  closefd:
600*00b67f09SDavid van Moolenbroek 	(void)close(fd);
601*00b67f09SDavid van Moolenbroek 
602*00b67f09SDavid van Moolenbroek  errout:
603*00b67f09SDavid van Moolenbroek 	UNLOCK(&ent->lock);
604*00b67f09SDavid van Moolenbroek 
605*00b67f09SDavid van Moolenbroek 	return (ret);
606*00b67f09SDavid van Moolenbroek }
607