Each time I think I’ve covered what I want to for Azure Information Protection (AIP), I think of another fun topic to explore. In this post I’m going to look at how AIP can be used to share information with users that exist outside your tenant. We’ll be looking at the scenario where an organization has a requirement to share protected content with another organization that has an Office 365 tenant.
Due to my requirements to test access from a second tenant, I’m going to supplement the lab I’ve been using. I’m adding to the mix my second Azure AD tenant at journeyofthegeek.com. Specific configuration items to note are as follows:
- The tenant’s custom domain of journeyofthegeek.com is an Azure AD (AAD)-managed domain.
- I’ve created two users for testing. The first is named Homer Simpson (homer.simpson@journeyofthegeek.com) and the second is Bart Simpson (bart.simpson@journeyofthegeek.com).
- Each user have been licensed with Office 365 E3 and Enterprise Mobility + Security E5 licenses.
- Three mail-enabled security groups have been created. The groups are named The Simpsons (thesimpsons@journeyofthegeek.com), JOG Accounting (jogaccounting@journeyofthegeek.com), and JOG IT (jogit@journeyofthegeek.com).
- Homer Simpson is a member of The Simpsons and JOG Accounting while Bart Simpson is a member of The Simpsons and JOG IT.
- Two additional AIP policies have been created in addition to the Global policy. One policy is named JOG IT and one is named JOG Accounting.
- The Global AIP policy has an additional label created named PII that enforces protection. The label is configured to detect at least one occurrence of a US social security number. The document is protection policy allows only members of the The Simpsons group to the role of viewer.
- The JOG Accounting and JOG IT AIP policies have both been configured with an additional label of either JOG Accounting or JOG IT. A sublabel for each label has also been created which enforces protection and restricts members of the relevant departmental group to the role of viewer.
- I’ve repurposed the GIWCLIENT2 machine and have created two local users named Bart Simpson and Homer Simpson.
Once I had my tenant configuration up and running, I initialized Homer Simpson on GIWCLIENT2. I already had the AIP Client installed on the machine, so upon first opening Microsoft Word, the same bootstrapping process I described in my previous post occurred for the MSIPC client and the AIP client. Notice that the document has had the Confidential \ All Employees label applied to the document automatically as was configured in the Global AIP policy. Notice also the Custom Permissions option which is presented to the user because I’ve enabled the appropriate setting in the relevant AIP policies.
I’ll be restricting access to the document by allowing users in the geekintheweeds.com organization to hold the Viewer role. The geekintheweeds.com domain is associated with my other Azure AD tenant that I have been using for the lab for this series of posts. First thing I do is change the classification label from Confidential \ All Employees to General. That label is a default label provided by Microsoft which has an RMS Template applied that restricts viewers to users within the tenant.
One interesting finding I discovered through my testing is that the user can go through the process of protecting with custom permissions using a label that has a pre-configured template and the AIP client won’t throw any errors, but the custom permissions won’t be applied. This makes perfect sense from a security perspective, but it would be nice to inform the user with an error or warning. I can see this creating unnecessary help desk calls with how it’s configured now.
When I attempt to change my classification label to General, I receive a prompt requiring me to justify the drop in classification. This is yet another setting I’ve configured in my Global AIP policy. This seems to be a standard feature in most data classification solutions from what I’ve observed in another major vendor.
After successfully classifying the document with the General label protection is removed from the document. At this point I can apply my custom permissions as seen below.
I repeated the process for another protected doc named jog_protected_for_Ash_Williams.docx with permissions restricted to ash.williams@geekintheweeds.com. I packaged both files into an email and sent them to Ash Williams who is a user in the Geek In The Weeds tenant. Keep in mind the users in the Geek In The Weeds tenant are synchronized from a Windows Active Directory domain and use federated authentication.
After opening Outlook the message email from Homer Simpson arrives in Ash William’s inbox. At this point I copied the files to my desktop, closed Outlook, opened Microsoft Word and used the “Reset Settings” options of the AIP client, and signed out of my Office profile.
At this point I started Fiddler and opened one of the Microsoft Word document. Microsoft Word pops-up a login prompt where I type in my username of ash.williams@geekintheweeds.com and I’m authenticated to Office 365 through the standard federated authentication flow. The document then pops open.
Examining the Fiddler capture we see a lot of chatter. Let’s take a look at this in chunks, first addressing the initial calls to the AIP endpoint.
If you have previous experience with the MSIPC client in the AD RMS world you’ll recall that it makes its calls in the following order:
- Searches HKLM registry hive
- Searches HKCU registry hive
- Web request to the RMS licensing pipeline for the RMS endpoint listed in the metadata attached to the protected document
In my previous deep dives into AD RMS we observed this behavior in action. In the AIP world, it looks like the MSIPC client performs similarly. The endpoint we see it first contacting is the Journey of the Geek which starts with 196d8e.
The client first sends an unauthenticated HTTP GET to the Server endpoint in the licensing pipeline. The response the server gives is a list of available SOAP functions which include GetLicensorCertificate and GetServerInfo as seen below.
The client follows up the actions below:
- Now that the client knows the endpoint supports the GetServerInfo SOAP function, it sends an unauthenticated HTTP POST which includes the SOAP action of GetServerInfo. The AIP endpoint returns a response which includes the capabilities of the AIP service and the relevant endpoints for certification and the like.
- It uses that information received from the previous request to send an unauthenticated HTTP POST which includes the SOAP action of ServiceDiscoveryForUser. The service returns a 401.
At this point the client needs to obtain a bearer access token to proceed. This process is actually pretty interesting and warrants a closer look.
Let’s step through the conversation:
- We first see a connection opened to odc.officeapps.live.com and an unauthenticated HTTP GET to the /odc/emailhrd/getfederationprovider URI with query strings of geekintheweeds.com. This is a home realm discovery process trying to the provider for the user’s email domain.
My guess is this is MSAL In action and is allowing support for multiple IdPs like Azure AD, Microsoft Live, Google, and the like. I’ll be testing this theory in a later post where I test consumption by a Google user.
The server responds with a number of headers containing information about the token endpoints for Azure AD (since this is domain associated with an Azure AD tenant.)
- A connection is then opened to odc.officeapps.live.com and an unauthenticated HTTP GET to the /odc/emailhrd/getidp with the email address for my user ash.williams@geekintheweeds.com. The response is interesting in that I would have thought it would return the user’s tenant ID. Instead it returns a JSON response of OrgId.
Since I’m a nosey geek, I decided to unlock the session for editing. First I put in the email address associated with a Microsoft Live. Instead of OrgId it returned MSA which indicates it detects it as being a Microsoft Live account. I then plugged in a @gmail.com account to see if I would get back Google but instead I received back neither. OrgId seems to indicate that it’s an account associated with an Azure AD tenant. Maybe it would perform alternative steps depending on whether it’s MSA or Azure AD in future steps? No clue.
- Next, a connection is made to oauth2 endpoint for the journeyofthegeek.com tenant. The machine makes an unathenticated requests an access token for the https://api.aadrm.com/ in order to impersonate Ash Williams. Now if you know your OAuth, you know the user needs to authenticate and approve the access before the access token can be issued. The response from the oauth2 endpoint is a redirect over to the AD FS server so the user can authenticate.
- After the user successfully authenticates, he is returned a security token and redirected back to login.microsoftonline.com where the assertion is posted and the user is successfully authenticated and is returned an authorization code.
- The machine then takes that authorization code and posts it to the oauth2 endpoint for my journeyofthegeek.com tenant. It receives back an Open ID Connect id token for ash.williams, a bearer access token, and a refresh token for the Azure RMS API.
Decoding the bearer access token we come across some interesting information. We can see the audience for the token is the Azure RMS API, the issuer of the token is the tenant id associated with journeyofthegeek.com (interesting right?), and the identity provider for the user is the tenant id for geekintheweeds.com.
- After the access token is obtained the machine closes out the session with login.microsoftonline.com and of course dumps a bunch of telemetry (can you see the trend here?).
- A connection is again made to odc.officeapps.live.com and the /odc/emailhrd/getfederationprovider URI with an unauthenticated request which includes a query string of geekintheweeds.com. The same process as before takes place.
Exhausted yet? Well it’s about to get even more interesting if you’re an RMS nerd like myself.
Let’s talk through the sessions above.
- A connection is opened to the geekintheweeds.com /wmcs/certification/server.asmx AIP endpoint with an unauthenticated HTTP POST and a SOAP action of GetServerInfo. The endpoint responds as we’ve observed previously with information about the AIP instance including features and endpoints for the various pipelines.
- A connection is opened to the geekintheweeds.com /wmcs/oauth2/servicediscovery/servicediscovery.asmx AIP endpoint with an unauthenticated HTTP POST and a SOAP action of ServiceDiscoveryForUser. We know from the bootstrapping process I covered in my last post, that this action requires authentication, so we see the service return a 401.
- A connection is opened to the geekintheweeds.com /wmcs/oauth2/certification/server.asmx AIP endpoint with an unauthenticated HTTP POST and SOAP action of GetLicensorCertificate. The SLC and its chain is returned to the machine in the response.
- A connection is opened to the geekintheweeds.com /wmcs/oauth2/certification/certification.asmx AIP endpoint with an unauthenticated HTTP POST and SOAP action of Certify. Again, we remember from my last post that this requires authentication, so the service again responds with a 401.
What we learned from the above is the bearer access token the client obtained earlier isn’t attended for the geekintheweeds.com AIP endpoint because we never see it used. So how will the machine complete its bootstrap process? Well let’s see.
- A connection is opened to the journeyofthegeek.com /wmcs/oauth2/servicediscovery/servicediscovery.asmx AIP endpoint with an unauthenticated HTTP POST and SOAP action of ServiceDiscoveryForUser. The service returns a 401 after which the client makes the same connection and HTTP POST again, but this time including its bearer access token it retrieved earlier. The service provides a response with the relevant pipelines for the journeyofthegeek.com AIP instance.
- A connection is opened to the journeyofthegeek.com /wmcs/oauth2/certification/server.asmx AIP endpoint with an authenticated (bearer access token) HTTP POST and SOAP action of GetLicensorCertificate. The service returns the SLC and its chain.
- A connection is opened to the journeyofthegeek.com /wmcs/oauth2/certification/certification.asmx AIP endpoint with an authenticated (bearer access token) HTTP POST and SOAP action of Certify. The service returns a RAC for the ash.williams@geekintheweeds.com along with relevant SLC and chain. Wait what? A RAC from the journeyofthegeek.com AIP instance for a user in geekintheweeds.com? Well folks this is supported through RMS’s support for federation. Since all Azure AD’s in a given offering (commercial, gov, etc) come pre-federated, this use case is supported.
- A connection is opened to the journeyofthegeek.com /wmcs/licensing/server.asmx AIP endpoint with an uauthenticated HTTP POST and SOAP action of GetServerInfo. We’ve covered this enough to know what’s returned.
- A connection is opened to the journeyofthegeek.com /wmcs/licensing/publish.asmx AIP endpoint with an authenticated (bearer access token) HTTP POST and SOAP action of GetClientLicensorandUserCertificates. The server returns the CLC and EUL to the user.
After this our protected document opens in Microsoft Word.
Pretty neat right? Smart move by Microsoft to take advantage and build upon of the federated capabilities built into AD RMS. This is another example showing just how far ahead of their game the product team for AD RMS was. Heck, there are SaaS vendors that still don’t support SAML, let alone on-premises products from 10 years ago.
In the next few posts (can you tell I find RMS fascinating yet?) of this series I’ll explore how Microsoft has integrated AIP into OneDrive, SharePoint Online, and Exchange Online.
Have a great week!