$OpenBSD$ index 0a64024..80de531 100644 --- toolkit/mozapps/update/updater/updater.cpp.orig Fri Feb 20 15:40:39 2015 +++ toolkit/mozapps/update/updater/updater.cpp Fri Feb 20 15:40:39 2015 @@ -25,7 +25,7 @@ * updatev3.manifest * ----------------- * method = "add" | "add-if" | "add-if-not" | "patch" | "patch-if" | - * "remove" | "rmdir" | "rmrfdir" | type + * "remove" | "rmdir" | "rmrfdir" | "addsymlink" | type * * 'add-if-not' adds a file if it doesn't exist. * @@ -50,6 +50,7 @@ #include #include #include +#include #include "updatelogging.h" @@ -107,6 +108,13 @@ static bool sUseHardLinks = true; # define MAYBE_USE_HARD_LINKS 0 #endif +#if defined(MOZ_VERIFY_MAR_SIGNATURE) +#if defined(MAR_NSS) || !defined(XP_WIN) +#include "nss.h" +#include "prerror.h" +#endif +#endif + #ifdef XP_WIN #include "updatehelper.h" @@ -368,10 +376,12 @@ get_full_path(const NS_tchar *relpath) * The line from the manifest that contains the path. * @param isdir * Whether the path is a directory path. Defaults to false. + * @param islinktarget + * Whether the path is a symbolic link target. Defaults to false. * @return valid filesystem path or nullptr if the path checks fail. */ static NS_tchar* -get_valid_path(NS_tchar **line, bool isdir = false) +get_valid_path(NS_tchar **line, bool isdir = false, bool islinktarget = false) { NS_tchar *path = mstrtok(kQuote, line); if (!path) { @@ -406,10 +416,12 @@ get_valid_path(NS_tchar **line, bool isdir = false) path[NS_tstrlen(path) - 1] = NS_T('\0'); } - // Don't allow relative paths that resolve to a parent directory. - if (NS_tstrstr(path, NS_T("..")) != nullptr) { - LOG(("get_valid_path: paths must not contain '..': " LOG_S, path)); - return nullptr; + if (!islinktarget) { + // Don't allow relative paths that resolve to a parent directory. + if (NS_tstrstr(path, NS_T("..")) != nullptr) { + LOG(("get_valid_path: paths must not contain '..': " LOG_S, path)); + return nullptr; + } } return path; @@ -444,7 +456,8 @@ static void ensure_write_permissions(const NS_tchar *path) (void) _wchmod(path, _S_IREAD | _S_IWRITE); #else struct stat fs; - if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) { + if (!NS_tlstat(path, &fs) && !S_ISLNK(fs.st_mode) + && !(fs.st_mode & S_IWUSR)) { (void)chmod(path, fs.st_mode | S_IWUSR); } #endif @@ -644,11 +657,9 @@ static int ensure_copy(const NS_tchar *path, const NS_tchar *dest) return READ_ERROR; } -#ifdef XP_UNIX if (S_ISLNK(ss.st_mode)) { return ensure_copy_symlink(path, dest); } -#endif #if MAYBE_USE_HARD_LINKS if (sUseHardLinks) { @@ -743,7 +754,7 @@ static int ensure_copy_recursive(const NS_tchar *path, const NS_tchar *dest, return READ_ERROR; } -#ifdef XP_UNIX +#ifndef XP_WIN if (S_ISLNK(sInfo.st_mode)) { return ensure_copy_symlink(path, dest); } @@ -802,14 +813,19 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, return rv; struct stat spathInfo; - rv = NS_tstat(spath, &spathInfo); + rv = NS_tlstat(spath, &spathInfo); if (rv) { LOG(("rename_file: failed to read file status info: " LOG_S ", " \ "err: %d", spath, errno)); return READ_ERROR; } - if (!S_ISREG(spathInfo.st_mode)) { +#ifdef XP_WIN + if (!S_ISREG(spathInfo.st_mode)) +#else + if (!S_ISREG(spathInfo.st_mode) && !S_ISLNK(spathInfo.st_mode)) +#endif + { if (allowDirs && !S_ISDIR(spathInfo.st_mode)) { LOG(("rename_file: path present, but not a file: " LOG_S ", err: %d", spath, errno)); @@ -819,7 +835,12 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, } } - if (!NS_taccess(dpath, F_OK)) { +#ifdef XP_WIN + if (!NS_taccess(dpath, F_OK)) +#else + if (!S_ISLNK(spathInfo.st_mode) && !NS_taccess(dpath, F_OK)) +#endif + { if (ensure_remove(dpath)) { LOG(("rename_file: destination file exists and could not be " \ "removed: " LOG_S, dpath)); @@ -856,7 +877,18 @@ static int backup_restore(const NS_tchar *path) NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]), NS_T("%s") BACKUP_EXT, path); - if (NS_taccess(backup, F_OK)) { + bool isLink = false; +#ifndef XP_WIN + struct stat linkInfo; + int rv = NS_tlstat(path, &linkInfo); + if (!rv) { + LOG(("backup_restore: cannot get info for backup file: " LOG_S, backup)); + return OK; + } + isLink = S_ISLNK(linkInfo.st_mode); +#endif + + if (!isLink && NS_taccess(backup, F_OK)) { LOG(("backup_restore: backup file doesn't exist: " LOG_S, backup)); return OK; } @@ -871,8 +903,18 @@ static int backup_discard(const NS_tchar *path) NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]), NS_T("%s") BACKUP_EXT, path); + bool isLink = false; +#ifndef XP_WIN + struct stat linkInfo; + int rv2 = NS_tlstat(backup, &linkInfo); + if (rv2) { + return OK; // File does not exist; nothing to do. + } + isLink = S_ISLNK(linkInfo.st_mode); +#endif + // Nothing to discard - if (NS_taccess(backup, F_OK)) { + if (!isLink && NS_taccess(backup, F_OK)) { return OK; } @@ -952,7 +994,7 @@ private: class RemoveFile : public Action { public: - RemoveFile() : mFile(nullptr), mSkip(0) { } + RemoveFile() : mFile(nullptr), mSkip(0), mIsLink(0) { } int Parse(NS_tchar *line); int Prepare(); @@ -962,6 +1004,7 @@ public: private: const NS_tchar *mFile; int mSkip; + int mIsLink; }; int @@ -979,28 +1022,39 @@ RemoveFile::Parse(NS_tchar *line) int RemoveFile::Prepare() { - // Skip the file if it already doesn't exist. - int rv = NS_taccess(mFile, F_OK); - if (rv) { - mSkip = 1; - mProgressCost = 0; - return OK; + int rv; +#ifndef XP_WIN + struct stat linkInfo; + rv = NS_tlstat(mFile, &linkInfo); + mIsLink = ((0 == rv) && S_ISLNK(linkInfo.st_mode)); +#endif + + if (!mIsLink) { + // Skip the file if it already doesn't exist. + rv = NS_taccess(mFile, F_OK); + if (rv) { + mSkip = 1; + mProgressCost = 0; + return OK; + } } LOG(("PREPARE REMOVEFILE " LOG_S, mFile)); - // Make sure that we're actually a file... - struct stat fileInfo; - rv = NS_tstat(mFile, &fileInfo); - if (rv) { - LOG(("failed to read file status info: " LOG_S ", err: %d", mFile, - errno)); - return READ_ERROR; - } + if (!mIsLink) { + // Make sure that we're actually a file... + struct stat fileInfo; + rv = NS_tstat(mFile, &fileInfo); + if (rv) { + LOG(("failed to read file status info: " LOG_S ", err: %d", mFile, + errno)); + return READ_ERROR; + } - if (!S_ISREG(fileInfo.st_mode)) { - LOG(("path present, but not a file: " LOG_S, mFile)); - return UNEXPECTED_FILE_OPERATION_ERROR; + if (!S_ISREG(fileInfo.st_mode)) { + LOG(("path present, but not a file: " LOG_S, mFile)); + return UNEXPECTED_FILE_OPERATION_ERROR; + } } NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile, NS_T('/')); @@ -1030,7 +1084,13 @@ RemoveFile::Execute() // The file is checked for existence here and in Prepare since it might have // been removed by a separate instruction: bug 311099. - int rv = NS_taccess(mFile, F_OK); + int rv = 0; + if (mIsLink) { + struct stat linkInfo; + rv = NS_tlstat(mFile, &linkInfo); + } else { + rv = NS_taccess(mFile, F_OK); + } if (rv) { LOG(("file cannot be removed because it does not exist; skipping")); mSkip = 1; @@ -1709,6 +1769,97 @@ PatchIfFile::Finish(int status) PatchFile::Finish(status); } +#ifndef XP_WIN +class AddSymlink : public Action +{ +public: + AddSymlink() : mLinkName(NULL) + , mTarget(NULL) + , mAdded(false) + { } + + virtual int Parse(NS_tchar *line); + virtual int Prepare(); + virtual int Execute(); + virtual void Finish(int status); + +private: + const NS_tchar *mLinkName; + const NS_tchar *mTarget; + bool mAdded; +}; + +int +AddSymlink::Parse(NS_tchar *line) +{ + // format "" "target" + + mLinkName = get_valid_path(&line); + if (!mLinkName) + return PARSE_ERROR; + + // consume whitespace between args + NS_tchar *q = mstrtok(kQuote, &line); + if (!q) + return PARSE_ERROR; + + mTarget = get_valid_path(&line, false, true); + if (!mTarget) + return PARSE_ERROR; + + return OK; +} + +int +AddSymlink::Prepare() +{ + LOG(("PREPARE ADDSYMLINK " LOG_S " -> " LOG_S, mLinkName, mTarget)); + + return OK; +} + +int +AddSymlink::Execute() +{ + LOG(("EXECUTE ADDSYMLINK " LOG_S " -> " LOG_S, mLinkName, mTarget)); + + // First make sure that we can actually get rid of any existing file or link. + struct stat linkInfo; + int rv = NS_tlstat(mLinkName, &linkInfo); + if ((0 == rv) && !S_ISLNK(linkInfo.st_mode)) { + rv = NS_taccess(mLinkName, F_OK); + } + if (rv == 0) { + rv = backup_create(mLinkName); + if (rv) + return rv; + } else { + rv = ensure_parent_dir(mLinkName); + if (rv) + return rv; + } + + // Create the link. + rv = symlink(mTarget, mLinkName); + if (!rv) { + mAdded = true; + } + + return rv; +} + +void +AddSymlink::Finish(int status) +{ + LOG(("FINISH ADDSYMLINK " LOG_S " -> " LOG_S, mLinkName, mTarget)); + // When there is an update failure and a link has been added it is removed + // here since there might not be a backup to replace it. + if (status && mAdded) + NS_tremove(mLinkName); + backup_finish(mLinkName, status); +} +#endif + //----------------------------------------------------------------------------- #ifdef XP_WIN @@ -1928,19 +2079,40 @@ CopyInstallDirToDestDir() // These files should not be copied over to the updated app #ifdef XP_WIN -#define SKIPLIST_COUNT 3 + #ifdef TOR_BROWSER_UPDATE + #define SKIPLIST_COUNT 5 + #else + #define SKIPLIST_COUNT 3 + #endif #else -#define SKIPLIST_COUNT 2 + #ifdef TOR_BROWSER_UPDATE + #define SKIPLIST_COUNT 4 + #else + #define SKIPLIST_COUNT 2 + #endif #endif copy_recursive_skiplist skiplist; #ifdef XP_MACOSX skiplist.append(0, installDir, NS_T("Updated.app")); skiplist.append(1, installDir, NS_T("Contents/MacOS/updates/0")); +#ifdef TOR_BROWSER_UPDATE + skiplist.append(2, installDir, NS_T("TorBrowser/Data/Browser/profile.default/.parentlock")); + skiplist.append(3, installDir, NS_T("TorBrowser/Data/Tor/lock")); +#endif #else skiplist.append(0, installDir, NS_T("updated")); skiplist.append(1, installDir, NS_T("updates/0")); +#ifdef TOR_BROWSER_UPDATE +#ifdef XP_UNIX + skiplist.append(2, installDir, NS_T("TorBrowser/Data/Browser/profile.default/.parentlock")); +#else + skiplist.append(2, installDir, NS_T("TorBrowser/Data/Browser/profile.default/parent.lock")); +#endif + skiplist.append(3, installDir, NS_T("TorBrowser/Data/Tor/lock")); +#endif #ifdef XP_WIN - skiplist.append(2, installDir, NS_T("updated.update_in_progress.lock")); + skiplist.append(SKIPLIST_COUNT - 1, installDir, + NS_T("updated.update_in_progress.lock")); #endif #endif @@ -2252,8 +2424,13 @@ UpdateThreadFunc(void *param) MARStrings.MARChannelID[0] = '\0'; } +#ifdef TOR_BROWSER_UPDATE + const char *appVersion = TOR_BROWSER_VERSION; +#else + const char *appVersion = MOZ_APP_VERSION; +#endif rv = gArchiveReader.VerifyProductInformation(MARStrings.MARChannelID, - MOZ_APP_VERSION); + appVersion); } } #endif @@ -2355,6 +2532,20 @@ int NS_main(int argc, NS_tchar **argv) _exit(1); } #endif + +#if defined(MOZ_VERIFY_MAR_SIGNATURE) +#if defined(MAR_NSS) || !defined(XP_WIN) + // 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(NULL) != SECSuccess) { + PRErrorCode error = PR_GetError(); + fprintf(stderr, "Could not initialize NSS: %s (%d)", + PR_ErrorToName(error), (int) error); + _exit(1); + } +#endif +#endif + InitProgressUI(&argc, &argv); // To process an update the updater command line must at a minimum have the @@ -2667,7 +2858,7 @@ int NS_main(int argc, NS_tchar **argv) return 1; } - char16_t *cmdLine = MakeCommandLine(argc - 1, argv + 1); + wchar_t *cmdLine = MakeCommandLine(argc - 1, argv + 1); if (!cmdLine) { CloseHandle(elevatedFileHandle); return 1; @@ -2984,7 +3175,7 @@ int NS_main(int argc, NS_tchar **argv) size_t callbackPrefixLength = PathCommonPrefixW(argv[callbackIndex], installDir, nullptr); - NS_tstrncpy(p, argv[callbackIndex] + max(callbackPrefixLength, commonPrefixLength), bufferLeft); + NS_tstrncpy(p, argv[callbackIndex] + std::max(callbackPrefixLength, commonPrefixLength), bufferLeft); targetPath = buffer; } if (!GetLongPathNameW(targetPath, callbackLongPath, @@ -3148,7 +3339,7 @@ int NS_main(int argc, NS_tchar **argv) LogFinish(); if (argc > callbackIndex) { -#if defined(XP_WIN) +#if defined(XP_WIN) && !defined(TOR_BROWSER_UPDATE) if (gSucceeded) { // The service update will only be executed if it is already installed. // For first time installs of the service, the install will happen from @@ -3776,6 +3967,11 @@ int DoUpdate() else if (NS_tstrcmp(token, NS_T("patch-if")) == 0) { // Patch if exists action = new PatchIfFile(); } +#ifndef XP_WIN + else if (NS_tstrcmp(token, NS_T("addsymlink")) == 0) { + action = new AddSymlink(); + } +#endif else { LOG(("DoUpdate: unknown token: " LOG_S, token)); return PARSE_ERROR;