| File: | src/bin/md5/md5.c |
| Warning: | line 396, column 9 Potential leak of memory pointed to by 'hf' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | |||||||
| 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
| ||||||
| 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 | /* | ||||||
| 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 | |||||||
| 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_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 | |||||||
| 790 | void | ||||||
| 791 | digest_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 | |||||||
| 835 | void | ||||||
| 836 | usage(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 | } |