Showing posts with label AD. Show all posts
Showing posts with label AD. Show all posts

Friday, March 08, 2019

AD : Domain Controller password policy

Every now and then you get an error:

"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

I have a web API on a server which talks to a DC (via LDAP) on an Azure VM and I have been connecting to it remotely no problem.

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=; domain = ''}
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

ADFS specifically targets authentication and authorisation.

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)

I was helping a customer who kept getting:

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, !, $, #, %)
The customer was adamant that the password conformed to these.

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)

The full error is:

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
Only the former was an option for me.

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
In other words, much the same as an IIS SSL certificate.

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;

try
{     
      ldapConnection = new DirectoryEntry(LDAP fqdn, user, password);               
}
with:
ldapConnection = new DirectoryEntry(LDAP fqdn, user, password, 
    Password,AuthenticationTypes.SecureSocketsLayer); 
Coming at this from another angle, this code also worked:

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)

Setting up a new system with an AD in a DC in another domain that is "sandpitted" and got this error.

"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

Best error message ever.

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

There are two kinds of attributes in AD viz. single valued and multi-valued. The latter obviously can have more than one value.

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)

This is truly a curved ball.

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.
UserPrincipal oUserPrincipal = GetUser(sUserName); 
userPrincipal.IsAccountLockedOut();
or if you want to get fancy - refer c# LDAP check user is locked or not
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 out
    return 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 and .NET

Active Directory Objects and C#

Active Directory With 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!