xref: /freebsd-11-stable/contrib/ntp/lib/isc/win32/entropy.c (revision 416ba5c74546f32a993436a99516d35008e9f384)
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 
42 typedef struct {
43 	int dummy;
44 } isc_entropyusocketsource_t;
45 
46 #include "../entropy.c"
47 
48 static unsigned int
get_from_filesource(isc_entropysource_t * source,isc_uint32_t desired)49 get_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  */
83 static void
fillpool(isc_entropy_t * ent,unsigned int desired,isc_boolean_t blocking)84 fillpool(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  */
229 static void
destroyfilesource(isc_entropyfilesource_t * source)230 destroyfilesource(isc_entropyfilesource_t *source) {
231 	CryptReleaseContext(source->handle, 0);
232 }
233 
234 static void
destroyusocketsource(isc_entropyusocketsource_t * source)235 destroyusocketsource(isc_entropyusocketsource_t *source) {
236 	UNUSED(source);
237 }
238 
239 
240 isc_result_t
isc_entropy_createfilesource(isc_entropy_t * ent,const char * fname)241 isc_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