1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/update/common/uachelper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,222 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <windows.h> 1.9 +#include <wtsapi32.h> 1.10 +#include "uachelper.h" 1.11 +#include "updatelogging.h" 1.12 + 1.13 +// See the MSDN documentation with title: Privilege Constants 1.14 +// At the time of this writing, this documentation is located at: 1.15 +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx 1.16 +LPCTSTR UACHelper::PrivsToDisable[] = { 1.17 + SE_ASSIGNPRIMARYTOKEN_NAME, 1.18 + SE_AUDIT_NAME, 1.19 + SE_BACKUP_NAME, 1.20 + // CreateProcess will succeed but the app will fail to launch on some WinXP 1.21 + // machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this happens 1.22 + // for limited user accounts on those machines. The define is kept here as a 1.23 + // reminder that it should never be re-added. 1.24 + // This permission is for directory watching but also from MSDN: "This 1.25 + // privilege also causes the system to skip all traversal access checks." 1.26 + // SE_CHANGE_NOTIFY_NAME, 1.27 + SE_CREATE_GLOBAL_NAME, 1.28 + SE_CREATE_PAGEFILE_NAME, 1.29 + SE_CREATE_PERMANENT_NAME, 1.30 + SE_CREATE_SYMBOLIC_LINK_NAME, 1.31 + SE_CREATE_TOKEN_NAME, 1.32 + SE_DEBUG_NAME, 1.33 + SE_ENABLE_DELEGATION_NAME, 1.34 + SE_IMPERSONATE_NAME, 1.35 + SE_INC_BASE_PRIORITY_NAME, 1.36 + SE_INCREASE_QUOTA_NAME, 1.37 + SE_INC_WORKING_SET_NAME, 1.38 + SE_LOAD_DRIVER_NAME, 1.39 + SE_LOCK_MEMORY_NAME, 1.40 + SE_MACHINE_ACCOUNT_NAME, 1.41 + SE_MANAGE_VOLUME_NAME, 1.42 + SE_PROF_SINGLE_PROCESS_NAME, 1.43 + SE_RELABEL_NAME, 1.44 + SE_REMOTE_SHUTDOWN_NAME, 1.45 + SE_RESTORE_NAME, 1.46 + SE_SECURITY_NAME, 1.47 + SE_SHUTDOWN_NAME, 1.48 + SE_SYNC_AGENT_NAME, 1.49 + SE_SYSTEM_ENVIRONMENT_NAME, 1.50 + SE_SYSTEM_PROFILE_NAME, 1.51 + SE_SYSTEMTIME_NAME, 1.52 + SE_TAKE_OWNERSHIP_NAME, 1.53 + SE_TCB_NAME, 1.54 + SE_TIME_ZONE_NAME, 1.55 + SE_TRUSTED_CREDMAN_ACCESS_NAME, 1.56 + SE_UNDOCK_NAME, 1.57 + SE_UNSOLICITED_INPUT_NAME 1.58 +}; 1.59 + 1.60 +/** 1.61 + * Opens a user token for the given session ID 1.62 + * 1.63 + * @param sessionID The session ID for the token to obtain 1.64 + * @return A handle to the token to obtain which will be primary if enough 1.65 + * permissions exist. Caller should close the handle. 1.66 + */ 1.67 +HANDLE 1.68 +UACHelper::OpenUserToken(DWORD sessionID) 1.69 +{ 1.70 + HMODULE module = LoadLibraryW(L"wtsapi32.dll"); 1.71 + HANDLE token = nullptr; 1.72 + decltype(WTSQueryUserToken)* wtsQueryUserToken = 1.73 + (decltype(WTSQueryUserToken)*) GetProcAddress(module, "WTSQueryUserToken"); 1.74 + if (wtsQueryUserToken) { 1.75 + wtsQueryUserToken(sessionID, &token); 1.76 + } 1.77 + FreeLibrary(module); 1.78 + return token; 1.79 +} 1.80 + 1.81 +/** 1.82 + * Opens a linked token for the specified token. 1.83 + * 1.84 + * @param token The token to get the linked token from 1.85 + * @return A linked token or nullptr if one does not exist. 1.86 + * Caller should close the handle. 1.87 + */ 1.88 +HANDLE 1.89 +UACHelper::OpenLinkedToken(HANDLE token) 1.90 +{ 1.91 + // Magic below... 1.92 + // UAC creates 2 tokens. One is the restricted token which we have. 1.93 + // the other is the UAC elevated one. Since we are running as a service 1.94 + // as the system account we have access to both. 1.95 + TOKEN_LINKED_TOKEN tlt; 1.96 + HANDLE hNewLinkedToken = nullptr; 1.97 + DWORD len; 1.98 + if (GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, 1.99 + &tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) { 1.100 + token = tlt.LinkedToken; 1.101 + hNewLinkedToken = token; 1.102 + } 1.103 + return hNewLinkedToken; 1.104 +} 1.105 + 1.106 + 1.107 +/** 1.108 + * Enables or disables a privilege for the specified token. 1.109 + * 1.110 + * @param token The token to adjust the privilege on. 1.111 + * @param priv The privilege to adjust. 1.112 + * @param enable Whether to enable or disable it 1.113 + * @return TRUE if the token was adjusted to the specified value. 1.114 + */ 1.115 +BOOL 1.116 +UACHelper::SetPrivilege(HANDLE token, LPCTSTR priv, BOOL enable) 1.117 +{ 1.118 + LUID luidOfPriv; 1.119 + if (!LookupPrivilegeValue(nullptr, priv, &luidOfPriv)) { 1.120 + return FALSE; 1.121 + } 1.122 + 1.123 + TOKEN_PRIVILEGES tokenPriv; 1.124 + tokenPriv.PrivilegeCount = 1; 1.125 + tokenPriv.Privileges[0].Luid = luidOfPriv; 1.126 + tokenPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; 1.127 + 1.128 + SetLastError(ERROR_SUCCESS); 1.129 + if (!AdjustTokenPrivileges(token, false, &tokenPriv, 1.130 + sizeof(tokenPriv), nullptr, nullptr)) { 1.131 + return FALSE; 1.132 + } 1.133 + 1.134 + return GetLastError() == ERROR_SUCCESS; 1.135 +} 1.136 + 1.137 +/** 1.138 + * For each privilege that is specified, an attempt will be made to 1.139 + * drop the privilege. 1.140 + * 1.141 + * @param token The token to adjust the privilege on. 1.142 + * Pass nullptr for current token. 1.143 + * @param unneededPrivs An array of unneeded privileges. 1.144 + * @param count The size of the array 1.145 + * @return TRUE if there were no errors 1.146 + */ 1.147 +BOOL 1.148 +UACHelper::DisableUnneededPrivileges(HANDLE token, 1.149 + LPCTSTR *unneededPrivs, 1.150 + size_t count) 1.151 +{ 1.152 + HANDLE obtainedToken = nullptr; 1.153 + if (!token) { 1.154 + // Note: This handle is a pseudo-handle and need not be closed 1.155 + HANDLE process = GetCurrentProcess(); 1.156 + if (!OpenProcessToken(process, TOKEN_ALL_ACCESS_P, &obtainedToken)) { 1.157 + LOG_WARN(("Could not obtain token for current process, no " 1.158 + "privileges changed. (%d)", GetLastError())); 1.159 + return FALSE; 1.160 + } 1.161 + token = obtainedToken; 1.162 + } 1.163 + 1.164 + BOOL result = TRUE; 1.165 + for (size_t i = 0; i < count; i++) { 1.166 + if (SetPrivilege(token, unneededPrivs[i], FALSE)) { 1.167 + LOG(("Disabled unneeded token privilege: %s.", 1.168 + unneededPrivs[i])); 1.169 + } else { 1.170 + LOG(("Could not disable token privilege value: %s. (%d)", 1.171 + unneededPrivs[i], GetLastError())); 1.172 + result = FALSE; 1.173 + } 1.174 + } 1.175 + 1.176 + if (obtainedToken) { 1.177 + CloseHandle(obtainedToken); 1.178 + } 1.179 + return result; 1.180 +} 1.181 + 1.182 +/** 1.183 + * Disables privileges for the specified token. 1.184 + * The privileges to disable are in PrivsToDisable. 1.185 + * In the future there could be new privs and we are not sure if we should 1.186 + * explicitly disable these or not. 1.187 + * 1.188 + * @param token The token to drop the privilege on. 1.189 + * Pass nullptr for current token. 1.190 + * @return TRUE if there were no errors 1.191 + */ 1.192 +BOOL 1.193 +UACHelper::DisablePrivileges(HANDLE token) 1.194 +{ 1.195 + static const size_t PrivsToDisableSize = 1.196 + sizeof(UACHelper::PrivsToDisable) / sizeof(UACHelper::PrivsToDisable[0]); 1.197 + 1.198 + return DisableUnneededPrivileges(token, UACHelper::PrivsToDisable, 1.199 + PrivsToDisableSize); 1.200 +} 1.201 + 1.202 +/** 1.203 + * Check if the current user can elevate. 1.204 + * 1.205 + * @return true if the user can elevate. 1.206 + * false otherwise. 1.207 + */ 1.208 +bool 1.209 +UACHelper::CanUserElevate() 1.210 +{ 1.211 + HANDLE token; 1.212 + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { 1.213 + return false; 1.214 + } 1.215 + 1.216 + TOKEN_ELEVATION_TYPE elevationType; 1.217 + DWORD len; 1.218 + bool canElevate = GetTokenInformation(token, TokenElevationType, 1.219 + &elevationType, 1.220 + sizeof(elevationType), &len) && 1.221 + (elevationType == TokenElevationTypeLimited); 1.222 + CloseHandle(token); 1.223 + 1.224 + return canElevate; 1.225 +}