{md5,shaN}sum: make -c EMPTY fail
[oweals/busybox.git] / coreutils / md5_sha1_sum.c
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 GPLv2 or later, see file LICENSE in this source tree.
7  */
8
9 //usage:#define md5sum_trivial_usage
10 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
11 //usage:#define md5sum_full_usage "\n\n"
12 //usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums"
13 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
14 //usage:     "\n        -c      Check sums against list in FILEs"
15 //usage:     "\n        -s      Don't output anything, status code shows success"
16 //usage:     "\n        -w      Warn about improperly formatted checksum lines"
17 //usage:        )
18 //usage:
19 //usage:#define md5sum_example_usage
20 //usage:       "$ md5sum < busybox\n"
21 //usage:       "6fd11e98b98a58f64ff3398d7b324003\n"
22 //usage:       "$ md5sum busybox\n"
23 //usage:       "6fd11e98b98a58f64ff3398d7b324003  busybox\n"
24 //usage:       "$ md5sum -c -\n"
25 //usage:       "6fd11e98b98a58f64ff3398d7b324003  busybox\n"
26 //usage:       "busybox: OK\n"
27 //usage:       "^D\n"
28 //usage:
29 //usage:#define sha1sum_trivial_usage
30 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
31 //usage:#define sha1sum_full_usage "\n\n"
32 //usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums"
33 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
34 //usage:     "\n        -c      Check sums against list in FILEs"
35 //usage:     "\n        -s      Don't output anything, status code shows success"
36 //usage:     "\n        -w      Warn about improperly formatted checksum lines"
37 //usage:        )
38 //usage:
39 //usage:#define sha256sum_trivial_usage
40 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
41 //usage:#define sha256sum_full_usage "\n\n"
42 //usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA256 checksums"
43 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
44 //usage:     "\n        -c      Check sums against list in FILEs"
45 //usage:     "\n        -s      Don't output anything, status code shows success"
46 //usage:     "\n        -w      Warn about improperly formatted checksum lines"
47 //usage:        )
48 //usage:
49 //usage:#define sha512sum_trivial_usage
50 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
51 //usage:#define sha512sum_full_usage "\n\n"
52 //usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA512 checksums"
53 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
54 //usage:     "\n        -c      Check sums against list in FILEs"
55 //usage:     "\n        -s      Don't output anything, status code shows success"
56 //usage:     "\n        -w      Warn about improperly formatted checksum lines"
57 //usage:        )
58 //usage:
59 //usage:#define sha3sum_trivial_usage
60 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
61 //usage:#define sha3sum_full_usage "\n\n"
62 //usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3-512 checksums"
63 //usage:        IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
64 //usage:     "\n        -c      Check sums against list in FILEs"
65 //usage:     "\n        -s      Don't output anything, status code shows success"
66 //usage:     "\n        -w      Warn about improperly formatted checksum lines"
67 //usage:        )
68
69 //FIXME: GNU coreutils 8.25 has no -s option, it has only these two long opts:
70 // --quiet   don't print OK for each successfully verified file
71 // --status  don't output anything, status code shows success
72
73 #include "libbb.h"
74
75 /* This is a NOEXEC applet. Be very careful! */
76
77 enum {
78         /* 4th letter of applet_name is... */
79         HASH_MD5 = 's', /* "md5>s<um" */
80         HASH_SHA1 = '1',
81         HASH_SHA256 = '2',
82         HASH_SHA3 = '3',
83         HASH_SHA512 = '5',
84 };
85
86 #define FLAG_SILENT  1
87 #define FLAG_CHECK   2
88 #define FLAG_WARN    4
89
90 /* This might be useful elsewhere */
91 static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
92                                 unsigned hash_length)
93 {
94         /* xzalloc zero-terminates */
95         char *hex_value = xzalloc((hash_length * 2) + 1);
96         bin2hex(hex_value, (char*)hash_value, hash_length);
97         return (unsigned char *)hex_value;
98 }
99
100 static uint8_t *hash_file(const char *filename)
101 {
102         int src_fd, hash_len, count;
103         union _ctx_ {
104                 sha3_ctx_t sha3;
105                 sha512_ctx_t sha512;
106                 sha256_ctx_t sha256;
107                 sha1_ctx_t sha1;
108                 md5_ctx_t md5;
109         } context;
110         uint8_t *hash_value;
111         void FAST_FUNC (*update)(void*, const void*, size_t);
112         void FAST_FUNC (*final)(void*, void*);
113         char hash_algo;
114
115         src_fd = open_or_warn_stdin(filename);
116         if (src_fd < 0) {
117                 return NULL;
118         }
119
120         hash_algo = applet_name[3];
121
122         /* figure specific hash algorithms */
123         if (ENABLE_MD5SUM && hash_algo == HASH_MD5) {
124                 md5_begin(&context.md5);
125                 update = (void*)md5_hash;
126                 final = (void*)md5_end;
127                 hash_len = 16;
128         } else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
129                 sha1_begin(&context.sha1);
130                 update = (void*)sha1_hash;
131                 final = (void*)sha1_end;
132                 hash_len = 20;
133         } else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
134                 sha256_begin(&context.sha256);
135                 update = (void*)sha256_hash;
136                 final = (void*)sha256_end;
137                 hash_len = 32;
138         } else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
139                 sha512_begin(&context.sha512);
140                 update = (void*)sha512_hash;
141                 final = (void*)sha512_end;
142                 hash_len = 64;
143         } else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
144                 sha3_begin(&context.sha3);
145                 update = (void*)sha3_hash;
146                 final = (void*)sha3_end;
147                 hash_len = 64;
148         } else {
149                 xfunc_die(); /* can't reach this */
150         }
151
152         {
153                 RESERVE_CONFIG_UBUFFER(in_buf, 4096);
154                 while ((count = safe_read(src_fd, in_buf, 4096)) > 0) {
155                         update(&context, in_buf, count);
156                 }
157                 hash_value = NULL;
158                 if (count < 0)
159                         bb_perror_msg("can't read '%s'", filename);
160                 else /* count == 0 */ {
161                         final(&context, in_buf);
162                         hash_value = hash_bin_to_hex(in_buf, hash_len);
163                 }
164                 RELEASE_CONFIG_BUFFER(in_buf);
165         }
166
167         if (src_fd != STDIN_FILENO) {
168                 close(src_fd);
169         }
170
171         return hash_value;
172 }
173
174 int md5_sha1_sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
175 int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
176 {
177         int return_value = EXIT_SUCCESS;
178         unsigned flags;
179
180         if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) {
181                 /* -s and -w require -c */
182                 opt_complementary = "s?c:w?c";
183                 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */
184                 flags = getopt32(argv, "scwbt");
185                 argv += optind;
186                 //argc -= optind;
187         } else {
188                 argv += 1;
189                 //argc -= 1;
190         }
191         if (!*argv)
192                 *--argv = (char*)"-";
193
194         do {
195                 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) {
196                         FILE *pre_computed_stream;
197                         char *line;
198                         int count_total = 0;
199                         int count_failed = 0;
200
201                         pre_computed_stream = xfopen_stdin(*argv);
202
203                         while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) {
204                                 uint8_t *hash_value;
205                                 char *filename_ptr;
206
207                                 count_total++;
208                                 filename_ptr = strstr(line, "  ");
209                                 /* handle format for binary checksums */
210                                 if (filename_ptr == NULL) {
211                                         filename_ptr = strstr(line, " *");
212                                 }
213                                 if (filename_ptr == NULL) {
214                                         if (flags & FLAG_WARN) {
215                                                 bb_error_msg("invalid format");
216                                         }
217                                         count_failed++;
218                                         return_value = EXIT_FAILURE;
219                                         free(line);
220                                         continue;
221                                 }
222                                 *filename_ptr = '\0';
223                                 filename_ptr += 2;
224
225                                 hash_value = hash_file(filename_ptr);
226
227                                 if (hash_value && (strcmp((char*)hash_value, line) == 0)) {
228                                         if (!(flags & FLAG_SILENT))
229                                                 printf("%s: OK\n", filename_ptr);
230                                 } else {
231                                         if (!(flags & FLAG_SILENT))
232                                                 printf("%s: FAILED\n", filename_ptr);
233                                         count_failed++;
234                                         return_value = EXIT_FAILURE;
235                                 }
236                                 /* possible free(NULL) */
237                                 free(hash_value);
238                                 free(line);
239                         }
240                         if (count_failed && !(flags & FLAG_SILENT)) {
241                                 bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
242                                                 count_failed, count_total);
243                         }
244                         if (count_total == 0) {
245                                 return_value = EXIT_FAILURE;
246                                 /*
247                                  * md5sum from GNU coreutils 8.25 says:
248                                  * md5sum: <FILE>: no properly formatted MD5 checksum lines found
249                                  */
250                                 bb_error_msg("%s: no checksum lines found", *argv);
251                         }
252                         fclose_if_not_stdin(pre_computed_stream);
253                 } else {
254                         uint8_t *hash_value = hash_file(*argv);
255                         if (hash_value == NULL) {
256                                 return_value = EXIT_FAILURE;
257                         } else {
258                                 printf("%s  %s\n", hash_value, *argv);
259                                 free(hash_value);
260                         }
261                 }
262         } while (*++argv);
263
264         return return_value;
265 }