Platform SDK: Active Directory, ADSI, and Directory Services

How an RpcNs Service Authenticates a Caller

For an RPC service, the RPC run time routine handles most of the details of mutual authentication. When the service starts up, it simply calls the RpcServerRegisterAuthInfo function to register its SPN and specify an authentication service. This example specifies the RPC_C_AUTHN_GSS_NEGOTIATE authentication service which performs the mutual authentication. The service then sets up the RPC bindings and waits for a client connection. When a client binds to the service, the RPC run time automatically performs the mutual authentication. If the client requests authentication and the client's credentials are invalid, the RPC run time does not dispatch the client's calls. Note, however, that if the client does not request the proper authentication level, the RPC run time dispatches the call without performing authentication. For this reason, your service must call the RpcBindingInqAuthClient function to verify the authentication parameters specified by the client.

The following code fragment composes the service's SPN and registers the service for authentication. For the SpnCompose source code, see Composing SPNs for an RpcNs Service.

RPC_STATUS    status;
DWORD         dwStatus;
TCHAR         **pspn;
ULONG         ulSpn=1;
 
dwStatus = SpnCompose(&pspn, &ulSpn);
 
// Register with the authentication service
status = RpcServerRegisterAuthInfo((TCHAR*)*pspn,
                                   RPC_C_AUTHN_GSS_NEGOTIATE,
                                   NULL,
                                   NULL);

If the client is authenticated, the service can call the RpcBindingInqAuthClient function to retrieve the security parameters specified by the client. This enables the service to enforce additional security requirements. For example, a service that uses mutual authentication should also require that communications between client and service be digitally signed and/or encrypted. The following code fragment calls the RpcBindingInqAuthClient function and rejects the client's call unless the client has specified the required authentication level.

boolean Shutdown()
{
RPC_STATUS          rpcStatus;
RPC_AUTHZ_HANDLE    hAuth;
TCHAR               *pszSpn;
ULONG               ulAuthnLevel;
ULONG               ulAuthnSvc;
ULONG               ulAuthzSvc;
 
_tprintf(TEXT("Shutdown called.\n"));
 
rpcStatus = RpcBindingInqAuthClient(
          0,
          &hAuth,
          &pszSpn,
          &ulAuthnLevel,  // Authentication level requested by client
          &ulAuthnSvc,
          &ulAuthzSvc);
 
if (rpcStatus != RPC_S_OK) {
    _tprintf(TEXT("RpcBindingInqAuthClient failed: %d\n"), rpcStatus);
    return false;
}
 
_tprintf(TEXT("Client SPN:%s\n"),pszSpn);
RpcStringFree(&pszSpn);
 
switch (ulAuthnLevel) 
{
    case RPC_C_AUTHN_LEVEL_DEFAULT:
        _tprintf(TEXT("Client Auth Level: DEFAULT\n"));
        return false; // Keep running
    case RPC_C_AUTHN_LEVEL_NONE:
        _tprintf(TEXT("Client Auth Level: NONE\n"));
        return false; // Keep running
    case RPC_C_AUTHN_LEVEL_CONNECT:
        _tprintf(TEXT("Client Auth Level: CONNECT\n"));
        return false; // Keep running
    case RPC_C_AUTHN_LEVEL_CALL:
        _tprintf(TEXT("Client Auth Level: CALL\n"));
        return false; // Keep running
    case RPC_C_AUTHN_LEVEL_PKT:
        _tprintf(TEXT("Client Auth Level: PACKET\n"));
        break;  // Shutdown
    case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY:
        _tprintf(TEXT("Client Auth Level: PACKET_INTEGRITY\n"));
        break;  // Shutdown
    case RPC_C_AUTHN_LEVEL_PKT_PRIVACY:
        _tprintf(TEXT("Client Auth Level: PACKET_PRIVACY\n"));
        break;  // Shutdown
    default:
        _tprintf(TEXT("Client Auth Level: UNKNOWN (%d)\n"),
                               ulAuthnLevel);
        return true;
}
 
// Shut down.
ReportStatusToSCMgr(
    SERVICE_STOP_PENDING,  // Service state
    NO_ERROR,              // Exit code
    3000);                 // Wait hint
 
_tprintf(TEXT("\nService shutting down by request.\n"));
RpcMgmtStopServerListening(NULL);
return true;
}