General exception handling issue - c#

I'm working with the Active Directory APIs, and am attempting to connect to the server using the following code:
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, (server + ":" + port), loginUsername, loginPassword);
Whenever an invalid login username or password are passed, an exception from the whole statement is not thrown, but the following code continues executing. On debugging, I have found that the PrincipalContext class however throws an error, as shown below:
Those are two properties contained within the class. On further examining the "ConnectedServer" property, the following is displayed in the debugger:
My problem is that since an error is not thrown externally, I am unsure of how to actually check for this error. I would like to show a simple error message if the username or password are invalid - basically finding a way to check if the above errors have been thrown.
How can this be done?

The classes of System.DirectoryServices.AccountManagement are differed execution. It does not attempt to connect to the Active Directory server till it has to. The ValidateCredentials method is the way to force a check, from the MSDN:
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.
So all you need to do is
using(PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, (server + ":" + port), loginUsername, loginPassword))
{
//This will force the connection to the server and validate that the credentials are good
//If the connection is good but the credentals are bad it will return "false", if the connection is bad it will throw a exception of some form.
if(principalContext.ValidateCredentials(null, null))
{
// Rest of code here.
//This is how you do the same check you where doing in your previous quesiton, notice that this is "userName", and "password" not "loginUsername" and "loginPassword"
valid = principalContext.ValidateCredentials(userName,password);
}
}

The best way to handle any exception from the principal context is by placing your code in try and then catch the exceptions as shown below.
string user = txtUsername.Text;
string pass = txtPassword.Text;
//start a try and catch method
try
{
//create a principalcontext object
var pc = new PrincipalContext(ContextType.Domain, "*****", user, pass);
{
//validate the user credentials
if (pc.ValidateCredentials(user, pass))
{
//create a user identity
UserPrincipal userp = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, user);
//check if the user is returned
if (userp != null)
{
//if user exists, return an array of authorized groups
var grps = userp.GetAuthorizationGroups();
//convert the array to a list to enable search of CIS group
List<string> strList = grps.Select(o => o == null ? String.Empty : o.ToString()).ToList();
//check for CIS group from the list
if (strList.Contains("CISS"))
{
//create a session variable to show the loggedin user and set the error panel to false
Session["username"] = user;
ErrorPanel.Visible = false;
//redirect the user to the homepage
Response.Redirect("appdesk/account.aspx");
}
else if (!strList.Contains("CISS"))
{
Label1.Text = "You Don't have the Rights to login to the platfrom";
ErrorPanel.Visible = true;
}
}
//if the user credentials are invalid
if (!pc.ValidateCredentials(user, pass))
{
Label1.Text = "Login Failed.Incorrect Username or Password";
ErrorPanel.Visible = true;
}
}
}
//catch the exceptions in the try
catch (Exception exc)
{
Label1.Text = exc.Message.ToString();
ErrorPanel.Visible = true;
}

A basic catch doesn't work? Something like:
private ADConnectResults Connect(string server, int port)
try
{
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, (server + ":" + port), loginUsername, loginPassword);
return new ADConnectResults(true, principalContext);
}
catch(DirectoryServicesCOMException dex)
{
Log(dex);
return new ADConnectResults(false);
}
}

I found that attempting to assign the PrincipalContext.ConnectedServer property to a variable allowed the exception to surface:
using(var _ctx = new PrincipalContext(ContextType.Domain, server + ":" + port))
{
try
{
var connectedServer = _ctx.ConnectedServer;
}
catch (Exception)
{
//do something with the caught exception
}
}

Related

How to validate domain credentials without considering the Cached Domain Credential

I want to know if there's a way to validate domain credential and make sure we don't use the Cached Domain Credential ?
I use this to validate the credential :
bool valid = false;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
valid = context.ValidateCredentials( username, password );
}
The problem is when I change the password, the old password is still working.
EDIT : If you force the password to be reset, the cached domain credential will not be use. But between the moment we force the reset, and moment the user reset the password, the old password will still work.
Question already has an answer Why does Active Directory validate last password?
Solution is to use a Kerberos authentication.
The following code shows how you can perform credential validation using only Kerberos. The authentication method at use will not fall back to NTLM in the event of failure.
private const int ERROR_LOGON_FAILURE = 0x31;
private bool ValidateCredentials(string username, string password, string domain)
{
NetworkCredential credentials
= new NetworkCredential(username, password, domain);
LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);
using(LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
{
connection.SessionOptions.Sealing = true;
connection.SessionOptions.Signing = true;
try
{
connection.Bind();
}
catch (LdapException lEx)
{
if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
{
return false;
}
throw;
}
return true;
}
you might try something like this
try
{
using (var directoryEntry = new DirectoryEntry(ldapPath, userName, password))
{
var invocation = directoryEntry.NativeObject;
return true;
}
}
catch (Exception ex)
{
return false;
}

Method returning 'true' when it should be returning 'false'

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:
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.
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";
}
}

Trying to connect using PrincipalContext on Workgroup Server returns Invalid User.

