User Management with Active Directory—Determining Domain-Wide Account Policies


Jump to: navigation, search
Visual C# Tutorials
C# Tutorials

User Management

© 2006 Pearson Education, Inc.

Determining Domain-Wide Account Policies

When working with user accounts in Active Directory, it is common to need to refer to domain-wide account policies. For example, policies such as the minimum and maximum password age and the minimum password length, as well as lockout policy, are determined at the domain level and apply to each user object in the domain.

All of the values are stored directly in the domain root object (not in RootDSE, but in the object pointed to by the defaultNamingContext attribute in RootDSE) as a set of attributes such as maxPwdAge, minPwdLength, and lockoutThreshold. Additionally, the password complexity rules are encoded in an enumerated value in the pwdProperties attribute.

These values tend to be quite static in most domains, so we would typically want to read these values only once per program execution. To make the policy values easy to consume, we show in Listing 10.7 a wrapper class for the domain account policies that converts all of the values into convenient .NET data types, such as TimeSpan. A special .NET enumeration type for the types of the password policy is also included. We won’t be able to include all of the class properties in the book, as that would take too much space, but we will have the full class available on the book’s web site.

We will refer to this sample in future discussions when demonstrating how to determine an account’s lockout status and for finding accounts with expiring passwords. It is also worthy to note that any LargeInteger values in these policy attributes are stored as negative values. We chose to invert them back to positive values because it is easier to think about them in this way. Developers choosing to use these attributes should keep this in mind, as it will throw off calculations later if not accounted for.

Listing 10.7: Determining Domain Policies

[Flags]
public enum PasswordPolicy
{
  DOMAIN_PASSWORD_COMPLEX=1,
  DOMAIN_PASSWORD_NO_ANON_CHANGE=2, 
  DOMAIN_PASSWORD_NO_CLEAR_CHANGE=4, 
  DOMAIN_LOCKOUT_ADMINS=8,
  DOMAIN_PASSWORD_STORE_CLEARTEXT=16,
  DOMAIN_REFUSE_PASSWORD_CHANGE=32
}
 
public class DomainPolicy
{
  ResultPropertyCollection attribs;
 
  public DomainPolicy(DirectoryEntry domainRoot)
  {
    string[] policyAttributes = new string[] {
      "maxPwdAge", "minPwdAge", "minPwdLength", 
      "lockoutDuration", "lockOutObservationWindow", 
      "lockoutThreshold", "pwdProperties", 
      "pwdHistoryLength", "objectClass", 
      "distinguishedName"
      };
 
    //we take advantage of the marshaling with
    //DirectorySearcher for LargeInteger values...
    DirectorySearcher ds = new DirectorySearcher(
      domainRoot,
      "(objectClass=domainDNS)",
      policyAttributes,
      SearchScope.Base
      );
 
    SearchResult result = ds.FindOne();
 
    //do some quick validation...							  
    if (result == null)
    {
      throw new ArgumentException(
        "domainRoot is not a domainDNS object."
        );
    }
 
    this.attribs = result.Properties;
  }
 
  //for some odd reason, the intervals are all stored
  //as negative numbers. We use this to "invert" them
  private long GetAbsValue(object longInt)
  {
    return Math.Abs((long)longInt);
  }
 
  public TimeSpan MaxPasswordAge
  {
    get
    {
      string val = "maxPwdAge";
      if (this.attribs.Contains(val))
      {
        long ticks = GetAbsValue(
          this.attribs[val][0]
          );
 
        if (ticks > 0)
          return TimeSpan.FromTicks(ticks);
      }
 
      return TimeSpan.MaxValue;
    }
  }
 
  public PasswordPolicy PasswordProperties
  {
    get
    {
      string val = "pwdProperties";
      //this should fail if not found
      return (PasswordPolicy)this.attribs[val][0];
    }
  }
  
  //truncated for book space
}

Listing 10.7 is meant to run on an Active Directory domain. Where does this leave ADAM instances? By default, ADAM will assume any local or domain policies on the Windows 2003 server where it is running. This means that if our Windows 2003 server is a member of the domain, we can simply use code similar to that in Listing 10.7. If, however, the server is running in a workgroup configuration, the policy will be determined locally. Therefore, Listing 10.7 would not be appropriate. Instead, we would need to know our local policy or attempt to discover it using Windows Management Instrumentation (WMI) classes.


Previous_Page_.gif Next_Page_.gif

Share this page
  • del.icio.us
  • Facebook
  • Google+
  • StumbleUpon