Milán Major

netscape-revival

1 branch
Code

security/cmd/jar/zigrat.c

/* FIX: verify_global */
/*
 *  ZIGRAT
 *
 *  Zignature Ratification.
 *
 *  To verify a tree:
 *
 *     zigrat manifest-file
 *
 *  To verify single files in archives:
 *
 *     zigrat -f archive.zip filename
 *
 *  Copyright 1996 Netscape Corporation
 * 
 */

#include "zig.h"
#include "base64.h"

#include "secutil.h"
#include "secpkcs7.h"

#ifdef _UNIX
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include "errno.h"

/* SVR4 is to prevent Solaris from blowing up --Rob */
#if defined(__sun) && !defined(SVR4)
extern int getopt(int, char**, char*);
extern char *optarg;
#endif

#define zigprint printf

int ssl_sign = 0;
char *cert_dir = NULL;

extern int zig_archive (char *filename, char *member);
extern int zig_list (char *filename);
extern int zigrat (char *dir, ZIG *zig);
extern int ratify (char *dir, ZIG *zig);
extern int zigshove (char *filename, ZIG *zig);
static int verify_global (ZIG *zig);
extern unsigned char *read_file (char *filename);
extern int usage (void);
static int init_crypto (void);
static int zig_who (char *filename);

/* Copies to prevent linking a whole new navigator */

XP_File XP_FileOpen 
    (const char* name, XP_FileType type, const XP_FilePerm permissions)
  {
  return fopen (name, permissions);
  }

#ifndef XP_FileClose
XP_FileClose (XP_File fp)
  {
  return fclose (fp);
  }
#endif

#ifndef XP_FileWrite
int XP_FileWrite (const void *source, int32 count, XP_File file)
  {
  return fwrite (source, 1, 
    count == -1 ? strlen((char*)source) : count, file);
  }
#endif

#if 0
int XP_FileRead
    (const void *dest, int32 count, XP_File file)
  {
  return fread (dest, 1, count, file);
  }

int XP_FileSeek (XP_File file, int32 offset, int32 whence)
  {
  return fseek (file, offset, whence);
  }
#endif

/* void FE_SetPasswordEnabled(MWContext *context, PRBool usePW) */
void FE_SetPasswordEnabled()
  {
  }

int tellwho = 0;
int check_crypto = 1;

int main (int argc, char *argv[])
  {
  int o;
  static char db [256];

  ZIG *zig;
  int zipfile = 0;

  char *jarfile = NULL;
  char *extract_file = NULL;

  if (argc < 2)
    {
    usage();
    exit (1);
    }

  zig = SOB_new();

  while ((o = getopt (argc, argv, "cd:f:sv:w:")) != -1) 
    {
    switch (o) 
      {
      case '?': usage(); exit (0);
	        break;

      case 'c': check_crypto = 0;
                /* not implemented */
                break;

      case 'd': cert_dir = XP_STRDUP (optarg);
                break;

      case 'f': extract_file = XP_STRDUP (optarg);
                zigprint ("extracting file: \"%s\"\n", extract_file);
                break;

      case 's': ssl_sign = 1;
                zigprint ("checking using SSL client auth certificates\n");
                break;

      case 'v': jarfile = XP_STRDUP (optarg);
                zigprint ("validating archive: \"%s\"\n", jarfile);
                break;

      case 'w': tellwho++;
                jarfile = XP_STRDUP (optarg);
                break;
      }
    }

  if (!cert_dir)
    {
    char *home;

    home = getenv ("HOME");

    if (home && *home)
      {
      sprintf (db, "%s/.netscape", home);
      cert_dir = db;
      }
    else
      {
      zigprint ("You must specify the location of your certificate directory\n");
      zigprint ("with the -d option. Example: -d ~/.netscape in many cases with Unix.\n");
      exit (0);
      }
    }

  zigprint ("using certificate directory: %s\n", cert_dir);

  if (extract_file)
    {
    zig_archive (argv [optind], extract_file);
    }
  else if (tellwho)
    {
    zig_who (jarfile);
    }
  else if (jarfile)
    {
    zig_list (jarfile);
    }
  else
    {
    zigrat (argv [optind], zig);
    ratify (argv [optind], zig);
    }

  exit (0);
  }

