toolkit/mozapps/update/common/uachelper.cpp

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

mercurial