问题
First, I cannot use Active Directory, so I cannot use System.DirectoryServices
directly. This will be a PC sending a query to a Novell network where only System.DirectoryServices.Protocol
is supported.
I am pretty sure that I am down to needing to provide the proper SearchRequest.
This is what I have so far:
private static String _certificatePath;
private static String _server;
private static SearchResponse Query(String user, String pwd, out String error)
{
SearchResponse result = null;
error = String.Empty;
if (File.Exists(_certificatePath))
{
var identifier = new LdapDirectoryIdentifier(_server, false, false);
try
{
using (var connection = new LdapConnection(identifier))
{
connection.SessionOptions.ProtocolVersion = 3;
var cert = new X509Certificate();
cert.Import(_certificatePath, null, X509KeyStorageFlags.DefaultKeySet);
connection.ClientCertificates.Add(cert);
connection.AuthType = AuthType.External;
connection.AutoBind = false;
var request = new SearchRequest()
{
DistinguishedName = user, //Find this person
Filter = "(objectClass=*)", //The type of entry we are looking for
Scope = System.DirectoryServices.Protocols.SearchScope.Subtree, //We want all entries below this ou
};
result = (SearchResponse)connection.SendRequest(request); //Run the query and get results
}
} catch (Exception err)
{
error = String.Format("SDSP::Query {0}: {1}", err.GetType(), err.Message);
}
}
else
{
error = "The system cannot find the Cryptography Certificate at the path specified in the Application Configuration file.";
}
return result;
}
How do I create a SearchRequest to validate a user
/ pwd
combination?
var request = new SearchRequest()
{
DistinguishedName = user, //Find this person
Filter = "(objectClass=*)", //The type of entry we are looking for
Scope = System.DirectoryServices.Protocols.SearchScope.Subtree, //We want all entries below this ou
};
回答1:
On Windows
You can append ContextOptions.Negotiate
parameter for ValidateCredentials
(Username and Password).
const int ldapErrorInvalidCredentials = 0x31;
const string server = "sd.example.com:636";
const string domain = "sd.example.com";
try
{
using (var ldapConnection = new LdapConnection(server))
{
var networkCredential = new NetworkCredential(_username, _password, domain);
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.AuthType = AuthType.Negotiate;
ldapConnection.Bind(networkCredential);
}
// If the bind succeeds, the credentials are valid
return true;
}
catch (LdapException ldapException)
{
// Invalid credentials throw an exception with a specific error code
if (ldapException.ErrorCode.Equals(ldapErrorInvalidCredentials))
{
return false;
}
throw;
}
Sources:
- How do I validate Active Directory creds over LDAP + SSL?
- https://msdn.microsoft.com/en-us/library/bb300969(v=vs.110).aspx
- https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.contextoptions(v=vs.110).aspx
On Novell
DirectoryEntry and DirectorySearcher are both high level class tools that are wrappers for Active Directory.
//use the users credentials for the query
DirectoryEntry root = new DirectoryEntry(
"LDAP://dc=domain,dc=com",
loginUser,
loginPassword
);
//query for the username provided
DirectorySearcher searcher = new DirectorySearcher(
root,
"(sAMAccountName=" + loginUser + ")"
);
//a success means the password was right
bool success = false;
try {
searcher.FindOne();
success = true;
}
catch {
success = false;
}
Referred to the answer.
回答2:
Let me show you my very best attempt to achieve this validation, maybe it will works for you.
In my context this doesn't work because my admin user can't read attribute "userPassword" and I can't figure why. I guess is some permission not assigned.
Anyway this is the code, hope it helps:
var server = "<SERVER:PORT>";
var adminUser = "<USERNAME>";
var adminPass = "<PASSWORD>";
using (var ldap = new LdapConnection(server))
{
ldap.SessionOptions.ProtocolVersion = 3;
// To simplify this example I'm not validating certificate. Your code is fine.
ldap.SessionOptions.VerifyServerCertificate += (connection, certificate) => true;
ldap.SessionOptions.SecureSocketLayer = true;
ldap.AuthType = AuthType.Basic;
ldap.Bind(new System.Net.NetworkCredential($"cn={adminUser},o=<ORGANIZATION>", adminPass));
// Now I will search to find user's DN.
// If you know exact DN, then you don't need to search, go to compare request directly.
var search = new SearchRequest
{
//Here goes base DN node to start searching. Node closest to entry improves performance.
// Best base DN is one level above.
DistinguishedName = "<BASEDN>", //i.e.: ou=users,o=google
Filter = "uid=<USERNAME>",
Scope = SearchScope.OneLevel
};
// Adding null to attributes collection, makes attributes list empty in the response.
// This improves performance because we don't need any info of the entry.
search.Attributes.Add(null);
var results = (SearchResponse)ldap.SendRequest(search);
if (results.Entries.Count == 0)
throw new Exception("User not found");
// Because I'm searching "uid" can't exists more than one entry.
var entry = results.Entries[0];
// Here I use DN from entry found.
var compare = new CompareRequest(entry.DistinguishedName, new DirectoryAttribute("userPassword", "<PASSWORD>"));
var response = (CompareResponse)ldap.SendRequest(compare);
if (response.ResultCode != ResultCode.CompareTrue)
throw new Exception("User and/or Password incorrect.");
}
来源:https://stackoverflow.com/questions/48197277/how-would-i-validate-a-username-password-using-system-directoryservices-protocol