Hur en klient autentiserar en SCP-baserad Windows Sockets-tjänst
Det här avsnittet visar koden som ett klientprogram använder för att skapa ett SPN för en tjänst. Klienten binder till tjänstens tjänstanslutningspunkt (SCP) för att hämta de data som krävs för att ansluta till tjänsten. SCP innehåller också data som klienten kan använda för att skapa tjänstens SPN. Mer information och ett kodexempel som binder till SCP och hämtar nödvändiga egenskaper finns i "Hur klienter hittar och använder en anslutningspunkt för tjänster" .
Det här avsnittet visar också hur en klient använder ett SSPI-säkerhetspaket och tjänstens SPN för att upprätta en ömsesidigt autentiserad anslutning till Windows Sockets-tjänsten. Tänk på att den här koden nästan är identisk med den kod som krävs i Microsoft Windows NT 4.0 och tidigare bara för att autentisera klienten till servern. Den enda skillnaden är att klienten måste ange SPN och ange flaggan ISC_REQ_MUTUAL_AUTH.
Klientkod för att skapa ett SPN för en tjänst
// 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);
}
.
.
.
Klientkod för att autentisera tjänsten
Det här kodexemplet består av två rutiner: DoAuthentication och GenClientContext. När du har anropat DsMakeSpn för att skapa ett SPN för tjänsten skickar klienten SPN till DoAuthentication rutin, som anropar GenClientContext för att generera den första bufferten som ska skickas till tjänsten. DoAuthentication använder sockethandtaget för att skicka bufferten och ta emot tjänstens svar som överförs till SSPI-paketet genom ett ytterligare anrop till GenClientContext. Den här loopen upprepas tills autentiseringen misslyckas eller GenClientContext anger en flagga som anger att autentiseringen lyckades.
GenClientContext--rutinen interagerar med SSPI-paketet för att generera autentiseringsdata som ska skickas till tjänsten och bearbeta de data som tas emot från tjänsten. De viktigaste komponenterna i autentiseringsdata som tillhandahålls av klienten är:
- Tjänstens huvudnamn som identifierar de autentiseringsuppgifter som tjänsten måste autentisera.
- Klientens autentiseringsuppgifter. Funktionen AcquireCredentialsHandle i säkerhetspaketet extraherar dessa autentiseringsuppgifter från klientens säkerhetskontext som upprättas vid inloggning.
- Om du vill begära ömsesidig autentisering måste klienten ange flaggan ISC_REQ_MUTUAL_AUTH när den anropar funktionen InitializeSecurityContext under GenClientContext- rutin.
// 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;
}