Welcome to MSDN Blogs Sign in | Join | Help

Inside look at one of the domain controller promotion process using DCPromo

In order to add an additional domain controller to an existing domain, DCPromo must be used to complete the task.  The process will install the AD DS on the Windows Server, join the machine to the domain and replicate all partitions from the existing domain controller.  The steps for running DCPromo to promote a server to DC have been well documented.  They can be done either through the Windows Interface for user attended installation, or via an answer file for unattended installation. [1][2]   This blog will focus on what really happens after the user input is taken and the real installation starts.  Hopefully this can help readers have a better understanding of domain controller installation process, so that they can have effective debugging if any problem arises.

Preparing  for AD DS Installation

  • DCPromo starts by checking if the binaries for Active Directory Domain Services have been installed. This should be done when the AD DS role is added through Server Manager.
  • The process validates the supplied paths for NTDS, including path for NTDS DIT path and System Volume Root Path (SYSVOL). The paths must be on an NTFS volume and a fixed hard drive.
  • Time synchronization with the existing domain controller is forced.
  • Stops and configures the Netlogon Service on the server to be promoted.
  • Creates SYSVOL and copies the initial AD database to the installation path.
  • Validates the user input parameter from the interface or answer file.
  • Verifies the existing DS environment to ensure:
    • The server name doesn't already exist in the existing site as a valid RDN
    • The site specified by user parameter or automatically discovered exists
  • Sets the credentials for the replication.
  • Configures the local machine (Windows server) for AD DS installation:
    • Sets DS configuration parameters in registry. The location is System\CurrentControlSet\Services\NTDS\Parameters. Some of the parameters including Root Domain, Configuration NC, etc. will be used in the next phase of installation.
    • Configures the event logging categories.

Starting the AD DS installation

  • Read DS configuration from the registry.
  • Create the NTDS setting object for the new domain controller in the existing remote DC. The object can be added using the DRSR RPC interface.
  • Start replicating Schema NC from the remote DC. After the replication is successfully done, the schema is reloaded.
  • Start replicating Configuration NC from remote DC.
  • Start replicating Domain NC from remote DC.
  • Convert the machine account to a domain controller machine account so it can be replicated in the next step
  • Do a domain NC Critical Only replication which replicates the system-critical objects only, as documented in 7.1.1.3 MS-ADTS[3]
  • After replication of critical domain objects is completed, check to make sure there is no duplicated machine name in the updated DIT that has the same SAM account name. If so, it has to be renamed.
  • Update Replication Epoch on the NTDS setting object.
  • Set the computer's root domain name to the name of domain to join.
  • Delete intermediate registry keys used during installation.

Once the installation is complete, the machine will be required to reboot in order to complete the entire installation.  Again the process above is just for adding an additional domain controller to an existing domain, which is important in some interoperability scenarios when partners try to join a Windows domain controller into a non-Windows domain.  We can leave the other domain controller promotion processes , such as creating the first domain controller in the forest, creating a child domain or creating a Read-only DC to the future blogs.

References

[1]   "Steps for Installing AD DS", http://technet.microsoft.com/en-us/library/cc754438(WS.10).aspx

[2]  "How to use unattended mode to install and remove Active Directory Domain Services on Windows Server 2008-based domain controllers", http://support.microsoft.com/kb/947034

[3]  "[MS-ADTS]: Active Directory Technical Specification", http://msdn.microsoft.com/en-us/library/cc223122(PROT.10).aspx

Verifying the server signature in Kerberos Privilege Account Certificate

This blog post focuses on understanding how a server signature is verified in a Kerberos Privilege Account Certificate (PAC). A PAC contains two signatures: a server signature and a KDC signature. In a previous blog, I introduced PAC validation, whereby the server requests the KDC to verify the PAC. In this blog, I will talk about the verification of PAC server signature, more specifically how the verification is done even when the encryption/hashing types are different for the service ticket and the PAC server signature.

 

Background

The Privilege Account Certificate (PAC) is an extension element of the authorization-data field contained in the clients' Kerberos ticket.  The PAC structure is defined in [MS-PAC] and conveys authorization data provided by domain controllers (DCs) in an Active Directory-enabled domain. It contains information such as security identifiers, group membership, user profile information, and password credentials.

More details can be found in [MS-PAC] Section 2, which describes the Encapsulation layers.

One key reason why a PAC should be verified as unaltered is to ensure that no privileges have been maliciously added to - or removed from - the ticket; that is to prevent a man-in-the-middle from spoofing and tampering the PAC.

The signatures allow the KDC or the principal verifying the PAC to determine if the contents have been modified. The first signature is the server signature, or server checksum. The second is the KDC signature.

In the remainder of this post, I will focus on the server signature or server checksum in a PAC_INFO_BUFFER with the ultype of 0x00000006, as defined in [MS-PAC] 2.4.

Understanding the cryptographic system used by the KDC to generate the PAC server signature

The following logic reflects the testing of [MS-KILE] and MS-PAC implementations in Windows-based servers 2008 and 2008 R2.

By default, HMAC-RC4 is used to compute the PAC server signature, unless:

  • - the server key type is one the newer encryption types i.e. AES256 or AES128, and
  • - the functional level of the domain NC is Windows Server 2008 or 2008 R2, or the registry setting disables the bit of HMAC-RC4 in the SupportedEncryptionTypes (registry entry on the DC at the following location (see KB977321): HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\parameters\)

 

This logic can be explained as follows. First, as highlighted by Windows behavior note <26> in [MS-KILE], AES is supported in Windows 2008 and onward.

Second, there are several DCs on the field that have 2003 functional level. Even though some of these DCs are running Windows Server 2008 or 2008 R2, domain administrators tend sometimes to be conservative and keep the functional level to 2003.

Third, unless otherwise configured, a DC that is operating at Windows Server 2003 functional level will use the strongest encryption type that the client can support to issue a Kerberos service ticket (e.g. AES 256 for Windows 7). However, the PAC signature could be fairly computed with HMAC-RC4. This would be important to support of cross-domain or cross-forest scenarios.

 

Excerpt from MS-KILE

This is an excerpt from MS-KILE.

 

3.3.5.3.2.3   Server Signature

The KDC creates a keyed hash ([RFC4757]) of the entire PAC message with the Signature fields of both PAC_SIGNATURE_DATA structures set to zero using the server account key with the strongest cryptography that the domain supports<26> and populates the returned PAC_SIGNATURE_DATA structure ([MS-PAC] section 2.8) fields as follows:

 The SignatureType SHOULD be the value ([MS-PAC] section 2.8) corresponding to the cryptographic system used to calculate the checksum.

 The Signature field SHOULD be the keyed hash ([RFC4757]) of the entire PAC message with the Signature fields of both PAC_SIGNATURE_DATA structures set to zero.

 

Windows Behavior Note

<26> Section 3.3.5.3.2.3: Active Directories with the msDS-Behavior-Version attribute on a domain NC root object equal to DS_BEHAVIOR_WIN2000, DS_BEHAVIOR_WIN20003_WITH_MIXED_DOMAINS, or DS_BEHAVIOR_WIN2003, cannot support AES.

Examples

These examples assume the default settings are used on all servers and clients.

Example 1: Same cryptosystem

DC: Windows Server 2008 SP2, functional level Windows Server 2008

Kerberos Server: Windows Server 2008 SP1

Kerberos client: Windows 7 RTM

The Kerberos client is issued a Ticket with an encryption type Etype: aes256-cts-hmac-sha1-96 (18). For the CIFS service, the PAC in the in the service ticket has a SignatureType of 0x00000010 (HMAC_SHA1_96_AES256), or 16. This signature type corresponds to the aes256-cts-hmac-sha1-96 cryptosystem. The resulting checksum size is 12 bytes as defined in MS-PAC Section 2.8.1.

Example 2: Different cryptosystems

DC: Windows Server 2008 SP2, functional level Windows Server 2003

Kerberos Server: Windows Server 2008 SP1

Kerberos client: Windows 7 RTM

The Kerberos client is issued a Ticket with an encryption type Etype: aes256-cts-hmac-sha1-96 (18). For the CIFS service, the PAC in the in the service ticket has a SignatureType of 0xFFFFFF76 (KERB_CHECKSUM_HMAC_MD5), or a checksum type of -138. This signature type corresponds to the rc4-hmac-md5 cryptosystem. The resulting checksum size is 16 bytes as defined in MS-PAC Section 2.8.1.

Algorithm used to verify the PAC server signature

When the server verifies the PAC signature, it must use the same cryptosystem that the KDC used to generate the server checksum. The list of signature types and corresponding cryptographic systems are documented in MS-PAC 2.8.

The cryptosystem that is used to compute the server checksum does not depend on the key type of the long term key that the KDC shares with the server. For instance in the Example 2 above, the server signature type is KERB_CHECKSUM_HMAC_MD5, the same algorithm RC4-HMAC-MD5 will be used to generate and verify the signature in case of AES 128/256 long-term keys, or RC4-HMAC-MD5 long-term key. Any attempt to use a cryptosystem that does not correspond to the signature type will produce a different keyed hashed server checksum.

Per HMAC RFC 2104 (referenced by RFC 4757), B=64 bytes is used for the padding for both MD5 and SHA1. The key can be of any length up to B, the block length of the hash function. Since the AES256 key length is 256 bits (32 bytes), the key material will not be truncated but appended with 32 bytes of zeroes; for AES128 the padding would have been 48 bytes of zeros.

Conclusion

When generating the PAC server signature, the KDC uses the long-term key that it shares with the server ([MS-PAC] 2.8. 1). The server must always use the long term key (that is used to decrypt the service ticket) as an input to the keyed hash. This allows the server to verify the signature on receiving the PAC, based on the signature type and the corresponding cryptographic system.

References

[MS-KILE] Kerberos Protocol Extensions

[MS-PAC] Privilege Attribute Certificate Data Structure

[RFC4120] The Kerberos Network Authentication Service (V5)

[RFC4757] The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows

[RFC2104] HMAC: Keyed-Hashing for Message Authentication

 

Details of three TIFF Tag extensions that Microsoft Office Document Imaging (MODI) software may write into the TIFF files it generates

Microsoft Office Document Imaging (MODI) software includes specific tags/constants in the documents it generates, of which some are enumerated here.  In addition to these enumerated tags there are some undocumented TIFF tag extensions that MODI may write into TIFF documents.

The following information details three of these TIFF tags (37679, 37681, 37680), and you can find corresponding sample source files here, which are provided to aid in implementation.

Tag 37679 contains plain text reflecting the contents of the page image in the TIFF file. This text can either come from an OCR operation on the image or directly from the document content if the TIFF file was produced by the “Microsoft Office Document Image Writer” virtual printer.

Tag 37681 contains positioning information which describes where the text from Tag 37679 appears on the page, as well as information about the position of other objects such as images, tables, and hyphens. The information in this tag is used by the MODI application to enable its text selection feature.

Tag 37680 contains a binary dump of an OLE Property Set Storage. The complete specification of property set storages can be found here. An OLE Property Set Storage is a particular kind of Compound File. The complete specification of the Compound File Binary File Format can be found here.

The easiest way to understand the properties that can be stored in this tag and how to read them is to examine the provided sample source code.

The sample code makes use of the following Windows APIs to extract the data from the file into memory and map a Property Set Storage interface onto it. Developers who want to read this OLE property set storage on platforms other than Windows can use the above mentioned specifications to write their own APIs that perform equivalent functions. More documentation for each of these APIs may be found on MSDN.

· GlobalAlloc to allocate memory (the C runtime function malloc does not return an HGLOBAL and thus isn’t sufficient for this particular purpose. The sample code uses malloc when it can).

· GlobalLock to get a pointer from the handle returned by GlobalAlloc.

· GlobalUnlock after reading data from the file into the pointer returned by GlobalLock.

· CreateILockBytesOnHGlobal followed by StgOpenStorageOnILockBytes to wrap the data in an IStorage.

· StgCreatePropSetStg to get an IPropertySetStorage interface.

A Property Set Storage contains multiple Property Storages. These Property Storages are identified by GUIDs whose names in the code are prefixed with the string FMTID.

The sample code shows how to call the IPropertySetStorage::Open method to extract an IPropertyStorage for each FMTID. The sample code reads each property from the IPropertyStorage using the IPropertyStorage::ReadMultiple method.

