Merge central to inbound

This commit is contained in:
Marco Bonardo 2012-02-25 11:12:42 +01:00
commit 0e771aedaf
76 changed files with 4305 additions and 69 deletions

View File

@ -106,10 +106,14 @@
gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
if (SEAMONKEY) {
todo(false, "shift tab from editable document fails on (Windows) SeaMonkey! (Bug 718235)");
} else {
if (LINUX)
todo(false, "shift tab from editable document Fails on linux!");
todo(false, "shift tab from editable document fails on linux!");
else
gQueue.push(new synthShiftTab("link", new focusChecker("link")));
} // ! SEAMONKEY
gQueue.invoke(); // Will call SimpleTest.finish();
}

View File

@ -42,6 +42,7 @@ MOZ_UPDATER=1
MOZ_PHOENIX=1
if test "$OS_ARCH" = "WINNT"; then
MOZ_VERIFY_MAR_SIGNATURE=1
if ! test "$HAVE_64BIT_OS"; then
MOZ_MAINTENANCE_SERVICE=1
fi
@ -58,6 +59,12 @@ MOZ_EXTENSIONS_DEFAULT=" gnomevfs"
MOZ_BRANDING_DIRECTORY=browser/branding/nightly
MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
# This should usually be the same as the value MAR_CHANNEL_ID.
# If more than one ID is needed, then you should use a comma separated list
# of values.
ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
MAR_CHANNEL_ID=firefox-mozilla-central
MOZ_PROFILE_MIGRATOR=1
MOZ_EXTENSION_MANAGER=1
MOZ_APP_STATIC_INI=1

View File

@ -97,6 +97,7 @@
@BINPATH@/@MOZ_APP_NAME@
#endif
@BINPATH@/application.ini
@BINPATH@/update-settings.ini
@BINPATH@/platform.ini
#ifndef XP_OS2
@BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@

View File

@ -68,6 +68,10 @@ endif
ifdef MOZ_APP_BASENAME
DIST_FILES = application.ini
ifneq (android,$(MOZ_WIDGET_TOOLKIT))
DIST_FILES += update-settings.ini
endif
ifdef LIBXUL_SDK
GRE_MILESTONE = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build Milestone)
APP_INI_DEPS = $(LIBXUL_DIST)/bin/platform.ini
@ -99,6 +103,8 @@ DEFINES += \
-DMOZ_APP_BASENAME="$(MOZ_APP_BASENAME)" \
-DMOZ_APP_VENDOR="$(MOZ_APP_VENDOR)" \
-DMOZ_APP_ID="$(MOZ_APP_ID)" \
-DMAR_CHANNEL_ID="$(MAR_CHANNEL_ID)" \
-DACCEPTED_MAR_CHANNEL_IDS="$(ACCEPTED_MAR_CHANNEL_IDS)" \
$(NULL)
ifdef MOZ_APP_PROFILE
@ -153,6 +159,12 @@ leaktest.py: leaktest.py.in
chmod +x $@
GARBAGE += leaktest.py
ifneq (android,$(MOZ_WIDGET_TOOLKIT))
update-settings.ini: update-settings.ini.in $(APP_INI_DEPS)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
GARBAGE += update-settings.ini
endif
ifdef MOZ_APP_BASENAME
application.ini: application.ini.in $(APP_INI_DEPS)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@

View File

@ -0,0 +1,44 @@
#if 0
; ***** 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 MAR channel config.
;
; The Initial Developer of the Original Code is
; Mozilla Foundation.
; Portions created by the Initial Developer are Copyright (C) 2011
; the Initial Developer. All Rights Reserved.
;
; Contributor(s):
; Brian R. Bondy <netzen@gmail.com>
;
; 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 *****
#endif
; If you modify this file updates may fail.
; Do not modify this file.
#filter substitution
[Settings]
ACCEPTED_MAR_CHANNEL_IDS=@ACCEPTED_MAR_CHANNEL_IDS@

View File

@ -53,6 +53,8 @@ MOZ_APP_BASENAME = @MOZ_APP_BASENAME@
MOZ_APP_VENDOR = @MOZ_APP_VENDOR@
MOZ_APP_PROFILE = @MOZ_APP_PROFILE@
MOZ_APP_ID = @MOZ_APP_ID@
MAR_CHANNEL_ID = @MAR_CHANNEL_ID@
ACCEPTED_MAR_CHANNEL_IDS = @ACCEPTED_MAR_CHANNEL_IDS@
MOZ_PROFILE_MIGRATOR = @MOZ_PROFILE_MIGRATOR@
MOZ_EXTENSION_MANAGER = @MOZ_EXTENSION_MANAGER@
MOZ_APP_UA_NAME = @MOZ_APP_UA_NAME@
@ -140,6 +142,7 @@ MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@
XPCOM_USE_LEA = @XPCOM_USE_LEA@
MOZ_INSTALLER = @MOZ_INSTALLER@
MOZ_MAINTENANCE_SERVICE = @MOZ_MAINTENANCE_SERVICE@
MOZ_VERIFY_MAR_SIGNATURE = @MOZ_VERIFY_MAR_SIGNATURE@
MOZ_UPDATER = @MOZ_UPDATER@
MOZ_UPDATE_CHANNEL = @MOZ_UPDATE_CHANNEL@
MOZ_UPDATE_PACKAGING = @MOZ_UPDATE_PACKAGING@

View File

@ -6481,6 +6481,23 @@ if test -n "$MOZ_MAINTENANCE_SERVICE"; then
fi
fi
dnl ========================================================
dnl Verify MAR signatures
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(verify-mar,
[ --enable-verify-mar Enable verifying MAR signatures],
MOZ_VERIFY_MAR_SIGNATURE=1,
MOZ_VERIFY_MAR_SIGNATURE= )
if test -n "$MOZ_VERIFY_MAR_SIGNATURE"; then
if test "$OS_ARCH" = "WINNT"; then
AC_DEFINE(MOZ_VERIFY_MAR_SIGNATURE)
else
AC_MSG_ERROR([Can only build with --enable-verify-mar with a Windows target])
fi
fi
dnl ========================================================
dnl Updater
dnl ========================================================
@ -8589,6 +8606,7 @@ AC_SUBST(MOZ_ONLY_TOUCH_EVENTS)
AC_SUBST(MOZ_USER_DIR)
AC_SUBST(MOZ_CRASHREPORTER)
AC_SUBST(MOZ_MAINTENANCE_SERVICE)
AC_SUBST(MOZ_VERIFY_MAR_SIGNATURE)
AC_SUBST(MOZ_UPDATER)
AC_SUBST(MOZ_ANGLE)
AC_SUBST(MOZ_DIRECTX_SDK_PATH)
@ -8676,6 +8694,8 @@ AC_SUBST(MOZ_APP_BASENAME)
AC_SUBST(MOZ_APP_VENDOR)
AC_SUBST(MOZ_APP_PROFILE)
AC_SUBST(MOZ_APP_ID)
AC_SUBST(MAR_CHANNEL_ID)
AC_SUBST(ACCEPTED_MAR_CHANNEL_IDS)
AC_SUBST(MOZ_PROFILE_MIGRATOR)
AC_SUBST(MOZ_EXTENSION_MANAGER)
AC_DEFINE_UNQUOTED(MOZ_APP_UA_NAME, "$MOZ_APP_UA_NAME")

View File

@ -43,6 +43,11 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = src tool
DIRS = sign verify src tool
MODULE = libmar_test
ifdef ENABLE_TESTS
DIRS += tests
endif
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,69 @@
# ***** 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 mar signing build config.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Brian R. Bondy <netzen@gmail.com>
#
# 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = signmar
LIBRARY_NAME = signmar
FORCE_STATIC_LIB = 1
ifeq ($(OS_ARCH),WINNT)
USE_STATIC_LIBS = 1
endif
# This makefile just builds support for reading archives.
CSRCS = \
mar_sign.c \
nss_secutil.c \
$(NULL)
LOCAL_INCLUDES += -I$(srcdir)/../src \
-I$(srcdir)/../verify \
-I$(topsrcdir)/dist/include \
$(NULL)
CFLAGS += -DMAR_NSS
include $(topsrcdir)/config/rules.mk
# The intermediate (.ii/.s) files for host and target can have the same name...
# disable parallel builds
.NOTPARALLEL:

View File

@ -0,0 +1,818 @@
/* ***** 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 Mozilla Archive signing code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* 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 ***** */
#ifdef XP_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "mar_private.h"
#include "mar_cmdline.h"
#include "mar.h"
#include "cryptox.h"
#ifndef XP_WIN
#include <unistd.h>
#endif
#include "nss_secutil.h"
/**
* Initializes the NSS context.
*
* @param NSSConfigDir The config dir containing the private key to use
* @return 0 on success
* -1 on error
*/
int
NSSInitCryptoContext(const char *NSSConfigDir)
{
SECStatus status = NSS_Initialize(NSSConfigDir,
"", "", SECMOD_DB, NSS_INIT_READONLY);
if (SECSuccess != status) {
fprintf(stderr, "ERROR: Could not initialize NSS\n");
return -1;
}
return 0;
}
/**
* Obtains a signing context.
*
* @param ctx A pointer to the signing context to fill
* @return 0 on success
* -1 on error
*/
int
NSSSignBegin(const char *certName,
SGNContext **ctx,
SECKEYPrivateKey **privKey,
CERTCertificate **cert,
PRUint32 *signatureLength)
{
secuPWData pwdata = { PW_NONE, 0 };
if (!certName || !ctx || !privKey || !cert || !signatureLength) {
fprintf(stderr, "ERROR: Invalid parameter passed to NSSSignBegin\n");
return -1;
}
/* Get the cert and embedded public key out of the database */
*cert = PK11_FindCertFromNickname(certName, &pwdata);
if (!*cert) {
fprintf(stderr, "ERROR: Could not find cert from nickname\n");
return -1;
}
/* Get the private key out of the database */
*privKey = PK11_FindKeyByAnyCert(*cert, &pwdata);
if (!*privKey) {
fprintf(stderr, "ERROR: Could not find private key\n");
return -1;
}
*signatureLength = PK11_SignatureLen(*privKey);
if (*signatureLength > BLOCKSIZE) {
fprintf(stderr,
"ERROR: Program must be compiled with a larger block size"
" to support signing with signatures this large: %u.\n",
*signatureLength);
return -1;
}
/* Check that the key length is large enough for our requirements */
if (*signatureLength < XP_MIN_SIGNATURE_LEN_IN_BYTES) {
fprintf(stderr, "ERROR: Key length must be >= %d bytes\n",
XP_MIN_SIGNATURE_LEN_IN_BYTES);
return -1;
}
*ctx = SGN_NewContext (SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE, *privKey);
if (!*ctx) {
fprintf(stderr, "ERROR: Could not create signature context\n");
return -1;
}
if (SGN_Begin(*ctx) != SECSuccess) {
fprintf(stderr, "ERROR: Could not begin signature\n");
return -1;
}
return 0;
}
/**
* Writes the passed buffer to the file fp and updates the signature context.
*
* @param fpDest The file pointer to write to.
* @param buffer The buffer to write.
* @param size The size of the buffer to write.
* @param ctx The signature context.
* @param err The name of what is being written to in case of error.
* @return 0 on success
* -2 on write error
* -3 on signature update error
*/
int
WriteAndUpdateSignature(FILE *fpDest, void *buffer,
PRUint32 size, SGNContext *ctx,
const char *err)
{
if (!size) {
return 0;
}
if (fwrite(buffer, size, 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write %s\n", err);
return -2;
}
if (SGN_Update(ctx, (const unsigned char *)buffer, size) != SECSuccess) {
fprintf(stderr, "ERROR: Could not update signature context for %s\n", err);
return -3;
}
return 0;
}
/**
* Adjusts each entry's content offset in the the passed in index by the
* specified amount.
*
* @param indexBuf A buffer containing the MAR index
* @param indexLength The length of the MAR index
* @param offsetAmount The amount to adjust each index entry by
*/
void
AdjustIndexContentOffsets(char *indexBuf, PRUint32 indexLength, PRUint32 offsetAmount)
{
PRUint32 *offsetToContent;
char *indexBufLoc = indexBuf;
/* Consume the index and adjust each index by the specified amount */
while (indexBufLoc != (indexBuf + indexLength)) {
/* Adjust the offset */
offsetToContent = (PRUint32 *)indexBufLoc;
*offsetToContent = ntohl(*offsetToContent);
*offsetToContent += offsetAmount;
*offsetToContent = htonl(*offsetToContent);
/* Skip past the offset, length, and flags */
indexBufLoc += 3 * sizeof(PRUint32);
indexBufLoc += strlen(indexBufLoc) + 1;
}
}
/**
* Reads from fpSrc, writes it to fpDest, and updates the signature context.
*
* @param fpSrc The file pointer to read from.
* @param fpDest The file pointer to write to.
* @param buffer The buffer to write.
* @param size The size of the buffer to write.
* @param ctx The signature context.
* @param err The name of what is being written to in case of error.
* @return 0 on success
* -1 on read error
* -2 on write error
* -3 on signature update error
*/
int
ReadWriteAndUpdateSignature(FILE *fpSrc, FILE *fpDest, void *buffer,
PRUint32 size, SGNContext *ctx,
const char *err)
{
if (!size) {
return 0;
}
if (fread(buffer, size, 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could not read %s\n", err);
return -1;
}
return WriteAndUpdateSignature(fpDest, buffer, size, ctx, err);
}
/**
* Reads from fpSrc, writes it to fpDest.
*
* @param fpSrc The file pointer to read from.
* @param fpDest The file pointer to write to.
* @param buffer The buffer to write.
* @param size The size of the buffer to write.
* @param err The name of what is being written to in case of error.
* @return 0 on success
* -1 on read error
* -2 on write error
*/
int
ReadAndWrite(FILE *fpSrc, FILE *fpDest, void *buffer,
PRUint32 size, const char *err)
{
if (!size) {
return 0;
}
if (fread(buffer, size, 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could not read %s\n", err);
return -1;
}
if (fwrite(buffer, size, 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write %s\n", err);
return -2;
}
return 0;
}
/**
* Writes out a copy of the MAR at src but with the signature block stripped.
*
* @param src The path of the source MAR file
* @param dest The path of the MAR file to write out that
has no signature block
* @return 0 on success
* -1 on error
*/
int
strip_signature_block(const char *src, const char * dest)
{
PRUint32 offsetToIndex, dstOffsetToIndex, indexLength,
numSignatures = 0, leftOver;
PRInt32 stripAmount = 0;
PRInt64 oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, numBytesToCopy,
numChunks, i;
FILE *fpSrc = NULL, *fpDest = NULL;
int rv = -1, hasSignatureBlock;
char buf[BLOCKSIZE];
char *indexBuf = NULL, *indexBufLoc;
if (!src || !dest) {
fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
return -1;
}
fpSrc = fopen(src, "rb");
if (!fpSrc) {
fprintf(stderr, "ERROR: could not open source file: %s\n", dest);
goto failure;
}
fpDest = fopen(dest, "wb");
if (!fpDest) {
fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
goto failure;
}
/* Determine if the source MAR file has the new fields for signing or not */
if (get_mar_file_info(src, &hasSignatureBlock, NULL, NULL, NULL, NULL)) {
fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
goto failure;
}
/* MAR ID */
if (ReadAndWrite(fpSrc, fpDest, buf, MAR_ID_SIZE, "MAR ID")) {
goto failure;
}
/* Offset to index */
if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could not read offset\n");
goto failure;
}
offsetToIndex = ntohl(offsetToIndex);
/* Get the real size of the MAR */
oldPos = ftello(fpSrc);
if (fseeko(fpSrc, 0, SEEK_END)) {
fprintf(stderr, "ERROR: Could not seek to end of file.\n");
goto failure;
}
realSizeOfSrcMAR = ftello(fpSrc);
if (fseeko(fpSrc, oldPos, SEEK_SET)) {
fprintf(stderr, "ERROR: Could not seek back to current location.\n");
goto failure;
}
if (hasSignatureBlock) {
/* Get the MAR length and adjust its size */
if (fread(&sizeOfEntireMAR,
sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could read mar size\n");
goto failure;
}
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
if (sizeOfEntireMAR != realSizeOfSrcMAR) {
fprintf(stderr, "ERROR: Source MAR is not of the right size\n");
goto failure;
}
/* Get the num signatures in the source file so we know what to strip */
if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could read num signatures\n");
goto failure;
}
numSignatures = ntohl(numSignatures);
for (i = 0; i < numSignatures; i++) {
PRUint32 signatureLen;
/* Skip past the signature algorithm ID */
if (fseeko(fpSrc, sizeof(PRUint32), SEEK_CUR)) {
fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n");
}
/* Read in the length of the signature so we know how far to skip */
if (fread(&signatureLen, sizeof(PRUint32), 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could not read signatures length.\n");
return CryptoX_Error;
}
signatureLen = ntohl(signatureLen);
/* Skip past the signature */
if (fseeko(fpSrc, signatureLen, SEEK_CUR)) {
fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n");
}
stripAmount += sizeof(PRUint32) + sizeof(PRUint32) + signatureLen;
}
} else {
sizeOfEntireMAR = realSizeOfSrcMAR;
numSignatures = 0;
}
if (((PRInt64)offsetToIndex) > sizeOfEntireMAR) {
fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n");
goto failure;
}
dstOffsetToIndex = offsetToIndex;
if (!hasSignatureBlock) {
dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
}
dstOffsetToIndex -= stripAmount;
/* Write out the index offset */
dstOffsetToIndex = htonl(dstOffsetToIndex);
if (fwrite(&dstOffsetToIndex, sizeof(dstOffsetToIndex), 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write offset to index\n");
goto failure;
}
dstOffsetToIndex = ntohl(dstOffsetToIndex);
/* Write out the new MAR file size */
if (!hasSignatureBlock) {
sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
}
sizeOfEntireMAR -= stripAmount;
/* Write out the MAR size */
sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write size of MAR\n");
goto failure;
}
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
/* Write out the number of signatures, which is 0 */
numSignatures = 0;
if (fwrite(&numSignatures, sizeof(numSignatures), 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write out num signatures\n");
goto failure;
}
/* Write out the rest of the MAR excluding the index header and index
offsetToIndex unfortunately has to remain 32-bit because for backwards
compatibility with the old MAR file format. */
if (ftello(fpSrc) > ((PRInt64)offsetToIndex)) {
fprintf(stderr, "ERROR: Index offset is too small.\n");
goto failure;
}
numBytesToCopy = ((PRInt64)offsetToIndex) - ftello(fpSrc);
numChunks = numBytesToCopy / BLOCKSIZE;
leftOver = numBytesToCopy % BLOCKSIZE;
/* Read each file and write it to the MAR file */
for (i = 0; i < numChunks; ++i) {
if (ReadAndWrite(fpSrc, fpDest, buf, BLOCKSIZE, "content block")) {
goto failure;
}
}
/* Write out the left over */
if (ReadAndWrite(fpSrc, fpDest, buf,
leftOver, "left over content block")) {
goto failure;
}
/* Length of the index */
if (ReadAndWrite(fpSrc, fpDest, &indexLength,
sizeof(indexLength), "index length")) {
goto failure;
}
indexLength = ntohl(indexLength);
/* Consume the index and adjust each index by the difference */
indexBuf = malloc(indexLength);
indexBufLoc = indexBuf;
if (fread(indexBuf, indexLength, 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could not read index\n");
goto failure;
}
/* Adjust each entry in the index */
if (hasSignatureBlock) {
AdjustIndexContentOffsets(indexBuf, indexLength, -stripAmount);
} else {
AdjustIndexContentOffsets(indexBuf, indexLength,
sizeof(sizeOfEntireMAR) +
sizeof(numSignatures) -
stripAmount);
}
if (fwrite(indexBuf, indexLength, 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write index\n");
goto failure;
}
rv = 0;
failure:
if (fpSrc) {
fclose(fpSrc);
}
if (fpDest) {
fclose(fpDest);
}
if (rv) {
remove(dest);
}
if (indexBuf) {
free(indexBuf);
}
if (rv) {
remove(dest);
}
return rv;
}
/**
* Writes out a copy of the MAR at src but with an embedded signature.
* The passed in MAR file must not already be signed or an error will
* be returned.
*
* @param NSSConfigDir The NSS directory containing the private key for signing
* @param certName The nickname of the certificate to use for signing
* @param src The path of the source MAR file to sign
* @param dest The path of the MAR file to write out that is signed
* @return 0 on success
* -1 on error
*/
int
mar_repackage_and_sign(const char *NSSConfigDir,
const char *certName,
const char *src,
const char *dest)
{
PRUint32 offsetToIndex, dstOffsetToIndex, indexLength,
numSignatures = 0, signatureLength, leftOver,
signatureAlgorithmID, signatureSectionLength;
PRInt64 oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR,
signaturePlaceholderOffset, numBytesToCopy,
numChunks, i;
FILE *fpSrc = NULL, *fpDest = NULL;
int rv = -1, hasSignatureBlock;
SGNContext *ctx = NULL;
SECItem secItem;
char buf[BLOCKSIZE];
SECKEYPrivateKey *privKey = NULL;
CERTCertificate *cert = NULL;
char *indexBuf = NULL, *indexBufLoc;
if (!NSSConfigDir || !certName || !src || !dest) {
fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
return -1;
}
if (NSSInitCryptoContext(NSSConfigDir)) {
fprintf(stderr, "ERROR: Could not init config dir: %s\n", NSSConfigDir);
goto failure;
}
PK11_SetPasswordFunc(SECU_GetModulePassword);
if (NSSSignBegin(certName, &ctx, &privKey, &cert, &signatureLength)) {
fprintf(stderr, "ERROR: NSSSignBegin failed\n");
goto failure;
}
fpSrc = fopen(src, "rb");
if (!fpSrc) {
fprintf(stderr, "ERROR: could not open source file: %s\n", dest);
goto failure;
}
fpDest = fopen(dest, "wb");
if (!fpDest) {
fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
goto failure;
}
/* Determine if the source MAR file has the new fields for signing or not */
if (get_mar_file_info(src, &hasSignatureBlock, NULL, NULL, NULL, NULL)) {
fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
goto failure;
}
/* MAR ID */
if (ReadWriteAndUpdateSignature(fpSrc, fpDest,
buf, MAR_ID_SIZE,
ctx, "MAR ID")) {
goto failure;
}
/* Offset to index */
if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could not read offset\n");
goto failure;
}
offsetToIndex = ntohl(offsetToIndex);
/* Get the real size of the MAR */
oldPos = ftello(fpSrc);
if (fseeko(fpSrc, 0, SEEK_END)) {
fprintf(stderr, "ERROR: Could not seek to end of file.\n");
goto failure;
}
realSizeOfSrcMAR = ftello(fpSrc);
if (fseeko(fpSrc, oldPos, SEEK_SET)) {
fprintf(stderr, "ERROR: Could not seek back to current location.\n");
goto failure;
}
if (hasSignatureBlock) {
/* Get the MAR length and adjust its size */
if (fread(&sizeOfEntireMAR,
sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could read mar size\n");
goto failure;
}
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
if (sizeOfEntireMAR != realSizeOfSrcMAR) {
fprintf(stderr, "ERROR: Source MAR is not of the right size\n");
goto failure;
}
/* Get the num signatures in the source file so we know what to skip over */
if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could read num signatures\n");
goto failure;
}
numSignatures = ntohl(numSignatures);
/* We do not support resigning */
if (numSignatures) {
fprintf(stderr, "ERROR: MAR is already signed\n");
goto failure;
}
} else {
sizeOfEntireMAR = realSizeOfSrcMAR;
}
if (((PRInt64)offsetToIndex) > sizeOfEntireMAR) {
fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n");
goto failure;
}
/* Write out the new offset to the index */
signatureSectionLength = sizeof(signatureAlgorithmID) +
sizeof(signatureLength) +
signatureLength;
dstOffsetToIndex = offsetToIndex;
if (!hasSignatureBlock) {
dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
}
dstOffsetToIndex += signatureSectionLength;
/* Write out the index offset */
dstOffsetToIndex = htonl(dstOffsetToIndex);
if (WriteAndUpdateSignature(fpDest, &dstOffsetToIndex,
sizeof(dstOffsetToIndex), ctx, "index offset")) {
goto failure;
}
dstOffsetToIndex = ntohl(dstOffsetToIndex);
/* Write out the new MAR file size */
sizeOfEntireMAR += signatureSectionLength;
if (!hasSignatureBlock) {
sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
}
/* Write out the MAR size */
sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
if (WriteAndUpdateSignature(fpDest, &sizeOfEntireMAR,
sizeof(sizeOfEntireMAR), ctx, "size of MAR")) {
goto failure;
}
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
/* Write out the number of signatures, for now only 1 is supported */
numSignatures = 1;
numSignatures = htonl(numSignatures);
if (WriteAndUpdateSignature(fpDest, &numSignatures,
sizeof(numSignatures), ctx, "num signatures")) {
goto failure;
}
numSignatures = ntohl(numSignatures);
/* Write out the signature ID, for now only an ID of 1 is supported */
signatureAlgorithmID = htonl(1);
if (WriteAndUpdateSignature(fpDest, &signatureAlgorithmID,
sizeof(signatureAlgorithmID),
ctx, "num signatures")) {
goto failure;
}
signatureAlgorithmID = ntohl(signatureAlgorithmID);
/* Write out the signature length */
signatureLength = htonl(signatureLength);
if (WriteAndUpdateSignature(fpDest, &signatureLength,
sizeof(signatureLength),
ctx, "signature length")) {
goto failure;
}
signatureLength = ntohl(signatureLength);
/* Write out a placeholder for the signature, we'll come back to this later
*** THIS IS NOT SIGNED because it is a placeholder that will be replaced
below, plus it is going to be the signature itself. *** */
memset(buf, 0, sizeof(buf));
signaturePlaceholderOffset = ftello(fpDest);
if (fwrite(buf, signatureLength, 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write signature length\n");
goto failure;
}
/* Write out the rest of the MAR excluding the index header and index
offsetToIndex unfortunately has to remain 32-bit because for backwards
compatibility with the old MAR file format. */
if (ftello(fpSrc) > ((PRInt64)offsetToIndex)) {
fprintf(stderr, "ERROR: Index offset is too small.\n");
goto failure;
}
numBytesToCopy = ((PRInt64)offsetToIndex) - ftello(fpSrc);
numChunks = numBytesToCopy / BLOCKSIZE;
leftOver = numBytesToCopy % BLOCKSIZE;
/* Read each file and write it to the MAR file */
for (i = 0; i < numChunks; ++i) {
if (ReadWriteAndUpdateSignature(fpSrc, fpDest, buf,
BLOCKSIZE, ctx, "content block")) {
goto failure;
}
}
/* Write out the left over */
if (ReadWriteAndUpdateSignature(fpSrc, fpDest, buf,
leftOver, ctx, "left over content block")) {
goto failure;
}
/* Length of the index */
if (ReadWriteAndUpdateSignature(fpSrc, fpDest, &indexLength,
sizeof(indexLength), ctx, "index length")) {
goto failure;
}
indexLength = ntohl(indexLength);
/* Consume the index and adjust each index by signatureSectionLength */
indexBuf = malloc(indexLength);
indexBufLoc = indexBuf;
if (fread(indexBuf, indexLength, 1, fpSrc) != 1) {
fprintf(stderr, "ERROR: Could not read index\n");
goto failure;
}
/* Adjust each entry in the index */
if (hasSignatureBlock) {
AdjustIndexContentOffsets(indexBuf, indexLength, signatureSectionLength);
} else {
AdjustIndexContentOffsets(indexBuf, indexLength,
sizeof(sizeOfEntireMAR) +
sizeof(numSignatures) +
signatureSectionLength);
}
if (WriteAndUpdateSignature(fpDest, indexBuf,
indexLength, ctx, "index")) {
goto failure;
}
/* Ensure that we don't sign a file that is too large to be accepted by
the verification function. */
if (ftello(fpDest) > MAX_SIZE_OF_MAR_FILE) {
goto failure;
}
/* Get the signature */
if (SGN_End(ctx, &secItem) != SECSuccess) {
fprintf(stderr, "ERROR: Could not end signature context\n");
goto failure;
}
if (signatureLength != secItem.len) {
fprintf(stderr, "ERROR: Signature is not the expected length\n");
goto failure;
}
/* Get back to the location of the signature placeholder */
if (fseeko(fpDest, signaturePlaceholderOffset, SEEK_SET)) {
fprintf(stderr, "ERROR: Could not seek to signature offset\n");
goto failure;
}
/* Write out the calculated signature.
*** THIS IS NOT SIGNED because it is the signature itself. *** */
if (fwrite(secItem.data, secItem.len, 1, fpDest) != 1) {
fprintf(stderr, "ERROR: Could not write signature\n");
goto failure;
}
rv = 0;
failure:
if (fpSrc) {
fclose(fpSrc);
}
if (fpDest) {
fclose(fpDest);
}
if (rv) {
remove(dest);
}
if (indexBuf) {
free(indexBuf);
}
if (ctx) {
SGN_DestroyContext(ctx, PR_TRUE);
}
if (cert) {
CERT_DestroyCertificate(cert);
}
if (privKey) {
SECKEY_DestroyPrivateKey(privKey);
}
if (rv) {
remove(dest);
}
return rv;
}

View File

@ -0,0 +1,269 @@
/* ***** 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 code copied from secutil and secpwd.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* 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 ***** */
/* With the exception of GetPasswordString, this file was
copied from NSS's cmd/lib/secutil.c hg revision 8f011395145e */
#include "nss_secutil.h"
#include "prprf.h"
#ifdef XP_WIN
#include <io.h>
#else
#include <unistd.h>
#endif
static char consoleName[] = {
#ifdef XP_UNIX
"/dev/tty"
#else
#ifdef XP_OS2
"\\DEV\\CON"
#else
"CON:"
#endif
#endif
};
#if defined(_WINDOWS)
static char * quiet_fgets (char *buf, int length, FILE *input)
{
int c;
char *end = buf;
/* fflush (input); */
memset (buf, 0, length);
if (!isatty(fileno(input))) {
return fgets(buf,length,input);
}
while (1)
{
#if defined (_WIN32_WCE)
c = getchar(); /* gets a character from stdin */
#else
c = getch(); /* getch gets a character from the console */
#endif
if (c == '\b')
{
if (end > buf)
end--;
}
else if (--length > 0)
*end++ = c;
if (!c || c == '\n' || c == '\r')
break;
}
return buf;
}
#endif
char *
GetPasswordString(void *arg, char *prompt)
{
FILE *input = stdin;
char phrase[200] = {'\0'};
int isInputTerminal = isatty(fileno(stdin));
#ifndef _WINDOWS
if (isInputTerminal) {
input = fopen(consoleName, "r");
if (input == NULL) {
fprintf(stderr, "Error opening input terminal for read\n");
return NULL;
}
}
#endif
if (isInputTerminal) {
fprintf(stdout, "Please enter your password:\n");
fflush(stdout);
}
QUIET_FGETS (phrase, sizeof(phrase), input);
if (isInputTerminal) {
fprintf(stdout, "\n");
}
#ifndef _WINDOWS
if (isInputTerminal) {
fclose(input);
}
#endif
/* Strip off the newlines if present */
if (phrase[PORT_Strlen(phrase)-1] == '\n' ||
phrase[PORT_Strlen(phrase)-1] == '\r') {
phrase[PORT_Strlen(phrase)-1] = 0;
}
return (char*) PORT_Strdup(phrase);
}
char *
SECU_FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg)
{
char* phrases, *phrase;
PRFileDesc *fd;
PRInt32 nb;
char *pwFile = arg;
int i;
const long maxPwdFileSize = 4096;
char* tokenName = NULL;
int tokenLen = 0;
if (!pwFile)
return 0;
if (retry) {
return 0; /* no good retrying - the files contents will be the same */
}
phrases = PORT_ZAlloc(maxPwdFileSize);
if (!phrases) {
return 0; /* out of memory */
}
fd = PR_Open(pwFile, PR_RDONLY, 0);
if (!fd) {
fprintf(stderr, "No password file \"%s\" exists.\n", pwFile);
PORT_Free(phrases);
return NULL;
}
nb = PR_Read(fd, phrases, maxPwdFileSize);
PR_Close(fd);
if (nb == 0) {
fprintf(stderr,"password file contains no data\n");
PORT_Free(phrases);
return NULL;
}
if (slot) {
tokenName = PK11_GetTokenName(slot);
if (tokenName) {
tokenLen = PORT_Strlen(tokenName);
}
}
i = 0;
do
{
int startphrase = i;
int phraseLen;
/* handle the Windows EOL case */
while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) i++;
/* terminate passphrase */
phrases[i++] = '\0';
/* clean up any EOL before the start of the next passphrase */
while ( (i<nb) && (phrases[i] == '\r' || phrases[i] == '\n')) {
phrases[i++] = '\0';
}
/* now analyze the current passphrase */
phrase = &phrases[startphrase];
if (!tokenName)
break;
if (PORT_Strncmp(phrase, tokenName, tokenLen)) continue;
phraseLen = PORT_Strlen(phrase);
if (phraseLen < (tokenLen+1)) continue;
if (phrase[tokenLen] != ':') continue;
phrase = &phrase[tokenLen+1];
break;
} while (i<nb);
phrase = PORT_Strdup((char*)phrase);
PORT_Free(phrases);
return phrase;
}
char *
SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg)
{
char prompt[255];
secuPWData *pwdata = (secuPWData *)arg;
secuPWData pwnull = { PW_NONE, 0 };
secuPWData pwxtrn = { PW_EXTERNAL, "external" };
char *pw;
if (pwdata == NULL)
pwdata = &pwnull;
if (PK11_ProtectedAuthenticationPath(slot)) {
pwdata = &pwxtrn;
}
if (retry && pwdata->source != PW_NONE) {
PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n");
return NULL;
}
switch (pwdata->source) {
case PW_NONE:
sprintf(prompt, "Enter Password or Pin for \"%s\":",
PK11_GetTokenName(slot));
return GetPasswordString(NULL, prompt);
case PW_FROMFILE:
/* Instead of opening and closing the file every time, get the pw
* once, then keep it in memory (duh).
*/
pw = SECU_FilePasswd(slot, retry, pwdata->data);
pwdata->source = PW_PLAINTEXT;
pwdata->data = PL_strdup(pw);
/* it's already been dup'ed */
return pw;
case PW_EXTERNAL:
sprintf(prompt,
"Press Enter, then enter PIN for \"%s\" on external device.\n",
PK11_GetTokenName(slot));
(void) GetPasswordString(NULL, prompt);
/* Fall Through */
case PW_PLAINTEXT:
return PL_strdup(pwdata->data);
default:
break;
}
PR_fprintf(PR_STDERR, "Password check failed: No password found.\n");
return NULL;
}

View File

@ -0,0 +1,73 @@
/* ***** 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 code copied from secutil and secpwd.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* 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 ***** */
/* With the exception of GetPasswordString, this file was
copied from NSS's cmd/lib/secutil.h hg revision 8f011395145e */
#ifndef NSS_SECUTIL_H_
#define NSS_SECUTIL_H_
#include "nss.h"
#include "pk11pub.h"
#include "cryptohi.h"
#include "hasht.h"
#include "cert.h"
#include "key.h"
typedef struct {
enum {
PW_NONE = 0,
PW_FROMFILE = 1,
PW_PLAINTEXT = 2,
PW_EXTERNAL = 3
} source;
char *data;
} secuPWData;
#if( defined(_WINDOWS) && !defined(_WIN32_WCE))
#include <conio.h>
#include <io.h>
#define QUIET_FGETS quiet_fgets
static char * quiet_fgets (char *buf, int length, FILE *input);
#else
#define QUIET_FGETS fgets
#endif
char *
SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg);
#endif

View File

@ -53,15 +53,20 @@ endif
# This makefile just builds support for reading archives.
CSRCS = \
HOST_CSRCS = \
mar_create.c \
mar_extract.c \
mar_read.c \
$(NULL)
HOST_CSRCS = $(CSRCS)
CSRCS = \
$(HOST_CSRCS) \
$(NULL)
EXPORTS = \
mar.h \
mar_cmdline.h \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -46,6 +46,11 @@
extern "C" {
#endif
struct ProductInformationBlock {
const char *MARChannelID;
const char *productVersion;
};
/**
* The MAR item data structure.
*/
@ -57,6 +62,13 @@ typedef struct MarItem_ {
char name[1]; /* file path */
} MarItem;
#define TABLESIZE 256
struct MarFile_ {
FILE *fp;
MarItem *item_table[TABLESIZE];
};
typedef struct MarFile_ MarFile;
/**
@ -64,7 +76,7 @@ typedef struct MarFile_ MarFile;
* @param mar The MAR file being visited.
* @param item The MAR item being visited.
* @param data The data parameter passed by the caller of mar_enum_items.
* @returns A non-zero value to stop enumerating.
* @return A non-zero value to stop enumerating.
*/
typedef int (* MarItemCallback)(MarFile *mar, const MarItem *item, void *data);
@ -72,7 +84,7 @@ typedef int (* MarItemCallback)(MarFile *mar, const MarItem *item, void *data);
* Open a MAR file for reading.
* @param path Specifies the path to the MAR file to open. This path must
* be compatible with fopen.
* @returns NULL if an error occurs.
* @return NULL if an error occurs.
*/
MarFile *mar_open(const char *path);
@ -90,7 +102,7 @@ void mar_close(MarFile *mar);
* Find an item in the MAR file by name.
* @param mar The MarFile object to query.
* @param item The name of the item to query.
* @returns A const reference to a MAR item or NULL if not found.
* @return A const reference to a MAR item or NULL if not found.
*/
const MarItem *mar_find_item(MarFile *mar, const char *item);
@ -100,7 +112,7 @@ const MarItem *mar_find_item(MarFile *mar, const char *item);
* @param callback The function to call for each MAR item.
* @param data A caller specified value that is passed along to the
* callback function.
* @returns Zero if the enumeration ran to completion. Otherwise, any
* @return 0 if the enumeration ran to completion. Otherwise, any
* non-zero return value from the callback is returned.
*/
int mar_enum_items(MarFile *mar, MarItemCallback callback, void *data);
@ -112,7 +124,7 @@ int mar_enum_items(MarFile *mar, MarItemCallback callback, void *data);
* @param offset The byte offset relative to the start of the item.
* @param buf A pointer to a buffer to copy the data into.
* @param bufsize The length of the buffer to copy the data into.
* @returns The number of bytes written or a negative value if an
* @return The number of bytes written or a negative value if an
* error occurs.
*/
int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
@ -125,18 +137,53 @@ int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
* @param numfiles The number of files to store in the archive.
* @param files The list of null-terminated file paths. Each file
* path must be compatible with fopen.
* @returns A non-zero value if an error occurs.
* @param infoBlock The information to store in the product information block.
* @return A non-zero value if an error occurs.
*/
int mar_create(const char *dest, int numfiles, char **files);
int mar_create(const char *dest,
int numfiles,
char **files,
struct ProductInformationBlock *infoBlock);
/**
* Extract a MAR file to the current working directory.
* @param path The path to the MAR file to extract. This path must be
* compatible with fopen.
* @returns A non-zero value if an error occurs.
* @return A non-zero value if an error occurs.
*/
int mar_extract(const char *path);
/**
* Verifies the embedded signature for the specified mar file.
* We do not check that the certificate was issued by any trusted authority.
* We assume it to be self-signed. We do not check whether the certificate
* is valid for this usage.
*
* @param mar The already opened MAR file.
* @param certData The certificate file data.
* @param sizeOfCertData The size of the cert data.
* @return 0 on success
* a negative number if there was an error
* a positive number if the signature does not verify
*/
#ifdef XP_WIN
int mar_verify_signatureW(MarFile *mar,
const char *certData,
PRUint32 sizeOfCertData);
#endif
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
mar_read_product_info_block(MarFile *mar,
struct ProductInformationBlock *infoBlock);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,122 @@
/* ***** 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 command line utility function declarations.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* 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 ***** */
#ifndef MAR_CMDLINE_H__
#define MAR_CMDLINE_H__
/* We use NSPR here just to import the definition of PRUint32 */
#include "prtypes.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Determines MAR file information.
*
* @param path The path of the MAR file to check.
* @param hasSignatureBlock Optional out parameter specifying if the MAR
* file is has a signature block or not.
* @param numSignatures Optional out parameter for storing the number
* of signatures in the MAR file.
* @param hasAdditionalBlocks Optional out parameter specifying if the MAR
* file has additional blocks or not.
* @param offsetAdditionalBlocks Optional out parameter for the offset to the
* first additional block. Value is only valid if
* has_additional_blocks
* is not equal to 0.
* @param numAdditionalBlocks Optional out parameter for the number of
* additional blocks. Value is only valid if
* has_additional_blocks is not euqal to 0.
* @return 0 on success and non-zero on failure.
*/
int get_mar_file_info(const char *path,
int *hasSignatureBlock,
int *numSignatures,
int *hasAdditionalBlocks,
int *offsetAdditionalBlocks,
int *numAdditionalBlocks);
/**
* Verifies the embedded signature of the specified file path.
* This is only used by the signmar program when used with arguments to verify
* a MAR. This should not be used to verify a MAR that will be extracted in the
* same operation by updater code. This function prints the error message if
* verification fails.
*
* @param pathToMAR The path of the MAR file who's signature should be checked
* @param certData The certificate file data.
* @param sizeOfCertData The size of the cert data.
* @param certName Used only if compiled as NSS, specifies the certName
* @return 0 on success
* a negative number if there was an error
* a positive number if the signature does not verify
*/
int mar_verify_signature(const char *pathToMAR,
const char *certData,
PRUint32 sizeOfCertData,
const char *certName);
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
read_product_info_block(char *path,
struct ProductInformationBlock *infoBlock);
/**
* Writes out a copy of the MAR at src but with the signature block stripped.
*
* @param src The path of the source MAR file
* @param dest The path of the MAR file to write out that
has no signature block
* @return 0 on success
* -1 on error
*/
int
strip_signature_block(const char *src, const char * dest);
#ifdef __cplusplus
}
#endif
#endif /* MAR_CMDLINE_H__ */

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
* Brian R. Bondy <netzen@gmail.com>
*
* 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
@ -40,10 +41,10 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mar.h"
#include "mar_private.h"
#include "mar_cmdline.h"
#include "mar.h"
#ifdef XP_WIN
#include <winsock2.h>
@ -125,9 +126,206 @@ static int mar_concat_file(FILE *fp, const char *path) {
return rv;
}
int mar_create(const char *dest, int num_files, char **files) {
/**
* Writes out the product information block to the specified file.
*
* @param fp The opened MAR file being created.
* @param stack A pointer to the MAR item stack being used to create
* the MAR
* @param product_info The product info block to store in the file.
* @return 0 on success.
*/
static int
mar_concat_product_info_block(FILE *fp,
struct MarItemStack *stack,
struct ProductInformationBlock *infoBlock)
{
char buf[PIB_MAX_MAR_CHANNEL_ID_SIZE + PIB_MAX_PRODUCT_VERSION_SIZE];
PRUint32 additionalBlockID = 1, infoBlockSize, unused;
if (!fp || !infoBlock ||
!infoBlock->MARChannelID ||
!infoBlock->productVersion) {
return -1;
}
/* The MAR channel name must be < 64 bytes per the spec */
if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE) {
return -1;
}
/* The product version must be < 32 bytes per the spec */
if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE) {
return -1;
}
/* Although we don't need the product information block size to include the
maximum MAR channel name and product version, we allocate the maximum
amount to make it easier to modify the MAR file for repurposing MAR files
to different MAR channels. + 2 is for the NULL terminators. */
infoBlockSize = sizeof(infoBlockSize) +
sizeof(additionalBlockID) +
PIB_MAX_MAR_CHANNEL_ID_SIZE +
PIB_MAX_PRODUCT_VERSION_SIZE + 2;
if (stack) {
stack->last_offset += infoBlockSize;
}
/* Write out the product info block size */
infoBlockSize = htonl(infoBlockSize);
if (fwrite(&infoBlockSize,
sizeof(infoBlockSize), 1, fp) != 1) {
return -1;
}
infoBlockSize = ntohl(infoBlockSize);
/* Write out the product info block ID */
additionalBlockID = htonl(additionalBlockID);
if (fwrite(&additionalBlockID,
sizeof(additionalBlockID), 1, fp) != 1) {
return -1;
}
additionalBlockID = ntohl(additionalBlockID);
/* Write out the channel name and NULL terminator */
if (fwrite(infoBlock->MARChannelID,
strlen(infoBlock->MARChannelID) + 1, 1, fp) != 1) {
return -1;
}
/* Write out the product version string and NULL terminator */
if (fwrite(infoBlock->productVersion,
strlen(infoBlock->productVersion) + 1, 1, fp) != 1) {
return -1;
}
/* Write out the rest of the block that is unused */
unused = infoBlockSize - (sizeof(infoBlockSize) +
sizeof(additionalBlockID) +
strlen(infoBlock->MARChannelID) +
strlen(infoBlock->productVersion) + 2);
memset(buf, 0, sizeof(buf));
if (fwrite(buf, unused, 1, fp) != 1) {
return -1;
}
return 0;
}
/**
* Refreshes the product information block with the new information.
* The input MAR must not be signed or the function call will fail.
*
* @param path The path to the MAR file who's product info block
* should be refreshed.
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
refresh_product_info_block(const char *path,
struct ProductInformationBlock *infoBlock)
{
FILE *fp ;
int rv;
PRUint32 hasSignatureBlock, numSignatures, additionalBlocks,
additionalBlockSize, additionalBlockID, offsetAdditionalBlocks,
numAdditionalBlocks, i;
PRInt64 oldPos;
rv = get_mar_file_info(path,
&hasSignatureBlock,
&numSignatures,
&additionalBlocks,
&offsetAdditionalBlocks,
&numAdditionalBlocks);
if (rv) {
fprintf(stderr, "ERROR: Could not obtain MAR information.\n");
return -1;
}
if (hasSignatureBlock && numSignatures) {
fprintf(stderr, "ERROR: Cannot refresh a signed MAR\n");
return -1;
}
fp = fopen(path, "r+b");
if (!fp) {
fprintf(stderr, "ERROR: could not open target file: %s\n", path);
return -1;
}
if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET)) {
fprintf(stderr, "ERROR: could not seek to additional blocks\n");
fclose(fp);
return -1;
}
for (i = 0; i < numAdditionalBlocks; ++i) {
/* Get the position of the start of this block */
oldPos = ftello(fp);
/* Read the additional block size */
if (fread(&additionalBlockSize,
sizeof(additionalBlockSize),
1, fp) != 1) {
return -1;
}
additionalBlockSize = ntohl(additionalBlockSize);
/* Read the additional block ID */
if (fread(&additionalBlockID,
sizeof(additionalBlockID),
1, fp) != 1) {
return -1;
}
additionalBlockID = ntohl(additionalBlockID);
if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
if (fseeko(fp, oldPos, SEEK_SET)) {
fprintf(stderr, "Could not seek back to Product Information Block\n");
fclose(fp);
return -1;
}
if (mar_concat_product_info_block(fp, NULL, infoBlock)) {
fprintf(stderr, "Could not concat Product Information Block\n");
fclose(fp);
return -1;
}
fclose(fp);
return 0;
} else {
/* This is not the additional block you're looking for. Move along. */
if (fseek(fp, additionalBlockSize, SEEK_CUR)) {
fprintf(stderr, "ERROR: Could not seek past current block.\n");
fclose(fp);
return -1;
}
}
}
/* If we had a product info block we would have already returned */
fclose(fp);
fprintf(stderr, "ERROR: Could not refresh because block does not exist\n");
return -1;
}
/**
* Create a MAR file from a set of files.
* @param dest The path to the file to create. This path must be
* compatible with fopen.
* @param numfiles The number of files to store in the archive.
* @param files The list of null-terminated file paths. Each file
* path must be compatible with fopen.
* @param infoBlock The information to store in the product information block.
* @return A non-zero value if an error occurs.
*/
int mar_create(const char *dest, int
num_files, char **files,
struct ProductInformationBlock *infoBlock) {
struct MarItemStack stack;
PRUint32 offset_to_index = 0, size_of_index;
PRUint32 offset_to_index = 0, size_of_index,
numSignatures, numAdditionalSections;
PRUint64 sizeOfEntireMAR = 0;
struct stat st;
FILE *fp;
int i, rv = -1;
@ -145,7 +343,35 @@ int mar_create(const char *dest, int num_files, char **files) {
if (fwrite(&offset_to_index, sizeof(PRUint32), 1, fp) != 1)
goto failure;
stack.last_offset = MAR_ID_SIZE + sizeof(PRUint32);
stack.last_offset = MAR_ID_SIZE +
sizeof(offset_to_index) +
sizeof(numSignatures) +
sizeof(numAdditionalSections) +
sizeof(sizeOfEntireMAR);
/* We will circle back on this at the end of the MAR creation to fill it */
if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) {
goto failure;
}
/* Write out the number of signatures, for now only at most 1 is supported */
numSignatures = 0;
if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) {
goto failure;
}
/* Write out the number of additional sections, for now just 1
for the product info block */
numAdditionalSections = htonl(1);
if (fwrite(&numAdditionalSections,
sizeof(numAdditionalSections), 1, fp) != 1) {
goto failure;
}
numAdditionalSections = ntohl(numAdditionalSections);
if (mar_concat_product_info_block(fp, &stack, infoBlock)) {
goto failure;
}
for (i = 0; i < num_files; ++i) {
if (stat(files[i], &st)) {
@ -168,12 +394,27 @@ int mar_create(const char *dest, int num_files, char **files) {
if (fwrite(stack.head, stack.size_used, 1, fp) != 1)
goto failure;
/* To protect against invalid MAR files, we assumes that the MAR file
size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
if (ftell(fp) > MAX_SIZE_OF_MAR_FILE) {
goto failure;
}
/* write out offset to index file in network byte order */
offset_to_index = htonl(stack.last_offset);
if (fseek(fp, MAR_ID_SIZE, SEEK_SET))
goto failure;
if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1)
goto failure;
offset_to_index = ntohl(stack.last_offset);
sizeOfEntireMAR = ((PRUint64)stack.last_offset) +
stack.size_used +
sizeof(size_of_index);
sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1)
goto failure;
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
rv = 0;
failure:

View File

@ -41,9 +41,8 @@
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "mar.h"
#include "mar_private.h"
#include "mar.h"
#ifdef XP_WIN
#include <io.h>

View File

@ -39,12 +39,79 @@
#ifndef MAR_PRIVATE_H__
#define MAR_PRIVATE_H__
#include "prtypes.h"
#include "limits.h"
/* Code in this module requires a guarantee that the size
of PRUint32 and PRUint64 are 4 and 8 bytes respectively. */
PR_STATIC_ASSERT(sizeof(PRUint32) == 4);
PR_STATIC_ASSERT(sizeof(PRUint64) == 8);
#define BLOCKSIZE 4096
#define ROUND_UP(n, incr) (((n) / (incr) + 1) * (incr))
#define MAR_ID "MAR1"
#define MAR_ID_SIZE 4
/* The signature block comes directly after the header block
which is 16 bytes */
#define SIGNATURE_BLOCK_OFFSET 16
/* We have a MAX_SIGNATURES limit so that an invalid MAR will never
waste too much of either updater's or signmar's time. */
#define MAX_SIGNATURES 8
/* Make sure the file is less than 500MB. We do this to protect against
invalid MAR files. */
#define MAX_SIZE_OF_MAR_FILE ((PRInt64)524288000)
/* Existing code makes assumptions that the file size is
smaller than LONG_MAX. */
PR_STATIC_ASSERT(MAX_SIZE_OF_MAR_FILE < ((PRInt64)LONG_MAX));
/* We store at most the size up to the signature block + 4
bytes per BLOCKSIZE bytes */
PR_STATIC_ASSERT(sizeof(BLOCKSIZE) < \
(SIGNATURE_BLOCK_OFFSET + sizeof(PRUint32)));
/* The maximum size of any signature supported by current and future
implementations of the signmar program. */
#define MAX_SIGNATURE_LENGTH 2048
/* Each additional block has a unique ID.
The product information block has an ID of 1. */
#define PRODUCT_INFO_BLOCK_ID 1
#define MAR_ITEM_SIZE(namelen) (3*sizeof(PRUint32) + (namelen) + 1)
/* Product Information Block (PIB) constants */
#define PIB_MAX_MAR_CHANNEL_ID_SIZE 63
#define PIB_MAX_PRODUCT_VERSION_SIZE 31
/* The mar program is compiled as a host bin so we don't have access to NSPR at
runtime. For that reason we use ntohl, htonl, and define HOST_TO_NETWORK64
instead of the NSPR equivalents. */
#ifdef XP_WIN
#include <winsock2.h>
#define ftello _ftelli64
#define fseeko _fseeki64
#else
#define _FILE_OFFSET_BITS 64
#include <netinet/in.h>
#include <unistd.h>
#endif
#include <stdio.h>
#define HOST_TO_NETWORK64(x) ( \
((((PRUint64) x) & 0xFF) << 56) | \
((((PRUint64) x) >> 8) & 0xFF) << 48) | \
(((((PRUint64) x) >> 16) & 0xFF) << 40) | \
(((((PRUint64) x) >> 24) & 0xFF) << 32) | \
(((((PRUint64) x) >> 32) & 0xFF) << 24) | \
(((((PRUint64) x) >> 40) & 0xFF) << 16) | \
(((((PRUint64) x) >> 48) & 0xFF) << 8) | \
(((PRUint64) x) >> 56)
#define NETWORK_TO_HOST64 HOST_TO_NETWORK64
#endif /* MAR_PRIVATE_H__ */

View File

@ -39,10 +39,9 @@
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mar.h"
#include "mar_private.h"
#include "mar.h"
#ifdef XP_WIN
#include <winsock2.h>
@ -50,13 +49,6 @@
#include <netinet/in.h>
#endif
#define TABLESIZE 256
struct MarFile_ {
FILE *fp;
MarItem *item_table[TABLESIZE];
};
/* this is the same hash algorithm used by nsZipArchive.cpp */
static PRUint32 mar_hash_name(const char *name) {
PRUint32 val = 0;
@ -243,6 +235,125 @@ void mar_close(MarFile *mar) {
free(mar);
}
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
read_product_info_block(char *path,
struct ProductInformationBlock *infoBlock)
{
int rv;
MarFile mar;
mar.fp = fopen(path, "rb");
if (!mar.fp) {
return -1;
}
rv = mar_read_product_info_block(&mar, infoBlock);
fclose(mar.fp);
return rv;
}
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
mar_read_product_info_block(MarFile *mar,
struct ProductInformationBlock *infoBlock)
{
int i, hasAdditionalBlocks, offset,
offsetAdditionalBlocks, numAdditionalBlocks,
additionalBlockSize, additionalBlockID;
/* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and
product version < 32 bytes + 3 NULL terminator bytes. */
char buf[97] = { '\0' };
int ret = get_mar_file_info_fp(mar->fp, NULL, NULL,
&hasAdditionalBlocks,
&offsetAdditionalBlocks,
&numAdditionalBlocks);
for (i = 0; i < numAdditionalBlocks; ++i) {
/* Read the additional block size */
if (fread(&additionalBlockSize,
sizeof(additionalBlockSize),
1, mar->fp) != 1) {
return -1;
}
additionalBlockSize = ntohl(additionalBlockSize) -
sizeof(additionalBlockSize) -
sizeof(additionalBlockID);
/* Read the additional block ID */
if (fread(&additionalBlockID,
sizeof(additionalBlockID),
1, mar->fp) != 1) {
return -1;
}
additionalBlockID = ntohl(additionalBlockID);
if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
const char *location;
int len;
/* This block must be at most 104 bytes.
MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL
terminator bytes. We only check for 96 though because we remove 8
bytes above from the additionalBlockSize: We subtract
sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
if (additionalBlockSize > 96) {
return -1;
}
if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
return -1;
}
/* Extract the MAR channel name from the buffer. For now we
point to the stack allocated buffer but we strdup this
if we are within bounds of each field's max length. */
location = buf;
len = strlen(location);
infoBlock->MARChannelID = location;
location += len + 1;
if (len >= 64) {
infoBlock->MARChannelID = NULL;
return -1;
}
/* Extract the version from the buffer */
len = strlen(location);
infoBlock->productVersion = location;
location += len + 1;
if (len >= 32) {
infoBlock->MARChannelID = NULL;
infoBlock->productVersion = NULL;
return -1;
}
infoBlock->MARChannelID =
strdup(infoBlock->MARChannelID);
infoBlock->productVersion =
strdup(infoBlock->productVersion);
return 0;
} else {
/* This is not the additional block you're looking for. Move along. */
if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) {
return -1;
}
}
}
/* If we had a product info block we would have already returned */
return -1;
}
const MarItem *mar_find_item(MarFile *mar, const char *name) {
PRUint32 hash;
const MarItem *item;
@ -291,3 +402,193 @@ int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
return fread(buf, 1, nr, mar->fp);
}
/**
* Determines the MAR file information.
*
* @param path The path of the MAR file to check.
* @param hasSignatureBlock Optional out parameter specifying if the MAR
* file is has a signature block or not.
* @param numSignatures Optional out parameter for storing the number
* of signatures in the MAR file.
* @param hasAdditionalBlocks Optional out parameter specifying if the MAR
* file has additional blocks or not.
* @param offsetAdditionalBlocks Optional out parameter for the offset to the
* first additional block. Value is only valid if
* has_additional_blocks
* is not equal to 0.
* @param numAdditionalBlocks Optional out parameter for the number of
* additional blocks. Value is only valid if
* has_additional_blocks is not euqal to 0.
* @return 0 on success and non-zero on failure.
*/
int get_mar_file_info(const char *path,
int *hasSignatureBlock,
int *numSignatures,
int *hasAdditionalBlocks,
int *offsetAdditionalBlocks,
int *numAdditionalBlocks)
{
int rv;
FILE *fp = fopen(path, "rb");
if (!fp) {
return -1;
}
rv = get_mar_file_info_fp(fp, hasSignatureBlock,
numSignatures, hasAdditionalBlocks,
offsetAdditionalBlocks, numAdditionalBlocks);
fclose(fp);
return rv;
}
/**
* Determines the MAR file information.
*
* @param fp An opened MAR file in read mode.
* @param hasSignatureBlock Optional out parameter specifying if the MAR
* file is has a signature block or not.
* @param numSignatures Optional out parameter for storing the number
* of signatures in the MAR file.
* @param hasAdditionalBlocks Optional out parameter specifying if the MAR
* file has additional blocks or not.
* @param offsetAdditionalBlocks Optional out parameter for the offset to the
* first additional block. Value is only valid if
* has_additional_blocks
* is not equal to 0.
* @param numAdditionalBlocks Optional out parameter for the number of
* additional blocks. Value is only valid if
* has_additional_blocks is not euqal to 0.
* @return 0 on success and non-zero on failure.
*/
int get_mar_file_info_fp(FILE *fp,
int *hasSignatureBlock,
int *numSignatures,
int *hasAdditionalBlocks,
int *offsetAdditionalBlocks,
int *numAdditionalBlocks)
{
PRUint32 offsetToIndex, offsetToContent, signatureCount, signatureLen;
int i;
/* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
if (!hasSignatureBlock && !hasAdditionalBlocks) {
return -1;
}
/* Skip to the start of the offset index */
if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
return -1;
}
/* Read the offset to the index. */
if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) {
return -1;
}
offsetToIndex = ntohl(offsetToIndex);
if (numSignatures) {
/* Skip past the MAR file size field */
if (fseek(fp, sizeof(PRUint64), SEEK_CUR)) {
return -1;
}
/* Read the offset to the index. */
if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
return -1;
}
*numSignatures = ntohl(*numSignatures);
}
/* Skip to the first index entry past the index size field
We do it in 2 calls because offsetToIndex + sizeof(PRUint32)
could oerflow in theory. */
if (fseek(fp, offsetToIndex, SEEK_SET)) {
return -1;
}
if (fseek(fp, sizeof(PRUint32), SEEK_CUR)) {
return -1;
}
/* Read the first offset to content field. */
if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 1) {
return -1;
}
offsetToContent = ntohl(offsetToContent);
/* Check if we have a new or old MAR file */
if (hasSignatureBlock) {
if (offsetToContent == MAR_ID_SIZE + sizeof(PRUint32)) {
*hasSignatureBlock = 0;
} else {
*hasSignatureBlock = 1;
}
}
/* If the caller doesn't care about the product info block
value, then just return */
if (!hasAdditionalBlocks) {
return 0;
}
/* Skip to the start of the signature block */
if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
return -1;
}
/* Get the number of signatures */
if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
return -1;
}
signatureCount = ntohl(signatureCount);
/* Check that we have less than the max amount of signatures so we don't
waste too much of either updater's or signmar's time. */
if (signatureCount > MAX_SIGNATURES) {
return -1;
}
/* Skip past the whole signature block */
for (i = 0; i < signatureCount; i++) {
/* Skip past the signature algorithm ID */
if (fseek(fp, sizeof(PRUint32), SEEK_CUR)) {
return -1;
}
/* Read the signature length and skip past the signature */
if (fread(&signatureLen, sizeof(PRUint32), 1, fp) != 1) {
return -1;
}
signatureLen = ntohl(signatureLen);
if (fseek(fp, signatureLen, SEEK_CUR)) {
return -1;
}
}
if (ftell(fp) == offsetToContent) {
*hasAdditionalBlocks = 0;
} else {
if (numAdditionalBlocks) {
/* We have an additional block, so read in the number of additional blocks
and set the offset. */
*hasAdditionalBlocks = 1;
if (fread(numAdditionalBlocks, sizeof(PRUint32), 1, fp) != 1) {
return -1;
}
*numAdditionalBlocks = ntohl(*numAdditionalBlocks);
if (offsetAdditionalBlocks) {
*offsetAdditionalBlocks = ftell(fp);
}
} else if (offsetAdditionalBlocks) {
/* numAdditionalBlocks is not specified but offsetAdditionalBlocks
is, so fill it! */
*offsetAdditionalBlocks = ftell(fp) + sizeof(PRUint32);
}
}
return 0;
}

View File

@ -0,0 +1,69 @@
# ***** 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 libmar test code.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2012
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Brian R. Bondy <netzen@gmail.com>
#
# 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = modules/libmar/tests
include $(DEPTH)/config/autoconf.mk
XPCSHELL_TESTS = \
unit \
$(NULL)
TESTROOT = $(call core_abspath,$(DEPTH))/_tests/xpcshell/$(relativesrcdir)
DEFINES += -DBIN_SUFFIX=$(BIN_SUFFIX)
include $(topsrcdir)/config/rules.mk
libs:: unit/head_libmar.js.in
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) $^ > $(TESTROOT)/unit/head_libmar.js
ifneq ($(OS_TARGET),Android)
ifndef MOZ_PROFILE_GENERATE
libs::
$(INSTALL) ../tool/signmar$(BIN_SUFFIX) $(TESTROOT)/unit
$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)nss3$(DLL_SUFFIX) $(TESTROOT)/unit
$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)nssutil3$(DLL_SUFFIX) $(TESTROOT)/unit
$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)plc4$(DLL_SUFFIX) $(TESTROOT)/unit
$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)nspr4$(DLL_SUFFIX) $(TESTROOT)/unit
$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)plds4$(DLL_SUFFIX) $(TESTROOT)/unit
endif
endif # Not Android

Binary file not shown.

View File

@ -0,0 +1 @@
1

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,140 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const BIN_SUFFIX = "@BIN_SUFFIX@";
const Cc = Components.classes;
const Ci = Components.interfaces;
/**
* Compares binary data of 2 arrays and throws if they aren't the same.
* Throws on mismatch, does nothing on match.
*
* @param arr1 The first array to compare
* @param arr2 The second array to compare
*/
function compareBinaryData(arr1, arr2) {
do_check_eq(arr1.length, arr2.length);
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
throw "Data differs at index " + i;
}
}
}
/**
* Reads a file's data and returns it
*
* @param file The file to read the data from
* @return a byte array for the data in the file.
*/
function getBinaryFileData(file) {
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
// Open as RD_ONLY with default permissions.
fileStream.init(file, 0x01, -1, null);
// Check the returned size versus the expected size.
let stream = Cc["@mozilla.org/binaryinputstream;1"].
createInstance(Ci.nsIBinaryInputStream);
stream.setInputStream(fileStream);
let bytes = stream.readByteArray(stream.available());
fileStream.close();
return bytes;
}
/**
* Runs each method in the passed in object
* Every method of the passed in object that starts with test_ will be ran
* The cleanup_per_test method of the object will be run right away, it will be
* registered to be the cleanup function, and it will be run between each test.
*
* @return The number of tests ran
*/
function run_tests(obj) {
let cleanup_per_test = obj.cleanup_per_test;
if (cleanup_per_test === undefined) {
cleanup_per_test = function() {};
}
do_register_cleanup(cleanup_per_test);
// Make sure there's nothing left over from a preious failed test
cleanup_per_test();
let ranCount = 0;
// hasOwnProperty ensures we only see direct properties and not all
for (let f in obj) {
if (typeof obj[f] === "function" &&
obj.hasOwnProperty(f) &&
f.toString().indexOf("test_") === 0) {
obj[f]();
cleanup_per_test();
ranCount++;
}
}
return ranCount;
}
/**
* Creates a MAR file with the content of files.
*
* @param outMAR The file where the MAR should be created to
* @param dataDir The directory where the relative file paths exist
* @param files The relative file paths of the files to include in the MAR
*/
function createMAR(outMAR, dataDir, files) {
// You cannot create an empy MAR.
do_check_true(files.length > 0);
// Get an nsIProcess to the signmar binary.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to create the MAR.
let args = ["-C", dataDir.path, "-c", outMAR.path];
args = args.concat(files);
do_print('Running: ' + signmarBin.path);
process.init(signmarBin);
process.run(true, args, args.length);
// Verify signmar returned 0 for success.
do_check_eq(process.exitValue, 0);
// Verify the out MAR file actually exists.
do_check_true(outMAR.exists());
}
/**
* Extracts a MAR file to the specified output directory.
*
* @param mar The MAR file that should be matched
* @param dataDir The directory to extract to
*/
function extractMAR(mar, dataDir) {
// Get an nsIProcess to the signmar binary.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to create the MAR.
let args = ["-C", dataDir.path, "-x", mar.path];
do_print('Running: ' + signmarBin.path);
process.init(signmarBin);
process.run(true, args, args.length);
// Verify signmar returned 0 for success.
do_check_eq(process.exitValue, 0);
}

View File

@ -0,0 +1,74 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
/**
* Creates MAR from the passed files, compares it to the reference MAR.
*
* @param refMARFileName The name of the MAR file that should match
* @param files The files that should go in the created MAR
* @param checkNoMAR If true return an error if a file already exists
*/
function run_one_test(refMARFileName, files, checkNoMAR) {
if (checkNoMAR === undefined) {
checkNoMAR = true;
}
// Ensure the MAR we will create doesn't already exist.
let outMAR = do_get_file("out.mar", true);
if (checkNoMAR) {
do_check_false(outMAR.exists());
}
// Create the actual MAR file.
createMAR(outMAR, do_get_file("data"), files);
// Get the reference MAR data.
let refMAR = do_get_file("data/" + refMARFileName);
let refMARData = getBinaryFileData(refMAR);
// Verify the data of the MAR is what it should be.
let outMARData = getBinaryFileData(outMAR);
compareBinaryData(outMARData, outMARData);
}
// Define the unit tests to run.
let tests = {
// Test creating a MAR file with a 0 byte file.
test_zero_sized: function() {
return run_one_test("0_sized_mar.mar", ["0_sized_file"]);
},
// Test creating a MAR file with a 1 byte file.
test_one_byte: function() {
return run_one_test("1_byte_mar.mar", ["1_byte_file"]);
},
// Test creating a MAR file with binary data.
test_binary_data: function() {
return run_one_test("binary_data_mar.mar", ["binary_data_file"]);
},
// Test creating a MAR file with multiple files inside of it.
test_multiple_file: function() {
return run_one_test("multiple_file_mar.mar",
["0_sized_file", "1_byte_file", "binary_data_file"]);
},
// Test creating a MAR file on top of a different one that already exists
// at the location the new one will be created at.
test_overwrite_already_exists: function() {
let differentFile = do_get_file("data/1_byte_mar.mar");
let outMARDir = do_get_file(".");
differentFile.copyTo(outMARDir, "out.mar");
return run_one_test("binary_data_mar.mar", ["binary_data_file"], false);
},
// Between each test make sure the out MAR does not exist.
cleanup_per_test: function() {
let outMAR = do_get_file("out.mar", true);
if (outMAR.exists()) {
outMAR.remove(false);
}
}
};
// Run all the tests, there should be 5.
do_check_eq(run_tests(tests), 5);
}

View File

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
/**
* Extracts a MAR and makes sure each file matches the reference files.
*
* @param marFileName The name of the MAR file to extract
* @param files The files that the extracted MAR should contain
*/
function run_one_test(marFileName, files) {
// Get the MAR file that we will be extracting
let mar = do_get_file("data/" + marFileName);
// Get the path that we will extract to
let outDir = do_get_file("out", true);
do_check_false(outDir.exists());
outDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
// Get the ref files and the files that will be extracted.
let outFiles = [];
let refFiles = [];
for (let i = 0; i < files.length; i++) {
let outFile = do_get_file("out/" + files[i], true);
do_check_false(outFile.exists());
outFiles.push(outFile);
refFiles.push(do_get_file("data/" + files[i]));
}
// Extract the MAR contents into the ./out dir.
extractMAR(mar, outDir);
// Compare to make sure the extracted files are the same.
for (let i = 0; i < files.length; i++) {
do_check_true(outFiles[i].exists());
let refFileData = getBinaryFileData(refFiles[i]);
let outFileData = getBinaryFileData(outFiles[i]);
compareBinaryData(refFileData, outFileData);
}
}
// Define the unit tests to run.
let tests = {
// Test extracting a MAR file with a 0 byte file.
test_zero_sized: function() {
return run_one_test("0_sized_mar.mar", ["0_sized_file"]);
},
// Test extracting a MAR file with a 1 byte file.
test_one_byte: function() {
return run_one_test("1_byte_mar.mar", ["1_byte_file"]);
},
// Test extracting a MAR file with binary data.
test_binary_data: function() {
return run_one_test("binary_data_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR without a product information block (PIB) which
// contains binary data.
test_no_pib: function() {
return run_one_test("no_pib_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR without a product information block (PIB) that is
// signed and which contains binary data.
test_no_pib_signed: function() {
return run_one_test("signed_no_pib_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR with a product information block (PIB) that is
// signed and which contains binary data.
test_pib_signed: function() {
return run_one_test("signed_pib_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR file with multiple files inside of it.
test_multiple_file: function() {
return run_one_test("multiple_file_mar.mar",
["0_sized_file", "1_byte_file", "binary_data_file"]);
},
// Between each test make sure the out directory and its subfiles do
// not exist.
cleanup_per_test: function() {
let outDir = do_get_file("out", true);
if (outDir.exists()) {
outDir.remove(true);
}
}
};
// Run all the tests, there should be 7.
do_check_eq(run_tests(tests), 7);
}

View File

@ -0,0 +1,187 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
/**
* Signs a MAR file.
*
* @param inMAR The MAR file that should be signed
* @param outMAR The MAR file to create
*/
function signMAR(inMAR, outMAR) {
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to create the MAR.
let NSSConfigDir = do_get_file("data");
let args = ["-d", NSSConfigDir.path, "-n", "mycert", "-s",
inMAR.path, outMAR.path];
do_print('Running sign operation: ' + signmarBin.path);
process.init(signmarBin);
process.run(true, args, args.length);
// Verify signmar returned 0 for success.
do_check_eq(process.exitValue, 0);
}
/**
* Verifies a MAR file.
*
* @param signedMAR Verifies a MAR file
*/
function verifyMAR(signedMAR, wantSuccess) {
if (wantSuccess === undefined) {
wantSuccess = true;
}
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
let DERFile = do_get_file("data/mycert.der");
// Will reference the arguments to use for verification in signmar
let args;
// The XPCShell test wiki indicates this is the preferred way for
// Windows detection.
var isWindows = ("@mozilla.org/windows-registry-key;1"
in Cc);
// Setup the command line arguments to create the MAR.
// Windows vs. Linux/Mac/... have different command line for verification
// since on Windows we verify with CryptoAPI and on all other platforms
// we verify with NSS. So on Windows we use an exported DER file and on
// other platforms we use the NSS config db.
if (isWindows) {
args = ["-D", DERFile.path, "-v", signedMAR.path];
} else {
let NSSConfigDir = do_get_file("data");
args = ["-d", NSSConfigDir.path, "-n", "mycert", "-v", signedMAR.path];
}
do_print('Running verify operation: ' + signmarBin.path);
process.init(signmarBin);
try {
// We put this in a try block because nsIProcess doesn't like -1 returns
process.run(true, args, args.length);
} catch (e) {
process.exitValue = -1;
}
// Verify signmar returned 0 for success.
if (wantSuccess) {
do_check_eq(process.exitValue, 0);
} else {
do_check_neq(process.exitValue, 0);
}
}
/**
* Strips a MAR signature.
*
* @param signedMAR The MAR file that should be signed
* @param outMAR The MAR file to write to with signature stripped
*/
function stripMARSignature(signedMAR, outMAR) {
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to create the MAR.
let args = ["-r", signedMAR.path, outMAR.path];
do_print('Running sign operation: ' + signmarBin.path);
process.init(signmarBin);
process.run(true, args, args.length);
// Verify signmar returned 0 for success.
do_check_eq(process.exitValue, 0);
}
function cleanup() {
let outMAR = do_get_file("signed_out.mar", true);
if (outMAR.exists()) {
outMAR.remove(false);
}
outMAR = do_get_file("out.mar", true);
if (outMAR.exists()) {
outMAR.remove(false);
}
let outDir = do_get_file("out", true);
if (outDir.exists()) {
outDir.remove(true);
}
}
// Define the unit tests to run.
let tests = {
// Test signing a MAR file
test_sign: function() {
let inMAR = do_get_file("data/binary_data_mar.mar");
let outMAR = do_get_file("signed_out.mar", true);
do_check_false(outMAR.exists());
signMAR(inMAR, outMAR);
do_check_true(outMAR.exists());
let outMARData = getBinaryFileData(outMAR);
let refMAR = do_get_file("data/signed_pib_mar.mar");
let refMARData = getBinaryFileData(refMAR);
compareBinaryData(outMARData, refMARData);
},
// Test verifying a signed MAR file
test_verify: function() {
let signedMAR = do_get_file("data/signed_pib_mar.mar");
verifyMAR(signedMAR);
},
// Test verifying a signed MAR file without a PIB
test_verify_no_pib: function() {
let signedMAR = do_get_file("data/signed_no_pib_mar.mar");
verifyMAR(signedMAR);
},
// Test verifying a crafted MAR file where the attacker tried to adjust
// the version number manually.
test_crafted_mar: function() {
let signedBadMAR = do_get_file("data/manipulated_signed_mar.mar");
verifyMAR(signedBadMAR, false);
},
// Test to make sure a stripped MAR is the same as the original MAR
test_strip_signature: function() {
let originalMAR = do_get_file("data/binary_data_mar.mar");
let signedMAR = do_get_file("signed_out.mar");
let outMAR = do_get_file("out.mar", true);
stripMARSignature(signedMAR, outMAR);
// Verify that the stripped MAR matches the original data MAR exactly
let outMARData = getBinaryFileData(outMAR);
let originalMARData = getBinaryFileData(originalMAR);
compareBinaryData(outMARData, originalMARData);
},
};
cleanup();
// Run all the tests, there should be 5.
do_check_eq(run_tests(tests), 5);
do_register_cleanup(cleanup);
}

View File

@ -0,0 +1,7 @@
[DEFAULT]
head = head_libmar.js
tail =
[test_create.js]
[test_extract.js]
[test_sign_verify.js]

View File

@ -51,23 +51,49 @@ endif
# The mar executable is output into dist/host/bin since it is something that
# would only be used by our build system and should not itself be included in a
# Mozilla distribution.
HOST_PROGRAM = mar$(HOST_BIN_SUFFIX)
HOST_PROGRAM = mar$(HOST_BIN_SUFFIX)
PROGRAM = signmar$(BIN_SUFFIX)
# Don't link the against libmozglue because we don't need it.
MOZ_GLUE_LDFLAGS =
MOZ_GLUE_PROGRAM_LDFLAGS =
HOST_CSRCS = \
mar.c \
$(NULL)
DEFINES += \
-DMAR_CHANNEL_ID='"$(MAR_CHANNEL_ID)"' \
-DMOZ_APP_VERSION='"$(MOZ_APP_VERSION)"' \
$(NULL)
HOST_LIBS = $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX)
HOST_CFLAGS += \
-DNO_SIGN_VERIFY \
$(DEFINES) \
$(NULL)
HOST_CSRCS = \
mar.c \
$(NULL)
CSRCS = $(HOST_CSRCS)
HOST_LIBS = $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX)
LIBS = $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX) \
$(DEPTH)/modules/libmar/sign/$(LIB_PREFIX)signmar.$(LIB_SUFFIX) \
$(DEPTH)/modules/libmar/verify/$(LIB_PREFIX)verifymar.$(LIB_SUFFIX) \
$(DIST)/lib/$(LIB_PREFIX)nss3.$(LIB_SUFFIX) \
$(DIST)/lib/$(LIB_PREFIX)nssutil3.$(LIB_SUFFIX) \
$(NSPR_LIBS) \
$(NULL)
ifeq ($(HOST_OS_ARCH),WINNT)
HOST_EXTRA_LIBS += $(call EXPAND_LIBNAME,ws2_32)
EXTRA_LIBS += $(call EXPAND_LIBNAME,ws2_32)
EXTRA_LIBS += $(call EXPAND_LIBNAME,crypt32)
EXTRA_LIBS += $(call EXPAND_LIBNAME,advapi32)
endif
include $(topsrcdir)/config/rules.mk
ifdef CROSS_COMPILE
ifdef HOST_NSPR_MDCPUCFG
HOST_CFLAGS += -DMDCPUCFG=$(HOST_NSPR_MDCPUCFG)
HOST_CFLAGS += -DMDCPUCFG=$(HOST_NSPR_MDCPUCFG)
CFLAGS += -DMDCPUCFG=$(HOST_NSPR_MDCPUCFG)
endif
endif

View File

@ -37,20 +37,50 @@
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include <stdlib.h>
#include "mar.h"
#include "mar_cmdline.h"
#ifdef XP_WIN
#include <windows.h>
#include <direct.h>
#define chdir _chdir
#else
#include <unistd.h>
#endif
#if !defined(NO_SIGN_VERIFY) && (!defined(XP_WIN) || defined(MAR_NSS))
int NSSInitCryptoContext(const char *NSSConfigDir);
#endif
int mar_repackage_and_sign(const char *NSSConfigDir,
const char *certName,
const char *src,
const char * dest);
static void print_usage() {
printf("usage: mar [-C dir] {-c|-x|-t} archive.mar [files...]\n");
printf("usage:\n");
printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
"{-c|-x|-t|-T} archive.mar [files...]\n");
#ifndef NO_SIGN_VERIFY
printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s "
"archive.mar out_signed_archive.mar\n");
printf(" mar [-C workingDir] -r "
"signed_input_archive.mar output_archive.mar\n");
#if defined(XP_WIN) && !defined(MAR_NSS)
printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
#else
printf(" mar [-C workingDir] -d NSSConfigDir -n certname "
"-v signed_archive.mar\n");
#endif
#endif
printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
"-i unsigned_archive_to_refresh.mar\n");
}
static int mar_test_callback(MarFile *mar, const MarItem *item, void *unused) {
static int mar_test_callback(MarFile *mar,
const MarItem *item,
void *unused) {
printf("%u\t0%o\t%s\n", item->length, item->flags, item->name);
return 0;
}
@ -70,26 +100,202 @@ static int mar_test(const char *path) {
}
int main(int argc, char **argv) {
int command;
char *NSSConfigDir = NULL;
char *certName = NULL;
char *MARChannelID = MAR_CHANNEL_ID;
char *productVersion = MOZ_APP_VERSION;
#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
HANDLE certFile;
DWORD fileSize;
DWORD read;
char *certBuffer;
char *DERFilePath = NULL;
#endif
if (argc < 3) {
print_usage();
return -1;
}
if (argv[1][1] == 'C') {
chdir(argv[2]);
argv += 2;
argc -= 2;
while (argc > 0) {
if (argv[1][0] == '-' && (argv[1][1] == 'c' ||
argv[1][1] == 't' || argv[1][1] == 'x' ||
argv[1][1] == 'v' || argv[1][1] == 's' ||
argv[1][1] == 'i' || argv[1][1] == 'T' ||
argv[1][1] == 'r')) {
break;
/* -C workingdirectory */
} else if (argv[1][0] == '-' && argv[1][1] == 'C') {
chdir(argv[2]);
argv += 2;
argc -= 2;
}
#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
/* -D DERFilePath */
else if (argv[1][0] == '-' && argv[1][1] == 'D') {
DERFilePath = argv[2];
argv += 2;
argc -= 2;
}
#endif
/* -d NSSConfigdir */
else if (argv[1][0] == '-' && argv[1][1] == 'd') {
NSSConfigDir = argv[2];
argv += 2;
argc -= 2;
/* -n certName */
} else if (argv[1][0] == '-' && argv[1][1] == 'n') {
certName = argv[2];
argv += 2;
argc -= 2;
/* MAR channel ID */
} else if (argv[1][0] == '-' && argv[1][1] == 'H') {
MARChannelID = argv[2];
argv += 2;
argc -= 2;
/* Product Version */
} else if (argv[1][0] == '-' && argv[1][1] == 'V') {
productVersion = argv[2];
argv += 2;
argc -= 2;
}
else {
print_usage();
return -1;
}
}
if (argv[1][0] != '-') {
print_usage();
return -1;
}
switch (argv[1][1]) {
case 'c':
return mar_create(argv[2], argc - 3, argv + 3);
case 'c': {
struct ProductInformationBlock infoBlock;
infoBlock.MARChannelID = MARChannelID;
infoBlock.productVersion = productVersion;
return mar_create(argv[2], argc - 3, argv + 3, &infoBlock);
}
case 'i': {
struct ProductInformationBlock infoBlock;
infoBlock.MARChannelID = MARChannelID;
infoBlock.productVersion = productVersion;
return refresh_product_info_block(argv[2], &infoBlock);
}
case 'T': {
int rv;
struct ProductInformationBlock infoBlock;
int hasSignatureBlock, numSignatures,
hasAdditionalBlock, numAdditionalBlocks;
if (!get_mar_file_info(argv[2],
&hasSignatureBlock,
&numSignatures,
&hasAdditionalBlock,
NULL, &numAdditionalBlocks)) {
if (hasSignatureBlock) {
printf("Signature block found with %d signature%s\n",
numSignatures,
numSignatures != 1 ? "s" : "");
}
if (hasAdditionalBlock) {
printf("%d additional block%s found:\n",
numAdditionalBlocks,
numAdditionalBlocks != 1 ? "s" : "");
}
rv = read_product_info_block(argv[2], &infoBlock);
if (!rv) {
printf(" - Product Information Block:\n");
printf(" - MAR channel name: %s\n"
" - Product version: %s\n",
infoBlock.MARChannelID,
infoBlock.productVersion);
free((void *)infoBlock.MARChannelID);
free((void *)infoBlock.productVersion);
}
}
printf("\n");
// The fall through from 'T' to 't' is intentional
}
case 't':
return mar_test(argv[2]);
case 'x':
return mar_extract(argv[2]);
#ifndef NO_SIGN_VERIFY
case 'v':
#if defined(XP_WIN) && !defined(MAR_NSS)
if (!DERFilePath) {
print_usage();
return -1;
}
/* If the mar program was built using CryptoAPI, then read in the buffer
containing the cert from disk. */
certFile = CreateFileA(DERFilePath, GENERIC_READ,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
0, NULL);
if (INVALID_HANDLE_VALUE == certFile) {
return -1;
}
fileSize = GetFileSize(certFile, NULL);
certBuffer = malloc(fileSize);
if (!ReadFile(certFile, certBuffer, fileSize, &read, NULL) ||
fileSize != read) {
CloseHandle(certFile);
free(certBuffer);
return -1;
}
CloseHandle(certFile);
if (mar_verify_signature(argv[2], certBuffer, fileSize, NULL)) {
/* Determine if the source MAR file has the new fields for signing */
int hasSignatureBlock;
free(certBuffer);
if (get_mar_file_info(argv[2], &hasSignatureBlock,
NULL, NULL, NULL, NULL)) {
fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
} else if (!hasSignatureBlock) {
fprintf(stderr, "ERROR: The MAR file is in the old format so has"
" no signature to verify.\n");
}
return -1;
}
free(certBuffer);
return 0;
#else
if (!NSSConfigDir || !certName) {
print_usage();
return -1;
}
if (NSSInitCryptoContext(NSSConfigDir)) {
fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
return -1;
}
return mar_verify_signature(argv[2], NULL, 0,
certName);
#endif /* defined(XP_WIN) && !defined(MAR_NSS) */
case 's':
if (!NSSConfigDir || !certName || argc < 4) {
print_usage();
return -1;
}
return mar_repackage_and_sign(NSSConfigDir, certName, argv[2], argv[3]);
case 'r':
return strip_signature_block(argv[2], argv[3]);
#endif /* endif NO_SIGN_VERIFY disabled */
default:
print_usage();
return -1;

View File

@ -0,0 +1,69 @@
# ***** 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 mar verify build config.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Brian R. Bondy <netzen@gmail.com>
#
# 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = verifymar
LIBRARY_NAME = verifymar
FORCE_STATIC_LIB = 1
ifeq ($(OS_ARCH),WINNT)
USE_STATIC_LIBS = 1
endif
# This makefile just builds support for reading archives.
CSRCS = \
mar_verify.c \
cryptox.c \
$(NULL)
LOCAL_INCLUDES += -I$(srcdir)/../src
ifneq ($(OS_ARCH),WINNT)
DEFINES += -DMAR_NSS
LOCAL_INCLUDES += -I$(srcdir)/../sign
endif
include $(topsrcdir)/config/rules.mk
# The intermediate (.ii/.s) files for host and target can have the same name...
# disable parallel builds
.NOTPARALLEL:

View File

@ -0,0 +1,304 @@
/* ***** 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 cryptographic wrappers for Mozilla archive code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* 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 ***** */
#ifdef XP_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif
#include <stdlib.h>
#include "cryptox.h"
#if defined(MAR_NSS)
/**
* Loads the public key for the specified cert name from the NSS store.
*
* @param certName The cert name to find.
* @param publicKey Out parameter for the public key to use.
* @param cert Out parameter for the certificate to use.
* @return CryptoX_Success on success, CryptoX_Error on error.
*/
CryptoX_Result
NSS_LoadPublicKey(const char *certNickname,
SECKEYPublicKey **publicKey,
CERTCertificate **cert)
{
secuPWData pwdata = { PW_NONE, 0 };
if (!cert || !publicKey || !cert) {
return CryptoX_Error;
}
/* Get the cert and embedded public key out of the database */
*cert = PK11_FindCertFromNickname(certNickname, &pwdata);
if (!*cert) {
return CryptoX_Error;
}
*publicKey = CERT_ExtractPublicKey(*cert);
if (!*publicKey) {
CERT_DestroyCertificate(*cert);
return CryptoX_Error;
}
return CryptoX_Success;
}
CryptoX_Result
NSS_VerifyBegin(VFYContext **ctx,
SECKEYPublicKey * const *publicKey)
{
SECStatus status;
if (!ctx || !publicKey || !*publicKey) {
return CryptoX_Error;
}
/* Check that the key length is large enough for our requirements */
if ((SECKEY_PublicKeyStrength(*publicKey) * 8) <
XP_MIN_SIGNATURE_LEN_IN_BYTES) {
fprintf(stderr, "ERROR: Key length must be >= %d bytes\n",
XP_MIN_SIGNATURE_LEN_IN_BYTES);
return CryptoX_Error;
}
*ctx = VFY_CreateContext(*publicKey, NULL,
SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE, NULL);
if (*ctx == NULL) {
return CryptoX_Error;
}
status = VFY_Begin(*ctx);
return SECSuccess == status ? CryptoX_Success : CryptoX_Error;
}
/**
* Verifies if a verify context matches the passed in signature.
*
* @param ctx The verify context that the signature should match.
* @param signature The signature to match.
* @param signatureLen The length of the signature.
* @return CryptoX_Success on success, CryptoX_Error on error.
*/
CryptoX_Result
NSS_VerifySignature(VFYContext * const *ctx,
const unsigned char *signature,
unsigned int signatureLen)
{
SECItem signedItem;
SECStatus status;
if (!ctx || !signature || !*ctx) {
return CryptoX_Error;
}
signedItem.len = signatureLen;
signedItem.data = (unsigned char*)signature;
status = VFY_EndWithSignature(*ctx, &signedItem);
return SECSuccess == status ? CryptoX_Success : CryptoX_Error;
}
#elif defined(XP_WIN)
/**
* Verifies if a signature + public key matches a hash context.
*
* @param hash The hash context that the signature should match.
* @param pubKey The public key to use on the signature.
* @param signature The signature to check.
* @param signatureLen The length of the signature.
* @return CryptoX_Success on success, CryptoX_Error on error.
*/
CryptoX_Result
CyprtoAPI_VerifySignature(HCRYPTHASH *hash,
HCRYPTKEY *pubKey,
const BYTE *signature,
DWORD signatureLen)
{
DWORD i;
BOOL result;
/* Windows APIs expect the bytes in the signature to be in little-endian
* order, but we write the signature in big-endian order. Other APIs like
* NSS and OpenSSL expect big-endian order.
*/
BYTE *signatureReversed;
if (!hash || !pubKey || !signature || signatureLen < 1) {
return CryptoX_Error;
}
signatureReversed = malloc(signatureLen);
if (!signatureReversed) {
return CryptoX_Error;
}
for (i = 0; i < signatureLen; i++) {
signatureReversed[i] = signature[signatureLen - 1 - i];
}
result = CryptVerifySignature(*hash, signatureReversed,
signatureLen, *pubKey, NULL, 0);
free(signatureReversed);
return result ? CryptoX_Success : CryptoX_Error;
}
/**
* Obtains the public key for the passed in cert data
*
* @param provider The cyrto provider
* @param certData Data of the certificate to extract the public key from
* @param sizeOfCertData The size of the certData buffer
* @param certStore Pointer to the handle of the certificate store to use
* @param CryptoX_Success on success
*/
CryptoX_Result
CryptoAPI_LoadPublicKey(HCRYPTPROV provider,
BYTE *certData,
DWORD sizeOfCertData,
HCRYPTKEY *publicKey,
HCERTSTORE *certStore)
{
CRYPT_DATA_BLOB blob;
CERT_CONTEXT *context;
if (!provider || !certData || !publicKey || !certStore) {
return CryptoX_Error;
}
blob.cbData = sizeOfCertData;
blob.pbData = certData;
if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
CERT_QUERY_CONTENT_FLAG_CERT,
CERT_QUERY_FORMAT_FLAG_BINARY,
0, NULL, NULL, NULL,
certStore, NULL, (const void **)&context)) {
return CryptoX_Error;
}
if (!CryptImportPublicKeyInfo(provider,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
&context->pCertInfo->SubjectPublicKeyInfo,
publicKey)) {
CertFreeCertificateContext(context);
return CryptoX_Error;
}
CertFreeCertificateContext(context);
return CryptoX_Success;
}
/* Try to acquire context in this way:
* 1. Enhanced provider without creating a new key set
* 2. Enhanced provider with creating a new key set
* 3. Default provider without creating a new key set
* 4. Default provider without creating a new key set
* #2 and #4 should not be needed because of the CRYPT_VERIFYCONTEXT,
* but we add it just in case.
*
* @param provider Out parameter containing the provider handle.
* @return CryptoX_Success on success, CryptoX_Error on error.
*/
CryptoX_Result
CryptoAPI_InitCryptoContext(HCRYPTPROV *provider)
{
if (!CryptAcquireContext(provider,
NULL,
MS_ENHANCED_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
if (!CryptAcquireContext(provider,
NULL,
MS_ENHANCED_PROV,
PROV_RSA_FULL,
CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
if (!CryptAcquireContext(provider,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
if (!CryptAcquireContext(provider,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
*provider = CryptoX_InvalidHandleValue;
return CryptoX_Error;
}
}
}
}
return CryptoX_Success;
}
/**
* Begins a signature verification hash context
*
* @param provider The crypt provider to use
* @param hash Out parameter for a handle to the hash context
* @return CryptoX_Success on success, CryptoX_Error on error.
*/
CryptoX_Result
CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash)
{
BOOL result;
if (!provider || !hash) {
return CryptoX_Error;
}
*hash = (HCRYPTHASH)NULL;
result = CryptCreateHash(provider, CALG_SHA1,
0, 0, hash);
return result ? CryptoX_Success : CryptoX_Error;
}
/**
* Updates a signature verification hash context
*
* @param hash The hash context to udpate
* @param buf The buffer to update the hash context with
* @param len The size of the passed in buffer
* @return CryptoX_Success on success, CryptoX_Error on error.
*/
CryptoX_Result
CryptoAPI_VerifyUpdate(HCRYPTHASH* hash, BYTE *buf, DWORD len)
{
BOOL result;
if (!hash || !buf) {
return CryptoX_Error;
}
result = CryptHashData(*hash, buf, len, 0);
return result ? CryptoX_Success : CryptoX_Error;
}
#endif

View File

@ -0,0 +1,150 @@
/* ***** 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 cryptographic wrappers for Mozilla archive code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* 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 ***** */
#ifndef CRYPTOX_H
#define CRYPTOX_H
#define XP_MIN_SIGNATURE_LEN_IN_BYTES 256
#define CryptoX_Result int
#define CryptoX_Success 0
#define CryptoX_Error (-1)
#define CryptoX_Succeeded(X) ((X) == CryptoX_Success)
#define CryptoX_Failed(X) ((X) != CryptoX_Success)
#if defined(MAR_NSS)
#include "nss_secutil.h"
CryptoX_Result NSS_LoadPublicKey(const char *certNickname,
SECKEYPublicKey **publicKey,
CERTCertificate **cert);
CryptoX_Result NSS_VerifyBegin(VFYContext **ctx,
SECKEYPublicKey * const *publicKey);
CryptoX_Result NSS_VerifySignature(VFYContext * const *ctx ,
const unsigned char *signature,
unsigned int signatureLen);
#define CryptoX_InvalidHandleValue NULL
#define CryptoX_ProviderHandle void*
#define CryptoX_SignatureHandle VFYContext *
#define CryptoX_PublicKey SECKEYPublicKey *
#define CryptoX_Certificate CERTCertificate *
#define CryptoX_InitCryptoProvider(CryptoHandle) \
CryptoX_Success
#define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
NSS_VerifyBegin(SignatureHandle, PublicKey)
#define CryptoX_VerifyUpdate(SignatureHandle, buf, len) \
VFY_Update(*SignatureHandle, (const unsigned char*)(buf), len)
#define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
publicKey, certName, cert) \
NSS_LoadPublicKey(certName, publicKey, cert)
#define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
NSS_VerifySignature(hash, (const unsigned char *)(signedData), len)
#define CryptoX_FreePublicKey(key) \
SECKEY_DestroyPublicKey(*key)
#define CryptoX_FreeCertificate(cert) \
CERT_DestroyCertificate(*cert)
#elif defined(XP_WIN)
#include <windows.h>
#include <wincrypt.h>
CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV *provider);
CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV hProv,
BYTE *certData,
DWORD sizeOfCertData,
HCRYPTKEY *publicKey,
HCERTSTORE *cert);
CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash);
CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash,
BYTE *buf, DWORD len);
CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash,
HCRYPTKEY *pubKey,
const BYTE *signature,
DWORD signatureLen);
#define CryptoX_InvalidHandleValue ((ULONG_PTR)NULL)
#define CryptoX_ProviderHandle HCRYPTPROV
#define CryptoX_SignatureHandle HCRYPTHASH
#define CryptoX_PublicKey HCRYPTKEY
#define CryptoX_Certificate HCERTSTORE
#define CryptoX_InitCryptoProvider(CryptoHandle) \
CryptoAPI_InitCryptoContext(CryptoHandle)
#define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
CryptoAPI_VerifyBegin(CryptoHandle, SignatureHandle)
#define CryptoX_VerifyUpdate(SignatureHandle, buf, len) \
CryptoAPI_VerifyUpdate(SignatureHandle, (BYTE *)(buf), len)
#define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
publicKey, certName, cert) \
CryptoAPI_LoadPublicKey(CryptoHandle, (BYTE*)(certData), \
dataSize, publicKey, cert)
#define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
CyprtoAPI_VerifySignature(hash, publicKey, signedData, len)
#define CryptoX_FreePublicKey(key) \
CryptDestroyKey(*(key))
#define CryptoX_FreeCertificate(cert) \
CertCloseStore(*(cert), CERT_CLOSE_STORE_FORCE_FLAG);
#else
/* This default implementation is necessary because we don't want to
* link to NSS from updater code on non Windows platforms. On Windows
* we use CyrptoAPI instead of NSS. We don't call any function as they
* would just fail, but this simplifies linking.
*/
#define CryptoX_InvalidHandleValue NULL
#define CryptoX_ProviderHandle void*
#define CryptoX_SignatureHandle void*
#define CryptoX_PublicKey void*
#define CryptoX_Certificate void*
#define CryptoX_InitCryptoProvider(CryptoHandle) \
CryptoX_Error
#define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
CryptoX_Error
#define CryptoX_VerifyUpdate(SignatureHandle, buf, len) CryptoX_Error
#define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
publicKey, certName, cert) \
CryptoX_Error
#define CryptoX_VerifySignature(hash, publicKey, signedData, len) CryptoX_Error
#define CryptoX_FreePublicKey(key) CryptoX_Error
#endif
#endif

View File

@ -0,0 +1,444 @@
/* ***** 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 Mozilla Archive verify code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* 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 ***** */
#ifdef XP_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "mar_private.h"
#include "mar.h"
#include "cryptox.h"
int mar_verify_signature_fp(FILE *fp,
CryptoX_ProviderHandle provider,
CryptoX_PublicKey key);
int mar_verify_signature_for_fp(FILE *fp,
CryptoX_ProviderHandle provider,
CryptoX_PublicKey key,
PRUint32 signatureCount,
char *extractedSignature);
/**
* Reads the specified number of bytes from the file pointer and
* stores them in the passed buffer.
*
* @param fp The file pointer to read from.
* @param buffer The buffer to store the read results.
* @param size The number of bytes to read, buffer must be
* at least of this size.
* @param ctx The verify context.
* @param err The name of what is being written to in case of error.
* @return 0 on success
* -1 on read error
* -2 on verify update error
*/
int
ReadAndUpdateVerifyContext(FILE *fp,
void *buffer,
PRUint32 size,
CryptoX_SignatureHandle *ctx,
const char *err)
{
if (!fp || !buffer || !ctx || !err) {
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
return CryptoX_Error;
}
if (!size) {
return CryptoX_Success;
}
if (fread(buffer, size, 1, fp) != 1) {
fprintf(stderr, "ERROR: Could not read %s\n", err);
return CryptoX_Error;
}
if (CryptoX_Failed(CryptoX_VerifyUpdate(ctx, buffer, size))) {
fprintf(stderr, "ERROR: Could not update verify context for %s\n", err);
return -2;
}
return CryptoX_Success;
}
/**
* Verifies the embedded signature of the specified file path.
* This is only used by the signmar program when used with arguments to verify
* a MAR. This should not be used to verify a MAR that will be extracted in the
* same operation by updater code. This function prints the error message if
* verification fails.
*
* @param pathToMAR The path of the MAR file who's signature should be checked
* @param certData The certificate file data.
* @param sizeOfCertData The size of the cert data.
* @param certName Used only if compiled as NSS, specifies the certName
* @return 0 on success
* a negative number if there was an error
* a positive number if the signature does not verify
*/
int
mar_verify_signature(const char *pathToMARFile,
const char *certData,
PRUint32 sizeOfCertData,
const char *certName) {
int rv;
CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
CryptoX_Certificate cert;
CryptoX_PublicKey key;
FILE *fp;
if (!pathToMARFile || (!certData && !certName)) {
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
return CryptoX_Error;
}
fp = fopen(pathToMARFile, "rb");
if (!fp) {
fprintf(stderr, "ERROR: Could not open MAR file.\n");
return CryptoX_Error;
}
if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) {
fclose(fp);
fprintf(stderr, "ERROR: Could not init crytpo library.\n");
return CryptoX_Error;
}
if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData, sizeOfCertData,
&key, certName, &cert))) {
fclose(fp);
fprintf(stderr, "ERROR: Could not load public key.\n");
return CryptoX_Error;
}
rv = mar_verify_signature_fp(fp, provider, key);
fclose(fp);
if (key) {
CryptoX_FreePublicKey(&key);
}
if (cert) {
CryptoX_FreeCertificate(&cert);
}
return rv;
}
#ifdef XP_WIN
/**
* Verifies a MAR file's signature by making sure at least one
* signature verifies.
*
* @param pathToMARFile The path of the MAR file who's signature
* should be calculated
* @param certData The certificate data
* @param sizeOfCertData The size of the data stored in certData
* @return 0 on success
*/
int
mar_verify_signatureW(MarFile *mar,
const char *certData,
PRUint32 sizeOfCertData) {
int rv;
CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
CryptoX_Certificate cert;
CryptoX_PublicKey key;
if (!mar || !certData) {
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
return CryptoX_Error;
}
if (!mar->fp) {
fprintf(stderr, "ERROR: MAR file is not open.\n");
return CryptoX_Error;
}
if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) {
fprintf(stderr, "ERROR: Could not init crytpo library.\n");
return CryptoX_Error;
}
if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData, sizeOfCertData,
&key, "", &cert))) {
fprintf(stderr, "ERROR: Could not load public key.\n");
return CryptoX_Error;
}
rv = mar_verify_signature_fp(mar->fp, provider, key);
if (key) {
CryptoX_FreePublicKey(&key);
}
if (cert) {
CryptoX_FreeCertificate(&cert);
}
return rv;
}
#endif
/**
* Verifies a MAR file's signature by making sure at least one
* signature verifies.
*
* @param fp An opened MAR file handle
* @param provider A library provider
* @param key The public key to use to verify the MAR
* @return 0 on success
*/
int
mar_verify_signature_fp(FILE *fp,
CryptoX_ProviderHandle provider,
CryptoX_PublicKey key) {
char buf[5] = {0};
PRUint32 signatureAlgorithmID, signatureCount, signatureLen, numVerified = 0;
int rv = -1;
PRInt64 curPos;
char *extractedSignature;
PRUint32 i;
if (!fp) {
fprintf(stderr, "ERROR: Invalid file pointer passed.\n");
return CryptoX_Error;
}
/* To protect against invalid MAR files, we assumes that the MAR file
size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
if (fseeko(fp, 0, SEEK_END)) {
fprintf(stderr, "ERROR: Could not seek to the end of the MAR file.\n");
return CryptoX_Error;
}
if (ftello(fp) > MAX_SIZE_OF_MAR_FILE) {
fprintf(stderr, "ERROR: MAR file is too large to be verified.\n");
return CryptoX_Error;
}
/* Skip to the start of the signature block */
if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
fprintf(stderr, "ERROR: Could not seek to the signature block.\n");
return CryptoX_Error;
}
/* Get the number of signatures */
if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
fprintf(stderr, "ERROR: Could not read number of signatures.\n");
return CryptoX_Error;
}
signatureCount = ntohl(signatureCount);
/* Check that we have less than the max amount of signatures so we don't
waste too much of either updater's or signmar's time. */
if (signatureCount > MAX_SIGNATURES) {
fprintf(stderr, "ERROR: At most %d signatures can be specified.\n",
MAX_SIGNATURES);
return CryptoX_Error;
}
for (i = 0; i < signatureCount && numVerified == 0; i++) {
/* Get the signature algorithm ID */
if (fread(&signatureAlgorithmID, sizeof(PRUint32), 1, fp) != 1) {
fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n");
return CryptoX_Error;
}
signatureAlgorithmID = ntohl(signatureAlgorithmID);
if (fread(&signatureLen, sizeof(PRUint32), 1, fp) != 1) {
fprintf(stderr, "ERROR: Could not read signatures length.\n");
return CryptoX_Error;
}
signatureLen = ntohl(signatureLen);
/* To protected against invalid input make sure the signature length
isn't too big. */
if (signatureLen > MAX_SIGNATURE_LENGTH) {
fprintf(stderr, "ERROR: Signature length is too large to verify.\n");
return CryptoX_Error;
}
extractedSignature = malloc(signatureLen);
if (!extractedSignature) {
fprintf(stderr, "ERROR: Could allocate buffer for signature.\n");
return CryptoX_Error;
}
if (fread(extractedSignature, signatureLen, 1, fp) != 1) {
fprintf(stderr, "ERROR: Could not read extracted signature.\n");
free(extractedSignature);
return CryptoX_Error;
}
/* We don't try to verify signatures we don't know about */
if (1 == signatureAlgorithmID) {
curPos = ftello(fp);
rv = mar_verify_signature_for_fp(fp,
provider,
key,
signatureCount,
extractedSignature);
if (CryptoX_Succeeded(rv)) {
numVerified++;
}
free(extractedSignature);
if (fseeko(fp, curPos, SEEK_SET)) {
fprintf(stderr, "ERROR: Could not seek back to last signature.\n");
return CryptoX_Error;
}
} else {
free(extractedSignature);
}
}
/* If we reached here and we verified at least one
signature, return success. */
if (numVerified > 0) {
return CryptoX_Success;
} else {
fprintf(stderr, "ERROR: No signatures were verified.\n");
return CryptoX_Error;
}
}
/**
* Verifies if a specific signature ID matches the extracted signature.
*
* @param fp An opened MAR file handle
* @param provider A library provider
* @param key The public key to use to verify the MAR
* @param signatureCount The number of signatures in the MAR file
* @param extractedSignature The signature that should be verified
* @return 0 on success
*/
int
mar_verify_signature_for_fp(FILE *fp,
CryptoX_ProviderHandle provider,
CryptoX_PublicKey key,
PRUint32 signatureCount,
char *extractedSignature) {
CryptoX_SignatureHandle signatureHandle;
char buf[BLOCKSIZE];
PRUint32 signatureLen;
PRUint32 i;
if (!extractedSignature) {
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
return CryptoX_Error;
}
/* This function is only called when we have at least one signature,
but to protected against future people who call this function we
make sure a non zero value is passed in.
*/
if (!signatureCount) {
fprintf(stderr, "ERROR: There must be at least one signature.\n");
return CryptoX_Error;
}
CryptoX_VerifyBegin(provider, &signatureHandle, &key);
/* Skip to the start of the file */
if (fseeko(fp, 0, SEEK_SET)) {
fprintf(stderr, "ERROR: Could not seek to start of the file\n");
return CryptoX_Error;
}
/* Bytes 0-3: MAR1
Bytes 4-7: index offset
Bytes 8-15: size of entire MAR
*/
if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, buf,
SIGNATURE_BLOCK_OFFSET +
sizeof(PRUint32),
&signatureHandle,
"signature block"))) {
return CryptoX_Error;
}
for (i = 0; i < signatureCount; i++) {
/* Get the signature algorithm ID */
if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp,
&buf,
sizeof(PRUint32),
&signatureHandle,
"signature algorithm ID"))) {
return CryptoX_Error;
}
if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp,
&signatureLen,
sizeof(PRUint32),
&signatureHandle,
"signature length"))) {
return CryptoX_Error;
}
signatureLen = ntohl(signatureLen);
/* Skip past the signature itself as those are not included */
if (fseeko(fp, signatureLen, SEEK_CUR)) {
fprintf(stderr, "ERROR: Could not seek past signature.\n");
return CryptoX_Error;
}
}
while (!feof(fp)) {
int numRead = fread(buf, 1, BLOCKSIZE , fp);
if (ferror(fp)) {
fprintf(stderr, "ERROR: Error reading data block.\n");
return CryptoX_Error;
}
if (CryptoX_Failed(CryptoX_VerifyUpdate(&signatureHandle,
buf, numRead))) {
fprintf(stderr, "ERROR: Error updating verify context with"
" data block.\n");
return CryptoX_Error;
}
}
if (CryptoX_Failed(CryptoX_VerifySignature(&signatureHandle,
&key,
extractedSignature,
signatureLen))) {
fprintf(stderr, "ERROR: Error verifying signature.\n");
return CryptoX_Error;
}
return CryptoX_Success;
}

View File

@ -109,3 +109,6 @@ run-if.config = ipc
[include:netwerk/cookie/test/unit_ipc/xpcshell.ini]
[include:toolkit/components/contentprefs/tests/unit_ipc/xpcshell.ini]
[include:uriloader/exthandler/tests/unit_ipc/xpcshell.ini]
[include:modules/libmar/tests/unit/xpcshell.ini]
skip-if = os == "android"

View File

@ -56,6 +56,13 @@
#define UPDATER_QUOTED_PATH_MEM_ERROR 14
#define BAD_ACTION_ERROR 15
#define STRING_CONVERSION_ERROR 16
#define CERT_LOAD_ERROR 17
#define CERT_HANDLING_ERROR 18
#define CERT_VERIFY_ERROR 19
#define ARCHIVE_NOT_OPEN 20
#define COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR 21
#define MAR_CHANNEL_MISMATCH_ERROR 22
#define VERSION_DOWNGRADE_ERROR 23
// The following error codes are only used by updater.exe
// when a fallback key exists and XPCShell tests are being run.

View File

@ -39,6 +39,7 @@
#include <stdio.h>
#include "shlobj.h"
#include "updatehelper.h"
#include "pathhash.h"
// Needed for PathAppendW
#include <shlwapi.h>
@ -337,7 +338,7 @@ StartServiceCommand(int argc, LPCWSTR* argv)
* @return ERROR_SUCCESS if successful
*/
DWORD
LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR* argv)
LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR* argv)
{
// The service command is the same as the updater.exe command line except
// it has 2 extra args: 1) The Path to udpater.exe, and 2) the command
@ -573,8 +574,8 @@ WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
*
* @param process The process to check for existance
* @return ERROR_NOT_FOUND if the process was not found
* @ ERROR_SUCCESS if the process was found and there were no errors
* @ Other Win32 system error code for other errors
* ERROR_SUCCESS if the process was found and there were no errors
* Other Win32 system error code for other errors
**/
DWORD
IsProcessRunning(LPCWSTR filename)
@ -631,3 +632,23 @@ WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
return applicationRunningError;
}
/**
* Determines if the fallback key exists or not
*
* @return TRUE if the fallback key exists and there was no error checking
*/
BOOL
DoesFallbackKeyExist()
{
HKEY testOnlyFallbackKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
TEST_ONLY_FALLBACK_KEY_PATH, 0,
KEY_READ | KEY_WOW64_64KEY,
&testOnlyFallbackKey) != ERROR_SUCCESS) {
return FALSE;
}
RegCloseKey(testOnlyFallbackKey);
return TRUE;
}

View File

@ -41,10 +41,11 @@ BOOL LaunchWinPostProcess(const WCHAR *installationDir,
HANDLE userToken);
BOOL StartServiceUpdate(int argc, LPWSTR *argv);
BOOL GetUpdateDirectoryPath(LPWSTR path);
DWORD LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR *argv);
DWORD LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR *argv);
BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
BOOL WriteStatusPending(LPCWSTR updateDirPath);
DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
BOOL DoesFallbackKeyExist();
#define SVC_NAME L"MozillaMaintenance"

View File

