joeware - never stop exploring... :)

Information about joeware mixed with wild and crazy opinions...

1/4/2012

sAMAccountName is always unique in a Windows domain… or is it?

by @ 7:33 pm. Filed under tech

So while chatting with an admin this week he mentioned that his company synced data from their single domain forest into an external database with a primary key of sAMAccountName. This was a topic of interest for him because they had just learned it was possible to have two different objects in Active Directory with the same sAMAccountName value. They learned this little known but important piece of information through direct experience as it actually happened to them. When something like that happens, it kind of messes with any external systems that are using sAMAccountName as a unique key causing varying levels of chaos and disaster and really isn’t all that great for the AD Admins either because it is likely to cause some level of authentication issues for whomever is duplicated.

I know what you are thinking, "but joe… while SAM Names aren’t unique within a multi-domain forest,  they are supposed to be unique within a domain!!!" And that is absolutely correct, they are *supposed* to be unique in a domain[1]. That is, however, a far cry from being guaranteed to be unique.  What’s the difference you ask? The difference is this… If something is supposed to be unique, some decent but reasonable amount of work will be put into play to produce a method that will be used to try to reach a state where you are generally going to have a unique value. If something is guaranteed to be unique, whatever amount of work is necessary to assure that there is no way you can duplicate values no matter what you do is used instead. The sAMAccountName attribute falls squarely into the *supposed* to be unique category.  Microsoft gives it the old college try to work towards uniqueness but don’t go whole hog and absolutely make sure it isn’t possible.

Those of us who have been around since the NT4 days or perhaps those who do a lot of work with the SAM DB on non-domain controllers (member servers, standalone servers, and client machines) are quite used to and expectant of the ability to have unique sAMAccountNames. Since you have but a single SAM DB (aka a portion of the registry) that can be updated at any given moment, that SAM DB is going to be able to enforce uniqueness. Once we moved into the world of Active Directory and a loosely consistent multi-master replication model things changed and the lines blurred a little.

Creating objects with duplicate sAMAccountName attributes method 1 – loosely consistent multi-master replication

The first method to produce objects with duplicate sAMAccountNames that cropped up, a side effect really of the new cool "loosely consistent" replication engine coupled with the multi-master approach to directories, was the day Windows 2000 was released. As soon as you have more than one domain controller for a given domain, you have the ability to create objects that break the uniqueness rule for sAMAccountName. It is quite simple to accomplish, you simply create an object on two different domain controllers inside of the replication convergence period for those DCs. If the DCs are in the same site, that means you have seconds or possibly minutes for convergence, if the DCs are in different sites then you could have hours or days to accomplish the task.

For example, you take a simple script like

admod -hh joeware-dc1 -add -b cn=testcase1,ou=usersample,dc=joeware,dc=local objectclass::user samaccountname::testcase1
admod -hh joeware-dc2 -add -b cn=testcase1,ou=test,dc=joeware,dc=local objectclass::user samaccountname::testcase1

and run it and it is pretty likely you will generate two different user objects with the same sAMAccountName after replication convergence. Like so:

C:\temp>adfind -default -f name=testcase1

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: JOEWARE-DC1.joeware.local:389
Directory: Windows Server 2003
Base DN: DC=joeware,DC=local

dn:CN=testcase1,OU=usersample,DC=joeware,DC=local
>objectClass: top
>objectClass: person
>objectClass: organizationalPerson
>objectClass: user
>cn: testcase1
>distinguishedName: CN=testcase1,OU=usersample,DC=joeware,DC=local
>instanceType: 4
>whenCreated: 20120103220701.0Z
>whenChanged: 20120103220701.0Z
>uSNCreated: 1011021
>uSNChanged: 1011022
>name: testcase1
>objectGUID: {85FF63EE-EF50-4EDB-8414-445ECEBB320A}
>userAccountControl: 546
>badPwdCount: 0
>codePage: 0
>countryCode: 0
>badPasswordTime: 0
>lastLogoff: 0
>lastLogon: 0
>pwdLastSet: 0
>primaryGroupID: 513
>objectSid: S-1-5-21-3641047700-3957557241-2644433309-7689
>accountExpires: 9223372036854775807
>logonCount: 0
>sAMAccountName: testcase1
>sAMAccountType: 805306368
>objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=joeware,DC=local

dn:CN=testcase1,OU=test,DC=joeware,DC=local
>objectClass: top
>objectClass: person
>objectClass: organizationalPerson
>objectClass: user
>cn: testcase1
>distinguishedName: CN=testcase1,OU=test,DC=joeware,DC=local
>instanceType: 4
>whenCreated: 20120103220702.0Z
>whenChanged: 20120103220717.0Z
>uSNCreated: 1011023
>uSNChanged: 1011023
>name: testcase1
>objectGUID: {8C815AFB-42E1-4A0F-84AD-D0ABC0228F88}
>userAccountControl: 546
>codePage: 0
>countryCode: 0
>pwdLastSet: 0
>primaryGroupID: 513
>objectSid: S-1-5-21-3641047700-3957557241-2644433309-8111
>accountExpires: 9223372036854775807
>sAMAccountName: testcase1
>sAMAccountType: 805306368
>objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=joeware,DC=local

2 Objects returned

 

"but joe… what about the collision detection logic?"