The actual numeric values of the GUIDs are in the provided sample code. The four FMTIDs included in this Property Set Storage are:

· FMTID_EPAPER_PROPSET_DOC

· FMTID_NBDOC_PROPSET_DOC

· FMTID_EPAPER_PROPSET_IMAGEGLOBAL

· FMTID_SummaryInformation

FMTID_SummaryInformation is a standard Windows FMTID, and you can find its definition here.

The following information concerns the other three FMTIDs.

These are the properties in FMTID_EPAPER_PROPSET_DOC

Property ID

Type

Meaning

0x0002

VT_UI4

This value exists for backward compatibility with earlier products. Current implementations of MODI always write 0x0000, and so should any third-party producer of these files.

0x0006

VT_UI2

The Unicode character to be used when representing text that has been rejected by the OCR system, for example when coping from the MODI viewer and pasting into a plain text editor such as Notepad.

0x000F

VT_UI4

The number of errors found during the OCR process.

0x0010

VT_UI4

The quality of the OCR result, from 0 to 1000. Higher numbers indicate higher confidence.

0x0014

VT_UI4

The LCID under which OCR was performed. You can find a list of LCID constants here.

0x0015

VT_UI4

Number of paragraphs of text

0x0016

VT_UI4

Number of lines of text

0x0017

VT_UI4

Word count (including recognized and unrecognized words)

0x0018

VT_UI4

Character count (including recognized and unrecognized characters)

0x001B

VT_UI4

The major revision of the software that generated this file. MODI always writes 0x0002.

0x001C

VT_UI4

The minor revision of the software that generated this file. MODI always writes 0x0000.

0x001E

VT_STORAGE

This storage contains the contents of the File-Properties dialog from the Microsoft Office Document Imaging application. See the sample code functions PstgPropSetFromProp and DbgDumpStandardProps for an example of how to extract this property set and read it.

0x01F

VT_UI4

Status of OCR in this file. Must have one of the following values:

0: OCR was not done

1: OCR was completed

2: OCR was attempted

3: OCR found no text

The following are the properties of FMTID_NBDOC_PROPSET_DOC

Property ID

Type

Meaning

2

VT_BLOB

This structure is defined in the sample code, see the function HrReadStationaryData and the structure called NB_STATIONERY_DATA.

3

VT_R4

Page width, in inches

4

VT_R4

Page height, in inches

5

VT_UI2

Language of the document, found here.

6

VT_UI4

The major revision of the software that generated this file. MODI always writes 0x0002.

7

VT_UI4

The minor revision of the software that generated this file. MODI always writes 0x0000.

The following are the properties of FMTID_EPAPER_PROPSET_IMAGEGLOBAL

Property ID

Type

Meaning

2

VT_ISTREAM

This stream contains a dictionary that maps images used during OCR to the text it was recognized as.

3

VT_ISTREAM

This stream contains embedded fonts.

Reflecting on another successful Interoperability Lab event

Engagement with partners is an integral part of achieving interoperability with Windows. In addition to helping users of the Microsoft Open Protocol Specifications, our team participates in a regular basis in interoperability labs dedicated to specific areas of focus of our partners.

Sun Microsystems is one our team’s most active partners. Recently, I had the opportunity to take part in the Sun’s IO lab hosted in the Microsoft Enterprise Engineering Center (ECC). The event focused on testing Solaris implementation against Windows in the areas of remote file sharing with extended security support, and Microsoft Distributed File System. It was a great adventure for me throughout the week. In this blog, I am reflecting on the event and sharing my experiences and story as a supporting Escalation Engineer.  

The Sun IO event was held at the ECC from 10/26 to 10/30. Prior to the event, the Protocol Engineering management worked closely together with the ECC program management to plan and set up a testing environment agreed upon with Sun. The environment included two forests, four domains with different domain functional levels, and a myriad of clients. A team of five attendees came from Sun primarily to test Solaris CIFS service with extended security support, and standalone DFS server under development. In the file sharing space, one key objective was to test Kerberos PAC support with Solaris (client or server) against a matrix of Windows clients and servers. Sun also planned to leverage the Microsoft Protocol Test suites to test their current implementation of SMB, KILE, PAC and DFS.

The team felt pleased with the excellent lab environment setup by the EEC as it provides the flexibility to experiment various scenarios. Solaris servers were installed either on physical machines or on virtual machines. On the first day, after the morning meeting, Sun’s team even requested a third forest with a specific functional level and Windows service pack, and it was created. This forest will be used for the Kerberos PAC testing.

Following the initial meeting, engineers from both Microsoft and Sun worked in configuring the testing environment. Sun’s engineers joined their machines to the lab network dedicated for the event and began compiling their software. From our side, we helped troubleshooting network issues and identifying the latest patches and hot fixes required to join Solaris to Windows domains. Others worked on compiling MIT KDC on a Solaris server. Later during the day, I began gathering more details about initial questions raised by Sun’s engineers regarding SMB/CIFS. The questions aimed at clarifying certain SMB/CIFS interoperability aspects in Windows-based implementations compared to Linux, Solaris, or Posix-based implementations. After some quick research and engagement with Microsoft subject matter experts, we had very interesting discussions on the topics of interest. 

The next day, the testing activities were getting substantial. I was called upon for consulting on a pass-through authentication issue. I decided to collect and analyze the resulting network trace, and ETW trace. As we resolved this scenario, and ensured the correct principal was initiating the SMB session, we ran into an interesting question on SPNEGO whereby the negotiation hint from the Solaris server would not contain a NTLM OID. In such a scenario, if the optimistic mechanism did not succeed, the Windows client would close the connection but this solely depends on the error that occurs during the authentication initiation. 

From now on, till the end of Day 3, I worked with two of Sun’s engineers and debugged a Kerberos PAC issue. After some brainstorming, we were all under the impression that this issue would be challenging because the Solaris server has to use exactly the same inputs and the same cryptographic algorithm Windows KDC used to generate the PAC server signature. The first phase of the investigation plan was to analyze Windows source code. The second phase was to capture Time Travel Traces (TTT) from the Windows KDC. It has also been critical to instrument the Solaris code and trace some key material, and encryption types passed to different calls during the server side of PAC verification. Invariably, I used the data from the Solaris syslog, and ran live debugging with Windows source attached. After a few hours of analysis, I asserted that the MIT API that was being leveraged by the Solaris server was producing the wrong key hash for the server signature. One of Sun’s engineers conducted code analysis and confirmed this. We then referred to the MS-PAC and MS-KILE documents and discussed the expected logic.

On Day 4, following the meeting with the security team, I was assigned to test LDAP interoperability of SASL between Windows Active Directory and Solaris LDAP clients. I worked with another Test Suites engineer to test various scenarios. We observed the Solaris bind security layer negotiation behavior with Windows Server 2008 R2. We also confirmed that the default Windows DC settings would allow LDAP operations to complete successfully via SASL.

The final day of my week was dedicated to wrap-up the issues that I had been tracking throughout the week. The joint debugging and testing efforts between the Sun and Microsoft teams have been very productive, and raised a few interoperability issues from both sides.

I was really impressed by the teamwork from Sun and Microsoft engineers and the depth of the testing activities. I will be pleased to work with them in another interoperability event in the future.

To KVNO or not to KVNO, what is the version!?

 

Shakespeare knew nothing about Kerberos V5… Nothing! 

But, I still like him! And that, despite the fact that he had the audacity to paraphrase me in his play “Hamlet”. Of course no one believes me!

I must admit it would be much easier to convince you about this historic truth if I had been born about ~400 years BEFORE him.

But, oh well…

What I CAN probably convince you about is today’s topic… KVNO is not always as decisive as it’s thought to be.

Aha! I bet you did not see THAT one coming!

First things first: “What does KVNO stand for?”

KVNO stands for: Key Version Number.

Ok, good. Now: “What key are we talking about?”

Of course we are talking about the Client’s Secret Key, what did you expect?

Each machine on the network possesses a Long Term Key (Secret Key) that is used to authenticate with the KDC in order to obtain tickets and to encrypt those tickets when sending them within the AP_REQ.

Before you ask: “What’s an AP_REQ?”

The AP_REQ is the initial message that the client machine sends to the Server on the network in order to request Kerberos authentication and gain access to a specific Service on that Server. That service could be SMB, LDP or any service that the server has registered with the KDC and has a SPN.

Ok, now we need to understand: “What is the KVNO field for?”

The KVNO is a field on the AP_REQ that indicates what version of the key has been used to encrypt the service ticket.

This is where the KVNO is located when looking into the AP_REQ:

 

And, this is the description of the KVNO found in RFC4120:

   Key Version Number (kvno)

      A tag associated with encrypted data identifies which key was used

      for encryption when a long-lived key associated with a principal

      changes over time.  It is used during the transition to a new key

      so that the party decrypting a message can tell whether the data

      was encrypted with the old or the new key.

 

Now, if there’s a KVNO field, it is safe to assume that there could be different versions of the key.

That leads to the next question: “When and how is the long term key changed?”

The key is generated by an algorithm that derives it from the account password.

With account I’m referring to the Active Directory object that represents the client computer. That account has a password just like any user account but, the one big difference is that the password for this account is not known or set by any user. Instead it’s by default automatically reset every 30 days by the computer itself.

Now, as we stated before, the key is derived from the password so… yes, you are right, the key has to change every time that the password changes. As you might have already figured out, the KVNO is incremented by 1 when this happens.

The big question now is: “What would be a scenario where the key used to encrypt the ticket is different from the one that the server has stored?”

Well, we can be facing this scenario in the following situation:

1)      A Client obtains a valid ticket from the KDC (this ticket is kept and used until expired) and used the KVNO=5

2)      The client then renews its password (if default, 30 days have passed since last reset)

3)      KVNO will increase to 6 and change is picked up by the target server

4)      Ticket is still valid since it has not expired nor the machine has been rebooted or the cache purged

5)      Machine tries to access service on target server with ticket encrypted with KVNO=5

This scenario is fairly usual and it’s covered by the  RFC4120 as well:

The ticket is decrypted using the version of the server's key specified by the ticket.  If the decryption routines detect a modification of the ticket (each encryption system MUST provide safeguards to detect modified ciphertext), the KRB_AP_ERR_BAD_INTEGRITY error is returned (chances are good that different keys were used to encrypt and decrypt).

 

I bet you are thinking: Ooohhhh!!! So that’s when KVNO saves the day right!?

Well, yes and no.

Yes, it can be used for that. No, Windows does not pay attention to KVNO.

 It simply ignores it. Like if KVNO wasn’t even present (Editor’s Note: Any similarities with the life of the author are pure coincidence)

Windows server will follow these steps:

1)      It will try to decrypt the service ticket in the AP_REQ with the current key

2)      If it succeeds, it then sends the AP_REP to the client and the process moves forward

3)      But if it fails, it will then make its best effort and try to decrypt the ticket with the previous version of the key (KVNO-1)

4)      If it succeeds, AP_REP and process moves forward

5)      If it fails, it will fail the AP_REQ and send an AP_REP with  KRB_AP_ERR_MODIFIED

I can read your mind; I know you are eager to say: I got it! This is very interesting information! I should visit this blog more often!

Well, you will be even more surprised if I say to you: But wait! There’s more! If you read the next few lines in the next 30 seconds, you will also receive a wonderful CAVEAT!

And you reached this line so… here’s the caveat.

Not always, Windows has the chance to store the previous key (KVNO-1) and make its best effort to decrypt a ticket that was encrypted with an older key.

That will depend on a simple requirement:

In order for the server to store the previous version of a key, the password change for the computer account must have been done on that particular server.

What I mean with this is that the server that received the request and that processed the password change, saves the old password and can use it as the KVNO-1 key. The rest of the servers, do not have a KVNO-1 available and will fail the request with  AP_REP with  KRB_AP_ERR_MODIFIED after trying with the current key.

When the client receives a  AP_REP with  KRB_AP_ERR_MODIFIED, purges its tickets cache and requests a new ticket to the KDC. This time, the ticket will be encrypted with the current version of the key and the exchange would succeed.

One last juicy perk for having resisted your desire to close the browser and run away from me!

 A script to change the computer password on demand and thus be able to test all this details!

Or did you think that it was going to be as simple as right clicking somewhere?

