зеркало из https://github.com/mozilla/pjs.git
246 строки
7.9 KiB
C++
246 строки
7.9 KiB
C++
/* ***** 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 <netzen@gmail.com>
|
|
*
|
|
* 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 <windows.h>
|
|
#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);
|
|
}
|