Bug Summary

File:src/bin/md5/md5.c
Warning:line 396, column 9
Potential leak of memory pointed to by 'hl.tqh_first'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name md5.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/bin/md5/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/bin/md5 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/bin/md5/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/bin/md5/md5.c
1/* $OpenBSD: md5.c,v 1.97 2020/10/19 18:15:18 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
56union 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
66struct 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
186TAILQ_HEAD(hash_list, hash_function)struct hash_list { struct hash_function *tqh_first; struct hash_function
**tqh_last; }
;
187
188void digest_end(const struct hash_function *, void *, char *, size_t, int);
189int digest_file(const char *, struct hash_list *, int);
190void digest_print(const struct hash_function *, const char *, const char *);
191#if !defined(SHA2_ONLY)
192int digest_filelist(const char *, struct hash_function *, int, char **);
193void digest_printstr(const struct hash_function *, const char *, const char *);
194void digest_string(char *, struct hash_list *);
195void digest_test(struct hash_list *);
196void digest_time(struct hash_list *, int);
197#endif /* !defined(SHA2_ONLY) */
198void hash_insert(struct hash_list *, struct hash_function *, int);
199void usage(void) __attribute__((__noreturn__));
200
201extern char *__progname;
202int qflag = 0;
203FILE *ofile = NULL((void *)0);
204
205int
206main(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)
1
Assuming the condition is false
2
Taking false branch
217 err(1, "pledge");
218
219 TAILQ_INIT(&hl)do { (&hl)->tqh_first = ((void *)0); (&hl)->tqh_last
= &(&hl)->tqh_first; } while (0)
;
3
Loop condition is false. Exiting loop
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)
4
Taking true branch
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) {
5
Assuming the condition is false
6
Loop condition is false. Execution continues on line 241
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) {
7
Assuming the condition is false
8
Loop condition is false. Execution continues on line 324
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
8.1
'ofile' is equal to NULL
== NULL((void *)0))
9
Taking true branch
328 ofile = stdout(&__sF[1]);
329
330 if (pledge("stdio rpath", NULL((void *)0)) == -1)
10
Assuming the condition is false
11
Taking false branch
331 err(1, "pledge");
332
333 /* Most arguments are mutually exclusive */
334 fl = pflag + (tflag
11.1
'tflag' is 0
? 1 : 0) + xflag + cflag + (input_string != NULL((void *)0));
12
'?' condition is false
335 if (fl
12.1
'fl' is <= 1
> 1 || (fl
12.2
'fl' is 0
&& argc && cflag == 0) || (rflag
12.3
'rflag' is 0
&& qflag) ||
336 (selective_checklist
12.4
'selective_checklist' is equal to NULL
!= NULL((void *)0) && argc == 0))
337 usage();
338 if (selective_checklist
12.5
'selective_checklist' is null
|| cflag
12.6
'cflag' is 0
) {
13
Taking false branch
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))) {
14
Taking true branch
346 for (hf = functions; hf->name != NULL((void *)0); hf++) {
15
Assuming field 'name' is equal to NULL
16
Loop condition is false. Execution continues on line 350
347 if (strcasecmp(hf->name, __progname) == 0)
348 break;
349 }
350 if (hf->name
16.1
Field 'name' is equal to NULL
== NULL((void *)0))
17
Taking true branch
351 hf = &functions[0]; /* default to cksum */
352 hash_insert(&hl, hf, (hf->base64 == -1 ? 0 : bflag));
18
Assuming the condition is true
19
'?' condition is true
20
Calling 'hash_insert'
25
Returned allocated memory
353 }
354
355 if (rflag
25.1
'rflag' is 0
|| qflag
25.2
'qflag' is 0
) {
26
Taking false branch
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
26.1
'tflag' is 0
)
27
Taking false branch
364 digest_time(&hl, tflag);
365 else if (xflag
27.1
'xflag' is 0
)
28
Taking false branch
366 digest_test(&hl);
367 else if (input_string
28.1
'input_string' is null
)
29
Taking false branch
368 digest_string(input_string, &hl);
369 else if (selective_checklist
29.1
'selective_checklist' is null
) {
30
Taking false branch
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
30.1
'cflag' is 0
) {
31
Taking false branch
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
31.1
'pflag' is 0
|| argc == 0)
32
Assuming 'argc' is not equal to 0
33
Taking false branch
391 error = digest_file("-", &hl, pflag);
392 else
393 while (argc--)
34
Loop condition is false. Execution continues on line 396
394 error += digest_file(*argv++, &hl, 0);
395
396 return(error ? EXIT_FAILURE1 : EXIT_SUCCESS0);
35
Potential leak of memory pointed to by 'hl.tqh_first'
397}
398
399void
400hash_insert(struct hash_list *hl, struct hash_function *hf, int base64)
401{
402 struct hash_function *hftmp;
403
404 hftmp = malloc(sizeof(*hftmp));
21
Memory is allocated
405 if (hftmp == NULL((void *)0))
22
Assuming 'hftmp' is not equal to NULL
23
Taking false branch
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)
;
24
Loop condition is false. Exiting loop
410}
411
412void
413digest_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)
431void
432digest_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
448void
449digest_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)
466void
467digest_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
484int
485digest_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 */
546int
547digest_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 /*
632 * Check the length to see if this could be
633 * a valid checksum. If hex, it will be 2x the
634 * size of the binary data. For base64, we have
635 * to check both with and without the '=' padding.
636 */
637 len = strlen(checksum);
638 if (len != hf->digestlen * 2) {
639 size_t len2;
640
641 if (checksum[len - 1] == '=') {
642 /* use padding */
643 len2 = 4 * ((hf->digestlen + 2) / 3);
644 } else {
645 /* no padding */
646 len2 = (4 * hf->digestlen + 2) / 3;
647 }
648 if (len != len2)
649 continue;
650 base64 = 1;
651 }
652 } else {
653 /* could be GNU form */
654 if ((hf = defhash) == NULL((void *)0))
655 continue;
656 algorithm = hf->name;
657 checksum = tmpline;
658 if ((p = strchr(checksum, ' ')) == NULL((void *)0))
659 continue;
660 if (hf->style == STYLE_CKSUM1) {
661 if ((p = strchr(p + 1, ' ')) == NULL((void *)0))
662 continue;
663 }
664 *p++ = '\0';
665 while (isspace((unsigned char)*p))
666 p++;
667 if (*p == '\0')
668 continue;
669 filename = p;
670 p = strpbrk(filename, "\t\r");
671 if (p != NULL((void *)0))
672 *p = '\0';
673 }
674 found = 1;
675
676 /*
677 * If only a selection of files is wanted, proceed only
678 * if the filename matches one of those in the selection.
679 */
680 if (sel != NULL((void *)0)) {
681 for (i = 0; i < selcount; i++) {
682 if (strcmp(sel[i], filename) == 0) {
683 sel_found[i] = 1;
684 break;
685 }
686 }
687 if (i == selcount)
688 continue;
689 }
690
691 if ((fp = fopen(filename, "r")) == NULL((void *)0)) {
692 warn("cannot open %s", filename);
693 (void)printf("(%s) %s: %s\n", algorithm, filename,
694 (errno(*__errno()) == ENOENT2 ? "MISSING" : "FAILED"));
695 error = 1;
696 continue;
697 }
698
699 hf->init(&context);
700 while ((nread = fread(data, 1UL, sizeof(data), fp)) > 0)
701 hf->update(&context, data, nread);
702 if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror
)(fp))
) {
703 warn("%s: read error", file);
704 error = 1;
705 fclose(fp);
706 continue;
707 }
708 fclose(fp);
709 digest_end(hf, &context, digest, sizeof(digest), base64);
710
711 if (base64)
712 cmp = strncmp(checksum, digest, len);
713 else
714 cmp = strcasecmp(checksum, digest);
715 if (cmp == 0) {
716 if (qflag == 0)
717 (void)printf("(%s) %s: OK\n", algorithm,
718 filename);
719 } else {
720 (void)printf("(%s) %s: FAILED\n", algorithm, filename);
721 error = 1;
722 }
723 }
724 free(line);
725 if (ferror(listfp)(!__isthreaded ? (((listfp)->_flags & 0x0040) != 0) : (
ferror)(listfp))
) {
726 warn("%s: getline", file);
727 error = 1;
728 }
729 if (listfp != stdin(&__sF[0]))
730 fclose(listfp);
731 if (!found)
732 warnx("%s: no properly formatted checksum lines found", file);
733 if (sel_found != NULL((void *)0)) {
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((void *)0);
741 }
742 free(sel_found);
743 }
744 return(error || !found);
745}
746
747#define TEST_BLOCK_LEN10000 10000
748#define TEST_BLOCK_COUNT10000 10000
749
750void
751digest_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_LEN10000];
759 char digest[MAX_DIGEST_LEN128 + 1];
760 double elapsed;
761 int count = TEST_BLOCK_COUNT10000;
762 while (--times > 0 && count < INT_MAX2147483647 / 10)
763 count *= 10;
764
765 TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = (
(hf)->tailq.tqe_next))
{
766 (void)printf("%s time trial. Processing %d %d-byte blocks...",
767 hf->name, count, TEST_BLOCK_LEN10000);
768 fflush(stdout(&__sF[1]));
769
770 /* Initialize data based on block number. */
771 for (i = 0; i < TEST_BLOCK_LEN10000; i++)
772 data[i] = (u_char)(i & 0xff);
773
774 getrusage(RUSAGE_SELF0, &start);
775 hf->init(&context);
776 for (i = 0; i < count; i++)
777 hf->update(&context, data, (size_t)TEST_BLOCK_LEN10000);
778 digest_end(hf, &context, digest, sizeof(digest), hf->base64);
779 getrusage(RUSAGE_SELF0, &stop);
780 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)
;
781 elapsed = (double)res.tv_sec + (double)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_LEN10000 * count / elapsed);
787 }
788}
789
790void
791digest_test(struct hash_list *hl)
792{
793 struct hash_function *hf;
794 union ANY_CTX context;
795 int i;
796 char digest[MAX_DIGEST_LEN128 + 1];
797 unsigned char buf[1000];
798 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 TAILQ_FOREACH(hf, hl, tailq)for((hf) = ((hl)->tqh_first); (hf) != ((void *)0); (hf) = (
(hf)->tailq.tqe_next))
{
812 (void)printf("%s test suite:\n", hf->name);
813
814 for (i = 0; i < 8; i++) {
815 hf->init(&context);
816 hf->update(&context, test_strings[i],
817 strlen(test_strings[i]));
818 digest_end(hf, &context, digest, sizeof(digest),
819 hf->base64);
820 digest_printstr(hf, test_strings[i], digest);
821 }
822
823 /* Now simulate a string of a million 'a' characters. */
824 memset(buf, 'a', sizeof(buf));
825 hf->init(&context);
826 for (i = 0; i < 1000; i++)
827 hf->update(&context, buf, sizeof(buf));
828 digest_end(hf, &context, digest, sizeof(digest), hf->base64);
829 digest_print(hf, "one million 'a' characters",
830 digest);
831 }
832}
833#endif /* !defined(SHA2_ONLY) */
834
835void
836usage(void)
837{
838#if !defined(SHA2_ONLY)
839 if (strcmp(__progname, "cksum") == 0)
840 fprintf(stderr(&__sF[2]), "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 fprintf(stderr(&__sF[2]), "usage:"
847 "\t%s [-bcpqrtx] [-C checklist] [-h hashfile] [-s string] "
848 "[file ...]\n",
849 __progname);
850
851 exit(EXIT_FAILURE1);
852}