Sometimes it’s nice to know what’s happening under the hood, so let’s talk about how Group Policy is built, by tearing down how to access a particular policy. First, Group Policy is implemented in 2 parts, an LDAP part and a file part, delivered via SMB (CIFS if you’re oldschool) via DFS (Distributed FIle SYstem). Because the DFS part is replicated completely differently than the AD part, there’s a version number for each Group Policy object that’s stored in both places to keep them in sync. Most GPO engines remember the last version they applied by remembering the lowest of the 2 numbers (the LDAP version and the file version in the GPT.INI), if they don’t match.

Let’s talk about the “Default Domain Policy” which everyone will have one of. To find where that policy lives, you have to ask AD. The policy doesn’t actually live in the OU or Domain where it’s linked, so we have to back out the link:
ldap_search_s(ld, "dc=company,dc=com", 2, "(objectClass=organizationalUnit)", gpLink, base, &msg)
We’ll get back something like:

gPLink: [LDAP://CN={6AC1786C-016F-11D2-945F-00C04fB984F9},CN=Policies,CN=System,DC=company,Dc=com;0]

Now, this is a multi-valued array, because multiple GPOs can be linked, in order, to a single OU or Domain or Site. But we only care about this one, so let’s see what’s in it:

ldap_search_s(ld, "CN={6AC1786C-016F-11D2-945F-00C04fB984F9},CN=Policies,CN=System,DC=company,DC=com", 2, "(objectClass=*)", gPCMachineExtensionNames;gPCFileSysPath;displayName;versionNumber, 0, &msg)

That’ll get us the Client Side Extensions (where the work actually happens), and what the file path to the files in the estension are stored, as well as the pretty name of the Group Policy Object:

displayName: Default Domain Policy;
gPCFileSysPath: \\child1.lwtest.corp\sysvol\child1.lwtest.corp\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9};
gPCMachineExtensionNames: [{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{53D6AB1B-2488-11D1-A28C-00C04FB94F17}{53D6AB1D-2488-11D1-A28C-00C04FB94F17}{D02B1F72-3407-48AE-BA88-E8213C6761F1}][{827D319E-6EAC-11D2-A4EA-00C04F79F83A}{803E14A0-B4FB-11D0-A0D0-00A0C90F574B}][{B1BE8D72-6EAC-11D2-A4EA-00C04F79F83A}{53D6AB1B-2488-11D1-A28C-00C04FB94F17}];
versionNumber: 15;

So we have the Default Domain Policy, as desired, but there are a bunch of client side extensions here. It’d be nice to know what they all do generically, without having to inspect each one.
And TechNet delivers on that desire: a list of all Client Side Extensions (in 2010) by GUID for easy reference. Now, I’m writing this, because someone asked where the Password Policy for the domain was stored. Well, that appears to be in: {827D319E-6EAC-11D2-A4EA-00C04F79F83A} Security, which our Default Domain policy applies. So, let’s go find the data!

One of the attributes in the list we last requested was gPCFileSysPath which returned a normal SMB share. If you browse to that share, you’ll see 3 objects:

  • A folder named “MACHINE”
  • A folder named “USER”
  • a file named “GPT.INI”

The GPT.INI will only have 2 lines:

[General]
Version=15

That’s the version number, that you can compare to the “versionNumber” property from the object. If they’re the same, you’re good. If not, your AD isn’t in sync.

In the “MACHINE” Folder are all the Computer Policy settings, and in the “User” folder are all the User Policy settings. Since we were talking about the Password Policy, which is affected on the SAM on the server, it’s a MACHINE setting. If you were to poke through, eventually you’d find this file:

\\domain\sysvol\domain\\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf

with this data:

[Registry Settings]
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\MaximumPasswordAge=4,15

And there’s your password policy, via LDAP and SMB only.

For a bit of additional background, when a computer processes this data, in this order, it will actually only apply CSEs from the gPCMachineExtensionNames that the computer recognizes and has DLLs (or whatever code, if it’s non-Microsoft vendor CSE) that can apply the CSE. This makes it technically safe to put multiple GPOs for multiple Operating sytsems on the same OU structure, knowing that the client computer won’t even bother downloading the files for un-recognized CSEs.

Now, that’s a lot of stuff to type into ldp.exe, how can we make a report on this a bit easier? Well, PowerShell could do it, but one of the products I work on is PowerBroker Open https://github.com/BeyondTrust/pbis-open and https://www.beyondtrust.com/products/powerbroker-identity-services-open/ which includes a CLI for browing ldap called “adtool”. With a bit of bash, we can list out all the group policy objects by name attached to a single OU:

$ cat report-gpos-by-ou.sh
GP=`adtool -a lookup-object --dn "$@" --attr gPLink`;
GPO=`echo $GP | sed -e 's/\[LDAP:\/\///g' -e 's/;[[:digit:]]\]/ /g'`
if [ -n "$GPO" ]; then
echo "";
echo "$OU";
for P in $GPO; do
G=`adtool -a lookup-object --dn "$P" --attr displayName`;
grep -q "$G" /tmp/gpos.txt;
if [ $? -ne 0 ]; then
echo $G >> /tmp/gpos.txt;
fi;
echo "$G";
done;
fi;
$ ./report-gpos-by-ou.sh "OU=Company,DC=domain,DC=com"
OU=Company,DC=domain,DC=com
PBUL Basics
GP-Preferences
$