Invoke(SetPassword) in Active Directory, Works, Then Access is Den

Discussion in 'ASP .Net Security' started by ptstesting, Mar 29, 2006.

  1. ptstesting

    ptstesting Guest

    My setup:
    Using ASP.NET web app, have permissions setup properly, using impersonation
    as admin account before modifying active directory entry. I have the latest
    ..NET 2.0 and service packs installed.

    The problem:
    I can call Active Directory's

    myDE.Invoke("SetPassword", new object[] {Password});

    successfully a few times, but after about 10 minutes, if I try the call
    again, I receive "Access is Denied". I have to re-upload my web application
    files to restart the web application for it to resume working correctly.

    Why would this work several calls in a row, and then begin failing after 10
    minutes?

    I read this post from a Microsoft techie:
    http://www.gotdotnet.com/Community/MessageBoard/Thread.aspx?id=157773

    It mentions something about a bug in .NET where the server stub runs down
    after several successful calls? Something about a Microsoft fix is available,
    but no download link?

    Does anyone know more about this issue?
     
    ptstesting, Mar 29, 2006
    #1
    1. Advertisements

  2. This depends on how the password modification is being done. SetPassword
    tries 3 different approaches and they all have different failure modes.

    The most reliable method involves using LDAP password modifications, but
    that requires that your DCs have SSL certs so that you can connect to LDAP
    using SSL. This is not done in AD by default, though. You would need to
    get your domain admins to configure it.

    The other two methods, Kerberos set password and NetUserSetInfo, may not be
    as reliable. It is hard to tell which one is being used without sniffing
    the network traffic.

    What is the OS the ASP.NET runs on (with SP)? That determines the version
    of ADSI that .NET uses. Also, what version of AD are you using (2003 or
    2000)?

    Joe K.
     
    Joe Kaplan \(MVP - ADSI\), Mar 31, 2006
    #2
    1. Advertisements

  3. ptstesting

    ptstesting Guest

    Joe,

    Thanks for your help.

    The server OS is Windows Server 2003, with .NET 2.0 installed. I'm not
    sure which version of AD, but Microsoft Management Console is 2.0 Version 5.2
    and ADSI Edit 5.2.

    This is how I make the call:

    internal static DirectoryEntry GetDirectoryObject(string UserName, string
    Password)
    {
    DirectoryEntry oDE;
    oDE = new DirectoryEntry(ADPath, UserName, Password,
    AuthenticationTypes.Secure);
    return oDE;
    }

    public static DirectoryEntry GetUser(string UserName)
    {
    DirectoryEntry de = Utility.GetDirectoryObject();
    DirectorySearcher deSearch = new DirectorySearcher();
    deSearch.SearchRoot = de;
    deSearch.Filter = "(&(objectClass=user)(CN=" + UserName + "))";
    deSearch.SearchScope = SearchScope.Subtree;
    SearchResult results = deSearch.FindOne();
    if (!(results == null))
    {
    de = new DirectoryEntry(results.Path, Utility.ADUser,
    Utility.ADPassword, AuthenticationTypes.Secure);
    return de;
    }
    else
    {
    return null;
    }
    }

    //
    // Actual call to set the password, which runs fine 10 minutes then begins
    failing.
    //
    try
    {
    string Password = "password";
    DirectoryEntry oDE = GetUser("Doe, John");
    oDE.Invoke("SetPassword", new object[] {Password});
    }
    catch (Exception excep)
    {
    //
    // "Access is Denied" error may occur here after 10 mins.
    // Run the invoke from a console app, launched via Process.Start() instead.
    //
    }

    I believe I have found a work-around though. I created a command-line
    console app, which uses impersonation as an admin user, and has a
    SetPassword() function with the same code as above. The web app tries to use
    the Invoke(SetPassword) first, but if that fails, it does a Process.Start()
    to let the console app do the Invoke command. This seems to be reliable and
    (so-far) is working after the 10 minute failing from the web app's invoke.
    Maybe some weird permissions bug?

    ptstesting

     
    ptstesting, Mar 31, 2006
    #3
  4. Try to find out if AD is 2003. If it is, then you can use an LDAP password
    modification using System.DirectoryServices.Protocols using Kerberos with
    SSPI encryption as 2003 support 128 bit cipher strength. Otherwise, you
    still need SSL/LDAP for an LDAP password mod.

    I have a sample from my upcoming book that shows how to do this in C#.

    To find out the server version, use a tool like LDP.exe (which you really
    should be using if you plan to do any serious LDAP programming against AD or
    ADAM) to check out what the server is advertising for its capabilities. LDP
    shows you this as soon as you connect by default.

    This will allow you to run the code inline without having to resort to the
    external process, which is a little hackish. :)

    Joe K.

     
    Joe Kaplan \(MVP - ADSI\), Mar 31, 2006
    #4
  5. ptstesting

    ptstesting Guest

    I've included the output from connecting in LDP.exe below (the real domain
    name is hidden). While the app seems to be working stable now with my custom
    command line tool as backup, it would be nice to get rid of the hackish part.
    Do you have a sample that you could post on how to change the password as you
    described? Or would I have to wait for your book? =)

    ld = ldap_open("mydomain.org", 389);
    Established connection to mydomain.org.
    Retrieving base DSA information...
    Result <0>: (null)
    Matched DNs:
    Getting 1 entries:1> currentTime: 04/03/2006 09:25:52 Eastern Standard Time Eastern Daylight
    Time;
    1> subschemaSubentry:
    CN=Aggregate,CN=Schema,CN=Configuration,DC=mydomain,DC=org;
    1> dsServiceName: CN=NTDS
    Settings,CN=mydomain-02,CN=Servers,CN=Default-First-Site,CN=Sites,CN=Configuration,DC=mydomain,DC=org;
    5> namingContexts: DC=mydomain,DC=org; CN=Configuration,DC=mydomain,DC=org;
    CN=Schema,CN=Configuration,DC=mydomain,DC=org;
    DC=ForestDnsZones,DC=mydomain,DC=org; DC=DomainDnsZones,DC=mydomain,DC=org;
    1> defaultNamingContext: DC=mydomain,DC=org;
    1> schemaNamingContext: CN=Schema,CN=Configuration,DC=mydomain,DC=org;
    1> configurationNamingContext: CN=Configuration,DC=mydomain,DC=org;
    1> rootDomainNamingContext: DC=mydomain,DC=org;
    22> supportedControl: 1.2.840.113556.1.4.319; 1.2.840.113556.1.4.801;
    1.2.840.113556.1.4.473; 1.2.840.113556.1.4.528; 1.2.840.113556.1.4.417;
    1.2.840.113556.1.4.619; 1.2.840.113556.1.4.841; 1.2.840.113556.1.4.529;
    1.2.840.113556.1.4.805; 1.2.840.113556.1.4.521; 1.2.840.113556.1.4.970;
    1.2.840.113556.1.4.1338; 1.2.840.113556.1.4.474; 1.2.840.113556.1.4.1339;
    1.2.840.113556.1.4.1340; 1.2.840.113556.1.4.1413; 2.16.840.1.113730.3.4.9;
    2.16.840.1.113730.3.4.10; 1.2.840.113556.1.4.1504; 1.2.840.113556.1.4.1852;
    1.2.840.113556.1.4.802; 1.2.840.113556.1.4.1907;
    2> supportedLDAPVersion: 3; 2;
    12> supportedLDAPPolicies: MaxPoolThreads; MaxDatagramRecv;
    MaxReceiveBuffer; InitRecvTimeout; MaxConnections; MaxConnIdleTime;
    MaxPageSize; MaxQueryDuration; MaxTempTableSize; MaxResultSetSize;
    MaxNotificationPerConn; MaxValRange;
    1> highestCommittedUSN: 15767330;
    4> supportedSASLMechanisms: GSSAPI; GSS-SPNEGO; EXTERNAL; DIGEST-MD5;
    1> dnsHostName: mydomain-dc02.mydomain.org;
    1> ldapServiceName: mydomain.org:mydomain-dc02$@mydomain.ORG;
    1> serverName:
    CN=mydomain-DC02,CN=Servers,CN=Default-First-Site,CN=Sites,CN=Configuration,DC=mydomain,DC=org;
    3> supportedCapabilities: 1.2.840.113556.1.4.800; 1.2.840.113556.1.4.1670;
    1.2.840.113556.1.4.1791;
    1> isSynchronized: TRUE;
    1> isGlobalCatalogReady: TRUE;
    1> domainFunctionality: 2 = ( DS_BEHAVIOR_WIN2003 );
    1> forestFunctionality: 2 = ( DS_BEHAVIOR_WIN2003 );
    1> domainControllerFunctionality: 2 = ( DS_BEHAVIOR_WIN2003 );
    -----------



     
    ptstesting, Apr 3, 2006
    #5
  6. Sure, here is the class from the book (this will be available as a free
    download on the book's website in the next few weeks anyway...). Since your
    DC is 2003 (according to LDP), then you should have no problem using
    Kerberos-based channel encryption to do the password mod instead of needing
    SSL.

    The main method included is just for test execution. You can hack that out
    or use it as you see fit. I hope this helps.

    Joe K.

    using System;
    using System.DirectoryServices.Protocols;
    using System.Net;
    using System.Text;

    public class PasswordModifier
    {
    public static void Main()
    {
    NetworkCredential credential = new NetworkCredential(
    "someuser",
    "Password1",
    "domain"
    );
    DirectoryConnection connection;

    try
    {
    //change these options to use SSL encryption
    //by adding :636 to the server name and setting useSsl to "true"
    connection = GetConnection(
    "domain.com",
    credential,
    false
    );

    ChangePassword(
    connection,
    "CN=someuser,CN=users,DC=domain,DC=com",
    "Password1",
    "Password2"
    );

    Console.WriteLine("Password modified!");
    IDisposable disposable = connection as IDisposable;
    if (disposable != null)
    disposable.Dispose();
    }
    catch (Exception ex)
    {
    Console.WriteLine(ex.ToString());
    }
    }

    private static DirectoryConnection GetConnection(
    string server,
    NetworkCredential credential,
    bool useSsl
    )
    {
    LdapConnection connection =
    new LdapConnection(server);

    if (useSsl)
    {
    connection.SessionOptions.SecureSocketLayer = true;
    }
    else
    {
    connection.SessionOptions.Sealing = true;
    }

    connection.Bind(credential);
    return connection;
    }

    private static void ChangePassword(
    DirectoryConnection connection,
    string userDN,
    string oldPassword,
    string newPassword
    )
    {
    DirectoryAttributeModification deleteMod =
    new DirectoryAttributeModification();
    deleteMod.Name = "unicodePwd";
    deleteMod.Add(GetPasswordData(oldPassword));
    deleteMod.Operation = DirectoryAttributeOperation.Delete;

    DirectoryAttributeModification addMod =
    new DirectoryAttributeModification();
    addMod.Name = "unicodePwd";
    addMod.Add(GetPasswordData(newPassword));
    addMod.Operation = DirectoryAttributeOperation.Add;

    ModifyRequest request = new ModifyRequest(
    userDN,
    deleteMod,
    addMod
    );
    DirectoryResponse response = connection.SendRequest(request);
    }

    private static void SetPassword(
    DirectoryConnection connection,
    string userDN,
    string password
    )
    {
    DirectoryAttributeModification pwdMod =
    new DirectoryAttributeModification();
    pwdMod.Name = "unicodePwd";
    pwdMod.Add(GetPasswordData(password));
    pwdMod.Operation = DirectoryAttributeOperation.Replace;

    ModifyRequest request = new ModifyRequest(
    userDN,
    pwdMod
    );
    DirectoryResponse response = connection.SendRequest(request);
    }

    private static byte[] GetPasswordData(string password)
    {
    string formattedPassword;
    formattedPassword = String.Format("\"{0}\"", password);
    return (Encoding.Unicode.GetBytes(formattedPassword));
    }
    }


     
    Joe Kaplan \(MVP - ADSI\), Apr 3, 2006
    #6
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.