modules/libmar/src/mar_create.c

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <sys/types.h>
     8 #include <sys/stat.h>
     9 #include <fcntl.h>
    10 #include <stdlib.h>
    11 #include <string.h>
    12 #include "mar_private.h"
    13 #include "mar_cmdline.h"
    14 #include "mar.h"
    16 #ifdef XP_WIN
    17 #include <winsock2.h>
    18 #else
    19 #include <netinet/in.h>
    20 #include <unistd.h>
    21 #endif
    23 struct MarItemStack {
    24   void *head;
    25   uint32_t size_used;
    26   uint32_t size_allocated;
    27   uint32_t last_offset;
    28 };
    30 /**
    31  * Push a new item onto the stack of items.  The stack is a single block
    32  * of memory.
    33  */
    34 static int mar_push(struct MarItemStack *stack, uint32_t length, uint32_t flags,
    35                     const char *name) {
    36   int namelen;
    37   uint32_t n_offset, n_length, n_flags;
    38   uint32_t size;
    39   char *data;
    41   namelen = strlen(name);
    42   size = MAR_ITEM_SIZE(namelen);
    44   if (stack->size_allocated - stack->size_used < size) {
    45     /* increase size of stack */
    46     size_t size_needed = ROUND_UP(stack->size_used + size, BLOCKSIZE);
    47     stack->head = realloc(stack->head, size_needed);
    48     if (!stack->head)
    49       return -1;
    50     stack->size_allocated = size_needed;
    51   }
    53   data = (((char *) stack->head) + stack->size_used);
    55   n_offset = htonl(stack->last_offset);
    56   n_length = htonl(length);
    57   n_flags = htonl(flags);
    59   memcpy(data, &n_offset, sizeof(n_offset));
    60   data += sizeof(n_offset);
    62   memcpy(data, &n_length, sizeof(n_length));
    63   data += sizeof(n_length);
    65   memcpy(data, &n_flags, sizeof(n_flags));
    66   data += sizeof(n_flags);
    68   memcpy(data, name, namelen + 1);
    70   stack->size_used += size;
    71   stack->last_offset += length;
    72   return 0;
    73 }
    75 static int mar_concat_file(FILE *fp, const char *path) {
    76   FILE *in;
    77   char buf[BLOCKSIZE];
    78   size_t len;
    79   int rv = 0;
    81   in = fopen(path, "rb");
    82   if (!in) {
    83     fprintf(stderr, "ERROR: could not open file in mar_concat_file()\n");
    84     perror(path);
    85     return -1;
    86   }
    88   while ((len = fread(buf, 1, BLOCKSIZE, in)) > 0) {
    89     if (fwrite(buf, len, 1, fp) != 1) {
    90       rv = -1;
    91       break;
    92     }
    93   }
    95   fclose(in);
    96   return rv;
    97 }
    99 /**
   100  * Writes out the product information block to the specified file.
   101  *
   102  * @param fp           The opened MAR file being created.
   103  * @param stack        A pointer to the MAR item stack being used to create 
   104  *                     the MAR
   105  * @param infoBlock    The product info block to store in the file.
   106  * @return 0 on success.
   107 */
   108 static int
   109 mar_concat_product_info_block(FILE *fp, 
   110                               struct MarItemStack *stack,
   111                               struct ProductInformationBlock *infoBlock)
   112 {
   113   char buf[PIB_MAX_MAR_CHANNEL_ID_SIZE + PIB_MAX_PRODUCT_VERSION_SIZE];
   114   uint32_t additionalBlockID = 1, infoBlockSize, unused;
   115   if (!fp || !infoBlock || 
   116       !infoBlock->MARChannelID ||
   117       !infoBlock->productVersion) {
   118     return -1;
   119   }
   121   /* The MAR channel name must be < 64 bytes per the spec */
   122   if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE) {
   123     return -1;
   124   }
   126   /* The product version must be < 32 bytes per the spec */
   127   if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE) {
   128     return -1;
   129   }
   131   /* Although we don't need the product information block size to include the
   132      maximum MAR channel name and product version, we allocate the maximum
   133      amount to make it easier to modify the MAR file for repurposing MAR files
   134      to different MAR channels. + 2 is for the NULL terminators. */
   135   infoBlockSize = sizeof(infoBlockSize) +
   136                   sizeof(additionalBlockID) +
   137                   PIB_MAX_MAR_CHANNEL_ID_SIZE +
   138                   PIB_MAX_PRODUCT_VERSION_SIZE + 2;
   139   if (stack) {
   140     stack->last_offset += infoBlockSize;
   141   }
   143   /* Write out the product info block size */
   144   infoBlockSize = htonl(infoBlockSize);
   145   if (fwrite(&infoBlockSize, 
   146       sizeof(infoBlockSize), 1, fp) != 1) {
   147     return -1;
   148   }
   149   infoBlockSize = ntohl(infoBlockSize);
   151   /* Write out the product info block ID */
   152   additionalBlockID = htonl(additionalBlockID);
   153   if (fwrite(&additionalBlockID, 
   154       sizeof(additionalBlockID), 1, fp) != 1) {
   155     return -1;
   156   }
   157   additionalBlockID = ntohl(additionalBlockID);
   159   /* Write out the channel name and NULL terminator */
   160   if (fwrite(infoBlock->MARChannelID, 
   161       strlen(infoBlock->MARChannelID) + 1, 1, fp) != 1) {
   162     return -1;
   163   }
   165   /* Write out the product version string and NULL terminator */
   166   if (fwrite(infoBlock->productVersion, 
   167       strlen(infoBlock->productVersion) + 1, 1, fp) != 1) {
   168     return -1;
   169   }
   171   /* Write out the rest of the block that is unused */
   172   unused = infoBlockSize - (sizeof(infoBlockSize) +
   173                             sizeof(additionalBlockID) +
   174                             strlen(infoBlock->MARChannelID) + 
   175                             strlen(infoBlock->productVersion) + 2);
   176   memset(buf, 0, sizeof(buf));
   177   if (fwrite(buf, unused, 1, fp) != 1) {
   178     return -1;
   179   }
   180   return 0;
   181 }
   183 /** 
   184  * Refreshes the product information block with the new information.
   185  * The input MAR must not be signed or the function call will fail.
   186  * 
   187  * @param path             The path to the MAR file whose product info block
   188  *                         should be refreshed.
   189  * @param infoBlock        Out parameter for where to store the result to
   190  * @return 0 on success, -1 on failure
   191 */
   192 int
   193 refresh_product_info_block(const char *path,
   194                            struct ProductInformationBlock *infoBlock)
   195 {
   196   FILE *fp ;
   197   int rv;
   198   uint32_t numSignatures, additionalBlockSize, additionalBlockID,
   199     offsetAdditionalBlocks, numAdditionalBlocks, i;
   200   int additionalBlocks, hasSignatureBlock;
   201   int64_t oldPos;
   203   rv = get_mar_file_info(path, 
   204                          &hasSignatureBlock,
   205                          &numSignatures,
   206                          &additionalBlocks,
   207                          &offsetAdditionalBlocks,
   208                          &numAdditionalBlocks);
   209   if (rv) {
   210     fprintf(stderr, "ERROR: Could not obtain MAR information.\n");
   211     return -1;
   212   }
   214   if (hasSignatureBlock && numSignatures) {
   215     fprintf(stderr, "ERROR: Cannot refresh a signed MAR\n");
   216     return -1;
   217   }
   219   fp = fopen(path, "r+b");
   220   if (!fp) {
   221     fprintf(stderr, "ERROR: could not open target file: %s\n", path);
   222     return -1;
   223   }
   225   if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET)) {
   226     fprintf(stderr, "ERROR: could not seek to additional blocks\n");
   227     fclose(fp);
   228     return -1;
   229   }
   231   for (i = 0; i < numAdditionalBlocks; ++i) {
   232     /* Get the position of the start of this block */
   233     oldPos = ftello(fp);
   235     /* Read the additional block size */
   236     if (fread(&additionalBlockSize, 
   237               sizeof(additionalBlockSize), 
   238               1, fp) != 1) {
   239       return -1;
   240     }
   241     additionalBlockSize = ntohl(additionalBlockSize);
   243     /* Read the additional block ID */
   244     if (fread(&additionalBlockID, 
   245               sizeof(additionalBlockID), 
   246               1, fp) != 1) {
   247       return -1;
   248     }
   249     additionalBlockID = ntohl(additionalBlockID);
   251     if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
   252       if (fseeko(fp, oldPos, SEEK_SET)) {
   253         fprintf(stderr, "Could not seek back to Product Information Block\n");
   254         fclose(fp);
   255         return -1;
   256       }
   258       if (mar_concat_product_info_block(fp, NULL, infoBlock)) {
   259         fprintf(stderr, "Could not concat Product Information Block\n");
   260         fclose(fp);
   261         return -1;
   262       }
   264       fclose(fp);
   265       return 0;
   266     } else {
   267       /* This is not the additional block you're looking for. Move along. */
   268       if (fseek(fp, additionalBlockSize, SEEK_CUR)) {
   269         fprintf(stderr, "ERROR: Could not seek past current block.\n");
   270         fclose(fp);
   271         return -1;
   272       }
   273     }
   274   }
   276   /* If we had a product info block we would have already returned */
   277   fclose(fp);
   278   fprintf(stderr, "ERROR: Could not refresh because block does not exist\n");
   279   return -1;
   280 }
   282 /**
   283  * Create a MAR file from a set of files.
   284  * @param dest      The path to the file to create.  This path must be
   285  *                  compatible with fopen.
   286  * @param numfiles  The number of files to store in the archive.
   287  * @param files     The list of null-terminated file paths.  Each file
   288  *                  path must be compatible with fopen.
   289  * @param infoBlock The information to store in the product information block.
   290  * @return A non-zero value if an error occurs.
   291  */
   292 int mar_create(const char *dest, int 
   293                num_files, char **files, 
   294                struct ProductInformationBlock *infoBlock) {
   295   struct MarItemStack stack;
   296   uint32_t offset_to_index = 0, size_of_index, 
   297     numSignatures, numAdditionalSections;
   298   uint64_t sizeOfEntireMAR = 0;
   299   struct stat st;
   300   FILE *fp;
   301   int i, rv = -1;
   303   memset(&stack, 0, sizeof(stack));
   305   fp = fopen(dest, "wb");
   306   if (!fp) {
   307     fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
   308     return -1;
   309   }
   311   if (fwrite(MAR_ID, MAR_ID_SIZE, 1, fp) != 1)
   312     goto failure;
   313   if (fwrite(&offset_to_index, sizeof(uint32_t), 1, fp) != 1)
   314     goto failure;
   316   stack.last_offset = MAR_ID_SIZE + 
   317                       sizeof(offset_to_index) +
   318                       sizeof(numSignatures) + 
   319                       sizeof(numAdditionalSections) +
   320                       sizeof(sizeOfEntireMAR);
   322   /* We will circle back on this at the end of the MAR creation to fill it */
   323   if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) {
   324     goto failure;
   325   }
   327   /* Write out the number of signatures, for now only at most 1 is supported */
   328   numSignatures = 0;
   329   if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) {
   330     goto failure;
   331   }
   333   /* Write out the number of additional sections, for now just 1 
   334      for the product info block */
   335   numAdditionalSections = htonl(1);
   336   if (fwrite(&numAdditionalSections, 
   337              sizeof(numAdditionalSections), 1, fp) != 1) {
   338     goto failure;
   339   }
   340   numAdditionalSections = ntohl(numAdditionalSections);
   342   if (mar_concat_product_info_block(fp, &stack, infoBlock)) {
   343     goto failure;
   344   }
   346   for (i = 0; i < num_files; ++i) {
   347     if (stat(files[i], &st)) {
   348       fprintf(stderr, "ERROR: file not found: %s\n", files[i]);
   349       goto failure;
   350     }
   352     if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i]))
   353       goto failure;
   355     /* concatenate input file to archive */
   356     if (mar_concat_file(fp, files[i]))
   357       goto failure;
   358   }
   360   /* write out the index (prefixed with length of index) */
   361   size_of_index = htonl(stack.size_used);
   362   if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1)
   363     goto failure;
   364   if (fwrite(stack.head, stack.size_used, 1, fp) != 1)
   365     goto failure;
   367   /* To protect against invalid MAR files, we assumes that the MAR file 
   368      size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
   369   if (ftell(fp) > MAX_SIZE_OF_MAR_FILE) {
   370     goto failure;
   371   }
   373   /* write out offset to index file in network byte order */
   374   offset_to_index = htonl(stack.last_offset);
   375   if (fseek(fp, MAR_ID_SIZE, SEEK_SET))
   376     goto failure;
   377   if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1)
   378     goto failure;
   379   offset_to_index = ntohl(stack.last_offset);
   381   sizeOfEntireMAR = ((uint64_t)stack.last_offset) +
   382                     stack.size_used +
   383                     sizeof(size_of_index);
   384   sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
   385   if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1)
   386     goto failure;
   387   sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
   389   rv = 0;
   390 failure: 
   391   if (stack.head)
   392     free(stack.head);
   393   fclose(fp);
   394   if (rv)
   395     remove(dest);
   396   return rv;
   397 }

mercurial