Run this on the domain controller where you want to change the account password:

Dim objComputer

Set objComputer = GetObject("LDAP://CN=computername,CN=computers,DC=yourdomain,DC=yoursuffix")

objComputer.SetPassword "P@ssw0rd1"

Wscript.Quit

 

Hope you found this information useful!

Hasta luego!

 

.MSG File Format (Part 1)

In my previous two blog entries, I’ve focused on becoming familiar with the Compound File Binary Format which we discovered was similar to a FAT file system within a file.  With that exercise behind us we’re ready to step up a level in the ecology of file formats.  Analogous to ascending from chemistry to simple cell organisms, CFBF has given us the building blocks with which the great variety of application file formats are assembled. 

Recently, I took the time to expand my view of application formats by investigating the workings of Outlook’s .msg format.  Specifically, I was required to explain how a Rights Managed Email message could be dissected in order to read the contents hidden within.  Having only a cursory knowledge of the Outlook message file format (.msg), and that being based of course, on CFBF, I needed only to discover where the critical components of the email message could be found.  I will divide this blog into two parts.  In part 1, I will overview the message file format described in MS-OXMSG in preparation for part 2.  In part 2, I will describe in some detail, including code fragments how to find the compressed email attachment in a rights managed email and how it can be decompressed in order to read it plainly. 

.MSG

In this overview section, my goal is to describe the message store structure in a way that will enable you, the reader to recognize quickly the storages and streams in a sample .msg file and understand what you’re seeing.  As always, to get the nitty-gritty detail of property names and fields sizes and the like, please refer to the actual documents that I’ll list as we navigate them.

