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