1258945Sroberto/* 2258945Sroberto * Copyright (C) 2004, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * Copyright (C) 2000-2002 Internet Software Consortium. 4258945Sroberto * 5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 6258945Sroberto * purpose with or without fee is hereby granted, provided that the above 7258945Sroberto * copyright notice and this permission notice appear in all copies. 8258945Sroberto * 9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 16258945Sroberto */ 17258945Sroberto 18280849Scy/* $Id: entropy.c,v 1.10 2009/01/18 23:48:14 tbox Exp $ */ 19258945Sroberto 20258945Sroberto/* 21258945Sroberto * This is the system dependent part of the ISC entropy API. 22258945Sroberto */ 23258945Sroberto 24258945Sroberto#include <config.h> 25258945Sroberto 26258945Sroberto#include <windows.h> 27258945Sroberto#include <wincrypt.h> 28258945Sroberto 29258945Sroberto#include <process.h> 30258945Sroberto#include <io.h> 31258945Sroberto#include <share.h> 32258945Sroberto 33258945Sroberto/* 34258945Sroberto * There is only one variable in the entropy data structures that is not 35258945Sroberto * system independent, but pulling the structure that uses it into this file 36258945Sroberto * ultimately means pulling several other independent structures here also to 37258945Sroberto * resolve their interdependencies. Thus only the problem variable's type 38258945Sroberto * is defined here. 39258945Sroberto */ 40258945Sroberto#define FILESOURCE_HANDLE_TYPE HCRYPTPROV 41258945Sroberto 42258945Srobertotypedef struct { 43258945Sroberto int dummy; 44258945Sroberto} isc_entropyusocketsource_t; 45258945Sroberto 46258945Sroberto#include "../entropy.c" 47258945Sroberto 48258945Srobertostatic unsigned int 49258945Srobertoget_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) { 50258945Sroberto isc_entropy_t *ent = source->ent; 51258945Sroberto unsigned char buf[128]; 52258945Sroberto HCRYPTPROV hcryptprov = source->sources.file.handle; 53258945Sroberto ssize_t ndesired; 54258945Sroberto unsigned int added; 55258945Sroberto 56258945Sroberto if (source->bad) 57258945Sroberto return (0); 58258945Sroberto 59258945Sroberto desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0); 60258945Sroberto 61258945Sroberto added = 0; 62258945Sroberto while (desired > 0) { 63258945Sroberto ndesired = ISC_MIN(desired, sizeof(buf)); 64258945Sroberto if (!CryptGenRandom(hcryptprov, ndesired, buf)) { 65258945Sroberto CryptReleaseContext(hcryptprov, 0); 66258945Sroberto source->bad = ISC_TRUE; 67258945Sroberto goto out; 68258945Sroberto } 69258945Sroberto 70258945Sroberto entropypool_adddata(ent, buf, ndesired, ndesired * 8); 71258945Sroberto added += ndesired * 8; 72258945Sroberto desired -= ndesired; 73258945Sroberto } 74258945Sroberto 75258945Sroberto out: 76258945Sroberto return (added); 77258945Sroberto} 78258945Sroberto 79258945Sroberto/* 80258945Sroberto * Poll each source, trying to get data from it to stuff into the entropy 81258945Sroberto * pool. 82258945Sroberto */ 83258945Srobertostatic void 84258945Srobertofillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) { 85258945Sroberto unsigned int added; 86258945Sroberto unsigned int remaining; 87258945Sroberto unsigned int needed; 88258945Sroberto unsigned int nsource; 89258945Sroberto isc_entropysource_t *source; 90258945Sroberto isc_entropysource_t *firstsource; 91258945Sroberto 92258945Sroberto REQUIRE(VALID_ENTROPY(ent)); 93258945Sroberto 94258945Sroberto needed = desired; 95258945Sroberto 96258945Sroberto /* 97258945Sroberto * This logic is a little strange, so an explanation is in order. 98258945Sroberto * 99258945Sroberto * If needed is 0, it means we are being asked to "fill to whatever 100258945Sroberto * we think is best." This means that if we have at least a 101258945Sroberto * partially full pool (say, > 1/4th of the pool) we probably don't 102258945Sroberto * need to add anything. 103258945Sroberto * 104258945Sroberto * Also, we will check to see if the "pseudo" count is too high. 105258945Sroberto * If it is, try to mix in better data. Too high is currently 106258945Sroberto * defined as 1/4th of the pool. 107258945Sroberto * 108258945Sroberto * Next, if we are asked to add a specific bit of entropy, make 109258945Sroberto * certain that we will do so. Clamp how much we try to add to 110258945Sroberto * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy). 111258945Sroberto * 112258945Sroberto * Note that if we are in a blocking mode, we will only try to 113258945Sroberto * get as much data as we need, not as much as we might want 114258945Sroberto * to build up. 115258945Sroberto */ 116258945Sroberto if (needed == 0) { 117258945Sroberto REQUIRE(!blocking); 118258945Sroberto 119258945Sroberto if ((ent->pool.entropy >= RND_POOLBITS / 4) 120258945Sroberto && (ent->pool.pseudo <= RND_POOLBITS / 4)) 121258945Sroberto return; 122258945Sroberto 123258945Sroberto needed = THRESHOLD_BITS * 4; 124258945Sroberto } else { 125258945Sroberto needed = ISC_MAX(needed, THRESHOLD_BITS); 126258945Sroberto needed = ISC_MIN(needed, RND_POOLBITS); 127258945Sroberto } 128258945Sroberto 129258945Sroberto /* 130258945Sroberto * In any case, clamp how much we need to how much we can add. 131258945Sroberto */ 132258945Sroberto needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy); 133258945Sroberto 134258945Sroberto /* 135258945Sroberto * But wait! If we're not yet initialized, we need at least 136258945Sroberto * THRESHOLD_BITS 137258945Sroberto * of randomness. 138258945Sroberto */ 139258945Sroberto if (ent->initialized < THRESHOLD_BITS) 140258945Sroberto needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized); 141258945Sroberto 142258945Sroberto /* 143258945Sroberto * Poll each file source to see if we can read anything useful from 144258945Sroberto * it. XXXMLG When where are multiple sources, we should keep a 145258945Sroberto * record of which one we last used so we can start from it (or the 146258945Sroberto * next one) to avoid letting some sources build up entropy while 147258945Sroberto * others are always drained. 148258945Sroberto */ 149258945Sroberto 150258945Sroberto added = 0; 151258945Sroberto remaining = needed; 152258945Sroberto if (ent->nextsource == NULL) { 153258945Sroberto ent->nextsource = ISC_LIST_HEAD(ent->sources); 154258945Sroberto if (ent->nextsource == NULL) 155258945Sroberto return; 156258945Sroberto } 157258945Sroberto source = ent->nextsource; 158258945Sroberto /* 159258945Sroberto * Remember the first source so we can break if we have looped back to 160258945Sroberto * the beginning and still have nothing 161258945Sroberto */ 162258945Sroberto firstsource = source; 163258945Sroberto again_file: 164258945Sroberto for (nsource = 0; nsource < ent->nsources; nsource++) { 165258945Sroberto unsigned int got; 166258945Sroberto 167258945Sroberto if (remaining == 0) 168258945Sroberto break; 169258945Sroberto 170258945Sroberto got = 0; 171258945Sroberto 172258945Sroberto if (source->type == ENTROPY_SOURCETYPE_FILE) 173258945Sroberto got = get_from_filesource(source, remaining); 174258945Sroberto 175258945Sroberto added += got; 176258945Sroberto 177258945Sroberto remaining -= ISC_MIN(remaining, got); 178258945Sroberto 179258945Sroberto source = ISC_LIST_NEXT(source, link); 180258945Sroberto if (source == NULL) 181258945Sroberto source = ISC_LIST_HEAD(ent->sources); 182258945Sroberto } 183258945Sroberto ent->nextsource = source; 184258945Sroberto 185258945Sroberto /* 186258945Sroberto * Go again only if there's been progress and we've not 187258945Sroberto * gone back to the beginning 188258945Sroberto */ 189258945Sroberto if (!(ent->nextsource == firstsource && added == 0)) { 190258945Sroberto if (blocking && remaining != 0) { 191258945Sroberto goto again_file; 192258945Sroberto } 193258945Sroberto } 194258945Sroberto 195258945Sroberto /* 196258945Sroberto * Here, if there are bits remaining to be had and we can block, 197258945Sroberto * check to see if we have a callback source. If so, call them. 198258945Sroberto */ 199258945Sroberto source = ISC_LIST_HEAD(ent->sources); 200258945Sroberto while ((remaining != 0) && (source != NULL)) { 201258945Sroberto unsigned int got; 202258945Sroberto 203258945Sroberto got = 0; 204258945Sroberto 205258945Sroberto if (source->type == ENTROPY_SOURCETYPE_CALLBACK) 206258945Sroberto got = get_from_callback(source, remaining, blocking); 207258945Sroberto 208258945Sroberto added += got; 209258945Sroberto remaining -= ISC_MIN(remaining, got); 210258945Sroberto 211258945Sroberto if (added >= needed) 212258945Sroberto break; 213258945Sroberto 214258945Sroberto source = ISC_LIST_NEXT(source, link); 215258945Sroberto } 216258945Sroberto 217258945Sroberto /* 218258945Sroberto * Mark as initialized if we've added enough data. 219258945Sroberto */ 220258945Sroberto if (ent->initialized < THRESHOLD_BITS) 221258945Sroberto ent->initialized += added; 222258945Sroberto} 223258945Sroberto 224258945Sroberto 225258945Sroberto 226258945Sroberto/* 227258945Sroberto * Requires "ent" be locked. 228258945Sroberto */ 229258945Srobertostatic void 230258945Srobertodestroyfilesource(isc_entropyfilesource_t *source) { 231258945Sroberto CryptReleaseContext(source->handle, 0); 232258945Sroberto} 233258945Sroberto 234258945Srobertostatic void 235258945Srobertodestroyusocketsource(isc_entropyusocketsource_t *source) { 236258945Sroberto UNUSED(source); 237258945Sroberto} 238258945Sroberto 239258945Sroberto 240258945Srobertoisc_result_t 241258945Srobertoisc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { 242258945Sroberto isc_result_t ret; 243258945Sroberto isc_entropysource_t *source; 244258945Sroberto HCRYPTPROV hcryptprov; 245258945Sroberto DWORD errval; 246258945Sroberto BOOL err; 247258945Sroberto 248258945Sroberto REQUIRE(VALID_ENTROPY(ent)); 249258945Sroberto REQUIRE(fname != NULL); 250258945Sroberto 251258945Sroberto LOCK(&ent->lock); 252258945Sroberto 253258945Sroberto source = NULL; 254258945Sroberto 255258945Sroberto /* 256258945Sroberto * The first time we just try to acquire the context 257258945Sroberto */ 258258945Sroberto err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL, 259258945Sroberto CRYPT_VERIFYCONTEXT); 260258945Sroberto if (!err){ 261258945Sroberto errval = GetLastError(); 262258945Sroberto ret = ISC_R_IOERROR; 263258945Sroberto goto errout; 264258945Sroberto } 265258945Sroberto 266258945Sroberto source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); 267258945Sroberto if (source == NULL) { 268258945Sroberto ret = ISC_R_NOMEMORY; 269258945Sroberto goto closecontext; 270258945Sroberto } 271258945Sroberto 272258945Sroberto /* 273258945Sroberto * From here down, no failures can occur. 274258945Sroberto */ 275258945Sroberto source->magic = SOURCE_MAGIC; 276258945Sroberto source->type = ENTROPY_SOURCETYPE_FILE; 277258945Sroberto source->ent = ent; 278258945Sroberto source->total = 0; 279258945Sroberto source->bad = ISC_FALSE; 280258945Sroberto memset(source->name, 0, sizeof(source->name)); 281258945Sroberto ISC_LINK_INIT(source, link); 282258945Sroberto source->sources.file.handle = hcryptprov; 283258945Sroberto 284258945Sroberto /* 285258945Sroberto * Hook it into the entropy system. 286258945Sroberto */ 287258945Sroberto ISC_LIST_APPEND(ent->sources, source, link); 288258945Sroberto ent->nsources++; 289258945Sroberto 290258945Sroberto UNLOCK(&ent->lock); 291258945Sroberto return (ISC_R_SUCCESS); 292258945Sroberto 293258945Sroberto closecontext: 294258945Sroberto CryptReleaseContext(hcryptprov, 0); 295258945Sroberto 296258945Sroberto errout: 297258945Sroberto if (source != NULL) 298258945Sroberto isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); 299258945Sroberto 300258945Sroberto UNLOCK(&ent->lock); 301258945Sroberto 302258945Sroberto return (ret); 303258945Sroberto} 304258945Sroberto 305258945Sroberto 306258945Sroberto 307258945Sroberto 308