Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой теме показан код, который клиентское приложение использует для составления 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 использует дескриптор сокета для отправки буфера и получения ответа службы, переданного пакету SSPI другим вызовом GenClientContext. Этот цикл повторяется до сбоя проверки подлинности или GenClientContext задает флаг, указывающий на успешное выполнение проверки подлинности.
Подпрограмма GenClientContext взаимодействует с пакетом SSPI для создания данных проверки подлинности для отправки в службу и обработки данных, полученных от службы. Основные компоненты данных проверки подлинности, предоставляемые клиентом, включают:
- Имя субъекта службы, указывающее на учетные данные, требующие проверки подлинности службой.
- Учетные данные клиента. Функция AcquireCredentialsHandle пакета безопасности извлекает эти учетные данные из контекста безопасности клиента, установленного при входе.
- Чтобы запросить взаимную проверку подлинности, клиент должен указать флаг ISC_REQ_MUTUAL_AUTH при вызове функции InitializeSecurityContext во время процедуры GenClientContext.
// 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;
}