本主题介绍客户端应用程序用于为服务编写 SPN 的代码。 客户端绑定到服务的服务连接点(SCP),以检索连接到服务所需的数据。 SCP 还包含客户端可用于撰写服务的 SPN 的数据。 有关详细信息和绑定到 SCP 并检索必要属性的代码示例,请参阅 客户端如何查找和使用服务连接点。
本主题还介绍了客户端如何使用 SSPI 安全包和服务 SPN 建立与 Windows 套接字服务的相互身份验证的连接。 请注意,此代码与 Microsoft Windows NT 4.0 及更早版本所需的代码几乎完全相同,只是为了向服务器验证客户端。 唯一的区别是客户端必须提供 SPN 并指定ISC_REQ_MUTUAL_AUTH标志。
用于为服务生成 SPN 的客户端代码
// Initialize these strings by querying the service's SCP.
TCHAR szDn[MAX_PATH], // DN of the service's SCP.
szServer[MAX_PATH], // DNS name of the service's server.
szClass[MAX_PATH]; // Service class.
TCHAR szSpn[MAX_PATH]; // Buffer for SPN.
SOCKET sockServer; // Socket connected to service.
DWORD dwRes, dwLen;
.
.
.
// Compose the SPN for the service using the DN, Class, and Server
// returned by ScpLocate.
dwLen = sizeof(szSpn);
dwRes = DsMakeSpn(
szClass, // Service class.
szDn, // DN of the service's SCP.
szServer, // DNS name of the server on which service is running.
0, // No port component in SPN.
NULL, // No referrer.
&dwLen, // Size of szSpn buffer.
szSpn); // Buffer to receive the SPN.
if (!DoAuthentication (sockServer, szSpn)) {
closesocket (sockServer);
return(FALSE);
}
.
.
.
用于对服务进行身份验证的客户端代码
此代码示例由两个例程组成: DoAuthentication 和 GenClientContext。 调用 DsMakeSpn 为服务编写 SPN 后,客户端会将 SPN 传递给 DoAuthentication 例程,该例程调用 GenClientContext 以生成要发送到服务的初始缓冲区。 DoAuthentication 使用套接字句柄发送缓冲区,并通过另一次调用 GenClientContext 接收传递给 SSPI 包的服务响应。 在身份验证失败或 GenClientContext 设置指示身份验证成功的标志之前,将重复此循环。
GenClientContext 例程与 SSPI 包交互,以生成要发送到服务的身份验证数据并处理从服务接收的数据。 客户端提供的身份验证数据的关键组件包括:
- 标识服务所需身份验证凭据的服务主体名称。
- 客户端凭据。 安全包的 AcquireCredentialsHandle 函数从客户端在登录时建立的安全上下文中提取这些凭据。
- 若要请求相互身份验证,客户端必须在 GenClientContext 例程期间调用 InitializeSecurityContext 函数时指定ISC_REQ_MUTUAL_AUTH标志。
// Structure for storing the state of the authentication sequence.
typedef struct _AUTH_SEQ
{
BOOL _fNewConversation;
CredHandle _hcred;
BOOL _fHaveCredHandle;
BOOL _fHaveCtxtHandle;
struct _SecHandle _hctxt;
} AUTH_SEQ, *PAUTH_SEQ;
/***************************************************************/
// DoAuthentication routine for the client.
//
// Manages the client's authentication conversation with the service
// using the supplied socket handle.
//
// Returns TRUE if the mutual authentication is successful.
// Otherwise, it returns FALSE.
//
/***************************************************************/
BOOL DoAuthentication (
SOCKET s,
LPTSTR szSpn)
{
BOOL done = FALSE;
DWORD cbOut, cbIn;
// Call the security package to generate the initial buffer of
// authentication data to send to the service.
cbOut = g_cbMaxMessage;
if (!GenClientContext (s, NULL, 0, g_pOutBuf,
&cbOut, &done, szSpn))
return(FALSE);
if (!SendMsg (s, g_pOutBuf, cbOut))
return(FALSE);
// Pass the service's response back to the security package, and send
// the package's output back to the service. Repeat until complete.
while (!done)
{
if (!ReceiveMsg (s, g_pInBuf, g_cbMaxMessage, &cbIn))
return(FALSE);
cbOut = g_cbMaxMessage;
if (!GenClientContext (s, g_pInBuf, cbIn, g_pOutBuf,
&cbOut, &done, szSpn))
return(FALSE);
if (!SendMsg (s, g_pOutBuf, cbOut))
return(FALSE);
}
return(TRUE);
}
/***************************************************************/
// GenClientContext routine
//
// Handles the client's interactions with the security package.
// Optionally takes an input buffer coming from the service
// and generates a buffer of data to send back to the
// service. Also returns an indication when the authentication
// is complete.
//
// Returns TRUE if the mutual authentication is successful.
// Otherwise, it returns FALSE.
//
/***************************************************************/
BOOL GenClientContext (
DWORD dwKey, // Socket handle used as key.
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone,
LPTSTR szSpn)
{
SECURITY_STATUS ssStatus;
TimeStamp Lifetime;
SecBufferDesc OutBuffDesc;
SecBuffer OutSecBuff;
SecBufferDesc InBuffDesc;
SecBuffer InSecBuff;
ULONG ContextAttributes;
PAUTH_SEQ pAS; // Structure to store state of authentication.
// Get structure that contains the state of the authentication sequence.
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);
if (pAS->_fNewConversation)
{
ssStatus = g_pFuncs->AcquireCredentialsHandle (
NULL, // Principal
PACKAGE_NAME,
SECPKG_CRED_OUTBOUND,
NULL, // LOGON id
NULL, // Authentication data
NULL, // Get key function.
NULL, // Get key argument.
&pAS->_hcred,
&Lifetime
);
if (SEC_SUCCESS (ssStatus))
pAS->_fHaveCredHandle = TRUE;
else
{
fprintf (stderr,
"AcquireCredentialsHandle failed: %u\n", ssStatus);
return(FALSE);
}
}
// Prepare output buffer.
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;
OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;
// Prepare input buffer.
if (!pAS->_fNewConversation)
{
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;
InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;
}
_tprintf(TEXT("InitializeSecurityContext: pszTarget=%s\n"),szSpn);
ssStatus = g_pFuncs->InitializeSecurityContext (
&pAS->_hcred,
pAS->_fNewConversation ? NULL : &pAS->_hctxt,
szSpn,
ISC_REQ_MUTUAL_AUTH, // Context requirements
0, // Reserved1
SECURITY_NATIVE_DREP,
pAS->_fNewConversation ? NULL : &InBuffDesc,
0, // Reserved2
&pAS->_hctxt,
&OutBuffDesc,
&ContextAttributes,
&Lifetime
);
if (!SEC_SUCCESS (ssStatus))
{
fprintf (stderr, "init context failed: %X\n", ssStatus);
return FALSE;
}
pAS->_fHaveCtxtHandle = TRUE;
// Complete token if applicable.
if ( (SEC_I_COMPLETE_NEEDED == ssStatus) ||
(SEC_I_COMPLETE_AND_CONTINUE == ssStatus))
{
if (g_pFuncs->CompleteAuthToken)
{
ssStatus = g_pFuncs->CompleteAuthToken (&pAS->_hctxt,
&OutBuffDesc);
if (!SEC_SUCCESS(ssStatus))
{
fprintf (stderr, "complete failed: %u\n", ssStatus);
return FALSE;
}
} else
{
fprintf (stderr, "Complete not supported.\n");
return FALSE;
}
}
*pcbOut = OutSecBuff.cbBuffer;
if (pAS->_fNewConversation)
pAS->_fNewConversation = FALSE;
*pfDone = !((SEC_I_CONTINUE_NEEDED == ssStatus) ||
(SEC_I_COMPLETE_AND_CONTINUE == ssStatus));
// Check the ISC_RET_MUTUAL_AUTH flag to verify that
// mutual authentication was performed.
if (*pfDone && !(ContextAttributes && ISC_RET_MUTUAL_AUTH) )
_tprintf(TEXT("Mutual Auth not set in returned context.\n"));
return TRUE;
}