2

I'm working with the Active Directory DirectoryServices.AccountManagement API, and am attempting to connect to the server using the following code:

PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, (server + ":" + port), loginUsername, loginPassword);

The first thing I would like to do is check that the loginUsername and loginPassword are valid and have enough permissions in the Active Directory instance. To achieve this, I call the following:

bool x = principalContext.ValidateCredentials(null, null);

According to the documentation, this validates the credentials specified in the constructor since null is passed. In the debugger, the following errors are thrown, indicating that the credentials are false:

enter image description here

However, the actual result to the ValidateCredentials check is strangely enough returning true and the code thus continues to execute.

How can this be solved?

EDIT:

Here is another screenshot elaborating on the errors. As shown in the screenshot, I am calling the ValidateCredentials method, and passing null values for the username and password, which according to the documentation will attempt to validate the credentials passed in the PrincipalContext class' constructor.

The screenshot also shows how the username and passwords passed are both "test", which are invalid and do not exist in the Active Directory. The method is returning true, even though there are a number of errors displayed.

enter image description here

Dot NET
  • 4,891
  • 13
  • 55
  • 98
  • 1
    Not sure to understand: you have an exception and a return value for your function? – ken2k Nov 12 '13 at 09:27
  • Just to be explicit: the credentials you are passing are *invalid*, and you do *not* want it to return `true` - correct? – Marc Gravell Nov 12 '13 at 09:29
  • How can a method that throws an exception return a value at the same time (and it is not an `out` parameter)? ***EDIT:*** Is the exception you show actually *handled* (caught with `try`-`catch`) inside the `ValidateCredentials` method? – Jeppe Stig Nielsen Nov 12 '13 at 09:30
  • Also, are you **absolutely sure** that the credentials in the constructor were non-empty? It sounds a lot like "If no credential were specified in the constructor, and the username and password parameters are null, this method validates the default credentials for the current principal." – Marc Gravell Nov 12 '13 at 09:30
  • Correct @MarcGravell. I am passing invalid credentials in the PrincipalContext constructor, on purpose so as to test it. And the ValidateCredentials method is returning true, even though an internal error is being thrown which states that the login credentials are false – Dot NET Nov 12 '13 at 09:30
  • @DotNET what exactly do you mean by "an internal error is being thrown..." in combination with "is returning true" ? At the boundary, either it throws or it returns. These things are mutually exclusive. – Marc Gravell Nov 12 '13 at 09:31
  • @MarcGravell - to clarify, the method seems to be returning true with no problems. However, its parent class "PrincipalContext" seems to have a number of errors being thrown, as shown in the screenshot of the Visual Studio Debugger. – Dot NET Nov 12 '13 at 09:33
  • @JeppeStigNielsen - the exception I'm showing belongs to the PrincipalContext class – Dot NET Nov 12 '13 at 09:36
  • I have edited the question to provide more details – Dot NET Nov 12 '13 at 09:43
  • Now, if the code `bool x = principalContext.ValidateCredentials(null, null);` is exactly as written, and if `x` becomes true after that (and it is not some other variable called `x`), then the method did return a value. Whether or not there were some exceptions that were handled in the process of determining the return value, is just an internal implementation detail that you must not focus too much on. The method returns true, therefore the credentials are valid. – Jeppe Stig Nielsen Nov 12 '13 at 09:43
  • The problem @JeppeStigNielsen is that the credentials cannot possibly be valid. I invented a username and password, and tried with random gibberish values, and it always returns true somehow. – Dot NET Nov 12 '13 at 09:51
  • I can confirm that the method can return true for unknown credentials. The following complete program writes true: `static void Main() { using (var c = new PrincipalContext( contextType: ContextType.Domain, name: null, userName: "weifuh", password: "iuheffwe")) { bool x = c.ValidateCredentials(null, null); Console.WriteLine(x); }` See the related thread [ValidateCredentials returns true for unknown user?](http://stackoverflow.com/questions/7336193/). But forget about the exception stuff. – Jeppe Stig Nielsen Nov 12 '13 at 11:15

1 Answers1

0

You simply need to stop looking up null values...

if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(username)) return false;

I ran some tests

    using (var pc = new PrincipalContext(ContextType.Domain, "mydomain.lan")){

    var isOk1 = pc.ValidateCredentials(null,null); //Always true
    var isOk2 = pc.ValidateCredentials("notexists","wrong"); //false
    var isOk2 = pc.ValidateCredentials("existing","correct"); //true
    }

and

    using (var pc = new PrincipalContext(ContextType.Domain, "mydomain.lan", "notright","wrong")){
        var isOk1 = pc.ValidateCredentials(null,null); //Always true
        var isOk2 = pc.ValidateCredentials("notexists","wrong"); //false
        var isOk2 = pc.ValidateCredentials("existing","correct"); //true
}

So the ValidateCredentials does not really need a user in the context... If you provide a false one a following lookup for say, users groups, will fail however

Yes, documentation reads:

The ValidateCredentials method binds to the server specified in the constructor. If the username and password parameters are null, the credentials specified in the constructor are validated. If no credential were specified in the constructor, and the username and password parameters are null, this method validates the default credentials for the current principal.
(http://msdn.microsoft.com/en-us/library/bb154889%28v=vs.100%29.aspx)

But I can't verify, that the creds in the constructor is in play

EDIT: You have already accepted, but maybe you can use this method for your problem?

 using (var pc = new PrincipalContext(ContextType.Domain, "domain.lan", username, password))
            {
                if (pc.ValidateCredentials(username, password))
                {
                    try
                    {
                        using (var searcher = new PrincipalSearcher(new UserPrincipal(pc)))
                        {
                            searcher.QueryFilter.SamAccountName = username;
                            Principal u = searcher.FindOne();
                        }
                    }
                    catch (Exception)
                    {
                        return "no rights to work on ad";
                    }
                }
                else
                {
                    return "user cannot login";
                }
            }
Steen
  • 2,749
  • 2
  • 20
  • 36
  • So instead of passing the username and password in the constructor, should I pass them in the ValidateCredentials method? If so, would it return false if the account has insufficient permissions, but correct details? As essentially, I am trying to limit accounts with low permissions from accessing the AD. – Dot NET Nov 12 '13 at 11:01
  • That simply checks the username/password combo. You want to check for rights towards what? Searching/Working with the AD? – Steen Nov 12 '13 at 11:05
  • @DotNET See the thread I just linked in a comment to the question. Can it be that there is a Guest account configured at the domain? Or something like that. – Jeppe Stig Nielsen Nov 12 '13 at 11:25
  • My requirements are that the application must first authenticate a user, and see that he has sufficient rights to access the Active Directory, as it can be run on a close network with high security.Infact that is why I tried to pass the credentials through the constructor first, because if the user account cannot access the Active Directory, then I assumed that it would not be able to connect at that stage. – Dot NET Nov 12 '13 at 11:28
  • @Steen - thanks for the suggestion. Could you kindly elaborate on it? From what I can understand, the code is attempting to run a query with the user who is logged in, and if he does not have sufficient rights, it should throw an exception? – Dot NET Nov 15 '13 at 09:44
  • Exactly. If he has the right to query he can find himself, as we know this user exists (validateCredentials says so) - thats why I make him query himself. He could query for anything else, but this way I know it will not fail for looking up nonexisting user. But yes, if the user is defined in the constructor of the principalContext, and it reaches the exception then we know: User exists, but user cannot search AD. – Steen Nov 15 '13 at 11:57