Lately I have to explain to one of our customers how to create attribute in Active Directory which can be protected with additional permissions from reading its content. Such possibility was introduced in Windows 2003 SP1 but when I looked for some information to point our customer to I didn’t found much documentation so I decided to blog about it. What are confidential attributes? These are ordinary Active Directory schema attributes for which confidentiality bit was set in searchFlags (I explain how to do that later in this post). Result of this operation is that Read permission is not enough for a user to read this attribute content. Such possibility can be useful if You need to store in AD some value which should not be readable by everyone who has an access to the object which contains this attribute. In my customer scenario this was a number called PESEL – in Poland this is number assigned to every citizen which lets You identify a person. In this scenario my customer wanted to store this number in AD for the purpose of data synchronization between different directories, but polish law forces organizations to protect such sensitive data. So this data can be visible only for a user, a specified subset of admin stuff and synchronization services account. First we have to extend the user object class with a new attribute, in our case we called it lb-UserPesel, below You will find simple LDIF file with this attribute definition (I used oidgen.exe to generate OIDs for this example but in production environment You should not use it. Instead of this You should request OIDs pool from Microsoft using MSDN page):
dn: CN=lb-UserPesel,CN=Schema,Cn=Configuration,DC=domain, DC=PL
changetype: add
objectClass: attributeSchema
lDAPDisplayName: lb-UserPesel
adminDescription: This attribute stores user’s sensitive personal number
attributeID: 1.2.840.113556.1.4.7000.233.28688.28684.8.223249.327329.421971.1060153
attributeSyntax: 2.5.5.3
oMSyntax: 27
isSingleValued: TRUE
showInAdvancedViewOnly: TRUE
This introduces new attribute in our AD schema. Next we have to create auxiliary class which will contain this attribute and will let us to extend standard user class with this attribute. Below is an example of simple LDIF file which will define auxiliary class called lb-SensitiveData which will contain our lbUserPesel attribute:
dn: CN=lb-SensitiveData,CN=Schema,Cn=Configuration,DC=Domain,DC=PL
changetype: add
objectClass: classSchema
lDAPDisplayName: lb-SensitiveData
adminDescription: This auxiliary class contains user’s sensitive data definitions
governSID: 1.2.840.113556.1.5.7000.111.28688.28684.8.265102.1812148.2063820.1081313
mayContain: 1.2.840.113556.1.4.7000.233.28688.28684.8.223249.327329.421971.1060153
objectClassCategory: 3
subClassOf: top
defaultHidingValue: TRUE
showInAdvancedViewOnly: TRUE
Last step is to extend user class auxiliary classes list with our newly created class:
dn: CN=User,CN=Schema,Cn=Configuration,DC=domain,DC=PL
changetype: modify
add: auxiliaryClass
auxiliaryClass: lb-SensitiveData
Now we can make our attribute confidential. To do this we have to set bit 0x80 (decimal 128) in searchFlags attribute value to 1. If this attribute has some value already of course we have to perform OR operation with previous value of searchFlags for this attribute. In our example we can view current searchFlags value with ldp.exe:
Expanding base 'CN=lb-UserPesel,CN=Schema,CN=Configuration,domain,DC=pl'...
Getting 1 entries:
>> Dn: CN=lb-UserPesel,CN=Schema,CN=Configuration,DC=domain,DC=pl
2> objectClass: top; attributeSchema;
1> cn: lb-UserPesel;
1> distinguishedName: CN=lb-UserPesel,CN=Schema,CN=Configuration,DC=domain,DC=pl;
1> instanceType: 0x4 = ( IT_WRITE );
(…)
1> searchFlags: 0x0 = ( );
1> lDAPDisplayName: lb-UserPesel;
(…)
1> objectCategory: CN=Attribute-Schema,CN=Schema,CN=Configuration,DC=franz,DC=era,DC=pl;
Because it is empty now we can add new value of 128 with LDP.EXE (see image below).
We can check if our attribute has correct bit set performing bitwise AND search against Your schema. To do this You have to search in Your schema partition with some tool using:
searchFlags:1.2.840.113556.1.4.803:=128
as a search criteria. String ‘1.2.840.113556.1.4.803' performs bitwise AND operation ,and just to give You additional information ‘1.2.840.113556.1.4.804' performs bitwise OR operation for specified attribute. Using LDP.exe (see image below) You should be able to find all the attributes with confidential bit set.
OK, so now we have confidential attribute defined in our schema and we have to make it readable for specified persons. The right to read content of confidential attribute is controlled with CONTROL_ACCESS permission. Security principal which should be able to read this should has to have READ_ATTRIBUTE and CONTROL_ACCESS set on specific object. Please notice that confidential bit is set on the attribute in the schema, but rights to read should be controlled on the object level, not schema permissions. To set this on the attribute itself one should define new structural class not auxiliary because auxiliary class doesn’t has its own permissions. It inherits permission from the structural class in which it is used.
The sad news is that standard Windows GUI would not support You with this operation, but in fact … who cares about GUI if we have cool tools like DSACLS or LDP., You can set CONTROL_ACCESS right using DSACLS.EXE or LDP.EXE but the version of LDP must be from Windows 2003 R2 install disk). LDP.EXE. DSACLS syntax to set this permission on container or object is:
dsacls <dn> /G <security principal>:ca;<attrName>;
Using LDP.EXE (notice that this is LDP.EXE taken from Windows 2003 R2 CD) You have to select container or object, right click it and choose Advanced -> Security descriptor with SACL option (see image below).
And this should be all folks. I strongly recommend to make all these changes in test environment first – changing the schema and dealing with permissions in AD is very sensitive operation so testing before going into production will be a good idea.