Configuring the MAPI download for EXCHANGE 2013

1Introduction

The information in this document applies to the MAPI/CDO 1.2.1 dated May 2013.

With the release of EXCHANGE 2013, certain architectural changes have been made that require compatibility updates to the CDO / MAPI download to support the new version. These updates require additional configuration from MAPI applications (or IT Admins) in order for the client to be able to connect.

1.1Changing Outlook Connectivity in EXCHANGE 2013

1.1.1Limiting connectivityto RPC/HTTP

As part of the architectural changes in EXCHANGE 2013, Outlook connectivity will be limited to RPC/HTTPto facilitate proxyingthrough the CAS role. RPC/TCP connections for the MAPI interface RPC endpoint will be blocked and fail.

1.1.2Personalized RPC Servers

In order to provide a more “stable” RPC endpoint, the RPC server target is no longer the server hosting the RPC Client Access Service (MSExchangeRPC) but is a virtual RPC endpoint that is comprised of the target’s mailbox information. Request to resolve this virtual RPC endpoint via the RFR will still work but return a fake endpoint. Unless MAPI apps are attempting to resolve the RPC endpoint independent of the RFR protocol, this change should not affect apps.

1.2EXCHANGE 2013Compatibility Updates to CDO / MAPI Download

In order to work with EXCHANGE 2013 and the changes mentioned above, the MAPI download has been updated to support RPC/HTTP connections. In order to connect via RPC/HTTP, applications using the MAPI download will need to some minimal configuration onthe RPC Proxy servers to be used prior to establishing RPC/HTTP connections. The configuration can be done programmaticallyvia:

  • Setting properties in MAPI profile or
  • Setting registry keys on the client machine

2Configuration

RPC Proxy server configuration and related settings can be configured via 2 methods:

  1. Setting registry keys on the client machine where the MAPI download is being used
  2. Settings properties in the global section of the MAPI Profile used to create a connection

Settings in made via the MAPI Profile will override settings made in registry. In either flavor, you need to specify the personalized name of RPC endpoint in the “Exchange Mailbox Server” field while creating profile with the Mapi profile wizard. Personalized mailbox server name can be obtained in the autodiscover response like:

<Protocol>

<Type>EXCH</Type>

<Server>f89f9bc8-b7ef-40a6-8144-9e7de65aabb1@Exchange 2013be.contoso.com</Server>

2.1Setting Registry Keys

Registry settings allow for a mapping of domains and the related proxy endpoint that should be used. The mapping will support wildcard matching of domains as well as support for mapping multiple domains to a single proxy endpoint.

2.1.1Authentication

There is no mechanism for defining a separate authentication context if configuringvia the registry. Authentication context will use whatever the application’s authentication context.

2.1.2Registry Key

The registry keys should be located under:

HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem

2.1.3Registry Value

The MAPI download will read all mappings from all values with value type REG_SZ and value names that begin with RPCHTTPProxyMap

It is recommended that each application append an additional identifier to prevent overwriting existing mappings like the following:

  • RPCHTTPProxyMap_ContosoApp1
  • RPCHTTPProxyMap_ContosoApp2

2.1.4Registry Data format

The standard format of data within the registry value that the MAPI download consumes is as follows:

DomainMapping[;AdditionalDomainMappings>]

Multiple domain mappings within a single registry value are delimited by a semicolon (;).

The following are samples that include single and multiple mappings:

  • contoso.com=
  • hosted.contoso.com,cloud.contoso.com=
2.1.4.1DomainMapping and <AdditionalDomainMapping> format

DomainMapping and AdditionalDomainMappings are defined as follows:

Domain>[,AdditionalDomains]=ProxyServerConfiguration

Domains are delimited from the proxy configuration by an equal sign (=).

2.1.4.2<Domain> and <AdditionalDomains format

Allows scoping of registry settings to multiple mailboxes:

