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/. */
11 #include "archivereader.h"
14 #include "updatehelper.h"
17 // These are generated at compile time based on the DER file for the channel
19 #ifdef VERIFY_MAR_SIGNATURE
21 #include "../xpcshellCert.h"
23 #include "onlineupdate/primaryCert.h"
24 #include "onlineupdate/secondaryCert.h"
29 #define UPDATER_NO_STRING_GLUE_STL
31 #include "nsVersionComparator.h"
32 #undef UPDATER_NO_STRING_GLUE_STL
35 # include <sys/types.h>
40 static int inbuf_size
= 262144;
41 static int outbuf_size
= 262144;
42 static char *inbuf
= nullptr;
43 static char *outbuf
= nullptr;
46 * Performs a verification on the opened MAR file with the passed in
47 * certificate name ID and type ID.
49 * @param archive The MAR file to verify the signature on.
50 * @param certData The certificate data.
51 * @return OK on success, CERT_VERIFY_ERROR on failure.
53 template<uint32_t SIZE
>
55 VerifyLoadedCert(MarFile
*archive
, const uint8_t (&certData
)[SIZE
])
60 #ifdef VERIFY_MAR_SIGNATURE
61 const uint32_t size
= SIZE
;
62 const uint8_t* const data
= &certData
[0];
63 if (mar_verify_signatures(archive
, &data
, &size
, 1))
65 return CERT_VERIFY_ERROR
;
73 * Performs a verification on the opened MAR file. Both the primary and backup
74 * keys stored are stored in the current process and at least the primary key
75 * will be tried. Success will be returned as long as one of the two
78 * @return OK on success
81 ArchiveReader::VerifySignature()
85 return ARCHIVE_NOT_OPEN
;
88 #ifndef VERIFY_MAR_SIGNATURE
92 int rv
= VerifyLoadedCert(mArchive
, xpcshellCertData
);
94 int rv
= VerifyLoadedCert(mArchive
, primaryCertData
);
97 rv
= VerifyLoadedCert(mArchive
, secondaryCertData
);
105 * Verifies that the MAR file matches the current product, channel, and version
107 * @param MARChannelID The MAR channel name to use, only updates from MARs
108 * with a matching MAR channel name will succeed.
109 * If an empty string is passed, no check will be done
110 * for the channel name in the product information block.
111 * If a comma separated list of values is passed then
112 * one value must match.
113 * @param appVersion The application version to use, only MARs with an
114 * application version >= to appVersion will be applied.
115 * @return OK on success
116 * COULD_NOT_READ_PRODUCT_INFO_BLOCK if the product info block
118 * MARCHANNEL_MISMATCH_ERROR if update-settings.ini's MAR
119 * channel ID doesn't match the MAR
120 * file's MAR channel ID.
121 * VERSION_DOWNGRADE_ERROR if the application version for
122 * this updater is newer than the
126 ArchiveReader::VerifyProductInformation(const char *MARChannelID
,
127 const char *appVersion
)
131 return ARCHIVE_NOT_OPEN
;
134 ProductInformationBlock productInfoBlock
;
135 int rv
= mar_read_product_info_block(mArchive
,
139 return COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR
;
142 // Only check the MAR channel name if specified, it should be passed in from
143 // the update-settings.ini file.
144 if (MARChannelID
&& strlen(MARChannelID
))
146 // Check for at least one match in the comma separated list of values.
147 const char *delimiter
= " ,\t";
148 // Make a copy of the string in case a read only memory buffer
149 // was specified. strtok modifies the input buffer.
150 char channelCopy
[512] = { 0 };
151 strncpy(channelCopy
, MARChannelID
, sizeof(channelCopy
) - 1);
152 char *channel
= strtok(channelCopy
, delimiter
);
153 rv
= MAR_CHANNEL_MISMATCH_ERROR
;
156 if (!strcmp(channel
, productInfoBlock
.MARChannelID
))
161 channel
= strtok(nullptr, delimiter
);
167 /* Compare both versions to ensure we don't have a downgrade
168 -1 if appVersion is older than productInfoBlock.productVersion
169 1 if appVersion is newer than productInfoBlock.productVersion
170 0 if appVersion is the same as productInfoBlock.productVersion
171 This even works with strings like:
172 - 12.0a1 being older than 12.0a2
173 - 12.0a2 being older than 12.0b1
174 - 12.0a1 being older than 12.0
175 - 12.0 being older than 12.1a1 */
176 int versionCompareResult
=
177 mozilla::CompareVersions(appVersion
, productInfoBlock
.productVersion
);
178 if (1 == versionCompareResult
)
180 rv
= VERSION_DOWNGRADE_ERROR
;
184 free((void *)productInfoBlock
.MARChannelID
);
185 free((void *)productInfoBlock
.productVersion
);
190 ArchiveReader::Open(const NS_tchar
*path
)
197 inbuf
= (char *)malloc(inbuf_size
);
200 // Try again with a smaller buffer.
202 inbuf
= (char *)malloc(inbuf_size
);
204 return ARCHIVE_READER_MEM_ERROR
;
210 outbuf
= (char *)malloc(outbuf_size
);
213 // Try again with a smaller buffer.
215 outbuf
= (char *)malloc(outbuf_size
);
217 return ARCHIVE_READER_MEM_ERROR
;
222 mArchive
= mar_wopen(path
);
224 mArchive
= mar_open(path
);
233 ArchiveReader::Close()
255 ArchiveReader::ExtractFile(const char *name
, const NS_tchar
*dest
)
257 const MarItem
*item
= mar_find_item(mArchive
, name
);
262 FILE* fp
= _wfopen(dest
, L
"wb+");
264 int fd
= creat(dest
, item
->flags
);
268 FILE *fp
= fdopen(fd
, "wb");
273 int rv
= ExtractItemToStream(item
, fp
);
280 ArchiveReader::ExtractFileToStream(const char *name
, FILE *fp
)
282 const MarItem
*item
= mar_find_item(mArchive
, name
);
286 return ExtractItemToStream(item
, fp
);
290 ArchiveReader::ExtractItemToStream(const MarItem
*item
, FILE *fp
)
292 /* decompress the data chunk by chunk */
295 int offset
, inlen
, ret
= OK
;
297 memset(&strm
, 0, sizeof(strm
));
298 if (BZ2_bzDecompressInit(&strm
, 0, 0) != BZ_OK
)
299 return UNEXPECTED_BZIP_ERROR
;
306 ret
= UNEXPECTED_MAR_ERROR
;
310 if (offset
< (int) item
->length
&& strm
.avail_in
== 0)
312 inlen
= mar_read(mArchive
, item
, offset
, inbuf
, inbuf_size
);
316 strm
.next_in
= inbuf
;
317 strm
.avail_in
= inlen
;
320 strm
.next_out
= outbuf
;
321 strm
.avail_out
= outbuf_size
;
323 ret
= BZ2_bzDecompress(&strm
);
324 if (ret
!= BZ_OK
&& ret
!= BZ_STREAM_END
)
326 ret
= UNEXPECTED_BZIP_ERROR
;
330 int outlen
= outbuf_size
- strm
.avail_out
;
333 if (fwrite(outbuf
, outlen
, 1, fp
) != 1)
335 ret
= WRITE_ERROR_EXTRACT
;
340 if (ret
== BZ_STREAM_END
)
347 BZ2_bzDecompressEnd(&strm
);