From da69892f56a081cb4cd65eb0a8065783d9473bd3 Mon Sep 17 00:00:00 2001 From: "Alex Xu (Hello71)" Date: Sun, 26 Aug 2018 19:41:23 -0400 Subject: Refactor. --- src/load.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 src/load.c (limited to 'src/load.c') diff --git a/src/load.c b/src/load.c new file mode 100644 index 0000000..e04f5e4 --- /dev/null +++ b/src/load.c @@ -0,0 +1,257 @@ +#include "config.h" + +#include "id.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RNDADDENTROPY _IOW( 'R', 0x03, int [2] ) + +/* random pool is always the same size, so use a fixed size array */ +struct rand_pool_info_ { + int entropy_count; + int buf_size; + uint32_t buf[RAND_POOL_SIZE / sizeof(uint32_t)]; +}; + +bool noperms; + +static inline signed char hexchr2int(char c) { + /* No, nobody uses EBCDIC anymore. */ + return (c >= '0' && c <= '9') ? (c - '0') : + (c >= 'a' && c <= 'f') ? (c - 'a' + 10) : + (c >= 'A' && c <= 'F') ? (c - 'A' + 10) : + -1; +} + +/** + * Decode hex. + * + * \param dest where to store the decoded data + * \param size the maximum number of bytes to decode + * \param src the hex-encoded data + * + * \return 0 if an error occurred, otherwise size + */ +static size_t hex2mem(unsigned char *dest, size_t size, const char *src) { + size_t i; + for (i = 0; i < size; i++) { + int n1 = hexchr2int(src[2*i]); + if (n1 < 0) return 0; + int n2 = hexchr2int(src[2*i+1]); + if (n2 < 0) return 0; + dest[i] = (unsigned char)(n1 << 4 | n2); + } + return i; +} + +static bool match_cmd_with_arg(const char *cmd, const char *my_cmd, char **arg) { + if (!streq(cmd, my_cmd)) + return false; + + *arg = strtok(NULL, " \t"); + if (!arg) { + fprintf(stderr, "error parsing seed file: expected argument to '%s'\n", my_cmd); + return false; + } + + return true; +} + +static bool hash_match(const unsigned char salt[static SALT_LEN], const void *buf, size_t buflen, const char theirhash[static HASH_STR_LEN]) { + if (streq(theirhash, "none")) { + return buf == NULL; + } + if (!buf) + return false; + unsigned char mydigest[HASH_LEN]; + hash(salt, mydigest, buf, buflen); + unsigned char theirdigest[HASH_LEN]; + if (hex2mem(theirdigest, HASH_LEN, theirhash) != HASH_LEN) { + fputs("error decoding hex hash\n", stderr); + return false; + } + if (memcmp(mydigest, theirdigest, HASH_LEN)) { + fputs("hash does not match\n", stderr); + return false; + } + return true; +} + +bool load(const char *seed_path) { + struct random_seed rs = {0}; + + if (streq(seed_path, "-")) { + rs.file = stdin; + } else { + rs.file = fopen(seed_path, "r"); + if (!rs.file) { + perror("error opening seed file"); + exit(1); + } + } + + bool credit_entropy = true; + + struct rand_pool_info_ rpi = { + .entropy_count = RAND_POOL_SIZE * CHAR_BIT, + .buf_size = RAND_POOL_SIZE, + .buf = { 0 } + }; + + uint64_t linenum = 0; + char *line = NULL; + size_t n = 0; + bool done = false; + + if (fread(&rpi.buf, 1, RAND_POOL_SIZE, rs.file) != RAND_POOL_SIZE) { + if (feof(rs.file)) { + fputs("premature EOF on seed file\n", stderr); + } else if (ferror(rs.file)) { + fputs("error reading from seed file\n", stderr); + } + // else: we got signalled, so return to main loop to quit or whatever + return false; + } + + unsigned char *salt = (unsigned char *)rpi.buf; + + while (1) { + errno = 0; + ssize_t line_length = getline(&line, &n, rs.file); + if (line_length == -1) { + if (errno) { + perror("error reading from seed file"); + credit_entropy = false; + } + break; + } + + linenum++; + + if (linenum == 1) { + if (streq(line, MAGIC)) { + continue; + } else { + fputs("error parsing seed file: invalid magic\n", stderr); + credit_entropy = false; + break; + } + } + + char *nul = memchr(line, '\0', (size_t)line_length); + if (nul) { + fprintf(stderr, "error parsing seed file: encountered NUL byte in commands line %zu char %zu\n", linenum, nul - line + 1); + credit_entropy = false; + break; + } + + char *cmd = strtok(line, " \t\n"); + if (!cmd) + /* empty line */ + continue; + + if (streq(cmd, "done")) { + done = true; + break; + } + + char *arg; + + if (match_cmd_with_arg(cmd, "machine-id", &arg)) { + const char *machine_id = get_machine_id(&machine_id); + if (!machine_id) { + credit_entropy = false; + break; + } + return hash_match(salt, machine_id, strlen(machine_id), arg); + } + + if (match_cmd_with_arg(cmd, "fs-id", &arg)) { + fsid_t fs_id; + credit_entropy = hash_match( + salt, + get_fs_id(&fs_id, fileno(rs.file)) ? &fs_id : NULL, + sizeof(fs_id), + arg + ); + break; + } + +#if defined(HAVE_LIBUDEV) || defined(HAVE_UTIL_LINUX) + if (match_cmd_with_arg(cmd, "fs-uuid", &arg)) { + const char *fs_uuid = get_fs_uuid(&rs); + credit_entropy = hash_match(salt, fs_uuid, fs_uuid ? strlen(fs_uuid) : 0, arg); + break; + } +#else + if (streq(cmd, "fs-uuid")) { + fputs("error: fs-uuid disabled at compile time\n", stderr); + credit_entropy = false; + break; + } +#endif + +#ifdef HAVE_LIBUDEV + if (match_cmd_with_arg(cmd, "drive-id", &arg)) { + const char *drive_id = get_drive_id(&rs); + credit_entropy = hash_match(salt, drive_id, drive_id ? strlen(drive_id) : 0, arg); + break; + } +#else + if (streq(cmd, "drive-id")) { + fputs("error: drive-id disabled at compile time\n", stderr); + credit_entropy = false; + break; + } +#endif + + fprintf(stderr, "error parsing seed file: unsupported command: %s\n", cmd); + credit_entropy = false; + break; + } + + if (!linenum) { + fputs("seed file has no commands, assuming legacy format. disabling entropy credit\n", stderr); + credit_entropy = false; + } + + if (credit_entropy && !done) { + fputs("missing done command, random seed file probably truncated. disabling entropy credit\n", stderr); + credit_entropy = false; + } + + int random_fd = open("/dev/random", O_RDWR, 0); + if (random_fd == -1) { + perror("error opening /dev/random"); + exit(1); + } + + if (credit_entropy) { + if (ioctl(random_fd, RNDADDENTROPY, &rpi) == -1) { + perror("ioctl(RNDADDENTROPY)"); + if (errno == EPERM) { + fputs("Continuing without crediting entropy.\n", stderr); + noperms = true; + } + credit_entropy = false; + } + } + + if (!credit_entropy) { + if (write(random_fd, &rpi.buf, RAND_POOL_SIZE) != RAND_POOL_SIZE) { + fputs("error writing entropy to /dev/random\n", stderr); + exit(1); + } + } + + return credit_entropy; +} + -- cgit v1.2.3-54-g00ecf