GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: md5.c,v 1.92 2017/09/11 16:35:38 millert Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* Copyright (c) 2001,2003,2005-2007,2010,2013,2014 |
||
5 |
* Todd C. Miller <Todd.Miller@courtesan.com> |
||
6 |
* |
||
7 |
* Permission to use, copy, modify, and distribute this software for any |
||
8 |
* purpose with or without fee is hereby granted, provided that the above |
||
9 |
* copyright notice and this permission notice appear in all copies. |
||
10 |
* |
||
11 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||
12 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||
13 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||
14 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||
15 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||
16 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||
17 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||
18 |
* |
||
19 |
* Sponsored in part by the Defense Advanced Research Projects |
||
20 |
* Agency (DARPA) and Air Force Research Laboratory, Air Force |
||
21 |
* Materiel Command, USAF, under agreement number F39502-99-1-0512. |
||
22 |
*/ |
||
23 |
|||
24 |
#include <sys/types.h> |
||
25 |
#include <sys/time.h> |
||
26 |
#include <sys/queue.h> |
||
27 |
#include <sys/resource.h> |
||
28 |
#include <netinet/in.h> |
||
29 |
#include <ctype.h> |
||
30 |
#include <err.h> |
||
31 |
#include <fcntl.h> |
||
32 |
#include <resolv.h> |
||
33 |
#include <stdio.h> |
||
34 |
#include <stdlib.h> |
||
35 |
#include <string.h> |
||
36 |
#include <limits.h> |
||
37 |
#include <time.h> |
||
38 |
#include <unistd.h> |
||
39 |
#include <errno.h> |
||
40 |
|||
41 |
#include <md5.h> |
||
42 |
#include <rmd160.h> |
||
43 |
#include <sha1.h> |
||
44 |
#include <sha2.h> |
||
45 |
#include <crc.h> |
||
46 |
|||
47 |
#define STYLE_MD5 0 |
||
48 |
#define STYLE_CKSUM 1 |
||
49 |
#define STYLE_TERSE 2 |
||
50 |
|||
51 |
#define MAX_DIGEST_LEN 128 |
||
52 |
|||
53 |
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
||
54 |
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
||
55 |
|||
56 |
union ANY_CTX { |
||
57 |
#if !defined(SHA2_ONLY) |
||
58 |
CKSUM_CTX cksum; |
||
59 |
MD5_CTX md5; |
||
60 |
RMD160_CTX rmd160; |
||
61 |
SHA1_CTX sha1; |
||
62 |
#endif /* !defined(SHA2_ONLY) */ |
||
63 |
SHA2_CTX sha2; |
||
64 |
}; |
||
65 |
|||
66 |
struct hash_function { |
||
67 |
const char *name; |
||
68 |
size_t digestlen; |
||
69 |
int style; |
||
70 |
int base64; |
||
71 |
void *ctx; /* XXX - only used by digest_file() */ |
||
72 |
void (*init)(void *); |
||
73 |
void (*update)(void *, const unsigned char *, size_t); |
||
74 |
void (*final)(unsigned char *, void *); |
||
75 |
char * (*end)(void *, char *); |
||
76 |
TAILQ_ENTRY(hash_function) tailq; |
||
77 |
} functions[] = { |
||
78 |
#if !defined(SHA2_ONLY) |
||
79 |
{ |
||
80 |
"CKSUM", |
||
81 |
CKSUM_DIGEST_LENGTH, |
||
82 |
STYLE_CKSUM, |
||
83 |
-1, |
||
84 |
NULL, |
||
85 |
(void (*)(void *))CKSUM_Init, |
||
86 |
(void (*)(void *, const unsigned char *, size_t))CKSUM_Update, |
||
87 |
(void (*)(unsigned char *, void *))CKSUM_Final, |
||
88 |
(char *(*)(void *, char *))CKSUM_End |
||
89 |
}, |
||
90 |
{ |
||
91 |
"MD5", |
||
92 |
MD5_DIGEST_LENGTH, |
||
93 |
STYLE_MD5, |
||
94 |
0, |
||
95 |
NULL, |
||
96 |
(void (*)(void *))MD5Init, |
||
97 |
(void (*)(void *, const unsigned char *, size_t))MD5Update, |
||
98 |
(void (*)(unsigned char *, void *))MD5Final, |
||
99 |
(char *(*)(void *, char *))MD5End |
||
100 |
}, |
||
101 |
{ |
||
102 |
"RMD160", |
||
103 |
RMD160_DIGEST_LENGTH, |
||
104 |
STYLE_MD5, |
||
105 |
0, |
||
106 |
NULL, |
||
107 |
(void (*)(void *))RMD160Init, |
||
108 |
(void (*)(void *, const unsigned char *, size_t))RMD160Update, |
||
109 |
(void (*)(unsigned char *, void *))RMD160Final, |
||
110 |
(char *(*)(void *, char *))RMD160End |
||
111 |
}, |
||
112 |
{ |
||
113 |
"SHA1", |
||
114 |
SHA1_DIGEST_LENGTH, |
||
115 |
STYLE_MD5, |
||
116 |
0, |
||
117 |
NULL, |
||
118 |
(void (*)(void *))SHA1Init, |
||
119 |
(void (*)(void *, const unsigned char *, size_t))SHA1Update, |
||
120 |
(void (*)(unsigned char *, void *))SHA1Final, |
||
121 |
(char *(*)(void *, char *))SHA1End |
||
122 |
}, |
||
123 |
{ |
||
124 |
"SHA224", |
||
125 |
SHA224_DIGEST_LENGTH, |
||
126 |
STYLE_MD5, |
||
127 |
0, |
||
128 |
NULL, |
||
129 |
(void (*)(void *))SHA224Init, |
||
130 |
(void (*)(void *, const unsigned char *, size_t))SHA224Update, |
||
131 |
(void (*)(unsigned char *, void *))SHA224Final, |
||
132 |
(char *(*)(void *, char *))SHA224End |
||
133 |
}, |
||
134 |
#endif /* !defined(SHA2_ONLY) */ |
||
135 |
{ |
||
136 |
"SHA256", |
||
137 |
SHA256_DIGEST_LENGTH, |
||
138 |
STYLE_MD5, |
||
139 |
0, |
||
140 |
NULL, |
||
141 |
(void (*)(void *))SHA256Init, |
||
142 |
(void (*)(void *, const unsigned char *, size_t))SHA256Update, |
||
143 |
(void (*)(unsigned char *, void *))SHA256Final, |
||
144 |
(char *(*)(void *, char *))SHA256End |
||
145 |
}, |
||
146 |
#if !defined(SHA2_ONLY) |
||
147 |
{ |
||
148 |
"SHA384", |
||
149 |
SHA384_DIGEST_LENGTH, |
||
150 |
STYLE_MD5, |
||
151 |
0, |
||
152 |
NULL, |
||
153 |
(void (*)(void *))SHA384Init, |
||
154 |
(void (*)(void *, const unsigned char *, size_t))SHA384Update, |
||
155 |
(void (*)(unsigned char *, void *))SHA384Final, |
||
156 |
(char *(*)(void *, char *))SHA384End |
||
157 |
}, |
||
158 |
{ |
||
159 |
"SHA512/256", |
||
160 |
SHA512_256_DIGEST_LENGTH, |
||
161 |
STYLE_MD5, |
||
162 |
0, |
||
163 |
NULL, |
||
164 |
(void (*)(void *))SHA512_256Init, |
||
165 |
(void (*)(void *, const unsigned char *, size_t))SHA512_256Update, |
||
166 |
(void (*)(unsigned char *, void *))SHA512_256Final, |
||
167 |
(char *(*)(void *, char *))SHA512_256End |
||
168 |
}, |
||
169 |
#endif /* !defined(SHA2_ONLY) */ |
||
170 |
{ |
||
171 |
"SHA512", |
||
172 |
SHA512_DIGEST_LENGTH, |
||
173 |
STYLE_MD5, |
||
174 |
0, |
||
175 |
NULL, |
||
176 |
(void (*)(void *))SHA512Init, |
||
177 |
(void (*)(void *, const unsigned char *, size_t))SHA512Update, |
||
178 |
(void (*)(unsigned char *, void *))SHA512Final, |
||
179 |
(char *(*)(void *, char *))SHA512End |
||
180 |
}, |
||
181 |
{ |
||
182 |
NULL, |
||
183 |
} |
||
184 |
}; |
||
185 |
|||
186 |
TAILQ_HEAD(hash_list, hash_function); |
||
187 |
|||
188 |
void digest_end(const struct hash_function *, void *, char *, size_t, int); |
||
189 |
int digest_file(const char *, struct hash_list *, int); |
||
190 |
void digest_print(const struct hash_function *, const char *, const char *); |
||
191 |
#if !defined(SHA2_ONLY) |
||
192 |
int digest_filelist(const char *, struct hash_function *, int, char **); |
||
193 |
void digest_printstr(const struct hash_function *, const char *, const char *); |
||
194 |
void digest_string(char *, struct hash_list *); |
||
195 |
void digest_test(struct hash_list *); |
||
196 |
void digest_time(struct hash_list *, int); |
||
197 |
#endif /* !defined(SHA2_ONLY) */ |
||
198 |
void hash_insert(struct hash_list *, struct hash_function *, int); |
||
199 |
void usage(void) __attribute__((__noreturn__)); |
||
200 |
|||
201 |
extern char *__progname; |
||
202 |
int qflag = 0; |
||
203 |
FILE *ofile = NULL; |
||
204 |
|||
205 |
int |
||
206 |
main(int argc, char **argv) |
||
207 |
{ |
||
208 |
struct hash_function *hf, *hftmp; |
||
209 |
564 |
struct hash_list hl; |
|
210 |
size_t len; |
||
211 |
char *cp, *input_string, *selective_checklist; |
||
212 |
const char *optstr; |
||
213 |
int fl, error, base64; |
||
214 |
int bflag, cflag, pflag, rflag, tflag, xflag; |
||
215 |
|||
216 |
✗✓ | 282 |
if (pledge("stdio rpath wpath cpath flock", NULL) == -1) |
217 |
err(1, "pledge"); |
||
218 |
|||
219 |
282 |
TAILQ_INIT(&hl); |
|
220 |
input_string = NULL; |
||
221 |
selective_checklist = NULL; |
||
222 |
282 |
error = bflag = cflag = pflag = qflag = rflag = tflag = xflag = 0; |
|
223 |
|||
224 |
#if !defined(SHA2_ONLY) |
||
225 |
✓✓ | 282 |
if (strcmp(__progname, "cksum") == 0) |
226 |
69 |
optstr = "a:bC:ch:pqrs:tx"; |
|
227 |
else |
||
228 |
#endif /* !defined(SHA2_ONLY) */ |
||
229 |
optstr = "bC:ch:pqrs:tx"; |
||
230 |
|||
231 |
/* Check for -b option early since it changes behavior. */ |
||
232 |
✓✓ | 573 |
while ((fl = getopt(argc, argv, optstr)) != -1) { |
233 |
✓✗✗ | 291 |
switch (fl) { |
234 |
case 'b': |
||
235 |
bflag = 1; |
||
236 |
break; |
||
237 |
case '?': |
||
238 |
usage(); |
||
239 |
} |
||
240 |
} |
||
241 |
282 |
optind = 1; |
|
242 |
282 |
optreset = 1; |
|
243 |
✓✓ | 855 |
while ((fl = getopt(argc, argv, optstr)) != -1) { |
244 |
✓✓✗✗ ✗✓✓✗ ✓✗✓✗ |
628 |
switch (fl) { |
245 |
case 'a': |
||
246 |
✓✓ | 92 |
while ((cp = strsep(&optarg, " \t,")) != NULL) { |
247 |
✗✓ | 46 |
if (*cp == '\0') |
248 |
continue; |
||
249 |
base64 = -1; |
||
250 |
✓✗ | 552 |
for (hf = functions; hf->name != NULL; hf++) { |
251 |
276 |
len = strlen(hf->name); |
|
252 |
✓✓ | 276 |
if (strncasecmp(cp, hf->name, len) != 0) |
253 |
continue; |
||
254 |
✓✗ | 46 |
if (cp[len] == '\0') { |
255 |
✓✗ | 46 |
if (hf->base64 != -1) |
256 |
46 |
base64 = bflag; |
|
257 |
break; /* exact match */ |
||
258 |
} |
||
259 |
if (cp[len + 1] == '\0' && |
||
260 |
(cp[len] == 'b' || cp[len] == 'x')) { |
||
261 |
base64 = |
||
262 |
cp[len] == 'b' ? 1 : 0; |
||
263 |
break; /* match w/ suffix */ |
||
264 |
} |
||
265 |
} |
||
266 |
✗✓ | 46 |
if (hf->name == NULL) { |
267 |
warnx("unknown algorithm \"%s\"", cp); |
||
268 |
usage(); |
||
269 |
} |
||
270 |
✗✓ | 46 |
if (hf->base64 == -1 && base64 != -1) { |
271 |
warnx("%s doesn't support %s", |
||
272 |
hf->name, |
||
273 |
base64 ? "base64" : "hex"); |
||
274 |
usage(); |
||
275 |
} |
||
276 |
/* Check for dupes. */ |
||
277 |
✗✓ | 92 |
TAILQ_FOREACH(hftmp, &hl, tailq) { |
278 |
if (hftmp->base64 == base64 && |
||
279 |
strcmp(hf->name, hftmp->name) == 0) |
||
280 |
break; |
||
281 |
} |
||
282 |
✗✓ | 46 |
if (hftmp == NULL) |
283 |
46 |
hash_insert(&hl, hf, base64); |
|
284 |
} |
||
285 |
break; |
||
286 |
case 'b': |
||
287 |
/* has already been parsed */ |
||
288 |
break; |
||
289 |
case 'h': |
||
290 |
ofile = fopen(optarg, "w"); |
||
291 |
if (ofile == NULL) |
||
292 |
err(1, "%s", optarg); |
||
293 |
break; |
||
294 |
#if !defined(SHA2_ONLY) |
||
295 |
case 'C': |
||
296 |
selective_checklist = optarg; |
||
297 |
break; |
||
298 |
case 'c': |
||
299 |
cflag = 1; |
||
300 |
break; |
||
301 |
#endif /* !defined(SHA2_ONLY) */ |
||
302 |
case 'p': |
||
303 |
pflag = 1; |
||
304 |
46 |
break; |
|
305 |
case 'q': |
||
306 |
15 |
qflag = 1; |
|
307 |
15 |
break; |
|
308 |
case 'r': |
||
309 |
rflag = 1; |
||
310 |
break; |
||
311 |
case 's': |
||
312 |
23 |
input_string = optarg; |
|
313 |
23 |
break; |
|
314 |
case 't': |
||
315 |
tflag++; |
||
316 |
break; |
||
317 |
case 'x': |
||
318 |
xflag = 1; |
||
319 |
161 |
break; |
|
320 |
default: |
||
321 |
usage(); |
||
322 |
} |
||
323 |
} |
||
324 |
282 |
argc -= optind; |
|
325 |
282 |
argv += optind; |
|
326 |
|||
327 |
✓✗ | 282 |
if (ofile == NULL) |
328 |
282 |
ofile = stdout; |
|
329 |
|||
330 |
✗✓ | 282 |
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
331 |
err(1, "pledge"); |
||
332 |
|||
333 |
/* Most arguments are mutually exclusive */ |
||
334 |
282 |
fl = pflag + (tflag ? 1 : 0) + xflag + cflag + (input_string != NULL); |
|
335 |
✓✗✓✓ ✓✗ |
800 |
if (fl > 1 || (fl && argc && cflag == 0) || (rflag && qflag) || |
336 |
✗✓ | 236 |
(selective_checklist != NULL && argc == 0)) |
337 |
usage(); |
||
338 |
✗✓ | 236 |
if (selective_checklist || cflag) { |
339 |
if (TAILQ_FIRST(&hl) != TAILQ_LAST(&hl, hash_list)) |
||
340 |
errx(1, "only a single algorithm may be specified " |
||
341 |
"in -C or -c mode"); |
||
342 |
} |
||
343 |
|||
344 |
/* No algorithm specified, check the name we were called as. */ |
||
345 |
✓✓ | 236 |
if (TAILQ_EMPTY(&hl)) { |
346 |
✓✗ | 1466 |
for (hf = functions; hf->name != NULL; hf++) { |
347 |
✓✓ | 733 |
if (strcasecmp(hf->name, __progname) == 0) |
348 |
break; |
||
349 |
} |
||
350 |
✗✓ | 190 |
if (hf->name == NULL) |
351 |
hf = &functions[0]; /* default to cksum */ |
||
352 |
190 |
hash_insert(&hl, hf, (hf->base64 == -1 ? 0 : bflag)); |
|
353 |
190 |
} |
|
354 |
|||
355 |
✓✓ | 236 |
if (rflag || qflag) { |
356 |
15 |
const int new_style = rflag ? STYLE_CKSUM : STYLE_TERSE; |
|
357 |
✓✓ | 60 |
TAILQ_FOREACH(hf, &hl, tailq) { |
358 |
15 |
hf->style = new_style; |
|
359 |
} |
||
360 |
15 |
} |
|
361 |
|||
362 |
#if !defined(SHA2_ONLY) |
||
363 |
✗✓ | 236 |
if (tflag) |
364 |
digest_time(&hl, tflag); |
||
365 |
✓✓ | 236 |
else if (xflag) |
366 |
161 |
digest_test(&hl); |
|
367 |
✗✓ | 75 |
else if (input_string) |
368 |
digest_string(input_string, &hl); |
||
369 |
✗✓ | 75 |
else if (selective_checklist) { |
370 |
int i; |
||
371 |
|||
372 |
error = digest_filelist(selective_checklist, TAILQ_FIRST(&hl), |
||
373 |
argc, argv); |
||
374 |
for (i = 0; i < argc; i++) { |
||
375 |
if (argv[i] != NULL) { |
||
376 |
warnx("%s does not exist in %s", argv[i], |
||
377 |
selective_checklist); |
||
378 |
error++; |
||
379 |
} |
||
380 |
} |
||
381 |
✗✓ | 75 |
} else if (cflag) { |
382 |
if (argc == 0) |
||
383 |
error = digest_filelist("-", TAILQ_FIRST(&hl), 0, NULL); |
||
384 |
else |
||
385 |
while (argc--) |
||
386 |
error += digest_filelist(*argv++, |
||
387 |
TAILQ_FIRST(&hl), 0, NULL); |
||
388 |
} else |
||
389 |
#endif /* !defined(SHA2_ONLY) */ |
||
390 |
✓✓ | 75 |
if (pflag || argc == 0) |
391 |
23 |
error = digest_file("-", &hl, pflag); |
|
392 |
else |
||
393 |
✓✓ | 230 |
while (argc--) |
394 |
89 |
error += digest_file(*argv++, &hl, 0); |
|
395 |
|||
396 |
472 |
return(error ? EXIT_FAILURE : EXIT_SUCCESS); |
|
397 |
236 |
} |
|
398 |
|||
399 |
void |
||
400 |
hash_insert(struct hash_list *hl, struct hash_function *hf, int base64) |
||
401 |
{ |
||
402 |
struct hash_function *hftmp; |
||
403 |
|||
404 |
472 |
hftmp = malloc(sizeof(*hftmp)); |
|
405 |
✗✓ | 236 |
if (hftmp == NULL) |
406 |
err(1, NULL); |
||
407 |
236 |
*hftmp = *hf; |
|
408 |
236 |
hftmp->base64 = base64; |
|
409 |
236 |
TAILQ_INSERT_TAIL(hl, hftmp, tailq); |
|
410 |
236 |
} |
|
411 |
|||
412 |
void |
||
413 |
digest_end(const struct hash_function *hf, void *ctx, char *buf, size_t bsize, |
||
414 |
int base64) |
||
415 |
{ |
||
416 |
u_char *digest; |
||
417 |
|||
418 |
✗✓ | 3122 |
if (base64 == 1) { |
419 |
if ((digest = malloc(hf->digestlen)) == NULL) |
||
420 |
err(1, NULL); |
||
421 |
hf->final(digest, ctx); |
||
422 |
if (b64_ntop(digest, hf->digestlen, buf, bsize) == -1) |
||
423 |
errx(1, "error encoding base64"); |
||
424 |
freezero(digest, hf->digestlen); |
||
425 |
} else { |
||
426 |
1561 |
hf->end(ctx, buf); |
|
427 |
} |
||
428 |
1561 |
} |
|
429 |
|||
430 |
#if !defined(SHA2_ONLY) |
||
431 |
void |
||
432 |
digest_string(char *string, struct hash_list *hl) |
||
433 |
{ |
||
434 |
struct hash_function *hf; |
||
435 |
char digest[MAX_DIGEST_LEN + 1]; |
||
436 |
union ANY_CTX context; |
||
437 |
|||
438 |
TAILQ_FOREACH(hf, hl, tailq) { |
||
439 |
hf->init(&context); |
||
440 |
hf->update(&context, string, strlen(string)); |
||
441 |
digest_end(hf, &context, digest, sizeof(digest), |
||
442 |
hf->base64); |
||
443 |
digest_printstr(hf, string, digest); |
||
444 |
} |
||
445 |
} |
||
446 |
#endif /* !defined(SHA2_ONLY) */ |
||
447 |
|||
448 |
void |
||
449 |
digest_print(const struct hash_function *hf, const char *what, |
||
450 |
const char *digest) |
||
451 |
{ |
||
452 |
✓✓✓✓ |
750 |
switch (hf->style) { |
453 |
case STYLE_MD5: |
||
454 |
212 |
(void)fprintf(ofile, "%s (%s) = %s\n", hf->name, what, digest); |
|
455 |
212 |
break; |
|
456 |
case STYLE_CKSUM: |
||
457 |
23 |
(void)fprintf(ofile, "%s %s\n", digest, what); |
|
458 |
23 |
break; |
|
459 |
case STYLE_TERSE: |
||
460 |
15 |
(void)fprintf(ofile, "%s\n", digest); |
|
461 |
15 |
break; |
|
462 |
} |
||
463 |
250 |
} |
|
464 |
|||
465 |
#if !defined(SHA2_ONLY) |
||
466 |
void |
||
467 |
digest_printstr(const struct hash_function *hf, const char *what, |
||
468 |
const char *digest) |
||
469 |
{ |
||
470 |
✓✓✗✓ |
3864 |
switch (hf->style) { |
471 |
case STYLE_MD5: |
||
472 |
1104 |
(void)fprintf(ofile, "%s (\"%s\") = %s\n", hf->name, what, digest); |
|
473 |
1104 |
break; |
|
474 |
case STYLE_CKSUM: |
||
475 |
184 |
(void)fprintf(ofile, "%s %s\n", digest, what); |
|
476 |
184 |
break; |
|
477 |
case STYLE_TERSE: |
||
478 |
(void)fprintf(ofile, "%s\n", digest); |
||
479 |
break; |
||
480 |
} |
||
481 |
1288 |
} |
|
482 |
#endif /* !defined(SHA2_ONLY) */ |
||
483 |
|||
484 |
int |
||
485 |
digest_file(const char *file, struct hash_list *hl, int echo) |
||
486 |
{ |
||
487 |
struct hash_function *hf; |
||
488 |
FILE *fp; |
||
489 |
size_t nread; |
||
490 |
224 |
u_char data[32 * 1024]; |
|
491 |
112 |
char digest[MAX_DIGEST_LEN + 1]; |
|
492 |
|||
493 |
✓✓ | 112 |
if (strcmp(file, "-") == 0) |
494 |
23 |
fp = stdin; |
|
495 |
✗✓ | 89 |
else if ((fp = fopen(file, "r")) == NULL) { |
496 |
warn("cannot open %s", file); |
||
497 |
return(1); |
||
498 |
} |
||
499 |
|||
500 |
✓✓ | 448 |
TAILQ_FOREACH(hf, hl, tailq) { |
501 |
✗✓ | 112 |
if ((hf->ctx = malloc(sizeof(union ANY_CTX))) == NULL) |
502 |
err(1, NULL); |
||
503 |
112 |
hf->init(hf->ctx); |
|
504 |
} |
||
505 |
✓✓ | 164168 |
while ((nread = fread(data, 1UL, sizeof(data), fp)) != 0) { |
506 |
✓✓ | 164056 |
if (echo) { |
507 |
23 |
(void)fwrite(data, nread, 1UL, stdout); |
|
508 |
✗✓ | 23 |
if (fflush(stdout) != 0) |
509 |
err(1, "stdout: write error"); |
||
510 |
} |
||
511 |
✓✓ | 656224 |
TAILQ_FOREACH(hf, hl, tailq) |
512 |
164056 |
hf->update(hf->ctx, data, nread); |
|
513 |
} |
||
514 |
✓✗✗✓ ✗✗ |
224 |
if (ferror(fp)) { |
515 |
warn("%s: read error", file); |
||
516 |
if (fp != stdin) |
||
517 |
fclose(fp); |
||
518 |
TAILQ_FOREACH(hf, hl, tailq) { |
||
519 |
free(hf->ctx); |
||
520 |
hf->ctx = NULL; |
||
521 |
} |
||
522 |
return(1); |
||
523 |
} |
||
524 |
✓✓ | 112 |
if (fp != stdin) |
525 |
89 |
fclose(fp); |
|
526 |
✓✓ | 448 |
TAILQ_FOREACH(hf, hl, tailq) { |
527 |
112 |
digest_end(hf, hf->ctx, digest, sizeof(digest), hf->base64); |
|
528 |
112 |
free(hf->ctx); |
|
529 |
112 |
hf->ctx = NULL; |
|
530 |
✓✓ | 112 |
if (fp == stdin) |
531 |
23 |
fprintf(ofile, "%s\n", digest); |
|
532 |
else |
||
533 |
89 |
digest_print(hf, file, digest); |
|
534 |
} |
||
535 |
112 |
return(0); |
|
536 |
112 |
} |
|
537 |
|||
538 |
#if !defined(SHA2_ONLY) |
||
539 |
/* |
||
540 |
* Parse through the input file looking for valid lines. |
||
541 |
* If one is found, use this checksum and file as a reference and |
||
542 |
* generate a new checksum against the file on the filesystem. |
||
543 |
* Print out the result of each comparison. |
||
544 |
*/ |
||
545 |
int |
||
546 |
digest_filelist(const char *file, struct hash_function *defhash, int selcount, |
||
547 |
char **sel) |
||
548 |
{ |
||
549 |
int found, base64, error, cmp, i; |
||
550 |
size_t algorithm_max, algorithm_min; |
||
551 |
const char *algorithm; |
||
552 |
char *filename, *checksum, *buf, *p; |
||
553 |
char digest[MAX_DIGEST_LEN + 1]; |
||
554 |
char *lbuf = NULL; |
||
555 |
FILE *listfp, *fp; |
||
556 |
size_t len, nread; |
||
557 |
int *sel_found = NULL; |
||
558 |
u_char data[32 * 1024]; |
||
559 |
union ANY_CTX context; |
||
560 |
struct hash_function *hf; |
||
561 |
|||
562 |
if (strcmp(file, "-") == 0) { |
||
563 |
listfp = stdin; |
||
564 |
} else if ((listfp = fopen(file, "r")) == NULL) { |
||
565 |
warn("cannot open %s", file); |
||
566 |
return(1); |
||
567 |
} |
||
568 |
|||
569 |
if (sel != NULL) { |
||
570 |
sel_found = calloc((size_t)selcount, sizeof(*sel_found)); |
||
571 |
if (sel_found == NULL) |
||
572 |
err(1, NULL); |
||
573 |
} |
||
574 |
|||
575 |
algorithm_max = algorithm_min = strlen(functions[0].name); |
||
576 |
for (hf = &functions[1]; hf->name != NULL; hf++) { |
||
577 |
len = strlen(hf->name); |
||
578 |
algorithm_max = MAXIMUM(algorithm_max, len); |
||
579 |
algorithm_min = MINIMUM(algorithm_min, len); |
||
580 |
} |
||
581 |
|||
582 |
error = found = 0; |
||
583 |
while ((buf = fgetln(listfp, &len))) { |
||
584 |
base64 = 0; |
||
585 |
if (buf[len - 1] == '\n') |
||
586 |
buf[len - 1] = '\0'; |
||
587 |
else { |
||
588 |
if ((lbuf = malloc(len + 1)) == NULL) |
||
589 |
err(1, NULL); |
||
590 |
|||
591 |
(void)memcpy(lbuf, buf, len); |
||
592 |
lbuf[len] = '\0'; |
||
593 |
buf = lbuf; |
||
594 |
} |
||
595 |
while (isspace((unsigned char)*buf)) |
||
596 |
buf++; |
||
597 |
|||
598 |
/* |
||
599 |
* Crack the line into an algorithm, filename, and checksum. |
||
600 |
* Lines are of the form: |
||
601 |
* ALGORITHM (FILENAME) = CHECKSUM |
||
602 |
* |
||
603 |
* Fallback on GNU form: |
||
604 |
* CHECKSUM FILENAME |
||
605 |
*/ |
||
606 |
p = strchr(buf, ' '); |
||
607 |
if (p != NULL && *(p + 1) == '(') { |
||
608 |
/* BSD form */ |
||
609 |
*p = '\0'; |
||
610 |
algorithm = buf; |
||
611 |
len = strlen(algorithm); |
||
612 |
if (len > algorithm_max || len < algorithm_min) |
||
613 |
continue; |
||
614 |
|||
615 |
filename = p + 2; |
||
616 |
p = strrchr(filename, ')'); |
||
617 |
if (p == NULL || strncmp(p + 1, " = ", (size_t)3) != 0) |
||
618 |
continue; |
||
619 |
*p = '\0'; |
||
620 |
|||
621 |
checksum = p + 4; |
||
622 |
p = strpbrk(checksum, " \t\r"); |
||
623 |
if (p != NULL) |
||
624 |
*p = '\0'; |
||
625 |
|||
626 |
/* |
||
627 |
* Check that the algorithm is one we recognize. |
||
628 |
*/ |
||
629 |
for (hf = functions; hf->name != NULL; hf++) { |
||
630 |
if (strcasecmp(algorithm, hf->name) == 0) |
||
631 |
break; |
||
632 |
} |
||
633 |
if (hf->name == NULL || *checksum == '\0') |
||
634 |
continue; |
||
635 |
/* |
||
636 |
* Check the length to see if this could be |
||
637 |
* a valid checksum. If hex, it will be 2x the |
||
638 |
* size of the binary data. For base64, we have |
||
639 |
* to check both with and without the '=' padding. |
||
640 |
*/ |
||
641 |
len = strlen(checksum); |
||
642 |
if (len != hf->digestlen * 2) { |
||
643 |
size_t len2; |
||
644 |
|||
645 |
if (checksum[len - 1] == '=') { |
||
646 |
/* use padding */ |
||
647 |
len2 = 4 * ((hf->digestlen + 2) / 3); |
||
648 |
} else { |
||
649 |
/* no padding */ |
||
650 |
len2 = (4 * hf->digestlen + 2) / 3; |
||
651 |
} |
||
652 |
if (len != len2) |
||
653 |
continue; |
||
654 |
base64 = 1; |
||
655 |
} |
||
656 |
} else { |
||
657 |
/* could be GNU form */ |
||
658 |
if ((hf = defhash) == NULL) |
||
659 |
continue; |
||
660 |
algorithm = hf->name; |
||
661 |
checksum = buf; |
||
662 |
if ((p = strchr(checksum, ' ')) == NULL) |
||
663 |
continue; |
||
664 |
if (hf->style == STYLE_CKSUM) { |
||
665 |
if ((p = strchr(p + 1, ' ')) == NULL) |
||
666 |
continue; |
||
667 |
} |
||
668 |
*p++ = '\0'; |
||
669 |
while (isspace((unsigned char)*p)) |
||
670 |
p++; |
||
671 |
if (*p == '\0') |
||
672 |
continue; |
||
673 |
filename = p; |
||
674 |
p = strpbrk(filename, "\t\r"); |
||
675 |
if (p != NULL) |
||
676 |
*p = '\0'; |
||
677 |
} |
||
678 |
found = 1; |
||
679 |
|||
680 |
/* |
||
681 |
* If only a selection of files is wanted, proceed only |
||
682 |
* if the filename matches one of those in the selection. |
||
683 |
*/ |
||
684 |
if (sel != NULL) { |
||
685 |
for (i = 0; i < selcount; i++) { |
||
686 |
if (strcmp(sel[i], filename) == 0) { |
||
687 |
sel_found[i] = 1; |
||
688 |
break; |
||
689 |
} |
||
690 |
} |
||
691 |
if (i == selcount) |
||
692 |
continue; |
||
693 |
} |
||
694 |
|||
695 |
if ((fp = fopen(filename, "r")) == NULL) { |
||
696 |
warn("cannot open %s", filename); |
||
697 |
(void)printf("(%s) %s: %s\n", algorithm, filename, |
||
698 |
(errno == ENOENT ? "MISSING" : "FAILED")); |
||
699 |
error = 1; |
||
700 |
continue; |
||
701 |
} |
||
702 |
|||
703 |
hf->init(&context); |
||
704 |
while ((nread = fread(data, 1UL, sizeof(data), fp)) > 0) |
||
705 |
hf->update(&context, data, nread); |
||
706 |
if (ferror(fp)) { |
||
707 |
warn("%s: read error", file); |
||
708 |
error = 1; |
||
709 |
fclose(fp); |
||
710 |
continue; |
||
711 |
} |
||
712 |
fclose(fp); |
||
713 |
digest_end(hf, &context, digest, sizeof(digest), base64); |
||
714 |
|||
715 |
if (base64) |
||
716 |
cmp = strncmp(checksum, digest, len); |
||
717 |
else |
||
718 |
cmp = strcasecmp(checksum, digest); |
||
719 |
if (cmp == 0) { |
||
720 |
if (qflag == 0) |
||
721 |
(void)printf("(%s) %s: OK\n", algorithm, |
||
722 |
filename); |
||
723 |
} else { |
||
724 |
(void)printf("(%s) %s: FAILED\n", algorithm, filename); |
||
725 |
error = 1; |
||
726 |
} |
||
727 |
} |
||
728 |
if (listfp != stdin) |
||
729 |
fclose(listfp); |
||
730 |
if (!found) |
||
731 |
warnx("%s: no properly formatted checksum lines found", file); |
||
732 |
free(lbuf); |
||
733 |
if (sel_found != NULL) { |
||
734 |
/* |
||
735 |
* Mark found files by setting them to NULL so that we can |
||
736 |
* detect files that are missing from the checklist later. |
||
737 |
*/ |
||
738 |
for (i = 0; i < selcount; i++) { |
||
739 |
if (sel_found[i]) |
||
740 |
sel[i] = NULL; |
||
741 |
} |
||
742 |
free(sel_found); |
||
743 |
} |
||
744 |
return(error || !found); |
||
745 |
} |
||
746 |
|||
747 |
#define TEST_BLOCK_LEN 10000 |
||
748 |
#define TEST_BLOCK_COUNT 10000 |
||
749 |
|||
750 |
void |
||
751 |
digest_time(struct hash_list *hl, int times) |
||
752 |
{ |
||
753 |
struct hash_function *hf; |
||
754 |
struct rusage start, stop; |
||
755 |
struct timeval res; |
||
756 |
union ANY_CTX context; |
||
757 |
u_int i; |
||
758 |
u_char data[TEST_BLOCK_LEN]; |
||
759 |
char digest[MAX_DIGEST_LEN + 1]; |
||
760 |
double elapsed; |
||
761 |
int count = TEST_BLOCK_COUNT; |
||
762 |
while (--times > 0 && count < INT_MAX / 10) |
||
763 |
count *= 10; |
||
764 |
|||
765 |
TAILQ_FOREACH(hf, hl, tailq) { |
||
766 |
(void)printf("%s time trial. Processing %d %d-byte blocks...", |
||
767 |
hf->name, count, TEST_BLOCK_LEN); |
||
768 |
fflush(stdout); |
||
769 |
|||
770 |
/* Initialize data based on block number. */ |
||
771 |
for (i = 0; i < TEST_BLOCK_LEN; i++) |
||
772 |
data[i] = (u_char)(i & 0xff); |
||
773 |
|||
774 |
getrusage(RUSAGE_SELF, &start); |
||
775 |
hf->init(&context); |
||
776 |
for (i = 0; i < count; i++) |
||
777 |
hf->update(&context, data, (size_t)TEST_BLOCK_LEN); |
||
778 |
digest_end(hf, &context, digest, sizeof(digest), hf->base64); |
||
779 |
getrusage(RUSAGE_SELF, &stop); |
||
780 |
timersub(&stop.ru_utime, &start.ru_utime, &res); |
||
781 |
elapsed = res.tv_sec + res.tv_usec / 1000000.0; |
||
782 |
|||
783 |
(void)printf("\nDigest = %s\n", digest); |
||
784 |
(void)printf("Time = %f seconds\n", elapsed); |
||
785 |
(void)printf("Speed = %f bytes/second\n", |
||
786 |
(double)TEST_BLOCK_LEN * count / elapsed); |
||
787 |
} |
||
788 |
} |
||
789 |
|||
790 |
void |
||
791 |
digest_test(struct hash_list *hl) |
||
792 |
{ |
||
793 |
struct hash_function *hf; |
||
794 |
322 |
union ANY_CTX context; |
|
795 |
int i; |
||
796 |
161 |
char digest[MAX_DIGEST_LEN + 1]; |
|
797 |
161 |
unsigned char buf[1000]; |
|
798 |
161 |
unsigned const char *test_strings[] = { |
|
799 |
"", |
||
800 |
"a", |
||
801 |
"abc", |
||
802 |
"message digest", |
||
803 |
"abcdefghijklmnopqrstuvwxyz", |
||
804 |
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", |
||
805 |
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" |
||
806 |
"0123456789", |
||
807 |
"12345678901234567890123456789012345678901234567890123456789" |
||
808 |
"012345678901234567890", |
||
809 |
}; |
||
810 |
|||
811 |
✓✓ | 644 |
TAILQ_FOREACH(hf, hl, tailq) { |
812 |
161 |
(void)printf("%s test suite:\n", hf->name); |
|
813 |
|||
814 |
✓✓ | 2898 |
for (i = 0; i < 8; i++) { |
815 |
1288 |
hf->init(&context); |
|
816 |
2576 |
hf->update(&context, test_strings[i], |
|
817 |
1288 |
strlen(test_strings[i])); |
|
818 |
2576 |
digest_end(hf, &context, digest, sizeof(digest), |
|
819 |
1288 |
hf->base64); |
|
820 |
1288 |
digest_printstr(hf, test_strings[i], digest); |
|
821 |
} |
||
822 |
|||
823 |
/* Now simulate a string of a million 'a' characters. */ |
||
824 |
161 |
memset(buf, 'a', sizeof(buf)); |
|
825 |
161 |
hf->init(&context); |
|
826 |
✓✓ | 322322 |
for (i = 0; i < 1000; i++) |
827 |
161000 |
hf->update(&context, buf, sizeof(buf)); |
|
828 |
161 |
digest_end(hf, &context, digest, sizeof(digest), hf->base64); |
|
829 |
161 |
digest_print(hf, "one million 'a' characters", |
|
830 |
digest); |
||
831 |
} |
||
832 |
161 |
} |
|
833 |
#endif /* !defined(SHA2_ONLY) */ |
||
834 |
|||
835 |
void |
||
836 |
usage(void) |
||
837 |
{ |
||
838 |
#if !defined(SHA2_ONLY) |
||
839 |
✗✓ | 92 |
if (strcmp(__progname, "cksum") == 0) |
840 |
fprintf(stderr, "usage: %s [-bcpqrtx] [-a algorithms] [-C checklist] " |
||
841 |
"[-h hashfile]\n" |
||
842 |
" [-s string] [file ...]\n", |
||
843 |
__progname); |
||
844 |
else |
||
845 |
#endif /* !defined(SHA2_ONLY) */ |
||
846 |
46 |
fprintf(stderr, "usage:" |
|
847 |
"\t%s [-bcpqrtx] [-C checklist] [-h hashfile] [-s string] " |
||
848 |
"[file ...]\n", |
||
849 |
__progname); |
||
850 |
|||
851 |
exit(EXIT_FAILURE); |
||
852 |
} |
Generated by: GCOVR (Version 3.3) |