I want to create an Application for editing user accounts on a Server.
The Server do not use AD only local accounts.
I use the following code to connect the remote server:
try
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Machine, "192.168.123.110", null, ContextOptions.Negotiate, "Administrator", "password");
try
{
MessageBox.Show(oPrincipalContext.ConnectedServer);
GroupPrincipal oGroupPrincipal = GroupPrincipal.FindByIdentity(oPrincipalContext, "Goetter");
try
{
// perform operations here
}
finally
{
oGroupPrincipal.Dispose();
}
}
finally
{
oPrincipalContext.Dispose();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Whenever I try this, I get an exception, that the user and or password is not authorized, independent of the user I use. Administrator is the build in Admin user account.
Does PrincipalContext only works with AD or also with local accounts? Is anything wrong with my code?
using(PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Machine, computer.Name, null, ContextOptions.Negotiate, Settings.UserName, Settings.UserPassword))
using(GroupPrincipal oGroupPrincipal = GroupPrincipal.FindByIdentity(oPrincipalContext, Settings.AdministratorsGroup))
{
// perform operations here
}
Change your code and wrap it around a using statement otherwise you may have some errors when trying to call the Dispose() method reason being when you try to dispose the connection may have already been closed by then.
you can use this code here and try either of the examples if you are using ActiveDirectory
example 1
If you work on .NET 3.5, you can use the System.DirectoryServices.AccountManagement namespace and easily verify your credentials:
// create a "principal context" - e.g. your domain (could be machine, too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "mypassword");
}
Example 2
using System.Security;
using System.DirectoryServices.AccountManagement;
public struct Credentials
{
public string Username;
public string Password;
}
public class Domain_Authentication
{
public Credentials Credentials;
public string Domain;
public Domain_Authentication(string Username, string Password, string SDomain)
{
Credentials.Username = Username;
Credentials.Password = Password;
Domain = SDomain;
}
public bool IsValid()
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain))
{
// validate the credentials
return pc.ValidateCredentials(Credentials.Username, Credentials.Password);
}
}
}
The public bool IsValid() Method above should work for what you are looking for.
Have a look at PrincipalContext.ValidateCredentials
for your FindByIdentity portion you can try the following replacement code
string strName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
// This is here because of a .Net error that gets 0x80005000 on "isUser = user.IsMemberOf(groupU);"
string domainName = strName.Split('\\')[0];
var pc = new PrincipalContext(ContextType.Domain, domainName);
Additional Reference Link StackOverFlow Post
ContextType.Machine

Validate a users credentials on the local machine

I have a Windows Service (running as the Local System user) that needs to validate a user based on username and password, in addition to checking if the user belongs to the group WSMA. My current code is like this:
var pc = new PrincipalContext(ContextType.Machine);
using (pc)
{
try
{
if (pc.ValidateCredentials(username, password))
{
using (var groupEntry = new DirectoryEntry("WinNT://./WSMA,group"))
{
foreach (object member in (IEnumerable)groupEntry.Invoke("Members"))
{
using (var memberEntry = new DirectoryEntry(member))
{
if (memberEntry.Path.ToLower().EndsWith(username.ToLower()))
{
return new LoginResult{ success = true };
}
}
}
}
}
return new LoginResult{ success = false };
}
catch (PrincipalOperationException poe)
{
if (poe.ErrorCode == -2147023688)
{
return new LoginResult { Success = false, ErrorMessage = "Password expired" };
}
throw poe;
}
}
This all works as it should, as long as I'm connected to the network, but if I plug out my network cable, then the ValidateCredentials call give me the following error message:
FileNotFoundException unhandeled by user code. The network path was not found.
I guess this has something to do with AD, but I only need to check the local users, and not domain users so a network access should not be required.
Any way to do this using the PrincipalContext, or some other way that will work in a disconnected scenario?
Here's a way to logon the User (and thus check that it's a valid user/pass): MSDN Link
I guess this should work disconnected, too, if you use a local account

Verify if password is correct

i need to verify if the password is correct for a user.
i have this code:
private bool checkOldPasswordValid(string password, string username)
{
using (DirectoryEntry entry = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer"))
{
entry.Username = username;
entry.Password = password;
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
try
{
searcher.FindOne();
}
catch (Exception ex)
{
return false;
}
return true;
}
}
but then directory searcher is not supported with WinNt, so i found another way to loop through all records.
foreach (DirectoryEntry dc in entry.Children)
{
// prints the name
System.Diagnostics.Debug.WriteLine(dc.Name);
}
but this just gets the name and doesnt verify the password.
please help . thanks
To autenticate against LDAP or WinNT, you need no DirectorySearcher. You only need to get the NativeObject from your DirectoryEntry instance. Here's a code sample that might guide you through the way.
public bool Authenticate(string username, string password, string domain) {
bool authenticated = false;
using (DirectoryEntry entry = new DirectoryEntry(#"WinNT://" + domain, username, password) {
try {
object nativeObject = entry.NativeObject;
authenticated = true;
} catch (DirectoryServicesCOMException ex) {
}
}
return authenticated;
}
This code will return either a user is authentic or not. Once you can get the NativeObject property using this DirectoryEntry class instance, this means that the AD (or local computer) used impersonation to get this object. If you get the object without having a thrown exception, this means that the AD (or local computer) was able to authenticate the impersonnated user.
While you can use the currently authenticated user by specifying no username and password, but only the domain (or local computer), by specifying a username and password, you say you want to use impersonnation, so the security infrastructure will use the given username and password to try to retrieve the NativeObject property from this DirectoryEntry class instance.
To authenticate against the AD, just replace the "WinNT://" for "LDAP://".
You can use DirectoryEntry itself.
See the example here: http://support.microsoft.com/kb/316748
Why are you using WinNT:// anyways?

Categories

Resources