Required? / Yes
Server configuration / n/a – settings are configured w/ different scopes
Autodiscover / n/a – results are scoped to user
MAPI profile property / n/a
Format / <Domain> and AdditionalDomains can be any domain or substring of a domain including * and ?wildcard characters. Additional domains beyond the first are delimited by a comma (,). At least 1 domain is required for the mapping to be valid.
The syntax of the wildcards is:
  • '*' matches a preceding string followed by any character one or more times. For example, "*contoso.com" matches either "hq.contoso.com" or "sub.contoso.com". “*” matches any domain.
  • '?' matches a preceding string followed by any single character. For example, "cafe?.contoso.com" matches "cafe1.contoso.com" or "cafe2.contoso.com"

Examples /
  • *.contoso.com
  • *contoso.com
  • *cloud.contoso.com
  • *.contoso.com
  • *cloud.contoso.com, *.hosted.contoso.com

2.1.4.3ProxyServerConfiguration format

ProxyServerConfigurationis defined as follows:

ProxyServer[,ProxyServerSettings>]

2.1.5<Proxy Server> format

Indicate the FQDN of the RPC/HTTP endpoint that should be used forthe domains previously denoted:

Required? / Yes
Server configuration / *-OutlookAnywhere –ExternalHostname
*-OutlookAnywhere –InternalHostname
Autodiscover / <Protocol>
<Type>EXHTTP</Type>
<Server>
MAPI profile property / PR_PROFILE_RPC_PROXY_SERVER (PT_STRING8)
Format / The value of this setting should be a standard FQDN. The prefix of vs. is used to indicate whether an HTTP or an HTTPS connection should be made. The proxy server is required for a valid domain mapping.
Example /

2.1.6ProxyServerSettings format

ProxyServerSettingsis defined as follows:

RpcHttpAuthenticationMethod,RpcAuthenticationMethod>,IgnoreSslCert>

If RpcAuthenticationMethod is explicitly set, a comma (,) is required to be present prior (whether or not a value for RpcHttpAuthenticationMethod is set)

If IgnoreSslCert is explicitly set, 2 commas (,) are required to be present prior (whether or not values for RpcAuthenticationMethod or RpcHttpAuthenticationMethod are set)

2.1.7RpcHttpAuthenticationMethod format

Configures the type of authentication that should be used atthe RPC/HTTP layer:

Required? / No – default value is NTLM
Server configuration / *-OutlookAnywhere–ExternalClientAuthenticationMethod
*-OutlookAnywhere–InternalClientAuthenticationMethod
Autodiscover / Protocol>
<Type>EXHTTP</Type>
AuthPackage>Negotiate</AuthPackage
MAPI profile property / PR_PROFILE_RPC_PROXY_SERVER_AUTH_PACKAGE
Format / Valid values consist of the following:
  • Negotiate
  • NTLM
  • WinNT
  • Basic
WinNT and NTLM are equivalent in this context.

2.1.8RpcAuthenticationMethod> format

Configures the type of authentication that should be used at the RPC/TCP layer:

Required? / No – default value is NTLM
Server configuration / n/a – uses Windows authentication
Autodiscover / <Protocol>
<Type>EXCH</Type>
AuthPackage>Anonymous</AuthPackage
MAPI profile property / PR_PROFILE_AUTH_PACKAGE
Format / Valid values consist of the following:
  • Negotiate
  • NTLM
  • WinNT
  • Anonymous
  • None
WinNT and NTLM are equivalent in this context.

2.1.9IgnoreSslCert> format

Configures whether or not the SSL certificate should be validated:

Required? / No – default value is false
Server configuration / *-OutlookProvider–CertPrincipalName:None
Autodiscover / <Protocol>
<Type>EXHTTP</Type>
CertPrincipalName>On</CertPrincipalName
MAPI profile property / PR_PROFILE_RPC_PROXY_SERVER_FLAGS
Format / Valid values consist of the following:
  • True
  • 1
  • Y
  • Yes
Any other values will be treated as false.

2.2Setting MAPI Properties for a Dynamic MAPI Profile

To configure the proxy server by setting MAPI Properties, there are 5 properties that can be configured:

Property / Type / Tag
PR_PROFILE_RPC_PROXY_SERVER / STRING8 / 0x6622
PR_PROFILE_RPC_PROXY_SERVER_FLAGS / LONG / 0x6623
PR_PROFILE_AUTH_PACKAGE / LONG / 0x6619
PR_PROFILE_RPC_PROXY_SERVER_AUTH_PACKAGE / LONG / 0x6627
PR_PROFILE_AUTH_USER_W / UNICODE / 0x66A0
PR_PROFILE_AUTH_PASSWORD / BINARY / 0x66A1