A .msg file can be saved in Outlook or compatible email client and then viewed in an hex editor or binary file parser like OffViz or 010MS-OXMSG tells us that a .msg file is a CFBF that contains storages and streams.  However, imposed on the organic building blocks is a greater scheme.  Message files contain objects which contain properties and collections of properties.   For all intents and purposes, objects are represented by storages (CFBF), and properties are represented and reside in streams (also CFBF; remember that since a .msg file is a CFBF, *everything* must fall into a storage or stream. 

Objects

Objects in a message store can be created by an email client (Outlook or compatible client) or the server (Exchange or compatible mail server).   There are three basic types of objects you’ll find in a .msg storage structure.  One is the email “object” represented by the .msg compound file itself.  There is only one of these objects at the top level or root but there can be more email objects at lower levels, contained within the other two object types.   The other two object types are the Recipient object and the Attachment object.  The first, second and third Recipient objects have storages named:

__recip_version1.0_#00000000

__recip_version1.0_#00000001

__recip_version1.0_#00000002

The subsequent Recipient objects simply increment the serial number after the pound sign.

Likewise, for the Attachment objects, the storage naming scheme is the same:

__attach_version1.0_#00000000

__attach_version1.0_#00000001

Each of these objects (storages) has properties associated with it.  This means that there are streams on each storage that contain information about the Attachment or Recipient in question.  The format of these property streams is a little more complex and I’ll defer that for now.

There are actually two kinds of Attachment objects.  There are embedded message attachments (which are other email messages that have been attached to this message) and there are “custom” attachments.   In both cases, a substorage named __substg1.0_3701000D is created under the appropriate __attach_version1.0_#<serial number> storage for that attachment.  What happens after that depends on the kind of attachment.  For an embedded message attachment, the contents of the __substg1.0_3701000D storage will be yet another message, or in other words, what you would find if it was in its own .msg file.  We could think of this recursively and define embedded message attachment storages to contain more messages.

On the other hand, a “custom” attachment is not another message, well, not necessarily.  Once the __substg1.0_3701000D storage is created for a “custom” attachment, the email client or server creating this message store simply stops writing and hands the pointer to the __substg1.0_3701000D storage object (remember IStorage* ?) to another application.  This other application could be Word, Excel, your favorite email client (that’s why I said, “not necessarily”) or any other application as long as it is capable of persisting it’s data to a compound file storage.  That’s the beauty of Object Linking and Embedding.  Outlook doesn’t care what’s in the __substg1.0_3701000D storage, it’s a custom data store. 

So, now you know how to find the Recipient and Attachment object stores in a .msg file.  But you should be thinking, “what about the properties?”.  You’re right, the properties are what make each object more than just a hollow, empty shell.  

Properties

Properties in the message file format as well as the Exchange and Outlook communication protocols, are defined in MS-OXPROPS.  They define attributes of the object like the sender email, whether a read receipt was requested by the sender, whether this message was auto forwarded, an attachment’s filename, and the list goes on an on.  Everything is described using properties.

Properties can be fixed or variable length and can also be multi-valued which are basically like arrays.  Fixed length property types can be things like PtypInteger16 which is a 16 bit integer or PtypCurrency which is a 64 bit currency type.  Variable length properties can be things like PtypString and PtypBinary which are strings and binary data, respectively.  Multi-valued properties (arrays) come in two types, fixed and variable length.  Fixed length multi-value properties are arrays with elements limited to certain fixed size types like the fixed length properties.  Variable multi-value properties are arrays with elements limited to basically the same types as variable length properties.  The most complex aspect of properties is most likely how they are stored in the message store. 

Before explaining that, it’s important to reveal that there is a property stream at the highest level in the message store and one in each Recipient and Attachment object storage.  This property stream, named “__properties_version1.0”, contains all the values or pointers to values for the properties of that object. 

Fixed length properties are contained completely within the __properties_version1.0 stream.  An entry for a fixed length property contains three pieces of information: a tag that uniquely identifies the property, a set of flags that tell whether the property is readable, writeable and/or must be present in this .msg file or attachment or recipient object, and an 8 byte field containing the value of the property (that’s the maximum length of a fixed property). 

Variable properties are also contained in the __properties_version1.0 stream, however, because they are variable in length, only the tag, size and flags are in the __properties_version1.0 entries.  By using the tag, the variable length property value can be located in a separate stream called “__substg1.0_<tag>”, where <tag> is the hexadecimal concatenation of the id and type for that property.   See figure 1 below for an example showing the id and type hex values.

Multi-value properties work similarly.  A multi-value fixed length property (array of fixed types) is stored identically to the way a variable length property is stored, only instead of each element of the array being say, a character as in a string, it’s some fixed type like a 64 bit integer.  

Multi-value variable length properties actually require N+1 streams.  N streams hold the values of the N elements and 1 stream holds the lengths of each of the individual elements.   Each element’s value is of the same type but different length, for instance an array of strings.  The length stream is named “__substg1.0_<tag>”, where <tag> again is the tag for the multi-valued variable length property being stored.  The value streams are just derived serially from the length stream’s name:  “__substg1.0_<tag>-00000000”, “__substg1.0_<tag>-00000001”, etc…

As an example to tie all this together, the PidTagSenderEmailAddress property is defined in MS-OXPROPS as:

2.1079   PidTagSenderEmailAddress
Canonical name: PidTagSenderEmailAddress
Property ID: 0x0C1F
Data type: PtypString, 0x001F

Figure 1: PidTagSenderEmailAddress definition from MS-OXPROPS

 

This tells us that the tag would be 0x0C1F001F (property id and data type), so this variable length property would be listed in the property stream (little endian format) as 1F001F0C for the tag.   We can look for this in a sample .msg file:

Here’s the property stream for the whole message:

 

image

Figure 2: Properties stream

 

In that stream, we see the entry corresponding to the PidTagSenderEmailAddress property:

 

image

Figure 3: Entry in properties stream for PidTagSenderEmailAddress

 

According to section 2.4.2.2 of MS-OXMSG, the first four bytes are the tag (which we verify as the hexadecimal for PidTagSenderEmailAddress).  The next four byte field contain the flags and the next four represent the count or length.  In this case, length is 0x6A which is 106 decimal.  Let’s look at the property stream and see the sender’s email address.  The property stream is “__substg1.0_<tag>” which ends up being “__substg1.0_0C1F001F”.

 

image

Figure 4: Stream name for PidTagSenderEmailAddress property value

 

Found it!  What are the contents of this stream?

image

Figure 5: String value for PidTagSenderEmailAddress

Well, “TOMJEBO”, that’s me! 

Remember that Attachment and Recipient objects have properties too.  The example above shows the sender email address property of the base message itself.  Let’s look at the Attachment object for this email message. 

 

image

Figure 6: stream name for PidTagAttachLongFilename property of attachment object (00000000)

 

Note that the highlighted stream is “__substg1.0_3707001F” under the “__attach_version1.0_#00000000” storage.  This tells us that this is the first attachment object in the message (by index 00000000) and that the stream represents the PidTagAttachLongFilename property of this attachment object.  This should be the actual name of the file attached to the email message based on the tag (id and type) of 0x3707001F.   

 

2.649   PidTagAttachLongFilename
Canonical name: PidTagAttachLongFilename
Property ID: 0x3707
Data type: PtypString, 0x001F

Figure 7: PidTagAttachLongFilename definition in MS-OXPROPS

 

image

Figure 8: String value for PidTagAttachLongFilename property of the attachment object

 

And that is indeed the file I sent to my fellow Sci-Fi friends!   This was also a string property and so to find the length, you would inspect the properties stream entry as we did in Figure 3 above.

One more note about properties.  There is actually another kind of property called a named property.  This is simply a property that is given a name and can be mapped to a property id or tag using the name.  A message store contains yet another storage called the named property mapping storage.    This storage is named “__nameid_version1.0”.  Within this storage are three streams, the GUID stream “__substg1.0_00020102”, Entry stream “__substg1.0_00030102” and String stream “__substg1.0_00040102”.  I won’t go into the details of this mechanism because it is more complex and we won’t need it in part 2 of this blog. 

So at this point, you know that there are objects in message files and that they are described by their properties.   You also have a working knowledge of the basic property types, how to find them and display their values.  In part 2 of the blog, I will overview the rights managed email message format that builds upon this format and is described in MS-OXORMMS.  I will also show how to use the publicly available ZLIB compression API’s for decompression of the contained email message. 

Posted by tomjebo | 0 Comments

Using Openssl to implement Crypto Operations in Netlogon Remote Protocol

 

Background 

The Netlogon Remote Protocol remote procedure call (RPC) interface is used primarily by Microsoft Windows to maintain the relationship between a machine and its domain.   In the protocol, a client delivers a logon request to the domain controller over an established secure channel between a DC and its clients.    Before a secure channel is established and any method can utilize it, the authentication process must be completed, as described in 3.1 of MS-NRPC.   The authentication process consists of the following steps:

·         A session key is negotiated to establish the secure channel (as specified in section 3.1.4.1) over non-protected RPC, which is a RPC connection without any underlying security support.   At the end of this process, if successful, client and server will mutually verify each other’s credentials and agree on a session key to be used to secure further RPC communications.   The two calculations involved with cryptographic operations are:

o   Session Key Computation (3.1.4.3  MS-NRPC)  

The client and the server use the shared secret (password), client challenge and server challenge to compute the session key. (For more information, see one of my previous blog on this topic).

o   Netlogon Credential Computation (3.1.4.4  MS-NRPC)  

The client and the server compute a credential using the challenge received and session key to compare it with the credential received. 

 

·         Once the secure channel has been established, almost all RPC calls will use Netlogon Authenticators that are calculated using session key and stored nelogon credential.  The algorithm in 3.1.4.4 is used.

Therefore, we can see that session key computation and Netlogon credential computation are very important operations in Netlogon remote protocol implementation.    In the latest Windows operating systems,   a stronger encryption (AES) and a hashing algorithm (SHA) are added.   As a valuable supplement to the protocol documentation, it could be very helpful to implement the algorithm using existing cryptographic libraries and provide sample data for implementers to verify their implementation. 

In this blog, we will show how to implement session key and Netlogon credential computation algorithms using OpenSSL, which is a general purpose cryptography open source library.    Our focus will be on the implementation when AES and strong key support are negotiated between the client and the server. 

The code shown can be compiled using gcc compiler with openssl library installed.

 

Session Key Computation

#include <openssl/crypto.h>

#include <openssl/aes.h>

#include <openssl/md4.h>

#include <openssl/md5.h>

#include <openssl/des.h>

#include <openssl/hmac.h>

#include <openssl/sha.h>

 

/*******************************************************************

*The function to compute theMD4  of a unicode string

*******************************************************************/

int ComputeM4ss(unsigned char *m4ss)

{

                int        retVal = 0;

                int                   pswd_l;

                char       *passwdPtr = globals.password;

                wchar_t    pswd_u[2 * MAX_PASSWORD_LEN + 2];

 

                mbstate_t  ps;

 

                MD4_CTX    ctx;

 

                /* Convert the ASCII password to UNICODE */

                bzero(pswd_u, 2*MAX_PASSWORD_LEN+2);

                 pswd_l = mbsrtowcs( pswd_u, (const char **)&passwdPtr, strlen(globals.password), &ps);

 

       

                if (pswd_l > 0)

                {

 

                       MD4_Init(&ctx);

                       MD4_Update(&ctx, pswd_u, pswd_l * sizeof(wchar_t));

                       MD4_Final(m4ss, &ctx);

                      retVal = 1;

                }

               return retVal;

}

 

 

/*******************************************************************

*The function to compute session key. 

*******************************************************************/

int NRPCComputeSessionKey(unsigned char *sessionKey)

{

                int  retVal = 0;

                int  zeros = 0;

                int  sts;

 

                long long *temp1;

                long long *temp2;

                 long long sum;

 

                unsigned char m4ss[16];

 

                unsigned char outBuf[16];

                unsigned char k1[8];

                unsigned char k2[8];

                unsigned char output[8];

                unsigned char output2[8];

 

                MD5_CTX     md5Ctx;

                  SHA256_CTX  shaCtx;

 

                 DES_cblock       keyStruct;

                 DES_key_schedule keySch;

 

                 HMAC_CTX             hmacCtx;

                 int         hmacLen = 16;

 

                int                   pswd_l;

                char       *passwdPtr = globals.password;

                wchar_t    pswd_u[2 * MAX_PASSWORD_LEN + 2];

 

                mbstate_t  ps;

 

                bzero(sessionKey, 16);

 

/*

*   If AES support is negotiaited, then  SHA will be used to calculate the session

*   key.

*/

                if (globals.aes & globals.strongkey)

                {

                           /*

                              * Convert the ASCII password to UNICODE

                            * The password is actually OWF hash of the shared secret

                             */

                           bzero(pswd_u, 2*MAX_PASSWORD_LEN+2);

                           pswd_l = mbsrtowcs( pswd_u, (const char **)&passwdPtr, strlen(globals.password), &ps);

 

                           SHA256_Init(&shaCtx);

                           SHA256_Update(&shaCtx, pswd_u, pswd_l*2);

                           SHA256_Update(&shaCtx, globals.clientChallenge, MAX_CHALLENGE_LEN);

                           SHA256_Update(&shaCtx, globals.serverChallenge, MAX_CHALLENGE_LEN);

                            SHA256_Final(sessionKey, &shaCtx);

                             

                            retVal = 1;

                }

 

/*

*   If strong key support is negotiaited, then  MD5  will be used to calculate the session

*   key.

*/

                else if (globals.strongkey)

                {

                              printf("\nSession Key: Strong key computation\n");

 

                              if (ComputeM4ss(m4ss))

                              {

                                      sts = MD5_Init(&md5Ctx);

                                      sts = MD5_Update(&md5Ctx, &zeros, 4);

                                      sts = MD5_Update(&md5Ctx, globals.clientChallenge, MAX_CHALLENGE_LEN);

                                      sts = MD5_Update(&md5Ctx, globals.serverChallenge, MAX_CHALLENGE_LEN);

                                      sts = MD5_Final(outBuf, &md5Ctx);

 

                                        HMAC_CTX_init(&hmacCtx);

                                       HMAC_Init_ex(&hmacCtx, m4ss, 16, EVP_md5(), NULL);

                                       HMAC_Update(&hmacCtx, outBuf, 16);

                                       HMAC_Final(&hmacCtx, sessionKey, &hmacLen);

                                       HMAC_CTX_cleanup(&hmacCtx);

                                       retVal = 1;

                               }

                 }

                

                 /*

*   If strong key support is not negotiaited, then  MD4  will be used to calculate the session

*   key.

*/

                 else if (!globals.strongkey & !globals.aes)

                 {

                               if (ComputeM4ss(m4ss))

                               {

                                       // SET sum to ClientChallenge + ServerChallenge

                                      temp1 = (long long *)&globals.clientChallenge;

                                      temp2 = (long long *)&globals.serverChallenge;

                                      sum = *temp1 + *temp2;      // TODO: make sure overflow is ignored.

 

                                       // SET k1 to lower 7 bytes of the M4SS

                                       bzero(k1, 8);

                                       bcopy(m4ss, k1, 7);

 

                                       // SET k2 to upper 7 bytes of the M4SS

                                       bzero(k2, 8);

                                       bcopy(&m4ss[8], k2, 7);

 

                                       bcopy(k1, keyStruct, sizeof(keyStruct));

                                       DES_key_sched(&keyStruct, &keySch);

                                        DES_ecb_encrypt((DES_cblock*)&sum, (DES_cblock*)output, &keySch, DES_ENCRYPT);

 

                                       bcopy(k2, keyStruct, sizeof(keyStruct));

                                       DES_key_sched(&keyStruct, &keySch);

                                       DES_ecb_encrypt((DES_cblock*)output, (DES_cblock*)output2, &keySch, DES_ENCRYPT);

 

                                        bzero(sessionKey, 16);

                                       bcopy(output2, sessionKey, 8);

                                       retVal = 1;

 

                              }

                }

                else /* Invalid to have just the AES flag set */

                {

                                printf("ERROR - invalid to have just the AES flag set without the Strong Key flag.\n");

                }

 

                return retVal;

}

 

Examples of Session Key Calculations

 

For AES support :

      OWF Password:    13 c0 b0 4b 66 25 0d 08-b8 a3 90 4d cc 8b 34 e3

      Client Challenge: 25 63 e3 5f 69 e1 5a 24

      Server Challenge: 9C 66 5F 90 D9 83 DF 43

      Session Key calculated: c9 c7 f7 2f c6 b9 13 e3-67 ae a9 1d 0a e3 a7 70

 

 For Strong Key support:

 

       OWF Password: 31 a5 90 17 0a 35 1f d5-11 48 b2 a1 0a f2 c3 05

       Client Challenge: 3a 03 90 a4 6d 0c 3d 4f

       Server Challenge: 0c 4c 13 d1 60 41 c8 60

       Session Key calculated: ee fe 8f 40 00 7a 2e eb  68 43 d0 d3 0a 5b e2 e3

 

Netlogon Credential Computation

 

 /*********************************************************************************

 * Arguments

 *    input: Is the client challenge when computing the credential for the client.

 *           It is the server challenge when computing the credential for the server.

 *

 *  Return

 *     1 = success

 *     0 = error

 **********************************************************************************/

int NRPCComputeNetlogonCredentials( unsigned char *challenge, unsigned char *sessionKey, unsigned char *output )

{

              int   retVal = 0;

              int   blockUsed = 0;

 

               unsigned char k1[8];

               unsigned char k2[8];

               unsigned char k3[8];

               unsigned char k4[8];

               unsigned char iv[16];

               unsigned char temp[16];

 

                AES_KEY  aesKey;

 

                DES_cblock       keyStruct;

                DES_key_schedule keySch;

 

               /*

                 * When AES is negotiated, simply AES encrypt the input with the session key

                * Note: Currently using an IV of all zeros to cover the statement:

                * "AES128 is the AES128 algorithm in CFB-8 mode without an initialization vector"

                 */

 

                 if (globals.aes & globals.strongkey)

                {

                                if (globals.debug)

                                {

                                    printf("AES Computation\n");

                                    printf("Challenge:\n");

                                    HexDump(challenge, 8);

                                    printf("Session key:\n");

                                    HexDump(sessionKey, 16);

                                }

 

                                bzero(iv, 16);

                                 bzero(temp, 16);

                                 bcopy(challenge, temp, 8);

                                 AES_set_encrypt_key(sessionKey, 128, &aesKey);

                                AES_cfb8_encrypt(temp, output, 16, &aesKey, iv, &blockUsed, AES_ENCRYPT);

                                retVal = 1;

                }

                else

                {

                                /* use DES */

                                bzero(k1, 8);

                                bzero(k2, 8);

                                bzero(k3, 8);

                                bzero(k4, 8);

 

                                bcopy(sessionKey, k1, 7);                        // SET k1 to bytes(0, 6, Sk)

                                InitLMKey(k1, k3);

                                bcopy(&sessionKey[7], k2, 7);                                // SET k2 to bytes(7, 13, Sk)

                                InitLMKey(k2, k4);

 

                                if (globals.debug)

                                {

                                                printf("\nNRPCComputeNetlogonCredentials()");

                                                printf("\nsessionKey");

                                                HexDump(sessionKey, 16);

                                                printf("\nk1");

                                                HexDump(k1, 8);

                                                printf("\nk2");

                                                HexDump(k2, 8);

                                                printf("\nk3");

                                                HexDump(k3, 8);

                                                printf("\nk4");

                                                HexDump(k4, 8);

                                }

 

                                // CALL DES_ECB(Input, k3, &output1)

                                bcopy(k3, keyStruct, sizeof(keyStruct));

                                DES_key_sched(&keyStruct, &keySch);

                                DES_ecb_encrypt((DES_cblock*)challenge, (DES_cblock*)temp, &keySch, DES_ENCRYPT);

 

                                // CALL DES_ECB(temp, k4, output)

                                bcopy(k4, keyStruct, sizeof(keyStruct));

                                DES_key_sched(&keyStruct, &keySch);

                                DES_ecb_encrypt((DES_cblock*)temp, (DES_cblock*)output, &keySch, DES_ENCRYPT);

 

                }

 

                return retVal;

}

 

Examples of Session Key Calculations

 

For AES support :

      Client Challenge: 25 63 e3 5f 69 e1 5a 24

      Session Key used: c9 c7 f7 2f c6 b9 13 e3-67 ae a9 1d 0a e3 a7 70

      Computed Client Netlogon Credential: 58 6a df 53 ef 72 78 d9     

     

  For Strong Key support:

      Client Challenge: 3a 03 90 a4 6d 0c 3d 4f

       Session Key used: ee fe 8f 40 00 7a 2e eb  68 43 d0 d3 0a 5b e2 e3

       Computed Client Netlogon Credential:  b6 38 95 82 44 fc ea cd

 

    With the code above, we demonstrated the implementation of NRPC cryptographic operations using the OpenSSL library.   Also included are examples of input and output data.   This will assist those in the implementation of interoperability solutions against Windows Netlogon RPC interface.

    Thanks to Nick Meier for providing the source code for this blog.

Beginning with the PowerPoint Document Stream

This blog will expand on my previous blog Parsing Pictures in a PowerPoint binary file, which details the Pictures Stream, and how you might parse the stream to extract pictures contained in the PowerPoint document. I’ll extend the concepts of the previous blog to apply to parsing the “PowerPoint Document” stream.

You’ll notice as you read through the following that you could just use the Pictures Stream, as shown previously, instead of the PowerPoint Document stream (the OfficeArtDggContainer) and accomplish the same thing, and in the strictest sense, yes, this is “almost” another way of doing the same thing. However, if you are building a generalized parser for PowerPoint binary files, and not just a one-off picture enumeration or extraction tool, then you are expanding beyond just the Pictures Stream and implementing a larger portion of the binary specification, so this is an evolutionary step in that direction.

Also, having detailed in the previous blog, and in this one, how to manually parse Office binary files with a Hex editor and code usage of IStorage to browse over the streams, at the end of this blog I’ll introduce a new tool by the Microsoft Security Response Center team (MSRC) that makes analyzing Office binary files much, much easier.

The binary file specification of course is still PowerPoint (MS-PPT), which you may use to follow along. However, as before, much of the parsing details will derive from definitions of structures in MS-ODRAW.

Let’s get started…

The PowerPoint Document Stream is defined in MS-PPT section 2.1.2:

A required stream whose name MUST be "PowerPoint Document".

The contents of this stream are specified by a sequence of top-level records.

Let a top-level record be specified as any one of the following: DocumentContainer,

MasterOrSlideContainer, HandoutContainer, SlideContainer, NotesContainer, ExOleObjStg,

ExControlStg, VbaProjectStg, PersistDirectoryAtom, or UserEditAtom record.

You may build on what we did previously by using the same code snippet and replacing the stream name with “PowerPoint Document” and/or follow the steps defined in MS-PPT section 2.1.2 to parse the “PowerPoint Document” stream to the Document Container, section 2.4.1 then to the DrawingGroupContainer (OfficeArtDgg), section 2.4.3. The definition of OfficeArtDgg is in MS-ODRAW, section 2.2.12 and is called OfficeArtDggContainer: This record specifies the container for all OfficeArt file records containing document-wide data. The OfficeArt record types are defined in MS-ODRAW section 2.2.

This is where we’ll start with the OfficeArtDggContainer structure (highlighted fields follow in sequence/color with the definitions):

73A0h: 0F 00 00 F0 30 01 00 00 00 00 06 F0 78 00 00 00  ...ð0......ðx...

73B0h: 01 34 00 00 0E 00 00 00 15 00 00 00 0D 00 00 00  .4..............

73C0h: 01 00 00 00 07 00 00 00 0D 00 00 00 05 00 00 00  ................

73D0h: 0C 00 00 00 01 00 00 00 0B 00 00 00 01 00 00 00  ................

73E0h: 0A 00 00 00 01 00 00 00 09 00 00 00 01 00 00 00  ................

73F0h: 08 00 00 00 01 00 00 00 07 00 00 00 01 00 00 00  ................

7400h: 06 00 00 00 01 00 00 00 05 00 00 00 01 00 00 00  ................

7410h: 04 00 00 00 01 00 00 00 03 00 00 00 01 00 00 00  ................

7420h: 02 00 00 00 01 00 00 00 2F 00 01 F0 58 00 00 00  ......../..ðX...

7430h: 52 00 07 F0 24 00 00 00 05 05 B1 A6 19 08 D8 C3  R..ð$.....±¦..ØÃ

7440h: 0B 6F B5 6C A3 98 8C 9E F4 65 FF 00 B6 03 00 00  .oµl£˜Œžôeÿ.¶...

7450h: 01 00 00 00 00 00 00 00 00 00 00 00 32 00 07 F0  ............2..ð

7460h: 24 00 00 00 03 04 27 CF 5A 3B 2E DC 9E 2E 16 A1  $.....'ÏZ;.Üž..¡

7470h: A1 59 52 1B 76 E9 FF 00 40 6D 00 00 01 00 00 00  ¡YR.véÿ.@m......

7480h: B6 03 00 00 00 00 00 00                          ¶.......  

2.2.12   OfficeArtDggContainer

 

2.2.48   OfficeArtFDGGBlock

Referenced by: OfficeArtDggContainer

 

2.2.20   OfficeArtBStoreContainer

Referenced by: OfficeArtDggContainer

This record specifies the container for all BLIPs used in all drawings associated with the parent OfficeArtDggContainer record.

 

2.2.22   OfficeArtBStoreContainerFileBlock

Referenced by: OfficeArtBStoreContainer, OfficeArtBStoreDelay, OfficeArtInlineSpContainer

 

2.2.32   OfficeArtFBSE

Referenced by: OfficeArtBStoreContainerFileBlock

This record specifies a File BLIP Store Entry (FBSE) that contains information about the BLIP.

 

Field                      Meaning

rh.recVer             MUST be 0x2.

rh.recInstance   MUST be the BLIP type.

** section 2.4.1 MSOBLIPTYPE:

   msoblipJPEG:     0x05  JPEG format.

   msoblipWMF:    0x03  WMF format.

rh.recType          MUST be 0xF007.

rh.recLen             An unsigned integer that specifies the number of bytes following the header. MUST be the size of nameData in bytes plus 36 if the BLIP is not embedded in this record or the size of nameData plus size plus 36 if the BLIP is embedded

 

btWin32 (1 byte): An MSOBLIPTYPE enumeration value that specifies the BLIP type.

From the example above (section 2.4.1   MSOBLIPTYPE, msoblipJPEG0x05 JPEG format, and 0x03 WMF format)

rgbUid (16 bytes): An MD4 digest, as specified in [RFC1320], that specifies the unique identifier of the pixel data in the BLIP.

size (4 bytes): An unsigned integer that specifies the size of the BLIP in bytes in the stream.  (0x000003B6 = 950 bytes, and 0x00006D40 = 27,968 bytes) ** You will note these are the same JPEG and WMF pictures used in the previous blog with the Pictures Stream, as it is the same document.

cRef (4 bytes): An unsigned integer that specifies the number of references to the BLIP. A value of 0x00000000 specifies an empty slot in the OfficeArtBStoreContainer.

foDelay (4 bytes): An MSOFO data type that specifies the file offset into the associated OfficeArtBStoreDelay (delay stream). A value of 0xFFFFFFFF specifies that the file is not in the delay stream and cRef MUST be 0x00000000.

 

2.2.21   OfficeArtBStoreDelay

This record specifies the delay loaded container of BLIPs in the host application. There is no OfficeArtRecordHeader for this container.

rgfb (variable)

...

rgfb (variable): An array of OfficeArtBStoreContainerFileBlock records that specifies BLIP data. The array continues while the rh.recType field of the OfficeArtBStoreContainerFileBlock record is equal to 0xF007 or between 0xF018 and 0xF117, inclusive.

 

Having detailed, above, how to manually browse these Office binary files with a Hex editor, yet again, the following tool I mentioned at the beginning of this blog will make this exercise a lot easier. They’ve done all the parsing for you! And, the application includes file Defragmentation and Repair tools!

Introducing, Microsoft Security Response Center team’s OffVis!

If you followed along in a Hex editor and IStorage up to this point, I doubt you need much help with OffVis since it is a drill down GUI. Everything we did with the hex editor above in parsing you can do with OffVis using a hierarchical GUI and simply drill down on the objects and see the hex view of the data on the left side of the window. Note, you can also double click on the hex view window area and it will adjust your position automatically to the appropriate hierarchy/record on the hierarchical view window. To get started just pick your file from the menu ribbon (File-> Open) then pick your Parser from the list box (in this case PowerPoint97_2003BinaryFormat) and click Parse, then drill down on the structures:

You can find more information about OffVis, including the download link, on the MSRC OffVis Blog site.

MSRC even provides a training video for OffVis. Although this tool is great for assisting with understanding the Office binary file formats the original purpose of the tool, and the focus of the video training, is for researching potential Office binary file exploits.

I hope this helps advance your understanding as you investigate the Office binary file formats.

Stay tuned for more…

A successful story of an Interoperability Lab event

 

     As the protocol documentation support team, we have the responsibility of helping the users of our published Microsoft Open Protocol Documentation achieve successful interoperability with Windows.    There's more to interoperability than just good technical documentation; engagement with partners is essential. 

   One of our team's most active partners is the Samba team, whose main areas are remote file system and Active Directory.  Recently, I had the chance to attend the second Samba IO Lab hosted in the Microsoft Enterprise Engineering Center (ECC), an event dedicated to testing the Samba implementation against Windows.   It was an incredible experience for me to support the testing and debugging activities during the event.   We achieved very positive results at the end of week in a pretty dramatic fashion, as described in Andrew Bartlett's blog.  In this blog, I would like to share my experiences and thoughts from the perspective of a supporting Escalation Engineer.

   The Samba IO event was held in the ECC from 09/21 to 09/25.   Before the event, the ECC already set up the testing environment that consists of four domains with different domain functional levels.  Everyone in the event felt that the lab environment was excellent.  A group of five Samba developers arrived after attending another interoperability event.  The Samba team was prepared to test protocols they implemented or changed lately, including DRSR, LDAP with complete schema and AES support in Netlogon.  The main focus was to have a Windows DC join to a Samba domain that holds all FSMO roles and perform a complete replication.  

   After the initial meeting, it was decided that I would work with Tridge (Andrew Tridgell) on the main issue of domain join and replication, while other Samba team members started working on protocol testing using the Microsoft Protocol Documentation test suites.  At the time, the AD Domain Service could not be installed due to an internal error  when dcPromo was run to promote the Windows  Server 2008 R2 to a DC in a Samba domain.  Our first approach was to check log files generated in domain join and the DC promotion processes and the very detailed logs produced by Samba.  We identified that the problem happened somewhere after domain join, but before any replication was attempted.    The error can be linked to many possible root causes.   We tried several approaches associated with the error and nothing worked.   We finally decided that we would capture a Time Travel Trace and run live debugging with Windows source attached.  We also captured ETW trace to assist in identifying the module for debugging.  (Even though I used private symbols to assist in the debugging, any developer can still utilize the Windows debugger and public symbols, to gain more information about the parameters passed into and returned from the interface functions.)  In this way, I could run debugging to figure out the real reason behind the behavior and Samba team could make changes based on the information I provided and also continue their debugging and code modification in parallel.  After a whole day of debugging , we finally found out that the root cause of problem was that the RootDomainSID returned from a Samba DC through LDAP query of attribute ncName uses upper case letters (A to F) for the string representation of SID.  But Windows only processes the string representation of SID in lower case.    We were very excited, thinking that replication would start and everything should work right away.   Just as Andrew documented in his blog, the moment didn't come for another four days, with many traces analyzed and more than ten problems identified and fixed.  This proved that tracing through the source code for the logic at this detailed level can be very useful for the debugging of interoperability solutions.    

   After a  long week (> 60 hours) of working side by side with Samba team,  I was really impressed by the Samba team's professionalism, passion, dedication, protocol knowledge,  programming  and debugging skills and team work spirit and really enjoyed working with the entire team.  Many times, they identified problems quickly and made correct changes on the fly.  We shared many happy and frustrated moments and everyone agreed that we had a very productive week.  Without the joint debugging effort between the Samba and Microsoft teams, it would not be possible to achieve so much while they were here.  Hopefully this can become a model for the future Interoperability lab events for Samba and other partners.

   I am looking forward to working on the Samba IO event again in the future!  

More ActiveSync

More ActiveSync

Dominic Michael Salemno

Introduction

In my previous ActiveSync blog, entitled An ActiveSync Primer, I delved into the basics of the ActiveSync Protocol. This is the second blog in a series intended to thoroughly explain the ActiveSync Protocol.

Communications

In my previous blog, I stated that ActiveSync uses HTTPS for its communications channel. This is not necessarily true, only mostlyJ. The truth is that HTTP can be used in place of HTTPS. This is not as secure as the latter. The transport method needed will depend on the Virtual Directory (VDir) configuration on the server.

Each HTTP POST will contain a series of request and response packets. Each command issued by the client will be in the form of a request. The response of course is the return packet from the server containing the results relevant to the preceding request.

Let’s look at an example:

This example fetches an e-mail item from the server.

Request

POST /Microsoft-Server-

ActiveSync?Cmd=ItemOperations&User=deviceuser&DeviceId=device1&

DeviceType=PocketPC HTTP/1.1

Content-Type: application/vnd.ms-sync.wbxml

MS-ASProtocolVersion: 14.0

 

<?xml version="1.0" encoding="utf-8"?>

<ItemOperations xmlns:airsync="AirSync:"

xmlns:airsyncbase="AirSyncBase:" xmlns="ItemOperations:">

  <Fetch>

    <Store>Mailbox</Store>

    <airsync:CollectionId>7</airsync:CollectionId>

    <airsync:ServerId>7:1</airsync:ServerId>

    <Options>

      <airsyncbase:BodyPreference>

        <airsyncbase:Type>1</airsyncbase:Type>

        <airsyncbase:TruncationSize>5120</airsyncbase:TruncationSize>

        <airsyncbase:AllOrNone>0</airsyncbase:AllOrNone>

      </airsyncbase:BodyPreference>

    </Options>

  </Fetch>

</ItemOperations>

Response

HTTP/1.1 200 OK

Cache-Control: private

Content-Length: 409

Content-Type: application/vnd.ms-sync

Server: Microsoft-IIS/6.0

MS-Server-ActiveSync: 14.0

Date: Tue, 08 May 2007 17:29:52 GMT

 

<?xml version="1.0" encoding="utf-8"?><ItemOperations

xmlns:airsync="AirSync:" xmlns:email="POOMMAIL:"

xmlns="ItemOperations:">

  <Status>1</Status>

  <Response>

    <Fetch>

      <Status>1</Status>

      <airsync:CollectionId>7</airsync:CollectionId>

      <airsync:ServerId>7:1</airsync:ServerId>

      <airsync:Class>Email</airsync:Class>

      <Properties>

        <email:To>"deviceuser" &lt;someone1@example.com&gt;</email:To>

        <email:Cc>"deviceuser3" &lt;someone3@example.com&gt;</email:Cc>

        <email:From>"deviceuser2" &lt;someone2@example.com&gt;

        </email:From>

        <email:Subject>Subject</email:Subject>

        <email:DateReceived>2007-05-08T17:29:07.890Z

        </email:DateReceived>

        <email:DisplayTo>DeviceUserDisplayName</email:DisplayTo>

        <email:ThreadTopic>Subject</email:ThreadTopic>

        <email:Importance>1</email:Importance>

        <email:Read>0</email:Read>

        <airsyncbase:Body>

          <airsyncbase:Type>1</airsyncbase:Type>

          <airsyncbase:EstimatedDataSize>20

          </airsyncbase:EstimatedDataSize>

          <airsyncbase:Data>Body as plain text</airsyncbase:Data>

        </airsyncbase:Body>

        <email:MessageClass>IPM.Note</email:MessageClass>

        <email:InternetCPID>28591</email:InternetCPID>

        <email:Flag />

        <email:ContentClass>urn:content-classes:message

        </email:ContentClass>

        <airsyncbase:NativeBodyType>1</airsyncbase:NativeBodyType>

      </Properties>

    </Fetch>

  </Response>

</ItemOperations>

 

The request illustrates using the fetch command to obtain an e-mail message from the server. Subsequently, the server sends out a fetch response containing the requested e-mail message.

This command as well as other commands is explained in much detail in the [MS-ASCMD] document (ActiveSync Command Reference Protocol Specification). This document describes all of the various ActiveSync commands that can be sent over the wire.

WBXML

The above examples are shown using the XML structures, not WBXML. This is to simplify matters and not in actuality. WBXML is not used for all communications, but most. In the request above, you’ll notice that WBXML is specified in the header:

Content-Type: application/vnd.ms-sync.wbxml

 

So really, this message should be encoded using WBXML, but the XML equivalent is shown for simplicity’s sake. This is done across the ActiveSync specification documents found in Microsoft’s Open Specifications.

Overview

To reiterate the concept behind ActiveSync, simplistically, ActiveSync ensures the mobile device in question is constantly sending various request packets to the server (usually an Exchange Server) in an effort to maintain the most up-to-date information on the device. Whether this is simply pulling new information from the server, or sending updated information from the device.

All of the protocol documents describing this protocol in detail can be found on Microsoft’s Open Specifications site. Underneath the Exchange Server Protocols section, one will find every document describing this protocol to begin with [MS-AS (AS denoting ActiveSync). All of the documents in question are as follows:

[MS-ASAIRS].pdf

 [MS-ASCAL].pdf

 [MS-ASCMD].pdf

 [MS-ASCNTC].pdf

 [MS-ASCON].pdf

 [MS-ASDOC].pdf

 [MS-ASDTYPE].pdf

 [MS-ASEMAIL].pdf

 [MS-ASHTTP].pdf

 [MS-ASMS].pdf

 [MS-ASNOTE].pdf

 [MS-ASPROV].pdf

 [MS-ASTASK].pdf

 [MS-ASWBXML].pdf

 

These documents can be daunting, so in an effort to simplify matters, these series will assist those wanting to learn the protocol in more depth. The next blog will walk through initialization and synchronization of a simple session.

Posted by doms | 0 Comments

Attachment(s): More ActiveSync.pdf

Exploring the Compound File Binary Format (part deux)

Exploring the Compound File Binary Format (part deux)

In this, part ni (pronounced ne; Japanese for deux), I pick up where we left off.  Where were we?  I had just demonstrated that the IStorage::CopyTo() method, at least Microsoft’s default  implementation provided in Windows’ ole32.dll, will indeed do what it claims which is to “…order the contents of streams sequentially…".   As we discovered, however, the data in the ministream is not ordered as the other “standard” streams are.  As a refresher, the ministream is also known as the “Root Entry” stream and is where all streams that contain less than 4096 bytes of data reside.   After using the CopyTo() method, we noticed that the ministream was still discontiguous (new word).  We reasoned that although the streams in the ministream are written by CopyTo() in the same manner that all the others are, the ministream needs to grow and therefore must interleave allocations of sectors with the other streams as well as the internal structures of the compound file like the FAT, DiFAT, Directory, etc…  It’s growth doesn’t happen once but possibly many times and other sector allocations happen in between these spurts of growth.  Hence the fragmentation.

To verify this behaviour, I created a sample compound file and wrote three streams to it in such a sequence that it would most likely produce fragmentation.  The algorithm went something like Figure 0.

 

1. Open three streams: stm1, stm2, stm3

2. write 2 minisectors worth of data to stm1

3. write 1 minisector worth of data to stm2

4. delete stm1

5. commit the root storage

6. write 8 minisectors worth of data to stm3

7. commit the root storage again

Figure 0: Stream fragmentation algorithm

 

I wrote ‘1’s to stm1, ‘2’s to stm2, ‘3’s to stms3 so they would be easily identifiable.  After using this algorithm, I had a ministream that looked like Figure 1.

 

0C00h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
0C10h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
0C20h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
0C30h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
...
1000h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
1010h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
1020h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
1030h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
...
1200h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
1210h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
1220h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
1230h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
1240h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
1250h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
1260h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
1270h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
… (more 33’s)

13F0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333

Figure 1: Fragmented streams

 

The ellipses (except for the last) are segments of the file that don’t contain valid stream data.  As you can see, stm2 and stm3 are both fragmented.  stm2 starts at 0C00h for 64 bytes but then discontinues at 0C30h and starts again at 1200h.  Likewise, stm3 starts at 1000h and temporarily ends at 1030h, continuing at 1240h.  Normally, the data is not “marked” nicely like this and is usually spread across a more vast number of sectors making it very tedious to piece together. 

The goal: to cause the stream data to defragment or order itself contiguously without having to read the raw bytes, traverse all the internal structures and rewrite the entire compound file. 

This is actually fairly straight forward, although the resulting code is more complex than our first attempt at defragmenting the stream data in the file.  As you might remember from last time, we simply copied the entire compound file to another one named <filename>.defrag.<ext> using the IStorage::CopyTo().  As explained, earlier, this left the ministream potentially fragmented.  We can use the same approach, copying element by element to a new location but will have to separate the copying of the ministream from the rest of the streams, storages and all internal structures (FAT, DiFAT, Directory…).  In order to do this, we need to save the ministream somewhere, copy just the remaining elements and then write the ministream elements, one by one so that each element is written in its entirety before writing the next.  This is what ensures contiguity. 

Refining the algorithm to make it easier to understand resulted in the following steps:

 

1. Open the source compound file

2. Open the target (to be defragmented) compound file

3. Open a new temporary storage or compound file

4. Copy the entire source compound file to the temporary storage

5. Copy only the non-ministream elements of the source compound file to the target file using IStorage::CopyTo() like before

6. Copy the ministream elements to the target

Figure 2: General algorithm to separate copying of ministream and non-ministream elements

 

This algorithm will cause the non-ministream elements to be written first and then the ministream elements, avoiding the interlacing allocation of sectors.  However, in order for step #5 to work, we need to remove the ministream elements from the source (after saving them of course) before the CopyTo(). Also, in order to remove the ministream elements from a compound file, you basically need to traverse the entire tree structure, testing streams for sizes of < 4096 and then call IStorage::DestroyElement() on them.  Because this is a tree structure, iterating over the tree can be made much more readable by using recursion.  We love recursion, right?  In a recursive algorithm for a tree we simply enumerate the elements in the current storage object, and if the current element is a stream, we can test it for residence in the ministream (<4096).  If it’s not a stream, simply recurse or call the containing function again passing this current element as the argument.  Pretty simple. 

Here's the pseudo code for the algorithm:

 

function remove_mini(element)
{
     while (more_elements)
     {

          element.enum_elements(&current))

          if (current is a stream)
          {
               if (current < 4096)
                    delete current;
          }
          else
               remove_mini(current);
      }
}

Figure 3a: Recursive function to remove ministream elements from a storage object

 

The function to “prune” the ministreams out of a storage looks like this:

 

#define LESSTHAN(x,y) ((x.HighPart <= y.HighPart) && (x.LowPart < y.LowPart))


HRESULT PruneStgMiniStream(LPSTORAGE lpSrcStg)
{
    HRESULT hr = S_OK;
    LPENUMSTATSTG lpEnumStatStg = NULL;
    hr  = lpSrcStg->EnumElements(NULL, NULL, NULL, &lpEnumStatStg);
    ULONG cfch = 0;
    STATSTG tstat;
    while (lpEnumStatStg->Next(1, &tstat, &cfch)==S_OK)
    {
        ULARGE_INTEGER mini_limit = {4096,0};

        // if it's a stream, it's not 0 bytes and it's less than 4096 bytes, then we delete it.
        if ((tstat.type == STGTY_STREAM) &&
            ((tstat.cbSize.LowPart > 0) || (tstat.cbSize.HighPart > 0)) &&
             (LESSTHAN(tstat.cbSize, mini_limit)))
            lpSrcStg->DestroyElement(tstat.pwcsName);

        if (tstat.type == STGTY_STORAGE)
        {
            LPSTORAGE lpNewStg = NULL;
            hr = lpSrcStg->OpenStorage(tstat.pwcsName, NULL,
                STGM_READWRITE|STGM_SHARE_EXCLUSIVE , NULL, NULL, &lpNewStg);

            PruneStgMiniStream(lpNewStg);

            lpNewStg->Release(); // Caller releases.
        }
    }

    lpEnumStatStg->Release();
    return hr;
}

Figure 3b: Recursive function to remove ministream elements from a storage object

 

PruneStgMiniStream() is called again within itself, passing lpNewStg as the new lpSrcStg parameter. 

So once steps 1-5 (Figure 2) have been accomplished, the ministreams have to be copied from the temporary storage to the target.  This is a little more complicated because we can’t just create a ministream element wherever we want, i.e. in the root storage of the target.  We have to make sure that each ministream element gets copied to its proper branch in the tree.  Thinking about this reminded me of those mechanical tracing devices we played with as kids allowing us to copy a drawing by having a mechanical arm mimic each movement.  In the same way, the algorithm can simply use the temporary storage tree structure as the traced shape, telling us where to deposit the ministream elements in the target, which is missing them.  I was able to use the same basic form as the PruneStgMiniStream() function but instead of deleting the ministream elements, it creates the element in the target and copies the data.  Here’s the resulting function to copy the ministream elements to the target:

 

Here's the pseudo code for the algorithm:

 

function copy_mini(src_element, dst_element)
{
     while (more_elements)
     {

          src_element.enum_elements(&src_current))

          if (src_current is a stream)
          {
               if (src_current < 4096)
                    dst_element.create(src_current.name, &dst_current);

                    src_current.copyto(dst_current);
          }
          else

          {

               src_element.open(&src_current);

               dst_element.open(&dst_current);
               copy_mini(src_current, dst_current);

           }
      }
}