admod -hh joeware-dc1 -add -b cn=testcase3,ou=usersample,dc=joeware,dc=local objectclass::user samaccountname::testcase3
admod -hh joeware-dc2 -add -b cn=testcase3,ou=usersample,dc=joeware,dc=local objectclass::user samaccountname::testcase3

C:\temp>adfind -default -f name=testcase3*

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: JOEWARE-DC1.joeware.local:389
Directory: Windows Server 2003
Base DN: DC=joeware,DC=local

dn:CN=testcase3,OU=usersample,DC=joeware,DC=local
>objectClass: top
>objectClass: person
>objectClass: organizationalPerson
>objectClass: user
>cn: testcase3
>distinguishedName: CN=testcase3,OU=usersample,DC=joeware,DC=local
>instanceType: 4
>whenCreated: 20120103221530.0Z
>whenChanged: 20120103221530.0Z
>uSNCreated: 1011039
>uSNChanged: 1011040
>name: testcase3
>objectGUID: {DFA20362-6899-41F6-94F2-3B7D5924FB41}
>userAccountControl: 546
>badPwdCount: 0
>codePage: 0
>countryCode: 0
>badPasswordTime: 0
>lastLogoff: 0
>lastLogon: 0
>pwdLastSet: 0
>primaryGroupID: 513
>objectSid: S-1-5-21-3641047700-3957557241-2644433309-7691
>accountExpires: 9223372036854775807
>logonCount: 0
>sAMAccountName: testcase3
>sAMAccountType: 805306368
>objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=joeware,DC=local

dn:CN=testcase3\0ACNF:c928020b-f156-4e08-8e87-314eb8817a2a,OU=usersample,DC=joeware,DC=local
>objectClass: top
>objectClass: person
>objectClass: organizationalPerson
>objectClass: user
>cn: testcase3\0ACNF:c928020b-f156-4e08-8e87-314eb8817a2a
>distinguishedName: CN=testcase3\0ACNF:c928020b-f156-4e08-8e87-314eb8817a2a,OU=usersample,DC=joeware,DC=local
>instanceType: 4
>whenCreated: 20120103221530.0Z
>whenChanged: 20120103221546.0Z
>uSNCreated: 1011041
>uSNChanged: 1011041
>name: testcase3\0ACNF:c928020b-f156-4e08-8e87-314eb8817a2a
>objectGUID: {C928020B-F156-4E08-8E87-314EB8817A2A}
>userAccountControl: 546
>codePage: 0
>countryCode: 0
>pwdLastSet: 0
>primaryGroupID: 513
>objectSid: S-1-5-21-3641047700-3957557241-2644433309-8113
>accountExpires: 9223372036854775807
>sAMAccountName: testcase3
>sAMAccountType: 805306368
>objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=joeware,DC=local

2 Objects returned

As you can clearly see, collision logic absolutely works for duplicated RDNs… but there is nothing around checking for duplication of sAMAccountName when replicating in objects from another DC. The only check in place for duplicate sAMAccountName attributes is on object instantiation when the SAM Rules code is called to validate the attribute settings for a given object.

 

Creating objects with duplicate sAMAccountName attributes method 2 – tombstone reanimation

The second method for creating objects with duplicate sAMAccountName attributes surfaced, again as a side effect, in Windows Server 2003 with the release of the new tombstone reanimation functionality. This is the functionality that gave you a basic object "undelete" capability in Active Directory. Prior to 2003 if you deleted something and needed it back, well you better have functioning backups. With Windows Server 2003 the AD team gave us the ability to recover the tombstones so they could be used again (YEAH![2]). This functionality is not all that well known and even some of the people who know about it don’t really know how to accomplish it so in most shops this method for duplicating the sAMAccountName attribute is not as likely[3].

So for example, here I will create a user object, delete it, create another user object with the same SAM Name and then recover the original user object’s tombstone.

C:\>admod -b CN=testcase5,CN=Users,DC=k8r2dom,DC=loc -add objectclass::user samaccountname::testcase5

AdMod V01.16.00cpp Joe Richards (joe@joeware.net) March 2011

DN Count: 1
Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2

Adding specified objects…
   DN: CN=testcase5,CN=Users,DC=k8r2dom,DC=loc…

The command completed successfully

C:\>adfind -showdel -default -f name=testcase5* samaccountname

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2
Base DN: DC=k8r2dom,DC=loc

dn:CN=testcase5,CN=Users,DC=k8r2dom,DC=loc
>sAMAccountName: testcase5

1 Objects returned

C:\>adfind -default -f name=testcase5* -dsq | admod -del

AdMod V01.16.00cpp Joe Richards (joe@joeware.net) March 2011

DN Count: 1
Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2

Deleting specified objects…
   DN: CN=testcase5,CN=Users,DC=k8r2dom,DC=loc…

The command completed successfully

C:\>admod -b CN=testcase5,ou=testou,DC=k8r2dom,DC=loc -add objectclass::user samaccountname::testcase5

AdMod V01.16.00cpp Joe Richards (joe@joeware.net) March 2011

DN Count: 1
Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2

Adding specified objects…
   DN: CN=testcase5,ou=testou,DC=k8r2dom,DC=loc…

The command completed successfully

C:\>adfind -showdel -default -f name=testcase5* samaccountname

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2
Base DN: DC=k8r2dom,DC=loc

dn:CN=testcase5,OU=testOU,DC=k8r2dom,DC=loc
>sAMAccountName: testcase5

