locked
Windows logon-Need another opinion RRS feed

  • Question

  • User-120142360 posted

    I have inherited some code for a web site and asked to expand on it. I grabbed the books and did some quick learning on asp.net and c#. For the last year I have been adding LDAP lookups and AD account management tools to the site. All of this is behind a logon page that worked, so I didn't mess with it.

    Over the past few weeks as the load on the system is growing I have been experiencing some errors at logon that I can't quite track the cause of and now I am looking at redesigning the login methods. The errors continue until I reboot the server. Luckilly it is load balanced so I can take one down and the other keeps the site up. Sometimes the error will clear on it's own, so when this is returned I let the user into the site. Cycling IIS or the ASP.Net processes do not clear the error.

    If I can get the current process working for now that would be best. They (higher up the food chain) have a list of priorities and doing this redesign in not high on the list. I can do it between other projects, but that will take a couple weeks.

    I have been looking at the forms documentation and just wanted to get a some input from more experianced minds. I still don't quite understand how I can get the logon information to authenticate against AD and tie that to the user object so I can do my LDAP calls later, but I am still reading.

    Situation:

    Student and Staff website with one logon page, redirects to sub-sited done by evaluation of values in AD.

    Need to be able to authenticate against AD with the username/password throughout the session. This is for LDAP/ADSI functions against AD. Currently storing the username/password pair in the session and passing it into methods as needed. Would like to find a more secure way of doing this.

    Current Code:

    Authentication is done in this manner -

    errormessage = ptools.login_successful(domain, LoginUserNameBox.Text, LoginPasswordBox.Text);

    public string login_successful(string domain, string username, string password)
    {

    // Defines and initializes string variables
    string errMessage = "";
    string guid = "";

    // Sets up Active Directory variables
    string machineAndUser = domain + "\\" + username;
    string adsPath = String.Format("WinNT://{0}/{1}, user", domain, username);

    // Defines and initializes an Active Directory user variable to an Active Directory object
    DirectoryEntry user = new DirectoryEntry(adsPath, machineAndUser, password, AuthenticationTypes.Secure);

    try
    {
       guid = user.Guid.ToString();
       errMessage = guid;

       if
    (guid != "")
       {
          errMessage =
    "Login successful";
       }
    }
    catch(Exception ex)

       
    // ex.Message extracts the message from the exception
       
    string message = ex.Message;
       // ex.InnerException provides a more user friendly error message from .NET
       
    while((ex = ex.InnerException) != null)
       {
          message = ex.Message;
       }
       errMessage = message;
    }
    finally

       
    // Deletes the user object from memory.
       
    user.Dispose();
    }
    return errMessage;
    }

    The error I have been recieving from this process is this:

    Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again

    One or two of these is not a problem. After about 20 or so users having this error on logon my LDAP Directory searches start to return:

    System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT has been returned from a call to a COM component.

    The code that returns that error is:

    // Create AD LDAP search target
    LDAPQuery = string.Format("LDAP://{0}", Domain);

    try
    {
    DirectoryEntry SearchRoot =
    new DirectoryEntry(LDAPQuery, UName, UPass, AuthenticationTypes.Secure);
    DirectorySearcher mySearcher =
    new DirectorySearcher(SearchRoot);

    // Configure Search Returns
    mySearcher.PropertiesToLoad.Add("userfunction");

    // Configure Search Filter
    mySearcher.Filter = ("(&(objectClass=user)(samaccountname=" + UName + "))");

    (...Find one code...)
    }
    catch (System.Runtime.InteropServices.COMException)
    {
       System.Runtime.InteropServices.
    COMException exception = new System.Runtime.InteropServices.COMException();

       errorMessage += exception;
    }

     

    Thanks in advance for any help,

    Jay

    Thursday, January 12, 2006 12:03 PM

