Specifying incorrect container does not affect user search - c#

I am using user the PrincipalContext class to connect to an Active Directory server and then use the ValidateCredentials method like this:
new PrincipalContext(ContextType.Domain, <some url>, <some container>);
principalContext.ValidateCredentials(userName, password, ContextOptions.Negotiate);
The some container parameter is of type Container and according to the documentation "All queries are performed under this root". Also according to this answer specifying the Container "... limits all queries using that DomainContext to the specified container."
The problem though is that against my companie's AD server if I specify a container that does not exist or just put in gibberish the AD server still finds a user if I specify a user that exists with the correct password.
Does anybody know why the user is still found? Is there some documentation you can point me to that explains this?

The simple answer is that ValidateCredentials doesn't use the specified container, simply because it doesn't need to. It doesn't actually search for the user. It just attempts to authenticate to the server with the credentials specified.
You can see the source code for ValidateCredentials here, which ends up calling CredentialValidator.Validate (an internal class).
In the constructor of PrincipalContext, it creates the CredentialValidator object, but you'll notice that it does not pass the container to it, only the name (the domain name).
_credValidate = new CredentialValidator(contextType, name, _serverProperties);
The _serverProperties variable is also built from only the server name, not the container, which you can see from the ReadServerConfig method.

Related

c# - Validating expired domain credentials across the forest

There are already a few questions which ask how to validate Active Directory domain questions. However, I do not believe they deal adequately with multi-domain scenarios across the forest. For starters, they both suggest that the most definitive way to perform AD authentication in C# with .NET 3.5+ should look something like this:
bool isValid = false;
using(var ctx = new PrincipalContext(ContextType.Domain, "foo.com"))
{
// verify user exists first
var lookedUp = UserPrincipal.FindByIdentity(ctx, "myuser");
if (lookedUp != null)
{
// validate credentials
isValid = pc.ValidateCredentials("myuser", "mypassword");
}
}
This is all well and good when the user you want to authenticate belongs to foo.com. However, there are some subtle unexpected differences in the case of a user belonging to a child domain, whose password is also expired. (I have successfully got the PrincipalContext to find the user in the child domain two different ways: (a) setting the domain argument to "foo.com:3268"; (b) adding a container argument w/ the base DN of the child domain: new PrincipalContext(ContextType.Domain, "foo.com", "dc=child,dc=foo,dc=com"). The problem I describe below occurs in both scenarios.)
If the user belongs to foo.com, and has an expired password, ValidateCredentials returns true (at least on Windows 7 where I'm testing; I've seen others say the behavior is different in Windows 2003). However, if the user belongs to child.foo.com and the password is expired, then ValidateCredentials returns false.
It is pretty important for me to be able to distinguish between "valid password but expired" and "invalid password". If the entered password is correct but expired, then I want to be able to redirect them to a "change password" screen. However, if the password they entered was totally wrong, then it could be considered leakage of information to forward them to a change password screen.
The only way forward I see is to use the LDAP interface to AD and try to parse the status messages it sends back to figure out the underlying error code. This sounds neither fun nor wise.
Can anyone provide a reason for this behavior when dealing with subdomains within the same forest? Can anyone provide a solution to what I am trying to accomplish here?
So the issue here it appears is that .NET ultimately tries to do what's called a fast concurrent LDAP bind to AD. That's a super lightweight mechanism and Google seems to indicate that perhaps it bypasses the expiry check. I didn't validate this, but, assuming it's true...
I think your options here are to either a) do the binding yourself (look at the LDAPConnection class and the associated flags) or b) P/Invoke LogonUser. You may need to dig in to figure out the passwory expiry status if the call fails as I'm not sure if either of those will tell you that it's expired or isn't as the reason for the failure.

Retrieving the local group name by which a particular user belongs to

