/* * Copyright (c) 2011, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ANDROID_CHANGES #include #include #else #define AID_SYSTEM 1000 #endif #ifndef min #define min(a,b) (((a)>(b))?(b):(a)) #endif typedef unsigned char bool; #define TRUE 1 #define FALSE 0 #define RANDOM_DEVICE "/dev/random" #define RANDOM_DEVICE_HW "/dev/hw_random" /* The device (/dev/random) internal limits 4096 bits of entropy, 512 bytes */ #define MAX_ENT_POOL_BITS 4096 #define MAX_ENT_POOL_BYTES (MAX_ENT_POOL_BITS / 8) #define MAX_ENT_POOL_WRITES 128 /* write pool with smaller chunks */ ///* Burst-mode timeout in us (micro-seconds) */ //#define BURST_MODE_TIMEOUT 100000 /* 100ms */ ///* Idle-mode wait in us (micro-seconds) */ //#define IDLE_MODE_WAIT 10000 /* 10ms */ /* Buffer to hold hardware entropy bytes (this must be 2KB for FIPS testing */ #define MAX_BUFFER 2048 /* do not change this value */ static unsigned char databuf[MAX_BUFFER]; /* create buffer for FIPS testing */ static unsigned long buffsize; /* size of data in buffer */ static unsigned long curridx; /* position of current index */ /* Globals */ //static bool read_blocked = FALSE; //static pid_t qrngd_pid; /* User parameters */ struct user_options { char input_device_name[128]; char output_device_name[128]; bool run_as_daemon; }; /* Version number of this source */ #define APP_VERSION "1.01" #define APP_NAME "qrngd" const char *program_version = APP_NAME " " APP_VERSION "\n" "Copyright (c) 2011, The Linux Foundation. All rights reserved.\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"; const char *program_usage = "Usage: " APP_NAME " [OPTION...]\n" " -b background - become a daemon (default)\n" " -f foreground - do not fork and become a daemon\n" " -r hardware random input device (default: /dev/hw_random)\n" " -o system random output device (default: /dev/random)\n" " -h help (this page)\n"; /* Logging information */ enum log_level { DEBUG = 0, INFO = 1, WARNING = 2, ERROR = 3, FATAL = 4, LOG_MAX = 4, }; /* Logging function for outputing to stderr or log */ void log_print(int level, char *format, ...) { if (level >= 0 && level <= LOG_MAX) { #ifdef ANDROID_CHANGES static int levels[5] = { ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL }; va_list ap; va_start(ap, format); __android_log_vprint(levels[level], APP_NAME, format, ap); va_end(ap); #else static char *levels = "DIWEF"; va_list ap; fprintf(stderr, "%c: ", levels[level]); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputc('\n', stderr); #endif } } static void title(void) { printf("%s", program_version); } static void usage(void) { printf("%s", program_usage); } /* Parse command line parameters */ static int get_user_options(struct user_options *user_ops, int argc, char **argv) { int max_params = argc; int itr = 1; /* skip program name */ while (itr < max_params) { if (argv[itr][0] != '-') return -1; switch (argv[itr++][1]) { case 'b': user_ops->run_as_daemon = TRUE; break; case 'f': user_ops->run_as_daemon = FALSE; break; case 'r': if (itr < max_params) { if (strlen(argv[itr]) < sizeof(user_ops->input_device_name)) strcpy(user_ops->input_device_name, argv[itr++]); else return -1; break; } else return -1; case 'o': if (itr < max_params) { if (strlen(argv[itr]) < sizeof(user_ops->output_device_name)) strcpy(user_ops->output_device_name, argv[itr++]); else return -1; break; } else return -1; case 'h': return -1; default: fprintf(stderr, "ERROR: Bad option: '%s'\n", argv[itr-1]); return -1; } } return 0; } /* Only check FIPS 140-2 (Continuous Random Number Generator Test) */ static int fips_test(const unsigned char *buf, size_t size) { unsigned long *buff_ul = (unsigned long *) buf; size_t size_ul = size >> 2; /* convert byte to word size */ unsigned long last_value; unsigned int rnd_ctr[256]; int i; /* Continuous Random Number Generator Test */ last_value = *(buff_ul++); size_ul--; while (size_ul > 0) { if (*buff_ul == last_value) { log_print(ERROR, "ERROR: Bad word value from hardware."); return -1; } else last_value = *buff_ul; buff_ul++; size_ul--; } /* count each random number */ for (i = 0; i < size; ++i) { rnd_ctr[buf[i]]++; } /* check random numbers to make sure they are not bogus */ for (i = 0; i < 256; ++i) { if (rnd_ctr[i] == 0) { log_print(ERROR, "ERROR: Bad spectral random number sample."); return -1; } } return 0; } /* Read data from the hardware RNG source */ static int read_src(int fd, void *buf, size_t size) { size_t offset = 0; char *chr = (char *) buf; ssize_t ret; if (!size) return -1; do { ret = read(fd, chr + offset, size); /* any read failure is bad */ if (ret == -1) return -1; size -= ret; offset += ret; } while (size > 0); /* should have read in all of requested data */ if (size > 0) return -1; return 0; } /*Hold minimal permissions, so as to get IOCTL working*/ static int qrng_update_cap() { int retvalue = 0; struct __user_cap_header_struct header; struct __user_cap_data_struct cap; memset(&header, 0, sizeof(header)); memset(&cap, 0, sizeof(cap)); prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); if( 0 != setgid(AID_SYSTEM)){ fprintf(stderr, "setgid error\n"); return -1; } if( 0 != setuid(AID_SYSTEM)){ fprintf(stderr, "setuid error\n"); return -1; } header.version = _LINUX_CAPABILITY_VERSION; header.pid = 0; cap.effective = (1 << CAP_SYS_ADMIN) | (1 << CAP_NET_RAW); cap.permitted = cap.effective; cap.inheritable = 0; retvalue = capset(&header, &cap); if(retvalue != 0){ fprintf(stderr, "capset error\n"); return -1; } return 0; } /* The beginning of everything */ int main(int argc, char **argv) { struct user_options user_ops; /* holds user configuration data */ struct rand_pool_info *rand = NULL; /* structure to pass entropy (IOCTL) */ int random_fd = 0; /* output file descriptor */ int random_hw_fd = 0; /* input file descriptor */ int ent_count; /* current system entropy */ int write_size; /* max entropy data to pass */ struct pollfd fds[1]; /* used for polling file descriptor */ int ret; int exitval = 0; /* set default parameters */ user_ops.run_as_daemon = TRUE; strcpy(user_ops.input_device_name, RANDOM_DEVICE_HW); strcpy(user_ops.output_device_name, RANDOM_DEVICE); /* display application header */ title(); /* get user preferences */ ret = get_user_options(&user_ops, argc, argv); if (ret < 0) { usage(); exitval = 1; goto exit; } /* open hardware random device */ random_hw_fd = open(user_ops.input_device_name, O_RDONLY); if (random_hw_fd < 0) { fprintf(stderr, "Can't open hardware random device file %s\n", user_ops.input_device_name); exitval = 1; goto exit; } /* open random device */ random_fd = open(user_ops.output_device_name, O_RDWR); if (random_fd < 0) { fprintf(stderr, "Can't open random device file %s\n", user_ops.output_device_name); exitval = 1; goto exit; } /* allocate memory for ioctl data struct and buffer */ rand = malloc(sizeof(struct rand_pool_info) + MAX_ENT_POOL_WRITES); if (!rand) { fprintf(stderr, "Can't allocate memory\n"); exitval = 1; goto exit; } if(0 != qrng_update_cap()){ log_print(ERROR, "qrngd permission reset failed, exiting\n"); exitval = 1; goto exit; } /* setup poll() data */ memset(fds, 0 , sizeof(fds)); fds[0].fd = random_fd; fds[0].events = POLLOUT; /* run as daemon if requested to do so */ if (user_ops.run_as_daemon) { fprintf(stderr, "Starting daemon.\n"); if (daemon(0, 0) < 0) { fprintf(stderr, "can't daemonize: %s\n", strerror(errno)); exitval = 1; goto exit; } #ifndef ANDROID_CHANGES openlog(APP_NAME, 0, LOG_DAEMON); #endif } /* log message */ log_print(INFO, APP_NAME " has started:\n" "Reading device:'%s' updating entropy for device:'%s'", user_ops.input_device_name, user_ops.output_device_name); /* main loop to get data from hardware and feed RNG entropy pool */ while (1) { /* Check for empty buffer and fill with hardware random generated numbers */ if (buffsize == 0) { /* fill buffer with random data from hardware */ ret = read_src(random_hw_fd, databuf, MAX_BUFFER); if (ret < 0) { log_print(ERROR, "ERROR: Can't read from hardware source."); exitval = 1; goto exit; } /* run FIPS test on buffer, if buffer fails then ditch it and get new data */ ret = fips_test(databuf, MAX_BUFFER); if (ret < 0) { buffsize = 0; log_print(INFO, "ERROR: Failed FIPS test."); } /* everything good, reset buffer variables to indicate full buffer */ else { buffsize = MAX_BUFFER; curridx = 0; } } /* We should have data here, if not then something bad happened above and we should wait and try again */ if (buffsize == 0) { log_print(ERROR, "ERROR: Timeout getting valid random data from hardware."); usleep(100000); /* 100ms */ continue; } /* Get current entropy pool size in bits and convert to bytes */ if (ioctl(random_fd, RNDGETENTCNT, &ent_count) != 0) { log_print(ERROR, "ERROR: Can't read entropy count."); exitval = 1; goto exit; } /* convert entropy bits to bytes */ ent_count >>= 3; /* fill entropy pool */ write_size = min(buffsize, MAX_ENT_POOL_WRITES); /* Write some data to the device */ rand->entropy_count = write_size * 8; rand->buf_size = write_size; memcpy(rand->buf, &databuf[curridx], write_size); curridx += write_size; buffsize -= write_size; /* Issue the ioctl to increase the entropy count */ if (ioctl(random_fd, RNDADDENTROPY, rand) < 0) { log_print(ERROR,"ERROR: RNDADDENTROPY ioctl() failed."); exitval = 1; goto exit; } /* Wait if entropy pool is full */ ret = poll(fds, 1, -1); if (ret < 0) { log_print(ERROR,"ERROR: poll call failed."); /* wait if error */ usleep(100000); } } exit: /* free other resources */ if (rand) free(rand); if (random_fd) close(random_fd); if (random_hw_fd) close(random_hw_fd); return exitval; }