2008-06-06 05:40:11 -07:00
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is the Netscape security libraries.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "signtool.h"
|
|
|
|
#include "zip.h"
|
|
|
|
#include "prmem.h"
|
|
|
|
#include "blapi.h"
|
|
|
|
#include "sechash.h" /* for HASH_GetHashObject() */
|
|
|
|
|
|
|
|
static int create_pk7 (char *dir, char *keyName, int *keyType);
|
|
|
|
static int jar_find_key_type (CERTCertificate *cert);
|
|
|
|
static int manifesto (char *dirname, char *install_script, PRBool recurse);
|
|
|
|
static int manifesto_fn(char *relpath, char *basedir, char *reldir,
|
|
|
|
char *filename, void *arg);
|
|
|
|
static int manifesto_xpi_fn(char *relpath, char *basedir, char *reldir,
|
|
|
|
char *filename, void *arg);
|
|
|
|
static int sign_all_arc_fn(char *relpath, char *basedir, char *reldir,
|
|
|
|
char *filename, void *arg);
|
|
|
|
static int add_meta (FILE *fp, char *name);
|
|
|
|
static int SignFile (FILE *outFile, FILE *inFile, CERTCertificate *cert);
|
|
|
|
static int generate_SF_file (char *manifile, char *who);
|
|
|
|
static int calculate_MD5_range (FILE *fp, long r1, long r2,
|
|
|
|
JAR_Digest *dig);
|
|
|
|
static void SignOut (void *arg, const char *buf, unsigned long len);
|
|
|
|
|
|
|
|
static char *metafile = NULL;
|
|
|
|
static int optimize = 0;
|
|
|
|
static FILE *mf;
|
|
|
|
static ZIPfile *zipfile = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* S i g n A r c h i v e
|
|
|
|
*
|
|
|
|
* Sign an individual archive tree. A directory
|
|
|
|
* called META-INF is created underneath this.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
SignArchive(char *tree, char *keyName, char *zip_file, int javascript,
|
|
|
|
char *meta_file, char *install_script, int _optimize, PRBool recurse)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
char tempfn [FNSIZE], fullfn [FNSIZE];
|
|
|
|
int keyType = rsaKey;
|
|
|
|
|
|
|
|
metafile = meta_file;
|
|
|
|
optimize = _optimize;
|
|
|
|
|
|
|
|
/* To create XPI compatible Archive manifesto() must be run before
|
|
|
|
* the zipfile is opened. This is so the signed files are not added
|
|
|
|
* the archive before the crucial rsa/dsa file*/
|
|
|
|
if (xpi_arc) {
|
|
|
|
manifesto (tree, install_script, recurse);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zip_file) {
|
|
|
|
zipfile = JzipOpen(zip_file, NULL /*no comment*/);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*Sign and add files to the archive normally with manifesto()*/
|
|
|
|
if (!xpi_arc) {
|
|
|
|
manifesto (tree, install_script, recurse);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyName) {
|
|
|
|
status = create_pk7 (tree, keyName, &keyType);
|
|
|
|
if (status < 0) {
|
|
|
|
PR_fprintf(errorFD, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n",
|
|
|
|
tree);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add the rsa/dsa file as the first file in the archive. This is crucial
|
|
|
|
* for a XPInstall compatible archive */
|
|
|
|
if (xpi_arc) {
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
PR_fprintf(outputFD, "%s \n", XPI_TEXT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* rsa/dsa to zip */
|
|
|
|
sprintf (tempfn, "META-INF/%s.%s", base, (keyType == dsaKey ?
|
|
|
|
"dsa" : "rsa"));
|
|
|
|
sprintf (fullfn, "%s/%s", tree, tempfn);
|
|
|
|
JzipAdd(fullfn, tempfn, zipfile, compression_level);
|
|
|
|
|
|
|
|
/* Loop through all files & subdirectories, add to archive */
|
|
|
|
foreach (tree, "", manifesto_xpi_fn, recurse, PR_FALSE /*include dirs */,
|
|
|
|
(void * )NULL);
|
|
|
|
}
|
|
|
|
/* mf to zip */
|
|
|
|
strcpy (tempfn, "META-INF/manifest.mf");
|
|
|
|
sprintf (fullfn, "%s/%s", tree, tempfn);
|
|
|
|
JzipAdd(fullfn, tempfn, zipfile, compression_level);
|
|
|
|
|
|
|
|
/* sf to zip */
|
|
|
|
sprintf (tempfn, "META-INF/%s.sf", base);
|
|
|
|
sprintf (fullfn, "%s/%s", tree, tempfn);
|
|
|
|
JzipAdd(fullfn, tempfn, zipfile, compression_level);
|
|
|
|
|
|
|
|
/* Add the rsa/dsa file to the zip archive normally */
|
|
|
|
if (!xpi_arc) {
|
|
|
|
/* rsa/dsa to zip */
|
|
|
|
sprintf (tempfn, "META-INF/%s.%s", base, (keyType == dsaKey ?
|
|
|
|
"dsa" : "rsa"));
|
|
|
|
sprintf (fullfn, "%s/%s", tree, tempfn);
|
|
|
|
JzipAdd(fullfn, tempfn, zipfile, compression_level);
|
|
|
|
}
|
|
|
|
|
|
|
|
JzipClose(zipfile);
|
|
|
|
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
if (javascript) {
|
|
|
|
PR_fprintf(outputFD, "jarfile \"%s\" signed successfully\n",
|
|
|
|
zip_file);
|
|
|
|
} else {
|
|
|
|
PR_fprintf(outputFD, "tree \"%s\" signed successfully\n",
|
|
|
|
tree);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char *keyName;
|
|
|
|
int javascript;
|
|
|
|
char *metafile;
|
|
|
|
char *install_script;
|
|
|
|
int optimize;
|
|
|
|
} SignArcInfo;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* S i g n A l l A r c
|
|
|
|
*
|
|
|
|
* Javascript may generate multiple .arc directories, one
|
|
|
|
* for each jar archive needed. Sign them all.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
SignAllArc(char *jartree, char *keyName, int javascript, char *metafile,
|
|
|
|
char *install_script, int optimize, PRBool recurse)
|
|
|
|
{
|
|
|
|
SignArcInfo info;
|
|
|
|
|
|
|
|
info.keyName = keyName;
|
|
|
|
info.javascript = javascript;
|
|
|
|
info.metafile = metafile;
|
|
|
|
info.install_script = install_script;
|
|
|
|
info.optimize = optimize;
|
|
|
|
|
|
|
|
return foreach(jartree, "", sign_all_arc_fn, recurse,
|
|
|
|
PR_TRUE /*include dirs*/, (void * )&info);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sign_all_arc_fn(char *relpath, char *basedir, char *reldir, char *filename,
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
char *zipfile = NULL;
|
|
|
|
char *arc = NULL, *archive = NULL;
|
|
|
|
int retval = 0;
|
|
|
|
SignArcInfo * infop = (SignArcInfo * )arg;
|
|
|
|
|
|
|
|
/* Make sure there is one and only one ".arc" in the relative path,
|
|
|
|
* and that it is at the end of the path (don't sign .arcs within .arcs) */
|
|
|
|
if ( (PL_strcaserstr(relpath, ".arc") == relpath + strlen(relpath) -
|
|
|
|
4) &&
|
|
|
|
(PL_strcasestr(relpath, ".arc") == relpath + strlen(relpath) - 4) ) {
|
|
|
|
|
|
|
|
if (!infop) {
|
|
|
|
PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
|
|
|
|
errorCount++;
|
|
|
|
retval = -1;
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
archive = PR_smprintf("%s/%s", basedir, relpath);
|
|
|
|
|
|
|
|
zipfile = PL_strdup(archive);
|
|
|
|
arc = PORT_Strrchr (zipfile, '.');
|
|
|
|
|
|
|
|
if (arc == NULL) {
|
|
|
|
PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
|
|
|
|
errorCount++;
|
|
|
|
retval = -1;
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
PL_strcpy (arc, ".jar");
|
|
|
|
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
PR_fprintf(outputFD, "\nsigning: %s\n", zipfile);
|
|
|
|
}
|
|
|
|
retval = SignArchive(archive, infop->keyName, zipfile,
|
|
|
|
infop->javascript, infop->metafile, infop->install_script,
|
|
|
|
infop->optimize, PR_TRUE /* recurse */);
|
|
|
|
}
|
|
|
|
finish:
|
|
|
|
if (archive)
|
|
|
|
PR_Free(archive);
|
|
|
|
if (zipfile)
|
|
|
|
PR_Free(zipfile);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
*
|
|
|
|
* c r e a t e _ p k 7
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
create_pk7 (char *dir, char *keyName, int *keyType)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
char *file_ext;
|
|
|
|
|
|
|
|
CERTCertificate * cert;
|
|
|
|
CERTCertDBHandle * db;
|
|
|
|
|
|
|
|
FILE * in, *out;
|
|
|
|
|
|
|
|
char sf_file [FNSIZE];
|
|
|
|
char pk7_file [FNSIZE];
|
|
|
|
|
|
|
|
/* open cert database */
|
|
|
|
db = CERT_GetDefaultCertDB();
|
|
|
|
|
|
|
|
if (db == NULL)
|
|
|
|
return - 1;
|
|
|
|
|
|
|
|
/* find cert */
|
|
|
|
/*cert = CERT_FindCertByNicknameOrEmailAddr(db, keyName);*/
|
2008-08-14 21:12:54 -07:00
|
|
|
cert = PK11_FindCertFromNickname(keyName, &pwdata);
|
2008-06-06 05:40:11 -07:00
|
|
|
|
|
|
|
if (cert == NULL) {
|
|
|
|
SECU_PrintError ( PROGRAM_NAME,
|
|
|
|
"Cannot find the cert \"%s\"", keyName);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* determine the key type, which sets the extension for pkcs7 object */
|
|
|
|
|
|
|
|
*keyType = jar_find_key_type (cert);
|
|
|
|
file_ext = (*keyType == dsaKey) ? "dsa" : "rsa";
|
|
|
|
|
|
|
|
sprintf (sf_file, "%s/META-INF/%s.sf", dir, base);
|
|
|
|
sprintf (pk7_file, "%s/META-INF/%s.%s", dir, base, file_ext);
|
|
|
|
|
|
|
|
if ((in = fopen (sf_file, "rb")) == NULL) {
|
|
|
|
PR_fprintf(errorFD, "%s: Can't open %s for reading\n", PROGRAM_NAME,
|
|
|
|
sf_file);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((out = fopen (pk7_file, "wb")) == NULL) {
|
|
|
|
PR_fprintf(errorFD, "%s: Can't open %s for writing\n", PROGRAM_NAME,
|
|
|
|
sf_file);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
status = SignFile (out, in, cert);
|
|
|
|
|
|
|
|
CERT_DestroyCertificate (cert);
|
|
|
|
fclose (in);
|
|
|
|
fclose (out);
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
PR_fprintf(errorFD, "%s: PROBLEM signing data (%s)\n",
|
|
|
|
PROGRAM_NAME, SECU_ErrorString ((int16) PORT_GetError()));
|
|
|
|
errorCount++;
|
|
|
|
return - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* j a r _ f i n d _ k e y _ t y p e
|
|
|
|
*
|
|
|
|
* Determine the key type for a given cert, which
|
|
|
|
* should be rsaKey or dsaKey. Any error return 0.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
jar_find_key_type (CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
SECKEYPrivateKey * privk = NULL;
|
|
|
|
KeyType keyType;
|
|
|
|
|
|
|
|
/* determine its type */
|
2008-08-14 21:12:54 -07:00
|
|
|
privk = PK11_FindKeyByAnyCert (cert, &pwdata);
|
2008-06-06 05:40:11 -07:00
|
|
|
if (privk == NULL) {
|
|
|
|
PR_fprintf(errorFD, "warning - can't find private key for this cert\n");
|
|
|
|
warningCount++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
keyType = privk->keyType;
|
|
|
|
SECKEY_DestroyPrivateKey (privk);
|
|
|
|
return keyType;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 *install_script, PRBool recurse)
|
|
|
|
{
|
|
|
|
char metadir [FNSIZE], sfname [FNSIZE];
|
|
|
|
|
|
|
|
/* Create the META-INF directory to hold signing info */
|
|
|
|
|
|
|
|
if (PR_Access (dirname, PR_ACCESS_READ_OK)) {
|
|
|
|
PR_fprintf(errorFD, "%s: unable to read your directory: %s\n",
|
|
|
|
PROGRAM_NAME, dirname);
|
|
|
|
errorCount++;
|
|
|
|
perror (dirname);
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PR_Access (dirname, PR_ACCESS_WRITE_OK)) {
|
|
|
|
PR_fprintf(errorFD, "%s: unable to write to your directory: %s\n",
|
|
|
|
PROGRAM_NAME, dirname);
|
|
|
|
errorCount++;
|
|
|
|
perror(dirname);
|
|
|
|
exit(ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf (metadir, "%s/META-INF", dirname);
|
|
|
|
|
|
|
|
strcpy (sfname, metadir);
|
|
|
|
|
|
|
|
PR_MkDir (metadir, 0777);
|
|
|
|
|
|
|
|
strcat (metadir, "/");
|
|
|
|
strcat (metadir, MANIFEST);
|
|
|
|
|
|
|
|
if ((mf = fopen (metadir, "wb")) == NULL) {
|
|
|
|
perror (MANIFEST);
|
|
|
|
PR_fprintf(errorFD, "%s: Probably, the directory you are trying to"
|
|
|
|
" sign has\n", PROGRAM_NAME);
|
|
|
|
PR_fprintf(errorFD, "%s: permissions problems or may not exist.\n",
|
|
|
|
PROGRAM_NAME);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
PR_fprintf(outputFD, "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 (install_script)
|
|
|
|
fprintf (mf, "Install-Script: %s\n", install_script);
|
|
|
|
|
|
|
|
if (metafile)
|
|
|
|
add_meta (mf, "+");
|
|
|
|
|
|
|
|
/* Loop through all files & subdirectories */
|
|
|
|
foreach (dirname, "", manifesto_fn, recurse, PR_FALSE /*include dirs */,
|
|
|
|
(void * )NULL);
|
|
|
|
|
|
|
|
fclose (mf);
|
|
|
|
|
|
|
|
strcat (sfname, "/");
|
|
|
|
strcat (sfname, base);
|
|
|
|
strcat (sfname, ".sf");
|
|
|
|
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
PR_fprintf(outputFD, "Generating %s.sf file..\n", base);
|
|
|
|
}
|
|
|
|
generate_SF_file (metadir, sfname);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* m a n i f e s t o _ x p i _ f n
|
|
|
|
*
|
|
|
|
* Called by pointer from SignArchive(), once for
|
|
|
|
* each file within the directory. This function
|
|
|
|
* is only used for adding to XPI compatible archive
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int manifesto_xpi_fn
|
|
|
|
(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
|
|
|
|
{
|
|
|
|
char fullname [FNSIZE];
|
|
|
|
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
PR_fprintf(outputFD, "--> %s\n", relpath);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* extension matching */
|
|
|
|
if (extensionsGiven) {
|
|
|
|
char *ext = PL_strrchr(relpath, '.');
|
|
|
|
if (!ext)
|
|
|
|
return 0;
|
|
|
|
if (!PL_HashTableLookup(extensions, ext))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
sprintf (fullname, "%s/%s", basedir, relpath);
|
|
|
|
JzipAdd(fullname, relpath, zipfile, compression_level);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* m a n i f e s t o _ f n
|
|
|
|
*
|
|
|
|
* Called by pointer from manifesto(), once for
|
|
|
|
* each file within the directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int manifesto_fn
|
|
|
|
(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
|
|
|
|
{
|
|
|
|
int use_js;
|
|
|
|
|
|
|
|
JAR_Digest dig;
|
|
|
|
char fullname [FNSIZE];
|
|
|
|
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
PR_fprintf(outputFD, "--> %s\n", relpath);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* extension matching */
|
|
|
|
if (extensionsGiven) {
|
|
|
|
char *ext = PL_strrchr(relpath, '.');
|
|
|
|
if (!ext)
|
|
|
|
return 0;
|
|
|
|
if (!PL_HashTableLookup(extensions, ext))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf (fullname, "%s/%s", basedir, relpath);
|
|
|
|
|
|
|
|
fprintf (mf, "\n");
|
|
|
|
|
|
|
|
use_js = 0;
|
|
|
|
|
|
|
|
if (scriptdir && !PORT_Strcmp (scriptdir, reldir))
|
|
|
|
use_js++;
|
|
|
|
|
|
|
|
/* sign non-.js files inside .arc directories using the javascript magic */
|
|
|
|
|
|
|
|
if ( (PL_strcaserstr(filename, ".js") != filename + strlen(filename) - 3)
|
|
|
|
&& (PL_strcaserstr(reldir, ".arc") == reldir + strlen(filename) - 4))
|
|
|
|
use_js++;
|
|
|
|
|
|
|
|
if (use_js) {
|
|
|
|
fprintf (mf, "Name: %s\n", filename);
|
|
|
|
fprintf (mf, "Magic: javascript\n");
|
|
|
|
|
|
|
|
if (optimize == 0)
|
|
|
|
fprintf (mf, "javascript.id: %s\n", filename);
|
|
|
|
|
|
|
|
if (metafile)
|
|
|
|
add_meta (mf, filename);
|
|
|
|
} else {
|
|
|
|
fprintf (mf, "Name: %s\n", relpath);
|
|
|
|
if (metafile)
|
|
|
|
add_meta (mf, relpath);
|
|
|
|
}
|
|
|
|
|
|
|
|
JAR_digest_file (fullname, &dig);
|
|
|
|
|
|
|
|
|
|
|
|
if (optimize == 0) {
|
|
|
|
fprintf (mf, "Digest-Algorithms: MD5 SHA1\n");
|
|
|
|
fprintf (mf, "MD5-Digest: %s\n", BTOA_DataToAscii (dig.md5,
|
|
|
|
MD5_LENGTH));
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf (mf, "SHA1-Digest: %s\n", BTOA_DataToAscii (dig.sha1, SHA1_LENGTH));
|
|
|
|
|
|
|
|
if (!use_js) {
|
|
|
|
JzipAdd(fullname, relpath, zipfile, compression_level);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* a d d _ m e t a
|
|
|
|
*
|
|
|
|
* Parse the metainfo file, and add any details
|
|
|
|
* necessary to the manifest file. In most cases you
|
|
|
|
* should be using the -i option (ie, for SmartUpdate).
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
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 (!PORT_Strcmp (pattern, name))
|
|
|
|
place = 1;
|
|
|
|
|
|
|
|
if (place) {
|
|
|
|
num++;
|
|
|
|
if (verbosity >= 0) {
|
|
|
|
PR_fprintf(outputFD, "[%s] %s\n", name, meta);
|
|
|
|
}
|
|
|
|
fprintf (fp, "%s\n", meta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose (met);
|
|
|
|
} else {
|
|
|
|
PR_fprintf(errorFD, "%s: can't open metafile: %s\n", PROGRAM_NAME,
|
|
|
|
metafile);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
*
|
|
|
|
* S i g n F i l e
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
SignFile (FILE *outFile, FILE *inFile, CERTCertificate *cert)
|
|
|
|
{
|
|
|
|
int nb;
|
|
|
|
char ibuf[4096], digestdata[32];
|
|
|
|
const SECHashObject *hashObj;
|
|
|
|
void *hashcx;
|
|
|
|
unsigned int len;
|
|
|
|
|
|
|
|
SECItem digest;
|
|
|
|
SEC_PKCS7ContentInfo * cinfo;
|
|
|
|
SECStatus rv;
|
|
|
|
|
|
|
|
if (outFile == NULL || inFile == NULL || cert == NULL)
|
|
|
|
return - 1;
|
|
|
|
|
|
|
|
/* XXX probably want to extend interface to allow other hash algorithms */
|
|
|
|
hashObj = HASH_GetHashObject(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, (unsigned char *) ibuf, nb);
|
|
|
|
}
|
|
|
|
|
|
|
|
(*hashObj->end)(hashcx, (unsigned char *) digestdata, &len, 32);
|
|
|
|
(*hashObj->destroy)(hashcx, PR_TRUE);
|
|
|
|
|
|
|
|
digest.data = (unsigned char *) digestdata;
|
|
|
|
digest.len = len;
|
|
|
|
|
|
|
|
cinfo = SEC_PKCS7CreateSignedData
|
|
|
|
(cert, certUsageObjectSigner, 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 */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-14 21:12:54 -07:00
|
|
|
rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL, &pwdata);
|
2008-06-06 05:40:11 -07:00
|
|
|
|
|
|
|
SEC_PKCS7DestroyContentInfo (cinfo);
|
|
|
|
|
|
|
|
if (rv != SECSuccess)
|
|
|
|
return - 1;
|
|
|
|
|
|
|
|
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 [FNSIZE];
|
|
|
|
char *buf, *name = NULL;
|
|
|
|
JAR_Digest dig;
|
|
|
|
int line = 0;
|
|
|
|
|
|
|
|
strcpy (whofile, who);
|
|
|
|
|
|
|
|
if ((mf = fopen (manifile, "rb")) == NULL) {
|
|
|
|
perror (manifile);
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sf = fopen (whofile, "wb")) == NULL) {
|
|
|
|
perror (who);
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = (char *) PORT_ZAlloc (BUFSIZ);
|
|
|
|
|
|
|
|
if (buf)
|
|
|
|
name = (char *) PORT_ZAlloc (BUFSIZ);
|
|
|
|
|
|
|
|
if (buf == NULL || name == NULL)
|
|
|
|
out_of_memory();
|
|
|
|
|
|
|
|
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) {
|
|
|
|
PR_fprintf(errorFD, "%s: empty manifest file!\n", PROGRAM_NAME);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp (buf, "Manifest-Version:", 17)) {
|
|
|
|
PR_fprintf(errorFD, "%s: not a manifest file!\n", PROGRAM_NAME);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
PR_fprintf(errorFD,
|
|
|
|
"warning: unexpected input in manifest file \"%s\" at line %d:\n",
|
|
|
|
manifile, line);
|
|
|
|
PR_fprintf(errorFD, "%s\n", name);
|
|
|
|
warningCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
r2 = r1;
|
|
|
|
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, MD5_LENGTH));
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf (sf, "SHA1-Digest: %s\n",
|
|
|
|
BTOA_DataToAscii (dig.sha1, SHA1_LENGTH));
|
|
|
|
|
|
|
|
/* restore normalcy after changing offset position */
|
|
|
|
fseek (mf, r3, SEEK_SET);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Free (buf);
|
|
|
|
PORT_Free (name);
|
|
|
|
|
|
|
|
fclose (sf);
|
|
|
|
fclose (mf);
|
|
|
|
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
calculate_MD5_range (FILE *fp, long r1, long r2, JAR_Digest *dig)
|
|
|
|
{
|
|
|
|
int num;
|
|
|
|
int range;
|
|
|
|
unsigned char *buf;
|
2008-08-14 21:12:54 -07:00
|
|
|
SECStatus rv;
|
2008-06-06 05:40:11 -07:00
|
|
|
|
|
|
|
range = r2 - r1;
|
|
|
|
|
|
|
|
/* position to the beginning of range */
|
|
|
|
fseek (fp, r1, SEEK_SET);
|
|
|
|
|
|
|
|
buf = (unsigned char *) PORT_ZAlloc (range);
|
|
|
|
if (buf == NULL)
|
|
|
|
out_of_memory();
|
|
|
|
|
|
|
|
if ((num = fread (buf, 1, range, fp)) != range) {
|
|
|
|
PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME,
|
|
|
|
range, num);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
2008-08-14 21:12:54 -07:00
|
|
|
rv = PK11_HashBuf(SEC_OID_MD5, dig->md5, buf, range);
|
|
|
|
if (rv == SECSuccess) {
|
|
|
|
rv =PK11_HashBuf(SEC_OID_SHA1, dig->sha1, buf, range);
|
|
|
|
}
|
|
|
|
if (rv != SECSuccess) {
|
2008-06-06 05:40:11 -07:00
|
|
|
PR_fprintf(errorFD, "%s: can't generate digest context\n",
|
|
|
|
PROGRAM_NAME);
|
|
|
|
errorCount++;
|
|
|
|
exit (ERRX);
|
|
|
|
}
|
|
|
|
|
|
|
|
PORT_Free (buf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void SignOut (void *arg, const char *buf, unsigned long len)
|
|
|
|
{
|
|
|
|
fwrite (buf, len, 1, (FILE * ) arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|