dn:CN=testcase5\0ADEL:9a4e0a1f-4498-4ef3-aa1e-76bf0c69c61e,CN=Deleted Objects,DC=k8r2dom,DC=loc
>sAMAccountName: testcase5

2 Objects returned

C:\>adfind -showdel -default -rb "cn=deleted objects" -f name=testcase5* -dsq | admod -undel

AdMod V01.16.00cpp Joe Richards (joe@joeware.net) March 2011

DN Count: 1
Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2

Undeleting specified objects…
   DN: CN=testcase5\0ADEL:9a4e0a1f-4498-4ef3-aa1e-76bf0c69c61e,CN=Deleted Objects,DC=k8r2dom,DC=loc…

The command completed successfully

C:\>adfind -showdel -default -f name=testcase5

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2
Base DN: DC=k8r2dom,DC=loc

dn:CN=testcase5,CN=Users,DC=k8r2dom,DC=loc
>objectClass: top
>objectClass: person
>objectClass: organizationalPerson
>objectClass: user
>cn: testcase5
>distinguishedName: CN=testcase5,CN=Users,DC=k8r2dom,DC=loc
>instanceType: 4
>whenCreated: 20120104214547.0Z
>whenChanged: 20120104214802.0Z
>uSNCreated: 498225
>uSNChanged: 498257
>name: testcase5
>objectGUID: {9A4E0A1F-4498-4EF3-AA1E-76BF0C69C61E}
>userAccountControl: 546
>badPwdCount: 0
>codePage: 0
>countryCode: 0
>badPasswordTime: 0
>lastLogoff: 0
>lastLogon: 0
>pwdLastSet: 0
>primaryGroupID: 513
>operatorCount: 0
>objectSid: S-1-5-21-1767008341-141532995-3086693677-62124
>adminCount: 0
>accountExpires: 0
>logonCount: 0
>sAMAccountName: testcase5
>sAMAccountType: 805306368
>lastKnownParent: CN=Users,DC=k8r2dom,DC=loc
>objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=k8r2dom,DC=loc
>dSCorePropagationData: 20120104214802.0Z
>dSCorePropagationData: 16010101000000.0Z

dn:CN=testcase5,OU=testOU,DC=k8r2dom,DC=loc
>objectClass: top
>objectClass: person
>objectClass: organizationalPerson
>objectClass: user
>cn: testcase5
>distinguishedName: CN=testcase5,OU=testOU,DC=k8r2dom,DC=loc
>instanceType: 4
>whenCreated: 20120104214650.0Z
>whenChanged: 20120104214650.0Z
>uSNCreated: 498237
>uSNChanged: 498238
>name: testcase5
>objectGUID: {05C87B3D-DFED-4740-9E6E-7F82BC499F82}
>userAccountControl: 546
>badPwdCount: 0
>codePage: 0
>countryCode: 0
>badPasswordTime: 0
>lastLogoff: 0
>lastLogon: 0
>pwdLastSet: 0
>primaryGroupID: 513
>objectSid: S-1-5-21-1767008341-141532995-3086693677-62125
>accountExpires: 9223372036854775807
>logonCount: 0
>sAMAccountName: testcase5
>sAMAccountType: 805306368
>objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=k8r2dom,DC=loc
>dSCorePropagationData: 16010101000000.0Z

2 Objects returned

C:\>

As you can see, we once again have two user objects in the same domain with the same sAMAccountName attribute values. A good argument could be put together that perhaps a validation of the SAM Name should be made in this case but like with replication, it just wasn’t something they wanted to put the extra work into as, again like replication, it is a pretty small edge case.

 

$DUPLICATE-<hexchars> for sAMAccountName???

"Ok joe… so while I don’t like it, it makes sense, but I swear I have seen $DUPLICATE-<hexchars> for a user or (more likely) computer objects in AD before so I thought that there was conflict resolution to handle these things for sAMAccountName like RDNs and (effectively) DNs???"

Yes it is definitely possible you saw this, but that conflict resolution did not occur during either of the processes above. It occurred later when someone tried to create yet another new object with the duplicated name or when someone tries to authenticate using the duplicated SAM Name.

Example 1 – creating a new object with the same duplicated SAM Name

C:\>admod -b CN=testcase5,cn=computers,DC=k8r2dom,DC=loc -add objectclass::user samaccountname::testcase5

AdMod V01.16.00cpp Joe Richards (joe@joeware.net) March 2011

DN Count: 1
Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2

Adding specified objects…
   DN: CN=testcase5,cn=computers,DC=k8r2dom,DC=loc…: [K8R2Dom-DC1.k8r2dom.loc] Error 0x44 (68) – Already Exists

ERROR: Too many errors encountered, terminating…

The command did not complete successfully

C:\>adfind -showdel -default -f name=testcase5 samaccountname

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2
Base DN: DC=k8r2dom,DC=loc

dn:CN=testcase5,CN=Users,DC=k8r2dom,DC=loc
>sAMAccountName: $DUPLICATE-f2ac

dn:CN=testcase5,OU=testOU,DC=k8r2dom,DC=loc
>sAMAccountName: testcase5

2 Objects returned

 

Example 2 – trying to authenticate to the duplicated SAM Name

C:\>adfind -showdel -default -f name=testcase1 samaccountname

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2
Base DN: DC=k8r2dom,DC=loc

