854 lines
24 KiB
C
854 lines
24 KiB
C
#ifndef LINT
|
|
static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/prandom.c,v 1.12 2001/07/26 01:20:09 marka Exp $";
|
|
#endif
|
|
/*
|
|
* Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
|
|
*
|
|
* 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.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
* TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
|
|
*/
|
|
|
|
#include "port_before.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <dirent.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "dst_internal.h"
|
|
#include "prand_conf.h"
|
|
|
|
#include "port_after.h"
|
|
|
|
#ifndef DST_NUM_HASHES
|
|
#define DST_NUM_HASHES 4
|
|
#endif
|
|
#ifndef DST_NUMBER_OF_COUNTERS
|
|
#define DST_NUMBER_OF_COUNTERS 5 /* 32 * 5 == 160 == SHA(1) > MD5 */
|
|
#endif
|
|
|
|
/*
|
|
* the constant below is a prime number to make fixed data structues like
|
|
* stat and time wrap over blocks. This adds certain uncertanty to what is
|
|
* in each digested block.
|
|
* The prime number 2879 has the special property that when
|
|
* divided by 2,4 and 6 the result is also a prime numbers
|
|
*/
|
|
|
|
#ifndef DST_RANDOM_BLOCK_SIZE
|
|
#define DST_RANDOM_BLOCK_SIZE 2879
|
|
#endif
|
|
|
|
/*
|
|
* This constant dictatates how many bits we shift to the right before using a
|
|
*/
|
|
#ifndef DST_SHIFT
|
|
#define DST_SHIFT 9
|
|
#endif
|
|
|
|
/*
|
|
* An initalizer that is as bad as any other with half the bits set
|
|
*/
|
|
#ifndef DST_RANDOM_PATTERN
|
|
#define DST_RANDOM_PATTERN 0x8765CA93
|
|
#endif
|
|
/*
|
|
* things must have changed in the last 3600 seconds to be used
|
|
*/
|
|
#define MAX_OLD 3600
|
|
|
|
|
|
/*
|
|
* these two data structure are used to process input data into digests,
|
|
*
|
|
* The first structure is containts a pointer to a DST HMAC key
|
|
* the variables accompanying are used for
|
|
* step : select every step byte from input data for the hash
|
|
* block: number of data elements going into each hash
|
|
* digested: number of data elements digested so far
|
|
* curr: offset into the next input data for the first byte.
|
|
*/
|
|
typedef struct hash {
|
|
DST_KEY *key;
|
|
void *ctx;
|
|
int digested, block, step, curr;
|
|
} prand_hash;
|
|
|
|
/*
|
|
* This data structure controlls number of hashes and keeps track of
|
|
* overall progress in generating correct number of bytes of output.
|
|
* output : array to store the output data in
|
|
* needed : how many bytes of output are needed
|
|
* filled : number of bytes in output so far.
|
|
* bytes : total number of bytes processed by this structure
|
|
* file_digest : the HMAC key used to digest files.
|
|
*/
|
|
typedef struct work {
|
|
int needed, filled, bytes;
|
|
u_char *output;
|
|
prand_hash *hash[DST_NUM_HASHES];
|
|
DST_KEY *file_digest;
|
|
} dst_work;
|
|
|
|
|
|
/*
|
|
* forward function declarations
|
|
*/
|
|
static int get_dev_random(u_char *output, int size);
|
|
static int do_time(dst_work *work);
|
|
static int do_ls(dst_work *work);
|
|
static int unix_cmd(dst_work *work);
|
|
static int digest_file(dst_work *work);
|
|
|
|
static void force_hash(dst_work *work, prand_hash *hash);
|
|
static int do_hash(dst_work *work, prand_hash *hash, const u_char *input,
|
|
int size);
|
|
static int my_digest(dst_work *tmp, const u_char *input, int size);
|
|
static prand_hash *get_hmac_key(int step, int block);
|
|
|
|
static int own_random(dst_work *work);
|
|
|
|
|
|
/*
|
|
* variables used in the quick random number generator
|
|
*/
|
|
static u_int32_t ran_val = DST_RANDOM_PATTERN;
|
|
static u_int32_t ran_cnt = (DST_RANDOM_PATTERN >> 10);
|
|
|
|
/*
|
|
* setting the quick_random generator to particular values or if both
|
|
* input parameters are 0 then set it to initial vlaues
|
|
*/
|
|
|
|
void
|
|
dst_s_quick_random_set(u_int32_t val, u_int32_t cnt)
|
|
{
|
|
ran_val = (val == 0) ? DST_RANDOM_PATTERN : val;
|
|
ran_cnt = (cnt == 0) ? (DST_RANDOM_PATTERN >> 10) : cnt;
|
|
}
|
|
|
|
/*
|
|
* this is a quick and random number generator that seems to generate quite
|
|
* good distribution of data
|
|
*/
|
|
u_int32_t
|
|
dst_s_quick_random(int inc)
|
|
{
|
|
ran_val = ((ran_val >> 13) ^ (ran_val << 19)) ^
|
|
((ran_val >> 7) ^ (ran_val << 25));
|
|
if (inc > 0) /* only increasing values accepted */
|
|
ran_cnt += inc;
|
|
ran_val += ran_cnt++;
|
|
return (ran_val);
|
|
}
|
|
|
|
/*
|
|
* get_dev_random: Function to read /dev/random reliably
|
|
* this function returns how many bytes where read from the device.
|
|
* port_after.h should set the control variable HAVE_DEV_RANDOM
|
|
*/
|
|
static int
|
|
get_dev_random(u_char *output, int size)
|
|
{
|
|
#ifdef HAVE_DEV_RANDOM
|
|
struct stat st;
|
|
int n = 0, fd = -1, s;
|
|
|
|
s = stat("/dev/random", &st);
|
|
if (s == 0 && S_ISCHR(st.st_mode)) {
|
|
if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) != -1) {
|
|
if ((n = read(fd, output, size)) < 0)
|
|
n = 0;
|
|
close(fd);
|
|
}
|
|
return (n);
|
|
}
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Portable way of getting the time values if gettimeofday is missing
|
|
* then compile with -DMISSING_GETTIMEOFDAY time() is POSIX compliant but
|
|
* gettimeofday() is not.
|
|
* Time of day is predictable, we are looking for the randomness that comes
|
|
* the last few bits in the microseconds in the timer are hard to predict when
|
|
* this is invoked at the end of other operations
|
|
*/
|
|
struct timeval *mtime;
|
|
static int
|
|
do_time(dst_work *work)
|
|
{
|
|
int cnt = 0;
|
|
static u_char tmp[sizeof(struct timeval) + sizeof(struct timezone)];
|
|
struct timezone *zone;
|
|
|
|
zone = (struct timezone *) tmp;
|
|
mtime = (struct timeval *)(tmp + sizeof(struct timezone));
|
|
gettimeofday(mtime, zone);
|
|
cnt = sizeof(tmp);
|
|
my_digest(work, tmp, sizeof(tmp));
|
|
|
|
return (cnt);
|
|
}
|
|
|
|
/*
|
|
* this function simulates the ls command, but it uses stat which gives more
|
|
* information and is harder to guess
|
|
* Each call to this function will visit the next directory on the list of
|
|
* directories, in a circular manner.
|
|
* return value is the number of bytes added to the temp buffer
|
|
*
|
|
* do_ls() does not visit subdirectories
|
|
* if attacker has access to machine it can guess most of the values seen
|
|
* thus it is important to only visit directories that are freqently updated
|
|
* Attacker that has access to the network can see network traffic
|
|
* when NFS mounted directories are accessed and know exactly the data used
|
|
* but may not know exactly in what order data is used.
|
|
* Returns the number of bytes that where returned in stat structures
|
|
*/
|
|
static int
|
|
do_ls(dst_work *work)
|
|
{
|
|
struct dir_info {
|
|
uid_t uid;
|
|
gid_t gid;
|
|
off_t size;
|
|
time_t atime, mtime, ctime;
|
|
};
|
|
static struct dir_info dir_info;
|
|
struct stat buf;
|
|
struct dirent *entry;
|
|
static int i = 0;
|
|
static unsigned long d_round = 0;
|
|
struct timeval tv;
|
|
int n = 0, dir_len, tb_i = 0, out = 0;
|
|
|
|
char file_name[1024];
|
|
u_char tmp_buff[1024];
|
|
DIR *dir = NULL;
|
|
|
|
if (dirs[i] == NULL) /* if at the end of the list start over */
|
|
i = 0;
|
|
if (stat(dirs[i++], &buf)) /* directory does not exist */
|
|
return (0);
|
|
|
|
gettimeofday(&tv, NULL);
|
|
if (d_round == 0)
|
|
d_round = tv.tv_sec - MAX_OLD;
|
|
else if (i==1) /* if starting a new round cut what we accept */
|
|
d_round += (tv.tv_sec - d_round)/2;
|
|
|
|
if (buf.st_atime < (time_t)d_round)
|
|
return (0);
|
|
|
|
EREPORT(("do_ls i %d filled %4d\n", i-1, work->filled));
|
|
memcpy(tmp_buff, &buf, sizeof(buf));
|
|
tb_i += sizeof(buf);
|
|
|
|
|
|
if ((dir = opendir(dirs[i-1])) == NULL)/* open it for read */
|
|
return (0);
|
|
strcpy(file_name, dirs[i-1]);
|
|
dir_len = strlen(file_name);
|
|
file_name[dir_len++] = '/';
|
|
while ((entry = readdir(dir))) {
|
|
int len = strlen(entry->d_name);
|
|
out += len;
|
|
if (my_digest(work, (u_char *)entry->d_name, len))
|
|
break;
|
|
|
|
memcpy(&file_name[dir_len], entry->d_name, len);
|
|
file_name[dir_len + len] = 0x0;
|
|
/* for all entries in dir get the stats */
|
|
if (stat(file_name, &buf) == 0) {
|
|
n++; /* count successfull stat calls */
|
|
/* copy non static fields */
|
|
dir_info.uid += buf.st_uid;
|
|
dir_info.gid += buf.st_gid;
|
|
dir_info.size += buf.st_size;
|
|
dir_info.atime += buf.st_atime;
|
|
dir_info.mtime += buf.st_mtime;
|
|
dir_info.ctime += buf.st_ctime;
|
|
out += sizeof(dir_info);
|
|
if(my_digest(work, (u_char *)&dir_info,
|
|
sizeof(dir_info)))
|
|
break;
|
|
}
|
|
}
|
|
closedir(dir); /* done */
|
|
out += do_time(work); /* add a time stamp */
|
|
return (out);
|
|
}
|
|
|
|
|
|
/*
|
|
* unix_cmd()
|
|
* this function executes the a command from the cmds[] list of unix commands
|
|
* configured in the prand_conf.h file
|
|
* return value is the number of bytes added to the randomness temp buffer
|
|
*
|
|
* it returns the number of bytes that where read in
|
|
* if more data is needed at the end time is added to the data.
|
|
* This function maintains a state to selects the next command to run
|
|
* returns the number of bytes read in from the command
|
|
*/
|
|
static int
|
|
unix_cmd(dst_work *work)
|
|
{
|
|
static int cmd_index = 0;
|
|
int cnt = 0, n;
|
|
FILE *pipe;
|
|
u_char buffer[4096];
|
|
|
|
if (cmds[cmd_index] == NULL)
|
|
cmd_index = 0;
|
|
EREPORT(("unix_cmd() i %d filled %4d\n", cmd_index, work->filled));
|
|
pipe = popen(cmds[cmd_index++], "r"); /* execute the command */
|
|
|
|
while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) {
|
|
cnt += n; /* process the output */
|
|
if (my_digest(work, buffer, n))
|
|
break;
|
|
/* this adds some randomness to the output */
|
|
cnt += do_time(work);
|
|
}
|
|
while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0)
|
|
(void)NULL; /* drain the pipe */
|
|
pclose(pipe);
|
|
return (cnt); /* read how many bytes where read in */
|
|
}
|
|
|
|
/*
|
|
* digest_file() This function will read a file and run hash over it
|
|
* input is a file name
|
|
*/
|
|
static int
|
|
digest_file(dst_work *work)
|
|
{
|
|
static int f_cnt = 0;
|
|
static unsigned long f_round = 0;
|
|
FILE *fp;
|
|
void *ctx;
|
|
const char *name;
|
|
int no, i;
|
|
struct stat st;
|
|
struct timeval tv;
|
|
u_char buf[1024];
|
|
|
|
if (f_round == 0 || files[f_cnt] == NULL || work->file_digest == NULL)
|
|
if (gettimeofday(&tv, NULL)) /* only do this if needed */
|
|
return (0);
|
|
if (f_round == 0) /* first time called set to one hour ago */
|
|
f_round = (tv.tv_sec - MAX_OLD);
|
|
name = files[f_cnt++];
|
|
if (files[f_cnt] == NULL) { /* end of list of files */
|
|
if(f_cnt <= 1) /* list is too short */
|
|
return (0);
|
|
f_cnt = 0; /* start again on list */
|
|
f_round += (tv.tv_sec - f_round)/2; /* set new cutoff */
|
|
work->file_digest = dst_free_key(work->file_digest);
|
|
}
|
|
if (work->file_digest == NULL) {
|
|
work->file_digest = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0,
|
|
(u_char *)&tv, sizeof(tv));
|
|
if (work->file_digest == NULL)
|
|
return (0);
|
|
}
|
|
if (access(name, R_OK) || stat(name, &st))
|
|
return (0); /* no such file or not allowed to read it */
|
|
if (strncmp(name, "/proc/", 6) && st.st_mtime < (time_t)f_round)
|
|
return(0); /* file has not changed recently enough */
|
|
if (dst_sign_data(SIG_MODE_INIT, work->file_digest, &ctx,
|
|
NULL, 0, NULL, 0)) {
|
|
work->file_digest = dst_free_key(work->file_digest);
|
|
return (0);
|
|
}
|
|
if ((fp = fopen(name, "r")) == NULL)
|
|
return (0);
|
|
for (no = 0; (i = fread(buf, sizeof(*buf), sizeof(buf), fp)) > 0;
|
|
no += i)
|
|
dst_sign_data(SIG_MODE_UPDATE, work->file_digest, &ctx,
|
|
buf, i, NULL, 0);
|
|
|
|
fclose(fp);
|
|
if (no >= 64) {
|
|
i = dst_sign_data(SIG_MODE_FINAL, work->file_digest, &ctx,
|
|
NULL, 0, &work->output[work->filled],
|
|
DST_HASH_SIZE);
|
|
if (i > 0)
|
|
work->filled += i;
|
|
}
|
|
else if (i > 0)
|
|
my_digest(work, buf, i);
|
|
my_digest(work, (const u_char *)name, strlen(name));
|
|
return (no + strlen(name));
|
|
}
|
|
|
|
/*
|
|
* function to perform the FINAL and INIT operation on a hash if allowed
|
|
*/
|
|
static void
|
|
force_hash(dst_work *work, prand_hash *hash)
|
|
{
|
|
int i = 0;
|
|
|
|
/*
|
|
* if more than half a block then add data to output
|
|
* otherwise adde the digest to the next hash
|
|
*/
|
|
if ((hash->digested * 2) > hash->block) {
|
|
i = dst_sign_data(SIG_MODE_FINAL, hash->key, &hash->ctx,
|
|
NULL, 0, &work->output[work->filled],
|
|
DST_HASH_SIZE);
|
|
|
|
hash->digested = 0;
|
|
dst_sign_data(SIG_MODE_INIT, hash->key, &hash->ctx,
|
|
NULL, 0, NULL, 0);
|
|
if (i > 0)
|
|
work->filled += i;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This function takes the input data does the selection of data specified
|
|
* by the hash control block.
|
|
* The step varialbe in the work sturcture determines which 1/step bytes
|
|
* are used,
|
|
*
|
|
*/
|
|
static int
|
|
do_hash(dst_work *work, prand_hash *hash, const u_char *input, int size)
|
|
{
|
|
const u_char *tmp = input;
|
|
u_char *save = NULL, *tp;
|
|
int i, cnt = size, n, needed, avail, dig, tmp_size = 0;
|
|
|
|
if (cnt <= 0 || input == NULL)
|
|
return (0);
|
|
|
|
if (hash->step > 1) { /* if using subset of input data */
|
|
tmp_size = size / hash->step + 2;
|
|
tmp = tp = save = malloc(tmp_size);
|
|
for (cnt = 0, i = hash->curr; i < size; i += hash->step, cnt++)
|
|
*(tp++) = input[i];
|
|
/* calcutate the starting point in the next input set */
|
|
hash->curr = (hash->step - (i - size)) % hash->step;
|
|
}
|
|
/* digest the data in block sizes */
|
|
for (n = 0; n < cnt; n += needed) {
|
|
avail = (cnt - n);
|
|
needed = hash->block - hash->digested;
|
|
dig = (avail < needed) ? avail : needed;
|
|
dst_sign_data(SIG_MODE_UPDATE, hash->key, &hash->ctx,
|
|
&tmp[n], dig, NULL, 0);
|
|
hash->digested += dig;
|
|
if (hash->digested >= hash->block)
|
|
force_hash(work, hash);
|
|
if (work->needed < work->filled) {
|
|
if (tmp_size > 0)
|
|
SAFE_FREE2(save, tmp_size);
|
|
return (1);
|
|
}
|
|
}
|
|
if (tmp_size > 0)
|
|
SAFE_FREE2(save, tmp_size);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Copy data from INPUT for length SIZE into the work-block TMP.
|
|
* If we fill the work-block, digest it; then,
|
|
* if work-block needs more data, keep filling with the rest of the input.
|
|
*/
|
|
static int
|
|
my_digest(dst_work *work, const u_char *input, int size)
|
|
{
|
|
|
|
int i, full = 0;
|
|
static unsigned counter;
|
|
|
|
counter += size;
|
|
/* first do each one of the hashes */
|
|
for (i = 0; i < DST_NUM_HASHES && full == 0; i++)
|
|
full = do_hash(work, work->hash[i], input, size) +
|
|
do_hash(work, work->hash[i], (u_char *) &counter,
|
|
sizeof(counter));
|
|
/*
|
|
* if enough data has be generated do final operation on all hashes
|
|
* that have enough date for that
|
|
*/
|
|
for (i = 0; full && (i < DST_NUM_HASHES); i++)
|
|
force_hash(work, work->hash[i]);
|
|
|
|
return (full);
|
|
}
|
|
|
|
/*
|
|
* this function gets some semi random data and sets that as an HMAC key
|
|
* If we get a valid key this function returns that key initalized
|
|
* otherwise it returns NULL;
|
|
*/
|
|
static prand_hash *
|
|
get_hmac_key(int step, int block)
|
|
{
|
|
|
|
u_char *buff;
|
|
int temp = 0, n = 0, size = 70;
|
|
DST_KEY *new_key = NULL;
|
|
prand_hash *new = NULL;
|
|
|
|
/* use key that is larger than digest algorithms (64) for key size */
|
|
buff = malloc(size);
|
|
if (buff == NULL)
|
|
return (NULL);
|
|
/* do not memset the allocated memory to get random bytes there */
|
|
/* time of day is somewhat random expecialy in the last bytes */
|
|
gettimeofday((struct timeval *) &buff[n], NULL);
|
|
n += sizeof(struct timeval);
|
|
|
|
/* get some semi random stuff in here stir it with micro seconds */
|
|
if (n < size) {
|
|
temp = dst_s_quick_random((int) buff[n - 1]);
|
|
memcpy(&buff[n], &temp, sizeof(temp));
|
|
n += sizeof(temp);
|
|
}
|
|
/* get the pid of this process and its parent */
|
|
if (n < size) {
|
|
temp = (int) getpid();
|
|
memcpy(&buff[n], &temp, sizeof(temp));
|
|
n += sizeof(temp);
|
|
}
|
|
if (n < size) {
|
|
temp = (int) getppid();
|
|
memcpy(&buff[n], &temp, sizeof(temp));
|
|
n += sizeof(temp);
|
|
}
|
|
/* get the user ID */
|
|
if (n < size) {
|
|
temp = (int) getuid();
|
|
memcpy(&buff[n], &temp, sizeof(temp));
|
|
n += sizeof(temp);
|
|
}
|
|
#ifndef GET_HOST_ID_MISSING
|
|
if (n < size) {
|
|
temp = (int) gethostid();
|
|
memcpy(&buff[n], &temp, sizeof(temp));
|
|
n += sizeof(temp);
|
|
}
|
|
#endif
|
|
/* get some more random data */
|
|
if (n < size) {
|
|
temp = dst_s_quick_random((int) buff[n - 1]);
|
|
memcpy(&buff[n], &temp, sizeof(temp));
|
|
n += sizeof(temp);
|
|
}
|
|
/* covert this into a HMAC key */
|
|
new_key = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, buff, size);
|
|
SAFE_FREE(buff);
|
|
|
|
/* get the control structure */
|
|
if ((new = malloc(sizeof(prand_hash))) == NULL)
|
|
return (NULL);
|
|
new->digested = new->curr = 0;
|
|
new->step = step;
|
|
new->block = block;
|
|
new->key = new_key;
|
|
if (dst_sign_data(SIG_MODE_INIT, new_key, &new->ctx, NULL, 0, NULL, 0))
|
|
return (NULL);
|
|
|
|
return (new);
|
|
}
|
|
|
|
/*
|
|
* own_random()
|
|
* This function goes out and from various sources tries to generate enough
|
|
* semi random data that a hash function can generate a random data.
|
|
* This function will iterate between the two main random source sources,
|
|
* information from programs and directores in random order.
|
|
* This function return the number of bytes added to the random output buffer.
|
|
*/
|
|
static int
|
|
own_random(dst_work *work)
|
|
{
|
|
int dir = 0, b;
|
|
int bytes, n, cmd = 0, dig = 0;
|
|
int start =0;
|
|
/*
|
|
* now get the initial seed to put into the quick random function from
|
|
* the address of the work structure
|
|
*/
|
|
bytes = (int) getpid();
|
|
/*
|
|
* proceed while needed
|
|
*/
|
|
while (work->filled < work->needed) {
|
|
EREPORT(("own_random r %08x b %6d f %6d\n",
|
|
ran_val, bytes, work->filled));
|
|
/* pick a random number in the range of 0..7 based on that random number
|
|
* perform some operations that yield random data
|
|
*/
|
|
start = work->filled;
|
|
n = (dst_s_quick_random(bytes) >> DST_SHIFT) & 0x07;
|
|
switch (n) {
|
|
case 0:
|
|
case 3:
|
|
if (sizeof(cmds) > 2 *sizeof(*cmds)) {
|
|
b = unix_cmd(work);
|
|
cmd += b;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
case 7:
|
|
if (sizeof(dirs) > 2 *sizeof(*dirs)) {
|
|
b = do_ls(work);
|
|
dir += b;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
case 5:
|
|
/* retry getting data from /dev/random */
|
|
b = get_dev_random(&work->output[work->filled],
|
|
work->needed - work->filled);
|
|
if (b > 0)
|
|
work->filled += b;
|
|
break;
|
|
|
|
case 6:
|
|
if (sizeof(files) > 2 * sizeof(*files)) {
|
|
b = digest_file(work);
|
|
dig += b;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
default: /* to make sure we make some progress */
|
|
work->output[work->filled++] = 0xff &
|
|
dst_s_quick_random(bytes);
|
|
b = 1;
|
|
break;
|
|
}
|
|
if (b > 0)
|
|
bytes += b;
|
|
}
|
|
return (work->filled);
|
|
}
|
|
|
|
|
|
/*
|
|
* dst_s_random() This function will return the requested number of bytes
|
|
* of randomness to the caller it will use the best available sources of
|
|
* randomness.
|
|
* The current order is to use /dev/random, precalculated randomness, and
|
|
* finaly use some system calls and programs to generate semi random data that
|
|
* is then digested to generate randomness.
|
|
* This function is thread safe as each thread uses its own context, but
|
|
* concurrent treads will affect each other as they update shared state
|
|
* information.
|
|
* It is strongly recommended that this function be called requesting a size
|
|
* that is not a multiple of the output of the hash function used.
|
|
*
|
|
* If /dev/random is not available this function is not suitable to generate
|
|
* large ammounts of data, rather it is suitable to seed a pseudo-random
|
|
* generator
|
|
* Returns the number of bytes put in the output buffer
|
|
*/
|
|
int
|
|
dst_s_random(u_char *output, int size)
|
|
{
|
|
int n = 0, s, i;
|
|
static u_char old_unused[DST_HASH_SIZE * DST_NUM_HASHES];
|
|
static int unused = 0;
|
|
|
|
if (size <= 0 || output == NULL)
|
|
return (0);
|
|
|
|
if (size >= 2048)
|
|
return (-1);
|
|
/*
|
|
* Read from /dev/random
|
|
*/
|
|
n = get_dev_random(output, size);
|
|
/*
|
|
* If old data is available and needed use it
|
|
*/
|
|
if (n < size && unused > 0) {
|
|
int need = size - n;
|
|
if (unused <= need) {
|
|
memcpy(output, old_unused, unused);
|
|
n += unused;
|
|
unused = 0;
|
|
} else {
|
|
memcpy(output, old_unused, need);
|
|
n += need;
|
|
unused -= need;
|
|
memcpy(old_unused, &old_unused[need], unused);
|
|
}
|
|
}
|
|
/*
|
|
* If we need more use the simulated randomness here.
|
|
*/
|
|
if (n < size) {
|
|
dst_work *my_work = (dst_work *) malloc(sizeof(dst_work));
|
|
if (my_work == NULL)
|
|
return (n);
|
|
my_work->needed = size - n;
|
|
my_work->filled = 0;
|
|
my_work->output = (u_char *) malloc(my_work->needed +
|
|
DST_HASH_SIZE *
|
|
DST_NUM_HASHES);
|
|
my_work->file_digest = NULL;
|
|
if (my_work->output == NULL)
|
|
return (n);
|
|
memset(my_work->output, 0x0, my_work->needed);
|
|
/* allocate upto 4 different HMAC hash functions out of order */
|
|
#if DST_NUM_HASHES >= 3
|
|
my_work->hash[2] = get_hmac_key(3, DST_RANDOM_BLOCK_SIZE / 2);
|
|
#endif
|
|
#if DST_NUM_HASHES >= 2
|
|
my_work->hash[1] = get_hmac_key(7, DST_RANDOM_BLOCK_SIZE / 6);
|
|
#endif
|
|
#if DST_NUM_HASHES >= 4
|
|
my_work->hash[3] = get_hmac_key(5, DST_RANDOM_BLOCK_SIZE / 4);
|
|
#endif
|
|
my_work->hash[0] = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE);
|
|
if (my_work->hash[0] == NULL) /* if failure bail out */
|
|
return (n);
|
|
s = own_random(my_work);
|
|
/* if more generated than needed store it for future use */
|
|
if (s >= my_work->needed) {
|
|
EREPORT(("dst_s_random(): More than needed %d >= %d\n",
|
|
s, my_work->needed));
|
|
memcpy(&output[n], my_work->output, my_work->needed);
|
|
n += my_work->needed;
|
|
/* saving unused data for next time */
|
|
unused = s - my_work->needed;
|
|
memcpy(old_unused, &my_work->output[my_work->needed],
|
|
unused);
|
|
} else {
|
|
/* XXXX This should not happen */
|
|
EREPORT(("Not enough %d >= %d\n", s, my_work->needed));
|
|
memcpy(&output[n], my_work->output, s);
|
|
n += my_work->needed;
|
|
}
|
|
|
|
/* delete the allocated work area */
|
|
for (i = 0; i < DST_NUM_HASHES; i++) {
|
|
dst_free_key(my_work->hash[i]->key);
|
|
SAFE_FREE(my_work->hash[i]);
|
|
}
|
|
SAFE_FREE(my_work->output);
|
|
SAFE_FREE(my_work);
|
|
}
|
|
return (n);
|
|
}
|
|
|
|
/*
|
|
* A random number generator that is fast and strong
|
|
* this random number generator is based on HASHing data,
|
|
* the input to the digest function is a collection of <NUMBER_OF_COUNTERS>
|
|
* counters that is incremented between digest operations
|
|
* each increment operation amortizes to 2 bits changed in that value
|
|
* for 5 counters thus the input will amortize to have 10 bits changed
|
|
* The counters are initaly set using the strong random function above
|
|
* the HMAC key is selected by the same methold as the HMAC keys for the
|
|
* strong random function.
|
|
* Each set of counters is used for 2^25 operations
|
|
*
|
|
* returns the number of bytes written to the output buffer
|
|
* or negative number in case of error
|
|
*/
|
|
int
|
|
dst_s_semi_random(u_char *output, int size)
|
|
{
|
|
static u_int32_t counter[DST_NUMBER_OF_COUNTERS];
|
|
static u_char semi_old[DST_HASH_SIZE];
|
|
static int semi_loc = 0, cnt = 0, hb_size = 0;
|
|
static DST_KEY *my_key = NULL;
|
|
prand_hash *hash;
|
|
int out = 0, i, n;
|
|
|
|
if (output == NULL || size <= 0)
|
|
return (-2);
|
|
|
|
/* check if we need a new key */
|
|
if (my_key == NULL || cnt > (1 << 25)) { /* get HMAC KEY */
|
|
if (my_key)
|
|
my_key->dk_func->destroy(my_key);
|
|
if ((hash = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE)) == NULL)
|
|
return (0);
|
|
my_key = hash->key;
|
|
/* check if the key works stir the new key using some old random data */
|
|
hb_size = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
|
|
(u_char *) counter, sizeof(counter),
|
|
semi_old, sizeof(semi_old));
|
|
if (hb_size <= 0) {
|
|
EREPORT(("dst_s_semi_random() Sign of alg %d failed %d\n",
|
|
my_key->dk_alg, hb_size));
|
|
return (-1);
|
|
}
|
|
/* new set the counters to random values */
|
|
dst_s_random((u_char *) counter, sizeof(counter));
|
|
cnt = 0;
|
|
}
|
|
/* if old data around use it first */
|
|
if (semi_loc < hb_size) {
|
|
if (size <= hb_size - semi_loc) { /* need less */
|
|
memcpy(output, &semi_old[semi_loc], size);
|
|
semi_loc += size;
|
|
return (size); /* DONE */
|
|
} else {
|
|
out = hb_size - semi_loc;
|
|
memcpy(output, &semi_old[semi_loc], out);
|
|
semi_loc += out;
|
|
}
|
|
}
|
|
/* generate more randome stuff */
|
|
while (out < size) {
|
|
/*
|
|
* modify at least one bit by incrementing at least one counter
|
|
* based on the last bit of the last counter updated update
|
|
* the next one.
|
|
* minimaly this operation will modify at least 1 bit,
|
|
* amortized 2 bits
|
|
*/
|
|
for (n = 0; n < DST_NUMBER_OF_COUNTERS; n++)
|
|
i = (int) counter[n]++;
|
|
|
|
i = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
|
|
(u_char *) counter, hb_size,
|
|
semi_old, sizeof(semi_old));
|
|
#ifdef REPORT_ERRORS
|
|
if (i != hb_size)
|
|
EREPORT(("HMAC SIGNATURE FAILURE %d\n", i));
|
|
#endif
|
|
cnt++;
|
|
if (size - out < i) /* Not all data is needed */
|
|
semi_loc = i = size - out;
|
|
memcpy(&output[out], semi_old, i);
|
|
out += i;
|
|
}
|
|
return (out);
|
|
}
|