Configuring the MAPI download for EXCHANGE 2013

INTRODUCTION

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.

CHANGING OUTLOOK CONNECTIVITY IN EXCHANGE 2013

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

Personalized 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.

EXCHANGE 2013 COMPATIBILITY 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 on the RPC Proxy servers to be used prior to establishing RPC/HTTP connections. The configuration can be done programmatically via:  Setting properties in MAPI profile or

 Setting registry keys on the client machine

CONFIGURATION

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:

EXCH f89f9bc8-b7ef-40a6-8144-9e7de65aabb1@Exchange 2013be.contoso.com

SETTING 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.

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

Registry Key The registry keys should be located under:

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

Registry 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

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

[;]

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=https://mail.contoso.com,ntlm,ntlm,false

 hosted.contoso.com,cloud.contoso.com=https://cloud.contoso.com,basic,anonymous,tru e;contoso.com=https://mail.contoso.com,ntlm,ntlm,false

and format and are defined as follows:

[,]=

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

and format Allows scoping of registry settings to multiple mailboxes:

Required? Yes Server n/a – settings are configured w/ different scopes configuration Autodiscover n/a – results are scoped to user MAPI profile n/a property Format and 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

format is defined as follows:

[,]

format Indicate the FQDN of the RPC/HTTP endpoint that should be used for the domains previously denoted: Required? Yes Server *-OutlookAnywhere –ExternalHostname configuration *-OutlookAnywhere –InternalHostname Autodiscover EXHTTP https://mail.contoso.com MAPI profile PR_PROFILE_RPC_PROXY_SERVER (PT_STRING8) property Format The value of this setting should be a standard FQDN. The prefix of http:// vs. https:// 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  http://mail.contoso.com  https://mail.contoso.com

format is defined as follows:

,,

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)

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

Required? No – default value is NTLM Server *-OutlookAnywhere –ExternalClientAuthenticationMethod configuration *-OutlookAnywhere –InternalClientAuthenticationMethod Autodiscover EXHTTP Negotiate MAPI profile PR_PROFILE_RPC_PROXY_SERVER_AUTH_PACKAGE property Format Valid values consist of the following:  Negotiate  NTLM  WinNT  Basic

WinNT and NTLM are equivalent in this context.

format Configures the type of authentication that should be used at the RPC/TCP layer: Required? No – default value is NTLM Server n/a – uses Windows authentication configuration Autodiscover EXCH Anonymous MAPI profile PR_PROFILE_AUTH_PACKAGE property Format Valid values consist of the following:  Negotiate  NTLM  WinNT  Anonymous  None WinNT and NTLM are equivalent in this context.

format Configures whether or not the SSL certificate should be validated:

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

SETTING 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_PACKAG LONG 0x6627 E 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. PR_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 *-OutlookAnywhere –ExternalHostname configuration *-OutlookAnywhere –InternalHostname Autodiscover EXHTTP https://mail.contoso.com Registry setting Format The value of this setting should be a standard FQDN. The prefix of http:// vs. https:// 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  http://mail.contoso.com  https://mail.contoso.com  mail.contoso.com

PR_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 enable-OutlookAnywhere configuration *-OutlookProvider –CertPrincipalName:None Autodiscover EXHTTP On On Registry setting Only 0x10 has a corresponding registry setting - 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 http:// vs. https:// is 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

PR_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 n/a – uses Windows authentication configuration Autodiscover EXCH Anonymous Registry setting Format Valid values consist of the following:  RPC_C_AUTHN_GSS_NEGOTIATE  RPC_C_AUTHN_WINNT  RPC_C_AUTHN_NONE

PR_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 *-OutlookAnywhere –ExternalClientAuthenticationMethod configuration *-OutlookAnywhere –InternalClientAuthenticationMethod Autodiscover EXHTTP Negotiate Registry setting 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

PR_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 n/a configuration 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  [email protected] PR_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 n/a configuration 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 “Encryption of Password - Encryption of Password” for an example of encrypting the password.

CODE SAMPLES

ENCRYPTION 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_W PROP_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. [email protected] or CONTOSO\johndoe) * pwszPassword - Password * * Returns: HRESULT * */ HRESULT HrSetProfileUserNameAndPassword(LPPROFSECT lpProfSect, LPWSTR pwszUserName, LPWSTR pwszPassword) { HRESULT hr = S_OK; DATA_BLOB dataBlobIn = {0}; DATA_BLOB dataBlobOut = {0}; SPropValue propValues[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); }

return hr; }

PROGRAMMATICALLY CREATING A MAPI PROFILE #include "windows.h" #include "mapix.h" #include "mapiutil.h" #include "mapiguid.h" static const 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; }

return hr;

Error: if (fProfileCreated) { pProfAdmin->DeleteProfile((LPTSTR)pszProfileName, 0); }

goto Cleanup; }