File: | src/bin/md5/md5.c |
Warning: | line 396, column 9 Potential leak of memory pointed to by 'hl.tqh_first' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: md5.c,v 1.98 2023/10/31 19:37:17 millert Exp $ */ | ||||||
2 | |||||||
3 | /* | ||||||
4 | * Copyright (c) 2001,2003,2005-2007,2010,2013,2014 | ||||||
5 | * Todd C. Miller <millert@openbsd.org> | ||||||
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(*__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_MD50 0 | ||||||
48 | #define STYLE_CKSUM1 1 | ||||||
49 | #define STYLE_TERSE2 2 | ||||||
50 | |||||||
51 | #define MAX_DIGEST_LEN128 128 | ||||||
52 | |||||||
53 | #define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b)) | ||||||
54 | #define MAXIMUM(a, b)(((a) > (b)) ? (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)struct { struct hash_function *tqe_next; struct hash_function **tqe_prev; } tailq; | ||||||
77 | } functions[] = { | ||||||
78 | #if !defined(SHA2_ONLY) | ||||||
79 | { | ||||||
80 | "CKSUM", | ||||||
81 | CKSUM_DIGEST_LENGTH4, | ||||||
82 | STYLE_CKSUM1, | ||||||
83 | -1, | ||||||
84 | NULL((void *)0), | ||||||
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_LENGTH16, | ||||||
93 | STYLE_MD50, | ||||||
94 | 0, | ||||||
95 | NULL((void *)0), | ||||||
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_LENGTH20, | ||||||
104 | STYLE_MD50, | ||||||
105 | 0, | ||||||
106 | NULL((void *)0), | ||||||
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_LENGTH20, | ||||||
115 | STYLE_MD50, | ||||||
116 | 0, | ||||||
117 | NULL((void *)0), | ||||||
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_LENGTH28, | ||||||
126 | STYLE_MD50, | ||||||
127 | 0, | ||||||
128 | NULL((void *)0), | ||||||
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_LENGTH32, | ||||||
138 | STYLE_MD50, | ||||||
139 | 0, | ||||||
140 | NULL((void *)0), | ||||||
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_LENGTH48, | ||||||
150 | STYLE_MD50, | ||||||
151 | 0, | ||||||
152 | NULL((void *)0), | ||||||
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_LENGTH32, | ||||||
161 | STYLE_MD50, | ||||||
162 | 0, | ||||||
163 | NULL((void *)0), | ||||||
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_LENGTH64, | ||||||
173 | STYLE_MD50, | ||||||
174 | 0, | ||||||
175 | NULL((void *)0), | ||||||
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((void *)0), | ||||||
183 | } | ||||||
184 | }; | ||||||
185 | |||||||
186 | TAILQ_HEAD(hash_list, hash_function)struct hash_list { struct hash_function *tqh_first; struct hash_function **tqh_last; }; | ||||||
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((void *)0); | ||||||
204 | |||||||
205 | int | ||||||
206 | main(int argc, char **argv) | ||||||
207 | { | ||||||
208 | struct hash_function *hf, *hftmp; | ||||||
209 | 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 | if (pledge("stdio rpath wpath cpath", NULL((void *)0)) == -1) | ||||||
| |||||||
217 | err(1, "pledge"); | ||||||
218 | |||||||
219 | TAILQ_INIT(&hl)do { (&hl)->tqh_first = ((void *)0); (&hl)->tqh_last = &(&hl)->tqh_first; } while (0); | ||||||
220 | input_string = NULL((void *)0); | ||||||
221 | selective_checklist = NULL((void *)0); | ||||||
222 | error = bflag = cflag = pflag = qflag = rflag = tflag = xflag = 0; | ||||||
223 | |||||||
224 | #if !defined(SHA2_ONLY) | ||||||
225 | if (strcmp(__progname, "cksum") == 0) | ||||||
226 | 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 | while ((fl = getopt(argc, argv, optstr)) != -1) { | ||||||
233 | switch (fl) { | ||||||
234 | case 'b': | ||||||
235 | bflag = 1; | ||||||
236 | break; | ||||||
237 | case '?': | ||||||
238 | usage(); | ||||||
239 | } | ||||||
240 | } | ||||||
241 | optind = 1; | ||||||
242 | optreset = 1; | ||||||
243 | while ((fl = getopt(argc, argv, optstr)) != -1) { | ||||||
244 | switch (fl) { | ||||||
245 | case 'a': | ||||||
246 | while ((cp = strsep(&optarg, " \t,")) != NULL((void *)0)) { | ||||||
247 | if (*cp == '\0') | ||||||
248 | continue; | ||||||
249 | base64 = -1; | ||||||
250 | for (hf = functions; hf->name != NULL((void *)0); hf++) { | ||||||
251 | len = strlen(hf->name); | ||||||
252 | if (strncasecmp(cp, hf->name, len) != 0) | ||||||
253 | continue; | ||||||
254 | if (cp[len] == '\0') { | ||||||
255 | if (hf->base64 != -1) | ||||||
256 | 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 | if (hf->name == NULL((void *)0)) { | ||||||
267 | warnx("unknown algorithm \"%s\"", cp); | ||||||
268 | usage(); | ||||||
269 | } | ||||||
270 | 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 | TAILQ_FOREACH(hftmp, &hl, tailq)for((hftmp) = ((&hl)->tqh_first); (hftmp) != ((void *) 0); (hftmp) = ((hftmp)->tailq.tqe_next)) { | ||||||
278 | if (hftmp->base64 == base64 && | ||||||
279 | strcmp(hf->name, hftmp->name) == 0) | ||||||
280 | break; | ||||||
281 | } | ||||||
282 | if (hftmp == NULL((void *)0)) | ||||||
283 | 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((void *)0)) | ||||||
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 | break; | ||||||
305 | case 'q': | ||||||
306 | qflag = 1; | ||||||
307 | break; | ||||||
308 | case 'r': | ||||||
309 | rflag = 1; | ||||||
310 | break; | ||||||
311 | case 's': | ||||||
312 | input_string = optarg; | ||||||
313 | break; | ||||||
314 | case 't': | ||||||
315 | tflag++; | ||||||
316 | break; | ||||||
317 | case 'x': | ||||||
318 | xflag = 1; | ||||||
319 | break; | ||||||
320 | default: | ||||||
321 | usage(); | ||||||
322 | } | ||||||
323 | } | ||||||
324 | argc -= optind; | ||||||
325 | argv += optind; | ||||||
326 | |||||||
327 | if (ofile
| ||||||
328 | ofile = stdout(&__sF[1]); | ||||||
329 | |||||||
330 | if (pledge("stdio rpath", NULL((void *)0)) == -1) | ||||||
331 | err(1, "pledge"); | ||||||
332 | |||||||
333 | /* Most arguments are mutually exclusive */ | ||||||
334 | fl = pflag + (tflag
| ||||||
335 | if (fl
| ||||||
336 | (selective_checklist
| ||||||
337 | usage(); | ||||||
338 | if (selective_checklist
| ||||||
339 | if (TAILQ_FIRST(&hl)((&hl)->tqh_first) != TAILQ_LAST(&hl, hash_list)(*(((struct hash_list *)((&hl)->tqh_last))->tqh_last ))) | ||||||
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 | if (TAILQ_EMPTY(&hl)(((&hl)->tqh_first) == ((void *)0))) { | ||||||
346 | for (hf = functions; hf->name != NULL((void *)0); hf++) { | ||||||
347 | if (strcasecmp(hf->name, __progname) == 0) | ||||||
348 | break; | ||||||
349 | } | ||||||
350 | if (hf->name
| ||||||
351 | hf = &functions[0]; /* default to cksum */ | ||||||
352 | hash_insert(&hl, hf, (hf->base64 == -1 ? 0 : bflag)); | ||||||
353 | } | ||||||
354 | |||||||
355 | if ((rflag
| ||||||
356 | const int new_style = rflag ? STYLE_CKSUM1 : STYLE_TERSE2; | ||||||
357 | TAILQ_FOREACH(hf, &hl, tailq)for((hf) = ((&hl)->tqh_first); (hf) != ((void *)0); (hf ) = ((hf)->tailq.tqe_next)) { | ||||||
358 | hf->style = new_style; | ||||||
359 | } | ||||||
360 | } | ||||||
361 | |||||||
362 | #if !defined(SHA2_ONLY) | ||||||
363 | if (tflag
| ||||||
364 | digest_time(&hl, tflag); | ||||||
365 | else if (xflag
| ||||||
366 | digest_test(&hl); | ||||||
367 | else if (input_string
| ||||||
368 | digest_string(input_string, &hl); | ||||||
369 | else if (selective_checklist
| ||||||
370 | int i; | ||||||
371 | |||||||
372 | error = digest_filelist(selective_checklist, TAILQ_FIRST(&hl)((&hl)->tqh_first), | ||||||
373 | argc, argv); | ||||||
374 | for (i = 0; i < argc; i++) { | ||||||
375 | if (argv[i] != NULL((void *)0)) { | ||||||
376 | warnx("%s does not exist in %s", argv[i], | ||||||
377 | selective_checklist); | ||||||
378 | error++; | ||||||
379 | } | ||||||
380 | } | ||||||
381 | } else if (cflag
| ||||||
382 | if (argc == 0) | ||||||
383 | error = digest_filelist("-", TAILQ_FIRST(&hl)((&hl)->tqh_first), 0, NULL((void *)0)); | ||||||
384 | else | ||||||
385 | while (argc--) | ||||||
386 | error += digest_filelist(*argv++, | ||||||
387 | TAILQ_FIRST(&hl)((&hl)->tqh_first), 0, NULL((void *)0)); | ||||||
388 | } else | ||||||
389 | #endif /* !defined(SHA2_ONLY) */ | ||||||
390 | if (pflag
| ||||||
391 | error = digest_file("-", &hl, pflag); | ||||||
392 | else | ||||||
393 | while (argc--) | ||||||
394 | error += digest_file(*argv++, &hl, 0); | ||||||
395 | |||||||
396 | return(error ? EXIT_FAILURE1 : EXIT_SUCCESS0); | ||||||
| |||||||
397 | } | ||||||
398 | |||||||
399 | void | ||||||
400 | hash_insert(struct hash_list *hl, struct hash_function *hf, int base64) | ||||||
401 | { | ||||||
402 | struct hash_function *hftmp; | ||||||
403 | |||||||
404 | hftmp = malloc(sizeof(*hftmp)); | ||||||
405 | if (hftmp == NULL((void *)0)) | ||||||
406 | err(1, NULL((void *)0)); | ||||||
407 | *hftmp = *hf; | ||||||
408 | hftmp->base64 = base64; | ||||||
409 | TAILQ_INSERT_TAIL(hl, hftmp, tailq)do { (hftmp)->tailq.tqe_next = ((void *)0); (hftmp)->tailq .tqe_prev = (hl)->tqh_last; *(hl)->tqh_last = (hftmp); ( hl)->tqh_last = &(hftmp)->tailq.tqe_next; } while ( 0); | ||||||
410 | } | ||||||
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 | if (base64 == 1) { | ||||||
419 | if ((digest = malloc(hf->digestlen)) == NULL((void *)0)) | ||||||
420 | err(1, NULL((void *)0)); | ||||||
421 | hf->final(digest, ctx); | ||||||
422 | if (b64_ntop__b64_ntop(digest, hf->digestlen, buf, bsize) == -1) | ||||||
423 | errx(1, "error encoding base64"); | ||||||
424 | free(digest); | ||||||
425 | } else { | ||||||
426 | hf->end(ctx, buf); | ||||||
427 | } | ||||||
428 | } | ||||||
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_LEN128 + 1]; | ||||||
436 | union ANY_CTX context; | ||||||
437 | |||||||
438 | TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = ( (hf)->tailq.tqe_next)) { | ||||||
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 | switch (hf->style) { | ||||||
453 | case STYLE_MD50: | ||||||
454 | (void)fprintf(ofile, "%s (%s) = %s\n", hf->name, what, digest); | ||||||
455 | break; | ||||||
456 | case STYLE_CKSUM1: | ||||||
457 | (void)fprintf(ofile, "%s %s\n", digest, what); | ||||||
458 | break; | ||||||
459 | case STYLE_TERSE2: | ||||||
460 | (void)fprintf(ofile, "%s\n", digest); | ||||||
461 | break; | ||||||
462 | } | ||||||
463 | } | ||||||
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 | switch (hf->style) { | ||||||
471 | case STYLE_MD50: | ||||||
472 | (void)fprintf(ofile, "%s (\"%s\") = %s\n", hf->name, what, digest); | ||||||
473 | break; | ||||||
474 | case STYLE_CKSUM1: | ||||||
475 | (void)fprintf(ofile, "%s %s\n", digest, what); | ||||||
476 | break; | ||||||
477 | case STYLE_TERSE2: | ||||||
478 | (void)fprintf(ofile, "%s\n", digest); | ||||||
479 | break; | ||||||
480 | } | ||||||
481 | } | ||||||
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 | u_char data[32 * 1024]; | ||||||
491 | char digest[MAX_DIGEST_LEN128 + 1]; | ||||||
492 | |||||||
493 | if (strcmp(file, "-") == 0) | ||||||
494 | fp = stdin(&__sF[0]); | ||||||
495 | else if ((fp = fopen(file, "r")) == NULL((void *)0)) { | ||||||
496 | warn("cannot open %s", file); | ||||||
497 | return(1); | ||||||
498 | } | ||||||
499 | |||||||
500 | TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = ( (hf)->tailq.tqe_next)) { | ||||||
501 | if ((hf->ctx = malloc(sizeof(union ANY_CTX))) == NULL((void *)0)) | ||||||
502 | err(1, NULL((void *)0)); | ||||||
503 | hf->init(hf->ctx); | ||||||
504 | } | ||||||
505 | while ((nread = fread(data, 1UL, sizeof(data), fp)) != 0) { | ||||||
506 | if (echo) { | ||||||
507 | (void)fwrite(data, nread, 1UL, stdout(&__sF[1])); | ||||||
508 | (void)fflush(stdout(&__sF[1])); | ||||||
509 | if (ferror(stdout)(!__isthreaded ? ((((&__sF[1]))->_flags & 0x0040) != 0) : (ferror)((&__sF[1])))) | ||||||
510 | err(1, "stdout: write error"); | ||||||
511 | } | ||||||
512 | TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = ( (hf)->tailq.tqe_next)) | ||||||
513 | hf->update(hf->ctx, data, nread); | ||||||
514 | } | ||||||
515 | if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror )(fp))) { | ||||||
516 | warn("%s: read error", file); | ||||||
517 | if (fp != stdin(&__sF[0])) | ||||||
518 | fclose(fp); | ||||||
519 | TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = ( (hf)->tailq.tqe_next)) { | ||||||
520 | free(hf->ctx); | ||||||
521 | hf->ctx = NULL((void *)0); | ||||||
522 | } | ||||||
523 | return(1); | ||||||
524 | } | ||||||
525 | if (fp != stdin(&__sF[0])) | ||||||
526 | fclose(fp); | ||||||
527 | TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = ( (hf)->tailq.tqe_next)) { | ||||||
528 | digest_end(hf, hf->ctx, digest, sizeof(digest), hf->base64); | ||||||
529 | free(hf->ctx); | ||||||
530 | hf->ctx = NULL((void *)0); | ||||||
531 | if (fp == stdin(&__sF[0])) | ||||||
532 | fprintf(ofile, "%s\n", digest); | ||||||
533 | else | ||||||
534 | digest_print(hf, file, digest); | ||||||
535 | } | ||||||
536 | return(0); | ||||||
537 | } | ||||||
538 | |||||||
539 | #if !defined(SHA2_ONLY) | ||||||
540 | /* | ||||||
541 | * Parse through the input file looking for valid lines. | ||||||
542 | * If one is found, use this checksum and file as a reference and | ||||||
543 | * generate a new checksum against the file on the filesystem. | ||||||
544 | * Print out the result of each comparison. | ||||||
545 | */ | ||||||
546 | int | ||||||
547 | digest_filelist(const char *file, struct hash_function *defhash, int selcount, | ||||||
548 | char **sel) | ||||||
549 | { | ||||||
550 | int found, base64, error, cmp, i; | ||||||
551 | size_t algorithm_max, algorithm_min; | ||||||
552 | const char *algorithm; | ||||||
553 | char *filename, *checksum, *line, *p, *tmpline; | ||||||
554 | char digest[MAX_DIGEST_LEN128 + 1]; | ||||||
555 | ssize_t linelen; | ||||||
556 | FILE *listfp, *fp; | ||||||
557 | size_t len, linesize, nread; | ||||||
558 | int *sel_found = NULL((void *)0); | ||||||
559 | u_char data[32 * 1024]; | ||||||
560 | union ANY_CTX context; | ||||||
561 | struct hash_function *hf; | ||||||
562 | |||||||
563 | if (strcmp(file, "-") == 0) { | ||||||
564 | listfp = stdin(&__sF[0]); | ||||||
565 | } else if ((listfp = fopen(file, "r")) == NULL((void *)0)) { | ||||||
566 | warn("cannot open %s", file); | ||||||
567 | return(1); | ||||||
568 | } | ||||||
569 | |||||||
570 | if (sel != NULL((void *)0)) { | ||||||
571 | sel_found = calloc((size_t)selcount, sizeof(*sel_found)); | ||||||
572 | if (sel_found == NULL((void *)0)) | ||||||
573 | err(1, NULL((void *)0)); | ||||||
574 | } | ||||||
575 | |||||||
576 | algorithm_max = algorithm_min = strlen(functions[0].name); | ||||||
577 | for (hf = &functions[1]; hf->name != NULL((void *)0); hf++) { | ||||||
578 | len = strlen(hf->name); | ||||||
579 | algorithm_max = MAXIMUM(algorithm_max, len)(((algorithm_max) > (len)) ? (algorithm_max) : (len)); | ||||||
580 | algorithm_min = MINIMUM(algorithm_min, len)(((algorithm_min) < (len)) ? (algorithm_min) : (len)); | ||||||
581 | } | ||||||
582 | |||||||
583 | error = found = 0; | ||||||
584 | line = NULL((void *)0); | ||||||
585 | linesize = 0; | ||||||
586 | while ((linelen = getline(&line, &linesize, listfp)) != -1) { | ||||||
587 | tmpline = line; | ||||||
588 | base64 = 0; | ||||||
589 | if (line[linelen - 1] == '\n') | ||||||
590 | line[linelen - 1] = '\0'; | ||||||
591 | while (isspace((unsigned char)*tmpline)) | ||||||
592 | tmpline++; | ||||||
593 | |||||||
594 | /* | ||||||
595 | * Crack the line into an algorithm, filename, and checksum. | ||||||
596 | * Lines are of the form: | ||||||
597 | * ALGORITHM (FILENAME) = CHECKSUM | ||||||
598 | * | ||||||
599 | * Fallback on GNU form: | ||||||
600 | * CHECKSUM FILENAME | ||||||
601 | */ | ||||||
602 | p = strchr(tmpline, ' '); | ||||||
603 | if (p != NULL((void *)0) && *(p + 1) == '(') { | ||||||
604 | /* BSD form */ | ||||||
605 | *p = '\0'; | ||||||
606 | algorithm = tmpline; | ||||||
607 | len = strlen(algorithm); | ||||||
608 | if (len > algorithm_max || len < algorithm_min) | ||||||
609 | continue; | ||||||
610 | |||||||
611 | filename = p + 2; | ||||||
612 | p = strrchr(filename, ')'); | ||||||
613 | if (p == NULL((void *)0) || strncmp(p + 1, " = ", (size_t)3) != 0) | ||||||
614 | continue; | ||||||
615 | *p = '\0'; | ||||||
616 | |||||||
617 | checksum = p + 4; | ||||||
618 | p = strpbrk(checksum, " \t\r"); | ||||||
619 | if (p != NULL((void *)0)) | ||||||
620 | *p = '\0'; | ||||||
621 | |||||||
622 | /* | ||||||
623 | * Check that the algorithm is one we recognize. | ||||||
624 | */ | ||||||
625 | for (hf = functions; hf->name != NULL((void *)0); hf++) { | ||||||
626 | if (strcasecmp(algorithm, hf->name) == 0) | ||||||
627 | break; | ||||||
628 | } | ||||||
629 | if (hf->name == NULL((void *)0) || *checksum == '\0') | ||||||
630 | continue; | ||||||
631 | } else { | ||||||
632 | /* could be GNU form */ | ||||||
633 | if ((hf = defhash) == NULL((void *)0)) | ||||||
634 | continue; | ||||||
635 | algorithm = hf->name; | ||||||
636 | checksum = tmpline; | ||||||
637 | if ((p = strchr(checksum, ' ')) == NULL((void *)0)) | ||||||
638 | continue; | ||||||
639 | if (hf->style == STYLE_CKSUM1) { | ||||||
640 | if ((p = strchr(p + 1, ' ')) == NULL((void *)0)) | ||||||
641 | continue; | ||||||
642 | } | ||||||
643 | *p++ = '\0'; | ||||||
644 | while (isspace((unsigned char)*p)) | ||||||
645 | p++; | ||||||
646 | if (*p == '\0') | ||||||
647 | continue; | ||||||
648 | filename = p; | ||||||
649 | p = strpbrk(filename, "\t\r"); | ||||||
650 | if (p != NULL((void *)0)) | ||||||
651 | *p = '\0'; | ||||||
652 | } | ||||||
653 | |||||||
654 | if (hf->style == STYLE_MD50) { | ||||||
655 | /* | ||||||
656 | * Check the length to see if this could be | ||||||
657 | * a valid digest. If hex, it will be 2x the | ||||||
658 | * size of the binary data. For base64, we have | ||||||
659 | * to check both with and without the '=' padding. | ||||||
660 | */ | ||||||
661 | len = strlen(checksum); | ||||||
662 | if (len != hf->digestlen * 2) { | ||||||
663 | size_t len2; | ||||||
664 | |||||||
665 | if (checksum[len - 1] == '=') { | ||||||
666 | /* use padding */ | ||||||
667 | len2 = 4 * ((hf->digestlen + 2) / 3); | ||||||
668 | } else { | ||||||
669 | /* no padding */ | ||||||
670 | len2 = (4 * hf->digestlen + 2) / 3; | ||||||
671 | } | ||||||
672 | if (len != len2) | ||||||
673 | continue; | ||||||
674 | base64 = 1; | ||||||
675 | } | ||||||
676 | } | ||||||
677 | found = 1; | ||||||
678 | |||||||
679 | /* | ||||||
680 | * If only a selection of files is wanted, proceed only | ||||||
681 | * if the filename matches one of those in the selection. | ||||||
682 | */ | ||||||
683 | if (sel != NULL((void *)0)) { | ||||||
684 | for (i = 0; i < selcount; i++) { | ||||||
685 | if (strcmp(sel[i], filename) == 0) { | ||||||
686 | sel_found[i] = 1; | ||||||
687 | break; | ||||||
688 | } | ||||||
689 | } | ||||||
690 | if (i == selcount) | ||||||
691 | continue; | ||||||
692 | } | ||||||
693 | |||||||
694 | if ((fp = fopen(filename, "r")) == NULL((void *)0)) { | ||||||
695 | warn("cannot open %s", filename); | ||||||
696 | (void)printf("(%s) %s: %s\n", algorithm, filename, | ||||||
697 | (errno(*__errno()) == ENOENT2 ? "MISSING" : "FAILED")); | ||||||
698 | error = 1; | ||||||
699 | continue; | ||||||
700 | } | ||||||
701 | |||||||
702 | hf->init(&context); | ||||||
703 | while ((nread = fread(data, 1UL, sizeof(data), fp)) > 0) | ||||||
704 | hf->update(&context, data, nread); | ||||||
705 | if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror )(fp))) { | ||||||
706 | warn("%s: read error", file); | ||||||
707 | error = 1; | ||||||
708 | fclose(fp); | ||||||
709 | continue; | ||||||
710 | } | ||||||
711 | fclose(fp); | ||||||
712 | digest_end(hf, &context, digest, sizeof(digest), base64); | ||||||
713 | |||||||
714 | if (base64) | ||||||
715 | cmp = strncmp(checksum, digest, len); | ||||||
716 | else | ||||||
717 | cmp = strcasecmp(checksum, digest); | ||||||
718 | if (cmp == 0) { | ||||||
719 | if (qflag == 0) | ||||||
720 | (void)printf("(%s) %s: OK\n", algorithm, | ||||||
721 | filename); | ||||||
722 | } else { | ||||||
723 | (void)printf("(%s) %s: FAILED\n", algorithm, filename); | ||||||
724 | error = 1; | ||||||
725 | } | ||||||
726 | } | ||||||
727 | free(line); | ||||||
728 | if (ferror(listfp)(!__isthreaded ? (((listfp)->_flags & 0x0040) != 0) : ( ferror)(listfp))) { | ||||||
729 | warn("%s: getline", file); | ||||||
730 | error = 1; | ||||||
731 | } | ||||||
732 | if (listfp != stdin(&__sF[0])) | ||||||
733 | fclose(listfp); | ||||||
734 | if (!found) | ||||||
735 | warnx("%s: no properly formatted checksum lines found", file); | ||||||
736 | if (sel_found != NULL((void *)0)) { | ||||||
737 | /* | ||||||
738 | * Mark found files by setting them to NULL so that we can | ||||||
739 | * detect files that are missing from the checklist later. | ||||||
740 | */ | ||||||
741 | for (i = 0; i < selcount; i++) { | ||||||
742 | if (sel_found[i]) | ||||||
743 | sel[i] = NULL((void *)0); | ||||||
744 | } | ||||||
745 | free(sel_found); | ||||||
746 | } | ||||||
747 | return(error || !found); | ||||||
748 | } | ||||||
749 | |||||||
750 | #define TEST_BLOCK_LEN10000 10000 | ||||||
751 | #define TEST_BLOCK_COUNT10000 10000 | ||||||
752 | |||||||
753 | void | ||||||
754 | digest_time(struct hash_list *hl, int times) | ||||||
755 | { | ||||||
756 | struct hash_function *hf; | ||||||
757 | struct rusage start, stop; | ||||||
758 | struct timeval res; | ||||||
759 | union ANY_CTX context; | ||||||
760 | u_int i; | ||||||
761 | u_char data[TEST_BLOCK_LEN10000]; | ||||||
762 | char digest[MAX_DIGEST_LEN128 + 1]; | ||||||
763 | double elapsed; | ||||||
764 | int count = TEST_BLOCK_COUNT10000; | ||||||
765 | while (--times > 0 && count < INT_MAX0x7fffffff / 10) | ||||||
766 | count *= 10; | ||||||
767 | |||||||
768 | TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = ( (hf)->tailq.tqe_next)) { | ||||||
769 | (void)printf("%s time trial. Processing %d %d-byte blocks...", | ||||||
770 | hf->name, count, TEST_BLOCK_LEN10000); | ||||||
771 | fflush(stdout(&__sF[1])); | ||||||
772 | |||||||
773 | /* Initialize data based on block number. */ | ||||||
774 | for (i = 0; i < TEST_BLOCK_LEN10000; i++) | ||||||
775 | data[i] = (u_char)(i & 0xff); | ||||||
776 | |||||||
777 | getrusage(RUSAGE_SELF0, &start); | ||||||
778 | hf->init(&context); | ||||||
779 | for (i = 0; i < count; i++) | ||||||
780 | hf->update(&context, data, (size_t)TEST_BLOCK_LEN10000); | ||||||
781 | digest_end(hf, &context, digest, sizeof(digest), hf->base64); | ||||||
782 | getrusage(RUSAGE_SELF0, &stop); | ||||||
783 | timersub(&stop.ru_utime, &start.ru_utime, &res)do { (&res)->tv_sec = (&stop.ru_utime)->tv_sec - (&start.ru_utime)->tv_sec; (&res)->tv_usec = ( &stop.ru_utime)->tv_usec - (&start.ru_utime)->tv_usec ; if ((&res)->tv_usec < 0) { (&res)->tv_sec-- ; (&res)->tv_usec += 1000000; } } while (0); | ||||||
784 | elapsed = (double)res.tv_sec + (double)res.tv_usec / 1000000.0; | ||||||
785 | |||||||
786 | (void)printf("\nDigest = %s\n", digest); | ||||||
787 | (void)printf("Time = %f seconds\n", elapsed); | ||||||
788 | (void)printf("Speed = %f bytes/second\n", | ||||||
789 | (double)TEST_BLOCK_LEN10000 * count / elapsed); | ||||||
790 | } | ||||||
791 | } | ||||||
792 | |||||||
793 | void | ||||||
794 | digest_test(struct hash_list *hl) | ||||||
795 | { | ||||||
796 | struct hash_function *hf; | ||||||
797 | union ANY_CTX context; | ||||||
798 | int i; | ||||||
799 | char digest[MAX_DIGEST_LEN128 + 1]; | ||||||
800 | unsigned char buf[1000]; | ||||||
801 | unsigned const char *test_strings[] = { | ||||||
802 | "", | ||||||
803 | "a", | ||||||
804 | "abc", | ||||||
805 | "message digest", | ||||||
806 | "abcdefghijklmnopqrstuvwxyz", | ||||||
807 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", | ||||||
808 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||||||
809 | "0123456789", | ||||||
810 | "12345678901234567890123456789012345678901234567890123456789" | ||||||
811 | "012345678901234567890", | ||||||
812 | }; | ||||||
813 | |||||||
814 | TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = ( (hf)->tailq.tqe_next)) { | ||||||
815 | (void)printf("%s test suite:\n", hf->name); | ||||||
816 | |||||||
817 | for (i = 0; i < 8; i++) { | ||||||
818 | hf->init(&context); | ||||||
819 | hf->update(&context, test_strings[i], | ||||||
820 | strlen(test_strings[i])); | ||||||
821 | digest_end(hf, &context, digest, sizeof(digest), | ||||||
822 | hf->base64); | ||||||
823 | digest_printstr(hf, test_strings[i], digest); | ||||||
824 | } | ||||||
825 | |||||||
826 | /* Now simulate a string of a million 'a' characters. */ | ||||||
827 | memset(buf, 'a', sizeof(buf)); | ||||||
828 | hf->init(&context); | ||||||
829 | for (i = 0; i < 1000; i++) | ||||||
830 | hf->update(&context, buf, sizeof(buf)); | ||||||
831 | digest_end(hf, &context, digest, sizeof(digest), hf->base64); | ||||||
832 | digest_print(hf, "one million 'a' characters", | ||||||
833 | digest); | ||||||
834 | } | ||||||
835 | } | ||||||
836 | #endif /* !defined(SHA2_ONLY) */ | ||||||
837 | |||||||
838 | void | ||||||
839 | usage(void) | ||||||
840 | { | ||||||
841 | #if !defined(SHA2_ONLY) | ||||||
842 | if (strcmp(__progname, "cksum") == 0) | ||||||
843 | fprintf(stderr(&__sF[2]), "usage: %s [-bcpqrtx] [-a algorithms] [-C checklist] " | ||||||
844 | "[-h hashfile]\n" | ||||||
845 | " [-s string] [file ...]\n", | ||||||
846 | __progname); | ||||||
847 | else | ||||||
848 | #endif /* !defined(SHA2_ONLY) */ | ||||||
849 | fprintf(stderr(&__sF[2]), "usage:" | ||||||
850 | "\t%s [-bcpqrtx] [-C checklist] [-h hashfile] [-s string] " | ||||||
851 | "[file ...]\n", | ||||||
852 | __progname); | ||||||
853 | |||||||
854 | exit(EXIT_FAILURE1); | ||||||
855 | } |