diff --git a/toolkit/mozapps/readstrings/errors.h b/toolkit/mozapps/readstrings/errors.h index cad5fe986d2b..7a156b42053d 100644 --- a/toolkit/mozapps/readstrings/errors.h +++ b/toolkit/mozapps/readstrings/errors.h @@ -59,6 +59,10 @@ #define CERT_LOAD_ERROR 17 #define CERT_HANDLING_ERROR 18 #define CERT_VERIFY_ERROR 19 +#define ARCHIVE_NOT_OPEN 20 +#define COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR 21 +#define MAR_CHANNEL_MISMATCH_ERROR 22 +#define VERSION_DOWNGRADE_ERROR 23 // The following error codes are only used by updater.exe // when a fallback key exists and XPCShell tests are being run. diff --git a/toolkit/mozapps/update/updater/Makefile.in b/toolkit/mozapps/update/updater/Makefile.in index bf4493c9ff58..d7321f5bf385 100644 --- a/toolkit/mozapps/update/updater/Makefile.in +++ b/toolkit/mozapps/update/updater/Makefile.in @@ -112,7 +112,10 @@ endif include $(topsrcdir)/config/rules.mk -DEFINES += -DNS_NO_XPCOM +DEFINES += -DNS_NO_XPCOM \ + -DMAR_CHANNEL_ID='"$(MAR_CHANNEL_ID)"' \ + -DMOZ_APP_VERSION='"$(MOZ_APP_VERSION)"' \ + $(NULL) ifdef _MSC_VER WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup diff --git a/toolkit/mozapps/update/updater/archivereader.cpp b/toolkit/mozapps/update/updater/archivereader.cpp index 324d5b19d129..c42e59837c53 100644 --- a/toolkit/mozapps/update/updater/archivereader.cpp +++ b/toolkit/mozapps/update/updater/archivereader.cpp @@ -42,6 +42,12 @@ #include "bzlib.h" #include "archivereader.h" #include "errors.h" +#include "nsAlgorithm.h" +#include "updatehelper.h" + +#define UPDATER_NO_STRING_GLUE_STL +#include "../../../../xpcom/build/nsVersionComparator.cpp" +#undef UPDATER_NO_STRING_GLUE_STL #if defined(XP_UNIX) # include @@ -113,7 +119,7 @@ VerifyLoadedCert(MarFile *archive, int name, int type) return CERT_LOAD_ERROR; } - if (!archive || mar_verify_signatureW(archive, data, size)) { + if (mar_verify_signatureW(archive, data, size)) { return CERT_VERIFY_ERROR; } @@ -133,6 +139,10 @@ VerifyLoadedCert(MarFile *archive, int name, int type) int ArchiveReader::VerifySignature() { + if (!mArchive) { + return ARCHIVE_NOT_OPEN; + } + #ifdef XP_WIN int rv = VerifyLoadedCert(mArchive, IDR_PRIMARY_CERT, TYPE_CERT); if (rv != OK) { @@ -144,6 +154,70 @@ ArchiveReader::VerifySignature() #endif } +/** + * Verifies that the MAR file matches the current product, channel, and version + * + * @param MARChannelID The MAR channel name to use, only updates from MARs + * with a matching MAR channel name will succeed. + * If an empty string is passed, no check will be done + * for the channel name in the product information block. + * @param appVersion The application version to use, only MARs with an + * application version >= to appVersion will be applied. + * @return OK on success + * COULD_NOT_READ_PRODUCT_INFO_BLOCK if the product info block + * could not be read. + * MARCHANNEL_MISMATCH_ERROR if update-settings.ini's MAR + * channel ID doesn't match the MAR + * file's MAR channel ID. + * VERSION_DOWNGRADE_ERROR if the application version for + * this updater is newer than the + * one in the MAR. + */ +int +ArchiveReader::VerifyProductInformation(const char *MARChannelID, + const char *appVersion) +{ + if (!mArchive) { + return ARCHIVE_NOT_OPEN; + } + + ProductInformationBlock productInfoBlock; + int rv = mar_read_product_info_block(mArchive, + &productInfoBlock); + if (rv != OK) { + return COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR; + } + + // Only check the MAR channel name if specified, it should be passed in from + // the update-settings.ini file. + if (MARChannelID && strlen(MARChannelID)) { + if (rv == OK && strcmp(MARChannelID, productInfoBlock.MARChannelID)) { + rv = MAR_CHANNEL_MISMATCH_ERROR; + } + } + + if (rv == OK) { + /* Compare both versions to ensure we don't have a downgrade + 1 if appVersion is older than productInfoBlock.productVersion + -1 if appVersion is newer than productInfoBlock.productVersion + 0 if appVersion is the same as productInfoBlock.productVersion + This even works with strings like: + - 12.0a1 being older than 12.0a2 + - 12.0a2 being older than 12.0b1 + - 12.0a1 being older than 12.0 + - 12.0 being older than 12.1a1 */ + int versionCompareResult = + NS_CompareVersions(appVersion, productInfoBlock.productVersion); + if (-1 == versionCompareResult) { + rv = VERSION_DOWNGRADE_ERROR; + } + } + + free((void *)productInfoBlock.MARChannelID); + free((void *)productInfoBlock.productVersion); + return rv; +} + int ArchiveReader::Open(const NS_tchar *path) { diff --git a/toolkit/mozapps/update/updater/archivereader.h b/toolkit/mozapps/update/updater/archivereader.h index d39f5c89ff73..cc05a927ba98 100644 --- a/toolkit/mozapps/update/updater/archivereader.h +++ b/toolkit/mozapps/update/updater/archivereader.h @@ -57,6 +57,8 @@ public: int Open(const NS_tchar *path); int VerifySignature(); + int VerifyProductInformation(const char *MARChannelID, + const char *appVersion); void Close(); int ExtractFile(const char *item, const NS_tchar *destination); diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 37ddf5cf994a..781448628253 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -68,6 +68,7 @@ #include "bspatch.h" #include "progressui.h" #include "archivereader.h" +#include "readstrings.h" #include "errors.h" #include "bzlib.h" @@ -191,6 +192,15 @@ private: FILE* mFile; }; +struct MARChannelStringTable { + MARChannelStringTable() + { + MARChannelID[0] = '\0'; + } + + char MARChannelID[MAX_TEXT_LEN]; +}; + //----------------------------------------------------------------------------- typedef void (* ThreadFunc)(void *param); @@ -1530,6 +1540,29 @@ WaitForServiceFinishThread(void *param) } #endif +/** + * This function reads in the MAR_CHANNEL_ID from update-settings.ini + * + * @param path The path to the ini file that is to be read + * @param results A pointer to the location to store the read strings + * @return OK on success + */ +static int +ReadMARChannelIDs(const NS_tchar *path, MARChannelStringTable *results) +{ + const unsigned int kNumStrings = 1; + const char *kUpdaterKeys = "MAR_CHANNEL_ID\0"; + char updater_strings[kNumStrings][MAX_TEXT_LEN]; + + int result = ReadStrings(path, kUpdaterKeys, kNumStrings, + updater_strings, "Settings"); + + strncpy(results->MARChannelID, updater_strings[0], MAX_TEXT_LEN - 1); + results->MARChannelID[MAX_TEXT_LEN - 1] = 0; + + return result; +} + static void UpdateThreadFunc(void *param) { @@ -1548,6 +1581,22 @@ UpdateThreadFunc(void *param) } #endif + if (rv == OK) { + NS_tchar updateSettingsPath[MAX_TEXT_LEN]; + NS_tsnprintf(updateSettingsPath, + sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]), + NS_T("%supdate-settings.ini"), gDestPath); + MARChannelStringTable MARStrings; + if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) { + // If we can't read from update-settings.ini then we shouldn't impose + // a MAR restriction. Some installatins won't even include this file. + MARStrings.MARChannelID[0] = '\0'; + } + + rv = gArchiveReader.VerifyProductInformation(MARStrings.MARChannelID, + MOZ_APP_VERSION); + } + if (rv == OK) { rv = DoUpdate(); gArchiveReader.Close(); diff --git a/xpcom/glue/nsVersionComparator.cpp b/xpcom/glue/nsVersionComparator.cpp index c1afa7748412..29b1a66f28db 100644 --- a/xpcom/glue/nsVersionComparator.cpp +++ b/xpcom/glue/nsVersionComparator.cpp @@ -39,7 +39,7 @@ #include #include -#ifdef XP_WIN +#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL) #include #include "nsStringGlue.h" #endif