If configuration was set in the registry, setting these properties will override the registry settings.

2.2.1PR_PROFILE_RPC_PROXY_SERVER

Indicate the FQDN of the RPC/HTTP endpoint that should be used for the connections made via this MAPI Profile:

Required? / Yes - if not set, RPC/TCP will be attempted (assuming the registry has not been set)
Server configuration / *-OutlookAnywhere–ExternalHostname
*-OutlookAnywhere–InternalHostname
Autodiscover / <Protocol>
<Type>EXHTTP</Type>
<Server>
Registry setting / <Proxy Server>
Format / The value of this setting should be a standard FQDN. The prefix of vs. is used to indicate whether an HTTP or an HTTPS connection should be made. If no prefix is used, SSL configuration will default to what is set in PR_PROFILE_RPC_PROXY_SERVER_FLAGS
Example /
  • mail.contoso.com

2.2.2PR_PROFILE_RPC_PROXY_SERVER_FLAGS

Configures various options related to RPC/HTTP. This property is required to be set otherwise the connection will only attempt RPC/TCP connections (assuming the registry has not been set):

Required? / Yes
Server configuration / enable-OutlookAnywhere
*-OutlookProvider–CertPrincipalName:None
Autodiscover / <Protocol>
<Type>EXHTTP</Type>
<SSL>On</SSL>
CertPrincipalName>On</CertPrincipalName
Registry setting / Only 0x10 has a corresponding registry setting - IgnoreSslCert
Format / The following bits are defined for usage:
Bit / Description
0x1 / Enable RPC/HTTP – This must be set to 1 to use RPC/HTTP. If set to 0, RPC/HTTP will not be used even if all other properties are set.
0x2 / Use SSL – if the prefix of vs. not present on PR_PROFILE_RPC_PROXY_SERVER, setting this to 1 will use SSL, 0 will NOT use SSL. If the prefix is present on PR_PROFILE_RPC_PROXY_SERVER, it will override this setting.
0x10 / Ignore SSL Certificate errors – if set to 1, the SSL certificate principal name will not be validated
Examples /
  • 0x1
  • 0x13

2.2.3PR_PROFILE_AUTH_PACKAGE

Configures the type of authentication that should be used at the RPC/TCP layer:

Required? / No – default value is RPC_C_AUTHN_WINNT
Server configuration / n/a – uses Windows authentication
Autodiscover / <Protocol>
<Type>EXCH</Type>
AuthPackage>Anonymous</AuthPackage
Registry setting / RpcAuthenticationMethod
Format / Valid values consist of the following:
  • RPC_C_AUTHN_GSS_NEGOTIATE
  • RPC_C_AUTHN_WINNT
  • RPC_C_AUTHN_NONE

2.2.4PR_PROFILE_RPC_PROXY_SERVER_AUTH_PACKAGE

Configures the type of authentication that should be used at the RPC/HTTP layer:

Required? / No – default value is RPC_C_HTTP_AUTHN_SCHEME_NTLM
Server configuration / *-OutlookAnywhere –ExternalClientAuthenticationMethod
*-OutlookAnywhere –InternalClientAuthenticationMethod
Autodiscover / <Protocol>
<Type>EXHTTP</Type>
AuthPackage>Negotiate</AuthPackage
Registry setting / RpcHttpAuthenticationMethod
Format / Valid values consist of the following:
  • RPC_C_HTTP_AUTHN_SCHEME_NEGOTIATE
  • RPC_C_HTTP_AUTHN_SCHEME_NTLM
  • RPC_C_HTTP_AUTHN_SCHEME_BASIC

2.2.5PR_PROFILE_AUTH_USER_W

User identity can be set via this property:

Required? / No – if not set, authentication context of the app will be used
Server configuration / n/a
Autodiscover / n/a
Registry setting / n/a – auth can only be set via MAPI Profile
Format / The user identity can either be in the domain\alias format or the UPN depending on what is required
Example /
  • Contoso\user

