00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifndef lint
00039 static char copyright[] =
00040 "@(#) Copyright (c) 2002\n\
00041 Netherlands Forensic Institute. All rights reserved.\n";
00042 #endif
00043
00044 #ifdef HAVE_CONFIG_H
00045 #include <config.h>
00046 #endif
00047
00048 #include <ctype.h>
00049 #include <errno.h>
00050 #include <limits.h>
00051 #include <stdlib.h>
00052 #include <stdio.h>
00053 #include <string.h>
00054 #include <sys/types.h>
00055 #include <sys/stat.h>
00056 #include <fcntl.h>
00057 #include <unistd.h>
00058 #include <assert.h>
00059
00060 #ifdef HAVE_OPENSSL
00061 #include <openssl/md5.h>
00062 #include <openssl/sha.h>
00063 #endif
00064
00065 #ifdef HAVE_OPENSSL
00066 #include <openssl/crypto.h>
00067 #else
00068 #error "Sorry, we need the openssl crypto lib to compile"
00069 #endif
00070
00071 #if defined(HAVE_LIBZ)
00072 #include <zlib.h>
00073 #else
00074 #error "Sorry, we need zlib to compile"
00075 #endif
00076
00077 #include "rdd.h"
00078 #include "reader.h"
00079 #include "writer.h"
00080 #include "filter.h"
00081 #include "filterset.h"
00082 #include "msgprinter.h"
00083 #include "rdd_internals.h"
00084 #include "error.h"
00085 #include "commandline.h"
00086
00087
00088
00089 #define VFY_MD5 0x1
00090 #define VFY_SHA1 0x2
00091 #define VFY_ADLER32 0x4
00092 #define VFY_CRC32 0x8
00093
00094 #define READ_SIZE 262144
00095 #define bool2str(b) ((b) ? "yes" : "no")
00096
00097 static struct verifier_opts {
00098 char **files;
00099 unsigned nfile;
00100 char *crc32file;
00101 char *adler32file;
00102 int verbose;
00103 int md5;
00104 int sha1;
00105 rdd_count_t progresslen;
00106 char *md5digest;
00107 char *sha1digest;
00108 } opts;
00109
00110 typedef rdd_checksum_t (*checksum_fun)(rdd_checksum_t, const unsigned char *, size_t);
00111
00112 static char *usage_message = "rdd-verify [local options] file1 ... \n";
00113
00114 static RDD_OPTION opttab[] = {
00115 {"-?", "--help", 0, 0, "Print this message", 0, 0},
00116 {"-A", "--adler32", "<file>", 0, "verify Adler32 checksums in <file> against input files", 0, 0},
00117 {"-C", "--checksum", "<file>", 0, "verify Adler32 checksums in <file> against input files", 0, 0},
00118 {"-c", "--crc32", "<file>", 0, "verify CRC32 checksums in <file> against input files", 0, 0},
00119 {"-m", "--md5", "<md5 digest>", 0, "verify MD5 hash", 0, 0},
00120 {"-s", "--sha1", "<sha-1 digest>", 0, "verify SHA1 hash", 0, 0},
00121 {"-V", "--version", 0, 0, "Report version number and exit", 0, 0},
00122 {"-v", "--verbose", 0, 0, "Be verbose", 0, 0},
00123 {0, 0, 0, 0, 0, 0, 0}
00124
00125 };
00126
00127 static void
00128 process_options(void)
00129 {
00130 char *arg;
00131
00132 if (rdd_opt_set(opttab, "help")) {
00133 rdd_opt_usage(opttab, 0, EXIT_SUCCESS);
00134 }
00135
00136 if (rdd_opt_set(opttab, "version")) {
00137 fprintf(stderr, "%s version %s\n", PACKAGE, VERSION);
00138 exit(EXIT_SUCCESS);
00139 }
00140
00141 opts.verbose = rdd_opt_set(opttab, "verbose");
00142 if (rdd_opt_set_arg(opttab, "md5",&arg)) {
00143 opts.md5 = 1;
00144 opts.md5digest = arg;
00145 }
00146 if (rdd_opt_set_arg(opttab, "sha1", &arg)) {
00147 opts.sha1 = 1;
00148 opts.sha1digest = arg;
00149 }
00150 if (rdd_opt_set_arg(opttab, "adler32", &arg)) {
00151 opts.adler32file = arg;
00152 }
00153 if (rdd_opt_set_arg(opttab, "crc32", &arg)) {
00154 opts.crc32file = arg;
00155 }
00156 if ((!opts.md5) && (!opts.sha1)
00157 && (opts.adler32file == NULL) && (opts.crc32file == NULL)) {
00158 rdd_opt_usage(opttab, 0, EXIT_FAILURE);
00159 }
00160 }
00161
00162 static void
00163 command_line(int argc, char **argv)
00164 {
00165 RDD_OPTION *od;
00166 unsigned i;
00167 char *opt;
00168 char *arg;
00169
00170 for (i = 1; i < (unsigned) argc; i++) {
00171 if ((od = rdd_get_opt_with_arg(opttab, argv, argc, &i, &opt, &arg)) == 0) {
00172 break;
00173 }
00174 }
00175
00176 process_options();
00177
00178 if (argc - i < 1) {
00179 rdd_opt_usage(opttab, 0, EXIT_FAILURE);
00180 }
00181
00182 opts.files = &argv[i];
00183 opts.nfile = argc - i;
00184 }
00185
00186 static u_int16_t
00187 swap16(u_int16_t n)
00188 {
00189 return ((n << 8) & 0xff00) | ((n >> 8) & 0x00ff);
00190 }
00191
00192 static u_int32_t
00193 swap32(u_int32_t n)
00194 {
00195 return
00196 ((n << 24) & 0xff000000)
00197 | ((n << 8) & 0x00ff0000)
00198 | ((n >> 8) & 0x0000ff00)
00199 | ((n >> 24) & 0x000000ff)
00200 ;
00201 }
00202
00203 static u_int64_t
00204 swap64(u_int64_t n)
00205 {
00206 u_int64_t lo_swapped, hi_swapped;
00207 u_int32_t lo, hi;
00208
00209 lo = (u_int32_t) (n & 0xffffffff);
00210 hi = (u_int32_t) ((n >> 32) & 0xffffffff);
00211
00212 lo_swapped = (u_int64_t) swap32(lo);
00213 hi_swapped = (u_int64_t) swap32(hi);
00214
00215 return (lo_swapped << 32) | hi_swapped;
00216 }
00217
00218 static RDD_READER *
00219 open_image_file(const char *path)
00220 {
00221 RDD_READER *reader = 0;
00222 int rc;
00223
00224 if ((rc = rdd_open_file_reader(&reader, path, 0)) != RDD_OK) {
00225 rdd_error(rc, "cannot open %s", path);
00226 }
00227
00228 return reader;
00229 }
00230
00231 static void
00232 close_image_file(const char *path, RDD_READER *reader)
00233 {
00234 int rc;
00235
00236 if ((rc = rdd_reader_close(reader, 1)) != RDD_OK) {
00237 rdd_error(rc, "cannot close %s", path);
00238 }
00239 }
00240
00241 static void
00242 swap_header(RDD_CHECKSUM_FILE_HEADER *hdr)
00243 {
00244 hdr->magic = swap16(hdr->magic);
00245 hdr->version = swap16(hdr->version);
00246 hdr->flags = swap16(hdr->flags);
00247 hdr->reserved = swap16(hdr->reserved);
00248 hdr->blocksize = swap32(hdr->blocksize);
00249 hdr->offset = swap64(hdr->offset);
00250 hdr->imagesize = swap64(hdr->imagesize);
00251 }
00252
00253 static void
00254 check_header(char *path, RDD_CHECKSUM_FILE_HEADER *header, int type, int *swap)
00255 {
00256 if (header->magic != RDD_CHECKSUM_MAGIC) {
00257 swap_header(header);
00258 *swap = 1;
00259 } else {
00260 *swap = 0;
00261 }
00262
00263 if (header->magic != RDD_CHECKSUM_MAGIC) {
00264 error("%s: header magic value is incorrect; "
00265 "expected %04x, got %04x",
00266 path, RDD_CHECKSUM_MAGIC, header->magic);
00267 }
00268
00269 if (header->version != RDD_CHECKSUM_VERSION) {
00270 error("%s: header version is incorrect; "
00271 "expected %04x, got %04x",
00272 path, RDD_CHECKSUM_VERSION, header->version);
00273 }
00274
00275 if ((header->flags & type) != type) {
00276 error("%s: the type found in the file is wrong; "
00277 "expected %04x got %04x",
00278 path, type, header->flags);
00279 }
00280 }
00281
00282 static FILE *
00283 open_checksum_file(char *path, int type, RDD_CHECKSUM_FILE_HEADER *hdr, int *swap)
00284 {
00285 FILE *fp;
00286
00287 if ((fp = fopen(path, "rb")) == NULL) {
00288 unix_error("cannot open checksum file %s", path);
00289 }
00290
00291 if (fread(hdr, sizeof(*hdr), 1, fp) < 1) {
00292 unix_error("cannot read header from %s", path);
00293 }
00294
00295 check_header(path, hdr, type, swap);
00296
00297 return fp;
00298 }
00299
00300 static void
00301 close_checksum_file(const char *path, FILE *fp)
00302 {
00303 if (fp == NULL) {
00304 return;
00305 }
00306 if (fgetc(fp) != EOF) {
00307 warn("unprocessed data in %s", path);
00308 }
00309 if (fclose(fp) == EOF) {
00310 unix_error("cannot close %s", path);
00311 }
00312 }
00313
00314 static void
00315 verify_file(RDD_FILTERSET *filters, const char *path)
00316 {
00317 RDD_READER *reader = 0;
00318 unsigned char buf[READ_SIZE];
00319 unsigned nread;
00320 int rc;
00321
00322 reader = open_image_file(path);
00323
00324 while (1) {
00325 rc = rdd_reader_read(reader, buf, READ_SIZE, &nread);
00326 if (rc != RDD_OK) {
00327 rdd_error(rc, "%s: read error", path);
00328 }
00329 if (nread == 0) break;
00330
00331 if ((rc = rdd_fset_push(filters, buf, nread)) != RDD_OK) {
00332 rdd_error(rc, "cannot push buffer into filter");
00333 }
00334 }
00335 if ((rc = rdd_fset_close(filters)) != RDD_OK) {
00336 rdd_error(rc, "cannot close filters");
00337 }
00338
00339 close_image_file(path, reader);
00340 }
00341
00342 static void
00343 add_filter(RDD_FILTERSET *fset, const char *name, RDD_FILTER *f)
00344 {
00345 int rc;
00346
00347 if ((rc = rdd_fset_add(fset, name, f)) != RDD_OK) {
00348 rdd_error(rc, "cannot install %s filter", name);
00349 }
00350 }
00351
00352 static void
00353 handle_checksum_error(rdd_count_t pos,
00354 rdd_checksum_t expected, rdd_checksum_t computed, void *env)
00355 {
00356 char *algorithm = (char *) env;
00357
00358 errlognl("%s checksum error; block offset %llu; "
00359 "expected 0x%08x, got 0x%08x",
00360 algorithm, pos, expected, computed);
00361 }
00362
00363 static void
00364 get_checksum_result(RDD_FILTERSET *fset, const char *name, unsigned *num_error)
00365 {
00366 RDD_FILTER *f = 0;
00367 int rc;
00368
00369 *num_error = 0;
00370
00371 if ((rc = rdd_fset_get(fset, name, &f)) != RDD_OK) {
00372 rdd_error(rc, "cannot find %s filter", name);
00373 }
00374
00375 rc = rdd_filter_get_result(f,
00376 (unsigned char *) num_error, sizeof(*num_error));
00377 if (rc != RDD_OK) {
00378 rdd_error(rc, "cannot get result for %s filter", name);
00379 }
00380 }
00381
00382 static void
00383 get_hash_result(RDD_FILTERSET *fset, const char *name,
00384 unsigned char *md, unsigned mdsize)
00385 {
00386 RDD_FILTER *f = 0;
00387 int rc;
00388
00389 memset(md, 0, mdsize);
00390
00391 if ((rc = rdd_fset_get(fset, name, &f)) != RDD_OK) {
00392 rdd_error(rc, "cannot find %s filter", name);
00393 }
00394 if ((rc = rdd_filter_get_result(f, md, mdsize)) != RDD_OK) {
00395 rdd_error(rc, "cannot get result for %s filter", name);
00396 }
00397 }
00398
00399 static int
00400 equal_digest(char *md1, char *md2, unsigned mdlen)
00401 {
00402 unsigned i;
00403
00404 for (i = 0; i < mdlen; i++) {
00405 if (tolower(md1[i]) != tolower(md2[i])) {
00406 return 0;
00407 }
00408 }
00409
00410 return 1;
00411 }
00412
00413 static int
00414 verify_files(char **files, unsigned nfile,
00415 FILE* adler32file, rdd_count_t a32len, int a32swap,
00416 FILE* crc32file, rdd_count_t crc32len, int crc32swap)
00417 {
00418 RDD_FILTERSET filters;
00419 RDD_FILTER *f = 0;
00420 unsigned num_error;
00421 int broken = 0;
00422 int rc;
00423 unsigned i;
00424
00425 if ((rc = rdd_fset_init(&filters)) != RDD_OK) {
00426 rdd_error(rc, "cannot initialize filter set");
00427 }
00428
00429 if (opts.md5) {
00430 rc = rdd_new_md5_streamfilter(&f);
00431 if (rc != RDD_OK) {
00432 rdd_error(rc, "cannot create MD5 filter");
00433 }
00434 add_filter(&filters, "MD5 stream", f);
00435 }
00436
00437 if (opts.sha1) {
00438 rc = rdd_new_sha1_streamfilter(&f);
00439 if (rc != RDD_OK) {
00440 rdd_error(rc, "cannot create SHA-1 filter");
00441 }
00442 add_filter(&filters, "SHA-1 stream", f);
00443 }
00444
00445 if (adler32file != 0) {
00446 rc = rdd_new_verify_adler32_blockfilter(&f, adler32file,
00447 a32len, a32swap,
00448 handle_checksum_error,
00449 "Adler32");
00450 if (rc != RDD_OK) {
00451 rdd_error(rc, "cannot create Adler32 verification filter");
00452 }
00453 add_filter(&filters, "Adler32 verification block", f);
00454 }
00455
00456 if (crc32file != 0) {
00457 rc = rdd_new_verify_crc32_blockfilter(&f, crc32file,
00458 crc32len, crc32swap,
00459 handle_checksum_error,
00460 "CRC-32");
00461 if (rc != RDD_OK) {
00462 rdd_error(rc, "cannot create CRC-32 verification filter");
00463 }
00464 add_filter(&filters, "CRC-32 verification block", f);
00465 }
00466
00467
00468
00469 for (i = 0; i < nfile; i++) {
00470 if (opts.verbose) {
00471 errlognl("verifying %s ...", files[i]);
00472 }
00473 verify_file(&filters, files[i]);
00474 }
00475
00476
00477
00478 if (adler32file != 0) {
00479 get_checksum_result(&filters, "Adler32 verification block",
00480 &num_error);
00481 if (num_error > 0) {
00482 broken |= VFY_ADLER32;
00483 }
00484 }
00485
00486 if (crc32file != 0) {
00487 get_checksum_result(&filters, "CRC-32 verification block",
00488 &num_error);
00489 if (num_error > 0) {
00490 broken |= VFY_CRC32;
00491 }
00492 }
00493
00494 if (opts.sha1) {
00495 unsigned char md[20];
00496 char hexmd[2*20 + 1];
00497 int rc;
00498
00499 get_hash_result(&filters, "SHA-1 stream", md, sizeof md);
00500 rc = rdd_buf2hex(md, sizeof md, hexmd, sizeof hexmd);
00501 if (rc != RDD_OK) {
00502 rdd_error(rc, "cannot print SHA-1 digest");
00503 }
00504
00505 if (opts.verbose) {
00506 errlognl("Found SHA1 digest: [%s]", hexmd);
00507 }
00508
00509 if (! equal_digest(hexmd, opts.sha1digest, 2*SHA_DIGEST_LENGTH)) {
00510 errlognl("SHA1 values do not match:");
00511 errlognl("\texpected: %s", opts.sha1digest);
00512 errlognl("\tfound: %s", hexmd);
00513 broken |= VFY_SHA1;
00514 }
00515 }
00516
00517 if (opts.md5) {
00518 unsigned char md[16];
00519 char hexmd[2*16 + 1];
00520 int rc;
00521
00522 get_hash_result(&filters, "MD5 stream", md, sizeof md);
00523 rc = rdd_buf2hex(md, sizeof md, hexmd, sizeof hexmd);
00524 if (rc != RDD_OK) {
00525 rdd_error(rc, "cannot print MD5 digest");
00526 }
00527
00528 if (opts.verbose) {
00529 errlognl("Found MD5 digest: [%s]", hexmd);
00530 }
00531
00532 if (! equal_digest(hexmd, opts.md5digest, 2*MD5_DIGEST_LENGTH)) {
00533 errlognl("MD5 values do not match:");
00534 errlognl("\texpected: %s", opts.md5digest);
00535 errlognl("\tfound: %s", hexmd);
00536 broken |= VFY_MD5;
00537 }
00538 }
00539
00540 if ((rc = rdd_fset_clear(&filters)) != RDD_OK) {
00541 rdd_error(rc, "cannot clean up filter set");
00542 }
00543
00544 return broken;
00545 }
00546
00547 int
00548 main(int argc, char** argv)
00549 {
00550 RDD_CHECKSUM_FILE_HEADER adler32hdr;
00551 RDD_CHECKSUM_FILE_HEADER crc32hdr;
00552 FILE *adler32file = NULL;
00553 FILE *crc32file = NULL;
00554 int adler32swap = 0;
00555 int crc32swap = 0;
00556 int res;
00557 int i;
00558
00559 rdd_opt_init(usage_message);
00560
00561 set_progname(argv[0]);
00562 set_logfile(stderr);
00563 memset(&opts, '\000', sizeof opts);
00564 command_line(argc, argv);
00565
00566 memset(&adler32hdr, 0, sizeof adler32hdr);
00567 memset(&crc32hdr, 0, sizeof crc32hdr);
00568
00569 if (opts.adler32file) {
00570 adler32file = open_checksum_file(opts.adler32file,
00571 RDD_ADLER32, &adler32hdr,
00572 &adler32swap);
00573 }
00574 if (opts.crc32file) {
00575 crc32file = open_checksum_file(opts.crc32file,
00576 RDD_CRC32, &crc32hdr,
00577 &crc32swap);
00578 }
00579
00580 errlognl("");
00581 errlognl("%s", rdd_ctime());
00582 errlognl("%s version %s (Internal rev $Rev: 252 $)", PACKAGE, VERSION);
00583 errlognl("Copyright (c) 2002 Nederlands Forensisch Instituut");
00584 #if defined(HAVE_LIBZ)
00585 errlognl("zlib version %s", zlibVersion());
00586 errlognl("Copyright (c) 1995-2002 Jean-loup Gailyy and Mark Adler");
00587 #endif
00588 #ifdef HAVE_OPENSSL
00589 errlognl("openssl version %s", OPENSSL_VERSION_TEXT);
00590 errlognl("Copyright (c) 1995-1998 Eric Young");
00591 #endif
00592 errlog("%s", argv[0]);
00593 for (i = 1; i < argc; i++) {
00594 errlog(" %s", argv[i]);
00595 }
00596 errlognl("");
00597 errlognl("");
00598
00599 if (opts.verbose) {
00600 errlognl("verbose: %s", bool2str(opts.verbose));
00601 }
00602
00603 res = verify_files(opts.files, opts.nfile,
00604 adler32file, adler32hdr.blocksize, adler32swap,
00605 crc32file, crc32hdr.blocksize, crc32swap);
00606
00607 if (res == 0) {
00608 errlognl("Verification complete: NO ERRORS");
00609 } else {
00610 errlognl("Verification complete: FAILURE DETECTED");
00611
00612 if ((res & VFY_ADLER32) != 0) {
00613 errlognl("Adler32 verification failed");
00614 }
00615 if ((res & VFY_CRC32) != 0) {
00616 errlognl("CRC32 verification failed");
00617 }
00618 if ((res & VFY_SHA1) != 0) {
00619 errlognl("SHA1 verification failed");
00620 }
00621 if ((res & VFY_MD5) != 0) {
00622 errlognl("MD5 verification failed");
00623 }
00624 }
00625
00626 close_checksum_file(opts.crc32file, crc32file);
00627 close_checksum_file(opts.adler32file, adler32file);
00628
00629 return (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
00630 }