Troubleshooting AD RMS – Client Side

As our long journey through AD RMS comes to a close, I wanted to touch on troubleshooting the service. While putting together the lab for the deep dive I ended up doing a ton of troubleshooting. The goal of this post is to pass that knowledge on in hopes it will save someone some time in the future.

When troubleshooting an AD RMS activation, consumption, or creation of protected documents, it’s best to start at the client.

Troubleshooting the AD RMS Client

  1. This may seem like an obvious troubleshooting step, but it is often overlooked.  Verify that the user has an email address assigned to the identity they are using to access the document.
  2. When troubleshooting the AD RMS client, your first step should be to clear the AD RMS cache and reset the AD RMS client. These two Microsoft articles do a wonderful job of outlining the steps:
    1. Reset the AD RMS Client (Office 2010)
    2. Reset the MSIPC AD RMS Client (Office 2013)

    Resetting the client according to the articles above will clear out those cached registry entries that were created during previous activations and consumption operations. It’s possible for these entries to become corrupt or outdated for some reason and they will be properly recreated during the next client activation. The other step the articles have you taking is clearing out cached keys (CLC, RAC, etc). By clearing out all the keys the client will process through the whole activation cycle.

    If activation is still failing, test on another client after resetting that client as well. If activation works on the second client, then you know there’s an issue with the first client. Move to the next step.

  3. Next let’s verify the AD RMS server information wasn’t hardcoded within the client’s registry. Maybe it was done for some troubleshooting purpose in the past or perhaps an SCP wasn’t used previously. The registry entries below are worth checking.  Keep in mind if you’re using a 64-bit Office client, you’ll want to check the SoftwareWow6432Node node.
    1. HKLMSoftwareMicrosoftMSDRMServiceLocationActivation – This is a hardcoded location for client activation.
    2. HKLMSoftwareMicrosoftMSDRMServiceLocationEnterprise – This is the hardcoded location to obtain a CLC.
    3. HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2010.
    4. HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2013.

    If you found any of these registry, talk to your administrator to see why things are being hardcoded. If you determine it’s remnants of previous troubleshooting, clear out the entries, reset the client, and try again.

  4. Still not working? Remember when I mentioned earlier about the importance AD RMS places on CRL checking? The AD RMS client will inherit the CRL checking configuration that is set in Internet Explorer. If you determine you can’t reach or validate the certificate distribution point in the AD RMS root cluster’s cert or SSL cert, disable CRL checking in Internet Explorer. Check out this article for details. Make sure that you uncheck both “check for server certificate revocation” and “check for publisher’s certificate revocation” options.Once that is complete, reset the client and try again.
  5. Still having issues? Well my friend, you have exhausted the easier troubleshooting steps and now you need to do some client side debug tracing. You can utilize the sample debug traces I provided in my deep dive to see what a normally functionomg client debug trace looks like. If you’ve taken the time to read through the previous posts, the issue should immediately pop out to you in the debug trace.

My next blog post will explore how to troubleshoot at the server end.

Unlocking the black box that is AD RMS Part 5

All right folks, this will be the final scenario that we cover.

