Monday, May 31, 2010

Problem with LdapMembershipProvider and users which belong to more than 1000 AD groups

Recently I investigated unclear issue on production environment: from appropriate moment special user’s account which was used for special purposes (some kind of “common” user account) stopped login into Sharepoint portal. We have web application which was extended to Internet zone with FBA using LdapMembershipProvider and LdapRoleProvider, i.e. all users are stored in AD. What was strange is that on local development environment common account still worked well.

In FBA zone we use custom login page with standard ASP.Net Login control. By default it uses the following code when user is authenticated:

   1: private void AuthenticateUsingMembershipProvider(AuthenticateEventArgs e)
   2: {
   3:     e.Authenticated = LoginUtil.GetProvider(this.MembershipProvider).
   4: ValidateUser(this.UserNameInternal, this.PasswordInternal);
   5: }
I.e. LdapMembershipProvider.ValidateUser() method returned false. I checked that user name and password were specified correctly. After unsuccessful login attempt the following record appeared in Sharepoint logs in 12/logs folder:

Logon failure: unknown user name or bad password.

at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

at System.DirectoryServices.DirectoryEntry.Bind()

at System.DirectoryServices.DirectoryEntry.get_NativeObject()

at Microsoft.Office.Server.Security.LdapMembershipProvider.ValidateUser(String name, String password)

I checked LdapMembershipProvider.ValidateUser() method in Reflector. It failed on the following lines:

   1: public override bool ValidateUser(string name, string password)
   2: {
   3:     ...
   4:     if (((username != null) && (username.Length > 0)) &&
   5: ((password != null) && (password.Length > 0)))
   6:     {
   7:         entry = new DirectoryEntry(DS.GetLDAPURL(this._LDAPServer,
   8: this._LDAPPort, "RootDSE"), username, password);
   9:         entry.AuthenticationType = LDAP.GetDSAuth(this._LDAPUseSSL);
  10:         object nativeObject = entry.NativeObject;
  11:         return true;
  12:     }
  13:     return false;
  14: }

So our “common” user account failed to bind to directory entry using its user name and password because of some reasons. And this was really strange because other users still was able to login.

I added this common account into Domain Admins group and tried to login into WFE directly under this account using Remote Desktop. But Windows didn’t allow me to login with the following message:

During a logon attempt, the user's security context accumulated too many security IDs.

This was actually the reason: we have too many groups membership for our common account (because of special business requirements). After this I found this article in Microsoft knowledge base which explains the reasons of this issue and that this behavior is by design:

This behavior occurs because Windows systems contain a limit that prevents a user's security access token from containing more than 1000 security identifiers (SIDs). This means that when a user is being validated for access rights to establish a new session with a server, that user must not be a member of more than 1000 groups in that server's domain. If this limit is exceeded, access to the server is denied, and the error code 1384 is returned to the user.

Microsoft has confirmed that this is a problem in the Microsoft products that are listed in the "Applies to" section.This behavior is by design.

As “Applies To” section mentions that this behavior valid also for Windows Vista and Server 2008 I think that there is no actually workaround for this at the current moment (I’m not sure about Windows 7 and Server 2008 R2). So we removed unnecessary groups memberships from our common user account in order to allow login into Sharepoint portal under this account.

Update 2010-06-02: note that Windows takes into account total number of groups which user belongs to recursively (i.e. not only those groups which user belongs to directly). E.g. User1 is member of Group1 and Group1 is in turn member of Group2_1, Group2_2, …, Group2_1000. In this case User1 will be member of 1001 groups (1 Group1 + 1000 Group2_n).

No comments:

Post a Comment