1 /* $NetBSD: cksum.c,v 1.53 2026/04/04 14:19:48 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * James W. Williams of NASA Goddard Space Flight Center. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /*- 36 * Copyright (c) 1997 Jason R. Thorpe. All rights reserved. 37 * 38 * This code is derived from software contributed to Berkeley by 39 * James W. Williams of NASA Goddard Space Flight Center. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. All advertising materials mentioning features or use of this software 50 * must display the following acknowledgement: 51 * This product includes software developed by the University of 52 * California, Berkeley and its contributors. 53 * 4. Neither the name of the University nor the names of its contributors 54 * may be used to endorse or promote products derived from this software 55 * without specific prior written permission. 56 * 57 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 58 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 60 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 61 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 63 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 64 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 65 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 66 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 67 * SUCH DAMAGE. 68 */ 69 70 #if HAVE_NBTOOL_CONFIG_H 71 #include "nbtool_config.h" 72 #endif 73 74 #include <sys/cdefs.h> 75 #if defined(__COPYRIGHT) && !defined(lint) 76 __COPYRIGHT("@(#) Copyright (c) 1991, 1993\ 77 The Regents of the University of California. All rights reserved."); 78 #endif /* not lint */ 79 80 #if defined(__RCSID) && !defined(lint) 81 #if 0 82 static char sccsid[] = "@(#)cksum.c 8.2 (Berkeley) 4/28/95"; 83 #endif 84 __RCSID("$NetBSD: cksum.c,v 1.53 2026/04/04 14:19:48 martin Exp $"); 85 #endif /* not lint */ 86 87 #include <sys/types.h> 88 89 #include <ctype.h> 90 #include <err.h> 91 #include <errno.h> 92 #include <fcntl.h> 93 #include <locale.h> 94 #include <md2.h> 95 #include <md4.h> 96 #include <md5.h> 97 #include <rmd160.h> 98 #include <sha1.h> 99 #include <sha2.h> 100 #include <stdio.h> 101 #include <stdlib.h> 102 #include <string.h> 103 #include <unistd.h> 104 #include <vis.h> 105 106 #include "extern.h" 107 108 #define PRINT_NORMAL 0x01 109 #define PRINT_QUIET 0x02 110 111 typedef char *(*_filefunc)(const char *, char *); 112 113 const struct hash { 114 const char *progname; 115 const char *hashname; 116 void (*stringfunc)(const char *); 117 void (*timetrialfunc)(void); 118 void (*testsuitefunc)(void); 119 void (*filterfunc)(int); 120 char *(*filefunc)(const char *, char *); 121 } hashes[] = { 122 { "md2", "MD2", 123 MD2String, MD2TimeTrial, MD2TestSuite, 124 MD2Filter, MD2File }, 125 { "md4", "MD4", 126 MD4String, MD4TimeTrial, MD4TestSuite, 127 MD4Filter, MD4File }, 128 { "md5", "MD5", 129 MD5String, MD5TimeTrial, MD5TestSuite, 130 MD5Filter, MD5File }, 131 { "rmd160", "RMD160", 132 RMD160String, RMD160TimeTrial, RMD160TestSuite, 133 RMD160Filter, (_filefunc) RMD160File }, 134 { "sha1", "SHA1", 135 SHA1String, SHA1TimeTrial, SHA1TestSuite, 136 SHA1Filter, (_filefunc) SHA1File }, 137 { "sha256", "SHA256", 138 SHA256_String, SHA256_TimeTrial, SHA256_TestSuite, 139 SHA256_Filter, (_filefunc) SHA256_File }, 140 { "sha384", "SHA384", 141 SHA384_String, SHA384_TimeTrial, SHA384_TestSuite, 142 SHA384_Filter, (_filefunc) SHA384_File }, 143 { "sha512", "SHA512", 144 SHA512_String, SHA512_TimeTrial, SHA512_TestSuite, 145 SHA512_Filter, (_filefunc) SHA512_File }, 146 { .progname = NULL, }, 147 }; 148 149 static int hash_digest_file(char *, const struct hash *, int); 150 __dead static void requirehash(const char *); 151 __dead static void usage(void); 152 153 int 154 main(int argc, char **argv) 155 { 156 int ch, fd, rval, pflag, nohashstdin; 157 uint32_t val; 158 off_t len; 159 char *fn; 160 const char *progname; 161 int (*cfncn) (int, uint32_t *, off_t *); 162 void (*pfncn) (char *, uint32_t, off_t); 163 const struct hash *hash; 164 int i, check_warn, do_check; 165 int print_flags; 166 167 cfncn = NULL; 168 pfncn = NULL; 169 pflag = nohashstdin = 0; 170 check_warn = 0; 171 do_check = 0; 172 print_flags = 0; 173 174 setlocale(LC_ALL, ""); 175 176 progname = getprogname(); 177 178 for (hash = hashes; hash->hashname != NULL; hash++) 179 if (strcmp(progname, hash->progname) == 0) 180 break; 181 182 if (hash->hashname == NULL) { 183 hash = NULL; 184 185 if (!strcmp(progname, "sum")) { 186 cfncn = csum1; 187 pfncn = psum1; 188 } else { 189 cfncn = crc; 190 pfncn = pcrc; 191 } 192 } 193 194 while ((ch = getopt(argc, argv, "a:cno:pqs:twx")) != -1) 195 switch(ch) { 196 case 'a': 197 if (hash) { 198 warnx("illegal use of -a option"); 199 usage(); 200 } 201 i = 0; 202 while (hashes[i].hashname != NULL) { 203 if (!strcasecmp(hashes[i].hashname, optarg)) { 204 hash = &hashes[i]; 205 break; 206 } 207 i++; 208 } 209 if (hash == NULL) { 210 if (!strcasecmp(optarg, "old1")) { 211 cfncn = csum1; 212 pfncn = psum1; 213 } else if (!strcasecmp(optarg, "old2")) { 214 cfncn = csum2; 215 pfncn = psum2; 216 } else if (!strcasecmp(optarg, "crc")) { 217 cfncn = crc; 218 pfncn = pcrc; 219 } else { 220 warnx("illegal argument to -a option"); 221 usage(); 222 } 223 } 224 break; 225 case 'c': 226 do_check = 1; 227 break; 228 case 'n': 229 print_flags |= PRINT_NORMAL; 230 break; 231 case 'o': 232 if (hash) { 233 warnx("%s mutually exclusive with sum", 234 hash->hashname); 235 usage(); 236 } 237 if (!strcmp(optarg, "1")) { 238 cfncn = csum1; 239 pfncn = psum1; 240 } else if (!strcmp(optarg, "2")) { 241 cfncn = csum2; 242 pfncn = psum2; 243 } else { 244 warnx("illegal argument to -o option"); 245 usage(); 246 } 247 break; 248 case 'p': 249 if (hash == NULL) 250 requirehash("-p"); 251 pflag = 1; 252 break; 253 case 'q': 254 print_flags |= PRINT_QUIET; 255 break; 256 case 's': 257 if (hash == NULL) 258 requirehash("-s"); 259 nohashstdin = 1; 260 hash->stringfunc(optarg); 261 break; 262 case 't': 263 if (hash == NULL) 264 requirehash("-t"); 265 nohashstdin = 1; 266 hash->timetrialfunc(); 267 break; 268 case 'w': 269 check_warn = 1; 270 break; 271 case 'x': 272 if (hash == NULL) 273 requirehash("-x"); 274 nohashstdin = 1; 275 hash->testsuitefunc(); 276 break; 277 case '?': 278 default: 279 usage(); 280 } 281 argc -= optind; 282 argv += optind; 283 284 if (do_check) { 285 /* 286 * Verify checksums 287 */ 288 FILE *f; 289 char buf[BUFSIZ]; 290 char *s, *p_filename, *p_cksum; 291 int l_filename, l_cksum; 292 char filename[BUFSIZ]; 293 char safe_filename[BUFSIZ*4+1]; 294 char cksum[BUFSIZ]; 295 int ok,cnt,badcnt; 296 297 rval = 0; 298 cnt = badcnt = 0; 299 300 if (argc == 0) { 301 f = fdopen(STDIN_FILENO, "r"); 302 } else { 303 f = fopen(argv[0], "r"); 304 } 305 if (f == NULL) 306 err(1, "Cannot read %s", 307 argc>0?argv[0]:"stdin"); 308 309 while(fgets(buf, sizeof(buf), f) != NULL) { 310 s = strrchr(buf, '\n'); 311 if (s) 312 *s = '\0'; 313 314 p_cksum = p_filename = NULL; 315 316 p_filename = strchr(buf, '('); 317 if (p_filename) { 318 /* 319 * Assume 'normal' output if there's a '(' 320 */ 321 p_filename += 1; 322 print_flags &= ~(PRINT_NORMAL); 323 324 p_cksum = strrchr(p_filename, ')'); 325 if (p_cksum == NULL) { 326 if (check_warn) 327 warnx("bogus format: %s. " 328 "Skipping...", 329 buf); 330 rval = 1; 331 continue; 332 } 333 p_cksum += 4; 334 335 l_cksum = strlen(p_cksum); 336 l_filename = p_cksum - p_filename - 4; 337 338 /* Sanity check, and find proper hash if 339 * it's not the same as the current program 340 */ 341 if (hash == NULL || 342 strncmp(buf, hash->hashname, 343 strlen(hash->hashname)) != 0) { 344 /* 345 * Search proper hash 346 */ 347 const struct hash *nhash; 348 349 for (nhash = hashes ; 350 nhash->hashname != NULL; 351 nhash++) 352 if (strncmp(buf, 353 nhash->hashname, 354 strlen(nhash->hashname)) == 0) 355 break; 356 357 358 if (nhash->hashname == NULL) { 359 if (check_warn) 360 warnx("unknown hash: %s", 361 buf); 362 rval = 1; 363 continue; 364 } else { 365 hash = nhash; 366 } 367 } 368 369 } else { 370 if (hash) { 371 int nspaces; 372 373 /* 374 * 'normal' output, no (ck)sum 375 */ 376 print_flags |= PRINT_NORMAL; 377 nspaces = 1; 378 379 p_cksum = buf; 380 p_filename = strchr(buf, ' '); 381 if (p_filename == NULL) { 382 if (check_warn) 383 warnx("no filename in %s? " 384 "Skipping...", buf); 385 rval = 1; 386 continue; 387 } 388 while (isspace((unsigned char)*++p_filename)) 389 nspaces++; 390 l_filename = strlen(p_filename); 391 l_cksum = p_filename - buf - nspaces; 392 } else { 393 /* 394 * sum/cksum output format 395 */ 396 p_cksum = buf; 397 s=strchr(p_cksum, ' '); 398 if (s == NULL) { 399 if (check_warn) 400 warnx("bogus format: %s." 401 " Skipping...", 402 buf); 403 rval = 1; 404 continue; 405 } 406 l_cksum = s - p_cksum; 407 408 p_filename = strrchr(buf, ' '); 409 if (p_filename == NULL) { 410 if (check_warn) 411 warnx("no filename in %s?" 412 " Skipping...", 413 buf); 414 rval = 1; 415 continue; 416 } 417 p_filename++; 418 l_filename = strlen(p_filename); 419 } 420 } 421 422 strlcpy(filename, p_filename, l_filename+1); 423 strlcpy(cksum, p_cksum, l_cksum+1); 424 425 if (strnvis(safe_filename, sizeof(safe_filename), 426 filename, VIS_META|VIS_CSTYLE) == -1) { 427 if (check_warn) 428 warnx("strnvis failed, can not print " 429 "filename"); 430 rval = 1; 431 continue; 432 } 433 434 if (hash) { 435 char *h; 436 437 if (access(filename, R_OK) == 0 438 && (h = hash->filefunc(filename, NULL)) != NULL 439 && strcmp(cksum, h) == 0) 440 ok = 1; 441 else 442 ok = 0; 443 } else { 444 if ((fd = open(filename, O_RDONLY, 0)) < 0) { 445 if (check_warn) 446 warn("%s", safe_filename); 447 rval = 1; 448 ok = 0; 449 } else { 450 if (cfncn(fd, &val, &len)) 451 ok = 0; 452 else { 453 uint32_t should_val; 454 455 should_val = 456 strtoul(cksum, NULL, 10); 457 if (val == should_val) 458 ok = 1; 459 else 460 ok = 0; 461 } 462 close(fd); 463 } 464 } 465 466 if (! ok) { 467 if (hash) 468 printf("(%s) ", hash->hashname); 469 printf("%s: FAILED\n", safe_filename); 470 badcnt++; 471 } 472 cnt++; 473 474 } 475 fclose(f); 476 477 if (badcnt > 0) 478 rval = 1; 479 480 } else { 481 /* 482 * Calculate checksums 483 */ 484 485 fd = STDIN_FILENO; 486 fn = NULL; 487 rval = 0; 488 do { 489 if (*argv) { 490 fn = *argv++; 491 if (hash != NULL) { 492 if (hash_digest_file(fn, hash, print_flags)) { 493 warn("%s", fn); 494 rval = 1; 495 } 496 continue; 497 } 498 if ((fd = open(fn, O_RDONLY, 0)) < 0) { 499 warn("%s", fn); 500 rval = 1; 501 continue; 502 } 503 } else if (hash && !nohashstdin) { 504 hash->filterfunc(pflag); 505 } 506 507 if (hash == NULL) { 508 if (cfncn(fd, &val, &len)) { 509 warn("%s", fn ? fn : "stdin"); 510 rval = 1; 511 } else 512 pfncn(fn, val, len); 513 (void)close(fd); 514 } 515 } while (*argv); 516 } 517 exit(rval); 518 } 519 520 static int 521 hash_digest_file(char *fn, const struct hash *hash, int flags) 522 { 523 char *cp; 524 525 cp = hash->filefunc(fn, NULL); 526 if (cp == NULL) 527 return 1; 528 529 if (flags & PRINT_QUIET) 530 printf("%s\n", cp); 531 else if (flags & PRINT_NORMAL) 532 printf("%s %s\n", cp, fn); 533 else 534 printf("%s (%s) = %s\n", hash->hashname, fn, cp); 535 536 free(cp); 537 538 return 0; 539 } 540 541 static void 542 requirehash(const char *flg) 543 { 544 warnx("%s flag requires `-a algorithm'", flg); 545 usage(); 546 } 547 548 static void 549 usage(void) 550 { 551 const char fileargs[] = "[file ... | -c [-w] [sumfile]]"; 552 const char sumargs[] = "[-n] [-a algorithm [-ptx] [-s string]] [-o 1|2]"; 553 const char hashargs[] = "[-nptx] [-s string]"; 554 555 (void)fprintf(stderr, "usage: cksum %s\n %s\n", 556 sumargs, fileargs); 557 (void)fprintf(stderr, " sum %s\n %s\n", 558 sumargs, fileargs); 559 (void)fprintf(stderr, " md2 %s %s\n", hashargs, fileargs); 560 (void)fprintf(stderr, " md4 %s %s\n", hashargs, fileargs); 561 (void)fprintf(stderr, " md5 %s %s\n", hashargs, fileargs); 562 (void)fprintf(stderr, " rmd160 %s %s\n", hashargs, fileargs); 563 (void)fprintf(stderr, " sha1 %s %s\n", hashargs, fileargs); 564 exit(1); 565 } 566