toolkit/mozapps/update/common/uachelper.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include <windows.h>
michael@0 6 #include <wtsapi32.h>
michael@0 7 #include "uachelper.h"
michael@0 8 #include "updatelogging.h"
michael@0 9
michael@0 10 // See the MSDN documentation with title: Privilege Constants
michael@0 11 // At the time of this writing, this documentation is located at:
michael@0 12 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx
michael@0 13 LPCTSTR UACHelper::PrivsToDisable[] = {
michael@0 14 SE_ASSIGNPRIMARYTOKEN_NAME,
michael@0 15 SE_AUDIT_NAME,
michael@0 16 SE_BACKUP_NAME,
michael@0 17 // CreateProcess will succeed but the app will fail to launch on some WinXP
michael@0 18 // machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this happens
michael@0 19 // for limited user accounts on those machines. The define is kept here as a
michael@0 20 // reminder that it should never be re-added.
michael@0 21 // This permission is for directory watching but also from MSDN: "This
michael@0 22 // privilege also causes the system to skip all traversal access checks."
michael@0 23 // SE_CHANGE_NOTIFY_NAME,
michael@0 24 SE_CREATE_GLOBAL_NAME,
michael@0 25 SE_CREATE_PAGEFILE_NAME,
michael@0 26 SE_CREATE_PERMANENT_NAME,
michael@0 27 SE_CREATE_SYMBOLIC_LINK_NAME,
michael@0 28 SE_CREATE_TOKEN_NAME,
michael@0 29 SE_DEBUG_NAME,
michael@0 30 SE_ENABLE_DELEGATION_NAME,
michael@0 31 SE_IMPERSONATE_NAME,
michael@0 32 SE_INC_BASE_PRIORITY_NAME,
michael@0 33 SE_INCREASE_QUOTA_NAME,
michael@0 34 SE_INC_WORKING_SET_NAME,
michael@0 35 SE_LOAD_DRIVER_NAME,
michael@0 36 SE_LOCK_MEMORY_NAME,
michael@0 37 SE_MACHINE_ACCOUNT_NAME,
michael@0 38 SE_MANAGE_VOLUME_NAME,
michael@0 39 SE_PROF_SINGLE_PROCESS_NAME,
michael@0 40 SE_RELABEL_NAME,
michael@0 41 SE_REMOTE_SHUTDOWN_NAME,
michael@0 42 SE_RESTORE_NAME,
michael@0 43 SE_SECURITY_NAME,
michael@0 44 SE_SHUTDOWN_NAME,
michael@0 45 SE_SYNC_AGENT_NAME,
michael@0 46 SE_SYSTEM_ENVIRONMENT_NAME,
michael@0 47 SE_SYSTEM_PROFILE_NAME,
michael@0 48 SE_SYSTEMTIME_NAME,
michael@0 49 SE_TAKE_OWNERSHIP_NAME,
michael@0 50 SE_TCB_NAME,
michael@0 51 SE_TIME_ZONE_NAME,
michael@0 52 SE_TRUSTED_CREDMAN_ACCESS_NAME,
michael@0 53 SE_UNDOCK_NAME,
michael@0 54 SE_UNSOLICITED_INPUT_NAME
michael@0 55 };
michael@0 56
michael@0 57 /**
michael@0 58 * Opens a user token for the given session ID
michael@0 59 *
michael@0 60 * @param sessionID The session ID for the token to obtain
michael@0 61 * @return A handle to the token to obtain which will be primary if enough
michael@0 62 * permissions exist. Caller should close the handle.
michael@0 63 */
michael@0 64 HANDLE
michael@0 65 UACHelper::OpenUserToken(DWORD sessionID)
michael@0 66 {
michael@0 67 HMODULE module = LoadLibraryW(L"wtsapi32.dll");
michael@0 68 HANDLE token = nullptr;
michael@0 69 decltype(WTSQueryUserToken)* wtsQueryUserToken =
michael@0 70 (decltype(WTSQueryUserToken)*) GetProcAddress(module, "WTSQueryUserToken");
michael@0 71 if (wtsQueryUserToken) {
michael@0 72 wtsQueryUserToken(sessionID, &token);
michael@0 73 }
michael@0 74 FreeLibrary(module);
michael@0 75 return token;
michael@0 76 }
michael@0 77
michael@0 78 /**
michael@0 79 * Opens a linked token for the specified token.
michael@0 80 *
michael@0 81 * @param token The token to get the linked token from
michael@0 82 * @return A linked token or nullptr if one does not exist.
michael@0 83 * Caller should close the handle.
michael@0 84 */
michael@0 85 HANDLE
michael@0 86 UACHelper::OpenLinkedToken(HANDLE token)
michael@0 87 {
michael@0 88 // Magic below...
michael@0 89 // UAC creates 2 tokens. One is the restricted token which we have.
michael@0 90 // the other is the UAC elevated one. Since we are running as a service
michael@0 91 // as the system account we have access to both.
michael@0 92 TOKEN_LINKED_TOKEN tlt;
michael@0 93 HANDLE hNewLinkedToken = nullptr;
michael@0 94 DWORD len;
michael@0 95 if (GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken,
michael@0 96 &tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) {
michael@0 97 token = tlt.LinkedToken;
michael@0 98 hNewLinkedToken = token;
michael@0 99 }
michael@0 100 return hNewLinkedToken;
michael@0 101 }
michael@0 102
michael@0 103
michael@0 104 /**
michael@0 105 * Enables or disables a privilege for the specified token.
michael@0 106 *
michael@0 107 * @param token The token to adjust the privilege on.
michael@0 108 * @param priv The privilege to adjust.
michael@0 109 * @param enable Whether to enable or disable it
michael@0 110 * @return TRUE if the token was adjusted to the specified value.
michael@0 111 */
michael@0 112 BOOL
michael@0 113 UACHelper::SetPrivilege(HANDLE token, LPCTSTR priv, BOOL enable)
michael@0 114 {
michael@0 115 LUID luidOfPriv;
michael@0 116 if (!LookupPrivilegeValue(nullptr, priv, &luidOfPriv)) {
michael@0 117 return FALSE;
michael@0 118 }
michael@0 119
michael@0 120 TOKEN_PRIVILEGES tokenPriv;
michael@0 121 tokenPriv.PrivilegeCount = 1;
michael@0 122 tokenPriv.Privileges[0].Luid = luidOfPriv;
michael@0 123 tokenPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
michael@0 124
michael@0 125 SetLastError(ERROR_SUCCESS);
michael@0 126 if (!AdjustTokenPrivileges(token, false, &tokenPriv,
michael@0 127 sizeof(tokenPriv), nullptr, nullptr)) {
michael@0 128 return FALSE;
michael@0 129 }
michael@0 130
michael@0 131 return GetLastError() == ERROR_SUCCESS;
michael@0 132 }
michael@0 133
michael@0 134 /**
michael@0 135 * For each privilege that is specified, an attempt will be made to
michael@0 136 * drop the privilege.
michael@0 137 *
michael@0 138 * @param token The token to adjust the privilege on.
michael@0 139 * Pass nullptr for current token.
michael@0 140 * @param unneededPrivs An array of unneeded privileges.
michael@0 141 * @param count The size of the array
michael@0 142 * @return TRUE if there were no errors
michael@0 143 */
michael@0 144 BOOL
michael@0 145 UACHelper::DisableUnneededPrivileges(HANDLE token,
michael@0 146 LPCTSTR *unneededPrivs,
michael@0 147 size_t count)
michael@0 148 {
michael@0 149 HANDLE obtainedToken = nullptr;
michael@0 150 if (!token) {
michael@0 151 // Note: This handle is a pseudo-handle and need not be closed
michael@0 152 HANDLE process = GetCurrentProcess();
michael@0 153 if (!OpenProcessToken(process, TOKEN_ALL_ACCESS_P, &obtainedToken)) {
michael@0 154 LOG_WARN(("Could not obtain token for current process, no "
michael@0 155 "privileges changed. (%d)", GetLastError()));
michael@0 156 return FALSE;
michael@0 157 }
michael@0 158 token = obtainedToken;
michael@0 159 }
michael@0 160
michael@0 161 BOOL result = TRUE;
michael@0 162 for (size_t i = 0; i < count; i++) {
michael@0 163 if (SetPrivilege(token, unneededPrivs[i], FALSE)) {
michael@0 164 LOG(("Disabled unneeded token privilege: %s.",
michael@0 165 unneededPrivs[i]));
michael@0 166 } else {
michael@0 167 LOG(("Could not disable token privilege value: %s. (%d)",
michael@0 168 unneededPrivs[i], GetLastError()));
michael@0 169 result = FALSE;
michael@0 170 }
michael@0 171 }
michael@0 172
michael@0 173 if (obtainedToken) {
michael@0 174 CloseHandle(obtainedToken);
michael@0 175 }
michael@0 176 return result;
michael@0 177 }
michael@0 178
michael@0 179 /**
michael@0 180 * Disables privileges for the specified token.
michael@0 181 * The privileges to disable are in PrivsToDisable.
michael@0 182 * In the future there could be new privs and we are not sure if we should
michael@0 183 * explicitly disable these or not.
michael@0 184 *
michael@0 185 * @param token The token to drop the privilege on.
michael@0 186 * Pass nullptr for current token.
michael@0 187 * @return TRUE if there were no errors
michael@0 188 */
michael@0 189 BOOL
michael@0 190 UACHelper::DisablePrivileges(HANDLE token)
michael@0 191 {
michael@0 192 static const size_t PrivsToDisableSize =
michael@0 193 sizeof(UACHelper::PrivsToDisable) / sizeof(UACHelper::PrivsToDisable[0]);
michael@0 194
michael@0 195 return DisableUnneededPrivileges(token, UACHelper::PrivsToDisable,
michael@0 196 PrivsToDisableSize);
michael@0 197 }
michael@0 198
michael@0 199 /**
michael@0 200 * Check if the current user can elevate.
michael@0 201 *
michael@0 202 * @return true if the user can elevate.
michael@0 203 * false otherwise.
michael@0 204 */
michael@0 205 bool
michael@0 206 UACHelper::CanUserElevate()
michael@0 207 {
michael@0 208 HANDLE token;
michael@0 209 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
michael@0 210 return false;
michael@0 211 }
michael@0 212
michael@0 213 TOKEN_ELEVATION_TYPE elevationType;
michael@0 214 DWORD len;
michael@0 215 bool canElevate = GetTokenInformation(token, TokenElevationType,
michael@0 216 &elevationType,
michael@0 217 sizeof(elevationType), &len) &&
michael@0 218 (elevationType == TokenElevationTypeLimited);
michael@0 219 CloseHandle(token);
michael@0 220
michael@0 221 return canElevate;
michael@0 222 }

mercurial