1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "updatelogging.h"
11 // See the MSDN documentation with title: Privilege Constants
12 // At the time of this writing, this documentation is located at:
13 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx
14 LPCTSTR
UACHelper::PrivsToDisable
[] = {
15 SE_ASSIGNPRIMARYTOKEN_NAME
,
18 // CreateProcess will succeed but the app will fail to launch on some WinXP
19 // machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this happens
20 // for limited user accounts on those machines. The define is kept here as a
21 // reminder that it should never be re-added.
22 // This permission is for directory watching but also from MSDN: "This
23 // privilege also causes the system to skip all traversal access checks."
24 // SE_CHANGE_NOTIFY_NAME,
25 SE_CREATE_GLOBAL_NAME
,
26 SE_CREATE_PAGEFILE_NAME
,
27 SE_CREATE_PERMANENT_NAME
,
28 SE_CREATE_SYMBOLIC_LINK_NAME
,
31 SE_ENABLE_DELEGATION_NAME
,
33 SE_INC_BASE_PRIORITY_NAME
,
34 SE_INCREASE_QUOTA_NAME
,
35 SE_INC_WORKING_SET_NAME
,
38 SE_MACHINE_ACCOUNT_NAME
,
39 SE_MANAGE_VOLUME_NAME
,
40 SE_PROF_SINGLE_PROCESS_NAME
,
42 SE_REMOTE_SHUTDOWN_NAME
,
47 SE_SYSTEM_ENVIRONMENT_NAME
,
48 SE_SYSTEM_PROFILE_NAME
,
50 SE_TAKE_OWNERSHIP_NAME
,
53 SE_TRUSTED_CREDMAN_ACCESS_NAME
,
55 SE_UNSOLICITED_INPUT_NAME
59 * Opens a user token for the given session ID
61 * @param sessionID The session ID for the token to obtain
62 * @return A handle to the token to obtain which will be primary if enough
63 * permissions exist. Caller should close the handle.
66 UACHelper::OpenUserToken(DWORD sessionID
)
68 HMODULE module
= LoadLibraryW(L
"wtsapi32.dll");
69 HANDLE token
= nullptr;
70 decltype(WTSQueryUserToken
)* wtsQueryUserToken
=
71 (decltype(WTSQueryUserToken
)*) GetProcAddress(module
, "WTSQueryUserToken");
72 if (wtsQueryUserToken
) {
73 wtsQueryUserToken(sessionID
, &token
);
80 * Opens a linked token for the specified token.
82 * @param token The token to get the linked token from
83 * @return A linked token or nullptr if one does not exist.
84 * Caller should close the handle.
87 UACHelper::OpenLinkedToken(HANDLE token
)
90 // UAC creates 2 tokens. One is the restricted token which we have.
91 // the other is the UAC elevated one. Since we are running as a service
92 // as the system account we have access to both.
93 TOKEN_LINKED_TOKEN tlt
;
94 HANDLE hNewLinkedToken
= nullptr;
96 if (GetTokenInformation(token
, (TOKEN_INFORMATION_CLASS
)TokenLinkedToken
,
97 &tlt
, sizeof(TOKEN_LINKED_TOKEN
), &len
)) {
98 token
= tlt
.LinkedToken
;
99 hNewLinkedToken
= token
;
101 return hNewLinkedToken
;
106 * Enables or disables a privilege for the specified token.
108 * @param token The token to adjust the privilege on.
109 * @param priv The privilege to adjust.
110 * @param enable Whether to enable or disable it
111 * @return TRUE if the token was adjusted to the specified value.
114 UACHelper::SetPrivilege(HANDLE token
, LPCTSTR priv
, BOOL enable
)
117 if (!LookupPrivilegeValue(nullptr, priv
, &luidOfPriv
)) {
121 TOKEN_PRIVILEGES tokenPriv
;
122 tokenPriv
.PrivilegeCount
= 1;
123 tokenPriv
.Privileges
[0].Luid
= luidOfPriv
;
124 tokenPriv
.Privileges
[0].Attributes
= enable
? SE_PRIVILEGE_ENABLED
: 0;
126 SetLastError(ERROR_SUCCESS
);
127 if (!AdjustTokenPrivileges(token
, false, &tokenPriv
,
128 sizeof(tokenPriv
), nullptr, nullptr)) {
132 return GetLastError() == ERROR_SUCCESS
;
136 * For each privilege that is specified, an attempt will be made to
137 * drop the privilege.
139 * @param token The token to adjust the privilege on.
140 * Pass nullptr for current token.
141 * @param unneededPrivs An array of unneeded privileges.
142 * @param count The size of the array
143 * @return TRUE if there were no errors
146 UACHelper::DisableUnneededPrivileges(HANDLE token
,
147 LPCTSTR
*unneededPrivs
,
150 HANDLE obtainedToken
= nullptr;
152 // Note: This handle is a pseudo-handle and need not be closed
153 HANDLE process
= GetCurrentProcess();
154 if (!OpenProcessToken(process
, TOKEN_ALL_ACCESS_P
, &obtainedToken
)) {
155 LOG_WARN(("Could not obtain token for current process, no "
156 "privileges changed. (%d)", GetLastError()));
159 token
= obtainedToken
;
163 for (size_t i
= 0; i
< count
; i
++) {
164 if (SetPrivilege(token
, unneededPrivs
[i
], FALSE
)) {
165 LOG(("Disabled unneeded token privilege: %s.",
168 LOG(("Could not disable token privilege value: %s. (%d)",
169 unneededPrivs
[i
], GetLastError()));
175 CloseHandle(obtainedToken
);
181 * Disables privileges for the specified token.
182 * The privileges to disable are in PrivsToDisable.
183 * In the future there could be new privs and we are not sure if we should
184 * explicitly disable these or not.
186 * @param token The token to drop the privilege on.
187 * Pass nullptr for current token.
188 * @return TRUE if there were no errors
191 UACHelper::DisablePrivileges(HANDLE token
)
193 static const size_t PrivsToDisableSize
=
194 sizeof(UACHelper::PrivsToDisable
) / sizeof(UACHelper::PrivsToDisable
[0]);
196 return DisableUnneededPrivileges(token
, UACHelper::PrivsToDisable
,
201 * Check if the current user can elevate.
203 * @return true if the user can elevate.
207 UACHelper::CanUserElevate()
210 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
, &token
)) {
214 TOKEN_ELEVATION_TYPE elevationType
;
216 bool canElevate
= GetTokenInformation(token
, TokenElevationType
,
218 sizeof(elevationType
), &len
) &&
219 (elevationType
== TokenElevationTypeLimited
);