2.2.6PR_PROFILE_AUTH_PASSWORD

User password can be set via this property:

Required? / No – if not set, authentication context of the app will be used
Server configuration / n/a
Autodiscover / n/a
Registry setting / n/a – auth can only be set via MAPI Profile
Format / The password should be encrypted via the standard Windows Crypto APIs. If used, the user context using the MAPI Profile must be the same user context that set the password or the MAPI download will be unable to decrypt the password. See “3.1 - Encryption of Password” for an example of encrypting the password.

3Code Samples

3.1Encryption of Password

An example using the Windows API to encrypt the password is as follows:

#include"mapix.h"

#include"wincrypt.h"

#ifndef PR_PROFILE_AUTH_USER_W

#define PR_PROFILE_AUTH_USER_WPROP_TAG(PT_UNICODE, 0x66A0)

#endif

#ifndef PR_PROFILE_AUTH_PASSWORD

#define PR_PROFILE_AUTH_PASSWORD PROP_TAG(PT_BINARY, 0x66A1)

#endif

/*

- HrSetProfileUserNameAndPassword

*

* Description:

*Sets a user name and password into a MAPI profile section.

*

* Parameters:

*lpProfSect - MAPI profile section to write properties (MUST be opened with
* MAPI_MODIFY flag)

*pwszUserName - User name (i.e. or CONTOSO\johndoe)

*pwszPassword - Password

*

* Returns: HRESULT

*

*/

HRESULT HrSetProfileUserNameAndPassword(LPPROFSECT lpProfSect, LPWSTR pwszUserName, LPWSTR pwszPassword)

{

HRESULThr = S_OK;

DATA_BLOBdataBlobIn = {0};

DATA_BLOBdataBlobOut = {0};

SPropValuepropValues[2] = {0};

// Validate parameters

if ((lpProfSect == NULL) ||

(pwszUserName == NULL) ||

(pwszPassword == NULL))

{

hr = MAPI_E_INVALID_PARAMETER;

goto Cleanup;

}

// Encrypt password based on local user authentication

dataBlobIn.pbData = (LPBYTE)pwszPassword;

// Include NULL character

dataBlobIn.cbData = (::wcslen(pwszPassword) + 1) * sizeof(WCHAR);

if (!CryptProtectData(

dataBlobIn,

NULL,

NULL,

NULL,

NULL,

0,

dataBlobOut))

{

hr = HRESULT_FROM_WIN32(::GetLastError());

goto Cleanup;

}

// Set properties on profile section

propValues[0].ulPropTag = PR_PROFILE_AUTH_USER_W;

propValues[0].Value.lpszW = pwszUserName;

propValues[1].ulPropTag = PR_PROFILE_AUTH_PASSWORD;

propValues[1].Value.bin.lpb = dataBlobOut.pbData;

propValues[1].Value.bin.cb = dataBlobOut.cbData;

hr = lpProfSect->SetProps(2, (LPSPropValue)&propValues, NULL);

Cleanup:

// Free memory returned from CryptProtectData

if (dataBlobOut.pbData != NULL)

{

LocalFree(dataBlobOut.pbData);

}

returnhr;

}

3.2Programmatically Creating a Mapi Profile

#include "windows.h"

#include "mapix.h"

#include "mapiutil.h"

#include "mapiguid.h"

staticconst char rgchEMSService[] = "MSEMS";

#define PR_PROFILE_CONFIG_FLAGS PROP_TAG( PT_LONG, 0x6601)

#define PR_PROFILE_UNRESOLVED_NAME PROP_TAG( PT_STRING8, 0x6607)

#define PR_PROFILE_UNRESOLVED_SERVER PROP_TAG( PT_STRING8, 0x6608)

#define PR_PROFILE_CONNECT_FLAGS PROP_TAG(PT_LONG, 0x6604)

#define PR_PROFILE_RPC_PROXY_SERVER PROP_TAG(PT_STRING8, 0x6622)

#define PR_PROFILE_RPC_PROXY_SERVER_FLAGS PROP_TAG(PT_LONG, 0x6623)

#define PR_PROFILE_RPC_PROXY_SERVER_PRINCIPAL PROP_TAG(PT_STRING8, 0x6625)