int usage (void)
  {
  printf ("\nzigrat\n\n");
  printf ("   zigrat -f extract_file jar_archive\n");
  printf ("       (extracts a file from a jar archive)\n\n");
  printf ("   zigrat -v jar_archive\n");
  printf ("       (lists and validates the contents of a jar archive\n\n");
  printf ("   zigrat directory_tree\n");
  printf ("       (validates a jar directory tree splayed on disk, no jar file)\n\n");
  }

int zig_archive (char *filename, char *member)
  {
  ZIG *zig;
  int res = 0;

  zig = SOB_new();

  SOB_pass_archive (ZIG_F_GUESS, filename, "some-url", zig);

  if (member)
    {
    zigprint ("attempting to extract & verify member: %s\n", member);
    res = SOB_verified_extract (zig, member, "output");
    }

  if (res < 0)
    zigprint ("extraction failed, code %d\n", res);

  return res;
  }

static int zig_who (char *filename)
  {
  ZIG *zig;
  int status;

  SOBITEM *it;
  FINGERZIG *fing;

  CERTCertificate *cert;

  init_crypto();

  zig = SOB_new();

  status = SOB_pass_archive (ZIG_F_GUESS, filename, "some-url", zig);

  if (status < 0 || zig->valid < 0)
    {
    zigprint ("NOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", filename);
    if (zig->valid < 0 && status != -1)
      zigprint ("  (purported reason: %s (%d) -- may be incorrect)\n", SECU_ErrorString (PORT_GetError()), status);
    }

  SOB_find (zig, "*", ZIG_FP2);

  while (SOB_find_next (zig, &it) >= 0)
    {
    fing = (FINGERZIG *) it->data;
    cert = (CERTCertificate *) fing->stuff;
    if (cert)
      {
      if (cert->nickname) 
        printf ("nickname: %s\n", cert->nickname);
      if (cert->subjectName)
        printf ("subject name: %s\n", cert->subjectName);
      if (cert->issuerName)
        printf ("issuer name: %s\n", cert->issuerName);
      }
    else
      printf ("no cert could be found\n");
    }

  SOB_destroy (&zig);
  }

int zig_list (char *filename)
  {
  int ret;
  int status;

  ZIG *zig;
  SOBITEM *it;

  init_crypto();

  zig = SOB_new();

  status = SOB_pass_archive (ZIG_F_GUESS, filename, "some-url", zig);

  if (status < 0 || zig->valid < 0)
    {
    zigprint ("NOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", filename);
    if (zig->valid < 0 && status != -1)
      zigprint ("  (purported reason: %s -- may be incorrect)\n", SECU_ErrorString (PORT_GetError()));
    zigprint ("entries shown below will have their digests checked only.\n"); 
    zig->valid = 0;
    }
  else
    zigprint ("archive \"%s\" has passed crypto verification.\n", filename);

  if (SOB_set_nailed (zig, 1) < 0)
    return -1;

  verify_global (zig);

  zigprint ("\n");
  zigprint ("%16s   %s\n", "status", "path");
  zigprint ("%16s   %s\n", "------------", "-------------------");

  SOB_find (zig, "*", ZIG_MF);

  while (SOB_find_next (zig, &it) >= 0)
    {
    if (it->pathname)
      {
      ret = SOB_verified_extract (zig, it->pathname, "output");
      zigprint ("%16s   %s\n", 
        ret >= 0 ? "verified" : "NOT VERIFIED", it->pathname);
      }
    }

  if (status < 0 || zig->valid < 0)
    zigprint ("\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", filename);

  SOB_destroy (&zig);

  return 0;
  }

int zigrat (char *dir, ZIG *zig)
  {
  char fullpath [256];

  strcpy (fullpath, dir);
  strcat (fullpath, "/META-INF/manifest.mf");

  zigshove (fullpath, zig);

  /* FIX - have this check for *.sf */

  strcpy (fullpath, dir);
  strcat (fullpath, "/META-INF/zigbert.sf");

  zigshove (fullpath, zig);

  return 0;
  }

