编写经过身份验证的 SSPI 客户端

所有 RPC 客户端/服务器会话都需要客户端和服务器之间的绑定。 若要为客户端/服务器应用程序添加安全性,程序必须使用经过身份验证的绑定。 本部分介绍在客户端和服务器之间创建经过身份验证的绑定的过程。

有关相关信息,请参阅平台软件开发工具包 (SDK) 中 大多数安全包和协议使用的过程

创建客户端绑定句柄

若要使用服务器程序创建经过身份验证的会话,客户端应用程序必须通过其绑定句柄提供身份验证信息。 若要设置经过身份验证的绑定句柄,客户端会调用 RpcBindingSetAuthInfoRpcBindingSetAuthInfoEx 函数。 这两个函数几乎完全相同。 它们之间的唯一区别在于,客户端可以使用 RpcBindingSetAuthInfoEx 函数指定服务质量。

以下代码片段显示了对 RpcBindingSetAuthInfo 的调用可能的外观。

// This code fragment assumes that rpcBinding is a valid binding 
// handle between the client and the server. It also assumes that
// pAuthCredentials is a valid pointer to a data structure which
// contains the user's authentication credentials.

dwStatus = DsMakeSpn(
    "ldap",
    "ServerName.domain.com",
    NULL,
    0,
    NULL,
    &pcSpnLength,
    pszSpn);

//...

rpcStatus = RpcBindingSetAuthInfo(
    rpcBinding,                       // Valid binding handle
    pszSpn,                           // Principal name 
    RPC_C_AUTHN_LEVEL_PKT_INTEGRITY,  // Authentication level
    RPC_C_AUTHN_GSS_NEGOTIATE,        // Use Negotiate SSP
    NULL,                             // Authentication credentials <entity type="ndash"/> use current thread credentials
    RPC_C_AUTHZ_NAME);                // Authorization service

客户端成功调用 RpcBindingSetAuthInfoRpcBindingSetAuthInfoEx 函数后,RPC 运行时库会自动对绑定上的所有 RPC 调用进行身份验证。 客户端选择的安全级别和身份验证仅适用于该绑定句柄。 派生自绑定句柄的上下文句柄将使用相同的安全信息,但对绑定句柄的后续修改不会反映在上下文句柄中。 有关详细信息,请参阅 上下文句柄

身份验证级别一直有效,直到客户端选择另一个级别,或直到进程终止。 大多数应用程序不需要更改安全级别。 客户端可以通过调用 RpcBindingInqAuthClient 并向其传递绑定句柄来查询任何绑定句柄以获取其授权信息。

向服务器提供客户端凭据

服务器使用客户端的绑定信息来强制实施安全性。 客户端始终将绑定句柄作为远程过程调用的第一个参数传递。 但是,服务器无法使用 句柄,除非在 IDL 文件或服务器的应用程序配置文件中声明为远程过程的第一个参数, (ACF) 。 可以选择列出 IDL 文件中的绑定句柄,但这会强制所有客户端声明和操作绑定句柄,而不是使用自动或隐式绑定。 有关详细信息,请参阅 IDL 和 ACF 文件

另一种方法是将绑定句柄保留在 IDL 文件中,并将 explicit_handle 属性放入服务器的 ACF 中。 这样,客户端可以使用最适合应用程序的绑定类型,而服务器则使用绑定句柄,就像显式声明绑定句柄一样。

从绑定句柄提取客户端凭据的过程如下所示:

  • RPC 客户端调用 RpcBindingSetAuthInfo ,并将其身份验证信息作为传递给服务器的绑定信息的一部分包含在内。
  • 通常,服务器调用 RpcImpersonateClient ,以便其行为就像是客户端一样。 如果未对绑定句柄进行身份验证,则调用将失败并RPC_S_NO_CONTEXT_AVAILABLE。 若要获取客户端的用户名,请在模拟时调用 RpcBindingInqAuthClient ,或者在 Windows XP 或更高版本的 Windows 上调用 RpcGetAuthorizationContextForClient 以获取授权上下文,然后使用 Authz 函数检索该名称。
  • 服务器通常会调用 CreatePrivateObjectSecurity 以使用 ACL 创建对象。 完成此操作后,安全检查将变为自动。