netscape-revival
security/cmd/jar/zigbert.c
/*
* ZIGBERT
*
* A command line tool to create manifest files
* from a directory hierarchy. It is assumed that
* the tree will be equivalent to what resides
* or will reside in an archive.
*
* Process to sign files:
*
* unpack archive into subdirectory SUB
* zigbert SUB
* add files from "META-INF/" to archive,
* or pack a new archive from hierarchy.
*
* Options:
*
* -eEXT :: sign only files ending in EXT
* -kKEY :: nickname of key to use for signature
* -mMET :: metainformation control file
* -dDIR :: point to a different ~/.netscape directory
* -jDIR :: sign these javascript files below here
* -pPW :: password on command line (insecure)
*
* Copyright 1996 Netscape Corporation
*
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "zig.h"
#include "base64.h"
#include "secutil.h"
#include "secpkcs7.h"
#include "pk11func.h"
#ifdef _UNIX
#include <unistd.h>
#endif
#ifdef XP_PC
/* Use the NSPR opendir routines */
#include "dirpoop.h"
#endif
/* 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
/* mf should not be global */
static FILE *mf;
#define MANIFEST "manifest.mf"
#define CREATOR "Zignature Shell (zigbert 0.5)"
#define BREAKAGE "PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT."
static int manifesto (char *dirname);
static int manifesto_fn (char *filename, char *dirname, char *basedir, char *basename);
static int foreach
(char *dirname, char *prefix, int (*fn)(char *filename, char *dirname, char *basedir, char *basename));
extern int calculate_MD5_range (FILE *fp, long r1, long r2, DIGESTS *dig);
static int generate_SF_file (char *manifile, char *who);
static int is_dir (char *filename);
/* extern int sign_data (void); */
static int do_signing (char *dir);
static int create_pk7 (FILE *inFile, FILE *outFile);
static int ugly_trav (char *key);
static int add_meta (FILE *mf, char *name);
static int verify_global (ZIG *zig);
static int zig_list (char *filename);
static int zig_who (char *filename);
static int init_crypto (void);
static char *get_default_cert_dir (void);
int print_error (int i);
static CERTCertDBHandle *OpenCertDB(void);
static void usage (void);
/* Never use a global in this manner */
CERTCertificate *ugly;
static int ugly_find_nickname (CERTCertDBHandle *handle, char *nickname);
/* bad global bad bad */
/* key used to sign with pkcs7 */
char *keyName = NULL;
/* meta control file, contains name: value pairs */
char *metafile = NULL;
/* password on command line. Use for build testing only */
char *password = NULL;
/* directory containing cert?.db and key?.db */
char *cert_dir = NULL;
/* directories or files to exclude in descent */
char *exclude = NULL;
/* special javascript directory */
char *scriptdir = NULL;
/* extensions of files to sign */
char *ext = NULL;
/* -v to verify a jar file */
char *verify = NULL;
/* list our certificates with the -L option */
int list_certs = 0;
/* optimize, leave out optional headers */
int optimize = 0;
/* zatharus is the man who knows no time, dies tragic death */
int no_time = 0;
/* -w to tell us who signed this jar file */
char *tell_who = NULL;
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
#ifndef XP_WIN
char *XP_GetString (int i)
{
return SECU_ErrorStringRaw ((int16) i);
}
#endif
void FE_SetPasswordEnabled()
{
}
MWContext *FE_GetInitContext (void)
{
return 0;
}
MWContext *XP_FindSomeContext()
{
/* No windows context in command tools */
return NULL;
}
main (int argc, char *argv[])
{
int o;
char *zigtree = NULL;
int status;
if (argc < 2)
{
usage();
}
while ((o = getopt (argc, argv, "e:m:n:k:d:p:j:Llx:v:ozw:E:")) != -1)
{
switch (o)
{
case '?': usage(); exit (0);
break;
case 'e': ext = XP_STRDUP (optarg);
zigprint ("signing only extension \"%s\"\n", ext);
break;
case 'm': metafile = XP_STRDUP (optarg);
zigprint ("meta control file \"%s\"\n", metafile);
break;
case 'n':
case 'k': keyName = XP_STRDUP (optarg);
zigprint ("using key \"%s\"\n", keyName);
break;
case 'd': cert_dir = XP_STRDUP (optarg);
break;
case 'j': scriptdir = XP_STRDUP (optarg);
zigprint ("javscript directory: %s\n", scriptdir);
break;
case 'p': password = XP_STRDUP (optarg);
break;
case 'L':
case 'l': list_certs = 1;
break;
case 'x': exclude = XP_STRDUP (optarg);
break;
case 'v': verify = XP_STRDUP (optarg);
break;
case 'o': optimize = 1;
break;
case 'z': no_time = 1;
zigprint ("will not store signing time in objects\n");
break;
case 'w': tell_who = XP_STRDUP (optarg);
break;
case 'E': print_error (atoi (optarg));
exit (0);
break;
}
}
if (!cert_dir)
cert_dir = get_default_cert_dir();
zigprint ("using certificate directory: %s\n", cert_dir);
init_crypto();
zigtree = argv [optind];
if (verify)
{
zig_list (verify);
}
else if (list_certs)
{
ugly_trav (keyName);
exit (0);
}
else if (tell_who)
{
zig_who (tell_who);
}
else if (zigtree)
{
manifesto (zigtree);
if (keyName)
{
status = do_signing (zigtree);
if (status < 0)
{
fprintf (stderr, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n", zigtree);
exit (1);
}
else
zigprint ("tree \"%s\" signed successfully\n", zigtree);
}
}
else
usage();
return 0;
}
static void usage (void)
{
printf ("\n");
printf ("zigbert 0.4 - a signing tool for jar files\n");
printf ("\n");
printf ("Usage: zigbert [options] directory-tree \n");
printf ("Options:\n");
printf (" -k\"cert nickname\"\n");
printf (" -d\"certificate directory\"\n");
printf (" -m\"metafile\"\n");
printf ("\n");
printf (" -L list the certificates in your database\n");
printf (" (this tells you what zigbert can see right now)\n");
printf ("\n");
printf ("Please note the following caveats:\n");
printf (" directory-tree must already exist.\n");
printf (" You will need to zip the directory-tree with a zip tool.\n\n");
exit (0);
}
int print_error (int err)
{
printf ("Error %d: %s\n", err, SOB_get_error (err));
}
/*
* m a n i f e s t o
*
* Run once for every subdirectory in which a
* manifest is to be created -- usually exactly once.
*
*/
static int manifesto (char *dirname)
{
char metadir [256], sfname [256];
/* Create the META-INF directory to hold signing info */
if (access (dirname, 004))
{
fprintf (stderr, "zigbert: unable to read your directory: %s\n", dirname);
perror (dirname);
exit (1);
}
strcpy (metadir, dirname);
strcat (metadir, "/META-INF");
strcpy (sfname, metadir);
#if defined(XP_UNIX)
{
char cmd [256];
strcpy (cmd, "mkdir ");
strcat (cmd, metadir);
system (cmd);
}
#elif defined(XP_WIN)
mkdir(metadir);
#else
XP_MakeDirectory (metadir, xpTemporary);
#endif
strcat (metadir, "/");
strcat (metadir, MANIFEST);
if ((mf = fopen (metadir, "wb")) == NULL)
{
perror (MANIFEST);
fprintf (stderr, "zigbert: Probably, the directory you are trying to sign has\n");
fprintf (stderr, "zigbert: permissions problems or may not exist.\n");
exit (1);
}
zigprint ("Generating %s file..\n", metadir);
fprintf (mf, "Manifest-Version: 1.0\n");
fprintf (mf, "Created-By: %s\n", CREATOR);
fprintf (mf, "Comments: %s\n", BREAKAGE);
if (scriptdir)
{
fprintf (mf, "Comments: --\n");
fprintf (mf, "Comments: --\n");
fprintf (mf, "Comments: -- This archive signs Javascripts which may not necessarily\n");
fprintf (mf, "Comments: -- be included in the physical jar file.\n");
fprintf (mf, "Comments: --\n");
fprintf (mf, "Comments: --\n");
}
if (metafile)
add_meta (mf, "+");
/* Loop through all files & subdirectories */
foreach (dirname, "", manifesto_fn);
fclose (mf);
strcat (sfname, "/zigbert.sf");
zigprint ("Generating ZIGBERT.SF file..\n");
generate_SF_file (metadir, sfname);
return 0;
}
static int manifesto_fn (char *filename, char *dirname, char *basedir, char *basename)
{
DIGESTS dig;
char fullname [256];
/* crude extension matching */
if (ext)
{
char *s;
if (strlen (filename) < strlen (ext))
return 0;
/* last n bytes */
s = strchr (filename, 0) - strlen (ext);
if (strcmp (s, ext))
return 0;
}
strcpy (fullname, dirname);
strcat (fullname, "/");
strcat (fullname, filename);
fprintf (mf, "\n");
if (scriptdir && !XP_STRCMP (scriptdir, basedir))
{
fprintf (mf, "Name: %s\n", basename);
fprintf (mf, "Magic: javascript\n");
if (optimize == 0)
fprintf (mf, "javascript.id: %s\n", basename);
if (metafile)
add_meta (mf, basename);
}
else
{
fprintf (mf, "Name: %s\n", filename);
if (metafile)
add_meta (mf, filename);
}
SOB_digest_file (fullname, &dig);
if (optimize == 0)
{
fprintf (mf, "Digest-Algorithms: MD5 SHA1\n");
fprintf (mf, "MD5-Digest: %s\n", BTOA_DataToAscii (dig.md5, dig.md5_length));
}
fprintf (mf, "SHA1-Digest: %s\n", BTOA_DataToAscii (dig.sha1, dig.sha1_length));
return 0;
}
static int add_meta (FILE *fp, char *name)
{
FILE *met;
char buf [BUFSIZ];
int place;
char *pattern, *meta;
int num = 0;
if ((met = fopen (metafile, "r")) != NULL)
{
while (fgets (buf, BUFSIZ, met))
{
char *s;
for (s = buf; *s && *s != '\n' && *s != '\r'; s++);
*s = 0;
if (*buf == 0)
continue;
pattern = buf;
/* skip to whitespace */
for (s = buf; *s && *s != ' ' && *s != '\t'; s++);
/* terminate pattern */
if (*s == ' ' || *s == '\t') *s++ = 0;
/* eat through whitespace */
while (*s == ' ' || *s == '\t') s++;
meta = s;
/* this will eventually be regexp matching */
place = 0;
if (!XP_STRCMP (pattern, name))
place = 1;
if (place)
{
num++;
zigprint ("[%s] %s\n", name, meta);
fprintf (fp, "%s\n", meta);
}
}
fclose (met);
}
else
zigprint ("can't open metafile: %s\n", metafile);
return num;
}
/*
* f o r e a c h
*
* A recursive function to loop through all names in
* the specified directory, as well as all subdirectories.
*
* FIX: Need to see if all platforms allow multiple
* opendir's to be called.
*
*/
static int foreach
(char *dirname, char *prefix,
int (*fn)(char *filename, char *dirname, char *basedir, char *basename))
{
char newdir [512];
XP_Dir dir;
XP_DirEntryStruct *entry;
#if 0
/* Note, I will need to add this later re: BSDI bug */
#ifdef XP_UNIX
{
XP_StatStruct st;
if (XP_Stat (dirname, &st, xpTemporary))
return -1;
if (!S_ISDIR (st.st_mode))
return -1;
}
#endif /* XP_UNIX */
#endif
strcpy (newdir, dirname);
if (*prefix)
{
strcat (newdir, "/");
strcat (newdir, prefix);
}
dir = XP_OpenDir (newdir, xpTemporary);
if (!dir) return -1;
for (entry = XP_ReadDir (dir); entry; entry = XP_ReadDir (dir))
{
if (*entry->d_name == '.' || *entry->d_name == '#')
continue;
/* can't sign self */
if (!strcmp (entry->d_name, "META-INF"))
continue;
/* -x option */
if (exclude && !strcmp (entry->d_name, exclude))
continue;
strcpy (newdir, dirname);
if (*dirname)
strcat (newdir, "/");
if (*prefix)
{
strcat (newdir, prefix);
strcat (newdir, "/");
}
strcat (newdir, entry->d_name);
if (is_dir (newdir))
{
char newprefix [512];
strcpy (newprefix, prefix);
if (*newprefix)
strcat (newprefix, "/");
strcat (newprefix, entry->d_name);
foreach (dirname, newprefix, fn);
}
else
{
char newpath [512];
strcpy (newpath, prefix);
if (*newpath)
strcat (newpath, "/");
strcat (newpath, entry->d_name);
zigprint ("--> %s\n", newpath);
(*fn) (newpath, dirname, prefix, entry->d_name);
}
}
XP_CloseDir (dir);
return 0;
}
/*
* c a l c u l a t e _ M D 5 _ r a n g e
*
* Calculate the MD5 digest on a range of bytes in
* the specified fopen'd file. Returns base64.
*
*/
int calculate_MD5_range (FILE *fp, long r1, long r2, DIGESTS *dig)
{
int num;
int range;
unsigned char *buf;
MD5Context *md5 = 0;
SHA1Context *sha1 = 0;
range = r2 - r1;
/* position to the beginning of range */
fseek (fp, r1, SEEK_SET);
buf = (unsigned char *) XP_CALLOC (1, range);
if (buf == NULL)
{
zigprint ("out of memory\n");
return -1;
}
if ((num = fread (buf, 1, range, fp)) != range)
zigprint ("zigbert: short read, expected %d, got %d\n", range, num);
md5 = MD5_NewContext();
sha1 = SHA1_NewContext();
if (md5 == NULL || sha1 == NULL)
{
zigprint ("can't generate digest context\n");
return -1;
}
MD5_Begin (md5);
SHA1_Begin (sha1);
MD5_Update (md5, buf, range);
SHA1_Update (sha1, buf, range);
MD5_End (md5, dig->md5, &dig->md5_length, MD5_LENGTH);
SHA1_End (sha1, dig->sha1, &dig->sha1_length, SHA1_LENGTH);
MD5_DestroyContext (md5, PR_TRUE);
SHA1_DestroyContext (sha1, PR_TRUE);
XP_FREE (buf);
return 0;
}
/*
* g e n e r a t e _ S F _ f i l e
*
* From the supplied manifest file, calculates
* digests on the various sections, creating a .SF
* file in the process.
*
*/
static int generate_SF_file (char *manifile, char *who)
{
FILE *sf;
FILE *mf;
long r1, r2, r3;
char whofile [256];
unsigned char *buf, *name;
DIGESTS dig;
int line = 0;
strcpy (whofile, who);
/* strcat (whofile, ".sf"); */
if ((mf = fopen (manifile, "rb")) == NULL)
{
perror (manifile);
return -1;
}
if ((sf = fopen (whofile, "wb")) == NULL)
{
perror (who);
return -1;
}
buf = (unsigned char *) XP_CALLOC (1, BUFSIZ);
name = (unsigned char *) XP_CALLOC (1, BUFSIZ);
if (buf == NULL || name == NULL)
{
zigprint ("out of memory\n");
return -1;
}
fprintf (sf, "Signature-Version: 1.0\n");
fprintf (sf, "Created-By: %s\n", CREATOR);
fprintf (sf, "Comments: %s\n", BREAKAGE);
if (fgets (buf, BUFSIZ, mf) == NULL)
{
zigprint ("zigbert: empty manifest file!\n");
return -1;
}
if (strncmp (buf, "Manifest-Version:", 17))
{
zigprint ("zigbert: not a manifest file!\n");
return -1;
}
fseek (mf, 0L, SEEK_SET);
/* Process blocks of headers, and calculate their hashen */
while (1)
{
/* Beginning range */
r1 = ftell (mf);
if (fgets (name, BUFSIZ, mf) == NULL)
break;
line++;
if (r1 != 0 && strncmp (name, "Name:", 5))
{
zigprint ("warning: unexpected input in manifest file \"%s\" at line %d:\n", manifile, line);
zigprint ("%s\n", name);
}
while (fgets (buf, BUFSIZ, mf))
{
if (*buf == 0 || *buf == '\n' || *buf == '\r')
break;
line++;
/* Ending range for hashing */
r2 = ftell (mf);
}
r3 = ftell (mf);
if (r1)
{
fprintf (sf, "\n");
fprintf (sf, "%s", name);
}
calculate_MD5_range (mf, r1, r2, &dig);
if (optimize == 0)
{
fprintf (sf, "Digest-Algorithms: MD5 SHA1\n");
fprintf (sf, "MD5-Digest: %s\n", BTOA_DataToAscii (dig.md5, dig.md5_length));
}
fprintf (sf, "SHA1-Digest: %s\n", BTOA_DataToAscii (dig.sha1, dig.sha1_length));
/* restore normalcy after changing offset position */
fseek (mf, r3, SEEK_SET);
}
XP_FREE (buf);
XP_FREE (name);
fclose (sf);
fclose (mf);
return 0;
}
/*
* i s _ d i r
*
* Return 1 if file is a directory.
* Wonder if this runs on a mac, trust not.
*
*/
#ifdef XP_MAC
static int is_dir (char *filename)
{
return 0;
}
#else
static int is_dir (char *filename)
{
struct stat sb;
if (stat (filename, &sb) < 0)
{
zigprint ("%s: %d\n", filename, errno);
return 0;
}
if ((sb.st_mode & S_IFMT) == S_IFDIR)
return 1;
return 0;
}
#endif
/*
extern SECStatus SEC_PKCS7Encode
(
SEC_PKCS7ContentInfo *cinfo,
SEC_PKCS7EncoderOutputCallback outputfn,
void *outputarg,
SECItem *bulkkey,
SECKEYGetPasswordKey pwfn,
void *pwfnarg
);
*/
/*** SIGNING MODULE ***/
extern void SEC_Init(void); /* XXX */
extern SECStatus SECU_PKCS11Init(void); /* XXX */
static int do_signing (char *dir)
{
FILE *in, *out;
char sf_file [256];
char pk7_file [256];
strcpy (sf_file, dir);
strcat (sf_file, "/META-INF/zigbert.sf");
strcpy (pk7_file, dir);
strcat (pk7_file, "/META-INF/zigbert.rsa");
in = fopen (sf_file, "rb");
out = fopen (pk7_file, "wb");
if (in == NULL || out == NULL)
{
zigprint ("error opening files for signing\n");
return -1;
}
return create_pk7 (in, out);
}
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");
return NULL;
}
else
{
/* stat error */
SECU_PrintError (progName, "stat: ", strerror(errno));
return NULL;
}
}
keyHandle = SECKEY_OpenKeyDBFilename (fName, PR_FALSE);
if (keyHandle == NULL)
{
SECU_PrintError (progName, "could not open key database %s", fName);
return NULL;
}
return (keyHandle);
}
static void SignOut (void *arg, const char *buf, unsigned long len)
{
fwrite (buf, len, 1, (FILE *) arg);
}
/*
* p a s s w o r d _ h a r d c o d e
*
* A function to use the password passed in the -p(password) argument
* of the command line. This is only to be used for build & testing purposes,
* as it's extraordinarily insecure.
*
* After use once, null it out otherwise PKCS11 calls us forever.
*
*/
char *password_hardcode (PK11SlotInfo *slot, void *arg)
{
char *pw;
pw = password ? XP_STRDUP (password) : NULL;
password = NULL;
return pw;
}
static int SignFile (FILE *outFile, FILE *inFile, CERTCertificate *cert)
{
int nb;
char ibuf[4096], digestdata[32];
SECHashObject *hashObj;
void *hashcx;
unsigned int len;
SECItem digest;
SEC_PKCS7ContentInfo *cinfo;
SECStatus rv;
SECCertUsage signwith;
/* Right now use the SSL certificates (a hack until
object signing is available */
signwith = certUsageObjectSigner;
if (outFile == NULL || inFile == NULL || cert == NULL)
return -1;
/* XXX probably want to extend interface to allow other hash algorithms */
hashObj = &SECHashObjects[HASH_AlgSHA1];
hashcx = (* hashObj->create)();
if (hashcx == NULL)
return -1;
(* hashObj->begin)(hashcx);
for (;;)
{
if (feof(inFile)) break;
nb = fread(ibuf, 1, sizeof(ibuf), inFile);
if (nb == 0)
{
if (ferror(inFile))
{
PORT_SetError(SEC_ERROR_IO);
(* hashObj->destroy)(hashcx, PR_TRUE);
return -1;
}
/* eof */
break;
}
(* hashObj->update)(hashcx, ibuf, nb);
}
(* hashObj->end)(hashcx, digestdata, &len, 32);
(* hashObj->destroy)(hashcx, PR_TRUE);
digest.data = digestdata;
digest.len = len;
/* XXX Need a better way to handle that usage stuff! */
cinfo = SEC_PKCS7CreateSignedData
(cert, signwith, NULL, SEC_OID_SHA1, &digest, NULL, NULL);
if (cinfo == NULL)
return -1;
rv = SEC_PKCS7IncludeCertChain (cinfo, NULL);
if (rv != SECSuccess)
{
SEC_PKCS7DestroyContentInfo (cinfo);
return -1;
}
if (no_time == 0)
{
rv = SEC_PKCS7AddSigningTime (cinfo);
if (rv != SECSuccess)
{
/* don't check error */
}
}
if (password)
{
PK11_SetPasswordFunc (password_hardcode);
rv = SEC_PKCS7Encode
(cinfo, SignOut, outFile, NULL, /*password_hardcode*/ NULL, NULL);
}
else
{
rv = SEC_PKCS7Encode
(cinfo, SignOut, outFile, NULL, SECU_GetPassword, NULL);
}
SEC_PKCS7DestroyContentInfo (cinfo);
if (rv != SECSuccess)
return -1;
return 0;
}
static int create_pk7 (FILE *inFile, FILE *outFile)
{
SECStatus rv;
CERTCertificate *cert;
SECKEYKeyDBHandle *keyHandle;
CERTCertDBHandle *certHandle;
/* Call the libsec initialization routines */
PR_Init ("sign", 1, 1, 0);
/* ugly */
SECU_ConfigDirectory (cert_dir);
rv = SECU_PKCS11Init();
if (rv != SECSuccess)
return -1;
SEC_Init();
/* open key database */
keyHandle = OpenKeyDB ("zigbert");
if (keyHandle == NULL)
return -1;
SECKEY_SetDefaultKeyDB (keyHandle);
/* open cert database */
certHandle = OpenCertDB();
if (certHandle == NULL)
return -1;
/* find cert */
cert = CERT_FindCertByNickname (certHandle, keyName);
#if 0
/* find cert */
/* below call works when the nickname index is trashed */
ugly_find_nickname (certHandle, keyName);
cert = ugly;
#endif
if (cert == NULL)
{
SECU_PrintError
(
"zigbert",
"the corresponding cert for key \"%s\" does not exist",
keyName
);
return -1;
}
CERT_SetDefaultCertDB (certHandle);
if (SignFile (outFile, inFile, cert))
{
fprintf (stderr, "%s: PROBLEM signing data (%s)\n",
"zigbert", SECU_ErrorString ((int16) PORT_GetError()));
return -1;
}
return 0;
}
/* There is a separate copy here than in ns/security/cmd/lib/secutil.c
to allow a base name "cert_dir" to be used */
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 = "zigbert";
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;
/* 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;
}
/******************** HACKY ****************************/
static SECStatus
ugly_find_nick_trav (CERTCertificate *cert, SECItem *k, void *data)
{
char *name;
char *target;
target = (char *) data;
if ( cert->dbEntry )
{
name = cert->dbEntry->nickname;
if ( name == NULL )
name = cert->emailAddr;
if (!XP_STRCMP (target, name))
{
printf ("FOUND: %s\n", name);
ugly = CERT_DupCertificate (cert);
}
}
return (SECSuccess);
}
static int
ugly_find_nickname (CERTCertDBHandle *handle, char *nickname)
{
int rv;
ugly = NULL;
rv = SEC_TraversePermCerts
(handle, ugly_find_nick_trav, (void *) nickname);
if (rv)
return -1;
return 0;
}
/*************************/
static SECStatus cert_trav_callback (CERTCertificate *cert, SECItem *k, void *data)
{
int trusted;
char *name;
char **ugly_list;
ugly_list = (char **) data;
if (cert->dbEntry)
{
name = cert->dbEntry->nickname;
if (name == NULL)
name = cert->emailAddr;
trusted = cert->trust->objectSigningFlags & CERTDB_USER;
/* Add this name or email to list */
if (name)
{
*ugly_list = XP_REALLOC (*ugly_list, XP_STRLEN (*ugly_list) + XP_STRLEN (name) + 4);
if (**ugly_list)
XP_STRCAT (*ugly_list, trusted ? "\n* " : "\n ");
else
XP_STRCAT (*ugly_list, trusted ? "* " : " ");
XP_STRCAT (*ugly_list, name);
}
}
return (SECSuccess);
}
static int ugly_trav (char *key)
{
SECStatus rv;
CERTCertificate *cert;
CERTCertDBHandle *certHandle;
char *ugly_list;
/* Call the libsec initialization routines */
PR_Init ("sign", 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;
ugly_list = XP_CALLOC (1, 16);
if (ugly_list == NULL)
return -1;
*ugly_list = 0;
rv = SEC_TraversePermCerts
(certHandle, cert_trav_callback, (void *) &ugly_list);
if (rv)
return -1;
printf ("S Certificates\n");
printf ("- ------------\n");
printf ("%s\n", ugly_list);
printf ("------------\n");
printf ("Certificates that can be used to sign objects have *'s to their left.\n");
if (key)
{
SECStatus rv;
CERTCertificate *issuerCert;
cert = CERT_FindCertByNickname (certHandle, key);
if (cert)
{
printf ("The cert with nickname \"%s\" was found:\n", cert->nickname);
printf (" subject name: %s\n", cert->subjectName);
printf (" issuer name: %s\n", cert->issuerName);
rv = CERT_CertTimesValid (cert);
if (rv != SECSuccess)
printf ("certificate %s is expired\n", cert->nickname);
else
printf ("(this cert is not expired)\n");
rv = CERT_VerifyCertNow (certHandle, cert, PR_TRUE, certUsageObjectSigner, NULL);
if (rv != SECSuccess)
printf ("certificate %s is not valid\n", cert->nickname);
else
printf ("(this cert is valid)\n");
issuerCert = CERT_FindCertIssuer (cert);
rv = CERT_CertTimesValid (issuerCert);
if (rv != SECSuccess)
printf ("certificate's issuer cert, %s, is expired\n", issuerCert->nickname);
else
printf ("(the issuer cert is not expired)\n");
rv = CERT_VerifyCertNow (certHandle, issuerCert, PR_TRUE, certUsageObjectSigner, NULL);
if (rv != SECSuccess)
printf ("issuer certificate %s is not valid\n", issuerCert->nickname);
else
printf ("(the issuer cert is valid)\n");
}
else
printf ("The cert with nickname \"%s\" was NOT FOUND\n", issuerCert->nickname);
}
return 0;
}
static int zig_list (char *filename)
{
FILE *fp;
int ret;
int status;
ZIG *zig;
SOBITEM *it;
init_crypto();
zig = SOB_new();
if ((fp = fopen (filename, "r")) == NULL)
{
perror (filename);
exit (1);
}
else
fclose (fp);
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 (status < 0)
zigprint (" (purported reason: %s -- may be incorrect)\n",
SECU_ErrorString ((int16) 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 && 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;
}
static int zig_who (char *filename)
{
FILE *fp;
ZIG *zig;
int status;
SOBITEM *it;
FINGERZIG *fing;
CERTCertificate *cert;
init_crypto();
zig = SOB_new();
if ((fp = fopen (filename, "r")) == NULL)
{
perror (filename);
exit (1);
}
else
fclose (fp);
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);
}
}
printf ("\nSigner information:\n\n");
SOB_find (zig, "*", ZIG_FP2);
while (SOB_find_next (zig, &it) >= 0)
{
fing = (FINGERZIG *) it->data;
cert = fing->cert;
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");
}
printf ("\nDumping the cert chain found in this signature:\n\n");
SOB_find (zig, "*", ZIG_WALK);
while (SOB_find_next (zig, &it) >= 0)
{
fing = (FINGERZIG *) it->data;
cert = fing->cert;
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);
}
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);
}
}
}
}
return 0;
}
/* this is only used for zigbert -v to view archives */
static int init_crypto (void)
{
SECStatus rv;
static int prior = 0;
CERTCertDBHandle *certHandle;
if (prior == 0)
{
/* 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();
prior = 1;
/* open cert database */
certHandle = OpenCertDB();
if (certHandle == NULL)
return -1;
CERT_SetDefaultCertDB (certHandle);
}
return 0;
}
/* Windows foolishness follows */
#ifdef _WINDOWS
char *NOT_NULL (const char *x)
{
return (char *) x;
}
void XP_AssertAtLine (char *pFileName, int iLine)
{
abort();
}
void SSL_InitHashLock (void)
{
}
void SSL3_Init (void)
{
}
void FE_Trace (const char *msg)
{
}
RPC_STATUS __stdcall UuidCreate (UUID * Uuid)
{
return 0;
}
#endif
/*
* get_default_cert_dir
*
* Attempt to locate a certificate directory.
* Failing that, complain that the user needs to
* use the -d(irectory) parameter.
*
*/
static char *get_default_cert_dir (void)
{
char *home;
char *cd = NULL;
static char db [256];
#ifdef XP_UNIX
home = getenv ("HOME");
if (home && *home)
{
sprintf (db, "%s/.netscape", home);
cd = db;
}
#endif
#ifdef XP_PC
FILE *fp;
/* first check the environment override */
home = getenv ("ZIG_HOME");
if (home && *home)
{
sprintf (db, "%s/cert7.db", home);
if ((fp = fopen (db, "r")) != NULL)
{
fclose (fp);
cd = home;
}
}
/* try the old navigator directory */
if (cd == NULL)
{
home = "c:/Program Files/Netscape/Navigator";
sprintf (db, "%s/cert7.db", home);
if ((fp = fopen (db, "r")) != NULL)
{
fclose (fp);
cd = home;
}
}
/* maybe USER= is defined.. */
if (cd == NULL)
{
home = getenv ("USER");
if (home && *home)
{
sprintf (db, "c:/Program Files/Netscape/Users/%s/cert7.db", home);
if ((fp = fopen (db, "r")) != NULL)
{
fclose (fp);
cd = home;
}
}
}
#endif
if (!cd)
{
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);
}
return cd;
}