I want to retrieve the local group name by which a particular user belongs to. IE, suppose if we know the user and don't know his/her group name in such case i want to retrieve the group name.
The same classes/libraries that can be used to retrieve such information for within an Active Directory domain can be used to retrieve this information from the local machine.
You can use the PrincipalContext class, and related classes to retrieve information about users, groups and much more. The constructor of the PrincipalContext class allows you to specify a context to search in.
By initializing the PrincipalContext class like this:
PrincipalContext ctx = new PrincipalContext(ContextType.Machine,Environment.MachineName);
You will operate within your local machine's context. Using the related classes and functions that the PrincipalContext class exposes, you can accomplish what you want.
Here is a reference on how to get a list of groups a user is a member of:
How to get the groups of a user in Active Directory? (c#, asp.net)
Even though the link above explains how to do this for an active directory. It still uses the PrincipalContext class. It's just a way of modifying how you initialize the instance.

Force PrincipalContext to connect to a specific server

Is there a way to force PrincipalContext to connect to a specific Domain Controller? I'm enumerating the list of locked accounts for my application, and I would like to be able to query multiple servers and return the list from all DCs.
Currently I get whichever DC my PrincipalContext happens to connect to, and my list is not always completely correct of accounts that are locked out.
I've done some digging, but don't see any way to make reference to which Domain Controller the call for PrincipalContext connects to.
Yes, you can connect to a specific domain controller.
new PrincipalContext(ContextType.Domain, name, container, username, password);
The name part of this principal context can be set to an IP address of a domain controller. I assume that you speak about different active directories otherwise you may have a problem how the domain controllers are distributing the information.
Also, make sure the container is the correct with OC=... and DC=....
Hope it helps!

Calling CreateService when explicitly specifying the local domain in lpServiceStartName errors out

I'm creating a service on a machine in C# via a win32 API call to CreateService. The MSDN page for that function says about the lpServiceStartName param:
The name of the account under which the service should run. If the
service type is SERVICE_WIN32_OWN_PROCESS, use an account name in the
form DomainName\UserName. The service process will be logged on as
this user. If the account belongs to the built-in domain, you can
specify .\UserName.
On the remote machine, the user myuser belongs to the default local domain WORKGROUP. If I pass ".\myuser", everything works fine. If I pass "WORKGROUP\myuser", I get ERROR_INVALID_SERVICE_ACCOUNT.
I'm curious about this behavior, as the above docs seem to say I can specify .\myuser, not that I must. Can anyone shed any light on this?
First of all, I'm not an expert in windows account management, and I may get a few downvotes here, but here is what I noticed when I was testing windows services log on identities.
As documentation states, you can specify ".\UserName" if the account belongs to the built-in domain. Built-in domain includes groups and users created when OS is installed (link).
So instead of ".\UserName", you can specify "BUILTIN\UserName" since BUILTIN is the name of the built-in domain.
In practice, it worked for "BUILTIN\Administrators" and "BUILTIN\Guests", but it didn't work for user "BUILTIN\Guest".
If you try to create a new local group and local user and set it as a log on identity, you will still see it in a form ".\UserName". This may be inaccuracy in documentation, but in any case, for local user accounts that are not built-in accounts you can replace the dot with machine name: "MACHINE\UserName".

MVC Active Directory Membership

I am trying to make use of the active directory membership rather than SQL but there is very limited documentation available online. I have managed to connect my application to the domain controller without any problems but when you use "Context.User.Identity.Name" it comes up with DOMAIN\User. I want to basically drill down and get information such as full name, e-mail address, etc.
I just need a useful link and the searching I have done doesn't appear to have got me anywhere!
Many thanks
This should give you a bit of a clue: http://msdn.microsoft.com/en-us/library/ms973834.aspx
and here is a list of LDAP properties that you might want to play around with in the search result: http://www.computerperformance.co.uk/Logon/LDAP_attributes_active_directory.htm
Have you tried with this doc?
http://msdn.microsoft.com/en-US/library/system.web.security.activedirectorymembershipprovider%28v=vs.90%29.aspx
Can help?
If you are making use of Active Directory then you are likely using Windows Authentication. If so, all you need to do is:
Reference System.DirectoryServices.AccountManagement
In code (perhaps a controller action or model constructor)
// establishes your domain as the context for your user lookup
var principalContext = new PrincipalContext(ContextType.Domain, "domainName");
// gets the current user's UserPrincipal object
var userPrincipal.FindByIdentity(principalContext, #User.Identity.Name)
// example
var email = userPrincipal.EmailAddress;
Note:
This works because Windows Authentication means User.Identity on the current HttpContext is a WindowsIdentity and thus its Name property can be used to search AD.
You aren't limited to looking up the current user. You can use FindByIdentity() to search any value passed, and this method exists on other principals (ex. GroupPrincipal). You can also designate you wish to search by another type such as SID instead of Name.
Enjoy!

Categories