/*
 *  r a t i f y
 *
 *  Display ascii representation of the internal
 *  data structures for the SOB routines.
 *
 */

int ratify (char *dir, ZIG *zig)
  {
  int i = 0;

  ZSList *list;

  SOBITEM *it;
 
  DIGESTS dig;
  char fullpath [BUFSIZ];

  int result;

  list = zig->list;

  if (ZS_ListEmpty (list))
    {
    zigprint ("empty list\n");
    return -1;
    }

  SOB_find (zig, "*", ZIG_SF);

  while (SOB_find_next (zig, &it) >= 0)
    {
    if (it->pathname)
      {
      /* FIX - overflow? */

      strcpy (fullpath, dir);
      strcat (fullpath, "/");
      strcat (fullpath, it->pathname);

      memset (&dig, 0, sizeof (DIGESTS));
      SOB_digest_file (fullpath, &dig);

      SOB_verify_digest (&result, zig, it->pathname, &dig);

      printf ("%2d      %-16.16s  %d    %s\n", 
          ++i, it->pathname, it->type, result ? "verified" : "FAILED");
      }
    }

  return 0;
  }

/* 
 *  z i g s h o v e
 *
 *  Read a manifest file into memory and
 *  hand it off to the parser. 
 *
 */

int zigshove (char *filename, ZIG *zig)
  {
  unsigned char *dat;

  dat = read_file (filename);

  if (dat)
    {
    if (SOB_parse_manifest (dat, strlen (dat), filename, "url", zig) < 0)
      return -1;
    XP_FREE (dat);
    }

  return 0;
  }

/*
 *  r e a d _ f i l e
 *
 *  Read a file into memory,
 *  in one fell swoop.
 *  FIX: Need to return length.
 *
 */

unsigned char *read_file (char *filename)
  {
  FILE *fp;

  char *filebuf;
  long size, num;

  if ((fp = fopen (filename, "rb")) == NULL)
    {
    perror (filename);
    return NULL;
    }

  /* Determine the size of this file */

  if (fseek (fp, 0L, SEEK_END) != 0)
    perror (filename);

  size = ftell (fp);
  rewind (fp);

  /* Read the whole damn thing */

  filebuf = (unsigned char *) XP_CALLOC (1, size + 1);
  if (filebuf == NULL)
    {
    fprintf (stderr, "out of memory allocating space for: %s\n", filename);
    exit (1);
    }

  memset (filebuf, 0, size + 1);
 
  if ((num = fread (filebuf, 1, size, fp)) != size) 
    {
    fprintf (stderr, "error reading from file: %s\n", filename);
    perror (filename);
    exit (1);
    }

  fclose (fp);

  return filebuf;
  }

