I am trying to get information about the currently logged in user via a Windows service, specifically is the user is an admin level role or not. The service is properly collecting the currently logged in user, and I have code that works when the computer the service is running on is connected to the network, but I can't always guarantee the computer will be on the network when the service does this user level check.
Long story short, does WindowsIdentity require the computer be connected to the network to create an identity? I can't find anything online that clearly states if this is the case.
I only need to get whether or not the current user is an admin level role or not for the local system. I don't care if it's an admin at the domain level, although I understand this is probably going to be the case if the system is on a network.
If there is a better way to do this for my needs, I am happy to see other recommendations.
I have done some major googling and looked at a number of other SO answers, but none of them seem to work when the computer is not on the network. They did help me get a version that works when the computer is on the network though.
I also looked at the MS documentation for this and don't see anything clear about network needs. I may have missed something thought.
The below method works while on network.
private bool IsUserAdministrator(string accountName)
{
var result = false;
try
{
using (var identity = new WindowsIdentity(accountName))
{
var principal = new WindowsPrincipal(identity);
var adminSid = new SecurityIdentifier("BA");
result = principal.UserClaims.Any(c => c.Value.Equals(adminSid.ToString()));
}
}
catch (Exception)
{
// Exception handled by returning false
}
return result;
}
This method also works, when on network. The failure point while debugging is the new WindowsIdentity().
private bool IsUserAdministrator(string accountName)
{
using (WindowsIdentity identity = new WindowsIdentity(accountName))
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
}
Once again, when connected to the network, both of these either return 'true' when the user is an admin role, or 'false' when the user is not admin role.
When not connected to the network this fails and throws the exception {"There are currently no logon servers available to service the logon request.\r\n"}.
Is this an issue with WindowsIdentity or is something else causing this? Is this an LDAP issue?
Sorry, I realize there are multiple questions spread throughout this, but the number one issue is, does WindowsIdentity need network connection, and if yes, is there another way to achieve my goals?
Im trying to get the following code to work, problem is, sometimes it does, sometimes it doesnt.
when it fails it gives the error 0x800704F1 "the system cannot contact a domain controller to service the authentication request"
I'd say about 90% of the time it fails.
I have tried giving it a static DC by adding it behind the contexttype this sadly did not help.
On an admin user it works always.. however i do believe users are supposed to be able to change their own password.
The error is triggered on the user.changepassword line
I hope someone else has a bright idea.
using (var context = new PrincipalContext(ContextType.Domain))
{
using (var user = UserPrincipal.Current)
{
try
{
user.ChangePassword(txt_old.Text, txt_new.Text);
user.Save();
}
catch(Exception p)
{
if (p.HResult.Equals("0x800708C5"))//Not secure enough according to password policy
{
MessageBox.Show("Volgens het systeem is uw nieuwe wachtwoord niet veilig genoeg, voldoet het aan alle eisen?", "Niet gelukt", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
else if (p.HResult.Equals("0x80070056")) //Wrong current password
{
MessageBox.Show("U heeft een verkeerd huidig wachtwoord ingevult, probeer het nogmaals", "Verkeerd wachtwoord", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
else if (p.InnerException.ToString().Contains("0x80070775")) //Temporarly locked out.
{
MessageBox.Show("Uw account is tijdelijk vergrendeld door te veel pogingen tot in te loggen met een foutief wachtwoord. Probeer het over 15minuten nogmaals of neem contact op met de helpdesk.", "vergrendeld.", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
else
{
MessageBox.Show(System.Security.Principal.WindowsIdentity.GetCurrent().Name + Environment.NewLine + p.HResult + Environment.NewLine + p.Message);
return;
}
}
}
}
The two Windows updates 3177108 and 3167679 have changed the behavior of ChangePassword.
There is a thread here about the issue: https://social.msdn.microsoft.com/Forums/vstudio/en-US/77dc733e-a13d-4349-9088-8065b85d5c3f/userprincipalchangepassword-stops-working-after-windows-updates-3177108-and-3167679?forum=netfxbcl
It seems, that you now have to specify a valid UPN when creating the PrincipalContext.
Before you could use a IP as endpoint when creating the context, now it seems it has to be a correct domain name aswell.
Furthermore, you now always receive the same exception when an error occurs - we used to receive the password policy exception for users choosing insufficient passwords, now we get:
System.DirectoryServices.AccountManagement.PrincipalOperationException:
The system cannot contact a domain controller to service the
authentication request. Please try again later. (Exception from
HRESULT: 0x800704F1)
UPDATE 04-10-2016:
The exception displayed above is really the general/generic error received for just about anything when calling ChangePassword after the updates.
If for instance some of the ports involved in the protocol is blocked by a firewall, you get this one as well (applicable if you call from a server/machine that is not domain joined).
Good resource for required ports: https://technet.microsoft.com/en-us/library/dd772723(v=ws.10).aspx
Note that the dynamic range is required as well.
If the user is not allowed to change password (domain policy, circumvent by setting MUST CHANGE AT NEXT LOGON FLAG) you also receive this exception.
Your problem may be that a password policy violation has occurred. That is, for example, if you have a password policy in place where users can't change their passwords to be one of their last 5, as an example, if they try to change to one of their last 5 you'll see this error thrown in my experience.
The error just before the exception you report (in my case) looks like this:
TargetInvocationException: COM error attempting to change an Active Directory password..
So i'd check your password policies and make sure that your users in these cases aren't violating it.
UPDATE: 10/12/2016:
Microsoft has updated this article: https://support.microsoft.com/en-us/kb/3177108 . Here they have given us problems created by the original "fixes" as well as some tips for working with Kerberos and self-service password reset.
As of October 11, 2016 Microsoft re-released the patches associated with https://technet.microsoft.com/en-us/library/security/ms16-101.aspx to resolve issues caused by the original updates (which you can read in https://support.microsoft.com/en-us/kb/3177108 including the fact that you could no longer change passwords on local accounts).
I believe I have the answer. Microsoft recently patched Windows so that NTLM can't be used to change passwords anymore.
Solution #1 (the "hammer"):
Try removing either one or both of these KB updates on your server running the code.
https://support.microsoft.com/en-us/kb/3177108
https://support.microsoft.com/en-us/kb/3167679
When you say you can change the password as an admin, do you mean only when your forms application is running on an admin's machine? Is the challenge when the application is running on a non-admin's machine?
I was able to take your code and get it work as is as well as changing the following:
using (var user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "test.user0001"))
and
using (var user = UserPrincipal.FindByIdentity(context, IdentityType.UserPrincipalName, "test.user0001#webactivedirectory.com"))
Using either one of these lines as well as yours (getting the current user) I was able to change admin and non-admin passwords alike. My question is whether your error happens when you have the forms app running on a non-admins machine.
I witnessed this from the sysadmin side on two different occasions: two applications with password management features had to be installed on two freshly deployed servers, which had of course been fully patched; in both cases, changing passwords failed with an error about the application being unable to contact a domain controller (which, of course, was actually there and available).
One application was a closed-source vendor-supplied one (CyberArk's Privileged Session Manager), while the other one was an in-house application developed by the customer where I'm currently working; in the second case, I was able to take a look at the code, which was indeed similar to that used in the original question.
Sadly, actually fixing the code was not an option in both cases: for the first application we had to report a bug to the application vendor, while for the second one we had to report a bug to the internal development team working on the application, be we couldn't get an immediate fix in either case. Management wanted both applications working now, thus we had to remove the offending updates (I know, this is bad and not a solution but a lazy and dangerous workaround, and I tried all I could to avoid that; but management is management, so... meh).
Anyway, the reason I'm jumping in with this answer: apart from removing KB3177108 and KB3167679, in my case (both cases) we also had to remove KB3175024 and KB3174644; while those two updates were installed, the password change function still refused to work, even after removing the first two ones.
So, if you find yourself in a situation where you can't fix the code, and removing KB3177108 and KB3167679 doesn't solve the issue, then you can try removing also KB3175024 and KB3174644.
I'm trying to do something that I don't even know if it is possible.
I have a web application based on C# that runs on a specific server. I want to build a code where the user introduces the domain where the app runs (this server depends on the client, for each client it runs on different servers obviously) and the app returns the local windows user accounts of that domain and information saying if the users are locked out or not.
I've tried to use Win32_UserAccount but it seems to get the users of the network I'm currently using.
Is this possible to do?
Thank you so much
Regards,
Flávio Justino
Try
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain"))
{
using(UserPrincipal usr = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "username"))
{
usr.IsAccountLockedOut(); //Gets if account is locked out
}
}
You need to add dpendency System.DirectoryServices.AccountManagement.dll for the above code to work.
We are working with another company whom has a Oracle Identity Management Server setup. We are to connect to this and authenticate users based on LDAP data retried from the server.
We have tried to plug into this by using an LdapConnection object passing in the server name and port along with the Network credentials they are providing to us so we are using a AuthType of Basic. However on the Bind() we are always failing because it says that we have invalid credentials. We have worked with the client to make sure they are correct and we have been able to log in to Oracle Identity Management with the credentials, although the user name we had to use data from the SearchRequests distinguished name. But even using that we keep on receiving the same error. Client is also using the credentials to connect via Java.
This is an issue where as I think there is no solution really, but does anyone out there have any idea on how to go about doing this? We have the same code running which is working and pulling from Active Directory. So our code should be fine as long as Oracle supports connecting in this fashion. But finding anything in regards to this topic is like pulling teeth.
Anyone have any experience with this out there? Please let me know I would be happy to provide any additional details if needed.
Thanks in advance!
I recently fumbled through connecting to OID using LDAP. Here's the code that ended up working for me:
// make sure the server and port are correct
using (var ldap = new LdapConnection("ldap.company.com:3060"))
{
// make sure to pass the username as a distinguishedName
var dn = string.Format("cn={0},cn=users,dc=company,dc=com", username);
// passing null for the domain worked for me
var credentials = new System.Net.NetworkCredential(dn, password, null);
ldap.AuthType = AuthType.Basic;
try
{
ldap.Bind(credentials);
return true;
}
catch (LdapException ex)
{
return false;
}
}
I have the following setup.
A Windows 2008 server that is NOT part of a domain but has many users configured locally on the machine (we are not using active directory). I have an app running on another machine (currently Windows XP) on the network (also not part of a domain) that must validate whether a user has valid credentials on our 2008 server.
I've tried a bunch of different combinations but cannot get things to work and have run out of things to try. I'm currently running the following code and can't figure out why it only works for the Administrator user.
PrincipalContext pc = new PrincipalContext(ContextType.Machine, "machineName");
//returns true
bool test1 = pc.ValidateCredentials("Administrator", "aValidPassword");
//returns false
bool test2 = pc.ValidateCredentials("Administrator", "anInvalidPassword");
//throws an exceptoin for any valid username/password combination I send
bool test3 = pc.ValidateCredentials("anotherUser", "aValidPassword");
The above code works correctly when trying to validate the Administrator but if I try and validate ANY other user on that machine I get the following exception.
System.UnauthorizedAccessException: General access denied error
at System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADs.Get(String bstrName)
at System.DirectoryServices.AccountManagement.CredentialValidator.BindSam(String target, String userName, String password)
at System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password)
at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password)
I should also mention that "anotherUser" is also an administrator on the 2008 server. Why is it that only the Administrator user can be validated? I've tried a few different non-Administrator accounts and none of them work. I know for sure the username/password that I am using is valid and that the users are not disabled or anything like that. I'm not having any luck googling for a solution, possibly because this should be so simple, I must be missing something blatantly obvious... Any help would be greatly appreciated.
(Edited to add the following paragraph)
I still haven't figured this problem out but thought I'd follow up in hopes someone would help, I'm at a loss. It's very strange what is happening. I'm looking at the Event Viewer->Windows Logs->Security to see if it would help point to the problem. The odd thing is that in the those logs everything is reported as a success and there are no problems indicated. Despite that, I'm still getting the UnauthorizedAccessException with message "General access denied error" on my C# client. I also noticed that the source of the exception is "Active Directory" despite this fact that we are not using active directory (the server isn't even on a domain, it's only on a workgroup). Does anyone know how I could possibly getting success logs in the Event Viewer but yet I'm still getting exceptions within the client?
(Edited to add the following paragraph)
Intresting, if I call ValidateCredentials with an invalid password for my "anotherUser" account it doesn't throw the exception and correctly returns false. It continues to throw the exception when the password is correct though. Does anyone know Why would this be? This makes no sense at all and I really don't want to have to depend on this strange behaviour, I want to know why the successful call is throwing an exception and fix the root cause...
Thanks!
(edited to correct error where I said the third call returns false when it actually throws an exception)