From 49672c336af63f6dd03175084d8bede0f8b13326 Mon Sep 17 00:00:00 2001 From: Kathy Brade Date: Wed, 17 Dec 2014 16:37:11 -0500 Subject: [PATCH] Bug 13379: Sign our MAR files. Configure with --enable-signmar (build the signmar tool). Configure with --enable-verify-mar (when updating, require a valid signature on the MAR file before it is applied). Use the Tor Browser version instead of the Firefox version inside the MAR file info block (necessary to prevent downgrade attacks). Use NSS on all platforms for checking MAR signatures (instead of using OS-native APIs, which Mozilla does on Mac OS and Windows). So that the NSS and NSPR libraries the updater depends on can be found at runtime, we add the firefox directory to the shared library search path on macOS. On Linux, rpath is used by Mozilla to solve that problem, but that approach won't work on macOS because the updater executable is copied during the update process to a location that is under TorBrowser-Data, and the location of TorBrowser-Data varies. Also includes the fix for bug 18900. --- .mozconfig-asan | 2 + .mozconfig-mac | 2 + .mozconfig-mingw | 2 + modules/libmar/tool/mar.c | 14 ++++-- modules/libmar/tool/moz.build | 10 +++- modules/libmar/verify/moz.build | 14 +++--- .../update/updater/updater-common.build | 24 +++++++-- toolkit/mozapps/update/updater/updater.cpp | 21 +++++--- toolkit/xre/moz.build | 3 ++ toolkit/xre/nsUpdateDriver.cpp | 50 +++++++++++++++++++ 10 files changed, 120 insertions(+), 22 deletions(-) diff --git a/.mozconfig-asan b/.mozconfig-asan index bb9c1f2bf8f0a..49b8f2bcd0371 100644 --- a/.mozconfig-asan +++ b/.mozconfig-asan @@ -28,6 +28,8 @@ ac_add_options --enable-official-branding ac_add_options --enable-default-toolkit=cairo-gtk3 ac_add_options --enable-tor-browser-update +ac_add_options --enable-signmar +ac_add_options --enable-verify-mar ac_add_options --disable-strip ac_add_options --disable-install-strip diff --git a/.mozconfig-mac b/.mozconfig-mac index 25bf2e4824263..5fdf0262e862f 100644 --- a/.mozconfig-mac +++ b/.mozconfig-mac @@ -42,6 +42,8 @@ ac_add_options --disable-debug ac_add_options --enable-tor-browser-data-outside-app-dir ac_add_options --enable-tor-browser-update +ac_add_options --enable-signmar +ac_add_options --enable-verify-mar ac_add_options --disable-crashreporter ac_add_options --disable-webrtc diff --git a/.mozconfig-mingw b/.mozconfig-mingw index 438ef3c228557..14aa8ff55193a 100644 --- a/.mozconfig-mingw +++ b/.mozconfig-mingw @@ -14,6 +14,8 @@ ac_add_options --enable-strip ac_add_options --enable-official-branding ac_add_options --enable-tor-browser-update +ac_add_options --enable-signmar +ac_add_options --enable-verify-mar ac_add_options --disable-bits-download # Let's make sure no preference is enabling either Adobe's or Google's CDM. diff --git a/modules/libmar/tool/mar.c b/modules/libmar/tool/mar.c index a8a7b84cf8d74..8b10fe6436459 100644 --- a/modules/libmar/tool/mar.c +++ b/modules/libmar/tool/mar.c @@ -30,7 +30,11 @@ int mar_repackage_and_sign(const char *NSSConfigDir, const char *src, const char *dest); static void print_version() { +#ifdef TOR_BROWSER_UPDATE + printf("Version: %s\n", TOR_BROWSER_VERSION_QUOTED); +#else printf("Version: %s\n", MOZ_APP_VERSION); +#endif printf("Default Channel ID: %s\n", MAR_CHANNEL_ID); } @@ -65,7 +69,7 @@ static void print_usage() { "signed_input_archive.mar base_64_encoded_signature_file " "changed_signed_output.mar\n"); printf("(i) is the index of the certificate to extract\n"); -#if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(MAR_NSS)) +#if (defined(XP_MACOSX) || defined(XP_WIN)) && !defined(MAR_NSS) printf("Verify a MAR file:\n"); printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n"); printf( @@ -127,7 +131,11 @@ int main(int argc, char **argv) { char *NSSConfigDir = NULL; const char *certNames[MAX_SIGNATURES]; char *MARChannelID = MAR_CHANNEL_ID; +#ifdef TOR_BROWSER_UPDATE + char *productVersion = TOR_BROWSER_VERSION_QUOTED; +#else char *productVersion = MOZ_APP_VERSION; +#endif uint32_t k; int rv = -1; uint32_t certCount = 0; @@ -150,7 +158,7 @@ int main(int argc, char **argv) { memset((void *)certBuffers, 0, sizeof(certBuffers)); #endif #if !defined(NO_SIGN_VERIFY) && \ - ((!defined(MAR_NSS) && defined(XP_WIN)) || defined(XP_MACOSX)) + (!defined(MAR_NSS) && (defined(XP_WIN) || defined(XP_MACOSX))) memset(DERFilePaths, 0, sizeof(DERFilePaths)); memset(fileSizes, 0, sizeof(fileSizes)); #endif @@ -182,7 +190,7 @@ int main(int argc, char **argv) { argc -= 2; } #if !defined(NO_SIGN_VERIFY) && \ - ((!defined(MAR_NSS) && defined(XP_WIN)) || defined(XP_MACOSX)) + (!defined(MAR_NSS) && (defined(XP_WIN) || defined(XP_MACOSX))) /* -D DERFilePath, also matches -D[index] DERFilePath We allow an index for verifying to be symmetric with the import and export command line arguments. */ diff --git a/modules/libmar/tool/moz.build b/modules/libmar/tool/moz.build index de066cbeb6195..ba201432f0a44 100644 --- a/modules/libmar/tool/moz.build +++ b/modules/libmar/tool/moz.build @@ -34,7 +34,13 @@ for var in ('MAR_CHANNEL_ID', 'MOZ_APP_VERSION'): DEFINES[var] = '"%s"' % CONFIG[var] HOST_DEFINES[var] = DEFINES[var] +if CONFIG['TOR_BROWSER_UPDATE']: + DEFINES['TOR_BROWSER_UPDATE'] = '%s' % CONFIG['TOR_BROWSER_UPDATE'] +if CONFIG['TOR_BROWSER_VERSION_QUOTED']: + DEFINES['TOR_BROWSER_VERSION_QUOTED'] = '%s' % CONFIG['TOR_BROWSER_VERSION_QUOTED'] + if CONFIG['MOZ_ENABLE_SIGNMAR']: + DEFINES['MAR_NSS'] = True USE_LIBS += [ 'nspr', 'nss', @@ -48,12 +54,12 @@ if CONFIG['OS_ARCH'] == 'WINNT': OS_LIBS += [ 'ws2_32', ] - if CONFIG['MOZ_ENABLE_SIGNMAR']: + if CONFIG['MOZ_ENABLE_SIGNMAR'] and not DEFINES['MAR_NSS']: OS_LIBS += [ 'crypt32', 'advapi32', ] -elif CONFIG['OS_ARCH'] == 'Darwin': +elif CONFIG['OS_ARCH'] == 'Darwin' and not DEFINES['MAR_NSS']: OS_LIBS += [ '-framework Security', ] diff --git a/modules/libmar/verify/moz.build b/modules/libmar/verify/moz.build index 9f7dc56f8f8ad..e1c6ced31c37a 100644 --- a/modules/libmar/verify/moz.build +++ b/modules/libmar/verify/moz.build @@ -16,15 +16,12 @@ FORCE_STATIC_LIB = True if CONFIG['OS_ARCH'] == 'WINNT': USE_STATIC_LIBS = True elif CONFIG['OS_ARCH'] == 'Darwin': - UNIFIED_SOURCES += [ - 'MacVerifyCrypto.cpp', - ] - OS_LIBS += [ - '-framework Security', + USE_LIBS += [ + 'nspr', + 'nss', + 'signmar', ] else: - DEFINES['MAR_NSS'] = True - LOCAL_INCLUDES += ['../sign'] USE_LIBS += [ 'nspr', 'nss', @@ -38,6 +35,9 @@ else: '-Wl,-rpath=\\$$ORIGIN', ] +DEFINES['MAR_NSS'] = True +LOCAL_INCLUDES += ['../sign'] + LOCAL_INCLUDES += [ '../src', ] diff --git a/toolkit/mozapps/update/updater/updater-common.build b/toolkit/mozapps/update/updater/updater-common.build index 98a386fd8267d..278ab74f69a30 100644 --- a/toolkit/mozapps/update/updater/updater-common.build +++ b/toolkit/mozapps/update/updater/updater-common.build @@ -4,6 +4,10 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +DEFINES['MAR_NSS'] = True + +link_with_nss = DEFINES['MAR_NSS'] or (CONFIG['OS_ARCH'] == 'Linux' and CONFIG['MOZ_VERIFY_MAR_SIGNATURE']) + srcs = [ 'archivereader.cpp', 'updater.cpp', @@ -36,10 +40,14 @@ if CONFIG['OS_ARCH'] == 'WINNT': 'ws2_32', 'shell32', 'shlwapi', - 'crypt32', - 'advapi32', ] + if not link_with_nss: + OS_LIBS += [ + 'crypt32', + 'advapi32', + ] + USE_LIBS += [ 'bspatch', 'mar', @@ -47,6 +55,13 @@ USE_LIBS += [ 'xz-embedded', ] +if link_with_nss: + USE_LIBS += [ + 'nspr', + 'nss', + 'signmar', + ] + if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: have_progressui = 1 srcs += [ @@ -61,9 +76,12 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': ] OS_LIBS += [ '-framework Cocoa', - '-framework Security', '-framework SystemConfiguration', ] + if not link_with_nss: + OS_LIBS += [ + '-framework Security', + ] UNIFIED_SOURCES += [ '/toolkit/xre/updaterfileutils_osx.mm', ] diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 9d6e637fac97b..add1dbf1af090 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -106,10 +106,12 @@ struct UpdateServerThreadArgs { # define USE_EXECV #endif -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX) +#if defined(MOZ_VERIFY_MAR_SIGNATURE) +#if defined(MAR_NSS) || (!defined(XP_WIN) && !defined(XP_MACOSX)) # include "nss.h" # include "prerror.h" #endif +#endif #include "crctable.h" @@ -2744,8 +2746,13 @@ static void UpdateThreadFunc(void* param) { if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) { rv = UPDATE_SETTINGS_FILE_CHANNEL; } else { +#ifdef TOR_BROWSER_UPDATE + const char *appVersion = TOR_BROWSER_VERSION_QUOTED; +#else + const char *appVersion = MOZ_APP_VERSION; +#endif rv = gArchiveReader.VerifyProductInformation(MARStrings.MARChannelID, - MOZ_APP_VERSION); + appVersion); } } } @@ -2944,11 +2951,10 @@ int NS_main(int argc, NS_tchar** argv) { } #endif -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX) - // On Windows and Mac we rely on native APIs to do verifications so we don't - // need to initialize NSS at all there. - // Otherwise, minimize the amount of NSS we depend on by avoiding all the NSS - // databases. +#if defined(MOZ_VERIFY_MAR_SIGNATURE) +#if defined(MAR_NSS) || (!defined(XP_WIN) && !defined(XP_MACOSX)) + // If using NSS for signature verification, initialize NSS but minimize + // the portion we depend on by avoiding all of the NSS databases. if (NSS_NoDB_Init(nullptr) != SECSuccess) { PRErrorCode error = PR_GetError(); fprintf(stderr, "Could not initialize NSS: %s (%d)", PR_ErrorToName(error), @@ -2956,6 +2962,7 @@ int NS_main(int argc, NS_tchar** argv) { _exit(1); } #endif +#endif #ifdef XP_MACOSX if (!isElevated) { diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build index 06604d5672cb0..93a220b9829e2 100644 --- a/toolkit/xre/moz.build +++ b/toolkit/xre/moz.build @@ -207,6 +207,9 @@ for var in ('APP_VERSION', 'APP_ID'): if CONFIG['MOZ_BUILD_APP'] == 'browser': DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True +if CONFIG['MOZ_ENABLE_SIGNMAR'] and CONFIG['TOR_BROWSER_UPDATE']: + DEFINES['MAR_NSS'] = True + LOCAL_INCLUDES += [ '../../other-licenses/nsis/Contrib/CityHash/cityhash', '../components/find', diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp index 4f89a71e9adf6..1d09036df0193 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -433,6 +433,42 @@ static bool CopyUpdaterIntoUpdateDir(nsIFile* greDir, nsIFile* appDir, } #endif +#if defined(TOR_BROWSER_UPDATE) && defined(MOZ_VERIFY_MAR_SIGNATURE) && \ + defined(MAR_NSS) && defined(XP_MACOSX) +/** + * Ideally we would save and restore the original library path value after + * the updater finishes its work (and before firefox is re-launched). + * Doing so would avoid potential problems like the following bug: + * https://bugzilla.mozilla.org/show_bug.cgi?id=1434033 + */ +/** + * Appends the specified path to the library path. + * This is used so that the updater can find libnss3.dylib and other + * shared libs. + * + * @param pathToAppend A new library path to prepend to the dynamic linker's + * search path. + */ +#include "prprf.h" +#define PATH_SEPARATOR ":" +#define LD_LIBRARY_PATH_ENVVAR_NAME "DYLD_LIBRARY_PATH" +static void AppendToLibPath(const char *pathToAppend) { + char *pathValue = getenv(LD_LIBRARY_PATH_ENVVAR_NAME); + if (nullptr == pathValue || '\0' == *pathValue) { + // Leak the string because that is required by PR_SetEnv. + char *s = + Smprintf("%s=%s", LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend).release(); + PR_SetEnv(s); + } else { + // Leak the string because that is required by PR_SetEnv. + char *s = Smprintf("%s=%s" PATH_SEPARATOR "%s", LD_LIBRARY_PATH_ENVVAR_NAME, + pathToAppend, pathValue) + .release(); + PR_SetEnv(s); + } +} +#endif + /** * Applies, switches, or stages an update. * @@ -701,6 +737,20 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir, PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); } +#if defined(TOR_BROWSER_UPDATE) && defined(MOZ_VERIFY_MAR_SIGNATURE) && \ + defined(MAR_NSS) && defined(XP_MACOSX) + // On macOS, append the app directory to the shared library search path + // so the system can locate the shared libraries that are needed by the + // updater, e.g., libnss3.dylib). + nsAutoCString appPath; + nsresult rv2 = appDir->GetNativePath(appPath); + if (NS_SUCCEEDED(rv2)) { + AppendToLibPath(appPath.get()); + } else { + LOG(("ApplyUpdate -- appDir->GetNativePath() failed (0x%x)\n", rv2)); + } +#endif + LOG(("spawning updater process [%s]\n", updaterPath.get())); #ifdef DEBUG dump_argv("ApplyUpdate updater", argv, argc); -- GitLab