mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 708690 - libmar enhancements for product information blocks. r=rstrong
This commit is contained in:
parent
2f4e84f6b1
commit
23505aa999
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user