Page #88 (Server-Side Security Programming)


Client-Side Security Programming

A client has to configure the underlying RPC run time to use one or more security packages and to set the default authentication level (and other security parameters) for the process. This is done by calling the CoInitializeSecurity function.

CoInitializeSecurity is perhaps the most important function that deals with COM security. Every process that uses COM (client or server) has to call CoInitializeSecurity. The function has to be called just once and before any interface is marshaled in or out of the process. If not called explicitly, COM will call the function automatically the first time a proxy (for the client) or a stub (for the server) is created.

Why didn t we deal with this function earlier when we were discussing server-side programming issues? Because this function cannot be called from a DLL-based component. Only EXE-based components (supported as non-configured components under COM+) are required to make this call and we haven t discussed EXE-based components in this book. For a configured COM+ component that is part of a server application, the COM+ surrogate (DllHost.exe) calls this function on startup.

The following is the prototype for the CoInitializeSecurity function:

 HRESULT CoInitializeSecurity(   [in] PSECURITY_DESCRIPTOR    pSecDesc,          // Server    [in] LONG      cAuthSvc,                        // Server    [in] SOLE_AUTHENTICATION_SERVICE *asAuthSvc,    // Server    [in] void      *pReserved1,                     // NULL    [in] DWORD     dwAuthnLevel,                    // Client/Server    [in] DWORD     dwImpLevel,                      // Client    [in] SOLE_AUTHENTICATION_LIST *pAuthList,       // Client    [in] DWORD dwCapabilities,                      // Client/Server    [in] void *pReserved3 );                        // NULL 

CoInitializeSecurity has a fairly large number of parameters. However, some of these parameters are meaningful only if the process is running as a server. We will focus on the parameters that the client process can set. Parameters pSecDesc, cAuthSvc, and asAuthSvc, are pure server-side settings and will be set to NULL, -1, and NULL, respectively.

Parameter dwAuthnLevel is used to specify the authentication level (Table 7.1). Each higher level of authentication adds more security, but at the expense of decreased performance. A reasonable value to specify that lets COM pick the right authentication level using the security blanket negotiation is RPC_C_AUTHN_LEVEL_DEFAULT.

Parameter dwCapabilities is used to specify any extra authentication capabilities that the client wishes to use. As discussed earlier, authentication capabilities are defined by the EOAC_xxx constants. A combination of EOAC_xxx constants can be specified as the desired capabilities. EOAC_NONE is specified when no authentication capabilities are desired. We will look at some other useful capabilities as we go along.

Parameter dwImpLevel is used to specify the impersonation level (Table 7.2) and parameter pAuthList is used to specify the identity that should be used with each authentication service. Both these security parameters deserve detailed attention.

Impersonation Levels

The level of impersonation affects the following:

  1. The ability of a server to access local resources, such as a local NTFS file.

  2. The ability of a server to access network resources, such as an NTFS file present on a shared network.

  3. The ability of a server to project the identity of the caller to another server on a remote machine.

In order to demonstrate the effect of various impersonation levels, we will conduct an experiment that involves chaining a call across various machines.

The experiment requires three computers, all running Windows 2000. In this experiment, the computers are MYSVRA, MYSVRB, and MYSVRC.

We also need three different user accounts for the experiment. In this experiment, the accounts are usera, userb, and userc.

Machine MYSVRC contains a local NTFS file, LocalFileC.txt, which is configured so that only usera can access it.

Machine MYSVRC also contains another file that is shared on the network, \\MYSVRC\Data\NetFile.txt. The file is also configured so that only usera can access it.

Machine MYSVRB contains a local NTFS file, LocalFileB.txt, which is configured so that only usera can access it.

We will develop three programs here: ClientA, ServerB, and ServerC.

ServerC is a COM+ configured component that runs on MYSVRC under the identity of userc. It implements a method DoIt that a) impersonates the caller, b) attempts to open LocalFileC.txt for reading, and c) returns the result of its attempt as an output parameter. The code snippet is as follows:

 STDMETHODIMP CMyServerC::DoIt(BSTR *pbsRetVal)  {   // Get the security interface pointer    CComPtr<IServerSecurity> spSec;    HRESULT hr =      ::CoGetCallContext(__uuidof(IServerSecurity),        (void**) &spSec);    if (FAILED(hr)) {     return Error(_T("Error getting call context"),          __uuidof(IMyServerC), hr);    }    // Impersonate the client    hr = spSec->ImpersonateClient();    if (FAILED(hr)) {     return Error(_T("Error impersonating client"),          __uuidof(IMyServerC), hr);    }    // Check if you can open a local file    CComBSTR bsOutput = "ServerC - Local file open: ";    bsOutput += OpenFileForReadOnly(GetLocalFileName());    *pbsRetVal = bsOutput.Detach();    return S_OK;  } 

ServerB is a COM+ configured component that runs on MYSVRB under the identity of userb. It implements a method, DoIt, that impersonates the caller and performs the following three operations:

  1. Attempts to open the local file, LocalFileB.txt.

  2. Attempts to open the network shared file, NetFile.txt.

  3. Attempts to instantiate the ServerC component from machine MYSVRC and invoke method DoIt on the object.

ClientA is a client application that runs on MYSVRA under the identity of usera. After setting up various security parameters by calling CoInitializeSecurity, the client instantiates the ServerB component from machine MYSVRA and calls method DoIt. Essentially, we are setting up a chain of calls ClientA from MYSVRA calls ServerB from MYSVRB which in turn calls ServerC from MYSVRC.

ClientA takes a command line parameter that dictates the impersonation level between ClientA and ServerB. The parameter maps to dwImpLevel in the call to CoInitializeSecurity, as shown in the following code snippet:

 HRESULT hr = ::CoInitializeSecurity(   NULL,    -1,    NULL,    NULL,    RPC_C_AUTHN_LEVEL_DEFAULT,    dwImpLevel,    NULL,    EOAC_NONE,    NULL); 

The impersonation level between ServerB and ServerC is set to RPC_C_AUTHN_LEVEL_IMPERSONATION. Being a configured component, ServerB obviously cannot call CoInitializeSecurity to do this. Fortunately, there is yet another way to specify the security blanket on a proxy by calling the SetBlanket method on interface IClientSecurity. We will cover this interface in the next section.

Let s try various impersonation level settings between ClientA and ServerB and see the results.


As discussed earlier in the chapter, RPC_C_IMP_LEVEL_ANONYMOUS just gets promoted to RPC_C_IMP_LEVEL_IDENTIFY.


If impersonation level RPC_C_IMP_LEVEL_IDENTIFY is used, ServerB gets an error, ERROR_BAD_IMPERSONATION_LEVEL, when it tries to open either the local file or the network file. This is because, though the server was able to impersonate the client, the impersonation token does not contain any information about the client. This also results in access denial when ServerB tries to instantiate ServerC.

RPC_C_IMP_LEVEL_IDENTIFY allows the server to examine the access token of the caller and peek inside the token to get information such as the user identification, the groups the user belongs to, and so on. But it does not allow the object to open any secured objects, hence resulting in ERROR_BAD_IMPERSONATION_LEVEL.


If impersonation level RPC_C_IMP_LEVEL_IMPERSONATE is used, ServerB is able to open the local file, but gets an access denial message (E_ACCESSDENIED) when opening the network file. ServerB is able to instantiate ServerC. However, ServerC also gets E_ACCESSDENIED when opening the local file.

Let s see what this means.

RPC_C_IMP_LEVEL_IMPERSONATE allows a server to peek into the access token as well as to access the securable objects using the caller s credentials. However, the privilege extends only to one machine hop. As a result, ServerB could open the local resource. However, ServerB could not open the network resource, as the client credentials could not be forwarded to the third machine.

A similar problem exists when ServerB calls DoIt on ServerC. ServerC sees userb as the client and not usera. As only usera is allowed to access the local file on MYSVRC, ServerC object s attempt to access the file results in an access denial.


If impersonation level RPC_C_IMP_LEVEL_DELEGATE is used, ServerB is able to open the local file as well as the network file. However, ServerC still gets an access denial trying to open the local file.

While the RPC_C_IMP_LEVEL_IMPERSONATE level impersonation restricts the client s credentials to be valid for just one machine hop, the RPC_C_IMP_LEVEL_DELEGATE level places no such restriction. The client s credentials can be passed across any number of machine hops.

This explains why ServerB could open the network file. However, it still doesn t explain why ServerC couldn t open the local file, even though ServerB was impersonating usera when it invoked the method on ServerC and ClientA had set delegate level impersonation.

When ServerB calls ServerC while impersonating ClientA (usera), the access token of ServerB (userb) is used even if ClientA has set delegate level impersonation. This means that ServerC sees the identity of the caller as userb not usera.

This may seem contrary to what the delegation level impersonation was really supposed to do. However, this was deliberately set up this way for reasons of backward compatibility with the behavior of COM prior to Windows 2000, where delegation level impersonation was not available. So as not to break existing COM applications under Windows 2000, the existing semantics of impersonation were left unchanged.

So how could we achieve true delegation? This is done through cloaking, an idea introduced in COM+.

Before we look at cloaking, carefully make a note of the following statement:

In order for delegation to work, the following requirements have to be met:

  1. As delegation level impersonation is supported only by Kerberos, the client, the server, and all downstream servers must be running Windows 2000 in a Windows 2000 domain.

  2. The client account that will be delegated must not be marked Account is sensitive and cannot be delegated. This property on the user can be manipulated from the Active Directory Users and Computers administrative tool.

  3. The identity under which the server is running must be marked Account is trusted for delegation.

  4. The machine that runs the server must be marked Trust computer for delegation. Once again, the same administrative tool mentioned earlier can be used for this purpose.

Note that the security APIs do not return a good description when delegation doesn t work. In most cases, the returned error is RPC_S_SEC_PKG_ERROR, indicating that a security-package-specific error has occurred.


If you are having problems getting delegation to work, and you think you have met all the requirements for delegation, just try rebooting all the involved machines. If this works, don t ask me why.


Cloaking does what the delegation level impersonation was supposed to do make ServerC see the identity of caller as ClientA and not ServerB.

In order to use cloaking, the impersonation level between ClientA and ServerB should be at least RPC_C_IMP_LEVEL_IMPERSONATE, and the impersonation level between ServerB and ServerC should be RPC_C_IMP_LEVEL_DELEGATE. Note that ClientA need not use delegate level impersonation. However, without delegation level impersonation, the access to the network resource will be denied from ServerB.

Besides specifying delegation level impersonation, ServerB also needs to indicate that it intends to cloak the credentials of the impersonating thread. Specifying the cloaking flag in the authentication capabilities parameter of the security blanket does this.

Windows 2000 supports two types of cloaking static (EOAC_STATIC_CLOAKING) and dynamic (EOAC_DYNAMIC_CLOAKING). With static cloaking, the client s credentials are determined during the first call on a proxy (or whenever IClientSecurity::SetBlanket is called). With dynamic cloaking, the client s credentials are determined each time a call is made on the proxy.

The following code fragment shows how ServerB can set the proxy on the ServerC object for dynamic cloaking:

 STDMETHODIMP CMyServerB::DoIt( )  {        CComPtr<IClientSecurity> spClientSec;    hr = spServerC->QueryInterface(&spClientSec);    hr = spClientSec->SetBlanket(spServerC,      RPC_C_AUTHN_DEFAULT,      RPC_C_AUTHZ_DEFAULT,      NULL,      RPC_C_AUTHN_LEVEL_DEFAULT,      RPC_C_IMP_LEVEL_DELEGATE,      NULL,      EOAC_DYNAMIC_CLOAKING);         hr = spServerC->DoIt(&bsOutput);  } 

With this code in place, when ClientA calls ServerB, ServerB can access the local as well as the network resource. When ServerB calls ServerC, ServerC sees the caller s identity as ClientA and can access the local file. The results of the experiment are summarized in Table 7.8.

Table 7.8. Results of Various Impersonation Levels


Local resource from ServerB

Network resource from ServerB

Local resource from ServerC


Bad impersonation level

Bad impersonation level

Access denied instantiating ServerC


Bad impersonation level

Bad impersonation level

Access denied instantiating ServerC



Access denied

Access denied




Access denied





Identity and Authentication Services

The seventh parameter to CoInitializeSecurity, pAuthList, is used to specify the identity that should be used with an authentication service. The structure that is pointed to by pAuthInfo, along with some other relevant C structures, is as follows:

 typedef struct tagSOLE_AUTHENTICATION_LIST  {   DWORD cAuthInfo;    SOLE_AUTHENTICATION_INFO *aAuthInfo;  }SOLE_AUTHENTICATION_LIST;  typedef struct tagSOLE_AUTHENTICATION_INFO  {   DWORD dwAuthnSvc;    DWORD dwAuthzSvc;    void *pAuthInfo;  }SOLE_AUTHENTICATION_INFO;  typedef struct _SEC_WINNT_AUTH_IDENTITY_W {   unsigned short *User;    unsigned long UserLength;    unsigned short *Domain;    unsigned long DomainLength;    unsigned short *Password;    unsigned long PasswordLength;    unsigned long Flags;  } SEC_WINNT_AUTH_IDENTITY_W;  typedef struct _SEC_WINNT_AUTH_IDENTITY_EXW {   unsigned long Version;    unsigned long Length;    unsigned short *User;    unsigned long UserLength;    unsigned short *Domain;    unsigned long DomainLength;    unsigned short *Password;    unsigned long PasswordLength;    unsigned long Flags;    unsigned short * PackageList;    unsigned long PackageListLength;  } SEC_WINNT_AUTH_IDENTITY_EXW; 

The SOLE_AUTHENTICATION_LIST structure contains a pointer to an array of SOLE_AUTHENTICATION_INFO structures. Each SOLE_AUTHENTICATION_INFO structure identifies an authentication service, the authorization service to be used with the authentication service, and the identity to be used with the authentication service. For NTLM and Kerberos packages, the identity is defined by the SEC_WINNT_AUTH_IDENTITY_W structure. For Snego, the identity is defined by the SEC_WINNT_AUTH_IDENTITY_EX_W structure.

When COM+ negotiates the default authentication service to be used for a proxy, it uses the information pointed to by pAuthInfo to obtain the identity that should be associated with the authentication service. If pAuthInfo is NULL, COM+ uses the process identity to represent the client.

Parameter pAuthInfo is useful for a client that wants to use a different identity for making calls on the proxy. The following code snippet shows how a client can associate an identity for an authentication service. The code specifies the same identity for NTLM and Kerberos services.

 // Auth Identity structure  SEC_WINNT_AUTH_IDENTITY_W authidentity;  ZeroMemory( &authidentity, sizeof(authidentity) );  authidentity.User = L"pvguest";  authidentity.UserLength = wcslen( authidentity.User );  authidentity.Domain = L"pvhome";  authidentity.DomainLength = wcslen( authidentity.Domain );  authidentity.Password = L"mypassword";  authidentity.PasswordLength = wcslen( authidentity.Password );  authidentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;  SOLE_AUTHENTICATION_INFO    authInfo[2];  ZeroMemory( authInfo, sizeof( authInfo ) );  // Kerberos Settings  authInfo[0].dwAuthnSvc = RPC_C_AUTHN_GSS_KERBEROS ;  authInfo[0].dwAuthzSvc = RPC_C_AUTHZ_NONE;  authInfo[0].pAuthInfo = &authidentity;  // NTLM Settings  authInfo[1].dwAuthnSvc = RPC_C_AUTHN_WINNT;  authInfo[1].dwAuthzSvc = RPC_C_AUTHZ_NONE;  authInfo[1].pAuthInfo = &authidentity;  SOLE_AUTHENTICATION_LIST    authList;  authList.cAuthInfo = 2;  authList.aAuthInfo = authInfo;  HRESULT hr = ::CoInitializeSecurity(   NULL,                            // Security descriptor    -1,                              // Count of entries in asAuthSvc    NULL,                            // asAuthSvc array    NULL,                            // Reserved for future use    RPC_C_AUTHN_LEVEL_DEFAULT,       // Authentication level    RPC_C_IMP_LEVEL_IMPERSONATE,     // Impersonation level    &authList,                       // Authentication Information    EOAC_NONE,                       // Additional capabilities    NULL                             // Reserved    );   

Note that the identity information set for an authentication service via CoInitializeSecurity is applicable only for proxies that the client obtains. It is not valid for activating the object that is done via CoCreateInstanceEx or its friends, CoGetClassObject, CoGetInstanceFromFile, etc. This distinction is very important. If a different identity needs to be used during activation, these functions accept an argument of the type COSERVERINFO. We used one of the member variables of this structure earlier to specify the name of the machine for remote object instantiate. Another variable of this structure, pAuthInfo, points to a structure of type COAUTHINFO that can be used to specify a different identity while activating an object. Using COAUTHINFO is left as an exercise for the readers. Hint: the usage is very similar to SOLE_AUTHENTICATION_INFO.

Also note that pAuthInfo can only be used under Windows 2000. It must be set to NULL on a Windows NT 4.0-based system.

Client Acting as a Server

So far we have tried setting parameters on CoInitializeSecurity that make sense for a client. However, there are cases when a client could act as a server. For such cases, it makes sense to specify server-side values for CoInitializeSecurity.

The most common case when a client is a server occurs when the client creates a sink object and hands it to the server. The intention is that the server will invoke methods on the sink object when it detects an interesting event.

To demonstrate the problem with such callbacks, let s develop a component that takes an interface pointer from the client and calls back on the interface. The following code fragment shows the interfaces involved and the server implementation of the interface:

 // Interface definitions  interface ICallMe : IUnknown  {   HRESULT Hello();  };  interface IMyServerX : IDispatch  {   HRESULT Advise([in] ICallMe* pCallMe);  };  // MyServerX.cpp   Implementation  STDMETHODIMP CMyServerX::Advise(ICallMe *pCallMe)  {   HRESULT hr = pCallMe->Hello();    if (FAILED(hr)) {     return Error(_T("Error invoking the sink"),        __uuidof(IMyServerX), hr);    }    return S_OK;  } 

Try to create a COM+ application to run under a specific user account. For my test case, it is usera. Add the component to this application.

Now let s write the client code that creates a sink object and calls Advise on the server object, passing the sink object as a parameter. The code snippet is as follows:

 class CCallMe : public ICallMe, public CComObjectRoot  { public:    CCallMe() {}    ~CCallMe() {}  BEGIN_COM_MAP(CCallMe)    COM_INTERFACE_ENTRY(ICallMe)  END_COM_MAP()    STDMETHODIMP Hello()    {     ::MessageBox(NULL, _T("Hello"), _T("Sink"), MB_OK);      return S_OK;    }  };  int WinMain( )  {   ::CoInitialize(NULL);    CComPtr<CComObject<CCallMe> > spCallMe;    HRESULT hr = CComObject<CCallMe>::CreateInstance(&spCallMe);    _ASSERT (SUCCEEDED(hr));    spCallMe->InternalAddRef();    SERVERXLib::IMyServerXPtr spSvrX(   __uuidof(SERVERXLib::MyServerX));    spSvrX->Advise(     reinterpret_cast<SERVERXLib::ICallMe*>        (static_cast<ICallMe*>(spCallMe)));  } 

Note that the client doesn t call CoInitializeSecurity explicitly. COM will automatically call it when generating the proxy for the IMyServerX interface.

Run the client application under an account other than the one specified for the server.

When the server tries to call Hello on the client s sink, it gets an E_ACCESSDENIED error. This is because, when COM calls CoInitializeSecurity, it sets up the default access for the client process to be accessed only by the client identity. Since the server is running under a different account, it cannot access the client s process, resulting in a failure.

The first parameter to CoInitializeSecurity, pSecDesc, is used to set up the default access permissions for the client process. If this parame-ter is specified as NULL, then COM sets up the access control so that any identity can access the client, as follows:

 hr = ::CoInitializeSecurity(   NULL,        // open access to everyone    -1,    NULL,    NULL,    RPC_C_AUTHN_LEVEL_DEFAULT,    RPC_C_IMP_LEVEL_IDENTIFY,    NULL,    EOAC_NONE,    NULL); 

Though this solves the problem of the server calling back into the client, it opens up the client process to be accessed by anybody. This is not desirable.

Instead of specifying NULL for pSecDesc, we need to specify those users who can access the client. In our case this is just one user the identity under which the server is running.

Parameter pSecDesc is a polymorphic pointer whose interpretation is based on the flag specified in dwCapabilities, the eighth parameter to CoInitializeSecurity.

If dwCapabilities specifies EOAC_NONE, then pSecDesc points to a Windows security descriptor. COM uses this security descriptor to set up the access permission on the client process.

If dwCapabilities specifies EOAC_APPID, pSecDesc points to a GUID of an AppID in the registry. In this case, COM obtains all the security settings from the registry.

Both of the above approaches tie COM to the underlying platform.

To specify the access control in a platform-independent way, COM defines a standard interface called IAccessControl. Using this interface, you can allow or deny access to specific users.

To use IAccessControl with CoInitializeSecurity, dwCapabilities should be specified as EOAC_ACCESS_CONTROL. In this case, COM expects pSecDesc to point to the IAccessControl interface pointer.

You don t have to implement this interface (although you can). COM provides a default implementation under a component named CLSID_DCOMAccessControl.

The following code fragment shows how to allow access to a user using the IAccessControl interface:

 void InitializeSecurityWithAccessControl()  {   CComPtr<IAccessControl> spAC;    HRESULT hr = ::CoCreateInstance(CLSID_DCOMAccessControl, NULL,      CLSCTX_INPROC_SERVER, IID_IAccessControl, (void**) &spAC);      _ASSERT (SUCCEEDED(hr));    ACTRL_ACCESSW access;    ACTRL_PROPERTY_ENTRYW propEntry;    access.cEntries = 1;    access.pPropertyAccessList = &propEntry;    ACTRL_ACCESS_ENTRY_LISTW entryList;    propEntry.lpProperty = NULL;    propEntry.pAccessEntryList = &entryList;    propEntry.fListFlags = 0;    ACTRL_ACCESS_ENTRYW entry;    entryList.cEntries = 1;    entryList.pAccessList = &entry;    // Set up the ACE    entry.Access = COM_RIGHTS_EXECUTE;    entry.ProvSpecificAccess = 0;    entry.Inheritance = NO_INHERITANCE;    entry.lpInheritProperty = NULL;    // allow access to "usera"    entry.fAccessFlags = ACTRL_ACCESS_ALLOWED;    entry.Trustee.TrusteeForm = TRUSTEE_IS_NAME;    entry.Trustee.TrusteeType = TRUSTEE_IS_USER;    entry.Trustee.ptstrName = L"PVHOME\\userb";    entry.Trustee.pMultipleTrustee = NULL;    entry.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;    hr = spAC->GrantAccessRights(&access);    _ASSERT (SUCCEEDED(hr));    hr = ::CoInitializeSecurity(     static_cast<IAccessControl*>(spAC),      -1,      NULL,      NULL,      RPC_C_AUTHN_LEVEL_DEFAULT,      RPC_C_IMP_LEVEL_IDENTIFY,      NULL,      EOAC_ACCESS_CONTROL,      NULL);      _ASSERT (SUCCEEDED(hr));  } 

You may be wondering how you can CoInitializeSecurity after an object has been instantiated. CoInitializeSecurity can be called just once and COM would already have called it while instantiating the object.

Recall that CoInitializeSecurity is invoked only when the client first gets a proxy. The interface pointer returned from instantiating the access control object is a direct pointer and not a proxy. Hence, COM had no need to call CoInitializeSecurity.

Adjusting Security for a Proxy

CoInitializeSecurity sets up process-wide default values for the security settings. Any proxy that gets created within the client process inherits these values for setting up the security blanket.

Sometimes the client may need fine-grained security control on calls to particular interfaces. For example, the authentication might be set at a low level for the process, but calls to a particular interface may require a high level authentication, such as when a user is passing credit card information over the wire.

Similar to interface IServerSecurity that is available for servers, COM defines another interface, IClientSecurity, for clients. This interface is available on the proxy manager [5] for each remoted object that the client holds. A client can obtain the interface pointer by calling QueryInterface for IID_IClientSecurity on any interface of the remoted object.

[5] Recall from Chapter 5 that a proxy manager acts as the client-side entity for the remote object.


If QueryInterface for IClientSecurity fails, either the client has a direct pointer to the object or the object is remoted by some custom marshaler that does not support security.

Following is the IDL definition of interface IClientSecurity:

 interface IClientSecurity : IUnknown  {   // Obtain the current security blanket information    HRESULT QueryBlanket (     [in]  IUnknown   *pProxy,      [out] DWORD      *pAuthnSvc,      [out] DWORD      *pAuthzSvc,      [out] OLECHAR    **pServerPrincName,      [out] DWORD      *pAuthnLevel,      [out] DWORD      *pImpLevel,      [out] void       **pAuthInfo,      [out] DWORD      *pCapabilites );  // Modify the security blanket  HRESULT SetBlanket (     [in] IUnknown    *pProxy,      [in] DWORD       dwAuthnSvc,      [in] DWORD       dwAuthzSvc,      [in] OLECHAR     *pServerPrincName,      [in] DWORD       dwAuthnLevel,      [in] DWORD       dwImpLevel,      [in] void        *pAuthInfo,      [in] DWORD       dwCapabilities );  // Make a copy of the specified proxy  HRESULT CopyProxy (     [in]  IUnknown   *pProxy,      [out] IUnknown   **ppCopy );  } 

Method QueryBlanket (and its equivalent helper API, CoQueryProxyBlanket) is similar in functionality to IServerSecurity::QueryBlanket.

Method SetBlanket (and its equivalent helper API, CoSetProxyBlanket) can be used to modify the current security settings for a specific interface proxy. We used this method earlier when we were discussing delegation. The following code snippet bumps up the authentication level to packet encryption. This is routinely done for secure credit card transactions.

 hr = spClientSec->SetBlanket(pCreditCardServer,      RPC_C_AUTHN_DEFAULT,      RPC_C_AUTHZ_DEFAULT,      NULL,      RPC_C_AUTHN_LEVEL_PKT_PRIVACY,      RPC_C_IMP_LEVEL_DEFAULT,      NULL,      EOAC_NONE); 

The first parameter to the method is the proxy for the specific interface for which the security settings need to be changed.

Note that the new settings apply to any future caller of this particular interface within the same context. However, you can call IClientSecurity::CopyProxy (or its helper function, CoCopyProxy) to make a private copy of the interface proxy). SetBlanket can then be called on the copy, thereby ensuring that other callers are not affected.


COM+ Programming. A Practical Guide Using Visual C++ and ATL
COM+ Programming. A Practical Guide Using Visual C++ and ATL
ISBN: 130886742
Year: 2000
Pages: 129 © 2008-2017.
If you may any questions please contact us: