зеркало из https://github.com/mozilla/gecko-dev.git
Bug 699700 - Add support for signing and verifying MAR files in libmar and the mar program. r=bsmith
This commit is contained in:
Родитель
029c03124b
Коммит
3458244776
|
@ -43,6 +43,6 @@ VPATH = @srcdir@
|
|||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = src tool
|
||||
DIRS = sign verify src tool
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -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:
|
|
@ -0,0 +1,525 @@
|
|||
/* ***** 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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, *offsetToContent, signatureSectionLength;
|
||||
PRInt64 oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR,
|
||||
signaturePlaceholderOffset, numBytesToCopy,
|
||||
numChunks, i;
|
||||
FILE *fpSrc = NULL, *fpDest = NULL;
|
||||
int rv = -1, oldMar;
|
||||
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 (is_old_mar(src, &oldMar)) {
|
||||
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 (!oldMar) {
|
||||
/* 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 (oldMar) {
|
||||
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 (oldMar) {
|
||||
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;
|
||||
}
|
||||
while (indexBufLoc != (indexBuf + indexLength)) {
|
||||
/* Adjust the offset */
|
||||
offsetToContent = (PRUint32 *)indexBufLoc;
|
||||
*offsetToContent = ntohl(*offsetToContent);
|
||||
if (oldMar) {
|
||||
*offsetToContent += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
|
||||
}
|
||||
*offsetToContent += signatureSectionLength;
|
||||
*offsetToContent = htonl(*offsetToContent);
|
||||
/* Skip past the offset, length, and flags */
|
||||
indexBufLoc += 3 * sizeof(PRUint32);
|
||||
indexBufLoc += strlen(indexBufLoc) + 1;
|
||||
}
|
||||
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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -57,6 +57,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;
|
||||
|
||||
/**
|
||||
|
@ -137,6 +144,25 @@ int mar_create(const char *dest, int numfiles, char **files);
|
|||
*/
|
||||
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
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* ***** 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 if the MAR file is new or old.
|
||||
*
|
||||
* @param path The path of the MAR file to check.
|
||||
* @param oldMar An out parameter specifying if the MAR file is new or old.
|
||||
* @return A non-zero value if an error occurred and the information
|
||||
cannot be determined.
|
||||
*/
|
||||
int is_old_mar(const char *path, int *oldMar);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MAR_CMDLINE_H__ */
|
|
@ -40,10 +40,9 @@
|
|||
#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.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <winsock2.h>
|
||||
|
@ -127,7 +126,8 @@ static int mar_concat_file(FILE *fp, const char *path) {
|
|||
|
||||
int mar_create(const char *dest, int num_files, char **files) {
|
||||
struct MarItemStack stack;
|
||||
PRUint32 offset_to_index = 0, size_of_index;
|
||||
PRUint32 offset_to_index = 0, size_of_index, num_signatures;
|
||||
PRUint64 size_of_entire_MAR = 0;
|
||||
struct stat st;
|
||||
FILE *fp;
|
||||
int i, rv = -1;
|
||||
|
@ -145,7 +145,21 @@ 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(num_signatures) +
|
||||
sizeof(offset_to_index) +
|
||||
sizeof(size_of_entire_MAR);
|
||||
|
||||
/* We will circle back on this at the end of the MAR creation to fill it */
|
||||
if (fwrite(&size_of_entire_MAR, sizeof(size_of_entire_MAR), 1, fp) != 1) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Write out the number of signatures, for now only at most 1 is supported */
|
||||
num_signatures = 0;
|
||||
if (fwrite(&num_signatures, sizeof(num_signatures), 1, fp) != 1) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_files; ++i) {
|
||||
if (stat(files[i], &st)) {
|
||||
|
@ -168,12 +182,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);
|
||||
|
||||
size_of_entire_MAR = ((PRUint64)stack.last_offset) +
|
||||
stack.size_used +
|
||||
sizeof(size_of_index);
|
||||
size_of_entire_MAR = HOST_TO_NETWORK64(size_of_entire_MAR);
|
||||
if (fwrite(&size_of_entire_MAR, sizeof(size_of_entire_MAR), 1, fp) != 1)
|
||||
goto failure;
|
||||
size_of_entire_MAR = NETWORK_TO_HOST64(size_of_entire_MAR);
|
||||
|
||||
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,71 @@
|
|||
#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
|
||||
|
||||
#define MAR_ITEM_SIZE(namelen) (3*sizeof(PRUint32) + (namelen) + 1)
|
||||
|
||||
/* 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;
|
||||
|
@ -291,3 +283,68 @@ int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
|
|||
|
||||
return fread(buf, 1, nr, mar->fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the MAR file is new or old.
|
||||
*
|
||||
* @param path The path of the MAR file to check.
|
||||
* @param oldMar An out parameter specifying if the MAR file is new or old.
|
||||
* @return A non-zero value if an error occurred and the information
|
||||
cannot be determined.
|
||||
*/
|
||||
int is_old_mar(const char *path, int *oldMar)
|
||||
{
|
||||
PRUint32 offsetToIndex, offsetToContent, oldPos;
|
||||
FILE *fp;
|
||||
|
||||
if (!oldMar) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
oldPos = ftell(fp);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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 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 (offsetToContent == MAR_ID_SIZE + sizeof(PRUint32)) {
|
||||
*oldMar = 1;
|
||||
} else {
|
||||
*oldMar = 0;
|
||||
}
|
||||
|
||||
/* Restore back our old position */
|
||||
if (fseek(fp, oldPos, SEEK_SET)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -51,23 +51,37 @@ 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)
|
||||
|
||||
HOST_CFLAGS += -DNO_SIGN_VERIFY
|
||||
|
||||
HOST_CSRCS = \
|
||||
mar.c \
|
||||
$(NULL)
|
||||
HOST_CSRCS = \
|
||||
mar.c \
|
||||
$(NULL)
|
||||
CSRCS = $(HOST_CSRCS)
|
||||
|
||||
HOST_LIBS = $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX)
|
||||
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,46 @@
|
|||
* ***** 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 [-C workingDir] {-c|-x|-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");
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
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,17 +96,59 @@ static int mar_test(const char *path) {
|
|||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int command;
|
||||
char *NSSConfigDir = NULL;
|
||||
char *certName = NULL;
|
||||
#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')) {
|
||||
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;
|
||||
} else {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argv[1][0] != '-') {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (argv[1][1]) {
|
||||
|
@ -90,6 +158,76 @@ int main(int argc, char **argv) {
|
|||
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)) {
|
||||
int oldMar = 0;
|
||||
free(certBuffer);
|
||||
|
||||
/* Determine if the source MAR file has the new fields for signing or not */
|
||||
if (is_old_mar(argv[2], &oldMar)) {
|
||||
fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
|
||||
} else if (oldMar) {
|
||||
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]);
|
||||
#endif /* endif NO_SIGN_VERIFY disabled */
|
||||
|
||||
default:
|
||||
print_usage();
|
||||
return -1;
|
||||
|
|
|
@ -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:
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -0,0 +1,445 @@
|
|||
/* ***** 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) {
|
||||
fclose(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;
|
||||
}
|
|
@ -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
|
||||
|
@ -139,4 +140,9 @@ 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)
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче