Platform SDK: Active Directory, ADSI, and Directory Services |
The following sample code shows the RPC client's side of a mutually authenticated connection. The client connects to the RPC name service to enumerate the bindings that match the RPC service's interface specification. For each binding handle in the enumeration, the client must call the DsMakeSpn function to compose the corresponding SPN.
void main(void) { RPC_STATUS rpcstatus; boolean bResult; ULONG ulCode; TCHAR szEntryName[] = TEXT("/.:/RpcExampleServiceEntry"); TCHAR szDsEntryName[MAX_PATH]; BOOL done=FALSE; RPC_NS_HANDLE hNs; // Context for import operations TCHAR *pszBind; int ilen; // Items for ADSI IADs *pRoot=NULL; VARIANT varDSRoot; HRESULT hr; // Items for Mutual Auth, SPN TCHAR szServiceClass[]=TEXT("RpcExample"); TCHAR szServiceInstance[MAX_PATH]; TCHAR szSpn[MAX_PATH]; ULONG ulSpn = sizeof(szSpn); RPC_SECURITY_QOS qos; // To build an SPN for the RPC service, the DN of the // service's entry in the RpcServices container is needed. To build // that DN, retrieve the default naming context and combine it with // the known strings for the RpcServices container and the service's // entry in that container. hr = CoInitialize(NULL); hr = ADsGetObject(TEXT("LDAP://RootDSE"), IID_IADs, (void**)&pRoot); hr = pRoot->Get(TEXT("defaultNamingContext"),&varDSRoot); _tprintf(TEXT("\nDS Root :%s\n"),varDSRoot.bstrVal); if (pRoot) pRoot->Release(); CoUninitialize(); // Compose the DN of the RPC service's connection point using // "RpcExampleServiceEntry", which is the name of this service's // entry in the RpcServices container. _tcscpy(szDsEntryName, TEXT("cn=RpcExampleServiceEntry,cn=RpcServices,cn=System,")); _tcscat(szDsEntryName,varDSRoot.bstrVal); // Use the service's RPC name service entry to get a handle to // enumerate the bindings that match our interface specification. rpcstatus = RpcNsBindingImportBegin(RPC_C_NS_SYNTAX_DCE, (TCHAR *)&szEntryName, RpcExample_v1_0_c_ifspec, NULL, &hNs); if (rpcstatus != RPC_S_OK) return; // Loop through the bindings and try each handle until one works. // In this example, implicit handles are used: the implicit handle // "RpcExample_IfHandle" is generated by MIDL and defined in the // MIDL-generated include file RpcExample.H. while (rpcstatus != RPC_S_NO_MORE_BINDINGS) { rpcstatus = RpcNsBindingImportNext(hNs, &RpcExample_IfHandle); if (rpcstatus != RPC_S_OK) continue; // Convert the binding to a string. RpcBindingToStringBinding(RpcExample_IfHandle,&pszBind); _tprintf(TEXT("String Binding:%s\n"),pszBind); // Extract the service name, the host, in this case. // Note that the DNS host name is used to compose the SPN. ilen=_tcscspn((const TCHAR *)pszBind,TEXT(":")); _tcscpy(szServiceInstance,(const TCHAR *)(pszBind+ilen+1)); RpcStringFree(&pszBind); // Set up the authentication info for the call // // First make the SPN, which is composed of the service class, // the DN of the RPC Service object in the DS, and the service // instance name, which is the DNS name of the host, // which was extracted from the RPC binding string. rpcstatus = DsMakeSpn( szServiceClass, szDsEntryName, // DN of the entry in RpcServices container szServiceInstance, // DNS name of the host for the service 0, // Use the default port NULL, // No referral host &ulSpn, // Size, in bytes, of the szSpn buffer szSpn // buffer to receive the SPN ); _tprintf(TEXT("Client will present SPN %s\n"),szSpn); // Set up the RPC_SECURITY_QOS struct for mutual authentication. qos.Version = RPC_C_SECURITY_QOS_VERSION; qos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; qos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE; // Set the authentication information for this binding handle. // Specify the service principal name and the QOS information. // Ask for PKT_INTEGRITY to ensure that no-one has tampered // with the traffic between the client and the authenticated // service. Failure to ask for at least PKT_INTEGRITY renders the // mutual authentication effectively worthless because an attacker // can steal and re-issue compromised packets. // For greater security, request PKT_PRIVACY which // also encrypts the traffic. PKT_INTEGRITY is a good compromise // between security and performance - an attacker can see the // traffic but not tamper with it. rpcstatus = RpcBindingSetAuthInfoEx( RpcExample_IfHandle, (TCHAR *)szSpn, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, NULL, &qos); // Now that mutual authentication parameters have been set, // make RPC calls. RpcTryExcept { bResult = Shutdown(); if (bResult) _tprintf(TEXT("Shutdown: Service accepted call\n")); else _tprintf(TEXT("Shutdown: Service rejected call\n")); // Stop looping through the bindings. rpcstatus = RPC_S_NO_MORE_BINDINGS; } RpcExcept(1) { ulCode = RpcExceptionCode(); _tprintf(TEXT("RPC exception 0x%lx = %ld\n"), ulCode, ulCode); } RpcEndExcept; // Free the imported binding RpcBindingFree(&RpcExample_IfHandle); } // Discard the Import handle. rpcstatus = RpcNsBindingImportDone(&hNs); return; }