Figure 4a: Recursive function to remove ministream elements from a storage object

 

Here’s the resulting function to copy the ministream elements to the target: 

 

HRESULT CopyStgMiniStream(LPSTORAGE lpSrcStg, LPSTORAGE lpDestStg)
{
    HRESULT hr = S_OK;
    LPENUMSTATSTG lpEnumStatStg = NULL;
    hr  = lpSrcStg->EnumElements(NULL, NULL, NULL, &lpEnumStatStg);

    // The assumption here is that the two input storages are identical except for
    // the ministream.  Therefore, we only need to enumerate on one since the other structure
    // will be consistent.  However, when we recurse, we drop down a level in the tree which means
    // we need to do that to both storages.

    ULONG cfch = 0;
    STATSTG tstat;
    while (lpEnumStatStg->Next(1, &tstat, &cfch)==S_OK)
    {
        LPSTREAM lpStmSrcTemp = NULL;
        LPSTREAM lpStmDestTemp = NULL;
        ULARGE_INTEGER nread, nwritten;
        ULARGE_INTEGER mini_limit = {4096,0};

        // if it's a stream, it's not 0 bytes and it's less than 4096 bytes, then we copy it.
        if ((tstat.type == STGTY_STREAM) &&
            ((tstat.cbSize.LowPart > 0) || (tstat.cbSize.HighPart > 0)) &&
                (LESSTHAN(tstat.cbSize, mini_limit)))
        {
            hr = lpSrcStg->OpenStream(tstat.pwcsName, NULL,
                STGM_READ|STGM_SHARE_EXCLUSIVE, NULL, &lpStmSrcTemp);
            hr = lpDestStg->CreateStream(tstat.pwcsName,
                STGM_WRITE|STGM_SHARE_EXCLUSIVE, NULL, NULL, &lpStmDestTemp);
            hr = lpStmSrcTemp->CopyTo(lpStmDestTemp, tstat.cbSize, &nread, &nwritten);
            lpStmSrcTemp->Release();
            lpStmDestTemp->Release();
        }
        if (tstat.type == STGTY_STORAGE)
        {
            LPSTORAGE lpNewSrcStg = NULL;
            LPSTORAGE lpNewDestStg = NULL;           
            hr = lpSrcStg->OpenStorage(tstat.pwcsName, NULL,
                STGM_READ|STGM_SHARE_EXCLUSIVE , NULL, NULL, &lpNewSrcStg);
            hr = lpDestStg->OpenStorage(tstat.pwcsName, NULL,
                STGM_READWRITE|STGM_SHARE_EXCLUSIVE , NULL, NULL, &lpNewDestStg);

            CopyStgMiniStream(lpNewSrcStg, lpNewDestStg);

            lpNewSrcStg->Release(); // Caller releases.
            lpNewDestStg->Release(); // Caller releases.
        }
    }
    lpEnumStatStg->Release();
    return hr;
}

