summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/update/common/commonupdatedir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/update/common/commonupdatedir.cpp')
-rw-r--r--toolkit/mozapps/update/common/commonupdatedir.cpp723
1 files changed, 723 insertions, 0 deletions
diff --git a/toolkit/mozapps/update/common/commonupdatedir.cpp b/toolkit/mozapps/update/common/commonupdatedir.cpp
new file mode 100644
index 0000000000..0ba9fcef94
--- /dev/null
+++ b/toolkit/mozapps/update/common/commonupdatedir.cpp
@@ -0,0 +1,723 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/*
+ * This file does not use many of the features Firefox provides such as
+ * nsAString and nsIFile because code in this file will be included not only
+ * in Firefox, but also in the Mozilla Maintenance Service, the Mozilla
+ * Maintenance Service installer, and TestAUSHelper.
+ */
+
+#include <cinttypes>
+#include <cwchar>
+#include <string>
+#include "city.h"
+#include "commonupdatedir.h"
+#include "updatedefines.h"
+
+#ifdef XP_WIN
+# include <accctrl.h>
+# include <aclapi.h>
+# include <cstdarg>
+# include <errno.h>
+# include <objbase.h>
+# include <shellapi.h>
+# include <shlobj.h>
+# include <strsafe.h>
+# include <winerror.h>
+# include "nsWindowsHelpers.h"
+# include "updateutils_win.h"
+#endif
+
+#ifdef XP_WIN
+// This is the name of the old update directory
+// (i.e. C:\ProgramData\<OLD_ROOT_UPDATE_DIR_NAME>)
+# define OLD_ROOT_UPDATE_DIR_NAME "Mozilla"
+// This is the name of the current update directory
+// (i.e. C:\ProgramData\<ROOT_UPDATE_DIR_NAME>)
+// It is really important that we properly set the permissions on this
+// directory at creation time. Thus, it is really important that this code be
+// the creator of this directory. We had many problems with the old update
+// directory having been previously created by old versions of Firefox. To avoid
+// this problem in the future, we are including a UUID in the root update
+// directory name to attempt to ensure that it will be created by this code and
+// won't already exist with the wrong permissions.
+# define ROOT_UPDATE_DIR_NAME "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38"
+// This describes the directory between the "Mozilla" directory and the install
+// path hash (i.e. C:\ProgramData\Mozilla\<UPDATE_PATH_MID_DIR_NAME>\<hash>)
+# define UPDATE_PATH_MID_DIR_NAME "updates"
+
+enum class WhichUpdateDir {
+ CurrentUpdateDir,
+ UnmigratedUpdateDir,
+};
+
+/**
+ * This is a very simple string class.
+ *
+ * This class has some substantial limitations for the sake of simplicity. It
+ * has no support whatsoever for modifying a string that already has data. There
+ * is, therefore, no append function and no support for automatically resizing
+ * strings.
+ *
+ * Error handling is also done in a slightly unusual manner. If there is ever
+ * a failure allocating or assigning to a string, it will do the simplest
+ * possible recovery: truncate itself to 0-length.
+ * This coupled with the fact that the length is cached means that an effective
+ * method of error checking is to attempt assignment and then check the length
+ * of the result.
+ */
+class SimpleAutoString {
+ private:
+ size_t mLength;
+ // Unique pointer frees the buffer when the class is deleted or goes out of
+ // scope.
+ mozilla::UniquePtr<wchar_t[]> mString;
+
+ /**
+ * Allocates enough space to store a string of the specified length.
+ */
+ bool AllocLen(size_t len) {
+ mString = mozilla::MakeUnique<wchar_t[]>(len + 1);
+ return mString.get() != nullptr;
+ }
+
+ /**
+ * Allocates a buffer of the size given.
+ */
+ bool AllocSize(size_t size) {
+ mString = mozilla::MakeUnique<wchar_t[]>(size);
+ return mString.get() != nullptr;
+ }
+
+ public:
+ SimpleAutoString() : mLength(0) {}
+
+ /*
+ * Allocates enough space for a string of the given length and formats it as
+ * an empty string.
+ */
+ bool AllocEmpty(size_t len) {
+ bool success = AllocLen(len);
+ Truncate();
+ return success;
+ }
+
+ /**
+ * These functions can potentially return null if no buffer has yet been
+ * allocated. After changing a string retrieved with MutableString, the Check
+ * method should be called to synchronize other members (ex: mLength) with the
+ * new buffer.
+ */
+ wchar_t* MutableString() { return mString.get(); }
+ const wchar_t* String() const { return mString.get(); }
+
+ size_t Length() const { return mLength; }
+
+ /**
+ * This method should be called after manually changing the string's buffer
+ * via MutableString to synchronize other members (ex: mLength) with the
+ * new buffer.
+ * Returns true if the string is now in a valid state.
+ */
+ bool Check() {
+ mLength = wcslen(mString.get());
+ return true;
+ }
+
+ void SwapBufferWith(mozilla::UniquePtr<wchar_t[]>& other) {
+ mString.swap(other);
+ if (mString) {
+ mLength = wcslen(mString.get());
+ } else {
+ mLength = 0;
+ }
+ }
+
+ void Swap(SimpleAutoString& other) {
+ mString.swap(other.mString);
+ size_t newLength = other.mLength;
+ other.mLength = mLength;
+ mLength = newLength;
+ }
+
+ /**
+ * Truncates the string to the length specified. This must not be greater than
+ * or equal to the size of the string's buffer.
+ */
+ void Truncate(size_t len = 0) {
+ if (len > mLength) {
+ return;
+ }
+ mLength = len;
+ if (mString) {
+ mString.get()[len] = L'\0';
+ }
+ }
+
+ /**
+ * Assigns a string and ensures that the resulting string is valid and has its
+ * length set properly.
+ *
+ * Note that although other similar functions in this class take length, this
+ * function takes buffer size instead because it is intended to be follow the
+ * input convention of sprintf.
+ *
+ * Returns the new length, which will be 0 if there was any failure.
+ *
+ * This function does no allocation or reallocation. If the buffer is not
+ * large enough to hold the new string, the call will fail.
+ */
+ size_t AssignSprintf(size_t bufferSize, const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ size_t returnValue = AssignVsprintf(bufferSize, format, ap);
+ va_end(ap);
+ return returnValue;
+ }
+ /**
+ * Same as the above, but takes a va_list like vsprintf does.
+ */
+ size_t AssignVsprintf(size_t bufferSize, const wchar_t* format, va_list ap) {
+ if (!mString) {
+ Truncate();
+ return 0;
+ }
+
+ int charsWritten = vswprintf(mString.get(), bufferSize, format, ap);
+ if (charsWritten < 0 || static_cast<size_t>(charsWritten) >= bufferSize) {
+ // charsWritten does not include the null terminator. If charsWritten is
+ // equal to the buffer size, we do not have a null terminator nor do we
+ // have room for one.
+ Truncate();
+ return 0;
+ }
+ mString.get()[charsWritten] = L'\0';
+
+ mLength = charsWritten;
+ return mLength;
+ }
+
+ /**
+ * Allocates enough space for the string and assigns a value to it with
+ * sprintf. Takes, as an argument, the maximum length that the string is
+ * expected to use (which, after adding 1 for the null byte, is the amount of
+ * space that will be allocated).
+ *
+ * Returns the new length, which will be 0 on any failure.
+ */
+ size_t AllocAndAssignSprintf(size_t maxLength, const wchar_t* format, ...) {
+ if (!AllocLen(maxLength)) {
+ Truncate();
+ return 0;
+ }
+ va_list ap;
+ va_start(ap, format);
+ size_t charsWritten = AssignVsprintf(maxLength + 1, format, ap);
+ va_end(ap);
+ return charsWritten;
+ }
+
+ /*
+ * Allocates enough for the formatted text desired. Returns maximum storable
+ * length of a string in the allocated buffer on success, or 0 on failure.
+ */
+ size_t AllocFromScprintf(const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ size_t returnValue = AllocFromVscprintf(format, ap);
+ va_end(ap);
+ return returnValue;
+ }
+ /**
+ * Same as the above, but takes a va_list like vscprintf does.
+ */
+ size_t AllocFromVscprintf(const wchar_t* format, va_list ap) {
+ int len = _vscwprintf(format, ap);
+ if (len < 0) {
+ Truncate();
+ return 0;
+ }
+ if (!AllocEmpty(len)) {
+ // AllocEmpty will Truncate, no need to call it here.
+ return 0;
+ }
+ return len;
+ }
+
+ /**
+ * Automatically determines how much space is necessary, allocates that much
+ * for the string, and assigns the data using swprintf. Returns the resulting
+ * length of the string, which will be 0 if the function fails.
+ */
+ size_t AutoAllocAndAssignSprintf(const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ size_t len = AllocFromVscprintf(format, ap);
+ va_end(ap);
+ if (len == 0) {
+ // AllocFromVscprintf will Truncate, no need to call it here.
+ return 0;
+ }
+
+ va_start(ap, format);
+ size_t charsWritten = AssignVsprintf(len + 1, format, ap);
+ va_end(ap);
+
+ if (len != charsWritten) {
+ Truncate();
+ return 0;
+ }
+ return charsWritten;
+ }
+
+ /**
+ * The following CopyFrom functions take various types of strings, allocate
+ * enough space to hold them, and then copy them into that space.
+ *
+ * They return an HRESULT that should be interpreted with the SUCCEEDED or
+ * FAILED macro.
+ */
+ HRESULT CopyFrom(const wchar_t* src) {
+ mLength = wcslen(src);
+ if (!AllocLen(mLength)) {
+ Truncate();
+ return E_OUTOFMEMORY;
+ }
+ HRESULT hrv = StringCchCopyW(mString.get(), mLength + 1, src);
+ if (FAILED(hrv)) {
+ Truncate();
+ }
+ return hrv;
+ }
+ HRESULT CopyFrom(const SimpleAutoString& src) {
+ if (!src.mString) {
+ Truncate();
+ return S_OK;
+ }
+ mLength = src.mLength;
+ if (!AllocLen(mLength)) {
+ Truncate();
+ return E_OUTOFMEMORY;
+ }
+ HRESULT hrv = StringCchCopyW(mString.get(), mLength + 1, src.mString.get());
+ if (FAILED(hrv)) {
+ Truncate();
+ }
+ return hrv;
+ }
+ HRESULT CopyFrom(const char* src) {
+ int bufferSize =
+ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, nullptr, 0);
+ if (bufferSize == 0) {
+ Truncate();
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ if (!AllocSize(bufferSize)) {
+ Truncate();
+ return E_OUTOFMEMORY;
+ }
+ int charsWritten = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src,
+ -1, mString.get(), bufferSize);
+ if (charsWritten == 0) {
+ Truncate();
+ return HRESULT_FROM_WIN32(GetLastError());
+ } else if (charsWritten != bufferSize) {
+ Truncate();
+ return E_FAIL;
+ }
+ mLength = charsWritten - 1;
+ return S_OK;
+ }
+
+ bool StartsWith(const SimpleAutoString& prefix) const {
+ if (!mString) {
+ return (prefix.mLength == 0);
+ }
+ if (!prefix.mString) {
+ return true;
+ }
+ if (prefix.mLength > mLength) {
+ return false;
+ }
+ return (wcsncmp(mString.get(), prefix.mString.get(), prefix.mLength) == 0);
+ }
+};
+
+// Deleter for use with UniquePtr
+struct CoTaskMemFreeDeleter {
+ void operator()(void* aPtr) { ::CoTaskMemFree(aPtr); }
+};
+
+/**
+ * A lot of data goes into constructing an ACL and security attributes, and the
+ * Windows documentation does not make it very clear what can be safely freed
+ * after these objects are constructed. This struct holds all of the
+ * construction data in one place so that it can be passed around and freed
+ * properly.
+ */
+struct AutoPerms {
+ SID_IDENTIFIER_AUTHORITY sidIdentifierAuthority;
+ UniqueSidPtr usersSID;
+ UniqueSidPtr adminsSID;
+ UniqueSidPtr systemSID;
+ EXPLICIT_ACCESS_W ea[3];
+ mozilla::UniquePtr<ACL, LocalFreeDeleter> acl;
+ mozilla::UniquePtr<uint8_t[]> securityDescriptorBuffer;
+ PSECURITY_DESCRIPTOR securityDescriptor;
+ SECURITY_ATTRIBUTES securityAttributes;
+};
+
+static bool GetCachedHash(const char16_t* installPath, HKEY rootKey,
+ const SimpleAutoString& regPath,
+ mozilla::UniquePtr<NS_tchar[]>& result);
+static HRESULT GetUpdateDirectory(const wchar_t* installPath,
+ WhichUpdateDir whichDir,
+ mozilla::UniquePtr<wchar_t[]>& result);
+static HRESULT GeneratePermissions(AutoPerms& result);
+static HRESULT MakeDir(const SimpleAutoString& path, const AutoPerms& perms);
+#endif // XP_WIN
+
+/**
+ * Returns a hash of the install path, suitable for uniquely identifying the
+ * particular Firefox installation that is running.
+ *
+ * This function includes a compatibility mode that should NOT be used except by
+ * GetUserUpdateDirectory. Previous implementations of this function could
+ * return a value inconsistent with what our installer would generate. When the
+ * update directory was migrated, this function was re-implemented to return
+ * values consistent with those generated by the installer. The compatibility
+ * mode is retained only so that we can properly get the old update directory
+ * when migrating it.
+ *
+ * @param installPath
+ * The null-terminated path to the installation directory (i.e. the
+ * directory that contains the binary). Must not be null. The path must
+ * not include a trailing slash.
+ * @param result
+ * The out parameter that will be set to contain the resulting hash.
+ * The value is wrapped in a UniquePtr to make cleanup easier on the
+ * caller.
+ * @return true if successful and false otherwise.
+ */
+bool GetInstallHash(const char16_t* installPath,
+ mozilla::UniquePtr<NS_tchar[]>& result) {
+ MOZ_ASSERT(installPath != nullptr,
+ "Install path must not be null in GetInstallHash");
+
+ size_t pathSize =
+ std::char_traits<char16_t>::length(installPath) * sizeof(*installPath);
+ uint64_t hash =
+ CityHash64(reinterpret_cast<const char*>(installPath), pathSize);
+
+ size_t hashStrSize = sizeof(hash) * 2 + 1; // 2 hex digits per byte + null
+ result = mozilla::MakeUnique<NS_tchar[]>(hashStrSize);
+ int charsWritten =
+ NS_tsnprintf(result.get(), hashStrSize, NS_T("%") NS_T(PRIX64), hash);
+ return !(charsWritten < 1 ||
+ static_cast<size_t>(charsWritten) > hashStrSize - 1);
+}
+
+#ifdef XP_WIN
+/**
+ * Returns true if the registry key was successfully found and read into result.
+ */
+static bool GetCachedHash(const char16_t* installPath, HKEY rootKey,
+ const SimpleAutoString& regPath,
+ mozilla::UniquePtr<NS_tchar[]>& result) {
+ // Find the size of the string we are reading before we read it so we can
+ // allocate space.
+ unsigned long bufferSize;
+ LSTATUS lrv = RegGetValueW(rootKey, regPath.String(),
+ reinterpret_cast<const wchar_t*>(installPath),
+ RRF_RT_REG_SZ, nullptr, nullptr, &bufferSize);
+ if (lrv != ERROR_SUCCESS) {
+ return false;
+ }
+ result = mozilla::MakeUnique<NS_tchar[]>(bufferSize);
+ // Now read the actual value from the registry.
+ lrv = RegGetValueW(rootKey, regPath.String(),
+ reinterpret_cast<const wchar_t*>(installPath),
+ RRF_RT_REG_SZ, nullptr, result.get(), &bufferSize);
+ return (lrv == ERROR_SUCCESS);
+}
+
+/**
+ * Returns the update directory path. The update directory needs to have
+ * different permissions from the default, so we don't really want anyone using
+ * the path without the directory already being created with the correct
+ * permissions. Therefore, this function also ensures that the base directory
+ * that needs permissions set already exists. If it does not exist, it is
+ * created with the needed permissions.
+ * The desired permissions give Full Control to SYSTEM, Administrators, and
+ * Users.
+ *
+ * @param installPath
+ * Must be the null-terminated path to the installation directory (i.e.
+ * the directory that contains the binary). The path must not include a
+ * trailing slash.
+ * @param result
+ * The out parameter that will be set to contain the resulting path.
+ * The value is wrapped in a UniquePtr to make cleanup easier on the
+ * caller.
+ *
+ * @return An HRESULT that should be tested with SUCCEEDED or FAILED.
+ */
+HRESULT
+GetCommonUpdateDirectory(const wchar_t* installPath,
+ mozilla::UniquePtr<wchar_t[]>& result) {
+ return GetUpdateDirectory(installPath, WhichUpdateDir::CurrentUpdateDir,
+ result);
+}
+
+/**
+ * This function is identical to the function above except that it gets the
+ * "old" (pre-migration) update directory.
+ *
+ * The other difference is that this function does not create the directory.
+ */
+HRESULT
+GetOldUpdateDirectory(const wchar_t* installPath,
+ mozilla::UniquePtr<wchar_t[]>& result) {
+ return GetUpdateDirectory(installPath, WhichUpdateDir::UnmigratedUpdateDir,
+ result);
+}
+
+/**
+ * This is a version of the GetCommonUpdateDirectory that can be called from
+ * Rust.
+ * The result parameter must be a valid pointer to a buffer of length
+ * MAX_PATH + 1
+ */
+extern "C" HRESULT get_common_update_directory(const wchar_t* installPath,
+ wchar_t* result) {
+ mozilla::UniquePtr<wchar_t[]> uniqueResult;
+ HRESULT hr = GetCommonUpdateDirectory(installPath, uniqueResult);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return StringCchCopyW(result, MAX_PATH + 1, uniqueResult.get());
+}
+
+/**
+ * This is a helper function that does all of the work for
+ * GetCommonUpdateDirectory and GetUserUpdateDirectory.
+ *
+ * For information on the parameters and return value, see
+ * GetCommonUpdateDirectory.
+ */
+static HRESULT GetUpdateDirectory(const wchar_t* installPath,
+ WhichUpdateDir whichDir,
+ mozilla::UniquePtr<wchar_t[]>& result) {
+ MOZ_ASSERT(installPath != nullptr,
+ "Install path must not be null in GetUpdateDirectory");
+
+ AutoPerms perms;
+ HRESULT hrv = GeneratePermissions(perms);
+ if (FAILED(hrv)) {
+ return hrv;
+ }
+
+ PWSTR baseDirParentPath;
+ hrv = SHGetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_CREATE, nullptr,
+ &baseDirParentPath);
+ // Free baseDirParentPath when it goes out of scope.
+ mozilla::UniquePtr<wchar_t, CoTaskMemFreeDeleter> baseDirParentPathUnique(
+ baseDirParentPath);
+ if (FAILED(hrv)) {
+ return hrv;
+ }
+
+ SimpleAutoString baseDir;
+ if (whichDir == WhichUpdateDir::UnmigratedUpdateDir) {
+ const wchar_t baseDirLiteral[] = NS_T(OLD_ROOT_UPDATE_DIR_NAME);
+ hrv = baseDir.CopyFrom(baseDirLiteral);
+ } else {
+ const wchar_t baseDirLiteral[] = NS_T(ROOT_UPDATE_DIR_NAME);
+ hrv = baseDir.CopyFrom(baseDirLiteral);
+ }
+ if (FAILED(hrv)) {
+ return hrv;
+ }
+
+ // Generate the base path
+ // (C:\ProgramData\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38)
+ SimpleAutoString basePath;
+ size_t basePathLen =
+ wcslen(baseDirParentPath) + 1 /* path separator */ + baseDir.Length();
+ basePath.AllocAndAssignSprintf(basePathLen, L"%s\\%s", baseDirParentPath,
+ baseDir.String());
+ if (basePath.Length() != basePathLen) {
+ return E_FAIL;
+ }
+
+ if (whichDir == WhichUpdateDir::CurrentUpdateDir) {
+ hrv = MakeDir(basePath, perms);
+ if (FAILED(hrv)) {
+ return hrv;
+ }
+ }
+
+ // Generate what we are going to call the mid-path
+ // (C:\ProgramData\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38\updates)
+ const wchar_t midPathDirName[] = NS_T(UPDATE_PATH_MID_DIR_NAME);
+ size_t midPathLen =
+ basePath.Length() + 1 /* path separator */ + wcslen(midPathDirName);
+ SimpleAutoString midPath;
+ midPath.AllocAndAssignSprintf(midPathLen, L"%s\\%s", basePath.String(),
+ midPathDirName);
+ if (midPath.Length() != midPathLen) {
+ return E_FAIL;
+ }
+
+ mozilla::UniquePtr<NS_tchar[]> hash;
+
+ // The Windows installer caches this hash value in the registry
+ bool gotHash = false;
+ SimpleAutoString regPath;
+ regPath.AutoAllocAndAssignSprintf(L"SOFTWARE\\Mozilla\\%S\\TaskBarIDs",
+ MOZ_APP_BASENAME);
+ if (regPath.Length() != 0) {
+ gotHash = GetCachedHash(reinterpret_cast<const char16_t*>(installPath),
+ HKEY_LOCAL_MACHINE, regPath, hash);
+ if (!gotHash) {
+ gotHash = GetCachedHash(reinterpret_cast<const char16_t*>(installPath),
+ HKEY_CURRENT_USER, regPath, hash);
+ }
+ }
+ // If we couldn't get it out of the registry, we'll just have to regenerate
+ // it.
+ if (!gotHash) {
+ bool success =
+ GetInstallHash(reinterpret_cast<const char16_t*>(installPath), hash);
+ if (!success) {
+ return E_FAIL;
+ }
+ }
+
+ size_t updatePathLen =
+ midPath.Length() + 1 /* path separator */ + wcslen(hash.get());
+ SimpleAutoString updatePath;
+ updatePath.AllocAndAssignSprintf(updatePathLen, L"%s\\%s", midPath.String(),
+ hash.get());
+ if (updatePath.Length() != updatePathLen) {
+ return E_FAIL;
+ }
+
+ updatePath.SwapBufferWith(result);
+ return S_OK;
+}
+
+/**
+ * Generates the permission set that we want to be applied to the update
+ * directory and its contents. Returns the permissions data via the result
+ * outparam.
+ *
+ * These are also the permissions that will be used to check that file
+ * permissions are correct.
+ */
+static HRESULT GeneratePermissions(AutoPerms& result) {
+ result.sidIdentifierAuthority = SECURITY_NT_AUTHORITY;
+ ZeroMemory(&result.ea, sizeof(result.ea));
+
+ // Make Users group SID and add it to the Explicit Access List.
+ PSID usersSID = nullptr;
+ BOOL success = AllocateAndInitializeSid(
+ &result.sidIdentifierAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &usersSID);
+ result.usersSID.reset(usersSID);
+ if (!success) {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ result.ea[0].grfAccessPermissions = FILE_ALL_ACCESS;
+ result.ea[0].grfAccessMode = SET_ACCESS;
+ result.ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ result.ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ result.ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+ result.ea[0].Trustee.ptstrName = static_cast<LPWSTR>(usersSID);
+
+ // Make Administrators group SID and add it to the Explicit Access List.
+ PSID adminsSID = nullptr;
+ success = AllocateAndInitializeSid(
+ &result.sidIdentifierAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminsSID);
+ result.adminsSID.reset(adminsSID);
+ if (!success) {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ result.ea[1].grfAccessPermissions = FILE_ALL_ACCESS;
+ result.ea[1].grfAccessMode = SET_ACCESS;
+ result.ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ result.ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ result.ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+ result.ea[1].Trustee.ptstrName = static_cast<LPWSTR>(adminsSID);
+
+ // Make SYSTEM user SID and add it to the Explicit Access List.
+ PSID systemSID = nullptr;
+ success = AllocateAndInitializeSid(&result.sidIdentifierAuthority, 1,
+ SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0,
+ 0, 0, &systemSID);
+ result.systemSID.reset(systemSID);
+ if (!success) {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ result.ea[2].grfAccessPermissions = FILE_ALL_ACCESS;
+ result.ea[2].grfAccessMode = SET_ACCESS;
+ result.ea[2].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ result.ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ result.ea[2].Trustee.TrusteeType = TRUSTEE_IS_USER;
+ result.ea[2].Trustee.ptstrName = static_cast<LPWSTR>(systemSID);
+
+ PACL acl = nullptr;
+ DWORD drv = SetEntriesInAclW(3, result.ea, nullptr, &acl);
+ // Put the ACL in a unique pointer so that LocalFree is called when it goes
+ // out of scope
+ result.acl.reset(acl);
+ if (drv != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(drv);
+ }
+
+ result.securityDescriptorBuffer =
+ mozilla::MakeUnique<uint8_t[]>(SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (!result.securityDescriptorBuffer) {
+ return E_OUTOFMEMORY;
+ }
+ result.securityDescriptor = reinterpret_cast<PSECURITY_DESCRIPTOR>(
+ result.securityDescriptorBuffer.get());
+ success = InitializeSecurityDescriptor(result.securityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION);
+ if (!success) {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ success =
+ SetSecurityDescriptorDacl(result.securityDescriptor, TRUE, acl, FALSE);
+ if (!success) {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ result.securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ result.securityAttributes.lpSecurityDescriptor = result.securityDescriptor;
+ result.securityAttributes.bInheritHandle = FALSE;
+ return S_OK;
+}
+
+/**
+ * Creates a directory with the permissions specified. If the directory already
+ * exists, this function will return success.
+ */
+static HRESULT MakeDir(const SimpleAutoString& path, const AutoPerms& perms) {
+ BOOL success = CreateDirectoryW(
+ path.String(),
+ const_cast<LPSECURITY_ATTRIBUTES>(&perms.securityAttributes));
+ if (success) {
+ return S_OK;
+ }
+ DWORD error = GetLastError();
+ if (error == ERROR_ALREADY_EXISTS) {
+ return S_OK;
+ }
+ return HRESULT_FROM_WIN32(error);
+}
+#endif // XP_WIN