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