Microsoft KB Archive/305731

= PRB: Problem with the COM+ Security When Impersonating the User =

Article ID: 305731

Article Last Modified on 2/13/2002

-

APPLIES TO


 * Microsoft COM+ 1.0

-



This article was previously published under Q305731



SYMPTOMS
When a Component Object Model (COM) component impersonates a specific user and calls other COM components, you expect the called components to run under the identity of the impersonated user. You may not see the same behavior when the components are deployed in COM+ server applications.



CAUSE
If the components are deployed as separate server packages, they run under the identity that is determined by the identity of the COM+ application. They run under the caller's identity only when this is specified by using the CoImpersonateClient function.

This behavior is by design. Note also that this is standard COM behavior and not unique to COM+.



Example
Name the first COM component Component1 and assume that it impersonates a specific user. Also assume that Component1 calls another COM component called &quot;Server&quot;.

Case 1: Client and Server Are In-Process Components

Both Component1 and Component2 are legacy in-process servers, which means that they would be running under the calling thread's identity. Component1 calls the LogonUser and ImpersonateLoggedOnUser functions, which make the current thread's identity that of the specified user, hence Component2 runs as the same user.

Case 2: Client and Server Are Deployed in Separate COM+ Applications

When Component1 and Component2 are in separate COM+ applications, they become out-of-process servers, each running in its own DLLHost.exe process. Although Component1 changes the current thread identity to that of &quot;userA&quot;, Component2 still runs as the user specified in its Identity tab (RunAs key), because the call will be delivered across the process boundary. However, Component2 could call CoImpersonateClient or the CoImpersonateClient function and could have impersonated its caller (userA) for the duration of the method call or until the CoRevertToSelf function is called.

Steps to Reproduce the Problem
  Create Component2.dll, and then add the following code to its class: ... BOOLEAN GetAppUserName(BSTR *pbstAppUserName) {   LPTSTR      ptstrUserName = NULL; DWORD      dwLength = (1024 + 1) * sizeof(TCHAR); BOOLEAN    bSuccess = false; CComBSTR   cbstUserName; if ((ptstrUserName = (LPTSTR)LocalAlloc(LPTR, dwLength))) {               //Retrieve the Caller's identity if (GetUserNameEx(NameSamCompatible,ptstrUserName,&dwLength)) cbstUserName = ptstrUserName;

LocalFree(ptstrUserName); *pbstAppUserName = cbstUserName.Copy; bSuccess = true; }

return bSuccess; }

STDMETHODIMP CComponent2::Test(void) {   USES_CONVERSION; CComBSTR cbstUserName;

GetAppUserName(&cbstUserName);

MessageBox(NULL, OLE2T(cbstUserName), LPCSTR(L&quot;Server&quot;), MB_OK);

return S_OK; }

 Follow these steps:   Add the following include:  Add Secur32.lib to the included libraries.  Add the following precompiler directive: SECURITY_WIN32  </li> Compile the code, and then build Component1.dll.</li>  Build Component1.dll as a COM component. Add the following code to the component's class: ... // CComp1 BOOLEAN GetAppUserName(BSTR *pbstAppUserName) {   LPTSTR      ptstrUserName = NULL; DWORD      dwLength = (1024 + 1) * sizeof(TCHAR); BOOLEAN    bSuccess = false; CComBSTR   cbstUserName; if ((ptstrUserName = (LPTSTR)LocalAlloc(LPTR, dwLength))) {      if (GetUserNameEx(NameSamCompatible,ptstrUserName,&dwLength)) cbstUserName = ptstrUserName;
 * 1) include &quot;Security.h&quot;

LocalFree(ptstrUserName); *pbstAppUserName = cbstUserName.Copy; bSuccess = true; }

return bSuccess; }

STDMETHODIMP CComp1::Test(void) {   CComPtr<IComponent2>  spICompServ; HRESULT    hr; CComBSTR   cbstUserName; CComBSTR   cbstUserId; CComBSTR   cbstPassword; CComBSTR   cbstDomain; HANDLE     hToken;

USES_CONVERSION;

cbstUserId += L&quot;account&quot;; cbstPassword += L&quot;password12&quot;; cbstDomain += L&quot;machinename&quot;;

if (!LogonUser(OLE2T(cbstUserId), OLE2T(cbstDomain), OLE2T(cbstPassword),LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,&hToken)) {       return E_FAIL; }   if (!ImpersonateLoggedOnUser(hToken)) {       return E_FAIL; }

GetAppUserName(&cbstUserName);

MessageBox(NULL, OLE2T(cbstUserName), &quot;Component 1&quot;, MB_OK);

//Create &quot;Server&quot; hr = spICompServ.CoCreateInstance(CLSID_CComponent2); //Call Server's method hr = spICompServ->Test; RevertToSelf; CloseHandle(hToken);

return hr; } ...                   </li> Again, follow these steps:   Add the following include: </li> Add Secur32.lib to the included libraries.</li>  Add the following precompiler directive: SECURITY_WIN32 </li></ol> </li> Add Component2_i.c and Component2.h includes.</li>  Create a client application that calls these COM components. You can save the following in a text file and rename the extension to .vbs: Dim o Set o = CreateObject(&quot;Component1.Comp1&quot;) o.Test End Sub </li></ol>
 * 1) include &quot;Security.h&quot;

If Component1 and Component2 are registered as in-process components, the components display message boxes with &quot;corpdomain\userA&quot;. If Component1 and Component2 are registered as separate COM+ applications, Component1 displays &quot;corpdomain\userA&quot;, and Component2 displays the username of the identity of the COM+ application in which it resides.

Keywords: kbprb KB305731

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.