dn:CN=testcase1,CN=Users,DC=k8r2dom,DC=loc
>sAMAccountName: testcase1

dn:CN=testcase1,OU=testOU,DC=k8r2dom,DC=loc
>sAMAccountName: testcase1

2 Objects returned

C:\>adfind -default -s base -dn -u k8r2dom\testcase1 -p password -exterr

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

LDAP_BIND: [] Error 0x31 (49) – Invalid Credentials
Extended Error: No extended error info available.
Terminating program.

C:\>adfind -showdel -default -f name=testcase1 samaccountname

AdFind V01.45.00cpp Joe Richards (joe@joeware.net) March 2011

Using server: K8R2Dom-DC1.k8r2dom.loc:389
Directory: Windows Server 2008 R2
Base DN: DC=k8r2dom,DC=loc

dn:CN=testcase1,CN=Users,DC=k8r2dom,DC=loc
>sAMAccountName: $DUPLICATE-f2aa

dn:CN=testcase1,OU=testOU,DC=k8r2dom,DC=loc
>sAMAccountName: testcase1

2 Objects returned

 

This concludes this blog post. I hope that it was informative and I hope to bring a lot more like this to the blog again this year. Once again I apologize for my relative absence last year. 🙂

 

   joe

 

 

[1] This is obviously about Active Directory Domain Services, ADAM or ADLDS can have duplicate sAMAccountName attributes all day assuming you have sAMAccountName defined in the schema. There is absolutely NOTHING in place to assure or guarantee anything for it.

[2] Anything that helps me avoid authoritative restores is a YEAH! type of thing. 🙂

[3] The main reason for the lack of knowing is likely that Microsoft never produced any kind of "reanimate" GUI interface which effectively prevented some 90% of the Windows admins out there from ever knowing about it and probably even more from using it. A second "strike", if you will, was that a tombstone object didn’t normally have all of the attributes that were populated on the object prior to deletion so even if you knew how to perform the reanimation, you may end up performing a restore of the object anyway to get all of the info for the object. Now if you knew about the functionality and you knew how to take advantage of it it likely was very useful for you when absolutely needed and you didn’t want to fuss around with an authoritative restore (again [2]). Of course now we have recycle bin so we are many steps beyond tombstone reanimation now (or have the capability to be…) but in terms of this blog post, tombstone reanimation gave us a new method to duplicate a sAMAccountName.

Rating 4.50 out of 5

9/5/2008

Smart Updates of bitwise attributes

by @ 12:46 am. Filed under tech

Recently in working with Laura Hunter on my Technical Review of Active Directory Cookbook 3rd Edition, I mentioned that AdFind and AdMod could be used together to do a smartâ„¢  update of bit flag attributes. She was a bit surprised so I figured I would mention it here as well for everyone.

But first let’s back up…

Some attributes in AD are not as intuitive as they could be. A subset of these attributes are attributes that are bit flags. This is an attribute with a numeric/integer format that has meaning at the bit level instead of the overall value level. The most common example is probably userAccountControl so let’s discuss that a little.

The userAccountControl value will look something like 512 or 514 or 546 or maybe 4096 or who knows what else… Many people will look at those at absolute values but you absolutely cannot do that… Because it isn’t right.

