📄 prandom.c
字号:
#ifndef LINTstatic const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/prandom.c,v 1.1 2001/02/22 07:22:09 mellon 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 <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 <netinet/in.h>#include <sys/socket.h>#define NEED_PRAND_CONF#include "minires/minires.h"#include "dst_internal.h"#include "arpa/nameser.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 { unsigned 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, unsigned 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, unsigned size);static int my_digest(dst_work *tmp, const u_char *input, unsigned size);static prand_hash *get_hmac_key(int step, int block);static unsigned 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 */voiddst_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_tdst_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 intget_dev_random(u_char *output, unsigned 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 intdo_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 intdo_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, tb_i = 0, out = 0; unsigned dir_len; 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 < d_round) return (0); EREPORT(("do_ls i %d filled %4d in_temp %4d\n", i-1, work->filled, work->in_temp)); 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))) { unsigned 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 intunix_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 in_temp %4d\n", cmd_index, work->filled, work->in_temp)); 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, (unsigned)n)) break; /* this adds some randomness to the output */ cnt += do_time(work); } while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) 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 < 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, (unsigned)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, (unsigned)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 voidforce_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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -