xref: /reactos/dll/win32/advapi32/misc/logon.c (revision d6eebaa4)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS system libraries
4  * FILE:        lib/advapi32/misc/logon.c
5  * PURPOSE:     Logon functions
6  * PROGRAMMER:  Eric Kohl
7  */
8 
9 #include <advapi32.h>
10 WINE_DEFAULT_DEBUG_CHANNEL(advapi);
11 
12 /* GLOBALS *****************************************************************/
13 
14 static const CHAR AdvapiTokenSourceName[] = "Advapi  ";
15 C_ASSERT(sizeof(AdvapiTokenSourceName) == RTL_FIELD_SIZE(TOKEN_SOURCE, SourceName) + 1);
16 
17 HANDLE LsaHandle = NULL;
18 ULONG AuthenticationPackage = 0;
19 
20 /* FUNCTIONS ***************************************************************/
21 
22 static
23 NTSTATUS
24 OpenLogonLsaHandle(VOID)
25 {
26     LSA_STRING LogonProcessName;
27     LSA_STRING PackageName;
28     LSA_OPERATIONAL_MODE SecurityMode = 0;
29     NTSTATUS Status;
30 
31     RtlInitAnsiString((PANSI_STRING)&LogonProcessName,
32                       "User32LogonProcess");
33 
34     Status = LsaRegisterLogonProcess(&LogonProcessName,
35                                      &LsaHandle,
36                                      &SecurityMode);
37     if (!NT_SUCCESS(Status))
38     {
39         TRACE("LsaRegisterLogonProcess failed (Status 0x%08lx)\n", Status);
40         goto done;
41     }
42 
43     RtlInitAnsiString((PANSI_STRING)&PackageName,
44                       MSV1_0_PACKAGE_NAME);
45 
46     Status = LsaLookupAuthenticationPackage(LsaHandle,
47                                             &PackageName,
48                                             &AuthenticationPackage);
49     if (!NT_SUCCESS(Status))
50     {
51         TRACE("LsaLookupAuthenticationPackage failed (Status 0x%08lx)\n", Status);
52         goto done;
53     }
54 
55     TRACE("AuthenticationPackage: 0x%08lx\n", AuthenticationPackage);
56 
57 done:
58     if (!NT_SUCCESS(Status))
59     {
60         if (LsaHandle != NULL)
61         {
62             Status = LsaDeregisterLogonProcess(LsaHandle);
63             if (!NT_SUCCESS(Status))
64             {
65                 TRACE("LsaDeregisterLogonProcess failed (Status 0x%08lx)\n", Status);
66             }
67         }
68     }
69 
70     return Status;
71 }
72 
73 
74 NTSTATUS
75 CloseLogonLsaHandle(VOID)
76 {
77     NTSTATUS Status = STATUS_SUCCESS;
78 
79     if (LsaHandle != NULL)
80     {
81         Status = LsaDeregisterLogonProcess(LsaHandle);
82         if (!NT_SUCCESS(Status))
83         {
84             TRACE("LsaDeregisterLogonProcess failed (Status 0x%08lx)\n", Status);
85         }
86     }
87 
88     return Status;
89 }
90 
91 
92 /**
93  * @brief
94  * Creates a default security descriptor that is going
95  * to be used by both the newly created process and thread
96  * by a call to CreateProcessAsUserA/W. This descriptor also
97  * serves for the newly duplicated token object that is going
98  * to be set for the token which acts as the main user.
99  *
100  * @param[in] TokenHandle
101  * A handle to a token. The function will use this token to
102  * query security details such as the owner and primary group
103  * associated with the security context of this token. The
104  * obtained information will then be assigned to the security
105  * descriptor.
106  *
107  * @param[out] Sd
108  * A pointer to an allocated security descriptor that is given
109  * to the caller.
110  *
111  * @return
112  * Return TRUE if the security descriptor has been successfully
113  * created, FALSE otherwise.
114  *
115  * @remarks
116  * When a process is created on behald of the user's security context
117  * this user will be the owner and responsible for that process. Whatever
118  * objects created or stuff done within the process space is at the
119  * discretion of the user, that is, further objects created are in
120  * charge by the user himself as is the owner of the process.
121  *
122  * !!!NOTE!!! -- On Windows the security descriptor is created by using
123  * CreatePrivateObjectSecurity(Ex) API call. Whilst the way the security
124  * descriptor is created in our end is not wrong per se, this function
125  * serves a placeholder until CreatePrivateObjectSecurity is implemented.
126  */
127 static
128 BOOL
129 CreateDefaultProcessSecurityCommon(
130     _In_ HANDLE TokenHandle,
131     _Out_ PSECURITY_DESCRIPTOR *Sd)
132 {
133     NTSTATUS Status;
134     BOOL Success;
135     PACL Dacl;
136     PTOKEN_OWNER OwnerOfToken;
137     PTOKEN_PRIMARY_GROUP PrimaryGroupOfToken;
138     SECURITY_DESCRIPTOR AbsoluteSd;
139     ULONG DaclSize, TokenOwnerSize, PrimaryGroupSize, RelativeSDSize = 0;
140     PSID OwnerSid = NULL, SystemSid = NULL, PrimaryGroupSid = NULL;
141     PSECURITY_DESCRIPTOR RelativeSD = NULL;
142     static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
143 
144     /*
145      * Since we do not know how much space
146      * is needed to allocate the buffer to
147      * hold the token owner, first we must
148      * query the exact size.
149      */
150     Status = NtQueryInformationToken(TokenHandle,
151                                      TokenOwner,
152                                      NULL,
153                                      0,
154                                      &TokenOwnerSize);
155     if (Status != STATUS_BUFFER_TOO_SMALL)
156     {
157         ERR("CreateDefaultProcessSecurityCommon(): Unexpected status code returned, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
158         return FALSE;
159     }
160 
161     /* We have the required space size, allocate the buffer now */
162     OwnerOfToken = RtlAllocateHeap(RtlGetProcessHeap(),
163                                    HEAP_ZERO_MEMORY,
164                                    TokenOwnerSize);
165     if (OwnerOfToken == NULL)
166     {
167         ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for token owner!\n");
168         return FALSE;
169     }
170 
171     /* Now query the token owner */
172     Status = NtQueryInformationToken(TokenHandle,
173                                      TokenOwner,
174                                      OwnerOfToken,
175                                      TokenOwnerSize,
176                                      &TokenOwnerSize);
177     if (!NT_SUCCESS(Status))
178     {
179         ERR("CreateDefaultProcessSecurityCommon(): Failed to query the token owner (Status 0x%08lx)\n", Status);
180         Success = FALSE;
181         goto Quit;
182     }
183 
184     /* Do the same process but for the primary group now */
185     Status = NtQueryInformationToken(TokenHandle,
186                                      TokenPrimaryGroup,
187                                      NULL,
188                                      0,
189                                      &PrimaryGroupSize);
190     if (Status != STATUS_BUFFER_TOO_SMALL)
191     {
192         ERR("CreateDefaultProcessSecurityCommon(): Unexpected status code returned, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
193         Success = FALSE;
194         goto Quit;
195     }
196 
197     /* Allocate the buffer */
198     PrimaryGroupOfToken = RtlAllocateHeap(RtlGetProcessHeap(),
199                                           HEAP_ZERO_MEMORY,
200                                           PrimaryGroupSize);
201     if (PrimaryGroupOfToken == NULL)
202     {
203         ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for primary group token!\n");
204         Success = FALSE;
205         goto Quit;
206     }
207 
208     /* Query the primary group now */
209     Status = NtQueryInformationToken(TokenHandle,
210                                      TokenPrimaryGroup,
211                                      PrimaryGroupOfToken,
212                                      PrimaryGroupSize,
213                                      &PrimaryGroupSize);
214     if (!NT_SUCCESS(Status))
215     {
216         ERR("CreateDefaultProcessSecurityCommon(): Failed to query the token owner (Status 0x%08lx)\n", Status);
217         Success = FALSE;
218         goto Quit;
219     }
220 
221     /* Create the SYSTEM SID */
222     if (!AllocateAndInitializeSid(&NtAuthority,
223                                   1,
224                                   SECURITY_LOCAL_SYSTEM_RID,
225                                   0, 0, 0, 0, 0, 0, 0,
226                                   &SystemSid))
227     {
228         ERR("CreateDefaultProcessSecurityCommon(): Failed to create Local System SID (error code %d)\n", GetLastError());
229         Success = FALSE;
230         goto Quit;
231     }
232 
233     /* Cache the token owner and primary group SID */
234     OwnerSid = OwnerOfToken->Owner;
235     PrimaryGroupSid = PrimaryGroupOfToken->PrimaryGroup;
236 
237     /* Set up the DACL size */
238     DaclSize = sizeof(ACL) +
239                sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(OwnerSid) +
240                sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(SystemSid);
241 
242     /* Allocate buffer for the DACL */
243     Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
244                            HEAP_ZERO_MEMORY,
245                            DaclSize);
246     if (Dacl == NULL)
247     {
248         ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for DACL!\n");
249         Success = FALSE;
250         goto Quit;
251     }
252 
253     /* Initialize the DACL */
254     if (!InitializeAcl(Dacl, DaclSize, ACL_REVISION))
255     {
256         ERR("CreateDefaultProcessSecurityCommon(): Failed to initialize DACL (error code %d)\n", GetLastError());
257         Success = FALSE;
258         goto Quit;
259     }
260 
261     /* Give full powers to the owner */
262     if (!AddAccessAllowedAce(Dacl,
263                              ACL_REVISION,
264                              GENERIC_ALL,
265                              OwnerSid))
266     {
267         ERR("CreateDefaultProcessSecurityCommon(): Failed to set up ACE for owner (error code %d)\n", GetLastError());
268         Success = FALSE;
269         goto Quit;
270     }
271 
272     /* Give full powers to SYSTEM as well */
273     if (!AddAccessAllowedAce(Dacl,
274                              ACL_REVISION,
275                              GENERIC_ALL,
276                              SystemSid))
277     {
278         ERR("CreateDefaultProcessSecurityCommon(): Failed to set up ACE for SYSTEM (error code %d)\n", GetLastError());
279         Success = FALSE;
280         goto Quit;
281     }
282 
283     /* Initialize the descriptor in absolute format */
284     if (!InitializeSecurityDescriptor(&AbsoluteSd, SECURITY_DESCRIPTOR_REVISION))
285     {
286         ERR("CreateDefaultProcessSecurityCommon(): Failed to initialize absolute security descriptor (error code %d)\n", GetLastError());
287         Success = FALSE;
288         goto Quit;
289     }
290 
291     /* Set the DACL to the security descriptor */
292     if (!SetSecurityDescriptorDacl(&AbsoluteSd, TRUE, Dacl, FALSE))
293     {
294         ERR("CreateDefaultProcessSecurityCommon(): Failed to set up DACL to absolute security descriptor (error code %d)\n", GetLastError());
295         Success = FALSE;
296         goto Quit;
297     }
298 
299     /* Set the owner for this descriptor */
300     if (!SetSecurityDescriptorOwner(&AbsoluteSd, OwnerSid, FALSE))
301     {
302         ERR("CreateDefaultProcessSecurityCommon(): Failed to set up owner to absolute security descriptor (error code %d)\n", GetLastError());
303         Success = FALSE;
304         goto Quit;
305     }
306 
307     /* Set the primary group for this descriptor */
308     if (!SetSecurityDescriptorGroup(&AbsoluteSd, PrimaryGroupSid, FALSE))
309     {
310         ERR("CreateDefaultProcessSecurityCommon(): Failed to set up group to absolute security descriptor (error code %d)\n", GetLastError());
311         Success = FALSE;
312         goto Quit;
313     }
314 
315     /*
316      * Determine the exact size space of the absolute
317      * descriptor so that we can allocate a buffer
318      * to hold the descriptor in a converted self
319      * relative format.
320      */
321     if (!MakeSelfRelativeSD(&AbsoluteSd, NULL, &RelativeSDSize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
322     {
323         ERR("CreateDefaultProcessSecurityCommon(): Unexpected error code (error code %d -- must be ERROR_INSUFFICIENT_BUFFER)\n", GetLastError());
324         Success = FALSE;
325         goto Quit;
326     }
327 
328     /* Allocate the buffer */
329     RelativeSD = RtlAllocateHeap(RtlGetProcessHeap(),
330                                  HEAP_ZERO_MEMORY,
331                                  RelativeSDSize);
332     if (RelativeSD == NULL)
333     {
334         ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for self relative descriptor!\n");
335         Success = FALSE;
336         goto Quit;
337     }
338 
339     /* Convert to a self relative format now */
340     if (!MakeSelfRelativeSD(&AbsoluteSd, RelativeSD, &RelativeSDSize))
341     {
342         ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate relative SD, buffer too smal (error code %d)\n", GetLastError());
343         Success = FALSE;
344         goto Quit;
345     }
346 
347     /* Success, give the descriptor to the caller */
348     *Sd = RelativeSD;
349     Success = TRUE;
350 
351 Quit:
352     /* Free all the stuff we have allocated */
353     if (OwnerOfToken != NULL)
354         RtlFreeHeap(RtlGetProcessHeap(), 0, OwnerOfToken);
355 
356     if (PrimaryGroupOfToken != NULL)
357         RtlFreeHeap(RtlGetProcessHeap(), 0, PrimaryGroupOfToken);
358 
359     if (SystemSid != NULL)
360         FreeSid(SystemSid);
361 
362     if (Dacl != NULL)
363         RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
364 
365     if (Success == FALSE)
366     {
367         if (RelativeSD != NULL)
368         {
369             RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSD);
370         }
371     }
372 
373     return Success;
374 }
375 
376 
377 /**
378  * @brief
379  * Changes the object security information of a process
380  * and thread that belongs to the process with new security
381  * data, basically by replacing the previous security descriptor
382  * with a new one.
383  *
384  * @param[in] ProcessHandle
385  * A handle to a valid process of which security information is
386  * to be changed by setting up a new security descriptor.
387  *
388  * @param[in] ThreadHandle
389  * A handle to a valid thread of which security information is
390  * to be changed by setting up a new security descriptor.
391  *
392  * @param[in] ProcessSecurity
393  * A pointer to a security descriptor that is for the process.
394  *
395  * @param[in] ThreadSecurity
396  * A pointer to a security descriptor that is for the thread.
397  *
398  * @return
399  * Return TRUE if new security information has been set, FALSE
400  * otherwise.
401  */
402 static
403 BOOL
404 InsertProcessSecurityCommon(
405     _In_ HANDLE ProcessHandle,
406     _In_ HANDLE ThreadHandle,
407     _In_ PSECURITY_DESCRIPTOR ProcessSecurity,
408     _In_ PSECURITY_DESCRIPTOR ThreadSecurity)
409 {
410     /* Set new security data for the process */
411     if (!SetKernelObjectSecurity(ProcessHandle,
412                                  DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
413                                  ProcessSecurity))
414     {
415         ERR("InsertProcessSecurityCommon(): Failed to set security for process (error code %d)\n", GetLastError());
416         return FALSE;
417     }
418 
419     /* Set new security data for the thread */
420     if (!SetKernelObjectSecurity(ThreadHandle,
421                                  DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
422                                  ThreadSecurity))
423     {
424         ERR("InsertProcessSecurityCommon(): Failed to set security for thread (error code %d)\n", GetLastError());
425         return FALSE;
426     }
427 
428     return TRUE;
429 }
430 
431 
432 /**
433  * @brief
434  * Sets a primary token to the newly created process.
435  * The primary token that gets assigned to is a token
436  * whose security context is associated with the logged
437  * in user. For futher documentation information, see
438  * Remarks.
439  *
440  * @param[in] ImpersonateAsSelf
441  * If set to TRUE, the function will act on behalf of
442  * the calling process by impersonating its security context.
443  * Generally the caller will disable impersonation and attempt
444  * to act on behalf of the said main process as a first tentative
445  * to acquire the needed privilege in order to assign a token
446  * to the process. If set to FALSE, the function won't act on behalf
447  * of the calling process.
448  *
449  * @param[in] ProcessHandle
450  * A handle to the newly created process. The function will use it
451  * as a mean to assign the primary token to this process.
452  *
453  * @param[in] ThreadHandle
454  * A handle to the newly and primary created thread associated with
455  * the process.
456  *
457  * @param[in] DuplicatedTokenHandle
458  * A handle to a duplicated access token. This token represents as a primary
459  * one, initially duplicated in form as a primary type from an impersonation
460  * type.
461  *
462  * @return
463  * STATUS_SUCCESS is returned if token assignment to process succeeded, otherwise
464  * a failure NTSTATUS code is returned. A potential failure status code is
465  * STATUS_ACCESS_DENIED which means the caller doesn't have enough rights
466  * to grant access for primary token assignment to process.
467  *
468  * @remarks
469  * This function acts like an internal helper for CreateProcessAsUserCommon (and as
470  * such for CreateProcessAsUserW/A as well) as once a process is created, the
471  * function is tasked to assign the security context of the logged in user to
472  * that process. However, the rate of success of inserting the token into the
473  * process ultimately depends on the caller.
474  *
475  * The caller will either succeed or fail at acquiring SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
476  * privilege depending on the security context of the user. If it's allowed, the caller
477  * would generally acquire such privilege immediately but if not, the caller will attempt
478  * to do a second try.
479  */
480 static
481 NTSTATUS
482 InsertTokenToProcessCommon(
483     _In_ BOOL ImpersonateAsSelf,
484     _In_ HANDLE ProcessHandle,
485     _In_ HANDLE ThreadHandle,
486     _In_ HANDLE DuplicatedTokenHandle)
487 {
488     NTSTATUS Status;
489     PROCESS_ACCESS_TOKEN AccessToken;
490     BOOLEAN PrivilegeSet;
491     BOOLEAN HavePrivilege;
492 
493     /*
494      * Assume the SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
495      * privilege hasn't been set.
496      */
497     PrivilegeSet = FALSE;
498 
499     /*
500      * The caller asked that we must impersonate as
501      * ourselves, that is, we'll be going to impersonate
502      * the security context of the calling process. If
503      * self impersonation fails then the caller has
504      * to do a "rinse and repeat" approach.
505      */
506     if (ImpersonateAsSelf)
507     {
508         Status = RtlImpersonateSelf(SecurityImpersonation);
509         if (!NT_SUCCESS(Status))
510         {
511             ERR("RtlImpersonateSelf(SecurityImpersonation) failed, Status 0x%08x\n", Status);
512             return Status;
513         }
514     }
515 
516     /*
517      * Attempt to acquire the process primary token assignment privilege
518      * in case we actually need it.
519      * The call will either succeed or fail when the caller has (or has not)
520      * enough rights.
521      * The last situation may not be dramatic for us. Indeed it may happen
522      * that the user-provided token is a restricted version of the caller's
523      * primary token (aka. a "child" token), or both tokens inherit (i.e. are
524      * children, and are together "siblings") from a common parent token.
525      * In this case the NT kernel allows us to assign the token to the child
526      * process without the need for the assignment privilege, which is fine.
527      * On the contrary, if the user-provided token is completely arbitrary,
528      * then the NT kernel will enforce the presence of the assignment privilege:
529      * because we failed (by assumption) to assign the privilege, the process
530      * token assignment will fail as required. It is then the job of the
531      * caller to manually acquire the necessary privileges.
532      */
533     Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
534                                 TRUE, TRUE, &PrivilegeSet);
535     HavePrivilege = NT_SUCCESS(Status);
536     if (!HavePrivilege)
537     {
538         ERR("RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE) failed, Status 0x%08lx, "
539             "attempting to continue without it...\n", Status);
540     }
541 
542     /*
543      * Assign the duplicated token and thread
544      * handle to the structure so that we'll
545      * use it to assign the primary token
546      * to process.
547      */
548     AccessToken.Token = DuplicatedTokenHandle;
549     AccessToken.Thread = ThreadHandle;
550 
551     /* Set the new process token */
552     Status = NtSetInformationProcess(ProcessHandle,
553                                      ProcessAccessToken,
554                                      (PVOID)&AccessToken,
555                                      sizeof(AccessToken));
556 
557     /* Restore the privilege */
558     if (HavePrivilege)
559     {
560         RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
561                            PrivilegeSet, TRUE, &PrivilegeSet);
562     }
563 
564     /*
565      * Check again if the caller wanted to impersonate
566      * as self. If that is the case we must revert this
567      * impersonation back.
568      */
569     if (ImpersonateAsSelf)
570     {
571         RevertToSelf();
572     }
573 
574     /*
575      * Finally, check if we actually succeeded on assigning
576      * a primary token to the process. If we failed, oh well,
577      * asta la vista baby e arrivederci. The caller has to do
578      * a rinse and repeat approach.
579      */
580     if (!NT_SUCCESS(Status))
581     {
582         ERR("Failed to assign primary token to the process (Status 0x%08lx)\n", Status);
583         return Status;
584     }
585 
586     return STATUS_SUCCESS;
587 }
588 
589 /**
590  * @brief
591  * Internal function that serves as a helper for
592  * CreateProcessAsUserW/A routines on creating
593  * a process within the context of the logged in
594  * user.
595  *
596  * @param[in] hToken
597  * A handle to an access token that is associated
598  * with the logged in user. If the caller does not
599  * submit a token, the helper will immediately quit
600  * and return success, and the newly created process
601  * will be created upon using the default security
602  * context.
603  *
604  * @param[in] dwCreationFlags
605  * Bit masks containing the creation process flags.
606  * The function uses this parameter to determine
607  * if the process wasn't created in a suspended way
608  * and if not the function will resume the main thread.
609  *
610  * @param[in] lpProcessAttributes
611  * A pointer to process attributes. This function uses
612  * this parameter to gather the security descriptor,
613  * if ever present. If it is, this descriptor takes
614  * precedence over the default one when setting
615  * new security information to the process.
616  *
617  * @param[in] lpThreadAttributes
618  * A pointer to thread attributes. This function uses
619  * this parameter to gather the security descriptor,
620  * if ever present. If it is, this descriptor takes
621  * precedence over the default one when setting
622  * new security information to the thread.
623  *
624  * @param[in,out] lpProcessInformation
625  * A pointer to a structure that contains process creation
626  * information data. Such pointer contains the process
627  * and thread handles and whatnot.
628  *
629  * @return
630  * Returns TRUE if the helper has successfully assigned
631  * the newly created process the user's security context
632  * to that process, otherwise FALSE is returned.
633  *
634  * @remarks
635  * In order for the helper function to assign the primary
636  * token to the process, it has to do a "rinse and repeat"
637  * approach. That is, the helper will stop the impersonation
638  * and attempt to assign the token to process by acting
639  * on behalf of the main process' security context. If that
640  * fails, the function will do a second attempt by doing this
641  * but with impersonation enabled instead.
642  */
643 static
644 BOOL
645 CreateProcessAsUserCommon(
646     _In_opt_ HANDLE hToken,
647     _In_ DWORD dwCreationFlags,
648     _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
649     _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
650     _Inout_ LPPROCESS_INFORMATION lpProcessInformation)
651 {
652     NTSTATUS Status = STATUS_SUCCESS, StatusOnExit;
653     BOOL Success;
654     TOKEN_TYPE Type;
655     ULONG ReturnLength;
656     OBJECT_ATTRIBUTES ObjectAttributes;
657     PSECURITY_DESCRIPTOR DefaultSd = NULL, ProcessSd, ThreadSd;
658     HANDLE hTokenDup = NULL;
659     HANDLE OriginalImpersonationToken = NULL;
660     HANDLE NullToken = NULL;
661 
662     if (hToken != NULL)
663     {
664         /* Check whether the user-provided token is a primary token */
665         // GetTokenInformation();
666         Status = NtQueryInformationToken(hToken,
667                                          TokenType,
668                                          &Type,
669                                          sizeof(Type),
670                                          &ReturnLength);
671         if (!NT_SUCCESS(Status))
672         {
673             ERR("NtQueryInformationToken() failed, Status 0x%08x\n", Status);
674             Success = FALSE;
675             goto Quit;
676         }
677 
678         if (Type != TokenPrimary)
679         {
680             ERR("Wrong token type for token 0x%p, expected TokenPrimary, got %ld\n", hToken, Type);
681             Status = STATUS_BAD_TOKEN_TYPE;
682             Success = FALSE;
683             goto Quit;
684         }
685 
686         /*
687          * Open the original token of the calling thread
688          * and halt the impersonation for the moment
689          * being. The opened thread token will be cached
690          * so that we will restore it back when we're done.
691          */
692         Status = NtOpenThreadToken(NtCurrentThread(),
693                                    TOKEN_QUERY | TOKEN_IMPERSONATE,
694                                    TRUE,
695                                    &OriginalImpersonationToken);
696         if (!NT_SUCCESS(Status))
697         {
698             /* We failed? Does this thread have a token at least? */
699             OriginalImpersonationToken = NULL;
700             if (Status != STATUS_NO_TOKEN)
701             {
702                 /*
703                  * OK so this thread has a token but we
704                  * could not open it for whatever reason.
705                  * Bail out then.
706                  */
707                 ERR("Failed to open thread token with 0x%08lx\n", Status);
708                 Success = FALSE;
709                 goto Quit;
710             }
711         }
712         else
713         {
714             /* We succeeded, stop the impersonation for now */
715             Status = NtSetInformationThread(NtCurrentThread(),
716                                             ThreadImpersonationToken,
717                                             &NullToken,
718                                             sizeof(NullToken));
719             if (!NT_SUCCESS(Status))
720             {
721                 ERR("Failed to stop impersonation with 0x%08lx\n", Status);
722                 Success = FALSE;
723                 goto Quit;
724             }
725         }
726 
727         /*
728          * Create a security descriptor that will be common for the
729          * newly created process on behalf of the context user.
730          */
731         if (!CreateDefaultProcessSecurityCommon(hToken, &DefaultSd))
732         {
733             ERR("Failed to create common security descriptor for the token for new process!\n");
734             Success = FALSE;
735             goto Quit;
736         }
737 
738         /*
739          * Duplicate the token for this new process. This token
740          * object will get a default security descriptor that we
741          * have created ourselves in ADVAPI32.
742          */
743         InitializeObjectAttributes(&ObjectAttributes,
744                                    NULL,
745                                    0,
746                                    NULL,
747                                    DefaultSd);
748         Status = NtDuplicateToken(hToken,
749                                   0,
750                                   &ObjectAttributes,
751                                   FALSE,
752                                   TokenPrimary,
753                                   &hTokenDup);
754         if (!NT_SUCCESS(Status))
755         {
756             ERR("NtDuplicateToken() failed, Status 0x%08x\n", Status);
757             Success = FALSE;
758             goto Quit;
759         }
760 
761         /*
762          * Now it's time to set the primary token into
763          * the process. On the first try, do it by
764          * impersonating the security context of the
765          * calling process (impersonate as self).
766          */
767         Status = InsertTokenToProcessCommon(TRUE,
768                                             lpProcessInformation->hProcess,
769                                             lpProcessInformation->hThread,
770                                             hTokenDup);
771         if (!NT_SUCCESS(Status))
772         {
773             /*
774              * OK, we failed. Our second (and last try) is to not
775              * impersonate as self but instead we will try by setting
776              * the original impersonation (thread) token and set the
777              * primary token to the process through this way. This is
778              * what we call -- the "rinse and repeat" approach.
779              */
780             Status = NtSetInformationThread(NtCurrentThread(),
781                                             ThreadImpersonationToken,
782                                             &OriginalImpersonationToken,
783                                             sizeof(OriginalImpersonationToken));
784             if (!NT_SUCCESS(Status))
785             {
786                 ERR("Failed to restore impersonation token for setting process token, Status 0x%08lx\n", Status);
787                 NtClose(hTokenDup);
788                 Success = FALSE;
789                 goto Quit;
790             }
791 
792             /* Retry again */
793             Status = InsertTokenToProcessCommon(FALSE,
794                                                 lpProcessInformation->hProcess,
795                                                 lpProcessInformation->hThread,
796                                                 hTokenDup);
797             if (!NT_SUCCESS(Status))
798             {
799                 /* Even the second try failed, bail out... */
800                 ERR("Failed to insert the primary token into process, Status 0x%08lx\n", Status);
801                 NtClose(hTokenDup);
802                 Success = FALSE;
803                 goto Quit;
804             }
805 
806             /* All good, now stop impersonation */
807             Status = NtSetInformationThread(NtCurrentThread(),
808                                             ThreadImpersonationToken,
809                                             &NullToken,
810                                             sizeof(NullToken));
811             if (!NT_SUCCESS(Status))
812             {
813                 ERR("Failed to unset impersonationg token after setting process token, Status 0x%08lx\n", Status);
814                 NtClose(hTokenDup);
815                 Success = FALSE;
816                 goto Quit;
817             }
818         }
819 
820         /*
821          * FIXME: As we have successfully set up a primary token to
822          * the newly created process, we must set up as well a definite
823          * limit of quota charges for this process on the context of
824          * this user.
825          */
826 
827         /*
828          * As we have successfully set the token into the process now
829          * it is time that we set up new security information for both
830          * the process and its thread as well, that is, these securable
831          * objects will grant a security descriptor. The security descriptors
832          * provided by the caller take precedence so we should use theirs
833          * if possible in this case. Otherwise both the process and thread
834          * will receive the default security descriptor that we have created
835          * ourselves.
836          *
837          * BEAR IN MIND!!! AT THE MOMENT when these securable objects get new
838          * security information, the process (and the thread) can't be opened
839          * by the creator anymore as the new owner will take in charge of
840          * the process and future objects that are going to be created within
841          * the process. For further information in regard of the documentation
842          * see https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw.
843          */
844         if (lpProcessAttributes && lpProcessAttributes->lpSecurityDescriptor)
845         {
846             ProcessSd = lpProcessAttributes->lpSecurityDescriptor;
847         }
848         else
849         {
850             ProcessSd = DefaultSd;
851         }
852 
853         if (lpThreadAttributes && lpThreadAttributes->lpSecurityDescriptor)
854         {
855             ThreadSd = lpThreadAttributes->lpSecurityDescriptor;
856         }
857         else
858         {
859             ThreadSd = DefaultSd;
860         }
861 
862         /* Set new security info to the process and thread now */
863         if (!InsertProcessSecurityCommon(lpProcessInformation->hProcess,
864                                          lpProcessInformation->hThread,
865                                          ProcessSd,
866                                          ThreadSd))
867         {
868             ERR("Failed to set new security information for process and thread!\n");
869             NtClose(hTokenDup);
870             Success = FALSE;
871             goto Quit;
872         }
873 
874         /* Close the duplicated token */
875         NtClose(hTokenDup);
876         Success = TRUE;
877     }
878 
879     /*
880      * If the caller did not supply a token then just declare
881      * ourselves as job done. The newly created process will use
882      * the default security context at this point anyway.
883      */
884     TRACE("No token supplied, the process will use default security context!\n");
885     Success = TRUE;
886 
887 Quit:
888     /*
889      * If we successfully opened the thread token before
890      * and stopped the impersonation then we have to assign
891      * its original token back and close that token we have
892      * referenced it.
893      */
894     if (OriginalImpersonationToken != NULL)
895     {
896         StatusOnExit = NtSetInformationThread(NtCurrentThread(),
897                                               ThreadImpersonationToken,
898                                               &OriginalImpersonationToken,
899                                               sizeof(OriginalImpersonationToken));
900 
901         /*
902          * We really must assert ourselves that we successfully
903          * set the original token back, otherwise if we fail
904          * then something is seriously going wrong....
905          * The status code is cached in a separate status
906          * variable because we would not want to tamper
907          * with the original status code that could have been
908          * returned by someone else above in this function code.
909          */
910         ASSERT(NT_SUCCESS(StatusOnExit));
911 
912         /* De-reference it */
913         NtClose(OriginalImpersonationToken);
914     }
915 
916     /* Terminate the process and set the last error status */
917     if (!NT_SUCCESS(Status))
918     {
919         TerminateProcess(lpProcessInformation->hProcess, Status);
920         SetLastError(RtlNtStatusToDosError(Status));
921     }
922 
923     /* Resume the main thread */
924     if (!(dwCreationFlags & CREATE_SUSPENDED))
925     {
926         ResumeThread(lpProcessInformation->hThread);
927     }
928 
929     /* Free the security descriptor from memory */
930     if (DefaultSd != NULL)
931     {
932         RtlFreeHeap(RtlGetProcessHeap(), 0, DefaultSd);
933     }
934 
935     return Success;
936 }
937 
938 
939 /*
940  * @implemented
941  */
942 BOOL
943 WINAPI
944 DECLSPEC_HOTPATCH
945 CreateProcessAsUserA(
946     _In_opt_ HANDLE hToken,
947     _In_opt_ LPCSTR lpApplicationName,
948     _Inout_opt_ LPSTR lpCommandLine,
949     _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
950     _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
951     _In_ BOOL bInheritHandles,
952     _In_ DWORD dwCreationFlags,
953     _In_opt_ LPVOID lpEnvironment,
954     _In_opt_ LPCSTR lpCurrentDirectory,
955     _In_ LPSTARTUPINFOA lpStartupInfo,
956     _Out_ LPPROCESS_INFORMATION lpProcessInformation)
957 {
958     TRACE("%p %s %s %p %p %d 0x%08x %p %s %p %p\n", hToken, debugstr_a(lpApplicationName),
959         debugstr_a(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles,
960         dwCreationFlags, lpEnvironment, debugstr_a(lpCurrentDirectory), lpStartupInfo, lpProcessInformation);
961 
962     /* Create the process with a suspended main thread */
963     if (!CreateProcessA(lpApplicationName,
964                         lpCommandLine,
965                         lpProcessAttributes,
966                         lpThreadAttributes,
967                         bInheritHandles,
968                         dwCreationFlags | CREATE_SUSPENDED,
969                         lpEnvironment,
970                         lpCurrentDirectory,
971                         lpStartupInfo,
972                         lpProcessInformation))
973     {
974         ERR("CreateProcessA failed, last error: %d\n", GetLastError());
975         return FALSE;
976     }
977 
978     /* Call the helper function */
979     return CreateProcessAsUserCommon(hToken,
980                                      dwCreationFlags,
981                                      lpProcessAttributes,
982                                      lpThreadAttributes,
983                                      lpProcessInformation);
984 }
985 
986 
987 /*
988  * @implemented
989  */
990 BOOL
991 WINAPI
992 DECLSPEC_HOTPATCH
993 CreateProcessAsUserW(
994     _In_opt_ HANDLE hToken,
995     _In_opt_ LPCWSTR lpApplicationName,
996     _Inout_opt_ LPWSTR lpCommandLine,
997     _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
998     _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
999     _In_ BOOL bInheritHandles,
1000     _In_ DWORD dwCreationFlags,
1001     _In_opt_ LPVOID lpEnvironment,
1002     _In_opt_ LPCWSTR lpCurrentDirectory,
1003     _In_ LPSTARTUPINFOW lpStartupInfo,
1004     _Out_ LPPROCESS_INFORMATION lpProcessInformation)
1005 {
1006     TRACE("%p %s %s %p %p %d 0x%08x %p %s %p %p\n", hToken, debugstr_w(lpApplicationName),
1007         debugstr_w(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles,
1008         dwCreationFlags, lpEnvironment, debugstr_w(lpCurrentDirectory), lpStartupInfo, lpProcessInformation);
1009 
1010     /* Create the process with a suspended main thread */
1011     if (!CreateProcessW(lpApplicationName,
1012                         lpCommandLine,
1013                         lpProcessAttributes,
1014                         lpThreadAttributes,
1015                         bInheritHandles,
1016                         dwCreationFlags | CREATE_SUSPENDED,
1017                         lpEnvironment,
1018                         lpCurrentDirectory,
1019                         lpStartupInfo,
1020                         lpProcessInformation))
1021     {
1022         ERR("CreateProcessW failed, last error: %d\n", GetLastError());
1023         return FALSE;
1024     }
1025 
1026     /* Call the helper function */
1027     return CreateProcessAsUserCommon(hToken,
1028                                      dwCreationFlags,
1029                                      lpProcessAttributes,
1030                                      lpThreadAttributes,
1031                                      lpProcessInformation);
1032 }
1033 
1034 
1035 /*
1036  * @implemented
1037  */
1038 BOOL
1039 WINAPI
1040 LogonUserA(
1041     _In_ LPSTR lpszUsername,
1042     _In_opt_ LPSTR lpszDomain,
1043     _In_opt_ LPSTR lpszPassword,
1044     _In_ DWORD dwLogonType,
1045     _In_ DWORD dwLogonProvider,
1046     _Out_opt_ PHANDLE phToken)
1047 {
1048     return LogonUserExA(lpszUsername,
1049                         lpszDomain,
1050                         lpszPassword,
1051                         dwLogonType,
1052                         dwLogonProvider,
1053                         phToken,
1054                         NULL,
1055                         NULL,
1056                         NULL,
1057                         NULL);
1058 }
1059 
1060 
1061 /*
1062  * @implemented
1063  */
1064 BOOL
1065 WINAPI
1066 LogonUserExA(
1067     _In_ LPSTR lpszUsername,
1068     _In_opt_ LPSTR lpszDomain,
1069     _In_opt_ LPSTR lpszPassword,
1070     _In_ DWORD dwLogonType,
1071     _In_ DWORD dwLogonProvider,
1072     _Out_opt_ PHANDLE phToken,
1073     _Out_opt_ PSID *ppLogonSid,
1074     _Out_opt_ PVOID *ppProfileBuffer,
1075     _Out_opt_ LPDWORD pdwProfileLength,
1076     _Out_opt_ PQUOTA_LIMITS pQuotaLimits)
1077 {
1078     UNICODE_STRING UserName;
1079     UNICODE_STRING Domain;
1080     UNICODE_STRING Password;
1081     BOOL ret = FALSE;
1082 
1083     UserName.Buffer = NULL;
1084     Domain.Buffer = NULL;
1085     Password.Buffer = NULL;
1086 
1087     if (!RtlCreateUnicodeStringFromAsciiz(&UserName, lpszUsername))
1088     {
1089         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1090         goto UsernameDone;
1091     }
1092 
1093     if (!RtlCreateUnicodeStringFromAsciiz(&Domain, lpszDomain))
1094     {
1095         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1096         goto DomainDone;
1097     }
1098 
1099     if (!RtlCreateUnicodeStringFromAsciiz(&Password, lpszPassword))
1100     {
1101         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1102         goto PasswordDone;
1103     }
1104 
1105     ret = LogonUserExW(UserName.Buffer,
1106                        Domain.Buffer,
1107                        Password.Buffer,
1108                        dwLogonType,
1109                        dwLogonProvider,
1110                        phToken,
1111                        ppLogonSid,
1112                        ppProfileBuffer,
1113                        pdwProfileLength,
1114                        pQuotaLimits);
1115 
1116     if (Password.Buffer != NULL)
1117         RtlFreeUnicodeString(&Password);
1118 
1119 PasswordDone:
1120     if (Domain.Buffer != NULL)
1121         RtlFreeUnicodeString(&Domain);
1122 
1123 DomainDone:
1124     if (UserName.Buffer != NULL)
1125         RtlFreeUnicodeString(&UserName);
1126 
1127 UsernameDone:
1128     return ret;
1129 }
1130 
1131 
1132 /*
1133  * @implemented
1134  */
1135 BOOL
1136 WINAPI
1137 LogonUserW(
1138     _In_ LPWSTR lpszUsername,
1139     _In_opt_ LPWSTR lpszDomain,
1140     _In_opt_ LPWSTR lpszPassword,
1141     _In_ DWORD dwLogonType,
1142     _In_ DWORD dwLogonProvider,
1143     _Out_opt_ PHANDLE phToken)
1144 {
1145     return LogonUserExW(lpszUsername,
1146                         lpszDomain,
1147                         lpszPassword,
1148                         dwLogonType,
1149                         dwLogonProvider,
1150                         phToken,
1151                         NULL,
1152                         NULL,
1153                         NULL,
1154                         NULL);
1155 }
1156 
1157 
1158 /*
1159  * @implemented
1160  */
1161 BOOL
1162 WINAPI
1163 LogonUserExW(
1164     _In_ LPWSTR lpszUsername,
1165     _In_opt_ LPWSTR lpszDomain,
1166     _In_opt_ LPWSTR lpszPassword,
1167     _In_ DWORD dwLogonType,
1168     _In_ DWORD dwLogonProvider,
1169     _Out_opt_ PHANDLE phToken,
1170     _Out_opt_ PSID *ppLogonSid,
1171     _Out_opt_ PVOID *ppProfileBuffer,
1172     _Out_opt_ LPDWORD pdwProfileLength,
1173     _Out_opt_ PQUOTA_LIMITS pQuotaLimits)
1174 {
1175     SID_IDENTIFIER_AUTHORITY LocalAuthority = {SECURITY_LOCAL_SID_AUTHORITY};
1176     SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
1177     PSID LogonSid = NULL;
1178     PSID LocalSid = NULL;
1179     LSA_STRING OriginName;
1180     UNICODE_STRING DomainName;
1181     UNICODE_STRING UserName;
1182     UNICODE_STRING Password;
1183     PMSV1_0_INTERACTIVE_LOGON AuthInfo = NULL;
1184     ULONG AuthInfoLength;
1185     ULONG_PTR Ptr;
1186     TOKEN_SOURCE TokenSource;
1187     PTOKEN_GROUPS TokenGroups = NULL;
1188     PMSV1_0_INTERACTIVE_PROFILE ProfileBuffer = NULL;
1189     ULONG ProfileBufferLength = 0;
1190     LUID Luid = {0, 0};
1191     LUID LogonId = {0, 0};
1192     HANDLE TokenHandle = NULL;
1193     QUOTA_LIMITS QuotaLimits;
1194     SECURITY_LOGON_TYPE LogonType;
1195     NTSTATUS SubStatus = STATUS_SUCCESS;
1196     NTSTATUS Status;
1197 
1198     if ((ppProfileBuffer != NULL && pdwProfileLength == NULL) ||
1199         (ppProfileBuffer == NULL && pdwProfileLength != NULL))
1200     {
1201         SetLastError(ERROR_INVALID_PARAMETER);
1202         return FALSE;
1203     }
1204 
1205     if (ppProfileBuffer != NULL && pdwProfileLength != NULL)
1206     {
1207         *ppProfileBuffer = NULL;
1208         *pdwProfileLength = 0;
1209     }
1210 
1211     if (phToken != NULL)
1212         *phToken = NULL;
1213 
1214     switch (dwLogonType)
1215     {
1216         case LOGON32_LOGON_INTERACTIVE:
1217             LogonType = Interactive;
1218             break;
1219 
1220         case LOGON32_LOGON_NETWORK:
1221             LogonType = Network;
1222             break;
1223 
1224         case LOGON32_LOGON_BATCH:
1225             LogonType = Batch;
1226             break;
1227 
1228         case LOGON32_LOGON_SERVICE:
1229             LogonType = Service;
1230             break;
1231 
1232        default:
1233             ERR("Invalid logon type: %ul\n", dwLogonType);
1234             Status = STATUS_INVALID_PARAMETER;
1235             goto done;
1236     }
1237 
1238     if (LsaHandle == NULL)
1239     {
1240         Status = OpenLogonLsaHandle();
1241         if (!NT_SUCCESS(Status))
1242             goto done;
1243     }
1244 
1245     RtlInitAnsiString((PANSI_STRING)&OriginName,
1246                       "Advapi32 Logon");
1247 
1248     RtlInitUnicodeString(&DomainName,
1249                          lpszDomain);
1250 
1251     RtlInitUnicodeString(&UserName,
1252                          lpszUsername);
1253 
1254     RtlInitUnicodeString(&Password,
1255                          lpszPassword);
1256 
1257     AuthInfoLength = sizeof(MSV1_0_INTERACTIVE_LOGON)+
1258                      DomainName.MaximumLength +
1259                      UserName.MaximumLength +
1260                      Password.MaximumLength;
1261 
1262     AuthInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1263                                HEAP_ZERO_MEMORY,
1264                                AuthInfoLength);
1265     if (AuthInfo == NULL)
1266     {
1267         Status = STATUS_INSUFFICIENT_RESOURCES;
1268         goto done;
1269     }
1270 
1271     AuthInfo->MessageType = MsV1_0InteractiveLogon;
1272 
1273     Ptr = (ULONG_PTR)AuthInfo + sizeof(MSV1_0_INTERACTIVE_LOGON);
1274 
1275     AuthInfo->LogonDomainName.Length = DomainName.Length;
1276     AuthInfo->LogonDomainName.MaximumLength = DomainName.MaximumLength;
1277     AuthInfo->LogonDomainName.Buffer = (DomainName.Buffer == NULL) ? NULL : (PWCHAR)Ptr;
1278     if (DomainName.MaximumLength > 0)
1279     {
1280         RtlCopyMemory(AuthInfo->LogonDomainName.Buffer,
1281                       DomainName.Buffer,
1282                       DomainName.MaximumLength);
1283 
1284         Ptr += DomainName.MaximumLength;
1285     }
1286 
1287     AuthInfo->UserName.Length = UserName.Length;
1288     AuthInfo->UserName.MaximumLength = UserName.MaximumLength;
1289     AuthInfo->UserName.Buffer = (PWCHAR)Ptr;
1290     if (UserName.MaximumLength > 0)
1291         RtlCopyMemory(AuthInfo->UserName.Buffer,
1292                       UserName.Buffer,
1293                       UserName.MaximumLength);
1294 
1295     Ptr += UserName.MaximumLength;
1296 
1297     AuthInfo->Password.Length = Password.Length;
1298     AuthInfo->Password.MaximumLength = Password.MaximumLength;
1299     AuthInfo->Password.Buffer = (PWCHAR)Ptr;
1300     if (Password.MaximumLength > 0)
1301         RtlCopyMemory(AuthInfo->Password.Buffer,
1302                       Password.Buffer,
1303                       Password.MaximumLength);
1304 
1305     /* Create the Logon SID */
1306     AllocateLocallyUniqueId(&LogonId);
1307     Status = RtlAllocateAndInitializeSid(&SystemAuthority,
1308                                          SECURITY_LOGON_IDS_RID_COUNT,
1309                                          SECURITY_LOGON_IDS_RID,
1310                                          LogonId.HighPart,
1311                                          LogonId.LowPart,
1312                                          SECURITY_NULL_RID,
1313                                          SECURITY_NULL_RID,
1314                                          SECURITY_NULL_RID,
1315                                          SECURITY_NULL_RID,
1316                                          SECURITY_NULL_RID,
1317                                          &LogonSid);
1318     if (!NT_SUCCESS(Status))
1319         goto done;
1320 
1321     /* Create the Local SID */
1322     Status = RtlAllocateAndInitializeSid(&LocalAuthority,
1323                                          1,
1324                                          SECURITY_LOCAL_RID,
1325                                          SECURITY_NULL_RID,
1326                                          SECURITY_NULL_RID,
1327                                          SECURITY_NULL_RID,
1328                                          SECURITY_NULL_RID,
1329                                          SECURITY_NULL_RID,
1330                                          SECURITY_NULL_RID,
1331                                          SECURITY_NULL_RID,
1332                                          &LocalSid);
1333     if (!NT_SUCCESS(Status))
1334         goto done;
1335 
1336     /* Allocate and set the token groups */
1337     TokenGroups = RtlAllocateHeap(RtlGetProcessHeap(),
1338                                   HEAP_ZERO_MEMORY,
1339                                   sizeof(TOKEN_GROUPS) + ((2 - ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES)));
1340     if (TokenGroups == NULL)
1341     {
1342         Status = STATUS_INSUFFICIENT_RESOURCES;
1343         goto done;
1344     }
1345 
1346     TokenGroups->GroupCount = 2;
1347     TokenGroups->Groups[0].Sid = LogonSid;
1348     TokenGroups->Groups[0].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED |
1349                                         SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_LOGON_ID;
1350     TokenGroups->Groups[1].Sid = LocalSid;
1351     TokenGroups->Groups[1].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED |
1352                                         SE_GROUP_ENABLED_BY_DEFAULT;
1353 
1354     /* Set the token source */
1355     RtlCopyMemory(TokenSource.SourceName,
1356                   AdvapiTokenSourceName,
1357                   sizeof(TokenSource.SourceName));
1358     AllocateLocallyUniqueId(&TokenSource.SourceIdentifier);
1359 
1360     Status = LsaLogonUser(LsaHandle,
1361                           &OriginName,
1362                           LogonType,
1363                           AuthenticationPackage,
1364                           (PVOID)AuthInfo,
1365                           AuthInfoLength,
1366                           TokenGroups,
1367                           &TokenSource,
1368                           (PVOID*)&ProfileBuffer,
1369                           &ProfileBufferLength,
1370                           &Luid,
1371                           &TokenHandle,
1372                           &QuotaLimits,
1373                           &SubStatus);
1374     if (!NT_SUCCESS(Status))
1375     {
1376         ERR("LsaLogonUser failed (Status 0x%08lx)\n", Status);
1377         goto done;
1378     }
1379 
1380     if (ProfileBuffer != NULL)
1381     {
1382         TRACE("ProfileBuffer: %p\n", ProfileBuffer);
1383         TRACE("MessageType: %u\n", ProfileBuffer->MessageType);
1384 
1385         TRACE("FullName: %p\n", ProfileBuffer->FullName.Buffer);
1386         TRACE("FullName: %S\n", ProfileBuffer->FullName.Buffer);
1387 
1388         TRACE("LogonServer: %p\n", ProfileBuffer->LogonServer.Buffer);
1389         TRACE("LogonServer: %S\n", ProfileBuffer->LogonServer.Buffer);
1390     }
1391 
1392     TRACE("Luid: 0x%08lx%08lx\n", Luid.HighPart, Luid.LowPart);
1393 
1394     if (TokenHandle != NULL)
1395     {
1396         TRACE("TokenHandle: %p\n", TokenHandle);
1397     }
1398 
1399     if (phToken != NULL)
1400         *phToken = TokenHandle;
1401 
1402     /* FIXME: return ppLogonSid and pQuotaLimits */
1403 
1404 done:
1405     if (ProfileBuffer != NULL)
1406         LsaFreeReturnBuffer(ProfileBuffer);
1407 
1408     if (!NT_SUCCESS(Status))
1409     {
1410         if (TokenHandle != NULL)
1411             CloseHandle(TokenHandle);
1412     }
1413 
1414     if (TokenGroups != NULL)
1415         RtlFreeHeap(RtlGetProcessHeap(), 0, TokenGroups);
1416 
1417     if (LocalSid != NULL)
1418         RtlFreeSid(LocalSid);
1419 
1420     if (LogonSid != NULL)
1421         RtlFreeSid(LogonSid);
1422 
1423     if (AuthInfo != NULL)
1424         RtlFreeHeap(RtlGetProcessHeap(), 0, AuthInfo);
1425 
1426     if (!NT_SUCCESS(Status))
1427     {
1428         SetLastError(RtlNtStatusToDosError(Status));
1429         return FALSE;
1430     }
1431 
1432     return TRUE;
1433 }
1434 
1435 /* EOF */
1436