aboutsummaryrefslogblamecommitdiffstats
path: root/common.c
blob: 45a764b7ae81d0158547368ca00fad04811c2656 (plain) (tree)



































































































































































































































                                                                                               
 

                                                                                                 








                                                          

                                                                                      







                              
                                                                                                    


                
























































                                                                                                        


                

                                                                                                               

                
/* 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);
}