Bug 708690 - libmar enhancements for product information blocks. r=rstrong

This commit is contained in:
Brian R. Bondy 2012-02-24 16:29:41 -05:00
parent 2f4e84f6b1
commit 23505aa999
8 changed files with 657 additions and 73 deletions

View File

@ -230,7 +230,7 @@ mar_repackage_and_sign(const char *NSSConfigDir,
signaturePlaceholderOffset, numBytesToCopy,
numChunks, i;
FILE *fpSrc = NULL, *fpDest = NULL;
int rv = -1, oldMar;
int rv = -1, hasSignatureBlock;
SGNContext *ctx = NULL;
SECItem secItem;
char buf[BLOCKSIZE];
@ -268,7 +268,7 @@ mar_repackage_and_sign(const char *NSSConfigDir,
}
/* Determine if the source MAR file has the new fields for signing or not */
if (is_old_mar(src, &oldMar)) {
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;
}
@ -299,7 +299,7 @@ mar_repackage_and_sign(const char *NSSConfigDir,
goto failure;
}
if (!oldMar) {
if (hasSignatureBlock) {
/* Get the MAR length and adjust its size */
if (fread(&sizeOfEntireMAR,
sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) {
@ -338,7 +338,7 @@ mar_repackage_and_sign(const char *NSSConfigDir,
sizeof(signatureLength) +
signatureLength;
dstOffsetToIndex = offsetToIndex;
if (oldMar) {
if (!hasSignatureBlock) {
dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
}
dstOffsetToIndex += signatureSectionLength;
@ -353,7 +353,7 @@ mar_repackage_and_sign(const char *NSSConfigDir,
/* Write out the new MAR file size */
sizeOfEntireMAR += signatureSectionLength;
if (oldMar) {
if (!hasSignatureBlock) {
sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
}
@ -445,7 +445,7 @@ mar_repackage_and_sign(const char *NSSConfigDir,
/* Adjust the offset */
offsetToContent = (PRUint32 *)indexBufLoc;
*offsetToContent = ntohl(*offsetToContent);
if (oldMar) {
if (!hasSignatureBlock) {
*offsetToContent += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
}
*offsetToContent += signatureSectionLength;

View File

@ -46,6 +46,11 @@
extern "C" {
#endif
struct ProductInformationBlock {
const char *MARChannelID;
const char *productVersion;
};
/**
* The MAR item data structure.
*/
@ -71,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);
@ -79,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);
@ -97,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);
@ -107,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);
@ -119,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,
@ -132,15 +137,19 @@ 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);
@ -163,6 +172,18 @@ int mar_verify_signatureW(MarFile *mar,
PRUint32 sizeOfCertData);
#endif
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
mar_read_product_info_block(MarFile *mar,
struct ProductInformationBlock *infoBlock);
#ifdef __cplusplus
}
#endif

View File

@ -46,14 +46,30 @@ 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.
* 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 is_old_mar(const char *path, int *oldMar);
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.
@ -75,6 +91,18 @@ int mar_verify_signature(const char *pathToMAR,
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);
#ifdef __cplusplus
}
#endif

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
* Brian R. Bondy <netzen@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -42,6 +43,7 @@
#include <stdlib.h>
#include <string.h>
#include "mar_private.h"
#include "mar_cmdline.h"
#include "mar.h"
#ifdef XP_WIN
@ -124,10 +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, num_signatures;
PRUint64 size_of_entire_MAR = 0;
PRUint32 offset_to_index = 0, size_of_index,
numSignatures, numAdditionalSections;
PRUint64 sizeOfEntireMAR = 0;
struct stat st;
FILE *fp;
int i, rv = -1;
@ -146,18 +344,32 @@ int mar_create(const char *dest, int num_files, char **files) {
goto failure;
stack.last_offset = MAR_ID_SIZE +
sizeof(num_signatures) +
sizeof(offset_to_index) +
sizeof(size_of_entire_MAR);
sizeof(numSignatures) +
sizeof(numAdditionalSections) +
sizeof(sizeOfEntireMAR);
/* 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) {
if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 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) {
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;
}
@ -196,13 +408,13 @@ int mar_create(const char *dest, int num_files, char **files) {
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)
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;
size_of_entire_MAR = NETWORK_TO_HOST64(size_of_entire_MAR);
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
rv = 0;
failure:

View File

@ -78,8 +78,16 @@ PR_STATIC_ASSERT(sizeof(BLOCKSIZE) < \
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. */

View File

@ -235,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;
@ -284,29 +403,81 @@ 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");
/**
* 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;
}
oldPos = ftell(fp);
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)) {
@ -314,10 +485,24 @@ int is_old_mar(const char *path, int *oldMar)
}
/* Read the offset to the index. */
if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1)
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. */
@ -329,22 +514,81 @@ int is_old_mar(const char *path, int *oldMar)
return -1;
}
/* Read the offset to content field. */
if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 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 (offsetToContent == MAR_ID_SIZE + sizeof(PRUint32)) {
*oldMar = 1;
} else {
*oldMar = 0;
if (hasSignatureBlock) {
if (offsetToContent == MAR_ID_SIZE + sizeof(PRUint32)) {
*hasSignatureBlock = 0;
} else {
*hasSignatureBlock = 1;
}
}
/* Restore back our old position */
if (fseek(fp, oldPos, SEEK_SET)) {
/* If the caller doesn't care about the product info block
value, then just return */
if (!hasAdditionalBlocks) {
return 0;
}
/* Skip to the start of the signature block */
if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
return -1;
}
/* Get the number of signatures */
if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
return -1;
}
signatureCount = ntohl(signatureCount);
/* Check that we have less than the max amount of signatures so we don't
waste too much of either updater's or signmar's time. */
if (signatureCount > MAX_SIGNATURES) {
return -1;
}
/* Skip past the whole signature block */
for (i = 0; i < signatureCount; i++) {
/* Skip past the signature algorithm ID */
if (fseek(fp, sizeof(PRUint32), SEEK_CUR)) {
return -1;
}
/* Read the signature length and skip past the signature */
if (fread(&signatureLen, sizeof(PRUint32), 1, fp) != 1) {
return -1;
}
signatureLen = ntohl(signatureLen);
if (fseek(fp, signatureLen, SEEK_CUR)) {
return -1;
}
}
if (ftell(fp) == offsetToContent) {
*hasAdditionalBlocks = 0;
} else {
if (numAdditionalBlocks) {
/* We have an additional block, so read in the number of additional blocks
and set the offset. */
*hasAdditionalBlocks = 1;
if (fread(numAdditionalBlocks, sizeof(PRUint32), 1, fp) != 1) {
return -1;
}
*numAdditionalBlocks = ntohl(*numAdditionalBlocks);
if (offsetAdditionalBlocks) {
*offsetAdditionalBlocks = ftell(fp);
}
} else if (offsetAdditionalBlocks) {
/* numAdditionalBlocks is not specified but offsetAdditionalBlocks
is, so fill it! */
*offsetAdditionalBlocks = ftell(fp) + sizeof(PRUint32);
}
}
return 0;
}

View File

@ -54,7 +54,15 @@ endif
HOST_PROGRAM = mar$(HOST_BIN_SUFFIX)
PROGRAM = signmar$(BIN_SUFFIX)
HOST_CFLAGS += -DNO_SIGN_VERIFY
DEFINES += \
-DMAR_CHANNEL_ID='"$(MAR_CHANNEL_ID)"' \
-DMOZ_APP_VERSION='"$(MOZ_APP_VERSION)"' \
$(NULL)
HOST_CFLAGS += \
-DNO_SIGN_VERIFY \
$(DEFINES) \
$(NULL)
HOST_CSRCS = \
mar.c \

View File

@ -60,7 +60,8 @@ int mar_repackage_and_sign(const char *NSSConfigDir,
static void print_usage() {
printf("usage:\n");
printf(" mar [-C workingDir] {-c|-x|-t} archive.mar [files...]\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");
@ -72,6 +73,8 @@ static void print_usage() {
"-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,
@ -98,6 +101,8 @@ static int mar_test(const char *path) {
int main(int argc, char **argv) {
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;
@ -114,7 +119,8 @@ int main(int argc, char **argv) {
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] == 'v' || argv[1][1] == 's' ||
argv[1][1] == 'i' || argv[1][1] == 'T')) {
break;
/* -C workingdirectory */
} else if (argv[1][0] == '-' && argv[1][1] == 'C') {
@ -140,7 +146,18 @@ int main(int argc, char **argv) {
certName = argv[2];
argv += 2;
argc -= 2;
} else {
/* 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;
}
@ -152,10 +169,56 @@ int main(int argc, char **argv) {
}
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]);
@ -190,13 +253,13 @@ int main(int argc, char **argv) {
CloseHandle(certFile);
if (mar_verify_signature(argv[2], certBuffer, fileSize, NULL)) {
int oldMar = 0;
/* Determine if the source MAR file has the new fields for signing */
int hasSignatureBlock;
free(certBuffer);
/* Determine if the source MAR file has the new fields for signing or not */
if (is_old_mar(argv[2], &oldMar)) {
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 (oldMar) {
} else if (!hasSignatureBlock) {
fprintf(stderr, "ERROR: The MAR file is in the old format so has"
" no signature to verify.\n");
}