static int verify_global (ZIG *zig)
  {
  FILE *fp;

  char *ext;

  SOBITEM *it;
  DIGESTS dig, *globaldig;

  char buf [BUFSIZ];

  unsigned char *md5_digest, *sha1_digest;

  SOB_find (zig, "*", ZIG_PHY);

  while (SOB_find_next (zig, &it) >= 0)
    {
    if (!XP_STRNCMP (it->pathname, "META-INF", 8))
      {
      for (ext = it->pathname; *ext; ext++);
      while (ext > it->pathname && *ext != '.') ext--;

      if (!XP_STRCASECMP (ext, ".rsa"))
        zigprint ("found a RSA signature file: %s\n", it->pathname);

      if (!XP_STRCASECMP (ext, ".dsa"))
        zigprint ("found a DSA signature file: %s\n", it->pathname);

      if (!XP_STRCASECMP (ext, ".mf"))
        zigprint ("found a MF master manifest file: %s\n", it->pathname);

      if (!XP_STRCASECMP (ext, ".sf"))
        {
        zigprint ("found a SF signature manifest file: %s\n", it->pathname);

        if (SOB_extract (zig, it->pathname, "output") < 0)
          {
          zigprint ("error extracting %s\n", it->pathname);
          continue;
          }

        md5_digest = NULL;
        sha1_digest = NULL;

        if ((fp = fopen ("output", "rb")) != NULL)
          {
          while (fgets (buf, BUFSIZ, fp))
            {
            char *s;

            if (*buf == 0 || *buf == '\n' || *buf == '\r')
              break;

            for (s = buf; *s && *s != '\n' && *s != '\r'; s++);
            *s = 0;

            if (!XP_STRNCMP (buf, "MD5-Digest: ", 12))
              {
              md5_digest = ATOB_AsciiToData (buf + 12, &dig.md5_length);
              }

            if (!XP_STRNCMP (buf, "SHA1-Digest: ", 13))
              {
              sha1_digest = ATOB_AsciiToData (buf + 13, &dig.sha1_length);
              }

            if (!XP_STRNCMP (buf, "SHA-Digest: ", 12))
              {
              sha1_digest = ATOB_AsciiToData (buf + 12, &dig.sha1_length);
              }
            }

          globaldig = zig->globalmeta;

          if (globaldig && md5_digest)
             {
             zigprint ("  md5 digest on global metainfo: %s\n", 
                 XP_MEMCMP (md5_digest, globaldig->md5, MD5_LENGTH) ? "no match" : "match");
             }

          if (globaldig && sha1_digest)
             {
             zigprint ("  sha digest on global metainfo: %s\n", 
                 XP_MEMCMP (sha1_digest, globaldig->sha1, SHA1_LENGTH) ? "no match" : "match");
             }

          if (globaldig == NULL)
             zigprint ("global metadigest is not available, strange.\n");

          fclose (fp);
          }
        }
      }
    }
  }

/* VERIFYING PORTION */

extern void SEC_Init(void); /* XXX */

static SECKEYKeyDBHandle *OpenKeyDB (char *progName)
  {
  int ret;
  char *fName;

  struct stat stat_buf;
  SECKEYKeyDBHandle *keyHandle;

  fName = SECU_DatabaseFileName(xpKeyDB);

  printf ("using key database: %s\n", fName);

  ret = stat (fName, &stat_buf);
  if (ret < 0) 
    {
    if (errno == ENOENT) 
      {
      /* no key.db */
      SECU_PrintError (progName, "unable to locate key database");
      exit (1);
      }
    else 
      {
      /* stat error */
      SECU_PrintError (progName, "stat error: ", strerror(errno));
      exit (1);
      }
    }

   keyHandle = SECKEY_OpenKeyDBFilename (fName, PR_FALSE);
   if (keyHandle == NULL) 
     {
     SECU_PrintError (progName, "could not open key database %s", fName);
     exit (1);
     }

  return (keyHandle);
  }

static char *
certDBNameCallback(void *arg, int dbVersion)
{
    char *fnarg;
    char *dir;
    char *filename;
    
    dir = SECU_ConfigDirectory (cert_dir);
 
    switch ( dbVersion ) {
      case 7:
        fnarg = "7";
        break;
      case 6:
	fnarg = "6";
	break;
      case 5:
	fnarg = "5";
	break;
      case 4:
      default:
	fnarg = "";
	break;
    }
    filename = PR_smprintf("%s/cert%s.db", dir, fnarg);
    return(filename);
}

static char *progName = "zigrat";

static CERTCertDBHandle
*OpenCertDB(void)
  /* NOTE: This routine has been modified to allow the libsec/pcertdb.c
   * routines to automatically find and convert the old cert database
   * into the new v3.0 format (cert db version 5).
   */
{
    CERTCertDBHandle *certHandle;
    SECStatus rv;
    struct stat stat_buf;
    int ret;

    /* Allocate a handle to fill with CERT_OpenCertDB below */
    certHandle = (CERTCertDBHandle *)PORT_ZAlloc(sizeof(CERTCertDBHandle));
    if (!certHandle) {
	SECU_PrintError(progName, "unable to get database handle");
	return NULL;
    }

    rv = CERT_OpenCertDB(certHandle, FALSE, certDBNameCallback, NULL);

    if (rv) {
	SECU_PrintError(progName, "could not open certificate database");
	if (certHandle) free (certHandle);  /* we don't want to leave anything behind... */
	return NULL;
    } else {
	CERT_SetDefaultCertDB(certHandle);
    }

    return certHandle;
}

static void PrintBytes
     (void *arg, const char *buf, unsigned long len)
  {
  fwrite (buf, len, 1, (FILE *) arg);
  }

int DecodeAndPrintFile (FILE *out, FILE *in, char *progName)
  {
  SECItem derdata;
  SEC_PKCS7ContentInfo *cinfo;
  SEC_PKCS7DecoderContext *dcx;

  SECCertUsage signwith;

  /* Right now use the SSL certificates (a hack until 
     object signing is available */

  signwith = ssl_sign ? certUsageSSLClient : certUsageObjectSigner;

  if (SECU_DER_Read(&derdata, in)) 
    {
    fprintf (stderr, "%s: error converting der (%s)\n", 
       progName, SECU_ErrorString(PORT_GetError()));
    return -1;
    }

  fprintf (out,
    "Content printed between bars (newline added before second bar):");

  fprintf (out, 
    "\n---------------------------------------------\n");

  dcx = SEC_PKCS7DecoderStart (PrintBytes, out, SECU_GetPassword, NULL);
  if (dcx != NULL) 
    {
    SEC_PKCS7DecoderUpdate(dcx, derdata.data, derdata.len);
    cinfo = SEC_PKCS7DecoderFinish (dcx);
    }

  fprintf(out, "\n---------------------------------------------\n");

  if (cinfo == NULL)
    return -1;

  fprintf (out, "Content %s encrypted.\n",
	    SEC_PKCS7ContentIsEncrypted (cinfo) ? "was" : "was not");

  if (SEC_PKCS7ContentIsSigned(cinfo)) 
    {
    PORT_SetError (0);

    if (SEC_PKCS7VerifySignature (cinfo, signwith, PR_FALSE))
      fprintf (out, "Signature is valid.\n");
    else
      fprintf (out, "Signature is invalid (Reason: %s).\n",
         SECU_ErrorString (PORT_GetError()));
    }
  else 
    fprintf (out, "Content was not signed.\n");

  SEC_PKCS7DestroyContentInfo(cinfo);
  return 0;
  }


/*
 * Print the contents of a PKCS7 message, indicating signatures, etc.
 */

int do_decode (FILE *inFile, FILE *outFile)
  {
  char *progName = "zigrat";
  int opt;
  SECKEYKeyDBHandle *keyHandle;
  CERTCertDBHandle *certHandle;

  /* Call the libsec initialization routines */
  PR_Init ("p7content", 1, 1, 0);
  SEC_Init();

  /* open key database */
  keyHandle = OpenKeyDB (progName);

  if (keyHandle == NULL) 
    return -1;

  SECKEY_SetDefaultKeyDB (keyHandle);

  /* open cert database */
  certHandle = OpenCertDB();

  if (certHandle == NULL) 
    return -1;

  CERT_SetDefaultCertDB (certHandle);

  if (DecodeAndPrintFile (outFile, inFile, progName)) 
    {
    fprintf (stderr, "%s: problem decoding data (%s)\n",
       progName, SECU_ErrorString (PORT_GetError()));
    return -1;
    }

  return 0;
  }

static int init_crypto (void)
  {
  SECStatus rv;

  CERTCertDBHandle *certHandle;

  /* Call the libsec initialization routines */
  PR_Init ("verify", 1, 1, 0);

  /* ugly */
  SECU_ConfigDirectory (cert_dir);

  rv = SECU_PKCS11Init();
  if (rv != SECSuccess)
      return -1;

  SEC_Init();

  /* open cert database */
  certHandle = OpenCertDB(); 

  if (certHandle == NULL) 
    return -1;

  CERT_SetDefaultCertDB (certHandle);

  return 0;
  }