Figure 4: Function to copy ministream elements to a target storage object.

 

The important functions have been written, now to just put it all together.  The ministreams have to be deleted from either the source or the temporary storage before copying to the target storage.  Because we don’t want to modify the source storage, it’s better to use the temporary storage for “pruning”, then copy all but the ministream elements from the temporary to the target storage.   So the code uses the algorithm but switching the source and temporary storages: 

 

… <omitted redundant code from original blog>

 

LPSTORAGE lpTempStg = NULL;

hr = StgCreateStorageEx(lptszTemp,

                         STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE| STGM_SHARE_EXCLUSIVE,

                         STGFMT_STORAGE, 0, NULL, NULL, IID_IStorage, (LPVOID*) &lpTempStg);

 

LPSTORAGE lpDestStg = NULL;

hr = StgCreateStorageEx(lptszTarget,

                        STGM_CREATE|STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,

                        STGFMT_STORAGE, 0, NULL, NULL, IID_IStorage, (LPVOID*) &lpDestStg);

 

… <omission>

 

hr = lpSrcStg->CopyTo(NULL, NULL, NULL, lpTempStg);

 

// this deletes all the streams that live in the ministream

hr = PruneStgMiniStream(lpTempStg); 

 

// copy everything but the ministream data

hr = lpTempStg->CopyTo(NULL, NULL, NULL, lpDestStg); 

 

