Sat, 03 Jan 2015 20:18:00 +0100
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.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include <string.h> |
michael@0 | 8 | #include <stdlib.h> |
michael@0 | 9 | #include <fcntl.h> |
michael@0 | 10 | #include "bzlib.h" |
michael@0 | 11 | #include "archivereader.h" |
michael@0 | 12 | #include "errors.h" |
michael@0 | 13 | #ifdef XP_WIN |
michael@0 | 14 | #include "nsAlgorithm.h" // Needed by nsVersionComparator.cpp |
michael@0 | 15 | #include "updatehelper.h" |
michael@0 | 16 | #endif |
michael@0 | 17 | |
michael@0 | 18 | #define UPDATER_NO_STRING_GLUE_STL |
michael@0 | 19 | #include "nsVersionComparator.cpp" |
michael@0 | 20 | #undef UPDATER_NO_STRING_GLUE_STL |
michael@0 | 21 | |
michael@0 | 22 | #if defined(XP_UNIX) |
michael@0 | 23 | # include <sys/types.h> |
michael@0 | 24 | #elif defined(XP_WIN) |
michael@0 | 25 | # include <io.h> |
michael@0 | 26 | #endif |
michael@0 | 27 | |
michael@0 | 28 | static int inbuf_size = 262144; |
michael@0 | 29 | static int outbuf_size = 262144; |
michael@0 | 30 | static char *inbuf = nullptr; |
michael@0 | 31 | static char *outbuf = nullptr; |
michael@0 | 32 | |
michael@0 | 33 | #ifdef XP_WIN |
michael@0 | 34 | #include "resource.h" |
michael@0 | 35 | |
michael@0 | 36 | /** |
michael@0 | 37 | * Obtains the data of the specified resource name and type. |
michael@0 | 38 | * |
michael@0 | 39 | * @param name The name ID of the resource |
michael@0 | 40 | * @param type The type ID of the resource |
michael@0 | 41 | * @param data Out parameter which sets the pointer to a buffer containing |
michael@0 | 42 | * the needed data. |
michael@0 | 43 | * @param size Out parameter which sets the size of the returned data buffer |
michael@0 | 44 | * @return TRUE on success |
michael@0 | 45 | */ |
michael@0 | 46 | BOOL |
michael@0 | 47 | LoadFileInResource(int name, int type, const uint8_t *&data, uint32_t& size) |
michael@0 | 48 | { |
michael@0 | 49 | HMODULE handle = GetModuleHandle(nullptr); |
michael@0 | 50 | if (!handle) { |
michael@0 | 51 | return FALSE; |
michael@0 | 52 | } |
michael@0 | 53 | |
michael@0 | 54 | HRSRC resourceInfoBlockHandle = FindResource(handle, |
michael@0 | 55 | MAKEINTRESOURCE(name), |
michael@0 | 56 | MAKEINTRESOURCE(type)); |
michael@0 | 57 | if (!resourceInfoBlockHandle) { |
michael@0 | 58 | FreeLibrary(handle); |
michael@0 | 59 | return FALSE; |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | HGLOBAL resourceHandle = LoadResource(handle, resourceInfoBlockHandle); |
michael@0 | 63 | if (!resourceHandle) { |
michael@0 | 64 | FreeLibrary(handle); |
michael@0 | 65 | return FALSE; |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | size = SizeofResource(handle, resourceInfoBlockHandle); |
michael@0 | 69 | data = static_cast<const uint8_t*>(::LockResource(resourceHandle)); |
michael@0 | 70 | FreeLibrary(handle); |
michael@0 | 71 | return TRUE; |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | /** |
michael@0 | 75 | * Performs a verification on the opened MAR file with the passed in |
michael@0 | 76 | * certificate name ID and type ID. |
michael@0 | 77 | * |
michael@0 | 78 | * @param archive The MAR file to verify the signature on |
michael@0 | 79 | * @param name The name ID of the resource |
michael@0 | 80 | * @param type THe type ID of the resource |
michael@0 | 81 | * @return OK on success, CERT_LOAD_ERROR or CERT_VERIFY_ERROR on failure. |
michael@0 | 82 | */ |
michael@0 | 83 | int |
michael@0 | 84 | VerifyLoadedCert(MarFile *archive, int name, int type) |
michael@0 | 85 | { |
michael@0 | 86 | uint32_t size = 0; |
michael@0 | 87 | const uint8_t *data = nullptr; |
michael@0 | 88 | if (!LoadFileInResource(name, type, data, size) || !data || !size) { |
michael@0 | 89 | return CERT_LOAD_ERROR; |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | if (mar_verify_signaturesW(archive, &data, &size, 1)) { |
michael@0 | 93 | return CERT_VERIFY_ERROR; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | return OK; |
michael@0 | 97 | } |
michael@0 | 98 | #endif |
michael@0 | 99 | |
michael@0 | 100 | |
michael@0 | 101 | /** |
michael@0 | 102 | * Performs a verification on the opened MAR file. Both the primary and backup |
michael@0 | 103 | * keys stored are stored in the current process and at least the primary key |
michael@0 | 104 | * will be tried. Success will be returned as long as one of the two |
michael@0 | 105 | * signatures verify. |
michael@0 | 106 | * |
michael@0 | 107 | * @return OK on success |
michael@0 | 108 | */ |
michael@0 | 109 | int |
michael@0 | 110 | ArchiveReader::VerifySignature() |
michael@0 | 111 | { |
michael@0 | 112 | if (!mArchive) { |
michael@0 | 113 | return ARCHIVE_NOT_OPEN; |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | #ifdef XP_WIN |
michael@0 | 117 | // If the fallback key exists we're running an XPCShell test and we should |
michael@0 | 118 | // use the XPCShell specific cert for the signed MAR. |
michael@0 | 119 | int rv; |
michael@0 | 120 | if (DoesFallbackKeyExist()) { |
michael@0 | 121 | rv = VerifyLoadedCert(mArchive, IDR_XPCSHELL_CERT, TYPE_CERT); |
michael@0 | 122 | } else { |
michael@0 | 123 | rv = VerifyLoadedCert(mArchive, IDR_PRIMARY_CERT, TYPE_CERT); |
michael@0 | 124 | if (rv != OK) { |
michael@0 | 125 | rv = VerifyLoadedCert(mArchive, IDR_BACKUP_CERT, TYPE_CERT); |
michael@0 | 126 | } |
michael@0 | 127 | } |
michael@0 | 128 | return rv; |
michael@0 | 129 | #else |
michael@0 | 130 | return OK; |
michael@0 | 131 | #endif |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | /** |
michael@0 | 135 | * Verifies that the MAR file matches the current product, channel, and version |
michael@0 | 136 | * |
michael@0 | 137 | * @param MARChannelID The MAR channel name to use, only updates from MARs |
michael@0 | 138 | * with a matching MAR channel name will succeed. |
michael@0 | 139 | * If an empty string is passed, no check will be done |
michael@0 | 140 | * for the channel name in the product information block. |
michael@0 | 141 | * If a comma separated list of values is passed then |
michael@0 | 142 | * one value must match. |
michael@0 | 143 | * @param appVersion The application version to use, only MARs with an |
michael@0 | 144 | * application version >= to appVersion will be applied. |
michael@0 | 145 | * @return OK on success |
michael@0 | 146 | * COULD_NOT_READ_PRODUCT_INFO_BLOCK if the product info block |
michael@0 | 147 | * could not be read. |
michael@0 | 148 | * MARCHANNEL_MISMATCH_ERROR if update-settings.ini's MAR |
michael@0 | 149 | * channel ID doesn't match the MAR |
michael@0 | 150 | * file's MAR channel ID. |
michael@0 | 151 | * VERSION_DOWNGRADE_ERROR if the application version for |
michael@0 | 152 | * this updater is newer than the |
michael@0 | 153 | * one in the MAR. |
michael@0 | 154 | */ |
michael@0 | 155 | int |
michael@0 | 156 | ArchiveReader::VerifyProductInformation(const char *MARChannelID, |
michael@0 | 157 | const char *appVersion) |
michael@0 | 158 | { |
michael@0 | 159 | if (!mArchive) { |
michael@0 | 160 | return ARCHIVE_NOT_OPEN; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | ProductInformationBlock productInfoBlock; |
michael@0 | 164 | int rv = mar_read_product_info_block(mArchive, |
michael@0 | 165 | &productInfoBlock); |
michael@0 | 166 | if (rv != OK) { |
michael@0 | 167 | return COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR; |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | // Only check the MAR channel name if specified, it should be passed in from |
michael@0 | 171 | // the update-settings.ini file. |
michael@0 | 172 | if (MARChannelID && strlen(MARChannelID)) { |
michael@0 | 173 | // Check for at least one match in the comma separated list of values. |
michael@0 | 174 | const char *delimiter = " ,\t"; |
michael@0 | 175 | // Make a copy of the string in case a read only memory buffer |
michael@0 | 176 | // was specified. strtok modifies the input buffer. |
michael@0 | 177 | char channelCopy[512] = { 0 }; |
michael@0 | 178 | strncpy(channelCopy, MARChannelID, sizeof(channelCopy) - 1); |
michael@0 | 179 | char *channel = strtok(channelCopy, delimiter); |
michael@0 | 180 | rv = MAR_CHANNEL_MISMATCH_ERROR; |
michael@0 | 181 | while(channel) { |
michael@0 | 182 | if (!strcmp(channel, productInfoBlock.MARChannelID)) { |
michael@0 | 183 | rv = OK; |
michael@0 | 184 | break; |
michael@0 | 185 | } |
michael@0 | 186 | channel = strtok(nullptr, delimiter); |
michael@0 | 187 | } |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | if (rv == OK) { |
michael@0 | 191 | /* Compare both versions to ensure we don't have a downgrade |
michael@0 | 192 | -1 if appVersion is older than productInfoBlock.productVersion |
michael@0 | 193 | 1 if appVersion is newer than productInfoBlock.productVersion |
michael@0 | 194 | 0 if appVersion is the same as productInfoBlock.productVersion |
michael@0 | 195 | This even works with strings like: |
michael@0 | 196 | - 12.0a1 being older than 12.0a2 |
michael@0 | 197 | - 12.0a2 being older than 12.0b1 |
michael@0 | 198 | - 12.0a1 being older than 12.0 |
michael@0 | 199 | - 12.0 being older than 12.1a1 */ |
michael@0 | 200 | int versionCompareResult = |
michael@0 | 201 | mozilla::CompareVersions(appVersion, productInfoBlock.productVersion); |
michael@0 | 202 | if (1 == versionCompareResult) { |
michael@0 | 203 | rv = VERSION_DOWNGRADE_ERROR; |
michael@0 | 204 | } |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | free((void *)productInfoBlock.MARChannelID); |
michael@0 | 208 | free((void *)productInfoBlock.productVersion); |
michael@0 | 209 | return rv; |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | int |
michael@0 | 213 | ArchiveReader::Open(const NS_tchar *path) |
michael@0 | 214 | { |
michael@0 | 215 | if (mArchive) |
michael@0 | 216 | Close(); |
michael@0 | 217 | |
michael@0 | 218 | if (!inbuf) { |
michael@0 | 219 | inbuf = (char *)malloc(inbuf_size); |
michael@0 | 220 | if (!inbuf) { |
michael@0 | 221 | // Try again with a smaller buffer. |
michael@0 | 222 | inbuf_size = 1024; |
michael@0 | 223 | inbuf = (char *)malloc(inbuf_size); |
michael@0 | 224 | if (!inbuf) |
michael@0 | 225 | return ARCHIVE_READER_MEM_ERROR; |
michael@0 | 226 | } |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | if (!outbuf) { |
michael@0 | 230 | outbuf = (char *)malloc(outbuf_size); |
michael@0 | 231 | if (!outbuf) { |
michael@0 | 232 | // Try again with a smaller buffer. |
michael@0 | 233 | outbuf_size = 1024; |
michael@0 | 234 | outbuf = (char *)malloc(outbuf_size); |
michael@0 | 235 | if (!outbuf) |
michael@0 | 236 | return ARCHIVE_READER_MEM_ERROR; |
michael@0 | 237 | } |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | #ifdef XP_WIN |
michael@0 | 241 | mArchive = mar_wopen(path); |
michael@0 | 242 | #else |
michael@0 | 243 | mArchive = mar_open(path); |
michael@0 | 244 | #endif |
michael@0 | 245 | if (!mArchive) |
michael@0 | 246 | return READ_ERROR; |
michael@0 | 247 | |
michael@0 | 248 | return OK; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | void |
michael@0 | 252 | ArchiveReader::Close() |
michael@0 | 253 | { |
michael@0 | 254 | if (mArchive) { |
michael@0 | 255 | mar_close(mArchive); |
michael@0 | 256 | mArchive = nullptr; |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | if (inbuf) { |
michael@0 | 260 | free(inbuf); |
michael@0 | 261 | inbuf = nullptr; |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | if (outbuf) { |
michael@0 | 265 | free(outbuf); |
michael@0 | 266 | outbuf = nullptr; |
michael@0 | 267 | } |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | int |
michael@0 | 271 | ArchiveReader::ExtractFile(const char *name, const NS_tchar *dest) |
michael@0 | 272 | { |
michael@0 | 273 | const MarItem *item = mar_find_item(mArchive, name); |
michael@0 | 274 | if (!item) |
michael@0 | 275 | return READ_ERROR; |
michael@0 | 276 | |
michael@0 | 277 | #ifdef XP_WIN |
michael@0 | 278 | FILE* fp = _wfopen(dest, L"wb+"); |
michael@0 | 279 | #else |
michael@0 | 280 | int fd = creat(dest, item->flags); |
michael@0 | 281 | if (fd == -1) |
michael@0 | 282 | return WRITE_ERROR; |
michael@0 | 283 | |
michael@0 | 284 | FILE *fp = fdopen(fd, "wb"); |
michael@0 | 285 | #endif |
michael@0 | 286 | if (!fp) |
michael@0 | 287 | return WRITE_ERROR; |
michael@0 | 288 | |
michael@0 | 289 | int rv = ExtractItemToStream(item, fp); |
michael@0 | 290 | |
michael@0 | 291 | fclose(fp); |
michael@0 | 292 | return rv; |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | int |
michael@0 | 296 | ArchiveReader::ExtractFileToStream(const char *name, FILE *fp) |
michael@0 | 297 | { |
michael@0 | 298 | const MarItem *item = mar_find_item(mArchive, name); |
michael@0 | 299 | if (!item) |
michael@0 | 300 | return READ_ERROR; |
michael@0 | 301 | |
michael@0 | 302 | return ExtractItemToStream(item, fp); |
michael@0 | 303 | } |
michael@0 | 304 | |
michael@0 | 305 | int |
michael@0 | 306 | ArchiveReader::ExtractItemToStream(const MarItem *item, FILE *fp) |
michael@0 | 307 | { |
michael@0 | 308 | /* decompress the data chunk by chunk */ |
michael@0 | 309 | |
michael@0 | 310 | bz_stream strm; |
michael@0 | 311 | int offset, inlen, outlen, ret = OK; |
michael@0 | 312 | |
michael@0 | 313 | memset(&strm, 0, sizeof(strm)); |
michael@0 | 314 | if (BZ2_bzDecompressInit(&strm, 0, 0) != BZ_OK) |
michael@0 | 315 | return UNEXPECTED_BZIP_ERROR; |
michael@0 | 316 | |
michael@0 | 317 | offset = 0; |
michael@0 | 318 | for (;;) { |
michael@0 | 319 | if (!item->length) { |
michael@0 | 320 | ret = UNEXPECTED_MAR_ERROR; |
michael@0 | 321 | break; |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | if (offset < (int) item->length && strm.avail_in == 0) { |
michael@0 | 325 | inlen = mar_read(mArchive, item, offset, inbuf, inbuf_size); |
michael@0 | 326 | if (inlen <= 0) |
michael@0 | 327 | return READ_ERROR; |
michael@0 | 328 | offset += inlen; |
michael@0 | 329 | strm.next_in = inbuf; |
michael@0 | 330 | strm.avail_in = inlen; |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | strm.next_out = outbuf; |
michael@0 | 334 | strm.avail_out = outbuf_size; |
michael@0 | 335 | |
michael@0 | 336 | ret = BZ2_bzDecompress(&strm); |
michael@0 | 337 | if (ret != BZ_OK && ret != BZ_STREAM_END) { |
michael@0 | 338 | ret = UNEXPECTED_BZIP_ERROR; |
michael@0 | 339 | break; |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | outlen = outbuf_size - strm.avail_out; |
michael@0 | 343 | if (outlen) { |
michael@0 | 344 | if (fwrite(outbuf, outlen, 1, fp) != 1) { |
michael@0 | 345 | ret = WRITE_ERROR; |
michael@0 | 346 | break; |
michael@0 | 347 | } |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | if (ret == BZ_STREAM_END) { |
michael@0 | 351 | ret = OK; |
michael@0 | 352 | break; |
michael@0 | 353 | } |
michael@0 | 354 | } |
michael@0 | 355 | |
michael@0 | 356 | BZ2_bzDecompressEnd(&strm); |
michael@0 | 357 | return ret; |
michael@0 | 358 | } |