用戶端如何驗證 SCP 型 Windows 套接字服務
本主題說明用戶端應用程式用來撰寫服務的SPN的程式代碼。 用戶端會系結至服務的服務連接點 (SCP),以擷取連線至服務所需的數據。 SCP 也包含用戶端可用來撰寫服務的 SPN 的數據。 如需系結至 SCP 並擷取必要屬性的詳細資訊和程式代碼範例,請參閱 用戶端如何尋找和使用服務連接點。
本主題也會說明用戶端如何使用 SSPI 安全性套件和服務 SPN 來建立與 Windows Sockets 服務的相互驗證連線。 請注意,此程式代碼與 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;
}