@ -141,6 +141,8 @@ const STATE_FAILED = "failed";
const WRITE_ERROR = 7;
const UNEXPECTED_ERROR = 8;
const ELEVATION_CANCELED = 9;
// Windows service specific errors
const SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000;
const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
const SERVICE_UPDATER_SIGN_ERROR = 16002;
@ -149,6 +151,15 @@ const SERVICE_UPDATER_IDENTITY_ERROR = 16004;
const SERVICE_STILL_APPLYING_ON_SUCCESS = 16005;
const SERVICE_STILL_APPLYING_ON_FAILURE = 16006;
// Updater MAR security errors
const CERT_LOAD_ERROR = 17;
const CERT_HANDLING_ERROR = 18;
const CERT_VERIFY_ERROR = 19;
const ARCHIVE_NOT_OPEN = 20;
const COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR = 21;
const MAR_CHANNEL_MISMATCH_ERROR = 22;
const VERSION_DOWNGRADE_ERROR = 23;
const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
@ -1413,7 +1424,14 @@ UpdateService.prototype = {
update.errorCode == SERVICE_UPDATER_COMPARE_ERROR ||
update.errorCode == SERVICE_UPDATER_IDENTITY_ERROR ||
update.errorCode == SERVICE_STILL_APPLYING_ON_SUCCESS ||
update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE) {
update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE ||
update.errorCode == CERT_LOAD_ERROR ||
update.errorCode == CERT_HANDLING_ERROR ||
update.errorCode == CERT_VERIFY_ERROR ||
update.errorCode == ARCHIVE_NOT_OPEN ||
update.errorCode == COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR ||
update.errorCode == MAR_CHANNEL_MISMATCH_ERROR ||
update.errorCode == VERSION_DOWNGRADE_ERROR) {
var failCount = getPref("getIntPref",
PREF_APP_UPDATE_SERVICE_ERRORS, 0);
var maxFail = getPref("getIntPref",

View File

@ -44,7 +44,7 @@ relativesrcdir = toolkit/mozapps/update/test/chrome
include $(DEPTH)/config/autoconf.mk
_OTHER_FILES = \
../unit/data/simple.mar \
../unit/data/simple_no_pib.mar \
$(NULL)
_CHROME_FILES = \

View File

@ -11,7 +11,7 @@
* features greater than JavaScript 1.7.
*/
const FILE_SIMPLE_MAR = "simple.mar";
const FILE_SIMPLE_MAR = "simple_no_pib.mar";
const SIZE_SIMPLE_MAR = "351";

View File

@ -90,6 +90,8 @@ const MAR_PARTIAL_FILE = "data/partial.mar";
const UPDATER_BIN_FILE = "updater" + BIN_SUFFIX;
const MAINTENANCE_SERVICE_BIN_FILE = "maintenanceservice.exe";
const MAINTENANCE_SERVICE_INSTALLER_BIN_FILE = "maintenanceservice_installer.exe";
const UPDATE_SETTINGS_INI_FILE = "update-settings.ini";
const UPDATE_SETTINGS_CONTENTS = "[Settings]\nMAR_CHANNEL_ID=xpcshell-test\n"
const UPDATES_DIR_SUFFIX = "_mar";
const LOG_COMPLETE_SUCCESS = "data/complete_log_success";
@ -443,6 +445,10 @@ function runUpdate() {
if (/ /.test(callbackAppPath))
callbackAppPath = '"' + callbackAppPath + '"';
let updateSettingsIni = getApplyDirFile(null, true);
updateSettingsIni.append(UPDATE_SETTINGS_INI_FILE);
writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
let args = [updatesDirPath, applyToDirPath, 0, cwdPath, callbackAppPath].
concat(gCallbackArgs);
let process = AUS_Cc["@mozilla.org/process/util;1"].
@ -749,6 +755,10 @@ function runUpdateUsingService(aInitialStatus, aExpectedStatus,
copyBinToApplyToDir(MAINTENANCE_SERVICE_BIN_FILE);
copyBinToApplyToDir(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE);
let updateSettingsIni = getApplyDirFile(null, true);
updateSettingsIni.append(UPDATE_SETTINGS_INI_FILE);
writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
// Firefox does not wait for the service command to finish, but
// we still launch the process sync to avoid intermittent failures with
// the log file not being written out yet.

View File

@ -104,6 +104,10 @@ function run_test() {
updaterIni.append(FILE_UPDATER_INI);
writeFile(updaterIni, updaterIniContents);
let updateSettingsIni = processDir.clone();
updateSettingsIni.append(UPDATE_SETTINGS_INI_FILE);
writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
let launchBin = getLaunchBin();
let args = getProcessArgs();
logTestInfo("launching " + launchBin.path + " " + args.join(" "));

View File

@ -66,6 +66,7 @@ LIBS += \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
LIBS += $(DEPTH)/modules/libmar/verify/$(LIB_PREFIX)verifymar.$(LIB_SUFFIX)
USE_STATIC_LIBS = 1
HAVE_PROGRESSUI = 1
RCINCLUDE = updater.rc
@ -111,7 +112,10 @@ endif
include $(topsrcdir)/config/rules.mk
DEFINES += -DNS_NO_XPCOM
DEFINES += -DNS_NO_XPCOM \
-DMAR_CHANNEL_ID='"$(MAR_CHANNEL_ID)"' \
-DMOZ_APP_VERSION='"$(MOZ_APP_VERSION)"' \
$(NULL)
ifdef _MSC_VER
WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
@ -139,4 +143,18 @@ ifeq (,$(filter-out WINNT,$(OS_ARCH)))
LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
endif
ifeq ($(OS_ARCH),WINNT)
EXTRA_LIBS += $(call EXPAND_LIBNAME,crypt32)
EXTRA_LIBS += $(call EXPAND_LIBNAME,advapi32)
endif
CXXFLAGS += $(BZ2_CFLAGS)
ifneq (,$(filter beta release esr,$(MOZ_UPDATE_CHANNEL)))
RCFLAGS += -DMAR_SIGNING_RELEASE_BETA=1
else
ifneq (,$(filter nightly aurora nightly-elm nightly-profiling nightly-oak,$(MOZ_UPDATE_CHANNEL)))
RCFLAGS += -DMAR_SIGNING_AURORA_NIGHTLY=1
endif
endif

View File

@ -42,6 +42,14 @@
#include "bzlib.h"
#include "archivereader.h"
#include "errors.h"
#include "nsAlgorithm.h"
#ifdef XP_WIN
#include "updatehelper.h"
#endif
#define UPDATER_NO_STRING_GLUE_STL
#include "../../../../xpcom/build/nsVersionComparator.cpp"
#undef UPDATER_NO_STRING_GLUE_STL
#if defined(XP_UNIX)
# include <sys/types.h>
@ -54,6 +62,185 @@ static int outbuf_size = 262144;
static char *inbuf = NULL;
static char *outbuf = NULL;
#ifdef XP_WIN
#include "resource.h"
/**
* Obtains the data of the specified resource name and type.
*
* @param name The name ID of the resource
* @param type The type ID of the resource
* @param data Out parameter which sets the pointer to a buffer containing
* the needed data.
* @param size Out parameter which sets the size of the returned data buffer
* @return TRUE on success
*/
BOOL
LoadFileInResource(int name, int type, const char *&data, DWORD& size)
{
HMODULE handle = GetModuleHandle(NULL);
if (!handle) {
return FALSE;
}
HRSRC resourceInfoBlockHandle = FindResource(handle,
MAKEINTRESOURCE(name),
MAKEINTRESOURCE(type));
if (!resourceInfoBlockHandle) {
FreeLibrary(handle);
return FALSE;
}
HGLOBAL resourceHandle = LoadResource(handle, resourceInfoBlockHandle);
if (!resourceHandle) {
FreeLibrary(handle);
return FALSE;
}
size = SizeofResource(handle, resourceInfoBlockHandle);
data = static_cast<const char*>(::LockResource(resourceHandle));
FreeLibrary(handle);
return TRUE;
}
/**
* Performs a verification on the opened MAR file with the passed in
* certificate name ID and type ID.
*
* @param archive The MAR file to verify the signature on
* @param name The name ID of the resource
* @param type THe type ID of the resource
* @return OK on success, CERT_LOAD_ERROR or CERT_VERIFY_ERROR on failure.
*/
int
VerifyLoadedCert(MarFile *archive, int name, int type)
{
DWORD size = 0;
const char *data = NULL;
if (!LoadFileInResource(name, type, data, size) || !data || !size) {
return CERT_LOAD_ERROR;
}
if (mar_verify_signatureW(archive, data, size)) {
return CERT_VERIFY_ERROR;
}
return OK;
}
#endif
/**
* Performs a verification on the opened MAR file. Both the primary and backup
* keys stored are stored in the current process and at least the primary key
* will be tried. Success will be returned as long as one of the two
* signatures verify.
*
* @return OK on success
*/
int
ArchiveReader::VerifySignature()
{
if (!mArchive) {
return ARCHIVE_NOT_OPEN;
}
#ifdef XP_WIN
// If the fallback key exists we're running an XPCShell test and we should
// use the XPCShell specific cert for the signed MAR.
int rv;
if (DoesFallbackKeyExist()) {
rv = VerifyLoadedCert(mArchive, IDR_XPCSHELL_CERT, TYPE_CERT);
} else {
rv = VerifyLoadedCert(mArchive, IDR_PRIMARY_CERT, TYPE_CERT);
if (rv != OK) {
rv = VerifyLoadedCert(mArchive, IDR_BACKUP_CERT, TYPE_CERT);
}
}
return rv;
#else
return OK;
#endif
}
/**
* Verifies that the MAR file matches the current product, channel, and version
*
* @param MARChannelID The MAR channel name to use, only updates from MARs
* with a matching MAR channel name will succeed.
* If an empty string is passed, no check will be done
* for the channel name in the product information block.
* If a comma separated list of values is passed then
* one value must match.
* @param appVersion The application version to use, only MARs with an
* application version >= to appVersion will be applied.
* @return OK on success
* COULD_NOT_READ_PRODUCT_INFO_BLOCK if the product info block
* could not be read.
* MARCHANNEL_MISMATCH_ERROR if update-settings.ini's MAR
* channel ID doesn't match the MAR
* file's MAR channel ID.
* VERSION_DOWNGRADE_ERROR if the application version for
* this updater is newer than the
* one in the MAR.
*/
int
ArchiveReader::VerifyProductInformation(const char *MARChannelID,
const char *appVersion)
{
if (!mArchive) {
return ARCHIVE_NOT_OPEN;
}
ProductInformationBlock productInfoBlock;
int rv = mar_read_product_info_block(mArchive,
&productInfoBlock);
if (rv != OK) {
return COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR;
}
// Only check the MAR channel name if specified, it should be passed in from
// the update-settings.ini file.
if (MARChannelID && strlen(MARChannelID)) {
// Check for at least one match in the comma separated list of values.
const char *delimiter = " ,\t";
// Make a copy of the string in case a read only memory buffer
// was specified. strtok modifies the input buffer.
char channelCopy[512] = { 0 };
strncpy(channelCopy, MARChannelID, sizeof(channelCopy) - 1);
char *channel = strtok(channelCopy, delimiter);
rv = MAR_CHANNEL_MISMATCH_ERROR;
while(channel) {
if (!strcmp(channel, productInfoBlock.MARChannelID)) {
rv = OK;
break;
}
channel = strtok(NULL, delimiter);
}
}
if (rv == OK) {
/* Compare both versions to ensure we don't have a downgrade
1 if appVersion is older than productInfoBlock.productVersion
-1 if appVersion is newer than productInfoBlock.productVersion
0 if appVersion is the same as productInfoBlock.productVersion
This even works with strings like:
- 12.0a1 being older than 12.0a2
- 12.0a2 being older than 12.0b1
- 12.0a1 being older than 12.0
- 12.0 being older than 12.1a1 */
int versionCompareResult =
NS_CompareVersions(appVersion, productInfoBlock.productVersion);
if (-1 == versionCompareResult) {
rv = VERSION_DOWNGRADE_ERROR;
}
}
free((void *)productInfoBlock.MARChannelID);
free((void *)productInfoBlock.productVersion);
return rv;
}
int
ArchiveReader::Open(const NS_tchar *path)
{

View File

@ -56,6 +56,9 @@ public:
~ArchiveReader() { Close(); }
int Open(const NS_tchar *path);
int VerifySignature();
int VerifyProductInformation(const char *MARChannelID,
const char *appVersion);
void Close();
int ExtractFile(const char *item, const NS_tchar *destination);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,7 +6,11 @@
#define IDC_PROGRESS 1000
#define IDC_INFO 1002
#define IDI_DIALOG 1003
#define TYPE_CERT 512
#define IDR_PRIMARY_CERT 1004
#define IDR_BACKUP_CERT 1005
#define IDS_UPDATER_IDENTITY 1006
#define IDR_XPCSHELL_CERT 1007
// Next default values for new objects
//
@ -14,7 +18,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1007
#define _APS_NEXT_CONTROL_VALUE 1008
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -68,6 +68,7 @@
#include "bspatch.h"
#include "progressui.h"
#include "archivereader.h"
#include "readstrings.h"
#include "errors.h"
#include "bzlib.h"
@ -191,6 +192,15 @@ private:
FILE* mFile;
};
struct MARChannelStringTable {
MARChannelStringTable()
{
MARChannelID[0] = '\0';
}
char MARChannelID[MAX_TEXT_LEN];
};
//-----------------------------------------------------------------------------
typedef void (* ThreadFunc)(void *param);
@ -1530,16 +1540,78 @@ WaitForServiceFinishThread(void *param)
}
#endif
/**
* This function reads in the ACCEPTED_MAR_CHANNEL_IDS from update-settings.ini
*
* @param path The path to the ini file that is to be read
* @param results A pointer to the location to store the read strings
* @return OK on success
*/
static int
ReadMARChannelIDs(const NS_tchar *path, MARChannelStringTable *results)
{
const unsigned int kNumStrings = 1;
const char *kUpdaterKeys = "ACCEPTED_MAR_CHANNEL_IDS\0";
char updater_strings[kNumStrings][MAX_TEXT_LEN];
int result = ReadStrings(path, kUpdaterKeys, kNumStrings,
updater_strings, "Settings");
strncpy(results->MARChannelID, updater_strings[0], MAX_TEXT_LEN - 1);
results->MARChannelID[MAX_TEXT_LEN - 1] = 0;
return result;
}
struct UpdateThreadData
{
UpdateThreadData(bool performMARChecks) :
mPerformMARChecks(performMARChecks)
{
}
bool mPerformMARChecks;
};
static void
UpdateThreadFunc(void *param)
{
UpdateThreadData *threadData = reinterpret_cast<UpdateThreadData*>(param);
bool performMARChecks = threadData && threadData->mPerformMARChecks;
delete threadData;
// open ZIP archive and process...
int rv;
NS_tchar dataFile[MAXPATHLEN];
NS_tsnprintf(dataFile, sizeof(dataFile)/sizeof(dataFile[0]),
NS_T("%s/update.mar"), gSourcePath);
int rv = gArchiveReader.Open(dataFile);
rv = gArchiveReader.Open(dataFile);
if (performMARChecks) {
#ifdef MOZ_VERIFY_MAR_SIGNATURE
if (rv == OK) {
rv = gArchiveReader.VerifySignature();
}
if (rv == OK) {
NS_tchar updateSettingsPath[MAX_TEXT_LEN];
NS_tsnprintf(updateSettingsPath,
sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]),
NS_T("%supdate-settings.ini"), gDestPath);
MARChannelStringTable MARStrings;
if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) {
// If we can't read from update-settings.ini then we shouldn't impose
// a MAR restriction. Some installations won't even include this file.
MARStrings.MARChannelID[0] = '\0';
}
rv = gArchiveReader.VerifyProductInformation(MARStrings.MARChannelID,
MOZ_APP_VERSION);
}
#endif
}
if (rv == OK) {
rv = DoUpdate();
gArchiveReader.Close();
@ -1617,15 +1689,7 @@ int NS_main(int argc, NS_tchar **argv)
// Our tests run with a different apply directory for each test.
// We use this registry key on our test slaves to store the
// allowed name/issuers.
HKEY testOnlyFallbackKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
TEST_ONLY_FALLBACK_KEY_PATH, 0,
KEY_READ | KEY_WOW64_64KEY,
&testOnlyFallbackKey) == ERROR_SUCCESS) {
testOnlyFallbackKeyExists = true;
RegCloseKey(testOnlyFallbackKey);
}
testOnlyFallbackKeyExists = DoesFallbackKeyExist();
#endif
#endif
@ -2011,7 +2075,7 @@ int NS_main(int argc, NS_tchar **argv)
const int max_retries = 10;
int retries = 1;
do {
// By opening a file handle wihout FILE_SHARE_READ to the callback
// By opening a file handle without FILE_SHARE_READ to the callback
// executable, the OS will prevent launching the process while it is
// being updated.
callbackFile = CreateFileW(argv[callbackIndex],
@ -2059,7 +2123,7 @@ int NS_main(int argc, NS_tchar **argv)
// before QuitProgressUI has been called, so wait for UpdateThreadFunc to
// terminate.
Thread t;
if (t.Run(UpdateThreadFunc, NULL) == 0) {
if (t.Run(UpdateThreadFunc, new UpdateThreadData(usingService)) == 0) {
ShowProgressUI();
}
t.Join();

View File

@ -36,6 +36,25 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
IDI_DIALOG ICON "updater.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Embedded certificates for allowed MARs
//
#if defined(MAR_SIGNING_RELEASE_BETA)
IDR_PRIMARY_CERT TYPE_CERT "release_primary.der"
IDR_BACKUP_CERT TYPE_CERT "release_secondary.der"
#elif defined(MAR_SIGNING_AURORA_NIGHTLY)
IDR_PRIMARY_CERT TYPE_CERT "nightly_aurora_level3_primary.der"
IDR_BACKUP_CERT TYPE_CERT "nightly_aurora_level3_secondary.der"
#else
IDR_PRIMARY_CERT TYPE_CERT "dep1.der"
IDR_BACKUP_CERT TYPE_CERT "dep2.der"
#endif
IDR_XPCSHELL_CERT TYPE_CERT "xpcshellCertificate.der"
/////////////////////////////////////////////////////////////////////////////
//
// Embedded an identifier to uniquely identiy this as a Mozilla updater.

Binary file not shown.

View File

@ -80,7 +80,6 @@ ifdef MOZ_UPDATER
ifndef MOZ_NATIVE_BZ2
tier_platform_dirs += modules/libbz2
endif
tier_platform_dirs += modules/libmar
tier_platform_dirs += other-licenses/bsdiff
endif
@ -212,14 +211,18 @@ ifdef MOZ_SPELLCHECK
tier_platform_dirs += extensions/spellcheck
endif
tier_platform_dirs += toolkit
ifdef MOZ_PSM
tier_platform_dirs += security/manager
else
tier_platform_dirs += security/manager/boot/public security/manager/ssl/public
endif
ifdef MOZ_UPDATER
tier_platform_dirs += modules/libmar
endif
tier_platform_dirs += toolkit
ifdef MOZ_PREF_EXTENSIONS
tier_platform_dirs += extensions/pref
endif

View File

@ -39,7 +39,7 @@
#include <stdlib.h>
#include <string.h>
#ifdef XP_WIN
#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
#include <wchar.h>
#include "nsStringGlue.h"
#endif