All replies

  • User1354132231 posted
    The WinNT provider does not do well in a server environment.  I am actually suprised you don't see this with a much smaller load.  I have been able to get this with only 2 or 3 users.

    Here is the deal, you can replace this with a few different options.  You can use LogonUser API in 2003 or XP safely which is a good choice for just checking username and passwords.  You can use SSPI logon with .NET 2.0 fairly easily which will also work nicely.  These all use the underlying APIs and not LDAP per se.  This also means they won't directly support things like ADAM.

    For LDAP types of authentication, you have two choices.  You can use the LDAP provider, which will help a little bit but will still fail under heavy load (don't know a magic #, but it will happen perhaps around 50-100 users most likely).  You can also use pure LDAP authentication with .NET 2.0 (this works especially well if you are using SSL with your AD or ADAM).  This will scale better than using a DirectoryEntry and could be a very good solution too (it might be the fastest if you can take advantage of fast concurrent binding).

    The easiest is to just change the code a bit to the LDAP provider which will give you some headroom.  Next, I would take the LogonUser approach if you are using 2003 or XP - this is also fairly easy.  Finally I would use the pure LDAP approach if I could use .NET 2.0.  If I was really high volume, I would use the pure LDAP approach.  Anywhere inbetween you could basically choose one of the three.
    Thursday, January 12, 2006 2:02 PM
  • User-120142360 posted

    While reading your post I had one of those "Well Duh" moments about the WinNT provider. Don't know why I didn't see it before, but that is why I needed another set of eyes to review the existing code.

    Thanks for the list of options. Considering I need this to scale to a potentially huge number of users I am going to look into the 2.0 LDAP option. Of course I am still waiting on the security guys to approve 2.0 on our environment and it has been like pulling teeth to get a server cert. Gotta love working for the Gov!

    I will fiddle with the LDAP provider on my dev site to see what I can do. If you have any code resources for this type of authentication please post a link. I come from an Active Directory, vbscript, ADSI background and have barely scratched the surface of asp.net.

    If I understand the concept correctly I should change my code to retrieve the user object using the passed in username/password. I will have to play with any errors to get the correct text to return to the user.

    Hmmm...

    Again, I think I understand it. I'll have to play some.

    Thanks for the help.

    Jay

    Thursday, January 12, 2006 2:55 PM
  • User-120142360 posted

    Ryan,

    I am in need of some more help. I have set up a test site and implemented the LDAP login using .Net 2.0. It works, but only under some fixed circumstances.

    If the user's account is normal, the site logs in just fine.

    If the user's account is set to change password at next login, the login fails.

    If the user's password has expired, the login fails.

    The problem I am having is I need to be able to capture the last two and redirect to a password reset page. I am currently doing this with the WinNT provider, but can't capture the messages out of the login control to do the redirects.

    Do you have any resources that you can point me to so I can figure this out?

    Jay

    Friday, February 10, 2006 3:29 PM
  • User1354132231 posted
    I don't know of any resources per se, but you could handle this yourself.  You just need to run a trusted process outside the login and first inspect the account for the two scenarios you have described before you attempt to log them in.  This is the standard way of handling.

    The only other option you have is to use the LogonUser API which I believe might throw an error that differs for the two situations and you could handle to decide where they go.  It is up to you how you want to proceed.  I would personally prototype the LogonUser API first to see what it will give you and then if that does not suffice setup the trusted system to do the check for you first.
    Monday, February 13, 2006 11:31 AM
  • User1814392858 posted
    Thank you both!  We are doing almost exactly the same thing and running into the same problem (sporadically).  We have fairly high volume and are authenticating against multiple domains - but the problem would only manifest itself every 3-4 weeks and only for a single domain - the rest are always fine, or if they get the multiple connections error, it would correct itself quickly.  I will try swapping out the code with the LogonUser API and see what happens.   
    Wednesday, March 1, 2006 5:50 PM
  • User1814392858 posted

    So far, switching to the LogonUser API seems to be working great, it actually seems really responsive.  I used the LOGON32_LOGON_NETWORK logon type.

    Thanks again to you both - we have been trying to scratch that itch for a long time.

     

    Friday, March 3, 2006 2:54 PM