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 lb-
UserPesel 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.