1878c2f44SGeorge Bișoc /*
2878c2f44SGeorge Bișoc * PROJECT: ReactOS Win32k subsystem
3878c2f44SGeorge Bișoc * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4878c2f44SGeorge Bișoc * PURPOSE: Security infrastructure of NTUSER component of Win32k
5*0f9be539SGeorge Bișoc * COPYRIGHT: Copyright 2022-2023 George Bișoc <george.bisoc@reactos.org>
6878c2f44SGeorge Bișoc */
7878c2f44SGeorge Bișoc
8878c2f44SGeorge Bișoc /* INCLUDES ******************************************************************/
9878c2f44SGeorge Bișoc
10878c2f44SGeorge Bișoc #include <win32k.h>
11878c2f44SGeorge Bișoc DBG_DEFAULT_CHANNEL(UserSecurity);
12878c2f44SGeorge Bișoc
13878c2f44SGeorge Bișoc /* FUNCTIONS *****************************************************************/
14878c2f44SGeorge Bișoc
15878c2f44SGeorge Bișoc /**
16878c2f44SGeorge Bișoc * @brief
17878c2f44SGeorge Bișoc * Opens an access token that represents the effective security
18878c2f44SGeorge Bișoc * context of the caller. The purpose of this function is to query
19878c2f44SGeorge Bișoc * the authenticated user that is associated with the security
20878c2f44SGeorge Bișoc * context.
21878c2f44SGeorge Bișoc *
22878c2f44SGeorge Bișoc * @return
23878c2f44SGeorge Bișoc * Returns a handle to an opened access token that represents the
24878c2f44SGeorge Bișoc * security context of the authenticated user, otherwise NULL.
25878c2f44SGeorge Bișoc */
26878c2f44SGeorge Bișoc HANDLE
IntGetCurrentAccessToken(VOID)27878c2f44SGeorge Bișoc IntGetCurrentAccessToken(VOID)
28878c2f44SGeorge Bișoc {
29878c2f44SGeorge Bișoc NTSTATUS Status;
30878c2f44SGeorge Bișoc HANDLE TokenHandle;
31878c2f44SGeorge Bișoc
32878c2f44SGeorge Bișoc /*
33878c2f44SGeorge Bișoc * Try acquiring the security context by opening
34878c2f44SGeorge Bișoc * the current thread (or so called impersonation)
35878c2f44SGeorge Bișoc * token. Such token represents the effective caller.
36878c2f44SGeorge Bișoc * Otherwise if the current thread does not have a
37878c2f44SGeorge Bișoc * token (hence no impersonation occurs) then open
38878c2f44SGeorge Bișoc * the token of main calling process instead.
39878c2f44SGeorge Bișoc */
40878c2f44SGeorge Bișoc Status = ZwOpenThreadToken(ZwCurrentThread(),
41878c2f44SGeorge Bișoc TOKEN_QUERY,
42878c2f44SGeorge Bișoc FALSE,
43878c2f44SGeorge Bișoc &TokenHandle);
44878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
45878c2f44SGeorge Bișoc {
46878c2f44SGeorge Bișoc /*
47878c2f44SGeorge Bișoc * We might likely fail to open the thread
48878c2f44SGeorge Bișoc * token if the process isn't impersonating
49878c2f44SGeorge Bișoc * a client. In scenarios where the server
50878c2f44SGeorge Bișoc * isn't impersonating, open the main process
51878c2f44SGeorge Bișoc * token.
52878c2f44SGeorge Bișoc */
53878c2f44SGeorge Bișoc if (Status == STATUS_NO_TOKEN)
54878c2f44SGeorge Bișoc {
55878c2f44SGeorge Bișoc TRACE("IntGetCurrentAccessToken(): The thread doesn't have a token, trying to open the process one...\n");
56878c2f44SGeorge Bișoc Status = ZwOpenProcessToken(ZwCurrentProcess(),
57878c2f44SGeorge Bișoc TOKEN_QUERY,
58878c2f44SGeorge Bișoc &TokenHandle);
59878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
60878c2f44SGeorge Bișoc {
61878c2f44SGeorge Bișoc /* We failed opening process token as well, bail out... */
62878c2f44SGeorge Bișoc ERR("IntGetCurrentAccessToken(): Failed to capture security context, couldn't open the process token (Status 0x%08lx)\n", Status);
63878c2f44SGeorge Bișoc return NULL;
64878c2f44SGeorge Bișoc }
65878c2f44SGeorge Bișoc
66878c2f44SGeorge Bișoc /* Return the opened token handle */
67878c2f44SGeorge Bișoc return TokenHandle;
68878c2f44SGeorge Bișoc }
69878c2f44SGeorge Bișoc
70878c2f44SGeorge Bișoc /* There's a thread token but we couldn't open it so bail out */
71878c2f44SGeorge Bișoc ERR("IntGetCurrentAccessToken(): Failed to capture security context, couldn't open the thread token (Status 0x%08lx)\n", Status);
72878c2f44SGeorge Bișoc return NULL;
73878c2f44SGeorge Bișoc }
74878c2f44SGeorge Bișoc
75878c2f44SGeorge Bișoc /* Return the opened token handle */
76878c2f44SGeorge Bișoc return TokenHandle;
77878c2f44SGeorge Bișoc }
78878c2f44SGeorge Bișoc
79878c2f44SGeorge Bișoc /**
80878c2f44SGeorge Bișoc * @brief
81878c2f44SGeorge Bișoc * Allocates a buffer within UM (user mode) address
82878c2f44SGeorge Bișoc * space area. Such buffer is reserved for security
83878c2f44SGeorge Bișoc * purposes, such as allocating a buffer for a DACL
84878c2f44SGeorge Bișoc * or a security descriptor.
85878c2f44SGeorge Bișoc *
86878c2f44SGeorge Bișoc * @param[in] Length
87878c2f44SGeorge Bișoc * The length of the buffer that has to be allocated,
88878c2f44SGeorge Bișoc * in bytes.
89878c2f44SGeorge Bișoc *
90878c2f44SGeorge Bișoc * @return
91878c2f44SGeorge Bișoc * Returns a pointer to an allocated buffer whose
92878c2f44SGeorge Bișoc * contents are arbitrary. If the function fails,
93878c2f44SGeorge Bișoc * it means no pages are available to reserve for
94878c2f44SGeorge Bișoc * memory allocation for this buffer.
95878c2f44SGeorge Bișoc */
96878c2f44SGeorge Bișoc PVOID
IntAllocateSecurityBuffer(_In_ SIZE_T Length)97878c2f44SGeorge Bișoc IntAllocateSecurityBuffer(
98878c2f44SGeorge Bișoc _In_ SIZE_T Length)
99878c2f44SGeorge Bișoc {
100878c2f44SGeorge Bișoc NTSTATUS Status;
101878c2f44SGeorge Bișoc PVOID Buffer = NULL;
102878c2f44SGeorge Bișoc
103878c2f44SGeorge Bișoc /* Allocate the buffer in UM memory space */
104878c2f44SGeorge Bișoc Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
105878c2f44SGeorge Bișoc &Buffer,
106878c2f44SGeorge Bișoc 0,
107878c2f44SGeorge Bișoc &Length,
108878c2f44SGeorge Bișoc MEM_COMMIT,
109878c2f44SGeorge Bișoc PAGE_READWRITE);
110878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
111878c2f44SGeorge Bișoc {
112878c2f44SGeorge Bișoc ERR("IntAllocateSecurityBuffer(): Failed to allocate the buffer (Status 0x%08lx)\n", Status);
113878c2f44SGeorge Bișoc return NULL;
114878c2f44SGeorge Bișoc }
115878c2f44SGeorge Bișoc
116878c2f44SGeorge Bișoc return Buffer;
117878c2f44SGeorge Bișoc }
118878c2f44SGeorge Bișoc
119878c2f44SGeorge Bișoc /**
120878c2f44SGeorge Bișoc * @brief
121878c2f44SGeorge Bișoc * Frees an allocated security buffer from UM
122878c2f44SGeorge Bișoc * memory that is been previously allocated by
123878c2f44SGeorge Bișoc * IntAllocateSecurityBuffer function.
124878c2f44SGeorge Bișoc *
125878c2f44SGeorge Bișoc * @param[in] Buffer
126878c2f44SGeorge Bișoc * A pointer to a buffer whose contents are
127878c2f44SGeorge Bișoc * arbitrary, to be freed from UM memory space.
128878c2f44SGeorge Bișoc *
129878c2f44SGeorge Bișoc * @return
130878c2f44SGeorge Bișoc * Nothing.
131878c2f44SGeorge Bișoc */
132878c2f44SGeorge Bișoc VOID
IntFreeSecurityBuffer(_In_ PVOID Buffer)133878c2f44SGeorge Bișoc IntFreeSecurityBuffer(
134878c2f44SGeorge Bișoc _In_ PVOID Buffer)
135878c2f44SGeorge Bișoc {
136878c2f44SGeorge Bișoc SIZE_T Size = 0;
137878c2f44SGeorge Bișoc
138878c2f44SGeorge Bișoc ZwFreeVirtualMemory(ZwCurrentProcess(),
139878c2f44SGeorge Bișoc &Buffer,
140878c2f44SGeorge Bișoc &Size,
141878c2f44SGeorge Bișoc MEM_RELEASE);
142878c2f44SGeorge Bișoc }
143878c2f44SGeorge Bișoc
144878c2f44SGeorge Bișoc /**
145878c2f44SGeorge Bișoc * @brief
146878c2f44SGeorge Bișoc * Queries the authenticated user security identifier
147878c2f44SGeorge Bișoc * (SID) that is associated with the security context
148878c2f44SGeorge Bișoc * of the access token that is being opened.
149878c2f44SGeorge Bișoc *
150878c2f44SGeorge Bișoc * @param[out] User
151878c2f44SGeorge Bișoc * A pointer to the token user that contains the security
152878c2f44SGeorge Bișoc * identifier of the authenticated user.
153878c2f44SGeorge Bișoc *
154878c2f44SGeorge Bișoc * @return
155878c2f44SGeorge Bișoc * Returns STATUS_SUCCESS if the function has successfully
156878c2f44SGeorge Bișoc * queried the token user. STATUS_UNSUCCESSFUL is returned
157878c2f44SGeorge Bișoc * if the effective token of the caller couldn't be opened.
158878c2f44SGeorge Bișoc * STATUS_NO_MEMORY is returned if memory allocation for
159878c2f44SGeorge Bișoc * token user buffer has failed because of lack of necessary
160878c2f44SGeorge Bișoc * pages reserved for such allocation. A failure NTSTATUS
161878c2f44SGeorge Bișoc * code is returned otherwise.
162878c2f44SGeorge Bișoc *
163878c2f44SGeorge Bișoc * @remarks
164878c2f44SGeorge Bișoc * !!!WARNING!!! -- THE CALLER WHO QUERIES THE TOKEN USER IS
165878c2f44SGeorge Bișoc * RESPONSIBLE TO FREE THE ALLOCATED TOKEN USER BUFFER THAT IS
166878c2f44SGeorge Bișoc * BEING GIVEN.
167878c2f44SGeorge Bișoc */
168878c2f44SGeorge Bișoc NTSTATUS
IntQueryUserSecurityIdentification(_Out_ PTOKEN_USER * User)169878c2f44SGeorge Bișoc IntQueryUserSecurityIdentification(
170878c2f44SGeorge Bișoc _Out_ PTOKEN_USER *User)
171878c2f44SGeorge Bișoc {
172878c2f44SGeorge Bișoc NTSTATUS Status;
173*0f9be539SGeorge Bișoc PTOKEN_USER UserToken = NULL;
174878c2f44SGeorge Bișoc HANDLE Token;
175878c2f44SGeorge Bișoc ULONG BufferLength;
176878c2f44SGeorge Bișoc
177878c2f44SGeorge Bișoc /* Initialize the parameter */
178878c2f44SGeorge Bișoc *User = NULL;
179878c2f44SGeorge Bișoc
180878c2f44SGeorge Bișoc /* Open the current token of the caller */
181878c2f44SGeorge Bișoc Token = IntGetCurrentAccessToken();
182878c2f44SGeorge Bișoc if (!Token)
183878c2f44SGeorge Bișoc {
184878c2f44SGeorge Bișoc ERR("IntQueryUserSecurityIdentification(): Couldn't capture the token!\n");
185878c2f44SGeorge Bișoc return STATUS_UNSUCCESSFUL;
186878c2f44SGeorge Bișoc }
187878c2f44SGeorge Bișoc
188878c2f44SGeorge Bișoc /*
189878c2f44SGeorge Bișoc * Since we do not know what the length
190878c2f44SGeorge Bișoc * of the buffer size should be exactly to
191878c2f44SGeorge Bișoc * hold the user data, let the function
192878c2f44SGeorge Bișoc * tell us the size.
193878c2f44SGeorge Bișoc */
194878c2f44SGeorge Bișoc Status = ZwQueryInformationToken(Token,
195878c2f44SGeorge Bișoc TokenUser,
196878c2f44SGeorge Bișoc NULL,
197878c2f44SGeorge Bișoc 0,
198878c2f44SGeorge Bișoc &BufferLength);
199*0f9be539SGeorge Bișoc if (Status == STATUS_BUFFER_TOO_SMALL)
200878c2f44SGeorge Bișoc {
201878c2f44SGeorge Bișoc /*
202878c2f44SGeorge Bișoc * Allocate some memory for the buffer
203878c2f44SGeorge Bișoc * based on the size that the function
204878c2f44SGeorge Bișoc * gave us.
205878c2f44SGeorge Bișoc */
206878c2f44SGeorge Bișoc UserToken = IntAllocateSecurityBuffer(BufferLength);
207878c2f44SGeorge Bișoc if (!UserToken)
208878c2f44SGeorge Bișoc {
209878c2f44SGeorge Bișoc /* Bail out if we failed */
210878c2f44SGeorge Bișoc ERR("IntQueryUserSecurityIdentification(): Couldn't allocate memory for the token user!\n");
211878c2f44SGeorge Bișoc ZwClose(Token);
212878c2f44SGeorge Bișoc return STATUS_NO_MEMORY;
213878c2f44SGeorge Bișoc }
214878c2f44SGeorge Bișoc }
215*0f9be539SGeorge Bișoc else if (!NT_SUCCESS(Status))
216*0f9be539SGeorge Bișoc {
217*0f9be539SGeorge Bișoc ERR("IntQueryUserSecurityIdentification(): Failed to query the necessary length for the buffer (Status 0x%08lx)!\n", Status);
218*0f9be539SGeorge Bișoc ZwClose(Token);
219*0f9be539SGeorge Bișoc return Status;
220*0f9be539SGeorge Bișoc }
221878c2f44SGeorge Bișoc
222878c2f44SGeorge Bișoc /* Query the user now as we have plenty of space to hold it */
223878c2f44SGeorge Bișoc Status = ZwQueryInformationToken(Token,
224878c2f44SGeorge Bișoc TokenUser,
225878c2f44SGeorge Bișoc UserToken,
226878c2f44SGeorge Bișoc BufferLength,
227878c2f44SGeorge Bișoc &BufferLength);
228878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
229878c2f44SGeorge Bișoc {
230878c2f44SGeorge Bișoc /* We failed, bail out */
231878c2f44SGeorge Bișoc ERR("IntQueryUserSecurityIdentification(): Failed to query token user (Status 0x%08lx)\n", Status);
232878c2f44SGeorge Bișoc IntFreeSecurityBuffer(UserToken);
233878c2f44SGeorge Bișoc ZwClose(Token);
234878c2f44SGeorge Bișoc return Status;
235878c2f44SGeorge Bișoc }
236878c2f44SGeorge Bișoc
237878c2f44SGeorge Bișoc /* All good, give the buffer to the caller and close the captured token */
238878c2f44SGeorge Bișoc *User = UserToken;
239878c2f44SGeorge Bișoc ZwClose(Token);
240878c2f44SGeorge Bișoc
241878c2f44SGeorge Bișoc return STATUS_SUCCESS;
242878c2f44SGeorge Bișoc }
243878c2f44SGeorge Bișoc
244878c2f44SGeorge Bișoc /**
245878c2f44SGeorge Bișoc * @brief
246aa815e1cSGeorge Bișoc * Assigns a security descriptor to the desktop
247aa815e1cSGeorge Bișoc * object during a desktop object parse procedure.
248aa815e1cSGeorge Bișoc *
249aa815e1cSGeorge Bișoc * @param[in] WinSta
250aa815e1cSGeorge Bișoc * A pointer to a window station object, of which
251aa815e1cSGeorge Bișoc * such object contains its own security descriptor
252aa815e1cSGeorge Bișoc * that will be captured.
253aa815e1cSGeorge Bișoc *
254aa815e1cSGeorge Bișoc * @param[in] Desktop
255aa815e1cSGeorge Bișoc * A pointer to a desktop object that is created
256aa815e1cSGeorge Bișoc * during a parse procedure.
257aa815e1cSGeorge Bișoc *
258aa815e1cSGeorge Bișoc * @param[in] AccessState
259aa815e1cSGeorge Bișoc * A pointer to an access state structure that
260aa815e1cSGeorge Bișoc * describes the progress state of an access in
261aa815e1cSGeorge Bișoc * action.
262aa815e1cSGeorge Bișoc *
263aa815e1cSGeorge Bișoc * @return
264aa815e1cSGeorge Bișoc * Returns STATUS_SUCCESS if the function has successfully
265aa815e1cSGeorge Bișoc * assigned new security descriptor to the desktop object.
266aa815e1cSGeorge Bișoc * A NTSTATUS failure code is returned otherwise.
267aa815e1cSGeorge Bișoc */
268aa815e1cSGeorge Bișoc NTSTATUS
269aa815e1cSGeorge Bișoc NTAPI
IntAssignDesktopSecurityOnParse(_In_ PWINSTATION_OBJECT WinSta,_In_ PDESKTOP Desktop,_In_ PACCESS_STATE AccessState)270aa815e1cSGeorge Bișoc IntAssignDesktopSecurityOnParse(
271aa815e1cSGeorge Bișoc _In_ PWINSTATION_OBJECT WinSta,
272aa815e1cSGeorge Bișoc _In_ PDESKTOP Desktop,
273aa815e1cSGeorge Bișoc _In_ PACCESS_STATE AccessState)
274aa815e1cSGeorge Bișoc {
275aa815e1cSGeorge Bișoc NTSTATUS Status;
276aa815e1cSGeorge Bișoc PSECURITY_DESCRIPTOR CapturedDescriptor;
277aa815e1cSGeorge Bișoc BOOLEAN MemoryAllocated;
278aa815e1cSGeorge Bișoc
279aa815e1cSGeorge Bișoc /*
280aa815e1cSGeorge Bișoc * Capture the security descriptor from
281aa815e1cSGeorge Bișoc * the window station. The window station
282aa815e1cSGeorge Bișoc * in question has a descriptor that is
283aa815e1cSGeorge Bișoc * inheritable and contains desktop access
284aa815e1cSGeorge Bișoc * rights as well.
285aa815e1cSGeorge Bișoc */
286aa815e1cSGeorge Bișoc Status = ObGetObjectSecurity(WinSta,
287aa815e1cSGeorge Bișoc &CapturedDescriptor,
288aa815e1cSGeorge Bișoc &MemoryAllocated);
289aa815e1cSGeorge Bișoc if (!NT_SUCCESS(Status))
290aa815e1cSGeorge Bișoc {
291aa815e1cSGeorge Bișoc ERR("IntAssignDesktopSecurityOnParse(): Failed to capture the security descriptor from window station (Status 0x%08lx)\n", Status);
292aa815e1cSGeorge Bișoc return Status;
293aa815e1cSGeorge Bișoc }
294aa815e1cSGeorge Bișoc
295aa815e1cSGeorge Bișoc /* Assign new security to the desktop */
296aa815e1cSGeorge Bișoc Status = ObAssignSecurity(AccessState,
297aa815e1cSGeorge Bișoc CapturedDescriptor,
298aa815e1cSGeorge Bișoc Desktop,
299aa815e1cSGeorge Bișoc ExDesktopObjectType);
300aa815e1cSGeorge Bișoc if (!NT_SUCCESS(Status))
301aa815e1cSGeorge Bișoc {
302aa815e1cSGeorge Bișoc ERR("IntAssignDesktopSecurityOnParse(): Failed to assign security information to the desktop object (Status 0x%08lx)\n", Status);
303aa815e1cSGeorge Bișoc }
304aa815e1cSGeorge Bișoc
305aa815e1cSGeorge Bișoc /* Release the descriptor that we have captured */
306aa815e1cSGeorge Bișoc ObReleaseObjectSecurity(CapturedDescriptor, MemoryAllocated);
307aa815e1cSGeorge Bișoc return Status;
308aa815e1cSGeorge Bișoc }
309aa815e1cSGeorge Bișoc
310aa815e1cSGeorge Bișoc /**
311aa815e1cSGeorge Bișoc * @brief
312878c2f44SGeorge Bișoc * Creates a security descriptor for the service.
313878c2f44SGeorge Bișoc *
314878c2f44SGeorge Bișoc * @param[out] ServiceSd
315878c2f44SGeorge Bișoc * A pointer to a newly allocated and created security
316878c2f44SGeorge Bișoc * descriptor for the service.
317878c2f44SGeorge Bișoc *
318878c2f44SGeorge Bișoc * @return
319878c2f44SGeorge Bișoc * Returns STATUS_SUCCESS if the function has successfully
320878c2f44SGeorge Bișoc * queried created the security descriptor. STATUS_NO_MEMORY
321878c2f44SGeorge Bișoc * is returned if memory allocation for security buffers because
322878c2f44SGeorge Bișoc * of a lack of needed pages to reserve for such allocation. A
323878c2f44SGeorge Bișoc * failure NTSTATUS code is returned otherwise.
324878c2f44SGeorge Bișoc */
325878c2f44SGeorge Bișoc NTSTATUS
326878c2f44SGeorge Bișoc NTAPI
IntCreateServiceSecurity(_Out_ PSECURITY_DESCRIPTOR * ServiceSd)327878c2f44SGeorge Bișoc IntCreateServiceSecurity(
328878c2f44SGeorge Bișoc _Out_ PSECURITY_DESCRIPTOR *ServiceSd)
329878c2f44SGeorge Bișoc {
330878c2f44SGeorge Bișoc NTSTATUS Status;
331878c2f44SGeorge Bișoc PACL ServiceDacl;
332878c2f44SGeorge Bișoc ULONG DaclSize;
333878c2f44SGeorge Bișoc ULONG RelSDSize;
334878c2f44SGeorge Bișoc SECURITY_DESCRIPTOR AbsSD;
335878c2f44SGeorge Bișoc PSECURITY_DESCRIPTOR RelSD;
336878c2f44SGeorge Bișoc PTOKEN_USER TokenUser;
337878c2f44SGeorge Bișoc
338878c2f44SGeorge Bișoc /* Initialize our local variables */
339878c2f44SGeorge Bișoc RelSDSize = 0;
340878c2f44SGeorge Bișoc TokenUser = NULL;
341878c2f44SGeorge Bișoc RelSD = NULL;
342878c2f44SGeorge Bișoc ServiceDacl = NULL;
343878c2f44SGeorge Bișoc
344878c2f44SGeorge Bișoc /* Query the logged in user of the current security context (aka token) */
345878c2f44SGeorge Bișoc Status = IntQueryUserSecurityIdentification(&TokenUser);
346878c2f44SGeorge Bișoc if (!TokenUser)
347878c2f44SGeorge Bișoc {
348878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to query the token user (Status 0x%08lx)\n", Status);
349878c2f44SGeorge Bișoc return Status;
350878c2f44SGeorge Bișoc }
351878c2f44SGeorge Bișoc
352878c2f44SGeorge Bișoc /* Initialize the absolute security descriptor */
353878c2f44SGeorge Bișoc Status = RtlCreateSecurityDescriptor(&AbsSD, SECURITY_DESCRIPTOR_REVISION);
354878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
355878c2f44SGeorge Bișoc {
356878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to initialize absolute SD (Status 0x%08lx)\n", Status);
357878c2f44SGeorge Bișoc goto Quit;
358878c2f44SGeorge Bișoc }
359878c2f44SGeorge Bișoc
360878c2f44SGeorge Bișoc /*
361878c2f44SGeorge Bișoc * Build up the size of access control
362878c2f44SGeorge Bișoc * list (the DACL) necessary to initialize
363878c2f44SGeorge Bișoc * our ACL. The first two entry members
364878c2f44SGeorge Bișoc * of ACL field are the authenticated user
365878c2f44SGeorge Bișoc * that is associated with the security
366878c2f44SGeorge Bișoc * context of the token. Then here come
367878c2f44SGeorge Bișoc * the last two entries which are admins.
368878c2f44SGeorge Bișoc * Why the ACL contains two ACEs of the
369878c2f44SGeorge Bișoc * same SID is because of service access
370878c2f44SGeorge Bișoc * rights and ACE inheritance.
371878c2f44SGeorge Bișoc *
372878c2f44SGeorge Bișoc * A service is composed of a default
373878c2f44SGeorge Bișoc * desktop and window station upon
374878c2f44SGeorge Bișoc * booting the system. On Windows connection
375878c2f44SGeorge Bișoc * to such service is being made if no
376878c2f44SGeorge Bișoc * default window station and desktop handles
377878c2f44SGeorge Bișoc * were created before. The desktop and winsta
378878c2f44SGeorge Bișoc * objects grant access on a separate type basis.
379878c2f44SGeorge Bișoc * The user is granted full access to the window
380878c2f44SGeorge Bișoc * station first and then full access to the desktop.
381878c2f44SGeorge Bișoc * After that admins are granted specific rights
382878c2f44SGeorge Bișoc * separately, just like the user. Ultimately the
383878c2f44SGeorge Bișoc * ACEs that handle desktop rights management are
384878c2f44SGeorge Bișoc * inherited to the default desktop object so
385878c2f44SGeorge Bișoc * that there's no need to have a separate security
386878c2f44SGeorge Bișoc * descriptor for the desktop object alone.
387878c2f44SGeorge Bișoc */
388878c2f44SGeorge Bișoc DaclSize = sizeof(ACL) +
389878c2f44SGeorge Bișoc sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
390878c2f44SGeorge Bișoc sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
391878c2f44SGeorge Bișoc sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid) +
392878c2f44SGeorge Bișoc sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid);
393878c2f44SGeorge Bișoc
394878c2f44SGeorge Bișoc /* Allocate memory for service DACL */
395878c2f44SGeorge Bișoc ServiceDacl = IntAllocateSecurityBuffer(DaclSize);
396878c2f44SGeorge Bișoc if (!ServiceDacl)
397878c2f44SGeorge Bișoc {
398878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to allocate memory for service DACL!\n");
399878c2f44SGeorge Bișoc Status = STATUS_NO_MEMORY;
400878c2f44SGeorge Bișoc goto Quit;
401878c2f44SGeorge Bișoc }
402878c2f44SGeorge Bișoc
403878c2f44SGeorge Bișoc /* Now create the DACL */
404878c2f44SGeorge Bișoc Status = RtlCreateAcl(ServiceDacl,
405878c2f44SGeorge Bișoc DaclSize,
406878c2f44SGeorge Bișoc ACL_REVISION);
407878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
408878c2f44SGeorge Bișoc {
409878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to create service DACL (Status 0x%08lx)\n", Status);
410878c2f44SGeorge Bișoc goto Quit;
411878c2f44SGeorge Bișoc }
412878c2f44SGeorge Bișoc
413878c2f44SGeorge Bișoc /*
414878c2f44SGeorge Bișoc * The authenticated user is the ultimate and absolute
415878c2f44SGeorge Bișoc * king in charge of the created (or opened, whatever that is)
416878c2f44SGeorge Bișoc * window station object.
417878c2f44SGeorge Bișoc */
418878c2f44SGeorge Bișoc Status = RtlAddAccessAllowedAceEx(ServiceDacl,
419878c2f44SGeorge Bișoc ACL_REVISION,
420878c2f44SGeorge Bișoc 0,
421878c2f44SGeorge Bișoc WINSTA_ACCESS_ALL,
422878c2f44SGeorge Bișoc TokenUser->User.Sid);
423878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
424878c2f44SGeorge Bișoc {
425878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for authenticated user (Status 0x%08lx)\n", Status);
426878c2f44SGeorge Bișoc goto Quit;
427878c2f44SGeorge Bișoc }
428878c2f44SGeorge Bișoc
429878c2f44SGeorge Bișoc /*
430878c2f44SGeorge Bișoc * The authenticated user also has the ultimate power
431878c2f44SGeorge Bișoc * over the desktop object as well. This ACE cannot
432878c2f44SGeorge Bișoc * be propagated but inherited. See the comment
433878c2f44SGeorge Bișoc * above regarding ACL size for further explanation.
434878c2f44SGeorge Bișoc */
435878c2f44SGeorge Bișoc Status = RtlAddAccessAllowedAceEx(ServiceDacl,
436878c2f44SGeorge Bișoc ACL_REVISION,
437878c2f44SGeorge Bișoc INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
438878c2f44SGeorge Bișoc DESKTOP_ALL_ACCESS,
439878c2f44SGeorge Bișoc TokenUser->User.Sid);
440878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
441878c2f44SGeorge Bișoc {
442878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for authenticated user (Status 0x%08lx)\n", Status);
443878c2f44SGeorge Bișoc goto Quit;
444878c2f44SGeorge Bișoc }
445878c2f44SGeorge Bișoc
446878c2f44SGeorge Bișoc /*
447878c2f44SGeorge Bișoc * Administrators can only enumerate window
448878c2f44SGeorge Bișoc * stations within a desktop.
449878c2f44SGeorge Bișoc */
450878c2f44SGeorge Bișoc Status = RtlAddAccessAllowedAceEx(ServiceDacl,
451878c2f44SGeorge Bișoc ACL_REVISION,
452878c2f44SGeorge Bișoc 0,
453878c2f44SGeorge Bișoc WINSTA_ENUMERATE,
454878c2f44SGeorge Bișoc SeExports->SeAliasAdminsSid);
455878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
456878c2f44SGeorge Bișoc {
457878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for admins (Status 0x%08lx)\n", Status);
458878c2f44SGeorge Bișoc goto Quit;
459878c2f44SGeorge Bișoc }
460878c2f44SGeorge Bișoc
461878c2f44SGeorge Bișoc /*
462878c2f44SGeorge Bișoc * Administrators have some share of power over
463878c2f44SGeorge Bișoc * the desktop object. They can enumerate desktops,
464878c2f44SGeorge Bișoc * write and read upon the object itself.
465878c2f44SGeorge Bișoc */
466878c2f44SGeorge Bișoc Status = RtlAddAccessAllowedAceEx(ServiceDacl,
467878c2f44SGeorge Bișoc ACL_REVISION,
468878c2f44SGeorge Bișoc INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
469878c2f44SGeorge Bișoc DESKTOP_ENUMERATE | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS,
470878c2f44SGeorge Bișoc SeExports->SeAliasAdminsSid);
471878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
472878c2f44SGeorge Bișoc {
473878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for admins (Status 0x%08lx)\n", Status);
474878c2f44SGeorge Bișoc goto Quit;
475878c2f44SGeorge Bișoc }
476878c2f44SGeorge Bișoc
477878c2f44SGeorge Bișoc /* Set the DACL to absolute SD */
478878c2f44SGeorge Bișoc Status = RtlSetDaclSecurityDescriptor(&AbsSD,
479878c2f44SGeorge Bișoc TRUE,
480878c2f44SGeorge Bișoc ServiceDacl,
481878c2f44SGeorge Bișoc FALSE);
482878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
483878c2f44SGeorge Bișoc {
484878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to set up service DACL to absolute SD (Status 0x%08lx)\n", Status);
485878c2f44SGeorge Bișoc goto Quit;
486878c2f44SGeorge Bișoc }
487878c2f44SGeorge Bișoc
488878c2f44SGeorge Bișoc /* This descriptor is ownerless */
489878c2f44SGeorge Bișoc Status = RtlSetOwnerSecurityDescriptor(&AbsSD,
490878c2f44SGeorge Bișoc NULL,
491878c2f44SGeorge Bișoc FALSE);
492878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
493878c2f44SGeorge Bișoc {
494878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as ownerless (Status 0x%08lx)\n", Status);
495878c2f44SGeorge Bișoc goto Quit;
496878c2f44SGeorge Bișoc }
497878c2f44SGeorge Bișoc
498878c2f44SGeorge Bișoc /* This descriptor has no primary group */
499878c2f44SGeorge Bișoc Status = RtlSetGroupSecurityDescriptor(&AbsSD,
500878c2f44SGeorge Bișoc NULL,
501878c2f44SGeorge Bișoc FALSE);
502878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
503878c2f44SGeorge Bișoc {
504878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as having no primary group (Status 0x%08lx)\n", Status);
505878c2f44SGeorge Bișoc goto Quit;
506878c2f44SGeorge Bișoc }
507878c2f44SGeorge Bișoc
508878c2f44SGeorge Bișoc /*
509878c2f44SGeorge Bișoc * Determine how much size is needed to allocate
510878c2f44SGeorge Bișoc * memory space for our relative security descriptor.
511878c2f44SGeorge Bișoc */
512878c2f44SGeorge Bișoc Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
513878c2f44SGeorge Bișoc NULL,
514878c2f44SGeorge Bișoc &RelSDSize);
515878c2f44SGeorge Bișoc if (Status != STATUS_BUFFER_TOO_SMALL)
516878c2f44SGeorge Bișoc {
517878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Unexpected status code, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
518878c2f44SGeorge Bișoc goto Quit;
519878c2f44SGeorge Bișoc }
520878c2f44SGeorge Bișoc
521878c2f44SGeorge Bișoc /* Allocate memory for this */
522878c2f44SGeorge Bișoc RelSD = IntAllocateSecurityBuffer(RelSDSize);
523878c2f44SGeorge Bișoc if (!RelSD)
524878c2f44SGeorge Bișoc {
525878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to allocate memory pool for relative SD!\n");
526878c2f44SGeorge Bișoc Status = STATUS_NO_MEMORY;
527878c2f44SGeorge Bișoc goto Quit;
528878c2f44SGeorge Bișoc }
529878c2f44SGeorge Bișoc
530878c2f44SGeorge Bișoc /* Convert the absolute SD into a relative one now */
531878c2f44SGeorge Bișoc Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
532878c2f44SGeorge Bișoc RelSD,
533878c2f44SGeorge Bișoc &RelSDSize);
534878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
535878c2f44SGeorge Bișoc {
536878c2f44SGeorge Bișoc ERR("IntCreateServiceSecurity(): Failed to convert absolute SD to a relative one (Status 0x%08lx)\n", Status);
537878c2f44SGeorge Bișoc goto Quit;
538878c2f44SGeorge Bișoc }
539878c2f44SGeorge Bișoc
540878c2f44SGeorge Bișoc /* All good, give the SD to the caller */
541878c2f44SGeorge Bișoc *ServiceSd = RelSD;
542878c2f44SGeorge Bișoc
543878c2f44SGeorge Bișoc Quit:
544878c2f44SGeorge Bișoc if (ServiceDacl)
545878c2f44SGeorge Bișoc {
546878c2f44SGeorge Bișoc IntFreeSecurityBuffer(ServiceDacl);
547878c2f44SGeorge Bișoc }
548878c2f44SGeorge Bișoc
549878c2f44SGeorge Bișoc if (TokenUser)
550878c2f44SGeorge Bișoc {
551878c2f44SGeorge Bișoc IntFreeSecurityBuffer(TokenUser);
552878c2f44SGeorge Bișoc }
553878c2f44SGeorge Bișoc
554878c2f44SGeorge Bișoc if (!NT_SUCCESS(Status))
555878c2f44SGeorge Bișoc {
556878c2f44SGeorge Bișoc if (RelSD)
557878c2f44SGeorge Bișoc {
558878c2f44SGeorge Bișoc IntFreeSecurityBuffer(RelSD);
559878c2f44SGeorge Bișoc }
560878c2f44SGeorge Bișoc }
561878c2f44SGeorge Bișoc
562878c2f44SGeorge Bișoc return Status;
563878c2f44SGeorge Bișoc }
564878c2f44SGeorge Bișoc
565878c2f44SGeorge Bișoc /* EOF */
566