/* See LICENSE file for copyright and license details. */ #include "common.h" void * erealloc(void *ptr, size_t n) { ptr = realloc(ptr, n); if (!ptr) { fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); exit(2); } return ptr; } void * emalloc(size_t n) { void *ptr = malloc(n); if (!ptr) { fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); exit(2); } return ptr; } static int parse_fd(const char *name) { long int num; char *end; if (!isdigit(*name)) return -1; errno = 0; num = strtol(name, &end, 10); if (num > INT_MAX || *end || errno) return -1; return (int)num; } int open_file(const char *path, int *closep) { int fd = -1; *closep = 0; if (!strcmp(path, "-")) fd = STDIN_FILENO; else if (!strcmp(path, "/dev/stdin")) fd = STDIN_FILENO; else if (!strcmp(path, "/dev/stdout")) fd = STDOUT_FILENO; else if (!strcmp(path, "/dev/stderr")) fd = STDERR_FILENO; else if (!strncmp(path, "/dev/fd/", sizeof("/dev/fd/") - 1)) fd = parse_fd(&path[sizeof("/dev/fd/") - 1]); else if (!strncmp(path, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) fd = parse_fd(&path[sizeof("/proc/self/fd/") - 1]); if (fd < 0) { fd = open(path, O_RDONLY); if (fd < 0) return -1; *closep = 1; } return fd; } static int check_and_print_file(const char *path, size_t hashlen, int decode_hex, char *expected) { unsigned char *hash; int r, fd, close_fd; fd = open_file(path, &close_fd); if (fd < 0) { if (errno != ENOENT) fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); missing: printf("%s: Missing\n", path); return -1; } hash = hashlen ? emalloc(hashlen / 8) : NULL; r = hash_fd(fd, path, decode_hex, hash); if (close_fd) close(fd); if (r < 0) { free(hash); goto missing; } libblake_decode_hex(expected, hashlen / 4, expected, &(int){0}); if (!memcmp(hash, expected, hashlen / 8)) { free(hash); printf("%s: OK\n", path); return 0; } else { free(hash); printf("%s: Fail\n", path); return -1; } } int check_and_print(const char *path, size_t hashlen, int decode_hex, char newline) { int fd, close_fd, status = 0; char *buf = NULL; size_t size = 0; size_t len = 0; ssize_t r; size_t i, j, k; fd = open_file(path, &close_fd); if (fd < 0) { fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); exit(2); } for (;;) { if (len == size) buf = erealloc(buf, size += 8 << 10); r = read(fd, &buf[len], size - len); if (r > 0) { len += (size_t)r; } else if (!r) { break; } else if (errno == EINTR) { continue; } else { fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); exit(2); } } buf = erealloc(buf, len + 1); buf[len] = '\0'; if (newline) { for (i = 0; i < len; i = k + 1) { while (isspace(buf[i])) i++; for (j = i; j - i < hashlen / 4; j++) if (!isxdigit(buf[j])) goto corrupt; if (j == len || !isblank(buf[j])) goto corrupt; buf[j] = '\0'; j++; while (isblank(buf[j])) j++; if (!buf[j]) goto corrupt; for (k = j; buf[k] && buf[k] != newline;) k++; buf[k] = '\0'; status |= check_and_print_file(&buf[j], hashlen, decode_hex, &buf[i]); } } else { for (i = 0; i < len; i = k + 1) { for (j = i; j - i < hashlen / 4; j++) if (!isxdigit(buf[j])) goto corrupt; if (buf[j + 0] != ' ' || buf[j + 1] != ' ') goto corrupt; buf[j] = '\0'; j += 2; k = j + strlen(&buf[j]); status |= check_and_print_file(&buf[j], hashlen, decode_hex, &buf[i]); } } if (close_fd) close(fd); return status; corrupt: fprintf(stderr, "%s: %s: invalid file content\n", argv0, path); exit(2); } static int hash_file(const char *path, int decode_hex, unsigned char hash[]) { int ret, fd, close_fd; fd = open_file(path, &close_fd); if (fd < 0) { fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); return -1; } ret = hash_fd(fd, path, decode_hex, hash); if (close_fd) close(fd); return ret; } int hash_and_print(const char *path, size_t hashlen, int decode_hex, char newline, int output_case) { unsigned char *hash; char *hex; hash = hashlen ? emalloc(hashlen / 8) : 0; hex = emalloc(hashlen / 4 + 1); if (hash_file(path, decode_hex, hash)) { free(hash); free(hex); return -1; } if (output_case < 0) { fwrite(hash, 1, hashlen / 8, stdout); } else { libblake_encode_hex(hash, hashlen / 8, hex, output_case); printf("%s %s%c", hex, path, newline); } free(hash); free(hex); return 0; } static void parse_salt_or_pepper(uint_least8_t *out, const char *s, size_t required_length, const char *type) { size_t i; for (i = 0; i < required_length; i++, s = &s[2]) { if (!s[0] || !s[1]) goto too_short; if (!isxdigit(s[0]) || !isxdigit(s[1])) goto not_hexadecimal; out[i] = (uint_least8_t)((((s[0] & 15) + (s[0] > '9' ? 9 : 0)) << 4) | (s[1] & 15) + (s[1] > '9' ? 9 : 0)); } if (*s) goto too_long; return; not_hexadecimal: fprintf(stderr, "%s: specified %s contains non-hexadecimal-digit character\n", argv0, type); exit(2); too_short: fprintf(stderr, "%s: specified %s is shorter than expected, should be %zu hexadecimal digits\n", argv0, type, required_length * 2); exit(2); too_long: fprintf(stderr, "%s: specified %s is longer than expected, should be %zu hexadecimal digits\n", argv0, type, required_length * 2); exit(2); } void parse_salt(uint_least8_t *salt, const char *s, size_t required_length) { parse_salt_or_pepper(salt, s, required_length, "salt"); } void parse_pepper(uint_least8_t *pepper, const char *s, size_t required_length) { parse_salt_or_pepper(pepper, s, required_length, "pepper"); } size_t parse_key(uint_least8_t *key, const char *s, size_t maximum_length) { size_t i; if (!*s) goto empty_key; for (i = 0; i < maximum_length; i++, s = &s[2]) { if (!s[0]) break; if (!s[1]) goto odd_length; if (!isxdigit(s[0]) || !isxdigit(s[1])) goto not_hexadecimal; key[i] = (uint_least8_t)((((s[0] & 15) + (s[0] > '9' ? 9 : 0)) << 4) | (s[1] & 15) + (s[1] > '9' ? 9 : 0)); } if (*s) goto too_long; return i; empty_key: fprintf(stderr, "%s: specified key is empty\n", argv0); exit(2); odd_length: fprintf(stderr, "%s: specified key contains an odd number of hexadecimal digits\n", argv0); exit(2); not_hexadecimal: fprintf(stderr, "%s: specified key contains non-hexadecimal-digit character\n", argv0); exit(2); too_long: fprintf(stderr, "%s: specified key is longer than allowed, should be at most %zu hexadecimal digits\n", argv0, maximum_length * 2); exit(2); }