summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/update/common/uachelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/update/common/uachelper.cpp')
-rw-r--r--toolkit/mozapps/update/common/uachelper.cpp186
1 files changed, 186 insertions, 0 deletions
diff --git a/toolkit/mozapps/update/common/uachelper.cpp b/toolkit/mozapps/update/common/uachelper.cpp
new file mode 100644
index 0000000000..07e9bd53f9
--- /dev/null
+++ b/toolkit/mozapps/update/common/uachelper.cpp
@@ -0,0 +1,186 @@
+/* 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/. */
+
+#include <windows.h>
+#include <wtsapi32.h>
+#include "uachelper.h"
+#include "updatecommon.h"
+
+// See the MSDN documentation with title: Privilege Constants
+// At the time of this writing, this documentation is located at:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx
+LPCTSTR UACHelper::PrivsToDisable[] = {
+ SE_ASSIGNPRIMARYTOKEN_NAME, SE_AUDIT_NAME, SE_BACKUP_NAME,
+ // CreateProcess will succeed but the app will fail to launch on some WinXP
+ // machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this
+ // happens for limited user accounts on those machines. The define is kept
+ // here as a reminder that it should never be re-added. This permission is
+ // for directory watching but also from MSDN: "This privilege also causes
+ // the system to skip all traversal access checks." SE_CHANGE_NOTIFY_NAME,
+ SE_CREATE_GLOBAL_NAME, SE_CREATE_PAGEFILE_NAME, SE_CREATE_PERMANENT_NAME,
+ SE_CREATE_SYMBOLIC_LINK_NAME, SE_CREATE_TOKEN_NAME, SE_DEBUG_NAME,
+ SE_ENABLE_DELEGATION_NAME, SE_IMPERSONATE_NAME, SE_INC_BASE_PRIORITY_NAME,
+ SE_INCREASE_QUOTA_NAME, SE_INC_WORKING_SET_NAME, SE_LOAD_DRIVER_NAME,
+ SE_LOCK_MEMORY_NAME, SE_MACHINE_ACCOUNT_NAME, SE_MANAGE_VOLUME_NAME,
+ SE_PROF_SINGLE_PROCESS_NAME, SE_RELABEL_NAME, SE_REMOTE_SHUTDOWN_NAME,
+ SE_RESTORE_NAME, SE_SECURITY_NAME, SE_SHUTDOWN_NAME, SE_SYNC_AGENT_NAME,
+ SE_SYSTEM_ENVIRONMENT_NAME, SE_SYSTEM_PROFILE_NAME, SE_SYSTEMTIME_NAME,
+ SE_TAKE_OWNERSHIP_NAME, SE_TCB_NAME, SE_TIME_ZONE_NAME,
+ SE_TRUSTED_CREDMAN_ACCESS_NAME, SE_UNDOCK_NAME, SE_UNSOLICITED_INPUT_NAME};
+
+/**
+ * Opens a user token for the given session ID
+ *
+ * @param sessionID The session ID for the token to obtain
+ * @return A handle to the token to obtain which will be primary if enough
+ * permissions exist. Caller should close the handle.
+ */
+HANDLE
+UACHelper::OpenUserToken(DWORD sessionID) {
+ HMODULE module = LoadLibraryW(L"wtsapi32.dll");
+ HANDLE token = nullptr;
+ decltype(WTSQueryUserToken)* wtsQueryUserToken =
+ (decltype(WTSQueryUserToken)*)GetProcAddress(module, "WTSQueryUserToken");
+ if (wtsQueryUserToken) {
+ wtsQueryUserToken(sessionID, &token);
+ }
+ FreeLibrary(module);
+ return token;
+}
+
+/**
+ * Opens a linked token for the specified token.
+ *
+ * @param token The token to get the linked token from
+ * @return A linked token or nullptr if one does not exist.
+ * Caller should close the handle.
+ */
+HANDLE
+UACHelper::OpenLinkedToken(HANDLE token) {
+ // Magic below...
+ // UAC creates 2 tokens. One is the restricted token which we have.
+ // the other is the UAC elevated one. Since we are running as a service
+ // as the system account we have access to both.
+ TOKEN_LINKED_TOKEN tlt;
+ HANDLE hNewLinkedToken = nullptr;
+ DWORD len;
+ if (GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken,
+ &tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) {
+ token = tlt.LinkedToken;
+ hNewLinkedToken = token;
+ }
+ return hNewLinkedToken;
+}
+
+/**
+ * Enables or disables a privilege for the specified token.
+ *
+ * @param token The token to adjust the privilege on.
+ * @param priv The privilege to adjust.
+ * @param enable Whether to enable or disable it
+ * @return TRUE if the token was adjusted to the specified value.
+ */
+BOOL UACHelper::SetPrivilege(HANDLE token, LPCTSTR priv, BOOL enable) {
+ LUID luidOfPriv;
+ if (!LookupPrivilegeValue(nullptr, priv, &luidOfPriv)) {
+ return FALSE;
+ }
+
+ TOKEN_PRIVILEGES tokenPriv;
+ tokenPriv.PrivilegeCount = 1;
+ tokenPriv.Privileges[0].Luid = luidOfPriv;
+ tokenPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
+
+ SetLastError(ERROR_SUCCESS);
+ if (!AdjustTokenPrivileges(token, false, &tokenPriv, sizeof(tokenPriv),
+ nullptr, nullptr)) {
+ return FALSE;
+ }
+
+ return GetLastError() == ERROR_SUCCESS;
+}
+
+/**
+ * For each privilege that is specified, an attempt will be made to
+ * drop the privilege.
+ *
+ * @param token The token to adjust the privilege on.
+ * Pass nullptr for current token.
+ * @param unneededPrivs An array of unneeded privileges.
+ * @param count The size of the array
+ * @return TRUE if there were no errors
+ */
+BOOL UACHelper::DisableUnneededPrivileges(HANDLE token, LPCTSTR* unneededPrivs,
+ size_t count) {
+ HANDLE obtainedToken = nullptr;
+ if (!token) {
+ // Note: This handle is a pseudo-handle and need not be closed
+ HANDLE process = GetCurrentProcess();
+ if (!OpenProcessToken(process, TOKEN_ALL_ACCESS_P, &obtainedToken)) {
+ LOG_WARN(
+ ("Could not obtain token for current process, no "
+ "privileges changed. (%lu)",
+ GetLastError()));
+ return FALSE;
+ }
+ token = obtainedToken;
+ }
+
+ BOOL result = TRUE;
+ for (size_t i = 0; i < count; i++) {
+ if (SetPrivilege(token, unneededPrivs[i], FALSE)) {
+ LOG(("Disabled unneeded token privilege: %s.", unneededPrivs[i]));
+ } else {
+ LOG(("Could not disable token privilege value: %s. (%lu)",
+ unneededPrivs[i], GetLastError()));
+ result = FALSE;
+ }
+ }
+
+ if (obtainedToken) {
+ CloseHandle(obtainedToken);
+ }
+ return result;
+}
+
+/**
+ * Disables privileges for the specified token.
+ * The privileges to disable are in PrivsToDisable.
+ * In the future there could be new privs and we are not sure if we should
+ * explicitly disable these or not.
+ *
+ * @param token The token to drop the privilege on.
+ * Pass nullptr for current token.
+ * @return TRUE if there were no errors
+ */
+BOOL UACHelper::DisablePrivileges(HANDLE token) {
+ static const size_t PrivsToDisableSize =
+ sizeof(UACHelper::PrivsToDisable) / sizeof(UACHelper::PrivsToDisable[0]);
+
+ return DisableUnneededPrivileges(token, UACHelper::PrivsToDisable,
+ PrivsToDisableSize);
+}
+
+/**
+ * Check if the current user can elevate.
+ *
+ * @return true if the user can elevate.
+ * false otherwise.
+ */
+bool UACHelper::CanUserElevate() {
+ HANDLE token;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
+ return false;
+ }
+
+ TOKEN_ELEVATION_TYPE elevationType;
+ DWORD len;
+ bool canElevate =
+ GetTokenInformation(token, TokenElevationType, &elevationType,
+ sizeof(elevationType), &len) &&
+ (elevationType == TokenElevationTypeLimited);
+ CloseHandle(token);
+
+ return canElevate;
+}