// now copy the ministreams to the destination storage

hr = CopyStgMiniStream(lpSrcStg, lpDestStg); 

 

hr = lpDestStg->Commit(STGC_CONSOLIDATE);

 

… <omission>

Figure 5: mainline code

I’ve omitted code that didn’t change much.  You can see that a new call to StgCreateStorageEx() creates the temp storage.   Also, it’s important to Commit() the temporary storage after “pruning” the ministream elements as well as Commit()’ing the destination storage after copying to it.  This ensures that the tree structure and data are written to disk in their final form.  The resulting file (Figure 6) has a ministream that is contiguous and therefore easier to follow application structures and data:

 

0C00h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C10h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C20h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C30h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C40h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C50h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C60h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C70h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C80h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0C90h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0CA0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0CB0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0CC0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0CD0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0CE0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0CF0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D00h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D10h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D20h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D30h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D40h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D50h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D60h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D70h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D80h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0D90h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0DA0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0DB0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0DC0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0DD0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0DE0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0DF0h: 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33  3333333333333333
0E00h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
0E10h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
0E20h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222
0E30h: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32  2222222222222222

Figure 6: Defragmented streams

I’ve attached defrag.cpp to this blog so you can see the code in its completeness.  Using only structured storage API’s, I’ve shown here that it’s possible to order the application stream data in a compound file to be contiguous thereby making it much less formidable to traverse. 

 

Posted by tomjebo | 0 Comments

Attachment(s): defrag.cpp

An ActiveSync Primer

An ActiveSync Primer

Dominic Salemno

What is ActiveSync?

When I speak of ActiveSync, I am speaking in regards to the protocol itself, not the application. In any communications protocol, there is always a purpose for the transmission. In the case of ActiveSync, it provides a means of ensuring that a specific mobile device is consistent in data, e-mail, and contacts with an exchange account. Ultimately, the goal is for the mobile device to contain the most up-to-date information.

How does it work?

The ActiveSync protocol uses request and response commands transported over HTTPS (HTTP Secured by Secure Sockets Layer). Each ActiveSync command is formatted in binary-encoded XML (WBXML) and sent via an HTTP POST (See [MS-ASWBXML]: http://msdn.microsoft.com/en-us/library/dd299442.aspx). This WBXML corresponds directly to XML elements and attributes by a tokenization process. The purpose behind this encoding is for efficient bandwidth usage due to the low-bandwidth nature of mobile clients employing the ActiveSync protocol.

Traversing down the stack, by undoing the tokenization process, you will see the XML Message. Each XML Message will vary in size and structure based upon the type of communications being done. However, the basic principle remains the same. Each XML Message will either be a request or a response.

An analysis of a sample ActiveSync Packet

An ActiveSync XML Message

 

<?xml version="1.0" encoding="utf-8"?>

<FolderSync xmlns="FolderHierarchy:">

  <SyncKey>2</SyncKey>

</FolderSync>

 

Binary-Encoded XML (WBXML) Representation of the XML Message

03 01 6A 00 00 07 56 52 03 32 00 01 01

HTTP POST Header and Body
See [MS-ASHTTP]:
http://msdn.microsoft.com/en-us/library/dd299446.aspx

POST /Microsoft-Server
ActiveSync?Cmd=FolderSync&User=fakename&DeviceId=v140Device&DeviceType=SmartPhone HTTP/1.1
Content-Type: application/vnd.ms-sync.wbxml
MS-ASProtocolVersion: 14.0
User-Agent: ASOM
Host: Contoso.com

03 01 6A 00 00 07 56 52 03 32 00 01 01

An Overview of the WBXML Encoding

The following table presents how the WBXML encoding has happened

Bytes

Description

03

Version number – WBXML version 1.3

01

Unknown public identifier

6A

Charset = UTF-8

00

String table length

00 07

Select code page 7 (FolderHierarchy)

56

<FolderSync>, with content (0x16 + 0x40)

52

<SyncKey>, with content

03

Inline string follows

32 00

2

01

</SyncKey>

01

</FolderSync>

 

Summary

This blog was intended to present an introduction to the ActiveSync protocol. Although the example was fairly simple, this is the first step in grasping the foundation for the subsequent information in the series to this entry. Stay-tuned for more articles getting more in-depth to the intricate details surrounding ActiveSync!

Posted by doms | 0 Comments

Attachment(s): ActiveSync_Primer.pdf

msDS-SupportedEncryptionTypes – Episode 1 - Computer accounts

Introduction

In order to be concise with this article, I need to assume that the reader is familiar with Kerberos and Active Directory.

If not, then I can quickly think of two scenarios:

1)      Your favorite search engine (Bing in my case) took you here as a misunderstanding.

2)      You came because you stumbled upon the name “msDS-SupportedEncryptionTypes” somewhere and you really really want to understand what it is related to, even if you need to learn about Kerberos and Active Directory as a pre-requisite.

Let’s assume (for the sake of the posting) that the option you fall into is #2 and that you are eager to know where to find the docs that are inherent to this article.

Here are the links:

The Kerberos Network Authentication Service (V5): http://www.ietf.org/rfc/rfc4120.txt

[MS-KILE]: Kerberos Protocol Extensions: http://msdn.microsoft.com/en-us/library/cc233855.aspx

[MS-ADA2]: Active Directory Schema Attributes M: http://msdn.microsoft.com/en-us/library/cc220154.aspx

[MS-ADA3]: Active Directory Schema Attributes N-Z: http://msdn.microsoft.com/en-us/library/cc220699.aspx

[MS-ADTS]: Active Directory Technical Specification: http://msdn.microsoft.com/en-us/library/cc223122.aspx

 

Juicy information

In order for the KDC to be able to generate tickets that the target server can read, there has to be some mean of communicating what type of encryptions the involved actors can understand.

For quite a while, that was not an issue because the older versions of Windows that had Kerberos5 implementations (Windows 2000 all flavors, Windows XP and Windows 2003 all flavors) only supported DES (RFC3961) and RC4 (RFC4757) as the methods of encryption.

However, Windows Vista, Windows Server 2008, Windows 7 and Windows Server 2008 R2 incorporated the newer and more secure algorithm AES (RFC3962) (128 AND 256). With this new addition, and with so many machines running previous versions of Windows, it was imperative to have a way to inform which algorithms each particular account could handle and to make sure that when newer algorithms should become available they would not necessarily represent many changes.

msDS-SupportedEncryptionTypes came up as the solution. This AD attribute (defined in MS-ADA2, section 2.324) is present in the Computer, User and Trust objects for Schema version 44 (Windows 2008) and later. Its sole purpose is to hold the values of the encryption types that the account owner supports.

Well, I guess that you could have deducted that from the name of the attribute so; I better go a little deeper.

Its size is 4 bytes, its type is Unsigned Integer and its format is a Bit Mask. The values that it can accept, are defined in the following table (and originally in MS-KILE section 3.1.5.4):


0


1


2


3


4


5


6


7


8


9

1
0


1


2


3


4


5


6


7


8


9

2
0


1


2


3


4


5


6


7


8


9

3
0


1

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

E

D

C

B

A

 

Where the bits are defined as:

Value

Description

A

DES-CBC-CRC

B

DES-CBC-MD5

C

RC4-HMAC

D

AES128-CTS-HMAC-SHA1-96

E

AES256-CTS-HMAC-SHA1-96

 

This is an example of how it looks like in AD (formatted per RFC2989):

CN=server2008,CN=Computers,DC=testdomain,DC=com

msDS-SupportedEncryptionTypes: 0x1F = ( DES_CBC_CRC | DES_CBC_MD5 | RC4_HMAC_MD5 | AES128_CTS_HMAC_SHA1_96 | AES256_CTS_HMAC_SHA1_96 );

 

Although this attribute is present in all the computer objects of the domain regardless of the version of the OS the physical machines have installed, not all of them are aware of its existence hence, older versions (2003 and earlier) do not populate it at any time.

Legacy systems leave it Blank, NULL, Zeroed or Empty at all times. However, more recent versions of the OS do specify a value that could be any combination of the first 5 bits being 31 (0x1F) the default. The initial set up of the value happens at domain join time with subsequent updates via LDAP should the algorithms that the machine supports change for any reason.

When the KDC checks the attribute to decide what encryption algorithm to use in order to encrypt the ticket, it could find basically two scenarios:

1)      The attribute is populated

2)      The attribute is empty

If the attribute is populated, then the deal is done since the KDC can determine the best common algorithm to encrypt the ticket with the value present.

However, if the attribute is empty then the KDC will have to work harder being the next step to check another attribute. This attribute is defined in MS-ADA3 (section 2.341) and described in MS-ADTS (section 2.2.15) and it’s called userAccountControl. This attribute is also a 4 byte Bit Mask that defines many aspects of the account but the only one the KDC is interested in is the DK (ADS_UF_USE_DES_KEY_ONLY ) bit.

This bit defines what legacy encryption method will be used:

1)      If the bit is set, then only DES will be used

2)      If the bit is NOT set, then DES and RC4 can be used

This check is especially relevant in domains that have Win7 and Windows Server 2008 R2 machines joined because those two newer OSs disable their bit by default so older DES is not an option for them.

 

Conclusion

Windows 2008, Vista, Windows 7 and Windows 2008 R2 have expanded the options available when securing resources and communications on the network.