#define PR_PROFILE_RPC_PROXY_SERVER_AUTH_PACKAGE PROP_TAG(PT_LONG, 0x6627)

#define PR_PROFILE_AUTH_PACKAGE PROP_TAG(PT_LONG, 0x6619)

#define PR_PROFILE_AUTH_USER_W PROP_TAG(PT_UNICODE, 0x66A0)

#define PR_PROFILE_AUTH_PASSWORD PROP_TAG(PT_BINARY, 0x66A1)

#pragma comment( lib, "mapi32" )

// Bit values for PR_PROFILE_RPC_PROXY_SERVER_FLAGS

#define PRXF_ENABLED ((DWORD)0x00000001)

#define PRXF_SSL ((DWORD)0x00000002)

#define PRXF_IGNORE_SEC_WARNING ((DWORD)0x00000010)

// Bit values for PR_PROFILE_CONFIG_FLAGS

#define CONFIG_SERVICE ((ULONG)0x00000001)

// Bit values for PR_PROFILE_CONNECT_FLAGS

#define CONNECT_USE_SEPARATE_CONNECTION ((ULONG)0x4)

#define CONNECT_NO_NOTIFICATIONS ((ULONG)0x20)

#define CONNECT_IGNORE_NO_PF ((ULONG)0x8000)

HRESULT HrCreateProfile(

LPSTR pszProfileName,

LPSTR pszServer,

LPSTR pszMailbox,

LPSTR pszRpcHttpProxy,

DWORD dwHttpAuthn,

DWORD dwAuthn,

LPWSTR pwszUser,

LPWSTR pwszPassword)

