|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
Packit Service |
ae04f2 |
*
|
|
Packit Service |
ae04f2 |
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
Packit Service |
ae04f2 |
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
Packit Service |
ae04f2 |
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
Packit Service |
ae04f2 |
*
|
|
Packit Service |
ae04f2 |
* See the COPYRIGHT file distributed with this work for additional
|
|
Packit Service |
ae04f2 |
* information regarding copyright ownership.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/* \file unix/entropy.c
|
|
Packit Service |
ae04f2 |
* \brief
|
|
Packit Service |
ae04f2 |
* This is the system dependent part of the ISC entropy API.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#include <config.h>
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#include <stdbool.h>
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
|
|
Packit Service |
ae04f2 |
#include <sys/types.h>
|
|
Packit Service |
ae04f2 |
#include <sys/time.h>
|
|
Packit Service |
ae04f2 |
#include <sys/stat.h>
|
|
Packit Service |
ae04f2 |
#include <sys/socket.h>
|
|
Packit Service |
ae04f2 |
#include <sys/un.h>
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#ifdef HAVE_NANOSLEEP
|
|
Packit Service |
ae04f2 |
#include <time.h>
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
#include <unistd.h>
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#include <isc/platform.h>
|
|
Packit Service |
ae04f2 |
#include <isc/print.h>
|
|
Packit Service |
ae04f2 |
#include <isc/strerror.h>
|
|
Packit Service |
ae04f2 |
#include <isc/string.h>
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#ifdef ISC_PLATFORM_NEEDSYSSELECTH
|
|
Packit Service |
ae04f2 |
#include <sys/select.h>
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#include "errno2result.h"
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*%
|
|
Packit Service |
ae04f2 |
* There is only one variable in the entropy data structures that is not
|
|
Packit Service |
ae04f2 |
* system independent, but pulling the structure that uses it into this file
|
|
Packit Service |
ae04f2 |
* ultimately means pulling several other independent structures here also to
|
|
Packit Service |
ae04f2 |
* resolve their interdependencies. Thus only the problem variable's type
|
|
Packit Service |
ae04f2 |
* is defined here.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
#define FILESOURCE_HANDLE_TYPE int
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
typedef struct {
|
|
Packit Service |
ae04f2 |
int handle;
|
|
Packit Service |
ae04f2 |
enum {
|
|
Packit Service |
ae04f2 |
isc_usocketsource_disconnected,
|
|
Packit Service |
ae04f2 |
isc_usocketsource_connecting,
|
|
Packit Service |
ae04f2 |
isc_usocketsource_connected,
|
|
Packit Service |
ae04f2 |
isc_usocketsource_ndesired,
|
|
Packit Service |
ae04f2 |
isc_usocketsource_wrote,
|
|
Packit Service |
ae04f2 |
isc_usocketsource_reading
|
|
Packit Service |
ae04f2 |
} status;
|
|
Packit Service |
ae04f2 |
size_t sz_to_recv;
|
|
Packit Service |
ae04f2 |
} isc_entropyusocketsource_t;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#include "../entropy.c"
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
static unsigned int
|
|
Packit Service |
ae04f2 |
get_from_filesource(isc_entropysource_t *source, uint32_t desired) {
|
|
Packit Service |
ae04f2 |
isc_entropy_t *ent = source->ent;
|
|
Packit Service |
ae04f2 |
unsigned char buf[128];
|
|
Packit Service |
ae04f2 |
int fd = source->sources.file.handle;
|
|
Packit Service |
ae04f2 |
ssize_t n, ndesired;
|
|
Packit Service |
ae04f2 |
unsigned int added;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (source->bad)
|
|
Packit Service |
ae04f2 |
return (0);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
added = 0;
|
|
Packit Service |
ae04f2 |
while (desired > 0) {
|
|
Packit Service |
ae04f2 |
ndesired = ISC_MIN(desired, sizeof(buf));
|
|
Packit Service |
ae04f2 |
n = read(fd, buf, ndesired);
|
|
Packit Service |
ae04f2 |
if (n < 0) {
|
|
Packit Service |
ae04f2 |
if (errno == EAGAIN || errno == EINTR)
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (n == 0)
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
entropypool_adddata(ent, buf, n, n * 8);
|
|
Packit Service |
ae04f2 |
added += n * 8;
|
|
Packit Service |
ae04f2 |
desired -= n;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
err:
|
|
Packit Service |
ae04f2 |
(void)close(fd);
|
|
Packit Service |
ae04f2 |
source->sources.file.handle = -1;
|
|
Packit Service |
ae04f2 |
source->bad = true;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
out:
|
|
Packit Service |
ae04f2 |
return (added);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
static unsigned int
|
|
Packit Service |
ae04f2 |
get_from_usocketsource(isc_entropysource_t *source, uint32_t desired) {
|
|
Packit Service |
ae04f2 |
isc_entropy_t *ent = source->ent;
|
|
Packit Service |
ae04f2 |
unsigned char buf[128];
|
|
Packit Service |
ae04f2 |
int fd = source->sources.usocket.handle;
|
|
Packit Service |
ae04f2 |
ssize_t n = 0, ndesired;
|
|
Packit Service |
ae04f2 |
unsigned int added;
|
|
Packit Service |
ae04f2 |
size_t sz_to_recv = source->sources.usocket.sz_to_recv;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (source->bad)
|
|
Packit Service |
ae04f2 |
return (0);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
added = 0;
|
|
Packit Service |
ae04f2 |
while (desired > 0) {
|
|
Packit Service |
ae04f2 |
ndesired = ISC_MIN(desired, sizeof(buf));
|
|
Packit Service |
ae04f2 |
eagain_loop:
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
switch ( source->sources.usocket.status ) {
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_ndesired:
|
|
Packit Service |
ae04f2 |
buf[0] = ndesired;
|
|
Packit Service |
ae04f2 |
if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
|
|
Packit Service |
ae04f2 |
if (errno == EWOULDBLOCK || errno == EINTR ||
|
|
Packit Service |
ae04f2 |
errno == ECONNRESET)
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
INSIST(n == 1);
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status =
|
|
Packit Service |
ae04f2 |
isc_usocketsource_wrote;
|
|
Packit Service |
ae04f2 |
goto eagain_loop;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_connecting:
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_connected:
|
|
Packit Service |
ae04f2 |
buf[0] = 1;
|
|
Packit Service |
ae04f2 |
buf[1] = ndesired;
|
|
Packit Service |
ae04f2 |
if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
|
|
Packit Service |
ae04f2 |
if (errno == EWOULDBLOCK || errno == EINTR ||
|
|
Packit Service |
ae04f2 |
errno == ECONNRESET)
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (n == 1) {
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status =
|
|
Packit Service |
ae04f2 |
isc_usocketsource_ndesired;
|
|
Packit Service |
ae04f2 |
goto eagain_loop;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
INSIST(n == 2);
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status =
|
|
Packit Service |
ae04f2 |
isc_usocketsource_wrote;
|
|
Packit Service |
ae04f2 |
/* FALLTHROUGH */
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_wrote:
|
|
Packit Service |
ae04f2 |
if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
|
|
Packit Service |
ae04f2 |
if (errno == EAGAIN) {
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* The problem of EAGAIN (try again
|
|
Packit Service |
ae04f2 |
* later) is a major issue on HP-UX.
|
|
Packit Service |
ae04f2 |
* Solaris actually tries the recvfrom
|
|
Packit Service |
ae04f2 |
* call again, while HP-UX just dies.
|
|
Packit Service |
ae04f2 |
* This code is an attempt to let the
|
|
Packit Service |
ae04f2 |
* entropy pool fill back up (at least
|
|
Packit Service |
ae04f2 |
* that's what I think the problem is.)
|
|
Packit Service |
ae04f2 |
* We go to eagain_loop because if we
|
|
Packit Service |
ae04f2 |
* just "break", then the "desired"
|
|
Packit Service |
ae04f2 |
* amount gets borked.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
#ifdef HAVE_NANOSLEEP
|
|
Packit Service |
ae04f2 |
struct timespec ts;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
ts.tv_sec = 0;
|
|
Packit Service |
ae04f2 |
ts.tv_nsec = 1000000;
|
|
Packit Service |
ae04f2 |
nanosleep(&ts, NULL);
|
|
Packit Service |
ae04f2 |
#else
|
|
Packit Service |
ae04f2 |
usleep(1000);
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
goto eagain_loop;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (errno == EWOULDBLOCK || errno == EINTR)
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status =
|
|
Packit Service |
ae04f2 |
isc_usocketsource_reading;
|
|
Packit Service |
ae04f2 |
sz_to_recv = buf[0];
|
|
Packit Service |
ae04f2 |
source->sources.usocket.sz_to_recv = sz_to_recv;
|
|
Packit Service |
ae04f2 |
if (sz_to_recv > sizeof(buf))
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
/* FALLTHROUGH */
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_reading:
|
|
Packit Service |
ae04f2 |
if (sz_to_recv != 0U) {
|
|
Packit Service |
ae04f2 |
n = recv(fd, buf, sz_to_recv, 0);
|
|
Packit Service |
ae04f2 |
if (n < 0) {
|
|
Packit Service |
ae04f2 |
if (errno == EWOULDBLOCK ||
|
|
Packit Service |
ae04f2 |
errno == EINTR)
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
} else
|
|
Packit Service |
ae04f2 |
n = 0;
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
default:
|
|
Packit Service |
ae04f2 |
goto err;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if ((size_t)n != sz_to_recv)
|
|
Packit Service |
ae04f2 |
source->sources.usocket.sz_to_recv -= n;
|
|
Packit Service |
ae04f2 |
else
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status =
|
|
Packit Service |
ae04f2 |
isc_usocketsource_connected;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (n == 0)
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
entropypool_adddata(ent, buf, n, n * 8);
|
|
Packit Service |
ae04f2 |
added += n * 8;
|
|
Packit Service |
ae04f2 |
desired -= n;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
goto out;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
err:
|
|
Packit Service |
ae04f2 |
close(fd);
|
|
Packit Service |
ae04f2 |
source->bad = true;
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status = isc_usocketsource_disconnected;
|
|
Packit Service |
ae04f2 |
source->sources.usocket.handle = -1;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
out:
|
|
Packit Service |
ae04f2 |
return (added);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Poll each source, trying to get data from it to stuff into the entropy
|
|
Packit Service |
ae04f2 |
* pool.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
static void
|
|
Packit Service |
ae04f2 |
fillpool(isc_entropy_t *ent, unsigned int desired, bool blocking) {
|
|
Packit Service |
ae04f2 |
unsigned int added;
|
|
Packit Service |
ae04f2 |
unsigned int remaining;
|
|
Packit Service |
ae04f2 |
unsigned int needed;
|
|
Packit Service |
ae04f2 |
unsigned int nsource;
|
|
Packit Service |
ae04f2 |
isc_entropysource_t *source;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
REQUIRE(VALID_ENTROPY(ent));
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
needed = desired;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* This logic is a little strange, so an explanation is in order.
|
|
Packit Service |
ae04f2 |
*
|
|
Packit Service |
ae04f2 |
* If needed is 0, it means we are being asked to "fill to whatever
|
|
Packit Service |
ae04f2 |
* we think is best." This means that if we have at least a
|
|
Packit Service |
ae04f2 |
* partially full pool (say, > 1/4th of the pool) we probably don't
|
|
Packit Service |
ae04f2 |
* need to add anything.
|
|
Packit Service |
ae04f2 |
*
|
|
Packit Service |
ae04f2 |
* Also, we will check to see if the "pseudo" count is too high.
|
|
Packit Service |
ae04f2 |
* If it is, try to mix in better data. Too high is currently
|
|
Packit Service |
ae04f2 |
* defined as 1/4th of the pool.
|
|
Packit Service |
ae04f2 |
*
|
|
Packit Service |
ae04f2 |
* Next, if we are asked to add a specific bit of entropy, make
|
|
Packit Service |
ae04f2 |
* certain that we will do so. Clamp how much we try to add to
|
|
Packit Service |
ae04f2 |
* (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
|
|
Packit Service |
ae04f2 |
*
|
|
Packit Service |
ae04f2 |
* Note that if we are in a blocking mode, we will only try to
|
|
Packit Service |
ae04f2 |
* get as much data as we need, not as much as we might want
|
|
Packit Service |
ae04f2 |
* to build up.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
if (needed == 0) {
|
|
Packit Service |
ae04f2 |
REQUIRE(!blocking);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if ((ent->pool.entropy >= RND_POOLBITS / 4)
|
|
Packit Service |
ae04f2 |
&& (ent->pool.pseudo <= RND_POOLBITS / 4))
|
|
Packit Service |
ae04f2 |
return;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
needed = THRESHOLD_BITS * 4;
|
|
Packit Service |
ae04f2 |
} else {
|
|
Packit Service |
ae04f2 |
needed = ISC_MAX(needed, THRESHOLD_BITS);
|
|
Packit Service |
ae04f2 |
needed = ISC_MIN(needed, RND_POOLBITS);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* In any case, clamp how much we need to how much we can add.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* But wait! If we're not yet initialized, we need at least
|
|
Packit Service |
ae04f2 |
* THRESHOLD_BITS
|
|
Packit Service |
ae04f2 |
* of randomness.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
if (ent->initialized < THRESHOLD_BITS)
|
|
Packit Service |
ae04f2 |
needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Poll each file source to see if we can read anything useful from
|
|
Packit Service |
ae04f2 |
* it. XXXMLG When where are multiple sources, we should keep a
|
|
Packit Service |
ae04f2 |
* record of which one we last used so we can start from it (or the
|
|
Packit Service |
ae04f2 |
* next one) to avoid letting some sources build up entropy while
|
|
Packit Service |
ae04f2 |
* others are always drained.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
added = 0;
|
|
Packit Service |
ae04f2 |
remaining = needed;
|
|
Packit Service |
ae04f2 |
if (ent->nextsource == NULL) {
|
|
Packit Service |
ae04f2 |
ent->nextsource = ISC_LIST_HEAD(ent->sources);
|
|
Packit Service |
ae04f2 |
if (ent->nextsource == NULL)
|
|
Packit Service |
ae04f2 |
return;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
source = ent->nextsource;
|
|
Packit Service |
ae04f2 |
again_file:
|
|
Packit Service |
ae04f2 |
for (nsource = 0; nsource < ent->nsources; nsource++) {
|
|
Packit Service |
ae04f2 |
unsigned int got;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (remaining == 0)
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
got = 0;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
switch ( source->type ) {
|
|
Packit Service |
ae04f2 |
case ENTROPY_SOURCETYPE_FILE:
|
|
Packit Service |
ae04f2 |
got = get_from_filesource(source, remaining);
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
case ENTROPY_SOURCETYPE_USOCKET:
|
|
Packit Service |
ae04f2 |
got = get_from_usocketsource(source, remaining);
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
added += got;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
remaining -= ISC_MIN(remaining, got);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
source = ISC_LIST_NEXT(source, link);
|
|
Packit Service |
ae04f2 |
if (source == NULL)
|
|
Packit Service |
ae04f2 |
source = ISC_LIST_HEAD(ent->sources);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
ent->nextsource = source;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (blocking && remaining != 0) {
|
|
Packit Service |
ae04f2 |
int fds;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
fds = wait_for_sources(ent);
|
|
Packit Service |
ae04f2 |
if (fds > 0)
|
|
Packit Service |
ae04f2 |
goto again_file;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Here, if there are bits remaining to be had and we can block,
|
|
Packit Service |
ae04f2 |
* check to see if we have a callback source. If so, call them.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
source = ISC_LIST_HEAD(ent->sources);
|
|
Packit Service |
ae04f2 |
while ((remaining != 0) && (source != NULL)) {
|
|
Packit Service |
ae04f2 |
unsigned int got;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
got = 0;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
|
|
Packit Service |
ae04f2 |
got = get_from_callback(source, remaining, blocking);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
added += got;
|
|
Packit Service |
ae04f2 |
remaining -= ISC_MIN(remaining, got);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (added >= needed)
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
source = ISC_LIST_NEXT(source, link);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Mark as initialized if we've added enough data.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
if (ent->initialized < THRESHOLD_BITS)
|
|
Packit Service |
ae04f2 |
ent->initialized += added;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
static int
|
|
Packit Service |
ae04f2 |
wait_for_sources(isc_entropy_t *ent) {
|
|
Packit Service |
ae04f2 |
isc_entropysource_t *source;
|
|
Packit Service |
ae04f2 |
int maxfd, fd;
|
|
Packit Service |
ae04f2 |
int cc;
|
|
Packit Service |
ae04f2 |
fd_set reads;
|
|
Packit Service |
ae04f2 |
fd_set writes;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
maxfd = -1;
|
|
Packit Service |
ae04f2 |
FD_ZERO(&reads);
|
|
Packit Service |
ae04f2 |
FD_ZERO(&writes);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
source = ISC_LIST_HEAD(ent->sources);
|
|
Packit Service |
ae04f2 |
while (source != NULL) {
|
|
Packit Service |
ae04f2 |
if (source->type == ENTROPY_SOURCETYPE_FILE) {
|
|
Packit Service |
ae04f2 |
fd = source->sources.file.handle;
|
|
Packit Service |
ae04f2 |
if (fd >= 0) {
|
|
Packit Service |
ae04f2 |
maxfd = ISC_MAX(maxfd, fd);
|
|
Packit Service |
ae04f2 |
FD_SET(fd, &reads);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
|
|
Packit Service |
ae04f2 |
fd = source->sources.usocket.handle;
|
|
Packit Service |
ae04f2 |
if (fd >= 0) {
|
|
Packit Service |
ae04f2 |
switch (source->sources.usocket.status) {
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_disconnected:
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_connecting:
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_connected:
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_ndesired:
|
|
Packit Service |
ae04f2 |
maxfd = ISC_MAX(maxfd, fd);
|
|
Packit Service |
ae04f2 |
FD_SET(fd, &writes);
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_wrote:
|
|
Packit Service |
ae04f2 |
case isc_usocketsource_reading:
|
|
Packit Service |
ae04f2 |
maxfd = ISC_MAX(maxfd, fd);
|
|
Packit Service |
ae04f2 |
FD_SET(fd, &reads);
|
|
Packit Service |
ae04f2 |
break;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
source = ISC_LIST_NEXT(source, link);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (maxfd < 0)
|
|
Packit Service |
ae04f2 |
return (-1);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
|
|
Packit Service |
ae04f2 |
if (cc < 0)
|
|
Packit Service |
ae04f2 |
return (-1);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
return (cc);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
static void
|
|
Packit Service |
ae04f2 |
destroyfilesource(isc_entropyfilesource_t *source) {
|
|
Packit Service |
ae04f2 |
(void)close(source->handle);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
static void
|
|
Packit Service |
ae04f2 |
destroyusocketsource(isc_entropyusocketsource_t *source) {
|
|
Packit Service |
ae04f2 |
close(source->handle);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Make a fd non-blocking
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
static isc_result_t
|
|
Packit Service |
ae04f2 |
make_nonblock(int fd) {
|
|
Packit Service |
ae04f2 |
int ret;
|
|
Packit Service |
ae04f2 |
char strbuf[ISC_STRERRORSIZE];
|
|
Packit Service |
ae04f2 |
#ifdef USE_FIONBIO_IOCTL
|
|
Packit Service |
ae04f2 |
int on = 1;
|
|
Packit Service |
ae04f2 |
#else
|
|
Packit Service |
ae04f2 |
int flags;
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
#ifdef USE_FIONBIO_IOCTL
|
|
Packit Service |
ae04f2 |
ret = ioctl(fd, FIONBIO, (char *)&on;;
|
|
Packit Service |
ae04f2 |
#else
|
|
Packit Service |
ae04f2 |
flags = fcntl(fd, F_GETFL, 0);
|
|
Packit Service |
ae04f2 |
flags |= PORT_NONBLOCK;
|
|
Packit Service |
ae04f2 |
ret = fcntl(fd, F_SETFL, flags);
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (ret == -1) {
|
|
Packit Service |
ae04f2 |
isc__strerror(errno, strbuf, sizeof(strbuf));
|
|
Packit Service |
ae04f2 |
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
Packit Service |
ae04f2 |
#ifdef USE_FIONBIO_IOCTL
|
|
Packit Service |
ae04f2 |
"ioctl(%d, FIONBIO, &on): %s", fd,
|
|
Packit Service |
ae04f2 |
#else
|
|
Packit Service |
ae04f2 |
"fcntl(%d, F_SETFL, %d): %s", fd, flags,
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
strbuf);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
return (ISC_R_UNEXPECTED);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
return (ISC_R_SUCCESS);
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
isc_result_t
|
|
Packit Service |
ae04f2 |
isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
|
|
Packit Service |
ae04f2 |
int fd;
|
|
Packit Service |
ae04f2 |
struct stat _stat;
|
|
Packit Service |
ae04f2 |
bool is_usocket = false;
|
|
Packit Service |
ae04f2 |
bool is_connected = false;
|
|
Packit Service |
ae04f2 |
isc_result_t ret;
|
|
Packit Service |
ae04f2 |
isc_entropysource_t *source;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
REQUIRE(VALID_ENTROPY(ent));
|
|
Packit Service |
ae04f2 |
REQUIRE(fname != NULL);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
LOCK(&ent->lock);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (stat(fname, &_stat) < 0) {
|
|
Packit Service |
ae04f2 |
ret = isc__errno2result(errno);
|
|
Packit Service |
ae04f2 |
goto errout;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
|
|
Packit Service |
ae04f2 |
* but it does return type S_IFIFO (the OS believes that
|
|
Packit Service |
ae04f2 |
* the socket is a fifo). This may be an issue if we tell
|
|
Packit Service |
ae04f2 |
* the program to look at an actual FIFO as its source of
|
|
Packit Service |
ae04f2 |
* entropy.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
#if defined(S_ISSOCK)
|
|
Packit Service |
ae04f2 |
if (S_ISSOCK(_stat.st_mode))
|
|
Packit Service |
ae04f2 |
is_usocket = true;
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
#if defined(S_ISFIFO) && defined(sun)
|
|
Packit Service |
ae04f2 |
if (S_ISFIFO(_stat.st_mode))
|
|
Packit Service |
ae04f2 |
is_usocket = true;
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
if (is_usocket)
|
|
Packit Service |
ae04f2 |
fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
Packit Service |
ae04f2 |
else
|
|
Packit Service |
ae04f2 |
fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (fd < 0) {
|
|
Packit Service |
ae04f2 |
ret = isc__errno2result(errno);
|
|
Packit Service |
ae04f2 |
goto errout;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
ret = make_nonblock(fd);
|
|
Packit Service |
ae04f2 |
if (ret != ISC_R_SUCCESS)
|
|
Packit Service |
ae04f2 |
goto closefd;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (is_usocket) {
|
|
Packit Service |
ae04f2 |
struct sockaddr_un sname;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
memset(&sname, 0, sizeof(sname));
|
|
Packit Service |
ae04f2 |
sname.sun_family = AF_UNIX;
|
|
Packit Service |
ae04f2 |
strlcpy(sname.sun_path, fname, sizeof(sname.sun_path));
|
|
Packit Service |
ae04f2 |
#ifdef ISC_PLATFORM_HAVESALEN
|
|
Packit Service |
ae04f2 |
#if !defined(SUN_LEN)
|
|
Packit Service |
ae04f2 |
#define SUN_LEN(su) \
|
|
Packit Service |
ae04f2 |
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
sname.sun_len = SUN_LEN(&sname);
|
|
Packit Service |
ae04f2 |
#endif
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (connect(fd, (struct sockaddr *) &sname,
|
|
Packit Service |
ae04f2 |
sizeof(struct sockaddr_un)) < 0) {
|
|
Packit Service |
ae04f2 |
if (errno != EINPROGRESS) {
|
|
Packit Service |
ae04f2 |
ret = isc__errno2result(errno);
|
|
Packit Service |
ae04f2 |
goto closefd;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
} else
|
|
Packit Service |
ae04f2 |
is_connected = true;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
|
|
Packit Service |
ae04f2 |
if (source == NULL) {
|
|
Packit Service |
ae04f2 |
ret = ISC_R_NOMEMORY;
|
|
Packit Service |
ae04f2 |
goto closefd;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* From here down, no failures can occur.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
source->magic = SOURCE_MAGIC;
|
|
Packit Service |
ae04f2 |
source->ent = ent;
|
|
Packit Service |
ae04f2 |
source->total = 0;
|
|
Packit Service |
ae04f2 |
source->bad = false;
|
|
Packit Service |
ae04f2 |
memset(source->name, 0, sizeof(source->name));
|
|
Packit Service |
ae04f2 |
ISC_LINK_INIT(source, link);
|
|
Packit Service |
ae04f2 |
if (is_usocket) {
|
|
Packit Service |
ae04f2 |
source->sources.usocket.handle = fd;
|
|
Packit Service |
ae04f2 |
if (is_connected)
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status =
|
|
Packit Service |
ae04f2 |
isc_usocketsource_connected;
|
|
Packit Service |
ae04f2 |
else
|
|
Packit Service |
ae04f2 |
source->sources.usocket.status =
|
|
Packit Service |
ae04f2 |
isc_usocketsource_connecting;
|
|
Packit Service |
ae04f2 |
source->sources.usocket.sz_to_recv = 0;
|
|
Packit Service |
ae04f2 |
source->type = ENTROPY_SOURCETYPE_USOCKET;
|
|
Packit Service |
ae04f2 |
} else {
|
|
Packit Service |
ae04f2 |
source->sources.file.handle = fd;
|
|
Packit Service |
ae04f2 |
source->type = ENTROPY_SOURCETYPE_FILE;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
/*
|
|
Packit Service |
ae04f2 |
* Hook it into the entropy system.
|
|
Packit Service |
ae04f2 |
*/
|
|
Packit Service |
ae04f2 |
ISC_LIST_APPEND(ent->sources, source, link);
|
|
Packit Service |
ae04f2 |
ent->nsources++;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
UNLOCK(&ent->lock);
|
|
Packit Service |
ae04f2 |
return (ISC_R_SUCCESS);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
closefd:
|
|
Packit Service |
ae04f2 |
(void)close(fd);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
errout:
|
|
Packit Service |
ae04f2 |
UNLOCK(&ent->lock);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
return (ret);
|
|
Packit Service |
ae04f2 |
}
|