]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/lib/isc/win32/entropy.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / ntp / lib / isc / win32 / entropy.c
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
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
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
230 destroyfilesource(isc_entropyfilesource_t *source) {
231         CryptReleaseContext(source->handle, 0);
232 }
233
234 static void
235 destroyusocketsource(isc_entropyusocketsource_t *source) {
236         UNUSED(source);
237 }
238
239
240 isc_result_t
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