c# Change AD password Directoryservices - c#

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.

Related

Active Directory LDAP connection using System.DirectoryServices - The server is not operational

I'm working on an application that uses some kind of single sign on with Active Directory.
On my side, I'm trying to get some groups to see if the user is member of them.
Sometimes, I get the following error:
The server is not operational
The DirectoryEntry object is created like this:
using(DirectoryEntry ldapConnection = new DirectoryEntry(ldapDomain))
{ Path = ldapPath, AuthenticationType = AuthenticationTypes.Secure }
where ldapDomain is x.y.corp and the ldapPath is
LDAP://OU=someAppId,OU=someGroupName,OU=someClusterName,OU=someResourceName,DC=x,DC=y,DC=corp
After some analysis with the AD team we find out that the controller was removed from the x.y.corp domain but for some reason the Domain Controller is still redirecting to it but the server is down. This generates the error message shown above.
My questions are:
is there any possible retry mechanism or error handling on my side? (The exception is indeed catch now but is thrown further)
is there a way to tell the domain controller to not use the server anymore from backend code?
is there a problem with the construction of the DirectoryEntry? Is it possible to request the "DC" parameters one at a time and to redirect outside the domain...?
is there any cache on my side for domain controllers?
Thank you!
This is something your administrators need to fix. It sounds like DNS is still advertising the DC that is down. You can confirm this by typing this in the command line:
nslookup x.y.corp
The DNS lookup will show several IPs in a different order each time you do the lookup. Whichever one shows up first is the one that will be used.
If the IP address of the decommissioned DC still shows up in the list, then they need to fix that. Anything you do in code will just be a hack to get around something that shouldn't be happening in the first place.

Can't store users in the default MVC application

I had some problems with using the authorization before so I got a brand new everything - new computer, new OS, fresh installation of VS, new app and DB in a new resource group on the Azure. The whole shabang.
I can confirm that I can log in to the Azure DB as the screenshots below show.
I can see the databases, tables, users etc.
The problem is that, although it works locally (using the default connection string provided automagically for me), it doesn't perform very well in the Azure (although I'm using the publish file from there). It said something about the file not being found and according to this answer, I needed to change the connection string.
After I've altered it, I get the following error. Please note that the firewall is open and that I can access the DB when I run the code of my applications. I feel that there's something that goes wrong when the authentication part is automatically configured. I'm out of ideas on how to trouble-shoot it, though.
[SqlException (0x80131904): Login failed for user 'Chamster'.
This session has been assigned a tracing ID of '09121235-87f3-4a92-a371-50bc475306ca'. Provide this tracing ID to customer support when you need assistance.]
The connection string I'm using is this.
Server=tcp:f8goq0bvq7.database.windows.net,1433;
Database=Squicker;
User ID=Chamster#f8goq0bvq7;
Password=Abc123();
Encrypt=True;
TrustServerCertificate=False;
Connection Timeout=10;
This issue's bothered me for a while and I'll be bounting it in two days. Any suggestion's warmly appreciated.
I believe I've managed to resolve this weird issue. It appears that the user I'm using, despite being admin with all bells and whistles isn't recognized as admin when used in the connection string and trying to create the tables (which is the case at the first registration).
My solution was to create two logins - one with db_owner role and one with db_datareader and db_datawriter. First, I've used the elevated user in my connection string and registered a single user. That created the tables in the database as shown below.
Then, while able to continue as admin, I realized that we should try the demoted user and tada!, it worked perfectly. Once the tables were there, the whole shabeling behaved as expected.
To be perfectly sure, I dropped the tables from the database and there it was - the same issues as before. When I changed to the elevated user, the tables were restored allowing me to get back to the demoted one.
I also tried dropping the tables, confirming the issues to re-appear and then creating the tables manually. That works too! So basically,the only gotcha that caused it all was the original admin who's not treated as admin.
It might have to do with the fact that my Azure account's getting a bit old, LiveID used there is ancient and that didn't have an updated version of DB in Azure (the pull-up to v12 was carried out the 18th of December, so it's possible that it also was a requirement to get it working). I'm too tired and lazy to check that out and I realize that I've no idea how to get an "old" type of account. Besides, the issue will decrease and gradually vanish because the old accounts get upgraded eventually.

C# Inconsistent Security Group Membership

I apologize for how open ended I'm sure this will end up. I'll try to break this up in manageable chunks.
I'm writing a program that deals with SCCM. This program is locked down by security groups.
If you are not a member of the 'Server Operators' group, you don't get in. Simple. I've been testing for awhile on my dev machine (a Dell that is on wireless) with no issues.
When I released a beta, I found that under certain circumstances the program will not pick up on the user's security group membership and therefore deny access.
I was able to reproduce the issue, it seems machines on wireless tend to have this issue. Though, it's more complicated than that.
-Freshly imaged machines seem to have this issue
-Not all wireless machines; my dev machine is wireless
-One desktop (no wireless) has this issue. (It's at a remote site, so I can't really pick that one apart) I think it's a fresh image as well. I did however test on another computer at the same site - worked fine.
-Connecting to ethernet seems to have an effect - 75% of the time it fixes the issue somehow - after a bit of a wait. (Works on both ethernet and wireless at that point)
I've been trying to get a breakpoint set on one of these machines so I can see what is going on. Problem is, by the time I get VS.net installed the problem solves itself. I know (very little) about remote debugging - currently looking into that. This scenario makes me wonder if it's update related (the image is fairly up to date, maybe a month or two out?)
I also wrote a small utility that tests the login procedure (using the same code) and it finds the security group every time. Wat.
Code for finding security group of a user:
(courtesy of Stack :) )
static bool IsUserMemberOf_(string user, string group)
{ // (I realize the user parameter is superfluous in this case)
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + TC.act_Domain, TC.act_AD_User, TC.enc_GetADPassword());
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(&(objectClass=user)(|(cn=" + TC.act_AD_User + ")(sAMAccountName=" + TC.act_AD_User + ")))";
SearchResult result = mySearcher.FindOne();
foreach (string GroupPath in result.Properties["memberOf"])
{
if (GroupPath.Contains(group))
{
return true;
}
}
}
catch (DirectoryServicesCOMException)
{
}
return false;
}
I've also tried another method (using Principal.IsMemberOf() ), which had the same result. I've also investigated DNS/Network problems. Not ruling it out, but doesn't seem to be a factor in the testing I've done.
I'm at a loss. If anyone has any thoughts, by all means please lay them on me.
Thanks
Yeah, it was the .net version. Our image starts with .net 4.0. Throwing 4.5.2 at it clears it right up. I would have expected an error at runtime, but I guess not.
... I'll show myself out

System.Runtime.InteropServices.COMException (0x80080005): Retrieving the COM class factory for component with CLSID

I am replacing MS Word MergeFields with data from SQL Server in a simple asp.net c# application and it works fine locally but when i publish it often i get the following:
System.Runtime.InteropServices.COMException (0x80080005): Retrieving the COM class factory for component with CLSID {00020906-0000-0000-C000-000000000046} failed due to the following error: 80080005.
I tried giving both IIS_IUSRS and NETWORK SERVICE full access, and also set the identity to interactive in the DCOM config but it didn't fix anything.
For nasty permissions stopping you from doing what you expect, (I've encountered in the past too). Sometimes I've had to change my DCOM/COM+ permissions to an object too. At the DCOM Config tab screen for your project, under security, make sure BOTH "Launch and Activation" and "Access" have been customized to explicitly include the user account you intend to allow.
Now, one more thing -- just to rule out all others. I've even gone the extent (for testing / confirmation only) is that on the "Identity" tab, you can select the "Interactive User", "Launching User" or "This User". I would put in your actual account that you KNOW has permissions (ensure your account is also in the launch and activation... and access permissions on previous tab). And also to enter/confirm your password.
If that still fails, then I would try adding the ADMIN account for permissions and try that... just for purposes of yes, its permissions based on a user. Once confirmed, you can scale it back, and even create some simplified restricted user who's primary purpose is to run this utility, and this new user has its adequate permissions to do so.
Once that is done, you should be good to go.
Now, that said, I've run into another flakey thing and its been a while, so it may not be an issue any more. Periodically, during certain "Windows" updates, the functionality would just stop working. However, if all I did was go back into user maintenance, do change password for the account (even if just keeping it the same original password) and saving it, then going back into DCOM and enter/re-confirm saving it there too, all became functional again.
You should share the folder where you save the document that is created from the template with the NETWORK SERVICE user.
Change the dcomcnfg identity to interactive instead of launching user.
Reference: Error : 80070005, there is something in this forum about
Retrieving the COM class factory for component with CLSID {7979FAA5-3A2A-4E50-A2C9-286C6F34C1D3} failed due to the following error: 80080005

Problems with .net ValidateCredentials throwing an exception for any user other than Administrator

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)

Categories

Resources