1/* vi: set sw=4 ts=4: */
2/*
3 *  Copyright (C) 2003 Glenn L. McGrath
4 *  Copyright (C) 2003-2004 Erik Andersen
5 *
6 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
7 */
8
9#include "libbb.h"
10
11typedef enum { HASH_SHA1, HASH_MD5 } hash_algo_t;
12
13#define FLAG_SILENT	1
14#define FLAG_CHECK	2
15#define FLAG_WARN	4
16
17/* This might be useful elsewhere */
18static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
19				unsigned hash_length)
20{
21	/* xzalloc zero-terminates */
22	char *hex_value = xzalloc((hash_length * 2) + 1);
23	bin2hex(hex_value, (char*)hash_value, hash_length);
24	return hex_value;
25}
26
27static uint8_t *hash_file(const char *filename, hash_algo_t hash_algo)
28{
29	int src_fd, hash_len, count;
30	union _ctx_ {
31		sha1_ctx_t sha1;
32		md5_ctx_t md5;
33	} context;
34	uint8_t *hash_value = NULL;
35	RESERVE_CONFIG_UBUFFER(in_buf, 4096);
36	void (*update)(const void*, size_t, void*);
37	void (*final)(void*, void*);
38
39	src_fd = STDIN_FILENO;
40	if (NOT_LONE_DASH(filename)) {
41		src_fd = open_or_warn(filename, O_RDONLY);
42		if (src_fd < 0) {
43			return NULL;
44		}
45	}
46
47	/* figure specific hash algorithims */
48	if (ENABLE_MD5SUM && hash_algo==HASH_MD5) {
49		md5_begin(&context.md5);
50		update = (void (*)(const void*, size_t, void*))md5_hash;
51		final = (void (*)(void*, void*))md5_end;
52		hash_len = 16;
53	} else if (ENABLE_SHA1SUM && hash_algo==HASH_SHA1) {
54		sha1_begin(&context.sha1);
55		update = (void (*)(const void*, size_t, void*))sha1_hash;
56		final = (void (*)(void*, void*))sha1_end;
57		hash_len = 20;
58	} else {
59		bb_error_msg_and_die("algorithm not supported");
60	}
61
62	while (0 < (count = safe_read(src_fd, in_buf, 4096))) {
63		update(in_buf, count, &context);
64	}
65
66	if (count == 0) {
67		final(in_buf, &context);
68		hash_value = hash_bin_to_hex(in_buf, hash_len);
69	}
70
71	RELEASE_CONFIG_BUFFER(in_buf);
72
73	if (src_fd != STDIN_FILENO) {
74		close(src_fd);
75	}
76
77	return hash_value;
78}
79
80int md5_sha1_sum_main(int argc, char **argv);
81int md5_sha1_sum_main(int argc, char **argv)
82{
83	int return_value = EXIT_SUCCESS;
84	uint8_t *hash_value;
85	unsigned flags;
86	hash_algo_t hash_algo = ENABLE_MD5SUM
87		? (ENABLE_SHA1SUM ? (applet_name[0] == 'm' ? HASH_MD5 : HASH_SHA1) : HASH_MD5)
88		: HASH_SHA1;
89
90	if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK)
91		flags = getopt32(argv, "scw");
92	else optind = 1;
93
94	if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) {
95		if (flags & FLAG_SILENT) {
96			bb_error_msg_and_die
97				("-%c is meaningful only when verifying checksums", 's');
98		} else if (flags & FLAG_WARN) {
99			bb_error_msg_and_die
100				("-%c is meaningful only when verifying checksums", 'w');
101		}
102	}
103
104	if (argc == optind) {
105		argv[argc++] = (char*)"-";
106	}
107
108	if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) {
109		FILE *pre_computed_stream;
110		int count_total = 0;
111		int count_failed = 0;
112		char *file_ptr = argv[optind];
113		char *line;
114
115		if (optind + 1 != argc) {
116			bb_error_msg_and_die
117				("only one argument may be specified when using -c");
118		}
119
120		pre_computed_stream = stdin;
121		if (NOT_LONE_DASH(file_ptr)) {
122			pre_computed_stream = xfopen(file_ptr, "r");
123		}
124
125		while ((line = xmalloc_getline(pre_computed_stream)) != NULL) {
126			char *filename_ptr;
127
128			count_total++;
129			filename_ptr = strstr(line, "  ");
130			/* handle format for binary checksums */
131			if (filename_ptr == NULL) {
132				filename_ptr = strstr(line, " *");
133			}
134			if (filename_ptr == NULL) {
135				if (flags & FLAG_WARN) {
136					bb_error_msg("invalid format");
137				}
138				count_failed++;
139				return_value = EXIT_FAILURE;
140				free(line);
141				continue;
142			}
143			*filename_ptr = '\0';
144			filename_ptr += 2;
145
146			hash_value = hash_file(filename_ptr, hash_algo);
147
148			if (hash_value && (strcmp((char*)hash_value, line) == 0)) {
149				if (!(flags & FLAG_SILENT))
150					printf("%s: OK\n", filename_ptr);
151			} else {
152				if (!(flags & FLAG_SILENT))
153					printf("%s: FAILED\n", filename_ptr);
154				count_failed++;
155				return_value = EXIT_FAILURE;
156			}
157			/* possible free(NULL) */
158			free(hash_value);
159			free(line);
160		}
161		if (count_failed && !(flags & FLAG_SILENT)) {
162			bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
163						 count_failed, count_total);
164		}
165		/*
166		if (fclose_if_not_stdin(pre_computed_stream) == EOF) {
167			bb_perror_msg_and_die("cannot close file %s", file_ptr);
168		}
169		*/
170	} else {
171		while (optind < argc) {
172			char *file_ptr = argv[optind++];
173
174			hash_value = hash_file(file_ptr, hash_algo);
175			if (hash_value == NULL) {
176				return_value = EXIT_FAILURE;
177			} else {
178				printf("%s  %s\n", hash_value, file_ptr);
179				free(hash_value);
180			}
181		}
182	}
183	return return_value;
184}
185