xref: /reactos/dll/win32/msv1_0/msv1_0.c (revision bbabe248)
1 /*
2  * PROJECT:     Authentication Package DLL
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/win32/msv1_0/msv1_0.c
5  * PURPOSE:     Main file
6  * COPYRIGHT:   Copyright 2013 Eric Kohl
7  */
8 
9 /* INCLUDES ****************************************************************/
10 
11 #include "precomp.h"
12 
13 WINE_DEFAULT_DEBUG_CHANNEL(msv1_0);
14 
15 
16 /* GLOBALS *****************************************************************/
17 
18 LSA_DISPATCH_TABLE DispatchTable;
19 
20 
21 /* FUNCTIONS ***************************************************************/
22 
23 static
24 NTSTATUS
25 GetAccountDomainSid(PRPC_SID *Sid)
26 {
27     LSAPR_HANDLE PolicyHandle = NULL;
28     PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
29     ULONG Length = 0;
30     NTSTATUS Status;
31 
32     Status = LsaIOpenPolicyTrusted(&PolicyHandle);
33     if (!NT_SUCCESS(Status))
34     {
35         TRACE("LsaIOpenPolicyTrusted() failed (Status 0x%08lx)\n", Status);
36         return Status;
37     }
38 
39     Status = LsarQueryInformationPolicy(PolicyHandle,
40                                         PolicyAccountDomainInformation,
41                                         &PolicyInfo);
42     if (!NT_SUCCESS(Status))
43     {
44         TRACE("LsarQueryInformationPolicy() failed (Status 0x%08lx)\n", Status);
45         goto done;
46     }
47 
48     Length = RtlLengthSid(PolicyInfo->PolicyAccountDomainInfo.Sid);
49 
50     *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
51     if (*Sid == NULL)
52     {
53         ERR("Failed to allocate SID\n");
54         Status = STATUS_INSUFFICIENT_RESOURCES;
55         goto done;
56     }
57 
58     memcpy(*Sid, PolicyInfo->PolicyAccountDomainInfo.Sid, Length);
59 
60 done:
61     if (PolicyInfo != NULL)
62         LsaIFree_LSAPR_POLICY_INFORMATION(PolicyAccountDomainInformation,
63                                           PolicyInfo);
64 
65     if (PolicyHandle != NULL)
66         LsarClose(&PolicyHandle);
67 
68     return Status;
69 }
70 
71 
72 static
73 NTSTATUS
74 GetNtAuthorityDomainSid(PRPC_SID *Sid)
75 {
76     SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
77     ULONG Length = 0;
78 
79     Length = RtlLengthRequiredSid(0);
80     *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
81     if (*Sid == NULL)
82     {
83         ERR("Failed to allocate SID\n");
84         return STATUS_INSUFFICIENT_RESOURCES;
85     }
86 
87     RtlInitializeSid(*Sid,&NtAuthority, 0);
88 
89     return STATUS_SUCCESS;
90 }
91 
92 
93 static
94 NTSTATUS
95 BuildInteractiveProfileBuffer(IN PLSA_CLIENT_REQUEST ClientRequest,
96                               IN PSAMPR_USER_INFO_BUFFER UserInfo,
97                               IN PWSTR ComputerName,
98                               OUT PMSV1_0_INTERACTIVE_PROFILE *ProfileBuffer,
99                               OUT PULONG ProfileBufferLength)
100 {
101     PMSV1_0_INTERACTIVE_PROFILE LocalBuffer = NULL;
102     PVOID ClientBaseAddress = NULL;
103     LPWSTR Ptr;
104     ULONG BufferLength;
105     USHORT ComputerNameLength;
106     NTSTATUS Status = STATUS_SUCCESS;
107 
108     *ProfileBuffer = NULL;
109     *ProfileBufferLength = 0;
110 
111     if (UIntPtrToUShort(wcslen(ComputerName), &ComputerNameLength) != S_OK)
112     {
113         return STATUS_INVALID_PARAMETER;
114     }
115 
116     BufferLength = sizeof(MSV1_0_INTERACTIVE_PROFILE) +
117                    UserInfo->All.FullName.Length + sizeof(WCHAR) +
118                    UserInfo->All.HomeDirectory.Length + sizeof(WCHAR) +
119                    UserInfo->All.HomeDirectoryDrive.Length + sizeof(WCHAR) +
120                    UserInfo->All.ScriptPath.Length + sizeof(WCHAR) +
121                    UserInfo->All.ProfilePath.Length + sizeof(WCHAR) +
122                    ((ComputerNameLength + 3) * sizeof(WCHAR));
123 
124     LocalBuffer = DispatchTable.AllocateLsaHeap(BufferLength);
125     if (LocalBuffer == NULL)
126     {
127         TRACE("Failed to allocate the local buffer!\n");
128         Status = STATUS_INSUFFICIENT_RESOURCES;
129         goto done;
130     }
131 
132     Status = DispatchTable.AllocateClientBuffer(ClientRequest,
133                                                 BufferLength,
134                                                 &ClientBaseAddress);
135     if (!NT_SUCCESS(Status))
136     {
137         TRACE("DispatchTable.AllocateClientBuffer failed (Status 0x%08lx)\n", Status);
138         goto done;
139     }
140 
141     TRACE("ClientBaseAddress: %p\n", ClientBaseAddress);
142 
143     Ptr = (LPWSTR)((ULONG_PTR)LocalBuffer + sizeof(MSV1_0_INTERACTIVE_PROFILE));
144 
145     LocalBuffer->MessageType = MsV1_0InteractiveProfile;
146     LocalBuffer->LogonCount = UserInfo->All.LogonCount;
147     LocalBuffer->BadPasswordCount = UserInfo->All.BadPasswordCount;
148 
149     LocalBuffer->LogonTime.LowPart = UserInfo->All.LastLogon.LowPart;
150     LocalBuffer->LogonTime.HighPart = UserInfo->All.LastLogon.HighPart;
151 
152     LocalBuffer->LogoffTime.LowPart = UserInfo->All.AccountExpires.LowPart;
153     LocalBuffer->LogoffTime.HighPart = UserInfo->All.AccountExpires.HighPart;
154 
155     LocalBuffer->KickOffTime.LowPart = UserInfo->All.AccountExpires.LowPart;
156     LocalBuffer->KickOffTime.HighPart = UserInfo->All.AccountExpires.HighPart;
157 
158     LocalBuffer->PasswordLastSet.LowPart = UserInfo->All.PasswordLastSet.LowPart;
159     LocalBuffer->PasswordLastSet.HighPart = UserInfo->All.PasswordLastSet.HighPart;
160 
161     LocalBuffer->PasswordCanChange.LowPart = UserInfo->All.PasswordCanChange.LowPart;
162     LocalBuffer->PasswordCanChange.HighPart = UserInfo->All.PasswordCanChange.HighPart;
163 
164     LocalBuffer->PasswordMustChange.LowPart = UserInfo->All.PasswordMustChange.LowPart;
165     LocalBuffer->PasswordMustChange.HighPart = UserInfo->All.PasswordMustChange.HighPart;
166 
167     LocalBuffer->LogonScript.Length = UserInfo->All.ScriptPath.Length;
168     LocalBuffer->LogonScript.MaximumLength = UserInfo->All.ScriptPath.Length + sizeof(WCHAR);
169     LocalBuffer->LogonScript.Buffer = (LPWSTR)((ULONG_PTR)ClientBaseAddress + (ULONG_PTR)Ptr - (ULONG_PTR)LocalBuffer);
170     memcpy(Ptr,
171            UserInfo->All.ScriptPath.Buffer,
172            UserInfo->All.ScriptPath.Length);
173 
174     Ptr = (LPWSTR)((ULONG_PTR)Ptr + LocalBuffer->LogonScript.MaximumLength);
175 
176     LocalBuffer->HomeDirectory.Length = UserInfo->All.HomeDirectory.Length;
177     LocalBuffer->HomeDirectory.MaximumLength = UserInfo->All.HomeDirectory.Length + sizeof(WCHAR);
178     LocalBuffer->HomeDirectory.Buffer = (LPWSTR)((ULONG_PTR)ClientBaseAddress + (ULONG_PTR)Ptr - (ULONG_PTR)LocalBuffer);
179     memcpy(Ptr,
180            UserInfo->All.HomeDirectory.Buffer,
181            UserInfo->All.HomeDirectory.Length);
182 
183     Ptr = (LPWSTR)((ULONG_PTR)Ptr + LocalBuffer->HomeDirectory.MaximumLength);
184 
185     LocalBuffer->FullName.Length = UserInfo->All.FullName.Length;
186     LocalBuffer->FullName.MaximumLength = UserInfo->All.FullName.Length + sizeof(WCHAR);
187     LocalBuffer->FullName.Buffer = (LPWSTR)((ULONG_PTR)ClientBaseAddress + (ULONG_PTR)Ptr - (ULONG_PTR)LocalBuffer);
188     memcpy(Ptr,
189            UserInfo->All.FullName.Buffer,
190            UserInfo->All.FullName.Length);
191     TRACE("FullName.Buffer: %p\n", LocalBuffer->FullName.Buffer);
192 
193     Ptr = (LPWSTR)((ULONG_PTR)Ptr + LocalBuffer->FullName.MaximumLength);
194 
195     LocalBuffer->ProfilePath.Length = UserInfo->All.ProfilePath.Length;
196     LocalBuffer->ProfilePath.MaximumLength = UserInfo->All.ProfilePath.Length + sizeof(WCHAR);
197     LocalBuffer->ProfilePath.Buffer = (LPWSTR)((ULONG_PTR)ClientBaseAddress + (ULONG_PTR)Ptr - (ULONG_PTR)LocalBuffer);
198     memcpy(Ptr,
199            UserInfo->All.ProfilePath.Buffer,
200            UserInfo->All.ProfilePath.Length);
201 
202     Ptr = (LPWSTR)((ULONG_PTR)Ptr + LocalBuffer->ProfilePath.MaximumLength);
203 
204     LocalBuffer->HomeDirectoryDrive.Length = UserInfo->All.HomeDirectoryDrive.Length;
205     LocalBuffer->HomeDirectoryDrive.MaximumLength = UserInfo->All.HomeDirectoryDrive.Length + sizeof(WCHAR);
206     LocalBuffer->HomeDirectoryDrive.Buffer = (LPWSTR)((ULONG_PTR)ClientBaseAddress + (ULONG_PTR)Ptr - (ULONG_PTR)LocalBuffer);
207     memcpy(Ptr,
208            UserInfo->All.HomeDirectoryDrive.Buffer,
209            UserInfo->All.HomeDirectoryDrive.Length);
210 
211     Ptr = (LPWSTR)((ULONG_PTR)Ptr + LocalBuffer->HomeDirectoryDrive.MaximumLength);
212 
213     LocalBuffer->LogonServer.Length = (ComputerNameLength + 2) * sizeof(WCHAR);
214     LocalBuffer->LogonServer.MaximumLength = LocalBuffer->LogonServer.Length + sizeof(WCHAR);
215     LocalBuffer->LogonServer.Buffer = (LPWSTR)((ULONG_PTR)ClientBaseAddress + (ULONG_PTR)Ptr - (ULONG_PTR)LocalBuffer);
216     wcscpy(Ptr, L"\\");
217     wcscat(Ptr, ComputerName);
218 
219     LocalBuffer->UserFlags = 0;
220 
221     Status = DispatchTable.CopyToClientBuffer(ClientRequest,
222                                               BufferLength,
223                                               ClientBaseAddress,
224                                               LocalBuffer);
225     if (!NT_SUCCESS(Status))
226     {
227         TRACE("DispatchTable.CopyToClientBuffer failed (Status 0x%08lx)\n", Status);
228         goto done;
229     }
230 
231     *ProfileBuffer = (PMSV1_0_INTERACTIVE_PROFILE)ClientBaseAddress;
232     *ProfileBufferLength = BufferLength;
233 
234 done:
235     if (LocalBuffer != NULL)
236         DispatchTable.FreeLsaHeap(LocalBuffer);
237 
238     if (!NT_SUCCESS(Status))
239     {
240         if (ClientBaseAddress != NULL)
241             DispatchTable.FreeClientBuffer(ClientRequest,
242                                            ClientBaseAddress);
243     }
244 
245     return Status;
246 }
247 
248 
249 static
250 PSID
251 AppendRidToSid(PSID SrcSid,
252                ULONG Rid)
253 {
254     PSID DstSid = NULL;
255     UCHAR RidCount;
256 
257     RidCount = *RtlSubAuthorityCountSid(SrcSid);
258     if (RidCount >= 8)
259         return NULL;
260 
261     DstSid = DispatchTable.AllocateLsaHeap(RtlLengthRequiredSid(RidCount + 1));
262     if (DstSid == NULL)
263         return NULL;
264 
265     RtlCopyMemory(DstSid,
266                   SrcSid,
267                   RtlLengthRequiredSid(RidCount));
268 
269     *RtlSubAuthorityCountSid(DstSid) = RidCount + 1;
270     *RtlSubAuthoritySid(DstSid, RidCount) = Rid;
271 
272     return DstSid;
273 }
274 
275 
276 static
277 NTSTATUS
278 BuildTokenUser(OUT PTOKEN_USER User,
279                IN PSID AccountDomainSid,
280                IN ULONG RelativeId)
281 {
282     User->User.Sid = AppendRidToSid(AccountDomainSid,
283                                     RelativeId);
284     if (User->User.Sid == NULL)
285     {
286         ERR("Could not create the user SID\n");
287         return STATUS_INSUFFICIENT_RESOURCES;
288     }
289 
290     User->User.Attributes = 0;
291 
292     return STATUS_SUCCESS;
293 }
294 
295 
296 static
297 NTSTATUS
298 BuildTokenPrimaryGroup(OUT PTOKEN_PRIMARY_GROUP PrimaryGroup,
299                        IN PSID AccountDomainSid,
300                        IN ULONG RelativeId)
301 {
302     PrimaryGroup->PrimaryGroup = AppendRidToSid(AccountDomainSid,
303                                                 RelativeId);
304     if (PrimaryGroup->PrimaryGroup == NULL)
305     {
306         ERR("Could not create the primary group SID\n");
307         return STATUS_INSUFFICIENT_RESOURCES;
308     }
309 
310     return STATUS_SUCCESS;
311 }
312 
313 
314 static
315 NTSTATUS
316 BuildTokenGroups(OUT PTOKEN_GROUPS *Groups,
317                  IN PSID AccountDomainSid,
318                  IN ULONG RelativeId,
319                  IN BOOL SpecialAccount)
320 {
321     SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
322     PTOKEN_GROUPS TokenGroups;
323     DWORD GroupCount = 0;
324     DWORD MaxGroups = 2;
325     PSID Sid;
326     NTSTATUS Status = STATUS_SUCCESS;
327 
328     if (SpecialAccount)
329         MaxGroups++;
330 
331     TokenGroups = DispatchTable.AllocateLsaHeap(sizeof(TOKEN_GROUPS) +
332                                                 MaxGroups * sizeof(SID_AND_ATTRIBUTES));
333     if (TokenGroups == NULL)
334     {
335         return STATUS_INSUFFICIENT_RESOURCES;
336     }
337 
338     if (SpecialAccount)
339     {
340         /* Self */
341         Sid = AppendRidToSid(AccountDomainSid, RelativeId);
342         if (Sid == NULL)
343         {
344 
345         }
346 
347         TokenGroups->Groups[GroupCount].Sid = Sid;
348         TokenGroups->Groups[GroupCount].Attributes =
349             SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
350         GroupCount++;
351 
352         /* Member of 'Users' alias */
353         RtlAllocateAndInitializeSid(&SystemAuthority,
354                                     2,
355                                     SECURITY_BUILTIN_DOMAIN_RID,
356                                     DOMAIN_ALIAS_RID_USERS,
357                                     SECURITY_NULL_RID,
358                                     SECURITY_NULL_RID,
359                                     SECURITY_NULL_RID,
360                                     SECURITY_NULL_RID,
361                                     SECURITY_NULL_RID,
362                                     SECURITY_NULL_RID,
363                                     &Sid);
364         TokenGroups->Groups[GroupCount].Sid = Sid;
365         TokenGroups->Groups[GroupCount].Attributes =
366             SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
367         GroupCount++;
368     }
369     else
370     {
371         /* Member of the domains users group */
372         Sid = AppendRidToSid(AccountDomainSid, DOMAIN_GROUP_RID_USERS);
373         if (Sid == NULL)
374         {
375 
376         }
377 
378         TokenGroups->Groups[GroupCount].Sid = Sid;
379         TokenGroups->Groups[GroupCount].Attributes =
380             SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
381         GroupCount++;
382     }
383 
384     /* Member of 'Authenticated users' */
385     RtlAllocateAndInitializeSid(&SystemAuthority,
386                                 1,
387                                 SECURITY_AUTHENTICATED_USER_RID,
388                                 SECURITY_NULL_RID,
389                                 SECURITY_NULL_RID,
390                                 SECURITY_NULL_RID,
391                                 SECURITY_NULL_RID,
392                                 SECURITY_NULL_RID,
393                                 SECURITY_NULL_RID,
394                                 SECURITY_NULL_RID,
395                                 &Sid);
396     TokenGroups->Groups[GroupCount].Sid = Sid;
397     TokenGroups->Groups[GroupCount].Attributes =
398         SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
399     GroupCount++;
400 
401     TokenGroups->GroupCount = GroupCount;
402     ASSERT(TokenGroups->GroupCount <= MaxGroups);
403 
404     *Groups = TokenGroups;
405 
406     return Status;
407 }
408 
409 
410 static
411 NTSTATUS
412 BuildTokenInformationBuffer(PLSA_TOKEN_INFORMATION_V1 *TokenInformation,
413                             PRPC_SID AccountDomainSid,
414                             PSAMPR_USER_INFO_BUFFER UserInfo,
415                             BOOL SpecialAccount)
416 {
417     PLSA_TOKEN_INFORMATION_V1 Buffer = NULL;
418     ULONG i;
419     NTSTATUS Status = STATUS_SUCCESS;
420 
421     Buffer = DispatchTable.AllocateLsaHeap(sizeof(LSA_TOKEN_INFORMATION_V1));
422     if (Buffer == NULL)
423     {
424         WARN("Failed to allocate the local buffer!\n");
425         Status = STATUS_INSUFFICIENT_RESOURCES;
426         goto done;
427     }
428 
429     Buffer->ExpirationTime.LowPart = UserInfo->All.AccountExpires.LowPart;
430     Buffer->ExpirationTime.HighPart = UserInfo->All.AccountExpires.HighPart;
431 
432     Status = BuildTokenUser(&Buffer->User,
433                             (PSID)AccountDomainSid,
434                             UserInfo->All.UserId);
435     if (!NT_SUCCESS(Status))
436     {
437         WARN("BuildTokenUser() failed (Status 0x%08lx)\n", Status);
438         goto done;
439     }
440 
441     Status = BuildTokenPrimaryGroup(&Buffer->PrimaryGroup,
442                                     (PSID)AccountDomainSid,
443                                     UserInfo->All.PrimaryGroupId);
444     if (!NT_SUCCESS(Status))
445     {
446         WARN("BuildTokenPrimaryGroup() failed (Status 0x%08lx)\n", Status);
447         goto done;
448     }
449 
450     Status = BuildTokenGroups(&Buffer->Groups,
451                               (PSID)AccountDomainSid,
452                               UserInfo->All.UserId,
453                               SpecialAccount);
454     if (!NT_SUCCESS(Status))
455     {
456         WARN("BuildTokenGroups() failed (Status 0x%08lx)\n", Status);
457         goto done;
458     }
459 
460     *TokenInformation = Buffer;
461 
462 done:
463     if (!NT_SUCCESS(Status))
464     {
465         if (Buffer != NULL)
466         {
467             if (Buffer->User.User.Sid != NULL)
468                 DispatchTable.FreeLsaHeap(Buffer->User.User.Sid);
469 
470             if (Buffer->Groups != NULL)
471             {
472                 for (i = 0; i < Buffer->Groups->GroupCount; i++)
473                 {
474                     if (Buffer->Groups->Groups[i].Sid != NULL)
475                         DispatchTable.FreeLsaHeap(Buffer->Groups->Groups[i].Sid);
476                 }
477 
478                 DispatchTable.FreeLsaHeap(Buffer->Groups);
479             }
480 
481             if (Buffer->PrimaryGroup.PrimaryGroup != NULL)
482                 DispatchTable.FreeLsaHeap(Buffer->PrimaryGroup.PrimaryGroup);
483 
484             if (Buffer->DefaultDacl.DefaultDacl != NULL)
485                 DispatchTable.FreeLsaHeap(Buffer->DefaultDacl.DefaultDacl);
486 
487             DispatchTable.FreeLsaHeap(Buffer);
488         }
489     }
490 
491     return Status;
492 }
493 
494 
495 static
496 NTSTATUS
497 MsvpChangePassword(IN PLSA_CLIENT_REQUEST ClientRequest,
498                    IN PVOID ProtocolSubmitBuffer,
499                    IN PVOID ClientBufferBase,
500                    IN ULONG SubmitBufferLength,
501                    OUT PVOID *ProtocolReturnBuffer,
502                    OUT PULONG ReturnBufferLength,
503                    OUT PNTSTATUS ProtocolStatus)
504 {
505     NTSTATUS Status;
506     PMSV1_0_CHANGEPASSWORD_REQUEST RequestBuffer;
507     ULONG_PTR PtrOffset;
508 
509     SAMPR_HANDLE ServerHandle = NULL;
510     SAMPR_HANDLE DomainHandle = NULL;
511     SAMPR_HANDLE UserHandle = NULL;
512     PRPC_SID DomainSid = NULL;
513     RPC_UNICODE_STRING Names[1];
514     SAMPR_ULONG_ARRAY RelativeIds = {0, NULL};
515     SAMPR_ULONG_ARRAY Use = {0, NULL};
516 
517     ENCRYPTED_NT_OWF_PASSWORD OldNtPassword;
518     ENCRYPTED_NT_OWF_PASSWORD NewNtPassword;
519     ENCRYPTED_LM_OWF_PASSWORD OldLmPassword;
520     ENCRYPTED_LM_OWF_PASSWORD NewLmPassword;
521     OEM_STRING LmPwdString;
522     CHAR LmPwdBuffer[15];
523     BOOLEAN OldLmPasswordPresent = FALSE;
524     BOOLEAN NewLmPasswordPresent = FALSE;
525 
526     ENCRYPTED_LM_OWF_PASSWORD OldLmEncryptedWithNewLm;
527     ENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithOldLm;
528     ENCRYPTED_LM_OWF_PASSWORD OldNtEncryptedWithNewNt;
529     ENCRYPTED_LM_OWF_PASSWORD NewNtEncryptedWithOldNt;
530     PENCRYPTED_LM_OWF_PASSWORD pOldLmEncryptedWithNewLm = NULL;
531     PENCRYPTED_LM_OWF_PASSWORD pNewLmEncryptedWithOldLm = NULL;
532 
533     TRACE("MsvpChangePassword()\n");
534 
535     /* Parameters validation */
536 
537     if (SubmitBufferLength < sizeof(MSV1_0_CHANGEPASSWORD_REQUEST))
538     {
539         ERR("Invalid SubmitBufferLength %lu\n", SubmitBufferLength);
540         return STATUS_INVALID_PARAMETER;
541     }
542 
543     RequestBuffer = (PMSV1_0_CHANGEPASSWORD_REQUEST)ProtocolSubmitBuffer;
544 
545     /* Fix-up pointers in the request buffer info */
546     PtrOffset = (ULONG_PTR)ProtocolSubmitBuffer - (ULONG_PTR)ClientBufferBase;
547 
548     Status = RtlValidateUnicodeString(0, &RequestBuffer->DomainName);
549     if (!NT_SUCCESS(Status))
550         return STATUS_INVALID_PARAMETER;
551     // TODO: Check for Buffer limits wrt. ClientBufferBase and alignment.
552     RequestBuffer->DomainName.Buffer = FIXUP_POINTER(RequestBuffer->DomainName.Buffer, PtrOffset);
553     RequestBuffer->DomainName.MaximumLength = RequestBuffer->DomainName.Length;
554 
555     Status = RtlValidateUnicodeString(0, &RequestBuffer->AccountName);
556     if (!NT_SUCCESS(Status))
557         return STATUS_INVALID_PARAMETER;
558     // TODO: Check for Buffer limits wrt. ClientBufferBase and alignment.
559     RequestBuffer->AccountName.Buffer = FIXUP_POINTER(RequestBuffer->AccountName.Buffer, PtrOffset);
560     RequestBuffer->AccountName.MaximumLength = RequestBuffer->AccountName.Length;
561 
562     Status = RtlValidateUnicodeString(0, &RequestBuffer->OldPassword);
563     if (!NT_SUCCESS(Status))
564         return STATUS_INVALID_PARAMETER;
565     // TODO: Check for Buffer limits wrt. ClientBufferBase and alignment.
566     RequestBuffer->OldPassword.Buffer = FIXUP_POINTER(RequestBuffer->OldPassword.Buffer, PtrOffset);
567     RequestBuffer->OldPassword.MaximumLength = RequestBuffer->OldPassword.Length;
568 
569     Status = RtlValidateUnicodeString(0, &RequestBuffer->NewPassword);
570     if (!NT_SUCCESS(Status))
571         return STATUS_INVALID_PARAMETER;
572     // TODO: Check for Buffer limits wrt. ClientBufferBase and alignment.
573     RequestBuffer->NewPassword.Buffer = FIXUP_POINTER(RequestBuffer->NewPassword.Buffer, PtrOffset);
574     RequestBuffer->NewPassword.MaximumLength = RequestBuffer->NewPassword.Length;
575 
576     TRACE("Domain: %S\n", RequestBuffer->DomainName.Buffer);
577     TRACE("Account: %S\n", RequestBuffer->AccountName.Buffer);
578     TRACE("Old Password: %S\n", RequestBuffer->OldPassword.Buffer);
579     TRACE("New Password: %S\n", RequestBuffer->NewPassword.Buffer);
580 
581     /* Connect to the SAM server */
582     Status = SamIConnect(NULL,
583                          &ServerHandle,
584                          SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
585                          TRUE);
586     if (!NT_SUCCESS(Status))
587     {
588         TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status);
589         goto done;
590     }
591 
592     /* Get the domain SID */
593     Status = SamrLookupDomainInSamServer(ServerHandle,
594                                          (PRPC_UNICODE_STRING)&RequestBuffer->DomainName,
595                                          &DomainSid);
596     if (!NT_SUCCESS(Status))
597     {
598         TRACE("SamrLookupDomainInSamServer failed (Status %08lx)\n", Status);
599         goto done;
600     }
601 
602     /* Open the domain */
603     Status = SamrOpenDomain(ServerHandle,
604                             DOMAIN_LOOKUP,
605                             DomainSid,
606                             &DomainHandle);
607     if (!NT_SUCCESS(Status))
608     {
609         TRACE("SamrOpenDomain failed (Status %08lx)\n", Status);
610         goto done;
611     }
612 
613     Names[0].Length = RequestBuffer->AccountName.Length;
614     Names[0].MaximumLength = RequestBuffer->AccountName.MaximumLength;
615     Names[0].Buffer = RequestBuffer->AccountName.Buffer;
616 
617     /* Try to get the RID for the user name */
618     Status = SamrLookupNamesInDomain(DomainHandle,
619                                      1,
620                                      Names,
621                                      &RelativeIds,
622                                      &Use);
623     if (!NT_SUCCESS(Status))
624     {
625         TRACE("SamrLookupNamesInDomain failed (Status %08lx)\n", Status);
626         Status = STATUS_NO_SUCH_USER;
627         goto done;
628     }
629 
630     /* Fail, if it is not a user account */
631     if (Use.Element[0] != SidTypeUser)
632     {
633         TRACE("Account is not a user account!\n");
634         Status = STATUS_NO_SUCH_USER;
635         goto done;
636     }
637 
638     /* Open the user object */
639     Status = SamrOpenUser(DomainHandle,
640                           USER_CHANGE_PASSWORD,
641                           RelativeIds.Element[0],
642                           &UserHandle);
643     if (!NT_SUCCESS(Status))
644     {
645         TRACE("SamrOpenUser failed (Status %08lx)\n", Status);
646         goto done;
647     }
648 
649 
650     /* Calculate the NT hash for the old password */
651     Status = SystemFunction007(&RequestBuffer->OldPassword,
652                                (LPBYTE)&OldNtPassword);
653     if (!NT_SUCCESS(Status))
654     {
655         TRACE("SystemFunction007 failed (Status 0x%08lx)\n", Status);
656         goto done;
657     }
658 
659     /* Calculate the NT hash for the new password */
660     Status = SystemFunction007(&RequestBuffer->NewPassword,
661                                (LPBYTE)&NewNtPassword);
662     if (!NT_SUCCESS(Status))
663     {
664         TRACE("SystemFunction007 failed (Status 0x%08lx)\n", Status);
665         goto done;
666     }
667 
668     /* Calculate the LM password and hash for the old password */
669     LmPwdString.Length = 15;
670     LmPwdString.MaximumLength = 15;
671     LmPwdString.Buffer = LmPwdBuffer;
672     ZeroMemory(LmPwdString.Buffer, LmPwdString.MaximumLength);
673 
674     Status = RtlUpcaseUnicodeStringToOemString(&LmPwdString,
675                                                &RequestBuffer->OldPassword,
676                                                FALSE);
677     if (NT_SUCCESS(Status))
678     {
679         /* Calculate the LM hash value of the password */
680         Status = SystemFunction006(LmPwdString.Buffer,
681                                    (LPSTR)&OldLmPassword);
682         if (NT_SUCCESS(Status))
683         {
684             OldLmPasswordPresent = TRUE;
685         }
686     }
687 
688     /* Calculate the LM password and hash for the new password */
689     LmPwdString.Length = 15;
690     LmPwdString.MaximumLength = 15;
691     LmPwdString.Buffer = LmPwdBuffer;
692     ZeroMemory(LmPwdString.Buffer, LmPwdString.MaximumLength);
693 
694     Status = RtlUpcaseUnicodeStringToOemString(&LmPwdString,
695                                                &RequestBuffer->NewPassword,
696                                                FALSE);
697     if (NT_SUCCESS(Status))
698     {
699         /* Calculate the LM hash value of the password */
700         Status = SystemFunction006(LmPwdString.Buffer,
701                                    (LPSTR)&NewLmPassword);
702         if (NT_SUCCESS(Status))
703         {
704             NewLmPasswordPresent = TRUE;
705         }
706     }
707 
708     /* Encrypt the old and new LM passwords, if they exist */
709     if (OldLmPasswordPresent && NewLmPasswordPresent)
710     {
711         /* Encrypt the old LM password */
712         Status = SystemFunction012((const BYTE *)&OldLmPassword,
713                                    (const BYTE *)&NewLmPassword,
714                                    (LPBYTE)&OldLmEncryptedWithNewLm);
715         if (!NT_SUCCESS(Status))
716         {
717             TRACE("SystemFunction012 failed (Status 0x%08lx)\n", Status);
718             goto done;
719         }
720 
721         /* Encrypt the new LM password */
722         Status = SystemFunction012((const BYTE *)&NewLmPassword,
723                                    (const BYTE *)&OldLmPassword,
724                                    (LPBYTE)&NewLmEncryptedWithOldLm);
725         if (!NT_SUCCESS(Status))
726         {
727             TRACE("SystemFunction012 failed (Status 0x%08lx)\n", Status);
728             goto done;
729         }
730 
731         pOldLmEncryptedWithNewLm = &OldLmEncryptedWithNewLm;
732         pNewLmEncryptedWithOldLm = &NewLmEncryptedWithOldLm;
733     }
734 
735     /* Encrypt the old NT password */
736     Status = SystemFunction012((const BYTE *)&OldNtPassword,
737                                (const BYTE *)&NewNtPassword,
738                                (LPBYTE)&OldNtEncryptedWithNewNt);
739     if (!NT_SUCCESS(Status))
740     {
741         TRACE("SystemFunction012 failed (Status 0x%08lx)\n", Status);
742         goto done;
743     }
744 
745     /* Encrypt the new NT password */
746     Status = SystemFunction012((const BYTE *)&NewNtPassword,
747                                (const BYTE *)&OldNtPassword,
748                                (LPBYTE)&NewNtEncryptedWithOldNt);
749     if (!NT_SUCCESS(Status))
750     {
751         TRACE("SystemFunction012 failed (Status 0x%08lx)\n", Status);
752         goto done;
753     }
754 
755     /* Change the password */
756     Status = SamrChangePasswordUser(UserHandle,
757                                     OldLmPasswordPresent && NewLmPasswordPresent,
758                                     pOldLmEncryptedWithNewLm,
759                                     pNewLmEncryptedWithOldLm,
760                                     TRUE,
761                                     &OldNtEncryptedWithNewNt,
762                                     &NewNtEncryptedWithOldNt,
763                                     FALSE,
764                                     NULL,
765                                     FALSE,
766                                     NULL);
767     if (!NT_SUCCESS(Status))
768     {
769         TRACE("SamrChangePasswordUser failed (Status %08lx)\n", Status);
770         goto done;
771     }
772 
773 done:
774     if (UserHandle != NULL)
775         SamrCloseHandle(&UserHandle);
776 
777     SamIFree_SAMPR_ULONG_ARRAY(&RelativeIds);
778     SamIFree_SAMPR_ULONG_ARRAY(&Use);
779 
780     if (DomainHandle != NULL)
781         SamrCloseHandle(&DomainHandle);
782 
783     if (DomainSid != NULL)
784         SamIFreeVoid(DomainSid);
785 
786     if (ServerHandle != NULL)
787         SamrCloseHandle(&ServerHandle);
788 
789     return Status;
790 }
791 
792 
793 static
794 NTSTATUS
795 MsvpCheckPassword(PUNICODE_STRING UserPassword,
796                   PSAMPR_USER_INFO_BUFFER UserInfo)
797 {
798     ENCRYPTED_NT_OWF_PASSWORD UserNtPassword;
799     ENCRYPTED_LM_OWF_PASSWORD UserLmPassword;
800     BOOLEAN UserLmPasswordPresent = FALSE;
801     BOOLEAN UserNtPasswordPresent = FALSE;
802     OEM_STRING LmPwdString;
803     CHAR LmPwdBuffer[15];
804     NTSTATUS Status;
805 
806     TRACE("(%p %p)\n", UserPassword, UserInfo);
807 
808     /* Calculate the LM password and hash for the users password */
809     LmPwdString.Length = 15;
810     LmPwdString.MaximumLength = 15;
811     LmPwdString.Buffer = LmPwdBuffer;
812     ZeroMemory(LmPwdString.Buffer, LmPwdString.MaximumLength);
813 
814     Status = RtlUpcaseUnicodeStringToOemString(&LmPwdString,
815                                                UserPassword,
816                                                FALSE);
817     if (NT_SUCCESS(Status))
818     {
819         /* Calculate the LM hash value of the users password */
820         Status = SystemFunction006(LmPwdString.Buffer,
821                                    (LPSTR)&UserLmPassword);
822         if (NT_SUCCESS(Status))
823         {
824             UserLmPasswordPresent = TRUE;
825         }
826     }
827 
828     /* Calculate the NT hash of the users password */
829     Status = SystemFunction007(UserPassword,
830                                (LPBYTE)&UserNtPassword);
831     if (NT_SUCCESS(Status))
832     {
833         UserNtPasswordPresent = TRUE;
834     }
835 
836     Status = STATUS_WRONG_PASSWORD;
837 
838     /* Succeed, if no password has been set */
839     if (UserInfo->All.NtPasswordPresent == FALSE &&
840         UserInfo->All.LmPasswordPresent == FALSE)
841     {
842         TRACE("No password check!\n");
843         Status = STATUS_SUCCESS;
844         goto done;
845     }
846 
847     /* Succeed, if NT password matches */
848     if (UserNtPasswordPresent && UserInfo->All.NtPasswordPresent)
849     {
850         TRACE("Check NT password hashes:\n");
851         if (RtlEqualMemory(&UserNtPassword,
852                            UserInfo->All.NtOwfPassword.Buffer,
853                            sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
854         {
855             TRACE("  success!\n");
856             Status = STATUS_SUCCESS;
857             goto done;
858         }
859 
860         TRACE("  failed!\n");
861     }
862 
863     /* Succeed, if LM password matches */
864     if (UserLmPasswordPresent && UserInfo->All.LmPasswordPresent)
865     {
866         TRACE("Check LM password hashes:\n");
867         if (RtlEqualMemory(&UserLmPassword,
868                            UserInfo->All.LmOwfPassword.Buffer,
869                            sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
870         {
871             TRACE("  success!\n");
872             Status = STATUS_SUCCESS;
873             goto done;
874         }
875         TRACE("  failed!\n");
876     }
877 
878 done:
879     return Status;
880 }
881 
882 
883 static
884 BOOL
885 MsvpCheckLogonHours(
886     _In_ PSAMPR_LOGON_HOURS LogonHours,
887     _In_ PLARGE_INTEGER LogonTime)
888 {
889 #if 0
890     LARGE_INTEGER LocalLogonTime;
891     TIME_FIELDS TimeFields;
892     USHORT MinutesPerUnit, Offset;
893     BOOL bFound;
894 
895     FIXME("MsvpCheckLogonHours(%p %p)\n", LogonHours, LogonTime);
896 
897     if (LogonHours->UnitsPerWeek == 0 || LogonHours->LogonHours == NULL)
898     {
899         FIXME("No logon hours!\n");
900         return TRUE;
901     }
902 
903     RtlSystemTimeToLocalTime(LogonTime, &LocalLogonTime);
904     RtlTimeToTimeFields(&LocalLogonTime, &TimeFields);
905 
906     FIXME("UnitsPerWeek: %u\n", LogonHours->UnitsPerWeek);
907     MinutesPerUnit = 10080 / LogonHours->UnitsPerWeek;
908 
909     Offset = ((TimeFields.Weekday * 24 + TimeFields.Hour) * 60 + TimeFields.Minute) / MinutesPerUnit;
910     FIXME("Offset: %us\n", Offset);
911 
912     bFound = (BOOL)(LogonHours->LogonHours[Offset / 8] & (1 << (Offset % 8)));
913     FIXME("Logon permitted: %s\n", bFound ? "Yes" : "No");
914 
915     return bFound;
916 #endif
917     return TRUE;
918 }
919 
920 
921 static
922 BOOL
923 MsvpCheckWorkstations(
924     _In_ PRPC_UNICODE_STRING WorkStations,
925     _In_ PWSTR ComputerName)
926 {
927     PWSTR pStart, pEnd;
928     BOOL bFound = FALSE;
929 
930     TRACE("MsvpCheckWorkstations(%p %S)\n", WorkStations, ComputerName);
931 
932     if (WorkStations->Length == 0 || WorkStations->Buffer == NULL)
933     {
934         TRACE("No workstations!\n");
935         return TRUE;
936     }
937 
938     TRACE("Workstations: %wZ\n", WorkStations);
939 
940     pStart = WorkStations->Buffer;
941     for (;;)
942     {
943         pEnd = wcschr(pStart, L',');
944         if (pEnd != NULL)
945             *pEnd = UNICODE_NULL;
946 
947         TRACE("Comparing '%S' and '%S'\n", ComputerName, pStart);
948         if (_wcsicmp(ComputerName, pStart) == 0)
949         {
950             bFound = TRUE;
951             if (pEnd != NULL)
952                 *pEnd = L',';
953             break;
954         }
955 
956         if (pEnd == NULL)
957             break;
958 
959         *pEnd = L',';
960         pStart = pEnd + 1;
961     }
962 
963     TRACE("Found allowed workstation: %s\n", (bFound) ? "Yes" : "No");
964 
965     return bFound;
966 }
967 
968 
969 /*
970  * @unimplemented
971  */
972 NTSTATUS
973 NTAPI
974 LsaApCallPackage(IN PLSA_CLIENT_REQUEST ClientRequest,
975                  IN PVOID ProtocolSubmitBuffer,
976                  IN PVOID ClientBufferBase,
977                  IN ULONG SubmitBufferLength,
978                  OUT PVOID *ProtocolReturnBuffer,
979                  OUT PULONG ReturnBufferLength,
980                  OUT PNTSTATUS ProtocolStatus)
981 {
982     NTSTATUS Status;
983     MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType;
984 
985     TRACE("LsaApCallPackage()\n");
986 
987     if (SubmitBufferLength < sizeof(MSV1_0_PROTOCOL_MESSAGE_TYPE))
988         return STATUS_INVALID_PARAMETER;
989 
990     MessageType = *((PMSV1_0_PROTOCOL_MESSAGE_TYPE)ProtocolSubmitBuffer);
991 
992     *ProtocolReturnBuffer = NULL;
993     *ReturnBufferLength = 0;
994 
995     switch (MessageType)
996     {
997         case MsV1_0Lm20ChallengeRequest:
998         case MsV1_0Lm20GetChallengeResponse:
999             Status = STATUS_NOT_IMPLEMENTED;
1000             break;
1001 
1002         case MsV1_0EnumerateUsers:
1003         case MsV1_0GetUserInfo:
1004         case MsV1_0ReLogonUsers:
1005             Status = STATUS_INVALID_PARAMETER;
1006             break;
1007 
1008         case MsV1_0ChangePassword:
1009             Status = MsvpChangePassword(ClientRequest,
1010                                         ProtocolSubmitBuffer,
1011                                         ClientBufferBase,
1012                                         SubmitBufferLength,
1013                                         ProtocolReturnBuffer,
1014                                         ReturnBufferLength,
1015                                         ProtocolStatus);
1016             break;
1017 
1018         case MsV1_0ChangeCachedPassword:
1019         case MsV1_0GenericPassthrough:
1020         case MsV1_0CacheLogon:
1021         case MsV1_0SubAuth:
1022         case MsV1_0DeriveCredential:
1023         case MsV1_0CacheLookup:
1024             Status = STATUS_NOT_IMPLEMENTED;
1025             break;
1026 
1027         default:
1028             return STATUS_INVALID_PARAMETER;
1029     }
1030 
1031     return Status;
1032 }
1033 
1034 
1035 /*
1036  * @unimplemented
1037  */
1038 NTSTATUS
1039 NTAPI
1040 LsaApCallPackagePassthrough(IN PLSA_CLIENT_REQUEST ClientRequest,
1041                             IN PVOID ProtocolSubmitBuffer,
1042                             IN PVOID ClientBufferBase,
1043                             IN ULONG SubmitBufferLength,
1044                             OUT PVOID *ProtocolReturnBuffer,
1045                             OUT PULONG ReturnBufferLength,
1046                             OUT PNTSTATUS ProtocolStatus)
1047 {
1048     TRACE("LsaApCallPackagePassthrough()\n");
1049     return STATUS_NOT_IMPLEMENTED;
1050 }
1051 
1052 
1053 /*
1054  * @implemented
1055  */
1056 NTSTATUS
1057 NTAPI
1058 LsaApCallPackageUntrusted(IN PLSA_CLIENT_REQUEST ClientRequest,
1059                           IN PVOID ProtocolSubmitBuffer,
1060                           IN PVOID ClientBufferBase,
1061                           IN ULONG SubmitBufferLength,
1062                           OUT PVOID *ProtocolReturnBuffer,
1063                           OUT PULONG ReturnBufferLength,
1064                           OUT PNTSTATUS ProtocolStatus)
1065 {
1066     ULONG MessageType;
1067     NTSTATUS Status;
1068 
1069     TRACE("LsaApCallPackageUntrusted()\n");
1070 
1071     if (SubmitBufferLength < sizeof(MSV1_0_PROTOCOL_MESSAGE_TYPE))
1072         return STATUS_INVALID_PARAMETER;
1073 
1074     MessageType = (ULONG)*((PMSV1_0_PROTOCOL_MESSAGE_TYPE)ProtocolSubmitBuffer);
1075 
1076     *ProtocolReturnBuffer = NULL;
1077     *ReturnBufferLength = 0;
1078 
1079     if (MessageType == MsV1_0ChangePassword)
1080         Status = MsvpChangePassword(ClientRequest,
1081                                     ProtocolSubmitBuffer,
1082                                     ClientBufferBase,
1083                                     SubmitBufferLength,
1084                                     ProtocolReturnBuffer,
1085                                     ReturnBufferLength,
1086                                     ProtocolStatus);
1087     else
1088         Status = STATUS_ACCESS_DENIED;
1089 
1090     return Status;
1091 }
1092 
1093 
1094 /*
1095  * @implemented
1096  */
1097 NTSTATUS
1098 NTAPI
1099 LsaApInitializePackage(IN ULONG AuthenticationPackageId,
1100                        IN PLSA_DISPATCH_TABLE LsaDispatchTable,
1101                        IN PLSA_STRING Database OPTIONAL,
1102                        IN PLSA_STRING Confidentiality OPTIONAL,
1103                        OUT PLSA_STRING *AuthenticationPackageName)
1104 {
1105     PANSI_STRING NameString;
1106     PCHAR NameBuffer;
1107 
1108     TRACE("LsaApInitializePackage(%lu %p %p %p %p)\n",
1109           AuthenticationPackageId, LsaDispatchTable, Database,
1110           Confidentiality, AuthenticationPackageName);
1111 
1112     /* Get the dispatch table entries */
1113     DispatchTable.CreateLogonSession = LsaDispatchTable->CreateLogonSession;
1114     DispatchTable.DeleteLogonSession = LsaDispatchTable->DeleteLogonSession;
1115     DispatchTable.AddCredential = LsaDispatchTable->AddCredential;
1116     DispatchTable.GetCredentials = LsaDispatchTable->GetCredentials;
1117     DispatchTable.DeleteCredential = LsaDispatchTable->DeleteCredential;
1118     DispatchTable.AllocateLsaHeap = LsaDispatchTable->AllocateLsaHeap;
1119     DispatchTable.FreeLsaHeap = LsaDispatchTable->FreeLsaHeap;
1120     DispatchTable.AllocateClientBuffer = LsaDispatchTable->AllocateClientBuffer;
1121     DispatchTable.FreeClientBuffer = LsaDispatchTable->FreeClientBuffer;
1122     DispatchTable.CopyToClientBuffer = LsaDispatchTable->CopyToClientBuffer;
1123     DispatchTable.CopyFromClientBuffer = LsaDispatchTable->CopyFromClientBuffer;
1124 
1125     /* Return the package name */
1126     NameString = DispatchTable.AllocateLsaHeap(sizeof(LSA_STRING));
1127     if (NameString == NULL)
1128         return STATUS_INSUFFICIENT_RESOURCES;
1129 
1130     NameBuffer = DispatchTable.AllocateLsaHeap(sizeof(MSV1_0_PACKAGE_NAME));
1131     if (NameBuffer == NULL)
1132     {
1133         DispatchTable.FreeLsaHeap(NameString);
1134         return STATUS_INSUFFICIENT_RESOURCES;
1135     }
1136 
1137     strcpy(NameBuffer, MSV1_0_PACKAGE_NAME);
1138 
1139     RtlInitAnsiString(NameString, NameBuffer);
1140 
1141     *AuthenticationPackageName = (PLSA_STRING)NameString;
1142 
1143     return STATUS_SUCCESS;
1144 }
1145 
1146 
1147 /*
1148  * @unimplemented
1149  */
1150 VOID
1151 NTAPI
1152 LsaApLogonTerminated(IN PLUID LogonId)
1153 {
1154     TRACE("LsaApLogonTerminated()\n");
1155 }
1156 
1157 
1158 /*
1159  * @implemented
1160  */
1161 NTSTATUS
1162 NTAPI
1163 LsaApLogonUserEx2(IN PLSA_CLIENT_REQUEST ClientRequest,
1164                   IN SECURITY_LOGON_TYPE LogonType,
1165                   IN PVOID ProtocolSubmitBuffer,
1166                   IN PVOID ClientBufferBase,
1167                   IN ULONG SubmitBufferSize,
1168                   OUT PVOID *ProfileBuffer,
1169                   OUT PULONG ProfileBufferSize,
1170                   OUT PLUID LogonId,
1171                   OUT PNTSTATUS SubStatus,
1172                   OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
1173                   OUT PVOID *TokenInformation,
1174                   OUT PUNICODE_STRING *AccountName,
1175                   OUT PUNICODE_STRING *AuthenticatingAuthority,
1176                   OUT PUNICODE_STRING *MachineName,
1177                   OUT PSECPKG_PRIMARY_CRED PrimaryCredentials, /* Not supported yet */
1178                   OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY *SupplementalCredentials) /* Not supported yet */
1179 {
1180     static const UNICODE_STRING NtAuthorityU = RTL_CONSTANT_STRING(L"NT AUTHORITY");
1181     static const UNICODE_STRING LocalServiceU = RTL_CONSTANT_STRING(L"LocalService");
1182     static const UNICODE_STRING NetworkServiceU = RTL_CONSTANT_STRING(L"NetworkService");
1183 
1184     NTSTATUS Status;
1185     PMSV1_0_INTERACTIVE_LOGON LogonInfo;
1186     WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
1187     SAMPR_HANDLE ServerHandle = NULL;
1188     SAMPR_HANDLE DomainHandle = NULL;
1189     SAMPR_HANDLE UserHandle = NULL;
1190     PRPC_SID AccountDomainSid = NULL;
1191     RPC_UNICODE_STRING Names[1];
1192     SAMPR_ULONG_ARRAY RelativeIds = {0, NULL};
1193     SAMPR_ULONG_ARRAY Use = {0, NULL};
1194     PSAMPR_USER_INFO_BUFFER UserInfo = NULL;
1195     BOOLEAN SessionCreated = FALSE;
1196     LARGE_INTEGER LogonTime;
1197     LARGE_INTEGER AccountExpires;
1198     LARGE_INTEGER PasswordMustChange;
1199     LARGE_INTEGER PasswordLastSet;
1200     DWORD ComputerNameSize;
1201     BOOL SpecialAccount = FALSE;
1202     UCHAR LogonPassHash;
1203     PUNICODE_STRING ErasePassword = NULL;
1204 
1205     TRACE("LsaApLogonUserEx2()\n");
1206 
1207     TRACE("LogonType: %lu\n", LogonType);
1208     TRACE("ProtocolSubmitBuffer: %p\n", ProtocolSubmitBuffer);
1209     TRACE("SubmitBufferSize: %lu\n", SubmitBufferSize);
1210 
1211     *ProfileBuffer = NULL;
1212     *ProfileBufferSize = 0;
1213     *SubStatus = STATUS_SUCCESS;
1214     *AccountName = NULL;
1215     *AuthenticatingAuthority = NULL;
1216 
1217     /* Parameters validation */
1218     if (LogonType == Interactive ||
1219         LogonType == Batch ||
1220         LogonType == Service)
1221     {
1222         ULONG_PTR PtrOffset;
1223 
1224         if (SubmitBufferSize < sizeof(MSV1_0_INTERACTIVE_LOGON))
1225         {
1226             ERR("Invalid SubmitBufferSize %lu\n", SubmitBufferSize);
1227             return STATUS_INVALID_PARAMETER;
1228         }
1229 
1230         LogonInfo = (PMSV1_0_INTERACTIVE_LOGON)ProtocolSubmitBuffer;
1231 
1232         if (LogonInfo->MessageType != MsV1_0InteractiveLogon &&
1233             LogonInfo->MessageType != MsV1_0WorkstationUnlockLogon)
1234         {
1235             ERR("Invalid MessageType %lu\n", LogonInfo->MessageType);
1236             return STATUS_BAD_VALIDATION_CLASS;
1237         }
1238 
1239 #if 0   // FIXME: These checks happen to be done on Windows. We however keep them general on ReactOS for now...
1240         if (LogonInfo->UserName.Length > 512) // CRED_MAX_STRING_LENGTH * sizeof(WCHAR) or (CREDUI_MAX_USERNAME_LENGTH (== CRED_MAX_USERNAME_LENGTH) - 1) * sizeof(WCHAR)
1241         {
1242             ERR("UserName too long (%lu, maximum 512)\n", LogonInfo->UserName.Length);
1243             return STATUS_NAME_TOO_LONG;
1244         }
1245         if (LogonInfo->Password.Length > 512) // CREDUI_MAX_PASSWORD_LENGTH * sizeof(WCHAR)
1246         {
1247             ERR("Password too long (%lu, maximum 512)\n", LogonInfo->Password.Length);
1248             return STATUS_NAME_TOO_LONG;
1249         }
1250 #endif
1251 
1252         /* Fix-up pointers in the authentication info */
1253         PtrOffset = (ULONG_PTR)ProtocolSubmitBuffer - (ULONG_PTR)ClientBufferBase;
1254 
1255         /* LogonDomainName is optional and can be an empty string */
1256         if (LogonInfo->LogonDomainName.Length)
1257         {
1258             // TODO: Check for Buffer limits wrt. ClientBufferBase and alignment.
1259             LogonInfo->LogonDomainName.Buffer = FIXUP_POINTER(LogonInfo->LogonDomainName.Buffer, PtrOffset);
1260             LogonInfo->LogonDomainName.MaximumLength = LogonInfo->LogonDomainName.Length;
1261         }
1262         else
1263         {
1264             LogonInfo->LogonDomainName.Buffer = NULL;
1265             LogonInfo->LogonDomainName.MaximumLength = 0;
1266         }
1267         Status = RtlValidateUnicodeString(0, &LogonInfo->LogonDomainName);
1268         if (!NT_SUCCESS(Status))
1269             return STATUS_INVALID_PARAMETER;
1270 
1271         /* UserName is mandatory and cannot be an empty string */
1272         // TODO: Check for Buffer limits wrt. ClientBufferBase and alignment.
1273         LogonInfo->UserName.Buffer = FIXUP_POINTER(LogonInfo->UserName.Buffer, PtrOffset);
1274         LogonInfo->UserName.MaximumLength = LogonInfo->UserName.Length;
1275 
1276         Status = RtlValidateUnicodeString(0, &LogonInfo->UserName);
1277         if (!NT_SUCCESS(Status))
1278             return STATUS_INVALID_PARAMETER;
1279 
1280         /* MS docs says max length is 0xFF bytes. But thats not the full story:
1281          *
1282          * A Quote from https://groups.google.com/forum/#!topic/microsoft.public.win32.programmer.kernel/eFGcCo_ZObk:
1283          * "... At least on my WinXP SP2. Domain and UserName are passed
1284          * in clear text, but the Password is NOT. ..."
1285          *
1286          * If the higher byte of length != 0 we have to use RtlRunDecodeUnicodeString.
1287          */
1288         LogonPassHash = (LogonInfo->Password.Length >> 8) & 0xFF;
1289         LogonInfo->Password.Length = LogonInfo->Password.Length & 0xFF;
1290 
1291         /* Password is optional and can be an empty string */
1292         if (LogonInfo->Password.Length)
1293         {
1294             // TODO: Check for Buffer limits wrt. ClientBufferBase and alignment.
1295             LogonInfo->Password.Buffer = FIXUP_POINTER(LogonInfo->Password.Buffer, PtrOffset);
1296             LogonInfo->Password.MaximumLength = LogonInfo->Password.Length;
1297         }
1298         else
1299         {
1300             LogonInfo->Password.Buffer = NULL;
1301             LogonInfo->Password.MaximumLength = 0;
1302         }
1303 
1304         /* Decode password */
1305         if (LogonPassHash > 0)
1306         {
1307             RtlRunDecodeUnicodeString(LogonPassHash, &LogonInfo->Password);
1308         }
1309 
1310         /* ErasePassword will be "erased" before we return */
1311         ErasePassword = &LogonInfo->Password;
1312 
1313         Status = RtlValidateUnicodeString(0, &LogonInfo->Password);
1314         if (!NT_SUCCESS(Status))
1315             return STATUS_INVALID_PARAMETER;
1316 
1317         TRACE("Domain: %wZ\n", &LogonInfo->LogonDomainName);
1318         TRACE("User: %wZ\n", &LogonInfo->UserName);
1319         TRACE("Password: %wZ\n", &LogonInfo->Password);
1320 
1321         // TODO: If LogonType == Service, do some extra work using LogonInfo->Password.
1322     }
1323     else
1324     {
1325         FIXME("LogonType %lu is not supported yet!\n", LogonType);
1326         return STATUS_NOT_IMPLEMENTED;
1327     }
1328     // TODO: Add other LogonType validity checks.
1329 
1330     /* Get the logon time */
1331     NtQuerySystemTime(&LogonTime);
1332 
1333     /* Get the computer name */
1334     ComputerNameSize = ARRAYSIZE(ComputerName);
1335     GetComputerNameW(ComputerName, &ComputerNameSize);
1336 
1337     /* Check for special accounts */
1338     // FIXME: Windows does not do this that way!! (msv1_0 does not contain these hardcoded values)
1339     if (RtlEqualUnicodeString(&LogonInfo->LogonDomainName, &NtAuthorityU, TRUE))
1340     {
1341         SpecialAccount = TRUE;
1342 
1343         /* Get the authority domain SID */
1344         Status = GetNtAuthorityDomainSid(&AccountDomainSid);
1345         if (!NT_SUCCESS(Status))
1346         {
1347             ERR("GetNtAuthorityDomainSid() failed (Status 0x%08lx)\n", Status);
1348             return Status;
1349         }
1350 
1351         if (RtlEqualUnicodeString(&LogonInfo->UserName, &LocalServiceU, TRUE))
1352         {
1353             TRACE("SpecialAccount: LocalService\n");
1354 
1355             if (LogonType != Service)
1356                 return STATUS_LOGON_FAILURE;
1357 
1358             UserInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1359                                        HEAP_ZERO_MEMORY,
1360                                        sizeof(SAMPR_USER_ALL_INFORMATION));
1361             if (UserInfo == NULL)
1362             {
1363                 Status = STATUS_INSUFFICIENT_RESOURCES;
1364                 goto done;
1365             }
1366 
1367             UserInfo->All.UserId = SECURITY_LOCAL_SERVICE_RID;
1368             UserInfo->All.PrimaryGroupId = SECURITY_LOCAL_SERVICE_RID;
1369         }
1370         else if (RtlEqualUnicodeString(&LogonInfo->UserName, &NetworkServiceU, TRUE))
1371         {
1372             TRACE("SpecialAccount: NetworkService\n");
1373 
1374             if (LogonType != Service)
1375                 return STATUS_LOGON_FAILURE;
1376 
1377             UserInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1378                                        HEAP_ZERO_MEMORY,
1379                                        sizeof(SAMPR_USER_ALL_INFORMATION));
1380             if (UserInfo == NULL)
1381             {
1382                 Status = STATUS_INSUFFICIENT_RESOURCES;
1383                 goto done;
1384             }
1385 
1386             UserInfo->All.UserId = SECURITY_NETWORK_SERVICE_RID;
1387             UserInfo->All.PrimaryGroupId = SECURITY_NETWORK_SERVICE_RID;
1388         }
1389         else
1390         {
1391             Status = STATUS_NO_SUCH_USER;
1392             goto done;
1393         }
1394     }
1395     else
1396     {
1397         TRACE("NormalAccount\n");
1398 
1399         /* Get the account domain SID */
1400         Status = GetAccountDomainSid(&AccountDomainSid);
1401         if (!NT_SUCCESS(Status))
1402         {
1403             ERR("GetAccountDomainSid() failed (Status 0x%08lx)\n", Status);
1404             return Status;
1405         }
1406 
1407         /* Connect to the SAM server */
1408         Status = SamIConnect(NULL,
1409                              &ServerHandle,
1410                              SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
1411                              TRUE);
1412         if (!NT_SUCCESS(Status))
1413         {
1414             TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status);
1415             goto done;
1416         }
1417 
1418         /* Open the account domain */
1419         Status = SamrOpenDomain(ServerHandle,
1420                                 DOMAIN_LOOKUP,
1421                                 AccountDomainSid,
1422                                 &DomainHandle);
1423         if (!NT_SUCCESS(Status))
1424         {
1425             ERR("SamrOpenDomain failed (Status %08lx)\n", Status);
1426             goto done;
1427         }
1428 
1429         Names[0].Length = LogonInfo->UserName.Length;
1430         Names[0].MaximumLength = LogonInfo->UserName.MaximumLength;
1431         Names[0].Buffer = LogonInfo->UserName.Buffer;
1432 
1433         /* Try to get the RID for the user name */
1434         Status = SamrLookupNamesInDomain(DomainHandle,
1435                                          1,
1436                                          Names,
1437                                          &RelativeIds,
1438                                          &Use);
1439         if (!NT_SUCCESS(Status))
1440         {
1441             ERR("SamrLookupNamesInDomain failed (Status %08lx)\n", Status);
1442             Status = STATUS_NO_SUCH_USER;
1443             goto done;
1444         }
1445 
1446         /* Fail, if it is not a user account */
1447         if (Use.Element[0] != SidTypeUser)
1448         {
1449             ERR("Account is not a user account!\n");
1450             Status = STATUS_NO_SUCH_USER;
1451             goto done;
1452         }
1453 
1454         /* Open the user object */
1455         Status = SamrOpenUser(DomainHandle,
1456                               USER_READ_GENERAL | USER_READ_LOGON |
1457                               USER_READ_ACCOUNT | USER_READ_PREFERENCES, /* FIXME */
1458                               RelativeIds.Element[0],
1459                               &UserHandle);
1460         if (!NT_SUCCESS(Status))
1461         {
1462             ERR("SamrOpenUser failed (Status %08lx)\n", Status);
1463             goto done;
1464         }
1465 
1466         Status = SamrQueryInformationUser(UserHandle,
1467                                           UserAllInformation,
1468                                           &UserInfo);
1469         if (!NT_SUCCESS(Status))
1470         {
1471             ERR("SamrQueryInformationUser failed (Status %08lx)\n", Status);
1472             goto done;
1473         }
1474 
1475         TRACE("UserName: %wZ\n", &UserInfo->All.UserName);
1476 
1477         /* Check the password */
1478         if ((UserInfo->All.UserAccountControl & USER_PASSWORD_NOT_REQUIRED) == 0)
1479         {
1480             Status = MsvpCheckPassword(&LogonInfo->Password,
1481                                        UserInfo);
1482             if (!NT_SUCCESS(Status))
1483             {
1484                 ERR("MsvpCheckPassword failed (Status %08lx)\n", Status);
1485                 goto done;
1486             }
1487         }
1488 
1489         /* Check account restrictions for non-administrator accounts */
1490         if (RelativeIds.Element[0] != DOMAIN_USER_RID_ADMIN)
1491         {
1492             /* Check if the account has been disabled */
1493             if (UserInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED)
1494             {
1495                 ERR("Account disabled!\n");
1496                 *SubStatus = STATUS_ACCOUNT_DISABLED;
1497                 Status = STATUS_ACCOUNT_RESTRICTION;
1498                 goto done;
1499             }
1500 
1501             /* Check if the account has been locked */
1502             if (UserInfo->All.UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
1503             {
1504                 ERR("Account locked!\n");
1505                 *SubStatus = STATUS_ACCOUNT_LOCKED_OUT;
1506                 Status = STATUS_ACCOUNT_RESTRICTION;
1507                 goto done;
1508             }
1509 
1510             /* Check if the account expired */
1511             AccountExpires.LowPart = UserInfo->All.AccountExpires.LowPart;
1512             AccountExpires.HighPart = UserInfo->All.AccountExpires.HighPart;
1513             if (LogonTime.QuadPart >= AccountExpires.QuadPart)
1514             {
1515                 ERR("Account expired!\n");
1516                 *SubStatus = STATUS_ACCOUNT_EXPIRED;
1517                 Status = STATUS_ACCOUNT_RESTRICTION;
1518                 goto done;
1519             }
1520 
1521             /* Check if the password expired */
1522             PasswordMustChange.LowPart = UserInfo->All.PasswordMustChange.LowPart;
1523             PasswordMustChange.HighPart = UserInfo->All.PasswordMustChange.HighPart;
1524             PasswordLastSet.LowPart = UserInfo->All.PasswordLastSet.LowPart;
1525             PasswordLastSet.HighPart = UserInfo->All.PasswordLastSet.HighPart;
1526 
1527             if (LogonTime.QuadPart >= PasswordMustChange.QuadPart)
1528             {
1529                 ERR("Password expired!\n");
1530                 if (PasswordLastSet.QuadPart == 0)
1531                     *SubStatus = STATUS_PASSWORD_MUST_CHANGE;
1532                 else
1533                     *SubStatus = STATUS_PASSWORD_EXPIRED;
1534 
1535                 Status = STATUS_ACCOUNT_RESTRICTION;
1536                 goto done;
1537             }
1538 
1539             /* Check logon hours */
1540             if (!MsvpCheckLogonHours(&UserInfo->All.LogonHours, &LogonTime))
1541             {
1542                 ERR("Invalid logon hours!\n");
1543                 *SubStatus = STATUS_INVALID_LOGON_HOURS;
1544                 Status = STATUS_ACCOUNT_RESTRICTION;
1545                 goto done;
1546             }
1547 
1548             /* Check workstations */
1549             if (!MsvpCheckWorkstations(&UserInfo->All.WorkStations, ComputerName))
1550             {
1551                 ERR("Invalid workstation!\n");
1552                 *SubStatus = STATUS_INVALID_WORKSTATION;
1553                 Status = STATUS_ACCOUNT_RESTRICTION;
1554                 goto done;
1555             }
1556         }
1557     }
1558 
1559     /* Return logon information */
1560 
1561     /* Create and return a new logon id */
1562     Status = NtAllocateLocallyUniqueId(LogonId);
1563     if (!NT_SUCCESS(Status))
1564     {
1565         TRACE("NtAllocateLocallyUniqueId failed (Status %08lx)\n", Status);
1566         goto done;
1567     }
1568 
1569     /* Create the logon session */
1570     Status = DispatchTable.CreateLogonSession(LogonId);
1571     if (!NT_SUCCESS(Status))
1572     {
1573         TRACE("CreateLogonSession failed (Status %08lx)\n", Status);
1574         goto done;
1575     }
1576 
1577     SessionCreated = TRUE;
1578 
1579     /* Build and fill the interactive profile buffer */
1580     Status = BuildInteractiveProfileBuffer(ClientRequest,
1581                                            UserInfo,
1582                                            ComputerName,
1583                                            (PMSV1_0_INTERACTIVE_PROFILE*)ProfileBuffer,
1584                                            ProfileBufferSize);
1585     if (!NT_SUCCESS(Status))
1586     {
1587         TRACE("BuildInteractiveProfileBuffer failed (Status %08lx)\n", Status);
1588         goto done;
1589     }
1590 
1591     /* Return the token information type */
1592     *TokenInformationType = LsaTokenInformationV1;
1593 
1594     /* Build and fill the token information buffer */
1595     Status = BuildTokenInformationBuffer((PLSA_TOKEN_INFORMATION_V1*)TokenInformation,
1596                                          AccountDomainSid,
1597                                          UserInfo,
1598                                          SpecialAccount);
1599     if (!NT_SUCCESS(Status))
1600     {
1601         TRACE("BuildTokenInformationBuffer failed (Status %08lx)\n", Status);
1602         goto done;
1603     }
1604 
1605 done:
1606     /* Erase password */
1607     if (ErasePassword)
1608     {
1609         RtlEraseUnicodeString(ErasePassword);
1610     }
1611 
1612     /* Update the logon time/count or the bad password time/count */
1613     if ((UserHandle != NULL) &&
1614         (Status == STATUS_SUCCESS || Status == STATUS_WRONG_PASSWORD))
1615     {
1616         SAMPR_USER_INFO_BUFFER InternalInfo;
1617 
1618         RtlZeroMemory(&InternalInfo, sizeof(InternalInfo));
1619 
1620         if (Status == STATUS_SUCCESS)
1621             InternalInfo.Internal2.Flags = USER_LOGON_SUCCESS;
1622         else
1623             InternalInfo.Internal2.Flags = USER_LOGON_BAD_PASSWORD;
1624 
1625         SamrSetInformationUser(UserHandle,
1626                                UserInternal2Information,
1627                                &InternalInfo);
1628     }
1629 
1630     /* Return the account name */
1631     *AccountName = DispatchTable.AllocateLsaHeap(sizeof(UNICODE_STRING));
1632     if (*AccountName != NULL)
1633     {
1634         (*AccountName)->Buffer = DispatchTable.AllocateLsaHeap(LogonInfo->UserName.Length +
1635                                                                sizeof(UNICODE_NULL));
1636         if ((*AccountName)->Buffer != NULL)
1637         {
1638             (*AccountName)->MaximumLength = LogonInfo->UserName.Length +
1639                                             sizeof(UNICODE_NULL);
1640             RtlCopyUnicodeString(*AccountName, &LogonInfo->UserName);
1641         }
1642     }
1643 
1644     /* Return the authenticating authority */
1645     *AuthenticatingAuthority = DispatchTable.AllocateLsaHeap(sizeof(UNICODE_STRING));
1646     if (*AuthenticatingAuthority != NULL)
1647     {
1648         (*AuthenticatingAuthority)->Buffer = DispatchTable.AllocateLsaHeap(LogonInfo->LogonDomainName.Length +
1649                                                                            sizeof(UNICODE_NULL));
1650         if ((*AuthenticatingAuthority)->Buffer != NULL)
1651         {
1652             (*AuthenticatingAuthority)->MaximumLength = LogonInfo->LogonDomainName.Length +
1653                                                         sizeof(UNICODE_NULL);
1654             RtlCopyUnicodeString(*AuthenticatingAuthority, &LogonInfo->LogonDomainName);
1655         }
1656     }
1657 
1658     /* Return the machine name */
1659     *MachineName = DispatchTable.AllocateLsaHeap(sizeof(UNICODE_STRING));
1660     if (*MachineName != NULL)
1661     {
1662         (*MachineName)->Buffer = DispatchTable.AllocateLsaHeap((ComputerNameSize + 1) * sizeof(WCHAR));
1663         if ((*MachineName)->Buffer != NULL)
1664         {
1665             (*MachineName)->MaximumLength = (ComputerNameSize + 1) * sizeof(WCHAR);
1666             (*MachineName)->Length = ComputerNameSize * sizeof(WCHAR);
1667             RtlCopyMemory((*MachineName)->Buffer, ComputerName, (*MachineName)->MaximumLength);
1668         }
1669     }
1670 
1671     if (!NT_SUCCESS(Status))
1672     {
1673         if (SessionCreated != FALSE)
1674             DispatchTable.DeleteLogonSession(LogonId);
1675 
1676         if (*ProfileBuffer != NULL)
1677         {
1678             DispatchTable.FreeClientBuffer(ClientRequest,
1679                                            *ProfileBuffer);
1680             *ProfileBuffer = NULL;
1681         }
1682     }
1683 
1684     if (UserHandle != NULL)
1685         SamrCloseHandle(&UserHandle);
1686 
1687     SamIFree_SAMPR_USER_INFO_BUFFER(UserInfo,
1688                                     UserAllInformation);
1689     SamIFree_SAMPR_ULONG_ARRAY(&RelativeIds);
1690     SamIFree_SAMPR_ULONG_ARRAY(&Use);
1691 
1692     if (DomainHandle != NULL)
1693         SamrCloseHandle(&DomainHandle);
1694 
1695     if (ServerHandle != NULL)
1696         SamrCloseHandle(&ServerHandle);
1697 
1698     if (AccountDomainSid != NULL)
1699         RtlFreeHeap(RtlGetProcessHeap(), 0, AccountDomainSid);
1700 
1701     if (Status == STATUS_NO_SUCH_USER ||
1702         Status == STATUS_WRONG_PASSWORD)
1703     {
1704         *SubStatus = Status;
1705         Status = STATUS_LOGON_FAILURE;
1706     }
1707 
1708     TRACE("LsaApLogonUserEx2 done (Status 0x%08lx, SubStatus 0x%08lx)\n", Status, *SubStatus);
1709 
1710     return Status;
1711 }
1712 
1713 
1714 /*
1715  * @unimplemented
1716  */
1717 NTSTATUS
1718 NTAPI
1719 SpLsaModeInitialize(
1720     _In_ ULONG LsaVersion,
1721     _Out_ PULONG PackageVersion,
1722     _Out_ PSECPKG_FUNCTION_TABLE *ppTables,
1723     _Out_ PULONG pcTables)
1724 {
1725     TRACE("SpLsaModeInitialize(0x%lx %p %p %p)\n",
1726           LsaVersion, PackageVersion, ppTables, pcTables);
1727 
1728     if (LsaVersion != SECPKG_INTERFACE_VERSION)
1729         return STATUS_INVALID_PARAMETER;
1730 
1731     *PackageVersion = SECPKG_INTERFACE_VERSION;
1732 
1733     RtlZeroMemory(NtlmLsaFn, sizeof(NtlmLsaFn));
1734 
1735     /* msv1_0 (XP, win2k) returns NULL for
1736      * InitializePackage, LsaLogonUser,LsaLogonUserEx,
1737      * SpQueryContextAttributes and SpAddCredentials */
1738     NtlmLsaFn[0].InitializePackage = NULL;
1739     NtlmLsaFn[0].LsaLogonUser = NULL;
1740     NtlmLsaFn[0].CallPackage = LsaApCallPackage;
1741     NtlmLsaFn[0].LogonTerminated = LsaApLogonTerminated;
1742     NtlmLsaFn[0].CallPackageUntrusted = LsaApCallPackageUntrusted;
1743     NtlmLsaFn[0].CallPackagePassthrough = LsaApCallPackagePassthrough;
1744     NtlmLsaFn[0].LogonUserEx = NULL;
1745     NtlmLsaFn[0].LogonUserEx2 = LsaApLogonUserEx2;
1746     NtlmLsaFn[0].Initialize = SpInitialize;
1747     NtlmLsaFn[0].Shutdown = LsaSpShutDown;
1748     NtlmLsaFn[0].GetInfo = LsaSpGetInfoW;
1749     NtlmLsaFn[0].AcceptCredentials = SpAcceptCredentials;
1750     NtlmLsaFn[0].SpAcquireCredentialsHandle = LsaSpAcquireCredentialsHandle;
1751     NtlmLsaFn[0].SpQueryCredentialsAttributes = LsaSpQueryCredentialsAttributes;
1752     NtlmLsaFn[0].FreeCredentialsHandle = LsaSpFreeCredentialsHandle;
1753     NtlmLsaFn[0].SaveCredentials = LsaSpSaveCredentials;
1754     NtlmLsaFn[0].GetCredentials = LsaSpGetCredentials;
1755     NtlmLsaFn[0].DeleteCredentials = LsaSpDeleteCredentials;
1756     NtlmLsaFn[0].InitLsaModeContext = LsaSpInitLsaModeContext;
1757     NtlmLsaFn[0].AcceptLsaModeContext = LsaSpAcceptLsaModeContext;
1758     NtlmLsaFn[0].DeleteContext = LsaSpDeleteContext;
1759     NtlmLsaFn[0].ApplyControlToken = LsaSpApplyControlToken;
1760     NtlmLsaFn[0].GetUserInfo = LsaSpGetUserInfo;
1761     NtlmLsaFn[0].GetExtendedInformation = LsaSpGetExtendedInformation;
1762     NtlmLsaFn[0].SpQueryContextAttributes = NULL;
1763     NtlmLsaFn[0].SpAddCredentials = NULL;
1764     NtlmLsaFn[0].SetExtendedInformation = LsaSpSetExtendedInformation;
1765 
1766     *ppTables = NtlmLsaFn;
1767     *pcTables = 1;
1768 
1769     return STATUS_SUCCESS;
1770 }
1771 
1772 
1773 /*
1774  * @unimplemented
1775  */
1776 NTSTATUS
1777 WINAPI
1778 SpUserModeInitialize(
1779     _In_ ULONG LsaVersion,
1780     _Out_ PULONG PackageVersion,
1781     _Out_ PSECPKG_USER_FUNCTION_TABLE *ppTables,
1782     _Out_ PULONG pcTables)
1783 {
1784     SECPKG_USER_FUNCTION_TABLE Tables[1];
1785 
1786     TRACE("SpUserModeInitialize(0x%lx %p %p %p)\n",
1787           LsaVersion, PackageVersion, ppTables, pcTables);
1788 
1789     if (LsaVersion != SECPKG_INTERFACE_VERSION)
1790         return STATUS_INVALID_PARAMETER;
1791 
1792     *PackageVersion = SECPKG_INTERFACE_VERSION;
1793 
1794     RtlZeroMemory(&Tables, sizeof(Tables));
1795 
1796 //    Tables[0].InstanceInit = SpInstanceInit;
1797 //    Tables[0].InitUserModeContext = NULL;
1798 //    Tables[0].MakeSignature = NULL;
1799 //    Tables[0].VerifySignature = NULL;
1800 //    Tables[0].SealMessage = NULL;
1801 //    Tables[0].UnsealMessage = NULL;
1802 //    Tables[0].GetContextToken = NULL;
1803 //    Tables[0].SpQueryContextAttributes = NULL;
1804 //    Tables[0].CompleteAuthToken = NULL;
1805 //    Tables[0].DeleteUserModeContext = NULL;
1806 //    Tables[0].FormatCredentials = NULL;
1807 //    Tables[0].MarshallSupplementalCreds = NULL;
1808 //    Tables[0].ExportContext = NULL;
1809 //    Tables[0].ImportContext = NULL;
1810 
1811     *ppTables = Tables;
1812     *pcTables = 1;
1813 
1814     return STATUS_SUCCESS;
1815 }
1816 
1817 /* EOF */
1818