public static IPrincipal AuthenticateUser(string domain, string userName, string password, out string errorMessage)
{
WindowsPrincipal principal = null;
int responseCode;
errorMessage = null;
if (UserInfo.IsLocalDomain(domain))
{
// Set the domain as the local machine if one is not specified
if (string.IsNullOrEmpty(domain))
domain = Environment.MachineName;
responseCode = AuthenticateUser(userName, password);
if (responseCode == 0)
principal = new WindowsPrincipal(new UnixIdentity(domain, userName));
else
errorMessage = string.Format("Failed to authenticate \"{0}\": {1}", userName, GetPAMErrorMessage(responseCode));
}
else
{
// Attempt PAM based authentication first - if configured, this will be the best option
string domainUserName = string.Format("{0}\\{1}", domain, userName);
responseCode = AuthenticateUser(domainUserName, password);
if (responseCode == 0)
principal = new WindowsPrincipal(new UnixIdentity(domain, userName));
// Try really hard to find a configured LDAP host
string ldapHost = GetLdapHost();
// If LDAP host cannot be determined, no LdapConnection can be established - if authentication
// succeeded, user will be treated as a local user
if ((object)ldapHost == null)
{
if ((object)principal == null)
errorMessage = string.Format("Failed to authenticate \"{0}\": {1}", domainUserName, GetPAMErrorMessage(responseCode));
else
errorMessage = string.Format("User authentication succeeded, but no LDAP path could be derived.");
}
else
{
try
{
// Attempt LDAP account authentication
LdapConnection connection = new LdapConnection();
if (ldapHost.StartsWith("LDAP", StringComparison.OrdinalIgnoreCase))
{
Uri ldapURI = new Uri(ldapHost);
ldapHost = ldapURI.Host + (ldapURI.Port == 0 ? "" : ":" + ldapURI.Port);
}
// If host LDAP path contains suffixed port number (e.g., host:port), this will be preferred over specified 389 default
connection.Connect(ldapHost, 389);
connection.Bind(string.Format("{0}@{1}", userName, domain), password);
if ((object)principal == null)
principal = new WindowsPrincipal(new UnixIdentity(domain, userName, connection));
else
((UnixIdentity)principal.Identity).Connection = connection;
}
catch (Exception ex)
{
if (responseCode == 0)
errorMessage = string.Format("User authentication succeeded, but LDAP connection failed. LDAP response: {0}", ex.Message);
else
errorMessage = string.Format("LDAP response: {0}{1}PAM response: {2}", ex.Message, Environment.NewLine, GetPAMErrorMessage(responseCode));
}
}
}
// Set current thread principal to authenticated user principal - this will allow access to
// needed LdapConnection information on the current thread...
Thread.CurrentPrincipal = principal;
return principal;
}