/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Maintenance service UAC helper functions. * * The Initial Developer of the Original Code is * Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include "uachelper.h" #include "updatelogging.h" typedef BOOL (WINAPI *LPWTSQueryUserToken)(ULONG, PHANDLE); // 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 }; /** * Determines if the OS is vista or later * * @return TRUE if the OS is vista or later. */ BOOL UACHelper::IsVistaOrLater() { // Check if we are running Vista or later. OSVERSIONINFO osInfo; osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); return GetVersionEx(&osInfo) && osInfo.dwMajorVersion >= 6; } /** * 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 = NULL; LPWTSQueryUserToken wtsQueryUserToken = (LPWTSQueryUserToken)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 NULL 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 = NULL; 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(NULL, 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), NULL, NULL)) { 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 NULL 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 = NULL; 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(("Could not obtain token for current process, no " "privileges changed. (%d)\n", 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.\n", unneededPrivs[i])); } else { LOG(("Could not disable token privilege value: %s. (%d)\n", 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 NULL 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); }