{

HRESULT hr = S_OK;

SPropValue pspvService[20] = {0};

BOOL fProfileCreated = FALSE;

ULONG ulProps = 0;

LPPROFADMIN pProfAdmin = NULL;

LPSERVICEADMIN pServiceAdmin = NULL;

LPPROFSECT pProfSect = NULL;

LPMAPITABLE pTable = NULL;

LPSRowSet pRows = NULL;

DATA_BLOB dataBlobIn = {0};

DATA_BLOB dataBlobOut = {0};

SizedSPropTagArray (2, spta) = {2,

{PR_SERVICE_UID,

PR_SERVICE_NAME}

};

ULONG ulIndex = 0;

///////////////////////////////////

// Create Temporary MAPI Profile //

///////////////////////////////////

hr = MAPIAdminProfiles(0, &pProfAdmin);

if (FAILED(hr))

{

printf("MAPIAdminProfiles failed (hr = %08lX)\n", hr);

goto Error;

}

hr = pProfAdmin->CreateProfile((LPTSTR)pszProfileName, NULL, 0, 0);

if (hr == MAPI_E_NO_ACCESS)

{

printf("pProfAdmin->CreateProfile failed (hr = %08lX)\n", hr);

pProfAdmin->DeleteProfile((LPTSTR)pszProfileName, 0);

hr = pProfAdmin->CreateProfile((LPTSTR)pszProfileName, NULL, 0, 0);

}

if (FAILED(hr))

{

printf("pProfAdmin->CreateProfile failed (hr = %08lX)\n", hr);

goto Error;

}

fProfileCreated = TRUE;

hr = pProfAdmin->AdminServices(

(LPTSTR)pszProfileName,

NULL,

NULL,

0,

&pServiceAdmin);

if (FAILED(hr))

{

printf("pProfAdmin->AdminServices failed (hr = %08lX)\n", hr);

goto Error;

}

hr = pServiceAdmin->CreateMsgService(

(LPTSTR)rgchEMSService,

(LPTSTR)rgchEMSService,

NULL,

0);

if (FAILED(hr))

{

printf("pMsgServiceAdmin->CreateMsgService failed (hr = %08lX)\n", hr);

goto Error;

}

hr = pServiceAdmin->GetMsgServiceTable(0, &pTable);

if (FAILED(hr))

{

printf("pMsgServiceAdmin->GetMsgServiceTable failed (hr = %08lX)\n", hr);

goto Error;

}

hr = HrQueryAllRows(pTable, (LPSPropTagArray)&spta, NULL, NULL, 0, &pRows);

if (FAILED(hr))

{

printf("HrQueryAllRows failed (hr = %08lX)\n", hr);

goto Error;

}

if ((pRows->cRows != 1) ||

(pRows->aRow->cValues != 2) ||

(pRows->aRow->lpProps[0].ulPropTag != PR_SERVICE_UID))

{

printf("pRows not valid!\n");

hr = E_FAIL;

goto Error;

}

ulProps = 0;

pspvService[ulProps].ulPropTag = PR_CONVERSION_PROHIBITED;

pspvService[ulProps].Value.b = TRUE;

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_CONFIG_FLAGS;

pspvService[ulProps].Value.l = CONFIG_SERVICE;

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_CONNECT_FLAGS;

pspvService[ulProps].Value.l = CONNECT_USE_SEPARATE_CONNECTION | CONNECT_NO_NOTIFICATIONS | CONNECT_IGNORE_NO_PF;

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_UNRESOLVED_SERVER;

pspvService[ulProps].Value.lpszA = pszServer;

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_UNRESOLVED_NAME;

pspvService[ulProps].Value.lpszA = pszMailbox;

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_RPC_PROXY_SERVER;

pspvService[ulProps].Value.lpszA = pszRpcHttpProxy;

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_RPC_PROXY_SERVER_AUTH_PACKAGE;

pspvService[ulProps].Value.l = dwHttpAuthn; // RPC_C_HTTP_AUTHN_SCHEME_NTLM, RPC_C_HTTP_AUTHN_SCHEME_BASIC

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_RPC_PROXY_SERVER_FLAGS;

pspvService[ulProps].Value.l = PRXF_ENABLED | PRXF_SSL; // | PRXF_IGNORE_SEC_WARNING;

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_AUTH_PACKAGE;

pspvService[ulProps].Value.l = dwAuthn; // RPC_C_AUTHN_WINNT, RPC_C_AUTHN_NEGOTIATE, RPC_C_AUTHN_NONE

ulProps++;

pspvService[ulProps].ulPropTag = PR_PROFILE_AUTH_USER_W;

pspvService[ulProps].Value.lpszW = pwszUser;

ulProps++;

LPBYTE pbData = (LPBYTE)pwszPassword;

DWORD cbData = (wcslen(pwszPassword) + 1) * sizeof(WCHAR);

dataBlobIn.pbData = pbData;

dataBlobIn.cbData = cbData;

if (!CryptProtectData(

&dataBlobIn,

NULL, // desc

NULL, // optional

NULL, // reserver

NULL, // prompt struct

0, // flags

&dataBlobOut))

{

printf("CryptProtectData failed!\n");

hr = E_FAIL;

goto Error;

}

pspvService[ulProps].ulPropTag = PR_PROFILE_AUTH_PASSWORD;

pspvService[ulProps].Value.bin.lpb = dataBlobOut.pbData;

pspvService[ulProps].Value.bin.cb = dataBlobOut.cbData;

ulProps++;

hr = pServiceAdmin->ConfigureMsgService(

(LPMAPIUID)pRows->aRow->lpProps[0].Value.bin.lpb,

NULL,

0,

ulProps,

pspvService);

if (FAILED(hr))

{

printf("pMsgServiceAdmin->ConfigureMsgService failed (hr = %08lX)\n", hr);

goto Error;

}

Cleanup:

if (pProfSect)

{

pProfSect->Release();

pProfSect = NULL;

}

if (pRows)

{

FreeProws(pRows);

pRows = NULL;

}

if (pTable)

{

pTable->Release();

pTable = NULL;

}

if (pServiceAdmin)

{

pServiceAdmin->Release();

pServiceAdmin = NULL;

}

if (pProfAdmin)

{

pProfAdmin->Release();

pProfAdmin = NULL;

}

if (dataBlobOut.pbData != NULL)

{

LocalFree(dataBlobOut.pbData);

dataBlobOut.pbData = NULL;

}

returnhr;

Error:

if (fProfileCreated)

{

pProfAdmin->DeleteProfile((LPTSTR)pszProfileName, 0);

}

goto Cleanup;

}