home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / pgp / pgp22 / contrib / md5sum / md5sum.c next >
Encoding:
C/C++ Source or Header  |  1993-03-06  |  5.0 KB  |  244 lines

  1. /*
  2.  * md5sum.c    - Generate/check MD5 Message Digests
  3.  *
  4.  * Compile and link with md5.c.  If you don't have getopt() in your library
  5.  * also include getopt.c.  For MSDOS you can also link with the wildcard
  6.  * initialization function (wildargs.obj for Turbo C and setargv.obj for MSC)
  7.  * so that you can use wildcards on the commandline.
  8.  *
  9.  * Written March 1993 by Branko Lankester
  10.  */
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include "md5.h"
  14.  
  15. #ifdef UNIX
  16. #define    FOPRTXT    "r"
  17. #define    FOPRBIN    "r"
  18. #else
  19. #ifdef VMS
  20. #define    FOPRTXT    "r","ctx=stm"
  21. #define    FOPRBIN    "rb","ctx=stm"
  22. #else
  23. #define    FOPRTXT    "r"
  24. #define    FOPRBIN    "rb"
  25. #endif
  26. #endif
  27.  
  28. extern char *optarg;
  29. extern int optind;
  30.  
  31. void usage();
  32. void print_digest();
  33. int mdfile(FILE *fp, unsigned char *digest);
  34. int do_check(FILE *chkf);
  35.  
  36. char *progname;
  37. int verbose = 0;
  38. int bin_mode = 0;
  39.  
  40. void
  41. main(int argc, char **argv)
  42. {
  43.     int opt, rc = 0;
  44.     int check = 0;
  45.     FILE *fp;
  46.     unsigned char digest[16];
  47.  
  48.     progname = *argv;
  49.     while ((opt = getopt(argc, argv, "cbvp:h")) != EOF) {
  50.         switch (opt) {
  51.             case 'c': check = 1; break;
  52.             case 'v': verbose = 1; break;
  53.             case 'b': bin_mode = 1; break;
  54.             default: usage();
  55.         }
  56.     }
  57.     argc -= optind;
  58.     argv += optind;
  59.     if (check) {
  60.         switch (argc) {
  61.             case 0: fp = stdin; break;
  62.             case 1: if ((fp = fopen(*argv, FOPRTXT)) == NULL) {
  63.                     perror(*argv);
  64.                     exit(2);
  65.                 }
  66.                 break;
  67.             default: usage();
  68.         }
  69.         exit(do_check(fp));
  70.     }
  71.     if (argc == 0) {
  72.         if (mdfile(stdin, digest)) {
  73.             fprintf(stderr, "%s: read error on stdin\n", progname);
  74.             exit(2);
  75.         }
  76.         print_digest(digest);
  77.         printf("\n");
  78.         exit(0);
  79.     }
  80.     for ( ; argc > 0; --argc, ++argv) {
  81.         if (bin_mode)
  82.             fp = fopen(*argv, FOPRBIN);
  83.         else
  84.             fp = fopen(*argv, FOPRTXT);
  85.         if (fp == NULL) {
  86.             perror(*argv);
  87.             rc = 2;
  88.             continue;
  89.         }
  90.         if (mdfile(fp, digest)) {
  91.             fprintf(stderr, "%s: error reading %s\n", progname, *argv);
  92.             rc = 2;
  93.         } else {
  94.             print_digest(digest);
  95.             printf(" %c%s\n", bin_mode ? '*' : ' ', *argv);
  96.         }
  97.         fclose(fp);
  98.     }
  99.     exit(rc);
  100. }
  101.  
  102. void
  103. usage()
  104. {
  105.     fprintf(stderr, "usage: md5sum [-bv] [-c [file]] | [file...]\n");
  106.     fprintf(stderr, "Generates or checks MD5 Message Digests\n");
  107.     fprintf(stderr, "    -c  check message digests (default is generate)\n");
  108.     fprintf(stderr, "    -v  verbose, print file names when checking\n");
  109.     fprintf(stderr, "    -b  read files in binary mode\n");
  110.     fprintf(stderr, "The input for -c should be the list of message digests and file names\n");
  111.     fprintf(stderr, "that is printed on stdout by this program when it generates digests.\n");
  112.     exit(2);
  113. }
  114.  
  115. int
  116. mdfile(FILE *fp, unsigned char *digest)
  117. {
  118.     unsigned char buf[1024];
  119.     MD5_CTX ctx;
  120.     int n;
  121.  
  122.     MD5Init(&ctx);
  123.     while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
  124.         MD5Update(&ctx, buf, n);
  125.     MD5Final(&ctx);
  126.     memcpy(digest, ctx.digest, 16);
  127.     if (ferror(fp))
  128.         return -1;
  129.     return 0;
  130. }
  131.  
  132. void
  133. print_digest(unsigned char *p)
  134. {
  135.     int i;
  136.  
  137.     for (i = 0; i < 16; ++i)
  138.         printf("%02x", *p++);
  139. }
  140.  
  141. int
  142. hex_digit(int c)
  143. {
  144.     if (c >= '0' && c <= '9')
  145.         return c - '0';
  146.     if (c >= 'a' && c <= 'f')
  147.         return c - 'a' + 10;
  148.     return -1;
  149. }
  150.  
  151. int
  152. get_md5_line(FILE *fp, unsigned char *digest, char *file)
  153. {
  154.     char buf[1024];
  155.     int i, d1, d2, rc;
  156.     char *p = buf;
  157.  
  158.     if (fgets(buf, sizeof(buf), fp) == NULL)
  159.         return -1;
  160.  
  161.     for (i = 0; i < 16; ++i) {
  162.         if ((d1 = hex_digit(*p++)) == -1)
  163.             return 0;
  164.         if ((d2 = hex_digit(*p++)) == -1)
  165.             return 0;
  166.         *digest++ = d1*16 + d2;
  167.     }
  168.     if (*p++ != ' ')
  169.         return 0;
  170.     /*
  171.      * next char is an attribute char, space means text file
  172.      * if it's a '*' the file should be checked in binary mode.
  173.      */
  174.     if (*p == ' ')
  175.         rc = 1;
  176.     else if (*p == '*')
  177.         rc = 2;
  178.     else {
  179.         fprintf(stderr, "%s: unrecognized line: %s", progname, buf);
  180.         return 0;
  181.     }
  182.     ++p;
  183.     i = strlen(p);
  184.     if (i < 2 || i > 255)
  185.         return 0;
  186.     p[i-1] = '\0';
  187.     strcpy(file, p);
  188.     return rc;
  189. }
  190.  
  191. int
  192. do_check(FILE *chkf)
  193. {
  194.     int rc, ex = 0, failed = 0, checked = 0;
  195.     unsigned char chk_digest[16], file_digest[16];
  196.     char filename[256];
  197.     FILE *fp;
  198.     int flen = 14;
  199.  
  200.     while ((rc = get_md5_line(chkf, chk_digest, filename)) >= 0) {
  201.         if (rc == 0)    /* not an md5 line */
  202.             continue;
  203.         if (verbose) {
  204.             if (strlen(filename) > flen)
  205.                 flen = strlen(filename);
  206.             fprintf(stderr, "%-*s ", flen, filename);
  207.         }
  208.         if (bin_mode || rc == 2)
  209.             fp = fopen(filename, FOPRBIN);
  210.         else
  211.             fp = fopen(filename, FOPRTXT);
  212.         if (fp == NULL) {
  213.             fprintf(stderr, "%s: can't open %s\n", progname, filename);
  214.             ex = 2;
  215.             continue;
  216.         }
  217.         if (mdfile(fp, file_digest)) {
  218.             fprintf(stderr, "%s: error reading %s\n", progname, filename);
  219.             ex = 2;
  220.             fclose(fp);
  221.             continue;
  222.         }
  223.         fclose(fp);
  224.         if (memcmp(chk_digest, file_digest, 16) != 0) {
  225.             if (verbose)
  226.                 fprintf(stderr, "FAILED\n");
  227.             else
  228.                 fprintf(stderr, "%s: MD5 check failed for '%s'\n", progname, filename);
  229.             ++failed;
  230.         } else if (verbose)
  231.             fprintf(stderr, "OK\n");
  232.         ++checked;
  233.     }
  234.     if (verbose && failed)
  235.         fprintf(stderr, "%s: %d of %d file(s) failed MD5 check\n", progname, failed, checked);
  236.     if (!checked) {
  237.         fprintf(stderr, "%s: no files checked\n", progname);
  238.         return 3;
  239.     }
  240.     if (!ex && failed)
  241.         ex = 1;
  242.     return ex;
  243. }
  244.