entropy.c revision 290001
1/*
2 * Copyright (C) 2004, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: entropy.c,v 1.10 2009/01/18 23:48:14 tbox Exp $ */
19
20/*
21 * This is the system dependent part of the ISC entropy API.
22 */
23
24#include <config.h>
25
26#include <windows.h>
27#include <wincrypt.h>
28
29#include <process.h>
30#include <io.h>
31#include <share.h>
32
33/*
34 * There is only one variable in the entropy data structures that is not
35 * system independent, but pulling the structure that uses it into this file
36 * ultimately means pulling several other independent structures here also to
37 * resolve their interdependencies.  Thus only the problem variable's type
38 * is defined here.
39 */
40#define FILESOURCE_HANDLE_TYPE	HCRYPTPROV
41
42typedef struct {
43	int dummy;
44} isc_entropyusocketsource_t;
45
46#include "../entropy.c"
47
48static unsigned int
49get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
50	isc_entropy_t *ent = source->ent;
51	unsigned char buf[128];
52	HCRYPTPROV hcryptprov = source->sources.file.handle;
53	ssize_t ndesired;
54	unsigned int added;
55
56	if (source->bad)
57		return (0);
58
59	desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
60
61	added = 0;
62	while (desired > 0) {
63		ndesired = ISC_MIN(desired, sizeof(buf));
64		if (!CryptGenRandom(hcryptprov, ndesired, buf)) {
65			CryptReleaseContext(hcryptprov, 0);
66			source->bad = ISC_TRUE;
67			goto out;
68		}
69
70		entropypool_adddata(ent, buf, ndesired, ndesired * 8);
71		added += ndesired * 8;
72		desired -= ndesired;
73	}
74
75 out:
76	return (added);
77}
78
79/*
80 * Poll each source, trying to get data from it to stuff into the entropy
81 * pool.
82 */
83static void
84fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
85	unsigned int added;
86	unsigned int remaining;
87	unsigned int needed;
88	unsigned int nsource;
89	isc_entropysource_t *source;
90	isc_entropysource_t *firstsource;
91
92	REQUIRE(VALID_ENTROPY(ent));
93
94	needed = desired;
95
96	/*
97	 * This logic is a little strange, so an explanation is in order.
98	 *
99	 * If needed is 0, it means we are being asked to "fill to whatever
100	 * we think is best."  This means that if we have at least a
101	 * partially full pool (say, > 1/4th of the pool) we probably don't
102	 * need to add anything.
103	 *
104	 * Also, we will check to see if the "pseudo" count is too high.
105	 * If it is, try to mix in better data.  Too high is currently
106	 * defined as 1/4th of the pool.
107	 *
108	 * Next, if we are asked to add a specific bit of entropy, make
109	 * certain that we will do so.  Clamp how much we try to add to
110	 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
111	 *
112	 * Note that if we are in a blocking mode, we will only try to
113	 * get as much data as we need, not as much as we might want
114	 * to build up.
115	 */
116	if (needed == 0) {
117		REQUIRE(!blocking);
118
119		if ((ent->pool.entropy >= RND_POOLBITS / 4)
120		    && (ent->pool.pseudo <= RND_POOLBITS / 4))
121			return;
122
123		needed = THRESHOLD_BITS * 4;
124	} else {
125		needed = ISC_MAX(needed, THRESHOLD_BITS);
126		needed = ISC_MIN(needed, RND_POOLBITS);
127	}
128
129	/*
130	 * In any case, clamp how much we need to how much we can add.
131	 */
132	needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
133
134	/*
135	 * But wait!  If we're not yet initialized, we need at least
136	 *	THRESHOLD_BITS
137	 * of randomness.
138	 */
139	if (ent->initialized < THRESHOLD_BITS)
140		needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
141
142	/*
143	 * Poll each file source to see if we can read anything useful from
144	 * it.  XXXMLG When where are multiple sources, we should keep a
145	 * record of which one we last used so we can start from it (or the
146	 * next one) to avoid letting some sources build up entropy while
147	 * others are always drained.
148	 */
149
150	added = 0;
151	remaining = needed;
152	if (ent->nextsource == NULL) {
153		ent->nextsource = ISC_LIST_HEAD(ent->sources);
154		if (ent->nextsource == NULL)
155			return;
156	}
157	source = ent->nextsource;
158	/*
159	 * Remember the first source so we can break if we have looped back to
160	 * the beginning and still have nothing
161	 */
162	firstsource = source;
163 again_file:
164	for (nsource = 0; nsource < ent->nsources; nsource++) {
165		unsigned int got;
166
167		if (remaining == 0)
168			break;
169
170		got = 0;
171
172		if (source->type == ENTROPY_SOURCETYPE_FILE)
173			got = get_from_filesource(source, remaining);
174
175		added += got;
176
177		remaining -= ISC_MIN(remaining, got);
178
179		source = ISC_LIST_NEXT(source, link);
180		if (source == NULL)
181			source = ISC_LIST_HEAD(ent->sources);
182	}
183	ent->nextsource = source;
184
185	/*
186	 * Go again only if there's been progress and we've not
187	 * gone back to the beginning
188	 */
189	if (!(ent->nextsource == firstsource && added == 0)) {
190		if (blocking && remaining != 0) {
191				goto again_file;
192		}
193	}
194
195	/*
196	 * Here, if there are bits remaining to be had and we can block,
197	 * check to see if we have a callback source.  If so, call them.
198	 */
199	source = ISC_LIST_HEAD(ent->sources);
200	while ((remaining != 0) && (source != NULL)) {
201		unsigned int got;
202
203		got = 0;
204
205		if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
206			got = get_from_callback(source, remaining, blocking);
207
208		added += got;
209		remaining -= ISC_MIN(remaining, got);
210
211		if (added >= needed)
212			break;
213
214		source = ISC_LIST_NEXT(source, link);
215	}
216
217	/*
218	 * Mark as initialized if we've added enough data.
219	 */
220	if (ent->initialized < THRESHOLD_BITS)
221		ent->initialized += added;
222}
223
224
225
226/*
227 * Requires "ent" be locked.
228 */
229static void
230destroyfilesource(isc_entropyfilesource_t *source) {
231	CryptReleaseContext(source->handle, 0);
232}
233
234static void
235destroyusocketsource(isc_entropyusocketsource_t *source) {
236	UNUSED(source);
237}
238
239
240isc_result_t
241isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
242	isc_result_t ret;
243	isc_entropysource_t *source;
244	HCRYPTPROV hcryptprov;
245	DWORD errval;
246	BOOL err;
247
248	REQUIRE(VALID_ENTROPY(ent));
249	REQUIRE(fname != NULL);
250
251	LOCK(&ent->lock);
252
253	source = NULL;
254
255	/*
256	 * The first time we just try to acquire the context
257	 */
258	err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL,
259				  CRYPT_VERIFYCONTEXT);
260	if (!err){
261		errval = GetLastError();
262		ret = ISC_R_IOERROR;
263		goto errout;
264	}
265
266	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
267	if (source == NULL) {
268		ret = ISC_R_NOMEMORY;
269		goto closecontext;
270	}
271
272	/*
273	 * From here down, no failures can occur.
274	 */
275	source->magic = SOURCE_MAGIC;
276	source->type = ENTROPY_SOURCETYPE_FILE;
277	source->ent = ent;
278	source->total = 0;
279	source->bad = ISC_FALSE;
280	memset(source->name, 0, sizeof(source->name));
281	ISC_LINK_INIT(source, link);
282	source->sources.file.handle = hcryptprov;
283
284	/*
285	 * Hook it into the entropy system.
286	 */
287	ISC_LIST_APPEND(ent->sources, source, link);
288	ent->nsources++;
289
290	UNLOCK(&ent->lock);
291	return (ISC_R_SUCCESS);
292
293 closecontext:
294	CryptReleaseContext(hcryptprov, 0);
295
296 errout:
297	if (source != NULL)
298		isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
299
300	UNLOCK(&ent->lock);
301
302	return (ret);
303}
304
305
306
307
308