Scenario:This scenario will involve protecting a document and assigning Read rights to Group3@contoso.local, which is a distribution group in contoso.local containing as a member acontact object representing UserGG from fabrikam.local. The contact object will be populated with UserGG’s email address of UserGG@fabrikam.local.
Author: UserA@contoso.local
Consumer: UserGG@contoso.local
Captures:Scenario 4 Captures
Let’s begin…

  1. Client searches registry to see if service connection point (SCP) has been hardcoded. It checks the following keys:
    • HKLMSoftwareMicrosoftMSDRMServiceLocationActivation – This is a hardcoded location for client activation.
    • HKLMSoftwareMicrosoftMSDRMServiceLocationEnterprise – This is the hardcoded location to obtain a CLC.
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2010.
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2013.
  2. Client contacts contoso.local AD RMS cluster via the licensing pipeline (https://adrms.contoso.local/_wmcs/licensing) stored in the protected document and requests a EUL. Client is redirected to license.asmx and acquisition of EUL begins.
  3. AD RMS Server in contoso.local makes the following queries to the global catalog in contoso.local:
    1. Resolve the group membership for the UserGG contact object in the contoso.local domain.
      • Filter: (& (| (mail=UserGG@fabrikam.local)(proxyAddresses=smtp:UserGG@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return the contact object in the contoso.local domain that exists with the UserC@fabrikam.local email address.  This search also allows the AD RMS server to verify UserC isn’t AKA UserA@contoso.local.

    2. Check contoso.local to see if a user, computer, or contact object exists with the SID of the consumer (UserGG@fabrikam.local).
      • Filter: ( & ( | (objectSid=ConsumerSID) (sIDHistory=ConsumerSID) ) ( | (objectCategory=Computer) (objectCategory=Person) ) )
        Attributes: mail,objectSid,sIDHistory,proxyAddresses,memberOf,primaryGroupID,distinguishedName,uSNChanged,msExchOriginatingForest,msExchDynamicDLBaseDN, msExchDynamicDLFilter,userPrincipalName,sAMAccountName

      This search will return zero results as no such object exists.  This search also is being used to check whether or not the authenticated user is AKA Group3@contoso.local

    3. Check contoso.local to see if a user, computer, or contact object exists with the consumer’s email address (UserGG@fabrikam.local).
      • Filter: (& (| (mail=UserGG@fabrikam.local)(proxyAddresses=smtp:UserGG@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return the contact object in the contoso.local domain that exists with the UserGG@fabrikam.local email address. This contact object is used to represent UserGG from the fabrikam.local domain.  This search also is being used to check whether or not email address represented in the consumer’s RAC is linked to an object with the group2@contoso.local email address.

    4. Check contoso.local to see whether or not Group2@contoso.local is a valid group.
      • Filter: (& (| (mail=group3@contoso.local)(proxyAddresses=smtp:group3@contoso.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return Group3 distribution group object in the contoso.local domain.  This check is being used to determine if the Group3 is a valid group.

    5. Check contoso.local for consumer’s (UserGG@fabrikam.local) SID to see if a user, computer, or contact object exists in the contoso.local domain with the MemberOf attribute populated with group3@contoso.local’s SID.
      • Filter: ( & ( | (objectSid=ConsumerSID9) (sIDHistory=ConsumerSID) ) ( | (objectCategory=Computer) (objectCategory=Person) ) )
        Attributes: mail,objectSid,sIDHistory,proxyAddresses,memberOf,primaryGroupID,distinguishedName,uSNChanged,msExchOriginatingForest,msExchDynamicDLBaseDN, msExchDynamicDLFilter,userPrincipalName,sAMAccountName

      This search will return zero results as no such object exists.  This check is being used to determine if the authenticated user is a valid user in the contoso.local forest, which it is not.

    6. Check contoso.local for any group or contact object with msExchOriginatingForest (think group expansion here) that is a member of Group2.
      • Filter: (|(&(objectCategory=group)(memberOf=<GUID=<GroupGUID>))(&(objectCategory=contact)(memberOf=<GroupGUID>)(msExchOriginatingForest Present)))
        Attributes: ( mail )( distinguishedName )( msExchOriginatingForest )

      This search will return zero results as no such object exists.  This check is being used to determine whether the authenticated user is a member of the group.

    7. Resolve the group membership of the Group3 group object in contoso.local.
        • Filter: (& (| (mail=group3@contoso.local)(proxyAddresses=smtp:group3@contoso.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
          Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return Group3 distribution group object in the contoso.local domain.  This check is again used to determine if Group2 is a valid group.

    8. Resolve the group membership for the UserGG contact object in the contoso.local domain.
      • Filter: (& (| (mail=UserGG@fabrikam.local)(proxyAddresses=smtp:UserGG@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return the contact object in the contoso.local domain that exists with the UserGG@fabrikam.local email address.  This verified that the contact object representing UserGG is a member of Group3.

    9. Check contoso.local to see if a user, computer, or contact object exists with the SID of the consumer (UserGG@fabrikam.local).
      • Filter: ( & ( | (objectSid=ConsumerSID) (sIDHistory=ConsumerSID) ) ( | (objectCategory=Computer) (objectCategory=Person) ) )
        Attributes: mail,objectSid,sIDHistory,proxyAddresses,memberOf,primaryGroupID,distinguishedName,uSNChanged,msExchOriginatingForest,msExchDynamicDLBaseDN, msExchDynamicDLFilter,userPrincipalName,sAMAccountName

      This search will return zero results as no such object exists.  This check is is being used to verify whether or or not the authenticated user is AKA the publisher (userA@contoso.local).

    10. Resolve the group membership for the UserGG contact object in the contoso.local domain.
      • Filter: (& (| (mail=UserGG@fabrikam.local)(proxyAddresses=smtp:UserGG@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return the contact object in the contoso.local domain that exists with the UserGG@fabrikam.local email address.  This check is being used to verify whether or not the contact object representing UserGG is AKA as the publisher (userA@contoso.local).

    11. Check to see whether the publisher (UserA@fabrikam.local) is a valid group.
      • Filter: (& (| (mail=UserA@contoso.local)(proxyAddresses=smtp:UserA@contoso.local))(|(objectcategory=group)(objectcategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return zero results as UserA is not a group or contact object with msExchOriginatingForest populated.

    12. Run the same query again for some reason.
      • Filter: (& (| (mail=UserA@fabrikam.local)(proxyAddresses=smtp:UserA@fabrikam.local))(|(objectcategory=group)(objectcategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )

      This search will return zero results as UserA is not a group or contact object with msExchOriginatingForest populated.

  4. After confirming group membership with the 9th LDAP query listed in the previous step, AD RMS Server in contoso.local issues EUL and sends back to client in fabrikam.local.
  5. Client receives EUL, consumes it, and contents are displayed in Microsoft Office application with appropriate rights applied.

I found a few articles from Microsoft stating that msDS-SourceObjectDN was required when doing group membership in this manner. The articles were specific to Microsoft Exchange IRM, so it is possible additional queries are performed when Microsoft Exchange is involved. For the purposes above, the attribute was not required for consumption.

Unlocking the black box that is AD RMS Part 4

For this point forward, we will be using an AD RMS client that has already activated and obtained a rights account certificate.

Scenario:This scenario will involve protecting a document and assigning Read rights to Group1, which is a distribution group in fabrikam.local containing UserC. We will also create a contact object in contoso.local representing Group1. We will populate the mail attribute with Group1.fabrikam.local and the msExchOriginatingForest attribute with the value of fabrikam.local for this contact object. The msExchOriginating forest is an indicator to the AD RMS service that the contact object represents a group in another forest and that the AD RMS service needs to contact the AD RMS cluster in that forest to start group expansion.
Author: UserA@contoso.local
Consumer: UserC@contoso.local
Captures: Scenario 3 Captures

Let’s begin…

  1. Client searches registry to see if service connection point (SCP) has been hardcoded. It checks the following keys:
    • HKLMSoftwareMicrosoftMSDRMServiceLocationActivation – This is a hardcoded location for client activation.
    • HKLMSoftwareMicrosoftMSDRMServiceLocationEnterprise – This is the hardcoded location to obtain a CLC.
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2010.
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2013.
  2. Client contacts contoso.local AD RMS cluster via the licensing pipeline (https://adrms.contoso.local/_wmcs/licensing) stored in the protected document and requests a EUL. Client is redirected to license.asmx and acquisition of EUL begins.
  3. AD RMS Server in contoso.local makes the following queries to the global catalog in contoso.local:
    1. Check contoso.local to see if a user object has both the author’s email address (UserA.contoso.local) and the consumer’s email address (UserC@contoso.local).
      • Filter: (& (| (mail=UserC@fabrikam.local)(proxyAddresses=smtp:UserC@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    2. Check contoso.local to see if a distribution list or contact object with msOriginatingForest exists in contoso.local that matches UserC’s SID and has the group1@fabrikam.local email address. I’m not sure how this query would ever ring true, because the SID it is querying with is the SID it retrieved from the authenticated user. Maybe for SIDHistory?
      • Filter: (& (| (objectSID=)(sIDHistory=)(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    3. Check contoso.local to see if a user object exists with both UserC@fabrikam.local and Group1@fabrikam.local as an email address. This is checking to see whether a user object has both consumer’s email address and the distribution group email address.
      • Filter: (& (| (mail=UserC@fabrikam.local)(proxyAddresses=smtp:UserC@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    4. Check contoso.local to see whether or not Group1@fabrikam.local is a valid group. This query comes back as successful since Group1@fabrikam.local is a valid contact object with msOriginatingForest populated.
      • Filter: (& (| (mail=group1@fabrikam.local)(proxyAddresses=smtp:group1@fabrikam.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    5. Check contoso.local for consumer’s SID to see whether or not the consumer is a valid user in the contoso.local forest.
      • Filter: ( & ( | (objectSid=S-1-5-21-2502964694-2714205513-579894926-1109) (sIDHistory=S-1-5-21-2502964694-2714205513-579894926-1109) ) ( | (objectCategory=Computer) (objectCategory=Person) ) )
        Attributes: mail,objectSid,sIDHistory,proxyAddresses,memberOf,primaryGroupID,distinguishedName,uSNChanged,msExchOriginatingForest,msExchDynamicDLBaseDN, msExchDynamicDLFilter,userPrincipalName,sAMAccountName
  4. AD RMS Server in contoso.local consumer msOriginatingForest from Group1 contact object and query global catalog in fabrikam.local for AD RMS SCP.
  5. Domain controller in fabrikam.local responds with AD RMS SCP.
  6. AD RMS Server in contoso.local contacts certification pipeline in fabrikam.local AD RMS cluster (https://adrms.fabrikam.local/certification/) Server.asmx and requests a service location for group expansion.
  7. AD RMS Server in fabrikam.local responds with group expansion service URL (https://adrms.fabrikam.local/groupexpansion/groupexpansion.asmx).
  8. AD RMS Server in contoso.local contacts group expansion pipeline in fabrikam.local and request group expansion for Group1@fabrikam.local.
  9. AD RMS Server in fabrikam.local queries global catalog in fabrikam.local with the following queries:
    1. Query to find the SID of group1@fabrikam.local. This is done to gather the SID, which will then be used when checking group membership of the consumer.
      • Filter: (& (| (mail=group1@fabrikam.local(proxyAddresses=smtp:group1@fabrikam.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    2. Query to find the consumer’s user account (UserC@fabrikam.local) and then check to see if the SID of the distribution group (Group1@fabrikam.local) is included in the MemberOf attribute of the user.
      • Filter: (& (| (mail=UserC@fabrikam.local)(proxyAddresses=smtp:UserC@fabrikam.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    3. Query to discover the consumer’s (UserC@fabrikam.local) primary group. Again, not sure why this is done.
      • Filter: ( & (objectCategory=CN=Group,CN=Schema,CN=Configuration,DC=fabrikam,DC=local) (objectSid=S-1-5-21-2502964694-2714205513-579894926-513) )
        Attributes: distinguishedName
  10. AD RMS Server in fabrikam.local returns message to AD RMS Server in contoso.local that consumer (UserC@fabrikam.local) is valid member of the distribution group (group1@fabrikam.local).
  11. AD RMS Server in contoso.local then performs the following queries against the global catalog in contoso.local.
    1. Check to see if contoso.local has a distribution group or contact object with msExchOriginatingForest populated that has a SID matching the consumer’s SID (UserC@fabrikam.local) and has an email address of the author (UserA@contoso.local).
      • Filter: (& (| (objectSID=)(sIDHistory=)(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    2. Check to see if contoso.local has a user object that has both the consumer email address (UserC@fabrikam.local) and author’s email address (UserA@contoso.local).
      • Filter: (& (| (mail=UserC@fabrikam.local)(proxyAddresses=smtp:UserC@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    3. Check to see if the object associated with the author’s email address (UserA@contoso.local) is a group or contact object with msOriginatingForest populated.
      • Filter: (& (| (mail=UserA@contoso.local)(proxyAddresses=smtp:UserA@contoso.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    4. Run the same check as above to see if the object associated with the author’s email address (UserA@contoso.local) is a group or contact object with msOriginatingForest populated. Not sure why this is done twice.
      • Filter: (& (| (mail=UserA@contoso.local)(proxyAddresses=smtp:UserA@contoso.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
  12. AD RMS Server in contoso.local issues EUL and sends back to client in fabrikam.local.
  13. Client receives EUL and saves it to C:UsersUserNameAppDataLocalMicrosoftDRM for Office 2010 and C:UsersUserNameAppDataLocalMicrosoftMISPC for Office 2013.

Unlocking the black box that is AD RMS Part 3

Let’s continue and we’ll introduce the second forest with its own AD RMS cluster.

Scenario: Client InterForest Activation and Consumption of Protected Content. In this scenario UserA in contoso.local (UserA@contoso.local) has protected a document and given UserB in fabrikam.local (UserB@fabrikam.local) the Read right. The steps below describe UserB’s process to activate, certify, and consume the content protected by UserA.
Author: UserA@contoso.local
Consumer: UserB@fabrikam.local
Captures: Scenario 2 Captures

  1. Client searches registry to see if service connection point (SCP) has been hardcoded. It checks the following keys:
    1. HKLMSoftwareMicrosoftMSDRMServiceLocationActivation – This is a hardcoded location for client activation.
    2. HKLMSoftwareMicrosoftMSDRMServiceLocationEnterprise – This is the hardcoded location to obtain a CLC.
    3. HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2010.
    4. HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2013.
  2. Client contacts contoso.local AD RMS cluster via the licensing pipeline (https://adrms.contoso.local/_wmcs/licensing) stored in the protected document metadata and hits the ServiceLocator.asmx file and requests service location for certification.
  3. AD RMS Server in contoso.local checks for presence of GICURL registry entry in HKLMSoftwareMicrosoftDRMS. This registry entry is used when you opt not to use an AD RMS SCP. Presence of this entry will override SCP discovery for both local and cross-forest clients.
  4. When the entry is not found, the AD RMS Server in contoso.local identifies that user belongs to fabrikam.local since the user was forced to authenticate to access the ServiceLocator.asmx and then queries global catalog in fabrikam.local for AD RMS SCP and returns to the client (https://adrms.fabrikam.local/_wmcs/certification).
  5. Client receives back a referral to the fabrikam.local SCP (https://adrms.fabrikam.local/_wmcs/certification) and writes the information to a registry entry under the registry key:
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – For office 2010
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – For office 2013
  6. Client contacts contoso.local AD RMS cluster via the licensing pipeline (https://adrms.contoso.local/_wmcs/licensing) stored in the protected document metadata and hits the ServiceLocator.asmx file and requests service location for obtaining a CLC.
  7. AD RMS Server in contoso.local identifies that user belongs to fabrikam.local since the user was forced to authenticate to access the ServiceLocator.asmx returns the client’s AD RMS SCP for the clients’s domain to the client (https://adrms.fabrikam.local/_wmcs/certification).
  8. Client receives back a referral to the fabrikam.local SCP (https://adrms.fabrikam.local/_wmcs/certification) and writes the information to a registry entry under the registry key:
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – For office 2010
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – For office 2013
  9. Client then contacts the SCP URL returned in the previous step, hits the ServiceLocator.asmx, and requests a service location for obtaining a CLC.
  10. AD RMS Server in fabrikam.local returns the licensing pipeline (https://adrms.fabrikam.local/_wmcs/licensing) information to the client.
  11. Client receives back the licensing URL and writes it to a registry entry in the registry key:
      Office 2010

    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMCachedCorpLicenseServer
    • Office 2013

    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMCachedCorpLicenseServer
  12. Client connects to fabrikam.local licensing pipeline and is directed to ServiceLocator.asmx and requests a service location URL for activation (obtain copy of SLC public key and perform machine activation).
  13. Fabrikam.local AD RMS Server returns activation pipeline (http(s)://adrms.fabrikam.local/_wmcs/certification).
  14. Client writes this information to a series of registry entries in the following key:
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – For office 2010
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – For office 2013
  15. Client contacts fabrikam.local activation URL and hits Server.asmx to obtain a copy of the SLC’s public key.
  16. Client receives a copy of the SLC public key and performs machine activation generating a machine key in C:UsersUsernameAppDataLocalMicrosoftDRM.
  17. Client contacts fabrikam.local certification URL https://adrms.fabrikam.local/_wmcs/certification) and hits certification.asmx to obtain a RAC.
  18. AD RMS Server checks it’s database to see if it has a copy of the user’s RAC, if not it generates one using the following process.
    1. AD RMS Server queries a DC in its local domain for the user’s email address using the user’s SID as an identifier. The following query is used:
      • Filter = ( & ( | (objectSid=User’sSID) (sIDHistory=User’sSID) ) ( | (objectCategory=CN=Computer,CN=Schema,CN=Configuration,DC=domain,DC=com) (objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=domain,DC=com) ) )
        Attributes = mail,objectSid,sIDHistory,proxyAddresses,memberOf,primaryGroupID,distinguishedName,uSNChanged,msExchOriginatingForest,msExchDynamicDLBaseDN, msExchDynamicDLFilter,userPrincipalName,sAMAccountName
    2. AD RMS Server queries a DC in its local domain for the user’s primary group. I’m unsure as to why it does this. The debug logs say something about establishing immediate group membership. The following query is used:
      • Filter = ( & (objectCategory=CN=Group,CN=Schema,CN=Configuration,DC=domain,DC=com)(objectSid=PrimaryGroup’sSID) )
        Attributes = distinguishedName
    3. AD RMS Server generates a RAC with the ID field populated with the user’s email address and sends back to user.
  19. Client receives RAC from fabrikam.local AD RMS server and saves it to C:UsersUserNameAppDataLocalMicrosoftDRM for Office 2010 and C:UsersUserNameAppDataLocalMicrosoftMISPC for Office 2013.
  20. Client contacts fabrikam.local CLC Service URL (https://adrms.fabrikam.local/_wmcs/licensing) and hits publish.asmx and requests a CLC.
  21. Fabrikam.local AD RMS Server creates a CLC and sends it to the user.
  22. Client receives CLC from fabrikam.local AD RMS serverand saves it to C:UsersUserNameAppDataLocalMicrosoftDRM for Office 2010 and C:UsersUserNameAppDataLocalMicrosoftMISPC for Office 2013.
  23. Client contacts contoso.local AD RMS server and request EUL.
  24. AD RMS Server in contoso.local contacts global catalog in contoso.local and performs the following queries:
    1. Check to see if the user exists in contoso.local and has an email address of UserA@contoso.local. This check is essentially checking to see whether a user exists in the author’s forest that matches the consumer’s SID and the author’s email address. This check probably exists to see whether or not the consumer should be issued full rights (author rights) to the doc.
      • Filter: (& (| (objectSID=SIDOfAuthenticatedUser)(sIDHistory=SIDOfAuthenticatedUser)(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    2. Check to see if a user object exists in contoso.local with the UserC@fabrikam.local as an email address. This check is looking for a user object with both the UserC@fabrikam.local email address and the author’s email address (UserA@fabrikam.local). Again, probably exists for the same reason as the previous query.
      • Filter: (& (| (mail=UserC@fabrikam.local)(proxyAddresses=smtp:UserC@fabrikam.local))(|(objectcategory=computer)(objectcategory=person)))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    3. Check to see if the a distribution group or contact with msExchOriginatingForest attribute populated exists with the email address of UserC@fabrikam.local exists. This check is looking to see if the AD RMS server needs to examine group membership of a group local to the contoso.local domain or perform group expansion a group in another domain.
      • Filter: (& (| (mail=UserC@fabrikam.local)(proxyAddresses=smtp:UserC@fabrikam.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
    4. Run the same check as the previous step. Not sure why, debug log has the same message that it’s not a valid group.
      • Filter: (& (| (mail=UserC@fabrikam.local)(proxyAddresses=smtp:UserC@fabrikam.local))(|(objectCategory=group)(objectCategory=msExchDynamicDistributionList)(&(objectCategory=contact)(msExchOriginatingForest Present))))
        Attributes: ( mail )( objectSid )( sIDHistory )( proxyAddresses )( memberOf )( primaryGroupId )( distinguishedName )( uSNChanged )( msExchOriginatingForest )( msExchDynamicDLBaseDN )( msExchDynamicDLFilter )( userPrincipalName )( sAMAccountName )
  25. AD RMS Server in contoso.local consumes RAC and issues EUL.
  26. Client receives EUL and saves it to C:UsersUserNameAppDataLocalMicrosoftDRM for Office 2010 and C:UsersUserNameAppDataLocalMicrosoftMISPC for Office 2013.

Unlocking the black box that is AD RMS Part 2

Let’s begin shall we. I’ve included a copy of the captures I used to gather the information below.

Scenario: Client IntraForest Activation and Creation of Protected Content
Author: UserA@contoso.local
Consumer: UserB@contoso.local
Captures: Scenario 1 Captures

  1. Client searches registry to see if service connection point (SCP) has been hardcoded. It checks the following keys:
    1. HKLMSoftwareMicrosoftMSDRMServiceLocationActivation – This is a hardcoded location for client activation.
    2. HKLMSoftwareMicrosoftMSDRMServiceLocationEnterprise – This is the hardcoded location to obtain a CLC.
    3. HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2010.
    4. HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocation – This is where previously discovered service locations are stored for Office 2013.
  2. Client then queries contoso.local global catalog for an AD RMS SCP with the following query:
    • Filter: (& (objectClass=serviceConnectionPoint)(keywords=MSRMRootCluster)(keywords=1.0))
      Attributes: ( serviceBindingInformation )
  3. Domain controller in client’s domain returns the AD RMS SCP information. This value is normally http(s)://RMSClusterDNSName/_wmcs/certification.
  4. Client connects to SCP which exists in AD RMS server and is directed to ServiceLocator.asmx and requests a service location URL for obtaining a CLC.
  5. AD RMS returns the Contoso.local AD RMS cluster’s licensing pipeline which is normally set to http(s)://RMSClusterDNSName/_wmcs/licensing
  6. Client writes this information to the following registry entries/keys:
      Office 2010

    • HKCUSoftwareMicrosoftOffice14.0CommonDRMCachedCorpLicenseServer
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocations
    • Office 2013

    • HKCUSoftwareMicrosoftOffice15.0CommonDRMCachedCorpLicenseServer
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocations
  7. Client connects to SCP which exists in AD RMS server and is directed to ServiceLocator.asmx and requests a service location URL for activation (obtaining a copy SLC public key).
  8. AD RMS Server returns activation pipeline which is normally set to http(s)://RMSClusterDNSName/_wmcs/certification.
  9. Client writes this information to a series of registry entries in the following key:
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocations – Office 2010
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocations – Office 2013
  10. Client connects to SCP which exists in AD RMS server and is directed to ServiceLocator.asmx and requests a service location URL for performing certification (obtaining a RAC)
  11. AD RMS Server returns certification pipeline which is normally set to http(s)://RMSClusterDNSName/_wmcs/certification.
  12. Client writes this information to a series of registry entries in the following key:
    • HKCUSoftwareMicrosoftOffice14.0CommonDRMServiceLocations – Office 2010
    • HKCUSoftwareMicrosoftOffice15.0CommonDRMServiceLocations – Office 2013
  13. Client contacts Activation URL (which is normally http(s)://RMSClusterDNSName/_wmcs/certification) and hits Server.asmx to obtain a copy of the SLC’s public key.
  14. AD RMS Server returns a copy of the SLC’s public key to the client.
  15. Client receives a copy of the SLC public key and performs machine activation generating a machine key in C:UsersUserNameAppDataLocalMicrosoftDRM for Office 2010 and C:UsersUserNameAppDataLocalMicrosoftMISPC for Office 2013.
  16. Client contacts Certification URL (which is normally http(s)://RMSClusterDNSName/_wmcs/certification) and hits Certification.asmx to obtain a RAC.
  17. AD RMS Server checks it’s database to see if it has a copy of the user’s RAC, if not it generates one using the following process.
    1. AD RMS Server queries a DC in its local domain for the user’s email address using the user’s SID as an identifier. The following query is used:
      • Filter = ( & ( | (objectSid=User’sSID) (sIDHistory=User’sSID) ) ( | (objectCategory=CN=Computer,CN=Schema,CN=Configuration,DC=domain,DC=com) (objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=domain,DC=com) ) )
        Attributes = mail,objectSid,sIDHistory,proxyAddresses,memberOf,primaryGroupID,distinguishedName,uSNChanged,msExchOriginatingForest,msExchDynamicDLBaseDN, msExchDynamicDLFilter,userPrincipalName,sAMAccountName
    2. AD RMS Server queries a DC in its local domain for the user’s primary group. I’m unsure as to why it does this. The debug logs say something about establishing immediate group membership. The following query is used:
      • Filter = ( & (objectCategory=CN=Group,CN=Schema,CN=Configuration,DC=domain,DC=com)(objectSid=PrimaryGroup’sSID) )
        Attributes = distinguishedName
    3. AD RMS Server generates a RAC with the ID field populated with the user’s email address and sends back to user.
  18. Client receives RAC and saves it to C:UsersUserNameAppDataLocalMicrosoftDRM for Office 2010 and C:UsersUserNameAppDataLocalMicrosoftMISPC for Office 2013.
  19. Client contacts CLC Service URL (which is normally http(s)://RMSClusterDNSName/_wmcs/licensing and hits publish.asmx and requests a CLC.
  20. AD RMS Server creates a CLC and sends it to the user.
  21. Client receives CLC and saves it to C:UsersUserNameAppDataLocalMicrosoftDRM for Office 2010 and C:UsersUserNameAppDataLocalMicrosoftMISPC for Office 2013.

Client Activation and Creation of protected content is complete.

Unlocking the black box that is AD RMS Part 1

So you want to understand what’s going on under the hood of Active Directory Rights Management Service (AD RMS)?  You can try searching Microsoft TechNet and MSDN, and what you’ll find is somewhat disappointing. I’m not sure why, but Microsoft hasn’t done a great job of breaking AD RMS down into the nit and grit as they have done with other applications.

My goal over the next few weeks is to fill that gap. Over the past few days, I have been working to break open that black box and I want to share what I’ve learned. I will not be focusing on the basics of AD RMS, as I feel the following posts do a good job of that:

Still curious? Well then, let’s begin. This first post will focus primarily on the tools I used to gather the information I will be presenting.

Packet Capturing Tools

You can’t properly study a tool that has a client-server model without capturing that network traffic.  Normally I stick with Wireshark, but for this process I ended up using Microsoft Network Monitor (Netmon).  The reason for Netmon over Wireshark is Netmon will report the PIDs and process names of the applications creating the traffic. This was a big help in monitoring AD RMS on the servers as we can narrow the traffic we care about down to the IIS worker process. I also find the LDAP query parser in Netmon to be far superior to Wireshark.

Web Proxy Debugging Tool

With AD RMS transactions taking place over HTTP/HTTPS, it only makes sense to utilize a tool such as Fiddler. For those unfamiliar with Fiddler, it is a web proxy debugging tool that makes analyzing web transactions (both HTTP and HTTPS) a breeze. I began using it when I was digging into Federation a year or two back. I find myself using it on a daily basis as more and more apps begin to communicate primarily over HTTP/HTTPS.

I used Fiddler to capture the web transactions between the AD RMS client and the AD RMS server. It parses the packets so nicely, you can easily dig into the XrML of the RAC/CLC/SLC/EULs going back and forth. This was a huge aid in understanding what exactly an AD RMS certificate looks like.

AD RMS Client and Server Side Tracing

Client and server side tracing was an absolute necessity. This is the stuff you’ll want to take a look at if you really want to understand AD RMS. Microsoft has some wonderful articles that describe how to set it up. Keep in mind the client tracing is specific to Office 2010.

Event Logs and Diagnostic Logging

You can’t dig into a Microsoft product without examining event logs! The Directory Services log on the domain controllers are huge here. You’ll need to enable AD diagnostic logging to capture the LDAP queries being run by the AD RMS client and server. This article is a great place to learn more about turning on AD diagnostic logging.

Procmon

Do you hate yourself? If so, procmon is the tool for you! Procmon came in very handy when I needed know what registry keys the AD RMS client and server were hitting when looking for hardcoded settings.

Lab Environment

Last and most importantly, let me describe my lab environment. For this project I setup four servers and two clients. The server/client setup is detailed below. A two-way transitive forest trust was established between contoso.local and fabrikam.local. A two-way AD RMS Trusted User Domain between the separate AD RMS clusters.

SERVER01
OS: Windows Server 2008 R2 Standard
Domain: contoso.local
Roles: Active Directory Domain Services, Active Directory Certificate Services
Applications: Microsoft Network Monitor

SERVER02
OS: Windows Server 2008 R2 Standard
Domain: fabrikam.local
Roles: Active Directory Domain Services, Active Directory Certificate Services
Applications: Microsoft Network Monitor

SERVER03
OS: Windows Server 2008 R2 Standard
Domain: contoso.local
Roles: Active Directory Rights Management Services, Windows Internal Database, SQL Management Studio
Applications: Microsoft Network Monitor, Debugview

SERVER02
OS: Windows Server 2008 R2 Standard
Domain: fabrikam.local
Roles: Active Directory Rights Management Services, Windows Internal Database, SQL Management Studio
Applications: Microsoft Network Monitor, Debugview

CLIENT01
OS: Windows 7 Professional
Domain: contoso.local
Application: Microsoft Office 2010 Professional, Microsoft Network Monitor, Debugview, Fiddler

CLIENT02
OS: Windows 7 Professional
Domain: fabrikam.local
Application: Microsoft Office 2010 Professional, Microsoft Network Monitor, Debugview, Fiddler

When working with AD RMS, it is critical to remember the importance of certificate and certificate revocation checks. In a lab environment, I would suggest you make it easy on yourself and disable CRL checking for your client and server boxes according to this article. This will save you some headaches.

Do yourself a favor and disable AD RMS caching. It will save you significant time when trying to replicate the acquisition of a fresh RAC or EUL. This article describes the process. Also, if you opt to use a WID, check out this article for connecting to the WID using SQL Management Studio.

All right folks, that will do it for this post. Next up will be a breakdown of intraforest consumption of protected content. To prep yourself for the post, I would suggest reviewing the following articles: