[MSV1_0] Add logon support for the LocalService and NetworkService accounts
authorEric Kohl <eric.kohl@reactos.org>
Sat, 26 May 2018 16:42:31 +0000 (18:42 +0200)
committerEric Kohl <eric.kohl@reactos.org>
Sat, 26 May 2018 16:43:46 +0000 (18:43 +0200)
dll/win32/msv1_0/msv1_0.c

index 624ea2b..8c9a1b6 100644 (file)
@@ -22,7 +22,7 @@ LSA_DISPATCH_TABLE DispatchTable;
 
 static
 NTSTATUS
-GetDomainSid(PRPC_SID *Sid)
+GetAccountDomainSid(PRPC_SID *Sid)
 {
     LSAPR_HANDLE PolicyHandle = NULL;
     PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
@@ -69,6 +69,27 @@ done:
 }
 
 
+static
+NTSTATUS
+GetNtAuthorityDomainSid(PRPC_SID *Sid)
+{
+    SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
+    ULONG Length = 0;
+
+    Length = RtlLengthRequiredSid(0);
+    *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
+    if (*Sid == NULL)
+    {
+        ERR("Failed to allocate SID\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlInitializeSid(*Sid,&NtAuthority, 0);
+
+    return STATUS_SUCCESS;
+}
+
+
 static
 NTSTATUS
 BuildInteractiveProfileBuffer(IN PLSA_CLIENT_REQUEST ClientRequest,
@@ -287,34 +308,72 @@ BuildTokenPrimaryGroup(OUT PTOKEN_PRIMARY_GROUP PrimaryGroup,
 static
 NTSTATUS
 BuildTokenGroups(OUT PTOKEN_GROUPS *Groups,
-                 IN PSID AccountDomainSid)
+                 IN PSID AccountDomainSid,
+                 IN ULONG RelativeId,
+                 IN BOOL SpecialAccount)
 {
     SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
     PTOKEN_GROUPS TokenGroups;
-#define MAX_GROUPS 2
     DWORD GroupCount = 0;
+    DWORD MaxGroups = 2;
     PSID Sid;
     NTSTATUS Status = STATUS_SUCCESS;
 
+    if (SpecialAccount)
+        MaxGroups++;
+
     TokenGroups = DispatchTable.AllocateLsaHeap(sizeof(TOKEN_GROUPS) +
-                                                MAX_GROUPS * sizeof(SID_AND_ATTRIBUTES));
+                                                MaxGroups * sizeof(SID_AND_ATTRIBUTES));
     if (TokenGroups == NULL)
     {
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    Sid = AppendRidToSid(AccountDomainSid, DOMAIN_GROUP_RID_USERS);
-    if (Sid == NULL)
+    if (SpecialAccount)
     {
+        /* Self */
+        Sid = AppendRidToSid(AccountDomainSid, RelativeId);
+        if (Sid == NULL)
+        {
+
+        }
 
+        TokenGroups->Groups[GroupCount].Sid = Sid;
+        TokenGroups->Groups[GroupCount].Attributes =
+            SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
+        GroupCount++;
+
+        /* Member of 'Users' alias */
+        RtlAllocateAndInitializeSid(&SystemAuthority,
+                                    2,
+                                    SECURITY_BUILTIN_DOMAIN_RID,
+                                    DOMAIN_ALIAS_RID_USERS,
+                                    SECURITY_NULL_RID,
+                                    SECURITY_NULL_RID,
+                                    SECURITY_NULL_RID,
+                                    SECURITY_NULL_RID,
+                                    SECURITY_NULL_RID,
+                                    SECURITY_NULL_RID,
+                                    &Sid);
+        TokenGroups->Groups[GroupCount].Sid = Sid;
+        TokenGroups->Groups[GroupCount].Attributes =
+            SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
+        GroupCount++;
     }
+    else
+    {
+        /* Member of the domains users group */
+        Sid = AppendRidToSid(AccountDomainSid, DOMAIN_GROUP_RID_USERS);
+        if (Sid == NULL)
+        {
 
-    /* Member of the domain */
-    TokenGroups->Groups[GroupCount].Sid = Sid;
-    TokenGroups->Groups[GroupCount].Attributes =
-        SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
-    GroupCount++;
+        }
 
+        TokenGroups->Groups[GroupCount].Sid = Sid;
+        TokenGroups->Groups[GroupCount].Attributes =
+            SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
+        GroupCount++;
+    }
 
     /* Member of 'Authenticated users' */
     RtlAllocateAndInitializeSid(&SystemAuthority,
@@ -334,7 +393,7 @@ BuildTokenGroups(OUT PTOKEN_GROUPS *Groups,
     GroupCount++;
 
     TokenGroups->GroupCount = GroupCount;
-    ASSERT(TokenGroups->GroupCount <= MAX_GROUPS);
+    ASSERT(TokenGroups->GroupCount <= MaxGroups);
 
     *Groups = TokenGroups;
 
@@ -346,7 +405,8 @@ static
 NTSTATUS
 BuildTokenInformationBuffer(PLSA_TOKEN_INFORMATION_V1 *TokenInformation,
                             PRPC_SID AccountDomainSid,
-                            PSAMPR_USER_INFO_BUFFER UserInfo)
+                            PSAMPR_USER_INFO_BUFFER UserInfo,
+                            BOOL SpecialAccount)
 {
     PLSA_TOKEN_INFORMATION_V1 Buffer = NULL;
     ULONG i;
@@ -376,7 +436,9 @@ BuildTokenInformationBuffer(PLSA_TOKEN_INFORMATION_V1 *TokenInformation,
         goto done;
 
     Status = BuildTokenGroups(&Buffer->Groups,
-                              (PSID)AccountDomainSid);
+                              (PSID)AccountDomainSid,
+                              UserInfo->All.UserId,
+                              SpecialAccount);
     if (!NT_SUCCESS(Status))
         goto done;
 
@@ -970,9 +1032,10 @@ LsaApLogonUser(IN PLSA_CLIENT_REQUEST ClientRequest,
 //    LARGE_INTEGER AccountExpires;
     LARGE_INTEGER PasswordMustChange;
     LARGE_INTEGER PasswordLastSet;
+    BOOL SpecialAccount = FALSE;
     NTSTATUS Status;
 
-    TRACE("()\n");
+    TRACE("LsaApLogonUser()\n");
 
     TRACE("LogonType: %lu\n", LogonType);
     TRACE("AuthenticationInformation: %p\n", AuthenticationInformation);
@@ -1012,153 +1075,215 @@ LsaApLogonUser(IN PLSA_CLIENT_REQUEST ClientRequest,
     /* Get the logon time */
     NtQuerySystemTime(&LogonTime);
 
-    /* Get the domain SID */
-    Status = GetDomainSid(&AccountDomainSid);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("GetDomainSid() failed (Status 0x%08lx)\n", Status);
-        return Status;
-    }
-
-    /* Connect to the SAM server */
-    Status = SamIConnect(NULL,
-                         &ServerHandle,
-                         SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
-                         TRUE);
-    if (!NT_SUCCESS(Status))
+    /* Check for special accounts */
+    if (_wcsicmp(LogonInfo->LogonDomainName.Buffer, L"NT AUTHORITY") == 0)
     {
-        TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status);
-        goto done;
-    }
+        SpecialAccount = TRUE;
 
-    /* Open the account domain */
-    Status = SamrOpenDomain(ServerHandle,
-                            DOMAIN_LOOKUP,
-                            AccountDomainSid,
-                            &DomainHandle);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SamrOpenDomain failed (Status %08lx)\n", Status);
-        goto done;
-    }
+        /* Get the authority domain SID */
+        Status = GetNtAuthorityDomainSid(&AccountDomainSid);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("GetNtAuthorityDomainSid() failed (Status 0x%08lx)\n", Status);
+            return Status;
+        }
 
-    Names[0].Length = LogonInfo->UserName.Length;
-    Names[0].MaximumLength = LogonInfo->UserName.MaximumLength;
-    Names[0].Buffer = LogonInfo->UserName.Buffer;
+        if (_wcsicmp(LogonInfo->UserName.Buffer, L"LocalService") == 0)
+        {
+            TRACE("SpecialAccount: LocalService\n");
 
-    /* Try to get the RID for the user name */
-    Status = SamrLookupNamesInDomain(DomainHandle,
-                                     1,
-                                     Names,
-                                     &RelativeIds,
-                                     &Use);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SamrLookupNamesInDomain failed (Status %08lx)\n", Status);
-        Status = STATUS_NO_SUCH_USER;
-        goto done;
-    }
+            if (LogonType != Service)
+                return STATUS_LOGON_FAILURE;
 
-    /* Fail, if it is not a user account */
-    if (Use.Element[0] != SidTypeUser)
-    {
-        TRACE("Account is not a user account!\n");
-        Status = STATUS_NO_SUCH_USER;
-        goto done;
-    }
+            UserInfo = RtlAllocateHeap(RtlGetProcessHeap(),
+                                       HEAP_ZERO_MEMORY,
+                                       sizeof(SAMPR_USER_ALL_INFORMATION));
+            if (UserInfo == NULL)
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto done;
+            }
 
-    /* Open the user object */
-    Status = SamrOpenUser(DomainHandle,
-                          USER_READ_GENERAL | USER_READ_LOGON |
-                          USER_READ_ACCOUNT | USER_READ_PREFERENCES, /* FIXME */
-                          RelativeIds.Element[0],
-                          &UserHandle);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SamrOpenUser failed (Status %08lx)\n", Status);
-        goto done;
-    }
+            UserInfo->All.UserId = SECURITY_LOCAL_SERVICE_RID;
+            UserInfo->All.PrimaryGroupId = SECURITY_LOCAL_SERVICE_RID;
+        }
+        else if (_wcsicmp(LogonInfo->UserName.Buffer, L"NetworkService") == 0)
+        {
+            TRACE("SpecialAccount: NetworkService\n");
 
-    Status = SamrQueryInformationUser(UserHandle,
-                                      UserAllInformation,
-                                      &UserInfo);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SamrQueryInformationUser failed (Status %08lx)\n", Status);
-        goto done;
-    }
+            if (LogonType != Service)
+                return STATUS_LOGON_FAILURE;
 
-    TRACE("UserName: %S\n", UserInfo->All.UserName.Buffer);
+            UserInfo = RtlAllocateHeap(RtlGetProcessHeap(),
+                                       HEAP_ZERO_MEMORY,
+                                       sizeof(SAMPR_USER_ALL_INFORMATION));
+            if (UserInfo == NULL)
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto done;
+            }
 
