Annotation of embedaddon/dhcp/dst/prandom.c, revision 1.1
1.1 ! misho 1: #ifndef LINT
! 2: static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/prandom.c,v 1.6 2007-11-30 21:51:43 fdupont Exp $";
! 3: #endif
! 4: /*
! 5: * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
! 6: * Portions Copyright (c) 2007 by Internet Systems Consortium, Inc. ("ISC")
! 7: *
! 8: * Permission to use, copy modify, and distribute this software for any
! 9: * purpose with or without fee is hereby granted, provided that the above
! 10: * copyright notice and this permission notice appear in all copies.
! 11: *
! 12: * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
! 13: * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
! 14: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
! 15: * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
! 16: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
! 17: * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
! 18: * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
! 19: * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
! 20: */
! 21:
! 22: #include <stdio.h>
! 23: #include <sys/types.h>
! 24: #include <stdlib.h>
! 25: #include <string.h>
! 26: #include <unistd.h>
! 27: #include <fcntl.h>
! 28: #include <time.h>
! 29: #include <dirent.h>
! 30: #include <sys/param.h>
! 31: #include <sys/stat.h>
! 32: #include <sys/time.h>
! 33:
! 34: #include <netinet/in.h>
! 35: #include <sys/socket.h>
! 36: #define NEED_PRAND_CONF
! 37: #include "minires/minires.h"
! 38: #include "dst_internal.h"
! 39: #include "arpa/nameser.h"
! 40:
! 41:
! 42: #ifndef DST_NUM_HASHES
! 43: #define DST_NUM_HASHES 4
! 44: #endif
! 45: #ifndef DST_NUMBER_OF_COUNTERS
! 46: #define DST_NUMBER_OF_COUNTERS 5 /* 32 * 5 == 160 == SHA(1) > MD5 */
! 47: #endif
! 48:
! 49: /*
! 50: * the constant below is a prime number to make fixed data structures like
! 51: * stat and time wrap over blocks. This adds certain randomness to what is
! 52: * in each digested block.
! 53: * The prime number 2879 has the special property that when
! 54: * divided by 2,4 and 6 the result is also a prime numbers
! 55: */
! 56:
! 57: #ifndef DST_RANDOM_BLOCK_SIZE
! 58: #define DST_RANDOM_BLOCK_SIZE 2879
! 59: #endif
! 60:
! 61: /*
! 62: * This constant dictates how many bits we shift to the right before using a
! 63: */
! 64: #ifndef DST_SHIFT
! 65: #define DST_SHIFT 9
! 66: #endif
! 67:
! 68: /*
! 69: * An initializer that is as bad as any other with half the bits set
! 70: */
! 71: #ifndef DST_RANDOM_PATTERN
! 72: #define DST_RANDOM_PATTERN 0x8765CA93
! 73: #endif
! 74: /*
! 75: * things must have changed in the last 3600 seconds to be used
! 76: */
! 77: #define MAX_OLD 3600
! 78:
! 79: /*
! 80: * Define a single set of configuration for prand stuff. A superset
! 81: * works okay (failed commands return no data, missing directories
! 82: * are skipped, and so on.
! 83: */
! 84: static const char *cmds[] = {
! 85: "/usr/bin/netstat -an 2>&1",
! 86: "/usr/sbin/netstat -an 2>&1",
! 87: "/usr/etc/netstat -an 2>&1",
! 88: "/bin/netstat -an 2>&1",
! 89: "/usr/ucb/netstat -an 2>&1",
! 90:
! 91: /* AIX */
! 92: "/bin/ps -ef 2>&1",
! 93: "/bin/df 2>&1",
! 94: "/usr/bin/uptime 2>&1",
! 95: "/usr/bin/printenv 2>&1",
! 96: "/usr/bin/netstat -s 2>&1",
! 97: "/usr/bin/w 2>&1",
! 98: /* Tru64 */
! 99: "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
! 100: "/usr/sbin/arp -an 2>&1",
! 101: "/usr/ucb/uptime 2>&1",
! 102: "/bin/iostat 2>&1",
! 103: /* BSD */
! 104: "/bin/ps -axlw 2>&1",
! 105: "/usr/sbin/iostat 2>&1",
! 106: "/usr/sbin/vmstat 2>&1",
! 107: /* FreeBSD */
! 108: "/usr/bin/vmstat 2>&1",
! 109: "/usr/bin/w 2>&1",
! 110: /* HP/UX */
! 111: "/usr/bin/ps -ef 2>&1",
! 112: /* IRIX */
! 113: "/usr/etc/arp -a 2>&1",
! 114: "/usr/bsd/uptime 2>&1",
! 115: "/usr/bin/printenv 2>&1",
! 116: "/usr/bsd/w 2>&1",
! 117: /* Linux */
! 118: "/sbin/arp -an 2>&1",
! 119: "/usr/bin/vmstat 2>&1",
! 120: /* NetBSD */
! 121: /* OpenBSD */
! 122: /* QNX */
! 123: "/bin/ps -a 2>&1",
! 124: "/bin/sin 2>&1",
! 125: "/bin/sin fds 2>&1",
! 126: "/bin/sin memory 2>&1",
! 127: /* Solaris */
! 128: "/usr/ucb/uptime 2>&1",
! 129: "/usr/ucb/netstat -an 2>&1",
! 130:
! 131: "/usr/bin/netstat -an 2>&1",
! 132: "/usr/sbin/netstat -an 2>&1",
! 133: "/usr/etc/netstat -an 2>&1",
! 134: "/bin/netstat -an 2>&1",
! 135: "/usr/ucb/netstat -an 2>&1",
! 136: NULL
! 137: };
! 138:
! 139: static const char *dirs[] = {
! 140: "/tmp",
! 141: "/var/tmp",
! 142: ".",
! 143: "/",
! 144: "/var/spool",
! 145: "/var/adm",
! 146: "/dev",
! 147: "/var/spool/mail",
! 148: "/var/mail",
! 149: "/home",
! 150: "/usr/home",
! 151: NULL
! 152: };
! 153:
! 154: static const char *files[] = {
! 155: "/var/adm/messages",
! 156: "/var/adm/wtmp",
! 157: "/var/adm/lastlog",
! 158: "/var/log/messages",
! 159: "/var/log/wtmp",
! 160: "/var/log/lastlog",
! 161: "/proc/stat",
! 162: "/proc/rtc",
! 163: "/proc/meminfo",
! 164: "/proc/interrupts",
! 165: "/proc/self/status",
! 166: "/proc/ipstats",
! 167: "/proc/dumper",
! 168: "/proc/self/as",
! 169: NULL
! 170: };
! 171:
! 172: /*
! 173: * these two data structure are used to process input data into digests,
! 174: *
! 175: * The first structure contains a pointer to a DST HMAC key
! 176: * the variables accompanying are used for
! 177: * step : select every step byte from input data for the hash
! 178: * block: number of data elements going into each hash
! 179: * digested: number of data elements digested so far
! 180: * curr: offset into the next input data for the first byte.
! 181: */
! 182: typedef struct hash {
! 183: DST_KEY *key;
! 184: void *ctx;
! 185: int digested, block, step, curr;
! 186: } prand_hash;
! 187:
! 188: /*
! 189: * This data structure controls number of hashes and keeps track of
! 190: * overall progress in generating correct number of bytes of output.
! 191: * output : array to store the output data in
! 192: * needed : how many bytes of output are needed
! 193: * filled : number of bytes in output so far.
! 194: * bytes : total number of bytes processed by this structure
! 195: * file_digest : the HMAC key used to digest files.
! 196: */
! 197: typedef struct work {
! 198: unsigned needed, filled, bytes;
! 199: u_char *output;
! 200: prand_hash *hash[DST_NUM_HASHES];
! 201: DST_KEY *file_digest;
! 202: } dst_work;
! 203:
! 204:
! 205: /*
! 206: * forward function declarations
! 207: */
! 208: static int get_dev_random(u_char *output, unsigned size);
! 209: static int do_time(dst_work *work);
! 210: static int do_ls(dst_work *work);
! 211: static int unix_cmd(dst_work *work);
! 212: static int digest_file(dst_work *work);
! 213:
! 214: static void force_hash(dst_work *work, prand_hash *hash);
! 215: static int do_hash(dst_work *work, prand_hash *hash, const u_char *input,
! 216: unsigned size);
! 217: static int my_digest(dst_work *tmp, const u_char *input, unsigned size);
! 218: static prand_hash *get_hmac_key(int step, int block);
! 219:
! 220: static unsigned own_random(dst_work *work);
! 221:
! 222:
! 223: /*
! 224: * variables used in the quick random number generator
! 225: */
! 226: static u_int32_t ran_val = DST_RANDOM_PATTERN;
! 227: static u_int32_t ran_cnt = (DST_RANDOM_PATTERN >> 10);
! 228:
! 229: /*
! 230: * setting the quick_random generator to particular values or if both
! 231: * input parameters are 0 then set it to initial values
! 232: */
! 233:
! 234: void
! 235: dst_s_quick_random_set(u_int32_t val, u_int32_t cnt)
! 236: {
! 237: ran_val = (val == 0) ? DST_RANDOM_PATTERN : val;
! 238: ran_cnt = (cnt == 0) ? (DST_RANDOM_PATTERN >> 10) : cnt;
! 239: }
! 240:
! 241: /*
! 242: * this is a quick and random number generator that seems to generate quite
! 243: * good distribution of data
! 244: */
! 245: u_int32_t
! 246: dst_s_quick_random(int inc)
! 247: {
! 248: ran_val = ((ran_val >> 13) ^ (ran_val << 19)) ^
! 249: ((ran_val >> 7) ^ (ran_val << 25));
! 250: if (inc > 0) /* only increasing values accepted */
! 251: ran_cnt += inc;
! 252: ran_val += ran_cnt++;
! 253: return (ran_val);
! 254: }
! 255:
! 256: /*
! 257: * get_dev_random: Function to read /dev/random reliably
! 258: * this function returns how many bytes where read from the device.
! 259: * port_after.h should set the control variable HAVE_DEV_RANDOM
! 260: */
! 261: static int
! 262: get_dev_random(u_char *output, unsigned size)
! 263: {
! 264: #ifdef HAVE_DEV_RANDOM
! 265: struct stat st;
! 266: int n = 0, fd = -1, s;
! 267:
! 268: s = stat("/dev/random", &st);
! 269: if (s == 0 && S_ISCHR(st.st_mode)) {
! 270: if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) != -1) {
! 271: if ((n = read(fd, output, size)) < 0)
! 272: n = 0;
! 273: close(fd);
! 274: }
! 275: return (n);
! 276: }
! 277: #endif
! 278: return (0);
! 279: }
! 280:
! 281: /*
! 282: * Portable way of getting the time values if gettimeofday is missing
! 283: * then compile with -DMISSING_GETTIMEOFDAY time() is POSIX compliant but
! 284: * gettimeofday() is not.
! 285: * Time of day is predictable, we are looking for the randomness that comes
! 286: * the last few bits in the microseconds in the timer are hard to predict when
! 287: * this is invoked at the end of other operations
! 288: */
! 289: struct timeval *mtime;
! 290: static int
! 291: do_time(dst_work *work)
! 292: {
! 293: int cnt = 0;
! 294: static u_char tmp[sizeof(struct timeval) + sizeof(struct timezone)];
! 295: struct timezone *zone;
! 296:
! 297: zone = (struct timezone *) tmp;
! 298: mtime = (struct timeval *)(tmp + sizeof(struct timezone));
! 299: gettimeofday(mtime, zone);
! 300: cnt = sizeof(tmp);
! 301: my_digest(work, tmp, sizeof(tmp));
! 302:
! 303: return (cnt);
! 304: }
! 305:
! 306: /*
! 307: * this function simulates the ls command, but it uses stat which gives more
! 308: * information and is harder to guess
! 309: * Each call to this function will visit the next directory on the list of
! 310: * directories, in a circular manner.
! 311: * return value is the number of bytes added to the temp buffer
! 312: *
! 313: * do_ls() does not visit subdirectories
! 314: * if attacker has access to machine it can guess most of the values seen
! 315: * thus it is important to only visit directories that are frequently updated
! 316: * Attacker that has access to the network can see network traffic
! 317: * when NFS mounted directories are accessed and know exactly the data used
! 318: * but may not know exactly in what order data is used.
! 319: * Returns the number of bytes that where returned in stat structures
! 320: */
! 321: static int
! 322: do_ls(dst_work *work)
! 323: {
! 324: struct dir_info {
! 325: uid_t uid;
! 326: gid_t gid;
! 327: off_t size;
! 328: time_t atime, mtime, ctime;
! 329: };
! 330: static struct dir_info dir_info;
! 331: struct stat buf;
! 332: struct dirent *entry;
! 333: static int i = 0;
! 334: static unsigned long d_round = 0;
! 335: struct timeval tv;
! 336: int n = 0, tb_i = 0, out = 0;
! 337: unsigned dir_len;
! 338:
! 339: char file_name[1024];
! 340: u_char tmp_buff[1024];
! 341: DIR *dir = NULL;
! 342:
! 343: if (dirs[i] == NULL) /* if at the end of the list start over */
! 344: i = 0;
! 345: if (stat(dirs[i++], &buf)) /* directory does not exist */
! 346: return (0);
! 347:
! 348: gettimeofday(&tv,NULL);
! 349: if (d_round == 0)
! 350: d_round = tv.tv_sec - MAX_OLD;
! 351: else if (i==1) /* if starting a new round cut what we accept */
! 352: d_round += (tv.tv_sec - d_round)/2;
! 353:
! 354: if (buf.st_atime < d_round)
! 355: return (0);
! 356:
! 357: EREPORT(("do_ls i %d filled %4d in_temp %4d\n",
! 358: i-1, work->filled, work->in_temp));
! 359: memcpy(tmp_buff, &buf, sizeof(buf));
! 360: tb_i += sizeof(buf);
! 361:
! 362:
! 363: if ((dir = opendir(dirs[i-1])) == NULL)/* open it for read */
! 364: return (0);
! 365: strcpy(file_name, dirs[i-1]);
! 366: dir_len = strlen(file_name);
! 367: file_name[dir_len++] = '/';
! 368: while ((entry = readdir(dir))) {
! 369: unsigned len = strlen(entry->d_name);
! 370: out += len;
! 371: if (my_digest(work, (u_char *)entry->d_name, len))
! 372: break;
! 373:
! 374: memcpy(&file_name[dir_len], entry->d_name, len);
! 375: file_name[dir_len + len] = 0x0;
! 376: /* for all entries in dir get the stats */
! 377: if (stat(file_name, &buf) == 0) {
! 378: n++; /* count successful stat calls */
! 379: /* copy non static fields */
! 380: dir_info.uid += buf.st_uid;
! 381: dir_info.gid += buf.st_gid;
! 382: dir_info.size += buf.st_size;
! 383: dir_info.atime += buf.st_atime;
! 384: dir_info.mtime += buf.st_mtime;
! 385: dir_info.ctime += buf.st_ctime;
! 386: out += sizeof(dir_info);
! 387: if(my_digest(work, (u_char *)&dir_info,
! 388: sizeof(dir_info)))
! 389: break;
! 390: }
! 391: }
! 392: closedir(dir); /* done */
! 393: out += do_time(work); /* add a time stamp */
! 394: return (out);
! 395: }
! 396:
! 397:
! 398: /*
! 399: * unix_cmd()
! 400: * this function executes the a command from the cmds[] list of unix commands
! 401: * configured in the prand_conf.h file
! 402: * return value is the number of bytes added to the randomness temp buffer
! 403: *
! 404: * it returns the number of bytes that where read in
! 405: * if more data is needed at the end time is added to the data.
! 406: * This function maintains a state to selects the next command to run
! 407: * returns the number of bytes read in from the command
! 408: */
! 409: static int
! 410: unix_cmd(dst_work *work)
! 411: {
! 412: static int cmd_index = 0;
! 413: int cnt = 0, n;
! 414: FILE *pipe;
! 415: u_char buffer[4096];
! 416:
! 417: if (cmds[cmd_index] == NULL)
! 418: cmd_index = 0;
! 419: EREPORT(("unix_cmd() i %d filled %4d in_temp %4d\n",
! 420: cmd_index, work->filled, work->in_temp));
! 421: pipe = popen(cmds[cmd_index++], "r"); /* execute the command */
! 422:
! 423: while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) {
! 424: cnt += n; /* process the output */
! 425: if (my_digest(work, buffer, (unsigned)n))
! 426: break;
! 427: /* this adds some randomness to the output */
! 428: cnt += do_time(work);
! 429: }
! 430: while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0)
! 431: ; /* drain the pipe */
! 432: pclose(pipe);
! 433: return (cnt); /* read how many bytes where read in */
! 434: }
! 435:
! 436: /*
! 437: * digest_file() This function will read a file and run hash over it
! 438: * input is a file name
! 439: */
! 440: static int
! 441: digest_file(dst_work *work)
! 442: {
! 443: static int f_cnt = 0;
! 444: static unsigned long f_round = 0;
! 445: FILE *fp;
! 446: void *ctx;
! 447: const char *name;
! 448: int no, i;
! 449: struct stat st;
! 450: struct timeval tv;
! 451: u_char buf[1024];
! 452:
! 453: if (f_round == 0 || files[f_cnt] == NULL || work->file_digest == NULL)
! 454: if (gettimeofday(&tv, NULL)) /* only do this if needed */
! 455: return (0);
! 456: if (f_round == 0) /* first time called set to one hour ago */
! 457: f_round = (tv.tv_sec - MAX_OLD);
! 458: name = files[f_cnt++];
! 459: if (files[f_cnt] == NULL) { /* end of list of files */
! 460: if(f_cnt <= 1) /* list is too short */
! 461: return (0);
! 462: f_cnt = 0; /* start again on list */
! 463: f_round += (tv.tv_sec - f_round)/2; /* set new cutoff */
! 464: work->file_digest = dst_free_key(work->file_digest);
! 465: }
! 466: if (work->file_digest == NULL) {
! 467: work->file_digest = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0,
! 468: (u_char *)&tv, sizeof(tv));
! 469: if (work->file_digest == NULL)
! 470: return (0);
! 471: }
! 472: if (access(name, R_OK) || stat(name, &st))
! 473: return (0); /* no such file or not allowed to read it */
! 474: if (strncmp(name, "/proc/", 6) && st.st_mtime < f_round)
! 475: return(0); /* file has not changed recently enough */
! 476: if (dst_sign_data(SIG_MODE_INIT, work->file_digest, &ctx,
! 477: NULL, 0, NULL, 0)) {
! 478: work->file_digest = dst_free_key(work->file_digest);
! 479: return (0);
! 480: }
! 481: if ((fp = fopen(name, "r")) == NULL)
! 482: return (0);
! 483: for (no = 0; (i = fread(buf, sizeof(*buf), sizeof(buf), fp)) > 0;
! 484: no += i)
! 485: dst_sign_data(SIG_MODE_UPDATE, work->file_digest, &ctx,
! 486: buf, (unsigned)i, NULL, 0);
! 487:
! 488: fclose(fp);
! 489: if (no >= 64) {
! 490: i = dst_sign_data(SIG_MODE_FINAL, work->file_digest, &ctx,
! 491: NULL, 0, &work->output[work->filled],
! 492: DST_HASH_SIZE);
! 493: if (i > 0)
! 494: work->filled += i;
! 495: }
! 496: else if (i > 0)
! 497: my_digest(work, buf, (unsigned)i);
! 498: my_digest(work, (const u_char *)name, strlen(name));
! 499: return (no + strlen(name));
! 500: }
! 501:
! 502: /*
! 503: * function to perform the FINAL and INIT operation on a hash if allowed
! 504: */
! 505: static void
! 506: force_hash(dst_work *work, prand_hash *hash)
! 507: {
! 508: int i = 0;
! 509:
! 510: /*
! 511: * if more than half a block then add data to output
! 512: * otherwise add the digest to the next hash
! 513: */
! 514: if ((hash->digested * 2) > hash->block) {
! 515: i = dst_sign_data(SIG_MODE_FINAL, hash->key, &hash->ctx,
! 516: NULL, 0, &work->output[work->filled],
! 517: DST_HASH_SIZE);
! 518:
! 519: hash->digested = 0;
! 520: dst_sign_data(SIG_MODE_INIT, hash->key, &hash->ctx,
! 521: NULL, 0, NULL, 0);
! 522: if (i > 0)
! 523: work->filled += i;
! 524: }
! 525: return;
! 526: }
! 527:
! 528: /*
! 529: * This function takes the input data does the selection of data specified
! 530: * by the hash control block.
! 531: * The step variable in the work structure determines which 1/step bytes
! 532: * are used,
! 533: *
! 534: */
! 535: static int
! 536: do_hash(dst_work *work, prand_hash *hash, const u_char *input, unsigned size)
! 537: {
! 538: const u_char *tmp = input;
! 539: u_char *tp, *abuf = (u_char *)0;
! 540: int i, n;
! 541: unsigned needed, avail, dig, cnt = size;
! 542: unsigned tmp_size = 0;
! 543:
! 544: if (cnt <= 0 || input == NULL)
! 545: return (0);
! 546:
! 547: if (hash->step > 1) { /* if using subset of input data */
! 548: tmp_size = size / hash->step + 2;
! 549: abuf = tp = malloc(tmp_size);
! 550: tmp = tp;
! 551: for (cnt = 0, i = hash->curr; i < size; i += hash->step, cnt++)
! 552: *(tp++) = input[i];
! 553: /* calculate the starting point in the next input set */
! 554: hash->curr = (hash->step - (i - size)) % hash->step;
! 555: }
! 556: /* digest the data in block sizes */
! 557: for (n = 0; n < cnt; n += needed) {
! 558: avail = (cnt - n);
! 559: needed = hash->block - hash->digested;
! 560: dig = (avail < needed) ? avail : needed;
! 561: dst_sign_data(SIG_MODE_UPDATE, hash->key, &hash->ctx,
! 562: &tmp[n], dig, NULL, 0);
! 563: hash->digested += dig;
! 564: if (hash->digested >= hash->block)
! 565: force_hash(work, hash);
! 566: if (work->needed < work->filled) {
! 567: if (abuf)
! 568: SAFE_FREE2(abuf, tmp_size);
! 569: return (1);
! 570: }
! 571: }
! 572: if (tmp_size > 0)
! 573: SAFE_FREE2(abuf, tmp_size);
! 574: return (0);
! 575: }
! 576:
! 577: /*
! 578: * Copy data from INPUT for length SIZE into the work-block TMP.
! 579: * If we fill the work-block, digest it; then,
! 580: * if work-block needs more data, keep filling with the rest of the input.
! 581: */
! 582: static int
! 583: my_digest(dst_work *work, const u_char *input, unsigned size)
! 584: {
! 585:
! 586: int i, full = 0;
! 587: static unsigned counter;
! 588:
! 589: counter += size;
! 590: /* first do each one of the hashes */
! 591: for (i = 0; i < DST_NUM_HASHES && full == 0; i++)
! 592: full = do_hash(work, work->hash[i], input, size) +
! 593: do_hash(work, work->hash[i], (u_char *) &counter,
! 594: sizeof(counter));
! 595: /*
! 596: * if enough data has be generated do final operation on all hashes
! 597: * that have enough date for that
! 598: */
! 599: for (i = 0; full && (i < DST_NUM_HASHES); i++)
! 600: force_hash(work, work->hash[i]);
! 601:
! 602: return (full);
! 603: }
! 604:
! 605: /*
! 606: * this function gets some semi random data and sets that as an HMAC key
! 607: * If we get a valid key this function returns that key initialized
! 608: * otherwise it returns NULL;
! 609: */
! 610: static prand_hash *
! 611: get_hmac_key(int step, int block)
! 612: {
! 613:
! 614: u_char *buff;
! 615: int temp = 0, n = 0;
! 616: unsigned size = 70;
! 617: DST_KEY *new_key = NULL;
! 618: prand_hash *new = NULL;
! 619:
! 620: /* use key that is larger than digest algorithms (64) for key size */
! 621: buff = malloc(size);
! 622: if (buff == NULL)
! 623: return (NULL);
! 624: /* do not memset the allocated memory to get random bytes there */
! 625: /* time of day is somewhat random especially in the last bytes */
! 626: gettimeofday((struct timeval *) &buff[n], NULL);
! 627: n += sizeof(struct timeval);
! 628:
! 629: /* get some semi random stuff in here stir it with micro seconds */
! 630: if (n < size) {
! 631: temp = dst_s_quick_random((int) buff[n - 1]);
! 632: memcpy(&buff[n], &temp, sizeof(temp));
! 633: n += sizeof(temp);
! 634: }
! 635: /* get the pid of this process and its parent */
! 636: if (n < size) {
! 637: temp = (int) getpid();
! 638: memcpy(&buff[n], &temp, sizeof(temp));
! 639: n += sizeof(temp);
! 640: }
! 641: if (n < size) {
! 642: temp = (int) getppid();
! 643: memcpy(&buff[n], &temp, sizeof(temp));
! 644: n += sizeof(temp);
! 645: }
! 646: /* get the user ID */
! 647: if (n < size) {
! 648: temp = (int) getuid();
! 649: memcpy(&buff[n], &temp, sizeof(temp));
! 650: n += sizeof(temp);
! 651: }
! 652: #ifndef GET_HOST_ID_MISSING
! 653: if (n < size) {
! 654: temp = (int) gethostid();
! 655: memcpy(&buff[n], &temp, sizeof(temp));
! 656: n += sizeof(temp);
! 657: }
! 658: #endif
! 659: /* get some more random data */
! 660: if (n < size) {
! 661: temp = dst_s_quick_random((int) buff[n - 1]);
! 662: memcpy(&buff[n], &temp, sizeof(temp));
! 663: n += sizeof(temp);
! 664: }
! 665: /* covert this into a HMAC key */
! 666: new_key = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, buff, size);
! 667: SAFE_FREE(buff);
! 668:
! 669: /* get the control structure */
! 670: if ((new = malloc(sizeof(prand_hash))) == NULL)
! 671: return (NULL);
! 672: new->digested = new->curr = 0;
! 673: new->step = step;
! 674: new->block = block;
! 675: new->key = new_key;
! 676: if (dst_sign_data(SIG_MODE_INIT, new_key, &new->ctx, NULL, 0, NULL, 0))
! 677: return (NULL);
! 678:
! 679: return (new);
! 680: }
! 681:
! 682: /*
! 683: * own_random()
! 684: * This function goes out and from various sources tries to generate enough
! 685: * semi random data that a hash function can generate a random data.
! 686: * This function will iterate between the two main random source sources,
! 687: * information from programs and directories in random order.
! 688: * This function return the number of bytes added to the random output buffer.
! 689: */
! 690: static unsigned
! 691: own_random(dst_work *work)
! 692: {
! 693: int dir = 0, b;
! 694: int bytes, n, cmd = 0, dig = 0;
! 695: int start =0;
! 696: /*
! 697: * now get the initial seed to put into the quick random function from
! 698: * the address of the work structure
! 699: */
! 700: bytes = (int) getpid();
! 701: /*
! 702: * proceed while needed
! 703: */
! 704: while (work->filled < work->needed) {
! 705: EREPORT(("own_random r %08x b %6d t %6d f %6d\n",
! 706: ran_val, bytes, work->in_temp, work->filled));
! 707: /* pick a random number in the range of 0..7 based on that random number
! 708: * perform some operations that yield random data
! 709: */
! 710: start = work->filled;
! 711: n = (dst_s_quick_random(bytes) >> DST_SHIFT) & 0x07;
! 712: switch (n) {
! 713: case 0:
! 714: case 3:
! 715: if (sizeof(cmds) > 2 *sizeof(*cmds)) {
! 716: b = unix_cmd(work);
! 717: cmd += b;
! 718: }
! 719: break;
! 720:
! 721: case 1:
! 722: case 7:
! 723: if (sizeof(dirs) > 2 *sizeof(*dirs)) {
! 724: b = do_ls(work);
! 725: dir += b;
! 726: }
! 727: break;
! 728:
! 729: case 4:
! 730: case 5:
! 731: /* retry getting data from /dev/random */
! 732: b = get_dev_random(&work->output[work->filled],
! 733: work->needed - work->filled);
! 734: if (b > 0)
! 735: work->filled += b;
! 736: break;
! 737:
! 738: case 6:
! 739: if (sizeof(files) > 2 * sizeof(*files)) {
! 740: b = digest_file(work);
! 741: dig += b;
! 742: }
! 743: break;
! 744:
! 745: case 2:
! 746: default: /* to make sure we make some progress */
! 747: work->output[work->filled++] = 0xff &
! 748: dst_s_quick_random(bytes);
! 749: b = 1;
! 750: break;
! 751: }
! 752: if (b > 0)
! 753: bytes += b;
! 754: }
! 755: return (work->filled);
! 756: }
! 757:
! 758:
! 759: /*
! 760: * dst_s_random() This function will return the requested number of bytes
! 761: * of randomness to the caller it will use the best available sources of
! 762: * randomness.
! 763: * The current order is to use /dev/random, precalculated randomness, and
! 764: * finally use some system calls and programs to generate semi random data
! 765: * that is then digested to generate randomness.
! 766: * This function is thread safe as each thread uses its own context, but
! 767: * concurrent treads will affect each other as they update shared state
! 768: * information.
! 769: * It is strongly recommended that this function be called requesting a size
! 770: * that is not a multiple of the output of the hash function used.
! 771: *
! 772: * If /dev/random is not available this function is not suitable to generate
! 773: * large amounts of data, rather it is suitable to seed a pseudo-random
! 774: * generator
! 775: * Returns the number of bytes put in the output buffer
! 776: */
! 777: int
! 778: dst_s_random(u_char *output, unsigned size)
! 779: {
! 780: int n = 0, i;
! 781: unsigned s;
! 782: static u_char old_unused[DST_HASH_SIZE * DST_NUM_HASHES];
! 783: static unsigned unused = 0;
! 784:
! 785: if (size <= 0 || output == NULL)
! 786: return (0);
! 787:
! 788: if (size >= 2048)
! 789: return (-1);
! 790: /*
! 791: * Read from /dev/random
! 792: */
! 793: n = get_dev_random(output, size);
! 794: /*
! 795: * If old data is available and needed use it
! 796: */
! 797: if (n < size && unused > 0) {
! 798: unsigned need = size - n;
! 799: if (unused <= need) {
! 800: memcpy(output, old_unused, unused);
! 801: n += unused;
! 802: unused = 0;
! 803: } else {
! 804: memcpy(output, old_unused, need);
! 805: n += need;
! 806: unused -= need;
! 807: memcpy(old_unused, &old_unused[need], unused);
! 808: }
! 809: }
! 810: /*
! 811: * If we need more use the simulated randomness here.
! 812: */
! 813: if (n < size) {
! 814: dst_work *my_work = (dst_work *) malloc(sizeof(dst_work));
! 815: if (my_work == NULL)
! 816: return (n);
! 817: my_work->needed = size - n;
! 818: my_work->filled = 0;
! 819: my_work->output = (u_char *) malloc(my_work->needed +
! 820: DST_HASH_SIZE *
! 821: DST_NUM_HASHES);
! 822: my_work->file_digest = NULL;
! 823: if (my_work->output == NULL)
! 824: return (n);
! 825: memset(my_work->output, 0x0, my_work->needed);
! 826: /* allocate upto 4 different HMAC hash functions out of order */
! 827: #if DST_NUM_HASHES >= 3
! 828: my_work->hash[2] = get_hmac_key(3, DST_RANDOM_BLOCK_SIZE / 2);
! 829: #endif
! 830: #if DST_NUM_HASHES >= 2
! 831: my_work->hash[1] = get_hmac_key(7, DST_RANDOM_BLOCK_SIZE / 6);
! 832: #endif
! 833: #if DST_NUM_HASHES >= 4
! 834: my_work->hash[3] = get_hmac_key(5, DST_RANDOM_BLOCK_SIZE / 4);
! 835: #endif
! 836: my_work->hash[0] = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE);
! 837: if (my_work->hash[0] == NULL) /* if failure bail out */
! 838: return (n);
! 839: s = own_random(my_work);
! 840: /* if more generated than needed store it for future use */
! 841: if (s >= my_work->needed) {
! 842: EREPORT(("dst_s_random(): More than needed %d >= %d\n",
! 843: s, my_work->needed));
! 844: memcpy(&output[n], my_work->output, my_work->needed);
! 845: n += my_work->needed;
! 846: /* saving unused data for next time */
! 847: unused = s - my_work->needed;
! 848: memcpy(old_unused, &my_work->output[my_work->needed],
! 849: unused);
! 850: } else {
! 851: /* XXXX This should not happen */
! 852: EREPORT(("Not enough %d >= %d\n", s, my_work->needed));
! 853: memcpy(&output[n], my_work->output, s);
! 854: n += my_work->needed;
! 855: }
! 856:
! 857: /* delete the allocated work area */
! 858: for (i = 0; i < DST_NUM_HASHES; i++) {
! 859: dst_free_key(my_work->hash[i]->key);
! 860: SAFE_FREE(my_work->hash[i]);
! 861: }
! 862: SAFE_FREE(my_work->output);
! 863: SAFE_FREE(my_work);
! 864: }
! 865: return (n);
! 866: }
! 867:
! 868: /*
! 869: * A random number generator that is fast and strong
! 870: * this random number generator is based on HASHing data,
! 871: * the input to the digest function is a collection of <NUMBER_OF_COUNTERS>
! 872: * counters that is incremented between digest operations
! 873: * each increment operation amortizes to 2 bits changed in that value
! 874: * for 5 counters thus the input will amortize to have 10 bits changed
! 875: * The counters are initially set using the strong random function above
! 876: * the HMAC key is selected by the same method as the HMAC keys for the
! 877: * strong random function.
! 878: * Each set of counters is used for 2^25 operations
! 879: *
! 880: * returns the number of bytes written to the output buffer
! 881: * or negative number in case of error
! 882: */
! 883: int
! 884: dst_s_semi_random(u_char *output, unsigned size)
! 885: {
! 886: static u_int32_t counter[DST_NUMBER_OF_COUNTERS];
! 887: static u_char semi_old[DST_HASH_SIZE];
! 888: static int semi_loc = 0, cnt = 0;
! 889: static unsigned hb_size = 0;
! 890: static DST_KEY *my_key = NULL;
! 891: prand_hash *hash;
! 892: unsigned out = 0;
! 893: unsigned i;
! 894: int n;
! 895:
! 896: if (output == NULL || size <= 0)
! 897: return (-2);
! 898:
! 899: /* check if we need a new key */
! 900: if (my_key == NULL || cnt > (1 << 25)) { /* get HMAC KEY */
! 901: if (my_key)
! 902: my_key->dk_func->destroy(my_key);
! 903: if ((hash = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE)) == NULL)
! 904: return (0);
! 905: my_key = hash->key;
! 906: /* check if the key works stir the new key using some old random data */
! 907: hb_size = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
! 908: (u_char *) counter, sizeof(counter),
! 909: semi_old, sizeof(semi_old));
! 910: if (hb_size <= 0) {
! 911: EREPORT(("dst_s_semi_random() Sign of alg %d failed %d\n",
! 912: my_key->dk_alg, hb_size));
! 913: return (-1);
! 914: }
! 915: /* new set the counters to random values */
! 916: dst_s_random((u_char *) counter, sizeof(counter));
! 917: cnt = 0;
! 918: }
! 919: /* if old data around use it first */
! 920: if (semi_loc < hb_size) {
! 921: if (size <= hb_size - semi_loc) { /* need less */
! 922: memcpy(output, &semi_old[semi_loc], size);
! 923: semi_loc += size;
! 924: return (size); /* DONE */
! 925: } else {
! 926: out = hb_size - semi_loc;
! 927: memcpy(output, &semi_old[semi_loc], out);
! 928: semi_loc += out;
! 929: }
! 930: }
! 931: /* generate more random stuff */
! 932: while (out < size) {
! 933: /*
! 934: * modify at least one bit by incrementing at least one counter
! 935: * based on the last bit of the last counter updated update
! 936: * the next one.
! 937: * minimally this operation will modify at least 1 bit,
! 938: * amortized 2 bits
! 939: */
! 940: for (n = 0; n < DST_NUMBER_OF_COUNTERS; n++)
! 941: i = (int) counter[n]++;
! 942:
! 943: i = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
! 944: (u_char *) counter, hb_size,
! 945: semi_old, sizeof(semi_old));
! 946: if (i != hb_size)
! 947: EREPORT(("HMAC SIGNATURE FAILURE %d\n", i));
! 948: cnt++;
! 949: if (size - out < i) /* Not all data is needed */
! 950: semi_loc = i = size - out;
! 951: memcpy(&output[out], semi_old, i);
! 952: out += i;
! 953: }
! 954: return (out);
! 955: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>