mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge central to inbound
This commit is contained in:
commit
0e771aedaf
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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@
|
||||
|
@ -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) $< > $@
|
||||
|
44
build/update-settings.ini.in
Normal file
44
build/update-settings.ini.in
Normal 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@
|
@ -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@
|
||||
|
20
configure.in
20
configure.in
@ -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")
|
||||
|
@ -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
|
||||
|
69
modules/libmar/sign/Makefile.in
Normal file
69
modules/libmar/sign/Makefile.in
Normal 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:
|
818
modules/libmar/sign/mar_sign.c
Normal file
818
modules/libmar/sign/mar_sign.c
Normal 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;
|
||||
}
|
269
modules/libmar/sign/nss_secutil.c
Normal file
269
modules/libmar/sign/nss_secutil.c
Normal 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;
|
||||
}
|
73
modules/libmar/sign/nss_secutil.h
Normal file
73
modules/libmar/sign/nss_secutil.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
122
modules/libmar/src/mar_cmdline.h
Normal file
122
modules/libmar/src/mar_cmdline.h
Normal 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__ */
|
@ -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:
|
||||
|
@ -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>
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
69
modules/libmar/tests/Makefile.in
Normal file
69
modules/libmar/tests/Makefile.in
Normal 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
|
0
modules/libmar/tests/unit/data/0_sized_file
Normal file
0
modules/libmar/tests/unit/data/0_sized_file
Normal file
BIN
modules/libmar/tests/unit/data/0_sized_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/0_sized_mar.mar
Normal file
Binary file not shown.
1
modules/libmar/tests/unit/data/1_byte_file
Normal file
1
modules/libmar/tests/unit/data/1_byte_file
Normal file
@ -0,0 +1 @@
|
||||
1
|
BIN
modules/libmar/tests/unit/data/1_byte_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/1_byte_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/binary_data_file
Normal file
BIN
modules/libmar/tests/unit/data/binary_data_file
Normal file
Binary file not shown.
After Width: | Height: | Size: 512 B |
BIN
modules/libmar/tests/unit/data/binary_data_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/binary_data_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/cert8.db
Normal file
BIN
modules/libmar/tests/unit/data/cert8.db
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/key3.db
Normal file
BIN
modules/libmar/tests/unit/data/key3.db
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/manipulated_signed_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/manipulated_signed_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/multiple_file_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/multiple_file_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/mycert.der
Normal file
BIN
modules/libmar/tests/unit/data/mycert.der
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/no_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/no_pib_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/secmod.db
Normal file
BIN
modules/libmar/tests/unit/data/secmod.db
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/signed_no_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/signed_no_pib_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/signed_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/signed_pib_mar.mar
Normal file
Binary file not shown.
140
modules/libmar/tests/unit/head_libmar.js.in
Normal file
140
modules/libmar/tests/unit/head_libmar.js.in
Normal 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);
|
||||
}
|
||||
|
||||
|
74
modules/libmar/tests/unit/test_create.js
Normal file
74
modules/libmar/tests/unit/test_create.js
Normal 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);
|
||||
}
|
90
modules/libmar/tests/unit/test_extract.js
Normal file
90
modules/libmar/tests/unit/test_extract.js
Normal 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);
|
||||
}
|
187
modules/libmar/tests/unit/test_sign_verify.js
Normal file
187
modules/libmar/tests/unit/test_sign_verify.js
Normal 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);
|
||||
}
|
7
modules/libmar/tests/unit/xpcshell.ini
Normal file
7
modules/libmar/tests/unit/xpcshell.ini
Normal file
@ -0,0 +1,7 @@
|
||||
[DEFAULT]
|
||||
head = head_libmar.js
|
||||
tail =
|
||||
|
||||
[test_create.js]
|
||||
[test_extract.js]
|
||||
[test_sign_verify.js]
|
@ -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
|
||||
|
@ -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;
|
||||
|
69
modules/libmar/verify/Makefile.in
Normal file
69
modules/libmar/verify/Makefile.in
Normal 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:
|
304
modules/libmar/verify/cryptox.c
Normal file
304
modules/libmar/verify/cryptox.c
Normal 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
|
||||
|
||||
|
||||
|
150
modules/libmar/verify/cryptox.h
Normal file
150
modules/libmar/verify/cryptox.h
Normal 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
|
444
modules/libmar/verify/mar_verify.c
Normal file
444
modules/libmar/verify/mar_verify.c
Normal 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;
|
||||
}
|
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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 = \
|
||||
|
@ -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";
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
toolkit/mozapps/update/test/unit/data/simple_no_pib.mar
Normal file
BIN
toolkit/mozapps/update/test/unit/data/simple_no_pib.mar
Normal file
Binary file not shown.
@ -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.
|
||||
|
@ -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(" "));
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
BIN
toolkit/mozapps/update/updater/dep1.der
Normal file
BIN
toolkit/mozapps/update/updater/dep1.der
Normal file
Binary file not shown.
BIN
toolkit/mozapps/update/updater/dep2.der
Normal file
BIN
toolkit/mozapps/update/updater/dep2.der
Normal file
Binary file not shown.
BIN
toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
Normal file
BIN
toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
Normal file
Binary file not shown.
Binary file not shown.
BIN
toolkit/mozapps/update/updater/release_primary.der
Normal file
BIN
toolkit/mozapps/update/updater/release_primary.der
Normal file
Binary file not shown.
BIN
toolkit/mozapps/update/updater/release_secondary.der
Normal file
BIN
toolkit/mozapps/update/updater/release_secondary.der
Normal file
Binary file not shown.
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
BIN
toolkit/mozapps/update/updater/xpcshellCertificate.der
Normal file
BIN
toolkit/mozapps/update/updater/xpcshellCertificate.der
Normal file
Binary file not shown.
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user