2000-06-03 02:15:08 +00:00
|
|
|
/*
|
2001-01-09 22:01:04 +00:00
|
|
|
* Copyright (C) 2000, 2001 Internet Software Consortium.
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2000-06-03 02:15:08 +00:00
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2000-07-27 09:55:03 +00:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
|
|
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
|
|
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
|
|
|
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
|
|
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
2000-06-03 02:15:08 +00:00
|
|
|
*/
|
|
|
|
|
2001-07-18 01:31:13 +00:00
|
|
|
/* $Id: entropy.c,v 1.60 2001/07/18 01:31:13 gson Exp $ */
|
2001-06-21 14:19:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the system depenedent part of the ISC entropy API.
|
|
|
|
*/
|
2000-06-22 22:00:42 +00:00
|
|
|
|
2000-06-03 02:15:08 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
2000-12-14 22:40:17 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
#include <unistd.h>
|
2000-06-03 02:15:08 +00:00
|
|
|
|
2001-01-23 03:07:18 +00:00
|
|
|
#include <isc/platform.h>
|
2000-06-03 02:15:08 +00:00
|
|
|
|
2001-01-23 03:07:18 +00:00
|
|
|
#ifdef ISC_PLATFORM_NEEDSYSSELECTH
|
|
|
|
#include <sys/select.h>
|
|
|
|
#endif
|
|
|
|
|
2001-07-18 01:31:13 +00:00
|
|
|
#include "errno2result.h"
|
|
|
|
|
2000-06-03 02:15:08 +00:00
|
|
|
/*
|
2001-06-21 14:19:20 +00:00
|
|
|
* There is only one variable in the entropy data structures that is not
|
|
|
|
* system independent, but pulling the structure that uses it into this file
|
|
|
|
* ultimately means pulling several other independent structures here also to
|
|
|
|
* resolve their interdependencies. Thus only the problem variable's type
|
|
|
|
* is defined here.
|
2000-06-08 06:35:49 +00:00
|
|
|
*/
|
2001-06-21 14:19:20 +00:00
|
|
|
#define FILESOURCE_HANDLE_TYPE int
|
2000-06-21 22:12:23 +00:00
|
|
|
|
2001-06-21 14:19:20 +00:00
|
|
|
#include "../entropy.c"
|
2000-06-21 22:12:23 +00:00
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
static unsigned int
|
2000-06-08 23:42:17 +00:00
|
|
|
get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
|
2000-06-09 18:22:11 +00:00
|
|
|
isc_entropy_t *ent = source->ent;
|
2000-06-08 23:42:17 +00:00
|
|
|
unsigned char buf[128];
|
2001-06-21 14:19:20 +00:00
|
|
|
int fd = source->sources.file.handle;
|
2000-06-08 23:42:17 +00:00
|
|
|
ssize_t n, ndesired;
|
2000-06-20 19:01:13 +00:00
|
|
|
unsigned int added;
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-11-23 01:04:00 +00:00
|
|
|
if (source->bad)
|
2000-06-08 23:42:17 +00:00
|
|
|
return (0);
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-09 17:48:08 +00:00
|
|
|
added = 0;
|
2000-06-08 23:42:17 +00:00
|
|
|
while (desired > 0) {
|
|
|
|
ndesired = ISC_MIN(desired, sizeof(buf));
|
|
|
|
n = read(fd, buf, ndesired);
|
|
|
|
if (n < 0) {
|
2001-04-04 20:57:35 +00:00
|
|
|
if (errno == EAGAIN || errno == EINTR)
|
2000-06-08 23:42:17 +00:00
|
|
|
goto out;
|
|
|
|
close(fd);
|
2000-11-23 01:04:00 +00:00
|
|
|
source->bad = ISC_TRUE;
|
2000-06-09 21:36:51 +00:00
|
|
|
goto out;
|
2000-06-08 23:42:17 +00:00
|
|
|
}
|
2000-06-09 21:36:51 +00:00
|
|
|
if (n == 0) {
|
|
|
|
close(fd);
|
2000-11-23 01:04:00 +00:00
|
|
|
source->bad = ISC_TRUE;
|
2000-06-08 23:42:17 +00:00
|
|
|
goto out;
|
2000-06-09 21:36:51 +00:00
|
|
|
}
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-09 18:22:11 +00:00
|
|
|
entropypool_adddata(ent, buf, n, n * 8);
|
2000-06-08 23:42:17 +00:00
|
|
|
added += n * 8;
|
2000-06-09 02:00:22 +00:00
|
|
|
desired -= n;
|
2000-06-08 23:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return (added);
|
|
|
|
}
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
/*
|
|
|
|
* Poll each source, trying to get data from it to stuff into the entropy
|
|
|
|
* pool.
|
|
|
|
*/
|
|
|
|
static void
|
2000-06-20 19:01:13 +00:00
|
|
|
fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
|
|
|
|
unsigned int added;
|
|
|
|
unsigned int remaining;
|
|
|
|
unsigned int needed;
|
2000-06-15 18:15:26 +00:00
|
|
|
unsigned int nsource;
|
2000-06-08 23:42:17 +00:00
|
|
|
isc_entropysource_t *source;
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-20 19:01:13 +00:00
|
|
|
needed = desired;
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
/*
|
2000-06-09 17:48:08 +00:00
|
|
|
* This logic is a little strange, so an explanation is in order.
|
|
|
|
*
|
|
|
|
* If needed is 0, it means we are being asked to "fill to whatever
|
|
|
|
* we think is best." This means that if we have at least a
|
|
|
|
* partially full pool (say, > 1/4th of the pool) we probably don't
|
|
|
|
* need to add anything.
|
|
|
|
*
|
|
|
|
* Also, we will check to see if the "pseudo" count is too high.
|
|
|
|
* If it is, try to mix in better data. Too high is currently
|
|
|
|
* defined as 1/4th of the pool.
|
|
|
|
*
|
|
|
|
* Next, if we are asked to add a specific bit of entropy, make
|
|
|
|
* certain that we will do so. Clamp how much we try to add to
|
|
|
|
* (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
|
2000-06-09 21:36:51 +00:00
|
|
|
*
|
|
|
|
* Note that if we are in a blocking mode, we will only try to
|
|
|
|
* get as much data as we need, not as much as we might want
|
|
|
|
* to build up.
|
2000-06-09 02:00:22 +00:00
|
|
|
*/
|
2000-06-09 17:48:08 +00:00
|
|
|
if (needed == 0) {
|
|
|
|
REQUIRE(!blocking);
|
|
|
|
|
2000-06-09 02:00:22 +00:00
|
|
|
if ((ent->pool.entropy >= RND_POOLBITS / 4)
|
|
|
|
&& (ent->pool.pseudo <= RND_POOLBITS / 4))
|
|
|
|
return;
|
2000-06-21 00:04:57 +00:00
|
|
|
|
|
|
|
needed = THRESHOLD_BITS * 4;
|
2000-06-09 17:48:08 +00:00
|
|
|
} else {
|
2000-06-12 22:28:05 +00:00
|
|
|
needed = ISC_MAX(needed, THRESHOLD_BITS);
|
2000-06-09 17:48:08 +00:00
|
|
|
needed = ISC_MIN(needed, RND_POOLBITS);
|
2000-06-09 02:00:22 +00:00
|
|
|
}
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-09 21:36:51 +00:00
|
|
|
/*
|
|
|
|
* In any case, clamp how much we need to how much we can add.
|
|
|
|
*/
|
|
|
|
needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
|
|
|
|
|
2000-06-12 18:19:43 +00:00
|
|
|
/*
|
2000-06-12 22:28:05 +00:00
|
|
|
* But wait! If we're not yet initialized, we need at least
|
|
|
|
* THRESHOLD_BITS
|
2000-06-15 18:15:26 +00:00
|
|
|
* of randomness.
|
2000-06-12 18:19:43 +00:00
|
|
|
*/
|
2000-06-12 22:28:05 +00:00
|
|
|
if (ent->initialized < THRESHOLD_BITS)
|
|
|
|
needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
|
2000-06-12 18:19:43 +00:00
|
|
|
|
2000-06-08 23:42:17 +00:00
|
|
|
/*
|
|
|
|
* Poll each file source to see if we can read anything useful from
|
|
|
|
* it. XXXMLG When where are multiple sources, we should keep a
|
|
|
|
* record of which one we last used so we can start from it (or the
|
|
|
|
* next one) to avoid letting some sources build up entropy while
|
|
|
|
* others are always drained.
|
|
|
|
*/
|
|
|
|
|
2000-06-10 01:09:22 +00:00
|
|
|
added = 0;
|
2000-06-10 01:47:06 +00:00
|
|
|
remaining = needed;
|
2000-06-15 18:15:26 +00:00
|
|
|
if (ent->nextsource == NULL) {
|
|
|
|
ent->nextsource = ISC_LIST_HEAD(ent->sources);
|
|
|
|
if (ent->nextsource == NULL)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
source = ent->nextsource;
|
2000-06-20 19:01:13 +00:00
|
|
|
again_file:
|
2000-06-15 18:15:26 +00:00
|
|
|
for (nsource = 0 ; nsource < ent->nsources ; nsource++) {
|
2000-06-20 19:01:13 +00:00
|
|
|
unsigned int got;
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-15 18:15:26 +00:00
|
|
|
if (remaining == 0)
|
|
|
|
break;
|
|
|
|
|
2000-06-10 01:47:06 +00:00
|
|
|
got = 0;
|
|
|
|
|
|
|
|
if (source->type == ENTROPY_SOURCETYPE_FILE)
|
|
|
|
got = get_from_filesource(source, remaining);
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-10 01:47:06 +00:00
|
|
|
added += got;
|
|
|
|
|
2000-06-21 00:04:57 +00:00
|
|
|
remaining -= ISC_MIN(remaining, got);
|
2000-06-10 01:47:06 +00:00
|
|
|
|
2000-06-15 18:15:26 +00:00
|
|
|
source = ISC_LIST_NEXT(source, link);
|
|
|
|
if (source == NULL)
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
2000-06-08 23:42:17 +00:00
|
|
|
}
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->nextsource = source;
|
2000-06-08 23:42:17 +00:00
|
|
|
|
2000-06-10 01:47:06 +00:00
|
|
|
if (blocking && remaining != 0) {
|
2000-06-10 01:09:22 +00:00
|
|
|
int fds;
|
2001-06-21 14:19:20 +00:00
|
|
|
|
2000-06-10 01:09:22 +00:00
|
|
|
fds = wait_for_sources(ent);
|
|
|
|
if (fds > 0)
|
2000-06-20 19:01:13 +00:00
|
|
|
goto again_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here, if there are bits remaining to be had and we can block,
|
|
|
|
* check to see if we have a callback source. If so, call them.
|
|
|
|
*/
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
2000-06-21 00:04:57 +00:00
|
|
|
while ((remaining != 0) && (source != NULL)) {
|
2000-06-20 19:01:13 +00:00
|
|
|
unsigned int got;
|
|
|
|
|
|
|
|
got = 0;
|
|
|
|
|
|
|
|
if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
|
2000-06-21 00:04:57 +00:00
|
|
|
got = get_from_callback(source, remaining, blocking);
|
2000-06-20 19:01:13 +00:00
|
|
|
|
|
|
|
added += got;
|
2000-06-21 00:04:57 +00:00
|
|
|
remaining -= ISC_MIN(remaining, got);
|
2000-06-20 19:01:13 +00:00
|
|
|
|
|
|
|
if (added >= needed)
|
|
|
|
break;
|
|
|
|
|
|
|
|
source = ISC_LIST_NEXT(source, link);
|
2000-06-09 21:36:51 +00:00
|
|
|
}
|
|
|
|
|
2000-06-12 18:19:43 +00:00
|
|
|
/*
|
|
|
|
* Mark as initialized if we've added enough data.
|
|
|
|
*/
|
2000-06-12 22:28:05 +00:00
|
|
|
if (ent->initialized < THRESHOLD_BITS)
|
2000-06-12 18:19:43 +00:00
|
|
|
ent->initialized += added;
|
2000-06-08 22:18:53 +00:00
|
|
|
}
|
|
|
|
|
2000-06-09 21:36:51 +00:00
|
|
|
static int
|
|
|
|
wait_for_sources(isc_entropy_t *ent) {
|
|
|
|
isc_entropysource_t *source;
|
|
|
|
int maxfd, fd;
|
|
|
|
int cc;
|
|
|
|
fd_set reads;
|
|
|
|
|
|
|
|
maxfd = -1;
|
|
|
|
FD_ZERO(&reads);
|
|
|
|
|
|
|
|
source = ISC_LIST_HEAD(ent->sources);
|
|
|
|
while (source != NULL) {
|
|
|
|
if (source->type == ENTROPY_SOURCETYPE_FILE) {
|
2001-06-21 14:19:20 +00:00
|
|
|
fd = source->sources.file.handle;
|
2000-06-09 21:36:51 +00:00
|
|
|
if (fd >= 0) {
|
|
|
|
maxfd = ISC_MAX(maxfd, fd);
|
|
|
|
FD_SET(fd, &reads);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
source = ISC_LIST_NEXT(source, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (maxfd < 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
cc = select(maxfd + 1, &reads, NULL, NULL, NULL);
|
|
|
|
if (cc < 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
return (cc);
|
|
|
|
}
|
|
|
|
|
2000-06-09 19:18:56 +00:00
|
|
|
static void
|
2001-06-21 14:19:20 +00:00
|
|
|
destroyfilesource(isc_entropyfilesource_t *source) {
|
|
|
|
close(source->handle);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
/*
|
|
|
|
* Make a fd non-blocking
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
|
|
|
make_nonblock(int fd) {
|
|
|
|
int ret;
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
flags = fcntl(fd, F_GETFL, 0);
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
ret = fcntl(fd, F_SETFL, flags);
|
|
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
|
|
"fcntl(%d, F_SETFL, %d): %s",
|
|
|
|
fd, flags, strerror(errno));
|
|
|
|
|
|
|
|
return (ISC_R_UNEXPECTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2000-06-07 22:15:55 +00:00
|
|
|
isc_result_t
|
2000-06-17 01:42:21 +00:00
|
|
|
isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
|
2000-06-08 22:18:53 +00:00
|
|
|
int fd;
|
|
|
|
isc_result_t ret;
|
|
|
|
isc_entropysource_t *source;
|
2000-06-08 06:35:49 +00:00
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
REQUIRE(VALID_ENTROPY(ent));
|
|
|
|
REQUIRE(fname != NULL);
|
|
|
|
|
|
|
|
LOCK(&ent->lock);
|
|
|
|
|
|
|
|
source = NULL;
|
|
|
|
|
|
|
|
fd = open(fname, O_RDONLY | O_NONBLOCK, 0);
|
|
|
|
if (fd < 0) {
|
2001-07-18 01:31:13 +00:00
|
|
|
ret = isc__errno2result(errno);
|
2000-06-08 22:18:53 +00:00
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
ret = make_nonblock(fd);
|
|
|
|
if (ret != ISC_R_SUCCESS)
|
2000-11-23 01:04:00 +00:00
|
|
|
goto closefd;
|
2000-06-08 22:18:53 +00:00
|
|
|
|
|
|
|
source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
|
|
|
|
if (source == NULL) {
|
|
|
|
ret = ISC_R_NOMEMORY;
|
2000-11-23 01:04:00 +00:00
|
|
|
goto closefd;
|
2000-06-08 22:18:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From here down, no failures can occur.
|
|
|
|
*/
|
2000-06-09 17:25:41 +00:00
|
|
|
source->magic = SOURCE_MAGIC;
|
2000-06-08 22:18:53 +00:00
|
|
|
source->type = ENTROPY_SOURCETYPE_FILE;
|
|
|
|
source->ent = ent;
|
|
|
|
source->total = 0;
|
2000-11-23 01:04:00 +00:00
|
|
|
source->bad = ISC_FALSE;
|
2000-06-08 22:18:53 +00:00
|
|
|
memset(source->name, 0, sizeof(source->name));
|
|
|
|
ISC_LINK_INIT(source, link);
|
2001-06-21 14:19:20 +00:00
|
|
|
source->sources.file.handle = fd;
|
2000-06-08 22:18:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Hook it into the entropy system.
|
|
|
|
*/
|
|
|
|
ISC_LIST_APPEND(ent->sources, source, link);
|
2000-06-15 18:15:26 +00:00
|
|
|
ent->nsources++;
|
2000-06-08 22:18:53 +00:00
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
2000-11-23 01:04:00 +00:00
|
|
|
closefd:
|
|
|
|
close(fd);
|
|
|
|
|
2000-06-08 22:18:53 +00:00
|
|
|
errout:
|
|
|
|
if (source != NULL)
|
|
|
|
isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
|
|
|
|
|
|
|
|
UNLOCK(&ent->lock);
|
|
|
|
|
|
|
|
return (ret);
|
2000-06-07 22:15:55 +00:00
|
|
|
}
|