-    /* Check the password */
-    if ((UserInfo->All.UserAccountControl & USER_PASSWORD_NOT_REQUIRED) == 0)
-    {
-        Status = MsvpCheckPassword(&(LogonInfo->Password),
-                                   UserInfo);
-        if (!NT_SUCCESS(Status))
+            UserInfo->All.UserId = SECURITY_NETWORK_SERVICE_RID;
+            UserInfo->All.PrimaryGroupId = SECURITY_NETWORK_SERVICE_RID;
+        }
+        else
         {
-            TRACE("MsvpCheckPassword failed (Status %08lx)\n", Status);
+            Status = STATUS_NO_SUCH_USER;
             goto done;
         }
     }
-
-    /* Check account restrictions for non-administrator accounts */
-    if (RelativeIds.Element[0] != DOMAIN_USER_RID_ADMIN)
+    else
     {
-        /* Check if the account has been disabled */
-        if (UserInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED)
+        TRACE("NormalAccount\n");
+
+        /* Get the account domain SID */
+        Status = GetAccountDomainSid(&AccountDomainSid);
+        if (!NT_SUCCESS(Status))
         {
-            ERR("Account disabled!\n");
-            *SubStatus = STATUS_ACCOUNT_DISABLED;
-            Status = STATUS_ACCOUNT_RESTRICTION;
+            ERR("GetAccountDomainSid() failed (Status 0x%08lx)\n", Status);
+            return Status;
+        }
+
+        /* Connect to the SAM server */
+        Status = SamIConnect(NULL,
+                             &ServerHandle,
+                             SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
+                             TRUE);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status);
             goto done;
         }
 
-        /* Check if the account has been locked */
-        if (UserInfo->All.UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
+        /* Open the account domain */
+        Status = SamrOpenDomain(ServerHandle,
+                                DOMAIN_LOOKUP,
+                                AccountDomainSid,
+                                &DomainHandle);
+        if (!NT_SUCCESS(Status))
         {
-            ERR("Account locked!\n");
-            *SubStatus = STATUS_ACCOUNT_LOCKED_OUT;
-            Status = STATUS_ACCOUNT_RESTRICTION;
+            ERR("SamrOpenDomain failed (Status %08lx)\n", Status);
             goto done;
         }
 
-#if 0
-        /* Check if the account expired */
-        AccountExpires.LowPart = UserInfo->All.AccountExpires.LowPart;
-        AccountExpires.HighPart = UserInfo->All.AccountExpires.HighPart;
+        Names[0].Length = LogonInfo->UserName.Length;
+        Names[0].MaximumLength = LogonInfo->UserName.MaximumLength;
+        Names[0].Buffer = LogonInfo->UserName.Buffer;
 
-        if (AccountExpires.QuadPart != 0 &&
-            LogonTime.QuadPart >= AccountExpires.QuadPart)
+        /* Try to get the RID for the user name */
+        Status = SamrLookupNamesInDomain(DomainHandle,
+                                         1,
+                                         Names,
+                                         &RelativeIds,
+                                         &Use);
+        if (!NT_SUCCESS(Status))
         {
-            ERR("Account expired!\n");
-            *SubStatus = STATUS_ACCOUNT_EXPIRED;
-            Status = STATUS_ACCOUNT_RESTRICTION;
+            ERR("SamrLookupNamesInDomain failed (Status %08lx)\n", Status);
+            Status = STATUS_NO_SUCH_USER;
             goto done;
         }
-#endif
 
-        /* Check if the password expired */
-        PasswordMustChange.LowPart = UserInfo->All.PasswordMustChange.LowPart;
-        PasswordMustChange.HighPart = UserInfo->All.PasswordMustChange.HighPart;
-        PasswordLastSet.LowPart = UserInfo->All.PasswordLastSet.LowPart;
-        PasswordLastSet.HighPart = UserInfo->All.PasswordLastSet.HighPart;
+        /* Fail, if it is not a user account */
+        if (Use.Element[0] != SidTypeUser)
+        {
+            ERR("Account is not a user account!\n");
+            Status = STATUS_NO_SUCH_USER;
+            goto done;
+        }
 
-        if (LogonTime.QuadPart >= PasswordMustChange.QuadPart)
+        /* Open the user object */
+        Status = SamrOpenUser(DomainHandle,
+                              USER_READ_GENERAL | USER_READ_LOGON |
+                              USER_READ_ACCOUNT | USER_READ_PREFERENCES, /* FIXME */
+                              RelativeIds.Element[0],
+                              &UserHandle);
+        if (!NT_SUCCESS(Status))
         {
-            ERR("Password expired!\n");
-            if (PasswordLastSet.QuadPart == 0)
-                *SubStatus = STATUS_PASSWORD_MUST_CHANGE;
-            else
-                *SubStatus = STATUS_PASSWORD_EXPIRED;
+            ERR("SamrOpenUser failed (Status %08lx)\n", Status);
+            goto done;
+        }
 
-            Status = STATUS_ACCOUNT_RESTRICTION;
+        Status = SamrQueryInformationUser(UserHandle,
+                                          UserAllInformation,
+                                          &UserInfo);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamrQueryInformationUser failed (Status %08lx)\n", Status);
             goto done;
         }
 