Having the msDS-SupportedEncryptionTypes attribute is a good starting point to further incorporating newer and more secure encryption algorithms in the future.

 

Parsing Pictures in a PowerPoint binary file

 

In this blog I’d like to cover every aspect of parsing Office binary documents, and do it in less than a thousand words.  But, what follows is more realistic, thus more narrow in focus.  Specifically, I’ll examine the PowerPoint binary format from the point of view of parsing/enumerating “Pictures”.  PowerPoint Pictures are found in the Pictures Stream (as you will see below).  By contrast, Shape objects (rectangles, squares, lines, etc) do not exist in the Pictures Stream.  So the discussion and details of the enumeration of Shape objects may be the subject of a future blog.

 

There are several Office binary file formats defined as part of the Open Specification document set and you can find them detailed here.  The one for which I’ll focus my angle of approach is the PowerPoint binary file format specification (MS-PPT).  And, since you can’t find Office pictures in a binary document without referring to the MS-ODRAW specification, much of the details of this blog will derive from definitions of structures in MS-ODRAW.  Since this analysis is specific to “Pictures”, which means an actual inserted picture in the PowerPoint document, to follow along you may refer to MS-PPT section 2.1.3 Pictures Stream: An optional stream whose name MUST be "Pictures".  The contents of the Pictures Stream are defined by the OfficeArtBStoreDelay record as specified in MS-ODRAW section 2.2.21.

 

Looking at MS-ODRAW section 2.2.21 OfficeArtBStoreDelay: This record specifies the delay loaded container of BLIPs in the host application. There is no OfficeArtRecordHeader for this container.

rgfb (variable): An array of OfficeArtBStoreContainerFileBlock records that specifies BLIP data.  The array continues while the rh.recType field of the OfficeArtBStoreContainerFileBlock record is equal to 0xF007 or between 0xF018 and 0xF117, inclusive.

 

For an example, I created a very simple MS-PPT document and inserted (Menu Ribbon -> Insert Tab) a Picture and a ClipArt object.  Based on the above specification details I’ll enumerate the image objects, as shown below (Hex file view after opening the Pictures Stream and delving to the OfficeArtBStoreContainerFileBlock).

 

You’ll find the color coded details below, which explain this hex data block.

A0 46 1D F0 AE 03 00 00 B1 A6 19 08 D8 C3 0B 6F //image data follows

B5 6C A3 98 8C 9E F4 65 FF FF D8 FF E0 00 10 4A

…//skipping forward to the next record

60 21 1B F0 38 6D 00 00 27 CF 5A 3B 2E DC 9E 2E //image data follows//

16 A1 A1 59 52 1B 76 E9 AC A7 00 00 DC F7 FF FF

 

The first type of image object shown above is the JPEG I inserted, as shown in MS-ODRAW section 2.2.22 OfficeArtBStoreContainerFileBlock:

Value            Meaning

0xF007           OfficeArtFBSE record

0xF018 – 0xF117  OfficeArtBlip record

 

I am only dealing with OfficeArtBlip records in my example, so MS-ODRAW section 2.2.23 OfficeArtBlip (note color coded hex dump above):

Value  Meaning

0xF01A OfficeArtBlipEMF  record

0xF01B OfficeArtBlipWMF  record

0xF01C OfficeArtBlipPICT record

0xF01D OfficeArtBlipJPEG record

0xF01E OfficeArtBlipPNG  record

0xF01F OfficeArtBlipDIB  record

0xF029 OfficeArtBlipTIFF record

0xF02A OfficeArtBlipJPEG record

 

The first record is the OfficeArtBlipJPEG, MS-ODRAW section 2.2.27 OfficeArtBlipJPEG:

rh.recVer      MUST be 0x0.

rh.recInstance Specified in the following table.

rh.recType     MUST be 0xF01D.

rh.recLen      An unsigned integer that specifies the number of bytes following the header.

MUST be the size of BLIPFileData plus 17 bytes if recInstance is 0x46A or 0x6E2 or the size of BLIPFileData plus 33 bytes if recInstance is 0x46B or 0x6E3.

 

0x46A    JPEG in RGB color space

0x46B    JPEG in RGB color space

0x6E2    JPEG in CMYK color space

0x6E3    JPEG in CMYK color space

 

Since 0x3AE = 942 bytes you would read the header record and note the type of the record (0xF01D) and skip forward 942 bytes to the next 8 byte header record and repeat while you had records to read in the Pictures Stream.  In this example, I have only two records, and would read the next record as 0xF01B OfficeArtBlipWMF, which is length of 0x6D38 = 27,960 bytes.

 

The following sample code snippet demonstrates how to delve the stream structures with IStorage.  You may use this as a simple starting point in your investigation of the Office binary file structures.  (Note, of course, you will want to implement with error handling and other coding best practices.  This is only a small snippet I used in this example to investigate the stream structures with IStorage)

 

int main(int argc, char* argv[])

{

      HRESULT hr;

      IStorage *pStg = NULL;

      CoInitialize(NULL);

 

      hr = StgOpenStorage(

            L"<path to file>\\powerpoint-file.ppt”,

            NULL,STGM_READ | STGM_SHARE_EXCLUSIVE,NULL,NULL,&pStg);

 

      if (!FAILED(hr))

      {

            IEnumSTATSTG *pEnumStat = NULL;

            pStg->EnumElements(NULL,NULL,NULL,&pEnumStat);

            DWORD dwFetched;

            STATSTG stat;

 

            while (pEnumStat->Next(1,&stat,&dwFetched) == S_OK)

            {

                  if (!wcscmp(L"Pictures",stat.pwcsName))

                  {

                        IStream *pStm = NULL;

                        hr = pStg->OpenStream(

                              stat.pwcsName,

                              NULL,STGM_READ | STGM_SHARE_EXCLUSIVE,NULL,&pStm);

 

                        // add processing here

 

                        pStm->Release();

                  }

            }

            pEnumStat->Release();

            pStg->Release();

      }

      CoUninitialize();

      return 0;

}

 

Note the details of the stat structure in the code snippet above:

Stat              {pwcsName=0x006d3708 "Pictures" type=2 cbSize={...} ...} tagSTATSTG

pwcsName          0x006d3708 "Pictures"               wchar_t *

type              2                                   unsigned long

cbSize            {28918}                             _ULARGE_INTEGER

mtime             {dwLowDateTime=0 dwHighDateTime=0 } _FILETIME

ctime             {dwLowDateTime=0 dwHighDateTime=0 } _FILETIME

atime             {dwLowDateTime=0 dwHighDateTime=0 } _FILETIME

grfMode           0                                   unsigned long

grfLocksSupported 0                                   unsigned long

clsid             {GUID_NULL}                         _GUID

grfStateBits      0                                   unsigned long

reserved          0                                   unsigned long

 

To expand on the preceding code, since you have the length of each picture and the image type, you could read the bytes into a byte array and write them out to disk, or whatever your requirements may entail.

 

I hope this helps shed some light on parsing Office binary files in general and how you might approach parsing PowerPoint binary files for pictures.

Understanding security descriptor defaulting rules for Active Directory objects

This blog post is to help understand the defaulting rules when assigning security descriptors to new active directory (AD) objects.

Background

The SECURITY_DESCRIPTOR structure defines the security attributes of an object. For instance, some of the attributes specify the owner, the access rights, privileges to perform operations, and level of audit logging.

The SECURITY_DESCRIPTOR structure is defined in [MS-DTYP] Section 2.4.6 http://msdn.microsoft.com/en-us/library/cc230366.aspx.

typedef struct _SECURITY_DESCRIPTOR {
  UCHAR Revision;
  UCHAR Sbz1;
  USHORT Control;
  PSID Owner;
  PSID Group;
  PACL Sacl;
  PACL Dacl;
 } SECURITY_DESCRIPTOR, 
 *PSECURITY_DESCRIPTOR;

The Owner field identifies the security identifier (SID) of the object owner. The Group field is the SID of group of the object. The DACL field is the discretionary access control list that specifies the user or group's access on the object. The SACL is the system access control list and controls the audit logging on the object.

Security descriptor for new objects

[MS-DTYP] Section 2.5.2.3 defines the CreateSecurityDescriptor algorithm, which in general, is applicable to all types of objects, e.g. file objects, registry objects, AD objects.

There are four main steps in the procedure of computing a security descriptor for a new object:

Step 1: Compute the Owner field

Step 2: Compute the Group field

Step 3: Compute the DACL

Step 4: Compute the SACL

In the next sections, we review these steps for active directory objects.

Security descriptor for new AD objects

There are some specifics related to AD objects. [MS-ADTS] Section 7.1.3 defines Security Descriptor Requirements for AD objects. It presents the parameters used by the CreateSecurityDescriptor algorithm to compute the resultant security descriptor value of an AD object.

Owner and Group defaulting rules

As specified in [MS-ADTS] 7.1.3.6 Owner and Group Defaulting Rules, the OWNER and GROUP fields are defaulted in the following conditions on the supplied control flags (see [MS-ADTS] 7.1.3.2   SD Flags Control):

§ The security descriptor flags do not include the OWNER bit.

§ The security descriptor flags include the OWNER bit, but the OWNER field in the supplied value is NULL.

In the preceding cases, the OWNER field is defaulted as follows:

1. The subject is a member of the default administrator group (DAG) for the object; the SID of this group becomes the OWNER SID in the security descriptor.

2. The subject's security context contains the TokenOwner field, then the SID contained in this field becomes the OWNER SID of the security descriptor.

3. The subject's SID becomes the OWNER SID of the security descriptor.

In case of a domain functional level of Windows Server 2008 or higher, if the DAG SID is used as the default OWNER field value, then the same SID is written into the GROUP field.

In all other cases, the GROUP field is not modified before the security descriptor value is passed to the CreateSecurityDescriptor algorithm.

Note: A subject is a thread executing in the security context provided by an access token. The creator of the object is the subject that is creating the object.

Rules for Default Administrator Group (DAG)

The rules for the DAG, which is used for OWNER/GROUP defaulting and for OWNER access checks, are defined [MS-ADTS] Section 7.1.3.7   Default Administrators Group. The rules depend on the object location (naming context of domain, configuration, schema, or application), and the requestor token (member of these groups Schema Admins, Domain Administrators, Enterprise Administrators).  

Computing resultant ACL and defaulting rules

When computing the resultant ACL for a new object, two important aspects are ACL inheritance and their defaulting rules. It is important to note these key points when an ACL is built for an AD object compared to other types of objects:

  • Generic inheritable ACEs apply to all types of child objects. Object-specific inheritable ACEs apply only to a specific type of child object.
  • If there is no specified security descriptor, no parent-inheritable ACEs, the operating system uses the ACL from the defaultSecurityDescriptor in the classSchema object, and in the absence thereof, the ACL from the creator's token.

The following procedure summarizes the steps to compute the resultant ACL for a new AD object. This procedure is based on the CreateSecurityDescriptor algorithm with amendments pertaining to the defaultSecurityDescriptor attribute of AD object classes.

  • Condition 1: Parent contains inheritable ACEs
    Step 1. If the control flags on the client allow inheritance then the inheritable ACEs from the parent form the newly created object's initial DACL and SACL
    Step 2. If the control flags on the client do not allow inheritance, set initial DACL and SACL to NULL
    Step 3. If an explicit security descriptor is provided by the client, it is merged with the initial DACL and SACL.
    Step 4. If an explicit security descriptor is not provided by the client then the DACL and SACL from the defaultSecurityDescriptor are merged with the initial DACL and SACL.
  • Condition 2: Parent does not contain inheritable ACEs and the classSchema of the object being created has a defaultSecurityDescriptor.
    If the defaultSecurityDesciptor is present, then the ACLs should be obtained from the defaultSecurityDescriptor.
  • Condition 3: Parent does not contain inheritable ACEs and defaultSecurityDescriptor is absent in the classSchema of the object being created
    If the defaultSecurityDescriptor is absent, then make use of Token as described in the ComputeACL method (MS-DTYP 2.5.2.4).

 

References

[MS-DTYP]: Windows Data Types http://msdn.microsoft.com/en-us/library/cc230273.aspx

[MS-ADTS]: Active Directory Technical Specification http://msdn.microsoft.com/en-us/library/cc223122.aspx

 

More Posts Next page »
 
Page view tracker