The userAccountControl is actually a bit flag where each bit in the value means something different. The current enumeration looks like (http://msdn.microsoft.com/en-us/library/ms680832.aspx)

Hexadecimal value
Identifier (defined in iads.h)
Description

0x00000001
ADS_UF_SCRIPT
The logon script is executed.

0x00000002
ADS_UF_ACCOUNTDISABLE
The user account is disabled.

0x00000008
ADS_UF_HOMEDIR_REQUIRED
The home directory is required.

0x00000010
ADS_UF_LOCKOUT
The account is currently locked out.

0x00000020
ADS_UF_PASSWD_NOTREQD
No password is required.

0x00000040
ADS_UF_PASSWD_CANT_CHANGE
The user cannot change the password.

Note  You cannot assign the permission settings of PASSWD_CANT_CHANGE by directly modifying the UserAccountControl attribute. For more information and a code example that shows how to prevent a user from changing the password, see User Cannot Change Password.

0x00000080
ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
The user can send an encrypted password.

0x00000100
ADS_UF_TEMP_DUPLICATE_ACCOUNT
This is an account for users whose primary account is in another domain. This account provides user access to this domain, but not to any domain that trusts this domain. Also known as a local user account.

0x00000200
ADS_UF_NORMAL_ACCOUNT
This is a default account type that represents a typical user.

0x00000800
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT
This is a permit to trust account for a system domain that trusts other domains.

0x00001000
ADS_UF_WORKSTATION_TRUST_ACCOUNT
This is a computer account for a computer that is a member of this domain.

0x00002000
ADS_UF_SERVER_TRUST_ACCOUNT
This is a computer account for a system backup domain controller that is a member of this domain.

0x00004000
N/A
Not used.

0x00008000
N/A
Not used.

0x00010000
ADS_UF_DONT_EXPIRE_PASSWD
The password for this account will never expire.

0x00020000
ADS_UF_MNS_LOGON_ACCOUNT
This is an MNS logon account.

0x00040000
ADS_UF_SMARTCARD_REQUIRED
The user must log on using a smart card.

0x00080000
ADS_UF_TRUSTED_FOR_DELEGATION
The service account (user or computer account), under which a service runs, is trusted for Kerberos delegation. Any such service can impersonate a client requesting the service.

0x00100000
ADS_UF_NOT_DELEGATED
The security context of the user will not be delegated to a service even if the service account is set as trusted for Kerberos delegation.

0x00200000
ADS_UF_USE_DES_KEY_ONLY
Restrict this principal to use only Data Encryption Standard (DES) encryption types for keys.

0x00400000
ADS_UF_DONT_REQUIRE_PREAUTH
This account does not require Kerberos pre-authentication for logon.

0x00800000
ADS_UF_PASSWORD_EXPIRED
The user password has expired. This flag is created by the system using data from the
Pwd-Last-Set attribute and the domain policy.

0x01000000
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
The account is enabled for delegation. This is a security-sensitive setting; accounts with this option enabled should be strictly controlled. This setting enables a service running under the account to assume a client identity and authenticate as that user to other remote servers on the network.

So if you have something like 512, that is actually 0x200 or 1000000000 (base 2) which looking at the list above translates to ADS_UF_NORMAL_ACCOUNT… Ok so a normal user account. 514 is 0x202 or 1000000010 (base 2) which is ADS_UF_NORMAL_ACCOUNT combined with ADS_UF_ACCOUNTDISABLE… A disabled account. See how easy?

Now the hard part is that you want to update that value but you don’t want to screw up what might already be there… For example, say you want to enable a user account, what value do you put in… Some may say right off (and incorrectly) to write the value 512 to the attribute. That may work some or most or none of the time… What if the account is set up to use a smart card?? The proper value then would be 262656 (0x40200 or 1000000001000000000 (base 2)).

So the smart, or proper, way to handle these types of attributes, attributes such as userAccountControl, options, searchFlags, systemFlags, etc is to do bitwise math when setting or clearing a value. This is the AND/OR/XOR/NOT stuff that you may have glossed over if you had a computer science class that actually tried to teach it…

Anyway you don’t really have to understand how it works in order to modify it properly. Just use adfind and admod together…

Say I want to enable an account with a samaccountname of joewareupdatesvc I would run a command like

adfind -default -f samaccountname=joewareupdatesvc useraccountcontrol -adcsv | admod useraccountcontrol::{{.:CLR:2}}

or to disable the account you would run

adfind -default -f samaccountname=joewareupdatesvc useraccountcontrol -adcsv | admod useraccountcontrol::{{.:SET:2}}

That way, regardless of the current settings, you wouldn’t mess anything up. You would do the one thing you wanted to do, enable or disable the account.

This isn’t special magic just for userAccountControl, adfind|admod can do that for any bit flag attribute. The idea is that you have AdFind pipe the current value of the attribute to AdMod so it can take that info, manipulate it, and then set the new value.

There are some special shortcuts in AdMod for this as well that do apply to userAccountControl, but in the background, they simply specify the commands above on your behalf, specifically you could use the -sc uacset, -sc uacclear, -sc ad-disable, -sc ad-enable shortcuts… Something like

adfind -default -f samaccountname=joewareupdatesvc useraccountcontrol -adcsv | admod -sc ad-enable

or

adfind -default -f samaccountname=joewareupdatesvc useraccountcontrol -adcsv | admod -sc ad-disable

 

Very easy… While I use the userAccountControl stuff a lot, I would say anymore my most common use of this is when playing around with schemas…. I am constantly flipping bits in the searchFlags attributes to enable/disable indexing for various attributes or other functions that are controlled by the bits in searchFlags (see http://msdn.microsoft.com/en-us/library/ms679765(VS.85).aspx). So when I am playing around and say need to index objectclass for a few minutes for a test I run

adfind -sc s:objectclass searchflags -adcsv | admod searchflags::{{.:SET:1}}

then when I want to go back to the previous setting…

adfind -sc s:objectclass searchflags -adcsv | admod searchflags::{{.:CLR:1}}

 

   joe

Rating 4.00 out of 5

3/4/2007

Protecting yourself from massive accidental deletes in Active Directory

by @ 4:33 pm. Filed under tech

Now I know that title caught your attention… Why? Because most of the folks reading this blog do something with Active Directory and this is a pain point in AD. An unnecessary pain point in my opinion, but a pain point none-the-less.

I bring it up because Tim Springston from PSS (or CSS, whatever you want to call them now. 🙂 brought it up over on his blog.

http://blogs.technet.com/ad/archive/2007/02/07/active-directory-in-longhorn-feature-feedback-requested.aspx

This is something that I have brought up on several occasions in the past and have submitted DCRs to MSFT about. The gist of the whole post is the idea of having a special check box on specific objects (in ADUC) that you have to uncheck to complete a delete. The implementation, as he mentions in the comments, is through a DENY ACE on the object for delete.

Now this is something, besides the checkbox in ADUC, that you can easily implement right now by yourself if you want that functionality. I don’t, in general, want MSFT spending lots of time on doing things that I can already set up for myself, especially when it isn’t a very comprehensive solution; as Tim admits, this won’t help subtree delete ops.

I would like to see MSFT solve this the way I have been pushing for for years, a better implementation of the delete functionality. This is something that we cannot do ourselves and would be comprehensive and EXTREMELY useful. It is something I have been asking for, again, for years both in ActiveDir Org posts as well as through submissions to MSFT for design changes.

The idea that I have been pushing is a staged delete process. Many, if not most, large companies already implement something like this manually to the best of their ability which is something many have problems with through mistakes or misunderstandings of how various apps will respond to the process.

The process most companies basically follow is that they move objects they would normally delete to some OU or container X. They disable the object and depending on the level of understanding they will set specific attributes to certain values to make it so apps like Exchange don’t have a complete identity crisis and soil their panties.

Many of you know the process as you implemented it and probably most of you have encountered issues with it that caused you to specifically tweak the process to work with xyz apps that you have running.

Some of you may even have put into place processes and procedures that specifically state “NO USER/GROUP OBJECT WILL EVER BE DELETED”… I have seen/heard of that as well. Then you have the de facto standards of no computer objects ever getting deleted and that is simply because no one is ever sure if they can be safely deleted.

 

My thoughts are that the delete process should be changed to a staged delete process. At a minimum I visualize four distinct configurable phases. It allows you to configure which object classes go through which phases and how long they hang out in each, plus you should have the ability to move objects through some of the phases quickly if need be – say for a test or some mass delete and you don’t want the normal tombstones to apply.

So what are the four stages? They are:

Stage 1: See you later alligator

In this phase, objects are marked as unusable by the directory in a similar way that objects are marked right now. Only they maintain all of their original attributes; including linked attributes. I could visualize this being implemented in a couple of ways. The first is via the standard tombstone/delete process now but you maintain all attributes. The second is a new container which is hidden like the deleted objects container and is avoided by the DS for things like group expansion when users log on for the case of say a deleted group. You have to use the show deleted objects control to check this new container out but can easily be added to say ADUC so admins could reach in and pull objects back out of it and drag them to another OU or right click and select restore to put the object back to where it was before the delete.

There is a new attribute added, something like mS-DS-DeletionStage or something like that. It will be set to 1 when the object is in this stage[1]. Also, you could use a mS-DS-WhenDeleted attribute to make things easier for LDAP or just read the metadata on the mS-DS-DeletionStage attribute to determine when the next stage of delete should occur. I would like to see that attribute myself as well, possibly, of a another attribute called mS-DS-WhenRecovered. Being able to find objects that have been recovered may be nice so you can go talk to the people who are deleting things that constantly need to be recovered. Oh yeah, we also want to see mS-DS-WhoDeleted and mS-DS-WhoRecovered and have something set other than “Administrators”. 🙂

This would be an OPTIONAL stage of delete, maybe you don’t want all objects to go here, but instead want them to a later delete stage immediately – say like AD Connection objects or maybe OUs which would get a tombstone period for this stage of 0 days. Or maybe you want different objects to be retained in this stage for different periods say like OU’s for 3 days but users for 365 days or even -1 days (i.e. keep in this stage forever). This would be configured with some new AD Schema definitions – however, it would be nice to have some overrides on a per object basis as well (mS-DS-DeletePolicy – which points to a custom defined delete policy which could also be used by the schema objects as I think about it) – say for any apps that like to use a generic object type like container for many of its objects but you normally don’t want those objects maintained.

By Default I would have the OS maintain user, computer, group objects for 30 days. That way even if someone isn’t aware enough to enable this, the primary objects of concern would still be kept.

 

Stage 2: Classic (or Don’t I know you?)

This would be a delete as we know it now where an object is immediately tombstoned upon receipt of the delete request. Attributes are scrubbed from the object as per the schema and internal hardcoding but only I would say add the ability to actually maintain link attributes still. This would be mS-DS-DeletionStage=2. Objects could be undeleted from here just like they are now, same processes, etc. Though with luck, you get back linked attributes. 🙂

I figure the vast majority of object types would hit this stage as their first stage as they would be set to skip stage 1.

The current tombstone lifetime would be used for this stage.

 

Stage 3: I seem to recall…

Another very optional stage that would be used only for very few object types I expect. This stage maintains a couple of key attributes that you cannot easily duplicate later if you need to recreate an object, namely the objectGuid, objectSid, sIDHistory, etc. I would recommend allowing customers to specify additional attributes to be maintained here as well like say sAMAccountName or whatever customs they never want to lose. Objects here can be recovered like regular tombstoned objects, you just have more stuff you have repopulate. On the positive side, if several months down the road you need to bring something back with the same old SID for some odd reason (yes I know you shouldn’t ever HAVE to but….) then you can relatively easily do so. I know of several companies that maintain deleted objects like this in directories outside of AD because they don’t ever want to lose the info in case they find a SID/GUID they can’t resolve or want to use sAMAccountNames as unique keys FOREVER so they need a way to check to make sure they don’t duplicate them. mS-DS-DeletionStage=3

Honestly I would only expect users and groups to go into this stage because security principals would be the primary need for this. By default I wouldn’t let anything into this category, admins would have to specifically choose to do this. I would expect objects that went into this stage would stay there indefinitely but if someone wanted a tombstone time on these to push them into stage 4 that would be fine too, maybe 3,5,10 year values?

The tricky thing here is that you couldn’t manually push an object from this stage into stage 4 easily because there is nothing to pass along to  the other DCs after doing so. I almost wonder too if these objects should actually pop back out of the deleted phase and reappear as another object type but viewable through normal means. I could visualize it being handy to be able to resolve these objects say for event logs, etc that have SIDs listed. How many times have you tried to resolve a SID only to come to the conclusion that the object must have been deleted? If you resolve and it comes out to be an object of type “user-Deleted” then you know for sure and you can even know the name of the user.

 

Stage 4: Hit the road Jack, don’t you come back no more no more no more no more…

This is the scavenge phase where the object is yanked out of the DS and no longer retrievable which happens at the end of the tombstone lifetime now.

 

Examples

So I have a few examples, that is where you flesh out many issues…

Ex 1:

Default production environment spun up anywhere. Default settings for user objects Stage1 TSL=30, Stage2 TSL=120, Stage3 TSL=0. Ten users get deleted, go into Stage1. Three of the users get restored in 2 weeks as accidental deletes, all of the work handled in ADUC with a simple move of the object from one container to another OR a right click and Restore op. Objects are exactly as before. In 30 days, the remaining 7 users go to Stage2 and get stripped down to attribute set as per schema. 120 days later, objects are scavenged from directory (Stage4).

Ex 2:

Test lab which is configured for speedy cleanup because of small size and huge churn in add/deletes of users. Tweaked settings for user objects Stage1 TSL=0, Stage2 TSL=1, Stage3 TSL=0. Ten users get deleted, go into Stage2 and get scrubbed. A day later the objects get scavenged (Stage4).

Ex 3:

Production environment tweaked such that user delete stages are configured as Stage1 TSL=365, Stage2 TSL=120, Stage3 TSL=-1. Ten users get deleted, users can be fully restored for up to 1 year. This means that if you delete 1000 users per month, you need to have DIT capacity to maintain your normal active users + 12,000 inactive users. After one year, the objects are moved into Stage2 and scrubbed of all attributes not specifically marked to be maintained in this stage. Finally after that 120 Stage2 period ends, the objects are further scrubbed down to just a couple of attributes like objectGuid, objectSid, sIDHistory, sAMAccountName, and name and will always exist from then on unless the configuration is changed later. This means that after 10 years at 1000 user deletes a month, your DIT would have 120,000 inactive users in your directory which assuming no sIDHistory and SAM Name and Name being 20 characters or less and my math hasn’t taken a complete right turn would be around 10MB of information. That is a pretty small cost IMO.

 

Do we need Microsoft for this?

No. We don’t strictly need MSFT for this… This could be implemented via a vendor. However to do so would really involve screwing with the DS pretty deeply. The vendor would have to intercept all of the delete ops which is pretty darn intrusive. It could be done by someone who has very strong understanding of AD and history of doing things similar. Quest comes immediately to mind. ;o)  Microsoft would be EXTREMELY unhappy about it though. I would be very slow to recommend someone implement something like this from a third party vendor. Like I said, I expect Microsoft would be extremely unhappy if someone did this. I would love to see MSFT step up and do this, I think it would help one of the more common issues out there that people end up running auth restores for and IMO, auth restores are dangerous for people to be doing.

I have walked people through setting up processes to implement this procedurally but seriously but they have to jump special hoops and it is kind of a pain. This would be great to have built in because then you don’t have to worry about whether people are following procedure or not. If they use ADUC or dsrm or admod or NET USER /DELETE it doesn’t matter, the rules would be applied.

 

   joe

 

[1] 0 or null if not in a deletion stage at all – i.e. normal object.

Rating 3.00 out of 5

8/11/2006

How do I: Find all accounts that will expire on such and such a day with AdFind plus some chatter about -e.

by @ 6:02 pm. Filed under tech

This question came in the newsgroups today and I thought it was a good one to show off an AdFind feature that you won’t find duplicated in many places… The ability to specify dates for those nasty Int8 fields…

The question was (paraphrased)….

How do I find all accounts that have a specific Account Expires Date?

Right off I need to explain something. This isn’t asking what accounts will have expired passwords on such and such a day, instead it is asking what accounts are configured to expire on a specific day. Account expiration and account password expiration are two entirely separate things. So now that that is out of the way…

AdFind as of, I think, V1.31.00 got two -binenc option modifiers added to it, one called LOCAL and the other called UTC. You can find the info on these modifiers and that switch when you do adfind /??? and look at [QUERY OPTIONS]. It says the following:

   -binenc       Transform filter elements to proper format:
                    {{GUID:guid value}} converts to LDAP format of binary.
                    {{SID:sid value}} converts to LDAP format of binary.
                    {{BIN:hex string}} converts to LDAP format of hex binary.
                    {{UTC:YYYY/MM/DD-HH:MM:SS}} converts to int8 of UTC date/time.
                    {{LOCAL:YYYY/MM/DD-HH:MM:SS}} converts to int8 of Local date/time.

A little secret that isn’t documented is that you don’t have to specify the time if you just want to specify a date. So if you need to find all accounts set to expire on 8/26/2006 for instance, the following query will do it.

adfind -default -tdcs -binenc -f “&(objectcategory=person)(accountexpires>={{LOCAL:2006/08/26}})(accountexpires< ={{LOCAL:2006/08/27}})" samaccountname accountexpires

 This query has the following switches

  • -default : look at the default NC of the default domain controller
  • -tdcs : Decode int8 time attributes into a sortable time format
  • -binenc : Encode binary attributes marked for replacement in the query

The filter itself is

“&(objectcategory=person)(accountexpires>={{LOCAL:2006/08/26}})(accountexpires< ={{LOCAL:2006/08/27}})" The & says take all of these separate pieces of the query and return to me the intersection of the results

  • (objectcategory=person) : contacts or users – you do this so you use an indexed attribute. You don’t have to worry about contacts as they don’t expire so they won’t have the attribute accountexpires set to anything…
  • (accountexpires>={{LOCAL:2006/08/26}}) : Convert the value 2006/08/26 to an int8 format, BTW, that is in my localtime, not UTC (GMT) and then give me a list of all accounts that have a value of that or greater.
  • (accountexpires< ={{LOCAL:2006/08/26}}) : Convert the value 2006/08/26 to an int8 format and then give me a list of all accounts that have a value of that or less.
  • Note, it should be obvious but that filter will not work in any other LDAP query tool alive. I don’t think anyone has started using my “language” for specifying filter metadata.

    The query returns the following attributes

    • sAMAccountName : Pre-W2K UserID
    • accountExpires: Date and time that the account expires (this is translated from int8 because of -tdcs)

     

    Here is a sample run:

    F:\>adfind -default -tdcs -binenc -f “&(objectcategory=person)(accountexpires>={{LOCAL:2006/08/26}})(accountexpires< ={{LOCAL:2006/08/27}})" samaccountname accountexpires -e AdFind V01.31.00cpp Joe Richards (joe@joeware.net) March 2006 Transformed Filter: &(objectcategory=person)(accountexpires>=128010384001650000)(accountexpires< =128011248001650000) Using server: 2k3dc02.joe.com:389 Directory: Windows Server 2003 Base DN: DC=joe,DC=com dn:CN=joe,OU=MailUsers,OU=joeware2,OU=Exchange,DC=joe,DC=com >accountExpires: 2006/08/26-01:00:00 Eastern Standard Time
    >sAMAccountName: joe
    1 Objects returned

    If you want that in CSV format, simply tack on the CSV switch

    F:\>adfind -csv -default -tdcs -binenc -f “&(objectcategory=person)(accountexpires>={{LOCAL:2006/08/26}})(accountexpires< ={{LOCAL:2006/08/27}})" samaccountname accountexpires -e "dn","samaccountname","accountexpires" "CN=joe,OU=MailUsers,OU=joeware2,OU=Exchange,DC=joe,DC=com","joe","2006/08/26-01:00:00 Eastern Standard Time"

    Oh you don’t want the DN, tack on the -nodn switch

    F:\>adfind -nodn -csv -default -tdcs -binenc -f “&(objectcategory=person)(accountexpires>={{LOCAL:2006/08/26}})(accountexpires< ={{LOCAL:2006/08/27}})" samaccountname accountexpires -e "samaccountname","accountexpires" "joe","2006/08/26-01:00:00 Eastern Standard Time"

     Oh, if you are paying really close attention, you probably notices the -e switch in there too…. This is one of my favorite switches that I use in a vast majority of my queries. It is the environment switch, what it does is that it looks at the environment variables currently defined and imports them into the current switches. My number one purpose in doing this is to point at various different Active Directory and ADAM directories… So to see what it did to the command above, use the -po switch which Prints Out the switches and parameters specified… Added to the command above in my current command prompt Window and it outputs the following extra info

    Selected Switches
        -binenc
        -csv
        -default
        -e
        -f &(objectcategory=person)(accountexpires>={{LOCAL:2006/08/26}})(accountexpires< ={{LOCAL:2006/08/27}})     -h 2k3dc02     -nodn     -po     -tdcs Selected Attributes     samaccountname     accountexpires

    Notice the -h 2k3dc02 that I didn’t specify on the command line? That is the only extra switch being picked up here.

    The -e switch is documented in adfind /??? under the [MISC OPTIONS] section and this is what it says:

       -e xxx        Load switches from environment. Will read env vars with prefix
                     and dash (adfind-) by default and load them in. Any switches
                     specified explicitly on the command line will override. To
                     specify a different prefix, specify string after -e. For
                     example to specify the host switch create an env var of
                     adfind-h. To specify properties specify the env var adfind-
                     or adfind-props. To specify a switch that doesn’t take a
                     a value, specify a value of {~} because you can’t set a
                     an environment variable to blank.
                        Ex: Queries ADAM on localhost port 5000 for subnets.
                           set adam1-h=.:5000
                           set adam1-config={~}
                           set adam1-f=objectcategory=subnet
                           set adam1-props=name siteobject
                           adfind -e adam1

    By default, I always have my primary AD/Exchange environment info set in the ADFIND-* variables. That way if I add a simple -e I don’t have to enter the hostname to get to that environment or even the authentication info if it is different[1]. So if you look at the adfind- variables right now you see

    F:\>set adfind-
    adfind-h=2k3dc02

    I also usually have A1 through A9 environment variables set for various ADAM instances I have running on different machines; these are used by specify -e A1 through -e A9. I also have an AB environment variable set that point at an Address Book ADAM instance which is used with -e AB.

    There is also a -ef option if you rather put the settings in a file.

    Like I said, I love the -e option, it makes for much less typing when I need to…

       joe 

      

    [1] I actually use CPAU to fire up a command prompt running in the security context of that environment as well so I don’t have to specify the userid/password in the environment variables. 

    Rating 3.50 out of 5

[joeware – never stop exploring… :) is proudly powered by WordPress.]