-        /* FIXME: more checks */
-        // STATUS_INVALID_LOGON_HOURS;
-        // STATUS_INVALID_WORKSTATION;
+        TRACE("UserName: %S\n", UserInfo->All.UserName.Buffer);
+
+        /* Check the password */
+        if ((UserInfo->All.UserAccountControl & USER_PASSWORD_NOT_REQUIRED) == 0)
+        {
+            Status = MsvpCheckPassword(&(LogonInfo->Password),
+                                       UserInfo);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("MsvpCheckPassword failed (Status %08lx)\n", Status);
+                goto done;
+            }
+        }
+
+        /* Check account restrictions for non-administrator accounts */
+        if (RelativeIds.Element[0] != DOMAIN_USER_RID_ADMIN)
+        {
+            /* Check if the account has been disabled */
+            if (UserInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED)
+            {
+                ERR("Account disabled!\n");
+                *SubStatus = STATUS_ACCOUNT_DISABLED;
+                Status = STATUS_ACCOUNT_RESTRICTION;
+                goto done;
+            }
+
+            /* Check if the account has been locked */
+            if (UserInfo->All.UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
+            {
+                ERR("Account locked!\n");
+                *SubStatus = STATUS_ACCOUNT_LOCKED_OUT;
+                Status = STATUS_ACCOUNT_RESTRICTION;
+                goto done;
+            }
+
+#if 0
+            /* Check if the account expired */
+            AccountExpires.LowPart = UserInfo->All.AccountExpires.LowPart;
+            AccountExpires.HighPart = UserInfo->All.AccountExpires.HighPart;
+
+            if (AccountExpires.QuadPart != 0 &&
+                LogonTime.QuadPart >= AccountExpires.QuadPart)
+            {
+                ERR("Account expired!\n");
+                *SubStatus = STATUS_ACCOUNT_EXPIRED;
+                Status = STATUS_ACCOUNT_RESTRICTION;
+                goto done;
+            }
+#endif
+
+            /* Check if the password expired */
+            PasswordMustChange.LowPart = UserInfo->All.PasswordMustChange.LowPart;
+            PasswordMustChange.HighPart = UserInfo->All.PasswordMustChange.HighPart;
+            PasswordLastSet.LowPart = UserInfo->All.PasswordLastSet.LowPart;
+            PasswordLastSet.HighPart = UserInfo->All.PasswordLastSet.HighPart;
+
+            if (LogonTime.QuadPart >= PasswordMustChange.QuadPart)
+            {
+                ERR("Password expired!\n");
+                if (PasswordLastSet.QuadPart == 0)
+                    *SubStatus = STATUS_PASSWORD_MUST_CHANGE;
+                else
+                    *SubStatus = STATUS_PASSWORD_EXPIRED;
+
+                Status = STATUS_ACCOUNT_RESTRICTION;
+                goto done;
+            }
+
+            /* FIXME: more checks */
+            // STATUS_INVALID_LOGON_HOURS;
+            // STATUS_INVALID_WORKSTATION;
+        }
     }
 
     /* Return logon information */
@@ -1199,7 +1324,8 @@ LsaApLogonUser(IN PLSA_CLIENT_REQUEST ClientRequest,
     /* Build and fill the token information buffer */
     Status = BuildTokenInformationBuffer((PLSA_TOKEN_INFORMATION_V1*)TokenInformation,
                                          AccountDomainSid,
-                                         UserInfo);
+                                         UserInfo,
+                                         SpecialAccount);
     if (!NT_SUCCESS(Status))
     {
         TRACE("BuildTokenInformationBuffer failed (Status %08lx)\n", Status);