Ideas and thoughts about Microsoft Identity, C# development, cabbages and kings and random flotsam on the incoming tide
Friday, March 08, 2019
AD : Domain Controller password policy
"Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain."
So you need to find out the allowed length in the password policy.
An easy way to do this is to run:
secpol.msc
Other reasons for this message are that you have already changed your password in the last 24 hours or that you have reused a password that you used in the last 24 passwords.
Enjoy!
Thursday, June 29, 2017
AD : LDAP connections work remotely but not locally
Remotely, I connect to xxx.cloudapp.net. The DC shows as xxx.dev.local in ADUC.
Then I deployed a copy of the web API locally on the VM and the web API started throwing LDAP errors and telling me that there was an invalid user name / password when trying the LDAP connection. The aforementioned user name / password work perfectly remotely.
WTF?
So using "LDP" locally, we see:
and then we bind:
This gives:
-----------
0 = ldap_set_option(ld, LDAP_OPT_ENCRYPT, 1)
res = ldap_bind_s(ld, NULL, &NtAuthIdentity, NEGOTIATE (1158)); // v.3
{NtAuthIdentity: User='xxxldap'; Pwd=unavailable; domain = 'dev.local'}
Error 49: ldap_bind_s() failed: Invalid Credentials.
Server error: 8009030C: LdapErr: DSID-0C090516, comment: AcceptSecurityContext error, data 52e, v3839
Error 0x8009030C The logon attempt failed
-----------
Now I can try any combination I like. I can leave out the domain name, just use "dev", ... Nothing works.
This works remotely i.e. use "xxx.cloudapp.net" as the LDAP address and login as dev/userxxx.
So what if I try the "dev.local" address?
The connect works. Now for the Bind.
This works! But only if I leave the domain blank.
This gives:
-----------
0 = ldap_set_option(ld, LDAP_OPT_ENCRYPT, 1)
res = ldap_bind_s(ld, NULL, &NtAuthIdentity, NEGOTIATE (1158)); // v.3
{NtAuthIdentity: User='xxxldap'; Pwd=
Authenticated as: 'DEV\xxxLDAP'.
-----------
I'm not a DC / LDAP guru so can't explain this but hopefully this will help someone stuck on the same issue.
Enjoy!
Tuesday, February 14, 2017
AD : Using PowerShell to create users and groups
It does not target provisioning users into AD or adding them to groups. You need an Identity Manager for that.
You can do this with PowerShell and there are many links to do this around on the Internet e.g. PowerShell: Bulk create AD Users from CSV file.
My version
csv file format is:
Firstname,Lastname,Maildomain,SAM,OU,Password,Email
Joe,Bloggs,mydomain.local,jbloggs,"OU=MyUsers,DC=mydomain,DC=local",password,jb@abc.co.nz
Script
$Users = Import-Csv -Path "C:\blah\Users.csv"
foreach ($User in $Users)
{
$Displayname = $User.'Firstname' + " " + $User.'Lastname'
$UserFirstname = $User.'Firstname'
$UserLastname = $User.'Lastname'
$OU = $User.'OU'
$SAM = $User.'SAM'
$UPN = $User.'Firstname' + "." + $User.'Lastname' + "@" + $User.'Maildomain'
$Password = $User.'Password'
$Email = $User.'Email'
New-ADUser -Name "$Displayname" -DisplayName "$Displayname" -SamAccountName $SAM -UserPrincipalName $UPN -GivenName "$UserFirstname" -Surname "$UserLastname" -Description "$Description" -AccountPassword (ConvertTo-SecureString $Password -AsPlainText -Force) -EmailAddress "$Email" -Enabled $true -Path "$OU" -ChangePasswordAtLogon $false –PasswordNeverExpires $true -server mydomain.local
Write-Host –NoNewLine "Adding user: "
Write-Host $SAM
}
$Users = Import-Csv -Path "C:\blah\Users.csv"
foreach ($User in $Users)
{
$SAM = $User.'SAM'
Add-ADGroupMember -Identity "My Group 1" -Member $SAM
Add-ADGroupMember -Identity "My Group 2" -Member $SAM
Write-Host –NoNewLine "Adding groups for user: "
Write-Host $SAM
}
Enjoy!
Monday, January 09, 2017
AD : ERROR_DS_CONSTRAINT_VIOLATION (0x8007202f)
ERROR_DS_CONSTRAINT_VIOLATION (0x8007202f)
when trying to change passwords programmatically.
I referred him to the password rules in the default policy i.e. the usual:
Passwords may not contain the user's samAccountName (Account Name) value or entire displayName (Full Name value). Both checks are not case sensitive.
The password contains characters from three of the following categories:
- Uppercase letters of European languages (A through Z, with diacritic marks, Greek and Cyrillic characters)
- Lowercase letters of European languages (a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters)
- Base 10 digits (0 through 9)
- Non-alphanumeric characters (special characters) (for example, !, $, #, %)
But there is also another constraint - by default you cannot change your password more than once a day.
This was the reason that was tripping the customer up.
The moral of the story is that if the error says "Password constraint" it's because there is one :-)
Bonus
You may be wondering how to handle the case where you register a new user programmatically. Normally you create a temporary password and send this in an email and the user clicks on a link which takes them to some kind of "Change Password" page.
The user then changes their password.
In this case, the password is being changed twice so how do you get around the constraint?
Add the "User must change the password at next logon" flag when you create the temporary password.
Enjoy!
Tuesday, September 13, 2016
AD : Attempt to compromise security (HRESULT: 0x800704F1)
System.DirectoryServices.AccountManagement.PrincipalOperationException: The system detected a possible attempt to compromise security. Please ensure that you can contact the server that authenticated you. (Exception from HRESULT: 0x800704F1) ---> System.Runtime.InteropServices.COMException: The system detected a possible attempt to compromise security. Please ensure that you can contact the server that authenticated you. (Exception from HRESULT: 0x800704F1)
This is described here:
MS16-101: Description of the security update for Windows authentication methods
Essentially, the updates disable the ability of the Negotiate process to fall back to NTLM when Kerberos authentication fails for password change operation.
I found these useful:
Enabling Secure LDAP on Windows Server 2008/2012 Domain Controllers
Enabling Secure LDAP on Windows Server 2008/2012 Domain Controllers: Configuration
There is a good write-up here:
Solution for Problem in Change Password after Windows Security Update
TL;DR
- Enable LDAPS
- Set "Trusted Hosts"
- Revert the update
And so began a journey into the depths of the Internet - or more specifically the huge amount of garbage, rubbish and misinformation out there :-(
One of the problems was the connection string.
I naively assumed that I simply needed to change:
LDAP://fqdn to
LDAPS://fqdn
But from what I can see, there is no actual protocol called LDAPS? It's official name is "Secure LDAP" and the connection string you need is:
LDAP://fqdn:636
The "correct" way to enable it is to install an Enterprise Root CA on a Domain Controller.
That wasn't an option (and a massive overkill) so I started wondering about installing a self-signed certificate on the DC.
Disclaimer: Using self-signed certificates or installing certificates as follows may not be the best solution security wise. Use this at your own risk!
The certificate that you need has to follow certain rules e.g.
- It has to be for "Server Authentication" (OID: 1.3.6.1.5.5.7.3.1)
- The Subject name must match the Fully Qualified Domain Name (FQDN) of the host machine, such as Subject:CN=server.domain.com
- (This is also true for the first name in the Subject Alternative Name (SAN))
- The host machine account needs to have access to the private key
Once you have the .pfx file, and using "mmc" with:
Service Account / Local Computer
Select "Active Directory Domain Services".
and import.
You need this certificate in "NTDS\Personal" and "NTDS\Trusted Root Certification Authorities".
To check this, you need to run "ldp" from the command line.
This should result in:
"Host supports SSL, SSL cipher strength = 256 bits"
If you need to run "ldp" from a non-DC machine, you need Remote Server Administration Tools (RSAT) for Windows Client and Windows Server.
Once "ldp" runs OK, you now have secure LDAP.
Now for the C# .NET side:
Replace the ldapConnection below:
DirectoryEntry ldapConnection = null;with:
try
{
ldapConnection = new DirectoryEntry(LDAP fqdn, user, password);
}
ldapConnection = new DirectoryEntry(LDAP fqdn, user, password,Coming at this from another angle, this code also worked:
Password,AuthenticationTypes.SecureSocketsLayer);
Authenticating a user over LDAP in .Net: LdapConnection vs. PrincipalContext
Footnote: I also found that I could leave out the extra parameter:
"Password,AuthenticationTypes.SecureSocketsLayer"
if the user was "domain\user" rather than "user". Go figure!
Enjoy!
Monday, November 17, 2014
AD : Information about the domain could not be retrieved (1355)
"Information about the domain could not be retrieved (1355)".
Can ping the DC but the IP / Name is in my host file - it's not on the DNS.
Lots of stuff on the Internet - mainly red herrings.
The problem was the DNS "hole" - adding this DC as my alternate DNS on my Windows 7 box fixed the problem.
Update
Turns out this is not sufficient. Still get this &^%&^% problem.
Start / Control Panel / Network and Internet / Network and Sharing Centre
On LHS at top / Change Adapter Settings
Right click / Disable
You will lose your network connection!!!
Right click / Enable
All will be well until you reset your PC and then you will have the joy of doing it all again.
Use at own risk but works for me.
Enjoy!
Wednesday, January 29, 2014
AD : PrincipalOperationException A device attached to the system is not functioning
Got this on a web site - WTF - what device?
Card reader, CD drive ... not on a web site, mate!
Turns out this is during a create on a user or a group in AD where the name is greater than the allowed field length.
So for Create User - the SAMAccountName can't be greater than 20 characters,
There's probably a similar restriction for group since I got the same error.
Good interview question :-)
Enjoy!
Friday, January 17, 2014
ADFS : Multi-valued attributes from AD
You can see the difference when you try and edit them. Single-valued has a single textbook while multi-valued has a textbox to enter a new value and a multi-line textbox to show all the current values.
If you look at a multi-valued attribute in AD using ADUC, you'll see it displayed as:
value1;value2;value3
Note: This is different to a single value attribute that contains the string:
value1 value2 value3
That's a string of ONE value which is"value1 value2 value3".
How do you find them.
Use ldp, click on the Base DN of "CN=Schema ..." and then run:
(isSingleValued=FALSE)
I couldn't find any under the "objectClass=user" category but there are some if you have added the AD extension attributes to the schema i.e. the ones that start with "msExch ...".
All of which is a segue into how ADFS handles this. It produces a new claim (of the same type) for each value.
So if you took the above and mapped them to a claim of type Values, you'll get:
.../claim/Value = value3
.../claim/Value = value2
.../claim/Value = value1
Interestingly, it seems to display the values in reverse order but I wouldn't make any assumptions about the order the claims are presented.
Enjoy!
Wednesday, July 03, 2013
AD : Locked accounts in Active Directory (AD)
Once upon a time, there was an attribute in AD called:
userAccountControl - ADS_UF_LOCKOUT = 16 (d) 10 (h)
However, in later versions of Windows Server (e.g. 2008), this was moved to:
msDS-User-Account-Control-Computed - UF_LOCKOUT = 16 (d) 10 (h).
BUT there was a slight problem. As the name implies this is a computed attribute i.e. it doesn't actually exist. Rather it is computed on the fly. The implication is that it can't be used in a standard search query.
Hence the curved ball.
Some blogs suggest you can use:
filter = "(&(objectClass=user)(lockoutTime>=0))"
This works in the sense that it refines a potentially huge list to a much smaller one.
However, it is not sufficient. When a user is locked out and then unlocked, this attribute can be set to zero (as opposed to the previous time that was stored).
Easy enough, change the filter to:
filter = "(&(objectClass=user)(lockoutTime>0))"
Dream on - that's not a valid filter query language construct.
So this first list has to be iterated through again to check that the user is actually locked.
Lots of code out there to do this e.g.
or if you want to get fancy - refer c# LDAP check user is locked or notUserPrincipal oUserPrincipal = GetUser(sUserName);userPrincipal.IsAccountLockedOut();
string attribName = "msDS-User-Account-Control-Computed";user.RefreshCache(new string[] { attribName });const int UF_LOCKOUT = 0x0010;int userFlags = (int)user.Properties[attribName].Value;if ( (userFlags & UF_LOCKOUT) == UF_LOCKOUT){// if this is the case, the account is locked outreturn true;}return false;
To unlock - refer Everything in Active Directory via C#.Net 3.5 (Using System.DirectoryServices.AccountManagement).
UserPrincipal oUserPrincipal = GetUser(sUserName);
oUserPrincipal.UnlockAccount();
oUserPrincipal.Save();
To unlock via ADUC, click the Account tab on the user's Properties and then check the "Unlock Account" check box. Note: You cannot lock an account either programmatically or through ADUC.
The system will lock the account based on the user's password policy e.g. user will be locked out after x invalid attempts. The policy may state that the user many never be locked out.
Enjoy!
Tuesday, June 18, 2013
ADFS : “Problem” with “Token-Groups–Unqualified Names”
ADFS has this clever feature where if you select this mapping in the claims rules and map it to Roles, you will get a set of roles claims that contain all the groups for the authenticated user e.g.
http://schemas.microsoft.com/ws/2008/06/identity/claims/role Role1
That’s well and good when the groups are “flat” i.e. the groups are not memberOf other groups.
If they are, then this mapping will work it’s way up the hierarchy and display ALL the groups.
So if Joe is a memberOf Role1 and if Role1 is a memberOf Role2, then ADFS will construct:
http://schemas.microsoft.com/ws/2008/06/identity/claims/role Role1
http://schemas.microsoft.com/ws/2008/06/identity/claims/role Role2
Now that’s fine if that’s what you want but if Joe has 20 roles and all these roles are at the bottom of a whole pile of other roles you end up with many, many claims and a complete mess!
So what to do if you only want the bottom layer i.e. the actual memberOf.
If you go have a look via ADUC, guess what? memberOf is not displayed as an attribute!
WTF!
To see it as an attribute in the attribute list, you need to click the “Filter” box (bottom right) in “Attribute Editor” and then select “Backlinks”.
OK – so what if we set up a claims rule mapping memberOf to Roles?
So we type memberOf into the LDAP attribute field (it is actually editable) and note that it displays as “Is-Member-Of-DL”.
Problem!
What we get back is the whole CN e.g.
CN=Role1,OU=Sales,OU=company,DC=com
when what we got before was just Role1.
Enter stage left Joji Oshima. He da man!
Refer: AD FS 2.0 Claims Rule Language Part 2.
and have a look at “Problem 1” which is exactly the scenario described above.
Problem solved!
Enjoy!
Tuesday, April 09, 2013
AD : Using a filter for UAC bits and null attributes
There’s a lot of functionality in the AD API’s and there are neat API’s to enable / disable users but what if you want to search for them e.g. find all users in an OU that are currently disabled.
You could just search for all the users in an OU and then enumerate through all of them where:
user.Enabled = false;
but that’s a pain.
Enter RFC2254.
Or look here: Search Filter Syntax
What you will find is the matching rule OIDs.
e.g.
1.2.840.113556.1.4.803 = A match is found only if all bits from the attribute match the value. This rule is equivalent to a bitwise AND operator.
UAC is described here: Attributes for AD Users : userAccountControl.
Note that: Const ADS_UF_ACCOUNT_DISABLE = 2.
So now that we have laid the framework, back to the original question.
To search for disabled users, the filter would be:
(userAccountControl:1.2.840.113556.1.4.803:=2)
One of the problems with AD is “null” attributes i.e. attributes that in ADUC display as “not set”.
To search for these e.g. for email, use the filter:
(!(email=*))
* is the AD wildcard so this searches for users who don’t have an email address.
So to search for disabled users who don’t currently have an email address, use:
(&(userAccountControl:1.2.840.113556.1.4.803:=2)(!(email=*)))
This stuff does your head in!
Enjoy!
Tuesday, August 07, 2012
AD : Programmatically getting password policy
On Windows Server 2008, this doesn’t seem possible. There doesn’t appear to be an API that gives you all the details of the current user’s password policy.
Why does this matter?
Because when the user needs to pick a password and they get it wrong, the standard message is:
“The password you have chosen does not meet corporate policy. Please contact the help desk”.
The standard wrt. complexity is normally:
“The password contains characters from three of the following categories:
- Uppercase letters of European languages (A through Z, with diacritic marks, Greek and Cyrillic characters)
- Lowercase letters of European languages (a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters)
- Base 10 digits (0 through 9)
- Non-alphanumeric characters (special characters) (for example, !, $, #, %)
- Any Unicode character that is categorized as an alphabetic character but is not uppercase or lowercase. This includes Unicode characters from Asian languages.”
However, you can summarise the above and then get the pieces of the puzzle individually. To do this, you would have to get each attribute from AD.
Refer Windows Domain Password Policies
msDS-PasswordSettingsPrecedence
Establishes what takes precedence in situations where a user has membership in multiple groups with different password policies.
msDS-PasswordReversibleEncryptionEnabled
Toggles whether reversible encryption is enabled.
msDS-PasswordHistoryLength
Determines how many intervening passwords must be unique before one can be reused.
msDS-PasswordComplexityEnabled
Establishes the number and type of characters required in a password.
msDS-MinimumPasswordLength
Establishes the minimum length of a password.
msDS-MinimumPasswordAge
Determines how long a user must use a password before changing it.
msDS-MaximumPasswordAge
Determines how long a user can use a password before being required to change it.
msDS-LockoutThreshold
Determines how many failed password attempts will be allowed before locking out user account.
msDS-LockoutObservationWindow
Determines the time after which the bad password counter will be reset.
msDS-LockoutDuration
Determines how long the account will be locked out after too many failed password attempts.
So the message could be something like:
“Your password must be <msDS-MinimumPasswordLength> characters long, you cannot use one of your previous <msDS-PasswordHistoryLength> passwords and you will have to change it every <msDS-MaximumPasswordAge> days”.
Enjoy!
Thursday, July 12, 2012
AD : Adding the extension attributes
Quite often when you work with AD, you need some extra attributes to store customer specific information. I normally use the extensionAttributes (1 through 15) for this.
The problem is that they are not in AD out-the-box.
They are part of the Microsoft Exchange Server schema. The schema is the only part you have to install. You do NOT have to install Exchange.
Refer Prepare Active Directory and Domains
I used Exchange 2010 SP2. I guess you could download the trial version if you don’t have the DVD to hand?
To install, just run “Setup /PrepareSchema” to have the additional extension attributes added.
Note that you need to be a “Domain Admin” & “Schema Admin” & “Enterprise Admin” in order to run this script.
Enjoy!
Tuesday, March 27, 2012
AD : Enumerating UserAccountControl
There doesn’t seem to anything built into the .NET framework to do this and it’s a lot easier to read actual names rather than hex boolean manipulation so I rolled my own:
[Flags]
enum UserAccountControl
{
ADS_UF_SCRIPT = 1, // 0x1
ADS_UF_ACCOUNTDISABLE = 2, // 0x2
ADS_UF_HOMEDIR_REQUIRED = 8, // 0x8
ADS_UF_LOCKOUT = 16, // 0x10
ADS_UF_PASSWD_NOTREQD = 32, // 0x20
ADS_UF_PASSWD_CANT_CHANGE = 64, // 0x40
ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128, // 0x80
ADS_UF_TEMP_DUPLICATE_ACCOUNT = 256, // 0x100
ADS_UF_NORMAL_ACCOUNT = 512, // 0x200
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 2048, // 0x800
ADS_UF_WORKSTATION_TRUST_ACCOUNT = 4096, // 0x1000
ADS_UF_SERVER_TRUST_ACCOUNT = 8192, // 0x2000
ADS_UF_DONT_EXPIRE_PASSWD = 65536, // 0x10000
ADS_UF_MNS_LOGON_ACCOUNT = 131072, // 0x20000
ADS_UF_SMARTCARD_REQUIRED = 262144, // 0x40000
ADS_UF_TRUSTED_FOR_DELEGATION = 524288, // 0x80000
ADS_UF_NOT_DELEGATED = 1048576, // 0x100000
ADS_UF_USE_DES_KEY_ONLY = 2097152, // 0x200000
ADS_UF_DONT_REQUIRE_PREAUTH = 4194304, // 0x400000
ADS_UF_PASSWORD_EXPIRED = 8388608, // 0x800000
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216 // 0x1000000
}
With this you can write code like:
val = val | (int) UserAccountControl.ADS_UF_DONT_EXPIRE_PASSWD;
Far more readable, I think you’ll agree.
And remember:
To clear a value, use:
val = val & (int) ~UserAccountControl.ADS_UF_ACCOUNTDISABLE;
The ~ is a boolean complement ~ Operator.
Enjoy!
Thursday, March 22, 2012
C# : Active Directory Resources
Being doing a bit of work in this space and I found the following links to be really useful:
Howto: (Almost) Everything In Active Directory via C#
Everything in Active Directory via C#.NET 3.5
Active Directory Objects and C#
So if you want to create users, work with their passwords, update their details, add them to groups etc. you’ll find something above to help you.
Enjoy!
Wednesday, March 14, 2012
C# : The server is unwilling to process the request
Busy doing some AD work with C#.
I’m creating the user, changing some of the attributes (e.g. setting “DONT_EXPIRE_PASSWORD”) and then setting the password.
Got the exception:
DirectoryServicesCOMException (0x80072035): The server is unwilling to process the request
Mr. Google to the rescue. After sorting through the huge pile of crap, found the answer.
You need to do it in the following order:
- Create the user
- Commit changes
- Set the password
- Commit changes
- Change the attributes
- Commit changes
i.e. Set the password BEFORE doing any attribute CRUD stuff.
Enjoy!
Monday, August 29, 2011
AD : Displaying the SPN
SPN stands for Service Principal Name – used (among others) by Kerberos.
Normally, you work with them via the Setspn tool.
e.g.
setspn –l <account name>
which lists all spns assigned to that account.
However, you can also do this by:
Start – Administrative Tools – Active Directory Users and Computers – View – Advanced Features
Then navigate to the account via the tree. Right click – Properties – Attribute Editor tab. Then scroll down to servicePrincipalName and double-click.
You can add and remove SPN’s from this window as well. (i.e. the equivalent of setspn –a … or setspn –d …).
Enjoy!