Delen via


.MSG File Format, Rights Managed Email Message (Part 2)

In my last blog, I discussed the general format for an email message saved to disk by Outlook or a compatible email client. The .msg file format explained in MS-OXMSG, is a Compound File with storages and streams containing properties and data describing the email message. I referred to a type of email message called a “rights managed email message”. This is an email message that has been encrypted and compressed in such a way that any email client receiving the message via normal communication with an Exchange server, will not be able to perform certain operations on the contents unless the user logged into that client is authorized.

In this discussion, I will be giving a brief description of a rights managed email message as specified by MS-OXORMMS and then I will show some code snippets that I used to decompress and extract the email contents from such a message once located.

What is a rights managed email message?

A rights managed email message is one whose contents have been protected by a “use license” acquired from a rights management server. I won’t go into the details of rights management servers and the interaction between them and client applications, but here is a quick, simple description. The client application (Outlook in this case), initializes a relationship with a rights management server by following the steps outlined in MS-RMPR “Rights Management Services (RMS): Client-to-Server Protocol Specification”. This results in a DRM license that is an XrML stream to be stored in the email message and used by the receiving email clients. This use license determines the actions that can be done to and with the attached email message.

In addition to the use license, the contents of the original email message are encrypted and compressed and stored as an attachment in a wrapper email message.

How do I know if a .msg represents a rights managed message?

As I alluded to just now, an email client creating a rights managed message actually creates two messages. The first, is referred to as the wrapper message. This message looks just like any email message when stored in .msg format. It has storages and streams that conform to MS-OXMSG, with properties that describe things like the recipient, subject and content. For example:

clip_image002[4]

The tag 0x01CA001F in the __substg1.0_01CA001F stream name above represents the value for the sending mailbox owner’s name (section 2.1120 PidTagSenderName in MS-OXPROPS):

clip_image004[4]

Again, that’s me. But the point is that the wrapper contains all the required properties to be a proper email message. The one extra element that you will find is an attachment. This attachment will have the following properties:

PidTagAttachLongFilename – set to the value “"message.rpmsg"

PidTagAttachMimeTag – set to the value "application/x-microsoft-rpmsg-message"

This message.rpmsg attachment should be the only attachment but if there happen to be others, message.rpmsg must be the first one. Here’s what the data name property looks like for this attachment:

clip_image006[4]

The _substg1.0_3707001F stream contains the value for the 0x3707001F or PidTagAttachFilename, at offset 0x8400 which contains:

clip_image007[4]

So we know that we have the correct attachment.

Let’s take a look at the binary data in this attachment.

clip_image009[4]

Above we see the stream for property 0x37010102 or the PidTagAttachDataBinary property which is the data for the attachment:

clip_image011[4]

 


Some code to decompress the storage container

According to section 3.1.4.1.2.1 of MS-OXORMMS, the message.rpmsg fundamentally has two things, a signature which looks like this:

0x76 0xE8 0x04 0x60 0xC4 0x11 0xE3 0x86 which translates to “vè`Ä㆔. This is garbage, I just wanted to make sure you were following me and hadn’t fallen asleep.

The second part of this binary data is the compressed contents of storage container with the original email message that was being protected. The contents are compressed using the ZLIB compression specified in RFC1950. The best way to deal with this content is to use the freely downloaded ZLib library with your own code to decompress these contents and find out what is stored within. Below is an example of code that calls the ZLib library functions. This is excerpt code and I’m assuming you can interpolate the obvious parts of the code like variable declarations not listed, etc…

First compare the bytes to make sure you have the signature, then take one block at a time and call inflate() on it. Each block is denoted by 0x0FA0 marker:

#define ZLIB_BUFFER_SIZE (4 * 1024)

#define ZLIB_DRM_HEADER_MAGIC (0x0FA0)

static const char c_szCompressedDrmMessageHeader[] = "\x76\xE8\x04\x60\xC4\x11\xE3\x86";

CHAR szHeader[sizeof(c_szCompressedDrmMessageHeader)] = {0};

streamIn=fopen(argv[paramFileIn], "rb");

fread(szHeader, 1, sizeof(szHeader)-1, streamIn);

if (strcmp(szHeader, c_szCompressedDrmMessageHeader) != 0)

{

printf("Not a DRM message header\n"

return -1;

}

z_stream zcpr;

ZLIBDRMHEADER theHeader = {0};

int ret=Z_OK;

long lOrigDone = 0;

int step=0;

memset(&zcpr,0,sizeof(z_stream));

inflateInit(&zcpr);

streamOut=fopen(argv[paramFileOut], "wb");

while (true)

{

ZeroMemory(rgbOriginal, sizeof(rgbOriginal));

if (fread(&theHeader, sizeof(theHeader), 1, streamIn) == 0)

{

printf("read failed");

break;

}

if (theHeader.ulCheck != ZLIB_DRM_HEADER_MAGIC)

{

printf("Header Check failed!");

return -3;

}

if (fread(rgbCompressed, theHeader.cbCompressed, 1, streamIn) == 0)

{

printf("read failed");

return -4;

}

zcpr.next_in = rgbCompressed;

zcpr.next_out = rgbOriginal;

zcpr.avail_in = theHeader.cbCompressed;

zcpr.avail_out = sizeof(rgbOriginal);

do

{

ret=inflate(&zcpr,Z_SYNC_FLUSH);

step++;

} while (ret==Z_OK);

fwrite(rgbOriginal, 1, theHeader.cbUncompressed, streamOut);

}

}

Once you have inflated the compressed data in the attachment stream as shown in the example code above, you will have the storage container for the original message along with some additional properties related to rights management. The format of the container is detailed in section 3.1.4.1.3 of MS-OXORMMS, “Format of the Storage Container”. In the example rights managed email message that I’ve used for this blog, the resulting storage container structure looks like this:

clip_image013[4]

Notice that this has all the storages and streams specified in section 3.1.4.1.3 of MS-OXORMMS. Two of the most important are DRMContent and DRMTransform\Primary streams. The former is the encrypted body of the original email message and the latter contains the transformation information on how to decode it. Looking at the DRMTransform\Primary stream, we see:

clip_image015[4]

Notice the “XrML…” string. This is XrML format XML data that tells us how to get access to the original email message and what rights we have once we decode it. XrML is a standard language used to specify rights on many kinds of digital content and an overview of the language can be found in the XrMLTechnicalOverview PDF file. I will reserve explanation of XrML and the subsequent decoding and rights discernment in this sample message to another installment of this blog. The encrypted contents of the original email message are shown here:

clip_image016[4]

And as you can see this is encrypted.

Comments

  • Anonymous
    November 17, 2010
    I'm implementing an email client, which is RMS capable. Using RMS API's I'm able to open and decrypt the DRMContent stream. However - I've hard time figuring out what it is. The stream has 00 10 00 00 00 00 00 00 at the begining, and then it looks very similar to *.msg file, although it is not (i.e. even after removing those 8 bytes, Outlook can't open it). I want to be able to both open and emit RMS protected messages. Is the format of the DRMContent stream documented somewhere?