Windows ソケット サーバーでの SSPI の使用

このサンプル プログラムは、 Windows ソケット クライアントで SSPI を使用するクライアント プログラムで動作します。 どちらのサンプル プログラムも SspiExample.h ヘッダー ファイルを使用します。これは、「 SSPI クライアントおよびサーバー サンプルのヘッダー ファイル」にあります。

このプログラムには、Secur32.lib と Ws2_32.lib の関数の呼び出しが含まれています。これはリンク ライブラリに含める必要があります。

このプログラムでは、次の例を示します。

  • クライアントとの Windows ソケット接続の確立。
  • セッションの初期化。
  • クライアントとの接続とセキュリティで保護された通信セッションの確立。 ここでのサーバーは、NTLM または Kerberos を使用してクライアントに応答できる Negotiate SSP を提供します。
  • クライアントの偽装と自己への戻りを使用します。
  • 暗号化されたメッセージをクライアントに送信します。

このサンプル プログラムでは、制限付きエラー処理を使用します。

//--------------------------------------------------------------------
//  This is a server-side SSPI Windows Sockets program.

#define usPort 2000
#define SECURITY_WIN32
#define SEC_SUCCESS(Status) ((Status) >= 0)

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include "Sspiexample.h"

CredHandle hcred;
struct _SecHandle  hctxt;

static PBYTE g_pInBuf = NULL;
static PBYTE g_pOutBuf = NULL;
static DWORD g_cbMaxMessage;
static TCHAR g_lpPackageName[1024];

BOOL AcceptAuthSocket (SOCKET *ServerSocket);

void main ()
{
CHAR pMessage[200];
DWORD cbMessage;
PBYTE pDataToClient = NULL;
DWORD cbDataToClient = 0;
PCHAR pUserName = NULL;
DWORD cbUserName = 0;
SOCKET Server_Socket;
WSADATA wsaData;
SECURITY_STATUS ss;
PSecPkgInfo pkgInfo;
SecPkgContext_Sizes SecPkgContextSizes;
SecPkgContext_NegotiationInfo SecPkgNegInfo;
ULONG cbMaxSignature;
ULONG cbSecurityTrailer;

//-----------------------------------------------------------------   
//  Set the default package to negotiate.

strcpy_s(g_lpPackageName, 1024 * sizeof(TCHAR), "Negotiate");

//-----------------------------------------------------------------   
//  Initialize the socket interface and the security package.

if( WSAStartup (0x0101, &wsaData))
{
    fprintf (stderr, "Could not initialize winsock: \n");
    cleanup();
}

ss = QuerySecurityPackageInfo (
   g_lpPackageName, 
   &pkgInfo);

if (!SEC_SUCCESS(ss)) 
{
     fprintf (stderr,
               "Could not query package info for %s, error 0x%08x\n",
               g_lpPackageName, ss);
     cleanup();  
}

g_cbMaxMessage = pkgInfo->cbMaxToken;

FreeContextBuffer(pkgInfo);

g_pInBuf = (PBYTE) malloc (g_cbMaxMessage);
g_pOutBuf = (PBYTE) malloc (g_cbMaxMessage);
   
if (NULL == g_pInBuf || NULL == g_pOutBuf)
{
     fprintf (stderr, "Memory allocation error.\n");
      cleanup();
}

//-----------------------------------------------------------------   
//  Start looping for clients.

while(TRUE)
{
  printf("Waiting for client to connect...\n");

//-----------------------------------------------------------------   
//  Make an authenticated connection with client.


  if (!AcceptAuthSocket (&Server_Socket))
  {
      fprintf (stderr, "Could not authenticate the socket.\n");
     cleanup();
  }
      
ss = QueryContextAttributes(
       &hctxt,
       SECPKG_ATTR_SIZES,
       &SecPkgContextSizes );

if (!SEC_SUCCESS(ss))  
{
    fprintf (stderr, "QueryContextAttributes failed: 0x%08x\n", ss);
    exit(1);
}

//----------------------------------------------------------------
//  The following values are used for encryption and signing.

cbMaxSignature = SecPkgContextSizes.cbMaxSignature;
cbSecurityTrailer = SecPkgContextSizes.cbSecurityTrailer;

ss = QueryContextAttributes(
      &hctxt,
      SECPKG_ATTR_NEGOTIATION_INFO,
      &SecPkgNegInfo );

if (!SEC_SUCCESS(ss))  
{
      fprintf (stderr, "QueryContextAttributes failed: 0x%08x\n", ss);
      exit(1);
}
else
{
      printf("Package Name: %s\n", SecPkgNegInfo.PackageInfo->Name);
}

//----------------------------------------------------------------
//  Free the allocated buffer.

FreeContextBuffer(SecPkgNegInfo.PackageInfo);

//-----------------------------------------------------------------   
//  Impersonate the client.

  ss = ImpersonateSecurityContext (&hctxt);
  if (!SEC_SUCCESS(ss)) 
  {
     fprintf (stderr, "Impersonate failed: 0x%08x\n", ss);
     cleanup();
  }
  else
  {
       printf("Impersonation worked. \n");
  }

  GetUserName (NULL, &cbUserName);
  pUserName = (PCHAR) malloc (cbUserName);

  if (!pUserName)
  {
    fprintf (stderr, "Memory allocation error. \n");
    cleanup();
  }

  if (!GetUserName (
     pUserName, 
     &cbUserName))
  {
    fprintf (stderr, "Could not get the client name. \n");
    cleanup();
  }
  else
  {
    printf ("Client connected as :  %s\n", pUserName);
  }

//-----------------------------------------------------------------   
//  Revert to self.

  ss = RevertSecurityContext (&hctxt);
  if (!SEC_SUCCESS(ss)) 
  {
     fprintf (stderr, "Revert failed: 0x%08x\n", ss);
     cleanup();
  }
  else
  {
      printf("Reverted to self.\n");
  }

//-----------------------------------------------------------------   
//  Send the client an encrypted message.

  strcpy_s(pMessage, sizeof(pMessage),
      "This is your server speaking");
  cbMessage = strlen(pMessage);

  EncryptThis (
       (PBYTE) pMessage,
       cbMessage,
       &pDataToClient, 
       &cbDataToClient,
       cbSecurityTrailer);

//-----------------------------------------------------------------   
//  Send the encrypted data to client.


  if (!SendBytes(
     Server_Socket, 
     pDataToClient, 
    cbDataToClient))
  {
     printf("send message failed. \n");
     cleanup();
  }

  printf(" %d encrypted bytes sent. \n", cbDataToClient);

  if (Server_Socket)
  {
    DeleteSecurityContext (&hctxt);
    FreeCredentialHandle (&hcred);
    shutdown (Server_Socket, 2) ; 
    closesocket (Server_Socket);
    Server_Socket = 0;
  }
  
  if (pUserName)
  {
     free (pUserName);
     pUserName = NULL;
     cbUserName = 0;
  }
  if(pDataToClient)
  {
     free (pDataToClient);
     pDataToClient = NULL;
     cbDataToClient = 0;
  }
}  // end while loop

printf("Server ran to completion without error.\n");
cleanup(); 
}  // end main

BOOL AcceptAuthSocket (SOCKET *ServerSocket)
{
SOCKET sockListen;
SOCKET sockClient;
SOCKADDR_IN sockIn;

//-----------------------------------------------------------------   
//  Create listening socket.

sockListen = socket (
   PF_INET, 
   SOCK_STREAM, 
   0);
   
if (INVALID_SOCKET == sockListen)  
{
   fprintf (stderr, "Failed to create socket: %u\n", GetLastError ());
   return(FALSE);
}
   
//-----------------------------------------------------------------   
//  Bind to local port.

sockIn.sin_family = AF_INET;
sockIn.sin_addr.s_addr = 0;
sockIn.sin_port = htons(usPort);
 
if (SOCKET_ERROR == bind (
   sockListen, 
   (LPSOCKADDR) &sockIn, 
   sizeof (sockIn)))  
{
    fprintf (stderr, "bind failed: %u\n", GetLastError ());
    return(FALSE);
}
   
//-----------------------------------------------------------------   
//  Listen for client.
   
if (SOCKET_ERROR == listen (sockListen, 1))  
{
   fprintf (stderr, "Listen failed: %u\n", GetLastError ());
   return(FALSE);
}
else
{
   printf("Listening ! \n");
}

//-----------------------------------------------------------------   
//  Accept client.
   
sockClient = accept (
   sockListen, 
   NULL, 
   NULL);

if (INVALID_SOCKET == sockClient)  
{
    fprintf (stderr, "accept failed: %u\n", GetLastError ());
    return(FALSE);
}
   
closesocket (sockListen);

*ServerSocket = sockClient;

   
return(DoAuthentication (sockClient));

}  // end AcceptAuthSocket  

BOOL DoAuthentication (SOCKET AuthSocket)
{
SECURITY_STATUS   ss;
DWORD cbIn,       cbOut;
BOOL              done =   FALSE;
TimeStamp         Lifetime;
BOOL              fNewConversation;

fNewConversation = TRUE;

ss = AcquireCredentialsHandle (
       NULL, 
       g_lpPackageName,
       SECPKG_CRED_INBOUND,
       NULL, 
       NULL, 
       NULL, 
       NULL, 
       &hcred,
       &Lifetime);

if (!SEC_SUCCESS (ss))
{
       fprintf (stderr, "AcquireCreds failed: 0x%08x\n", ss);
       return(FALSE);
}

while(!done) 
{
   if (!ReceiveMsg (
     AuthSocket, 
     g_pInBuf, 
     g_cbMaxMessage, 
     &cbIn))
   {
      return(FALSE);
   }
   
   cbOut = g_cbMaxMessage;

    if (!GenServerContext (
       g_pInBuf, 
       cbIn, 
       g_pOutBuf, 
       &cbOut, 
       &done,
       fNewConversation))
   {
        fprintf(stderr,"GenServerContext failed.\n");
        return(FALSE);
    }
    fNewConversation = FALSE;
    if (!SendMsg (
        AuthSocket, 
        g_pOutBuf, 
        cbOut))
    {
        fprintf(stderr,"Sending message failed.\n");
        return(FALSE);
    }
} 

return(TRUE);
}  // end DoAuthentication

BOOL GenServerContext (
         BYTE *pIn,
         DWORD cbIn,
         BYTE *pOut,
         DWORD *pcbOut,
         BOOL *pfDone,
         BOOL fNewConversation)
{
SECURITY_STATUS   ss;
TimeStamp         Lifetime;
SecBufferDesc     OutBuffDesc;
SecBuffer         OutSecBuff;
SecBufferDesc     InBuffDesc;
SecBuffer         InSecBuff;
ULONG             Attribs = 0;
 
//----------------------------------------------------------------
//  Prepare output buffers.

OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

//----------------------------------------------------------------
//  Prepare input buffers.

InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;

printf ("Token buffer received (%lu bytes):\n", InSecBuff.cbBuffer);
PrintHexDump (InSecBuff.cbBuffer, (PBYTE)InSecBuff.pvBuffer);

ss = AcceptSecurityContext (
       &hcred,
       fNewConversation ? NULL : &hctxt,
       &InBuffDesc,
       Attribs, 
       SECURITY_NATIVE_DREP,
       &hctxt,
       &OutBuffDesc,
       &Attribs,
       &Lifetime);

if (!SEC_SUCCESS (ss))  
{
    fprintf (stderr, "AcceptSecurityContext failed: 0x%08x\n", ss);
    return FALSE;
}

//----------------------------------------------------------------
//  Complete token if applicable.
   
if ((SEC_I_COMPLETE_NEEDED == ss) 
   || (SEC_I_COMPLETE_AND_CONTINUE == ss))  
{
    ss = CompleteAuthToken (&hctxt, &OutBuffDesc);
    if (!SEC_SUCCESS(ss))  
   {
       fprintf (stderr, "complete failed: 0x%08x\n", ss);
       return FALSE;
    }
}

*pcbOut = OutSecBuff.cbBuffer;

//  fNewConversation equals FALSE.

printf ("Token buffer generated (%lu bytes):\n", 
     OutSecBuff.cbBuffer);
PrintHexDump (
   OutSecBuff.cbBuffer, 
   (PBYTE)OutSecBuff.pvBuffer);

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) 
   || (SEC_I_COMPLETE_AND_CONTINUE == ss));

printf("AcceptSecurityContext result = 0x%08x\n", ss);

return TRUE;

}  // end GenServerContext


BOOL EncryptThis (
         PBYTE pMessage, 
         ULONG cbMessage,
         BYTE ** ppOutput,
         ULONG * pcbOutput,
         ULONG cbSecurityTrailer)
{
SECURITY_STATUS   ss;
SecBufferDesc     BuffDesc;
SecBuffer         SecBuff[2];
ULONG             ulQop = 0;
ULONG             SigBufferSize;

//-----------------------------------------------------------------
//  The size of the trailer (signature + padding) block is 
//  determined from the global cbSecurityTrailer.

SigBufferSize = cbSecurityTrailer;

printf ("Data before encryption: %s\n", pMessage);
printf ("Length of data before encryption: %d \n",cbMessage);

//-----------------------------------------------------------------
//  Allocate a buffer to hold the signature,
//  encrypted data, and a DWORD  
//  that specifies the size of the trailer block.

* ppOutput = (PBYTE) malloc (
 SigBufferSize + cbMessage + sizeof(DWORD));

//------------------------------------------------------------------
//  Prepare buffers.

BuffDesc.ulVersion = 0;
BuffDesc.cBuffers = 2;
BuffDesc.pBuffers = SecBuff;

SecBuff[0].cbBuffer = SigBufferSize;
SecBuff[0].BufferType = SECBUFFER_TOKEN;
SecBuff[0].pvBuffer = *ppOutput + sizeof(DWORD);

SecBuff[1].cbBuffer = cbMessage;
SecBuff[1].BufferType = SECBUFFER_DATA;
SecBuff[1].pvBuffer = pMessage;

ss = EncryptMessage(
    &hctxt,
    ulQop,
    &BuffDesc,
    0);

if (!SEC_SUCCESS(ss)) 
{
   fprintf (stderr, "EncryptMessage failed: 0x%08x\n", ss);
   return(FALSE);
}
else
{
   printf("The message has been encrypted. \n");
}

//------------------------------------------------------------------
//  Indicate the size of the buffer in the first DWORD. 

*((DWORD *) *ppOutput) = SecBuff[0].cbBuffer;

//-----------------------------------------------------------------
//  Append the encrypted data to our trailer block
//  to form a single block. 
//  Putting trailer at the beginning of the buffer works out 
//  better. 

memcpy (*ppOutput+SecBuff[0].cbBuffer+sizeof(DWORD), pMessage,
    cbMessage);

*pcbOutput = cbMessage + SecBuff[0].cbBuffer + sizeof(DWORD);

printf ("data after encryption including trailer (%lu bytes):\n",
    *pcbOutput);
PrintHexDump (*pcbOutput, *ppOutput);

return TRUE;

}  // end EncryptThis



void PrintHexDump(DWORD length, PBYTE buffer)
{
DWORD i,count,index;
CHAR rgbDigits[]="0123456789abcdef";
CHAR rgbLine[100];
char cbLine;

for(index = 0; length;
   length -= count, buffer += count, index += count) 
{
   count = (length > 16) ? 16:length;

   sprintf_s(rgbLine, 100, "%4.4x  ",index);
   cbLine = 6;

   for(i=0;i<count;i++) 
   {
      rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4];
      rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f];
      if(i == 7) 
      {
         rgbLine[cbLine++] = ':';
      } 
      else 
      {
         rgbLine[cbLine++] = ' ';
      }
   }
   for(; i < 16; i++) 
   {
      rgbLine[cbLine++] = ' ';
      rgbLine[cbLine++] = ' ';
      rgbLine[cbLine++] = ' ';
   }

   rgbLine[cbLine++] = ' ';

   for(i = 0; i < count; i++) 
   {
      if(buffer[i] < 32 || buffer[i] > 126) 
      {
         rgbLine[cbLine++] = '.';
      } 
      else 
      {
         rgbLine[cbLine++] = buffer[i];
      }
   }

   rgbLine[cbLine++] = 0;
   printf("%s\n", rgbLine);
}
}  // end PrintHexDump


BOOL SendMsg (
   SOCKET s, 
   PBYTE pBuf, 
   DWORD cbBuf)
{
if (0 == cbBuf)
   return(TRUE);

//----------------------------------------------------------------
//  Send the size of the message.

if (!SendBytes (
  s, 
  (PBYTE)&cbBuf, 
  sizeof (cbBuf)))
{
    return(FALSE);
}

//----------------------------------------------------------------    
//  Send the body of the message.

if (!SendBytes (
   s, 
   pBuf, 
   cbBuf))
{
    return(FALSE);
}

return(TRUE);
} // end SendMsg    

BOOL ReceiveMsg (
  SOCKET s, 
  PBYTE pBuf, 
  DWORD cbBuf, 
  DWORD *pcbRead)
{
DWORD cbRead;
DWORD cbData;

//-----------------------------------------------------------------
//  Retrieve the number of bytes in the message.

if (!ReceiveBytes (
  s, 
  (PBYTE)&cbData, 
  sizeof (cbData), 
  &cbRead))
{
  return(FALSE);
}

if (sizeof (cbData) != cbRead)
{
   return(FALSE);
}

//----------------------------------------------------------------
//  Read the full message.

if (!ReceiveBytes (
  s, 
  pBuf, 
  cbData, 
  &cbRead))
{
   return(FALSE);
}

if (cbRead != cbData)
{
  return(FALSE);
}

*pcbRead = cbRead;

return(TRUE);
}  // end ReceiveMsg    

BOOL SendBytes (
  SOCKET s, 
  PBYTE pBuf, 
  DWORD cbBuf)
{
PBYTE pTemp = pBuf;
int cbSent, cbRemaining = cbBuf;

if (0 == cbBuf)
{
   return(TRUE);
}

while (cbRemaining) 
{
   cbSent = send (
   s, 
   (const char *)pTemp, 
   cbRemaining, 
   0);
  if (SOCKET_ERROR == cbSent) 
{
   fprintf (stderr, "send failed: %u\n", GetLastError ());
   return FALSE;
}

pTemp += cbSent;
cbRemaining -= cbSent;
}

return TRUE;
}  // end SendBytes

BOOL ReceiveBytes (
  SOCKET s, 
  PBYTE pBuf, 
  DWORD cbBuf, 
  DWORD *pcbRead)
{
PBYTE pTemp = pBuf;
int cbRead, cbRemaining = cbBuf;

while (cbRemaining) 
{
    cbRead = recv (
        s, 
        (char *)pTemp, 
        cbRemaining, 
        0);
    if (0 == cbRead)
   {
      break;
   }

   if (SOCKET_ERROR == cbRead) 
  {
      fprintf (stderr, "recv failed: %u\n", GetLastError ());
      return FALSE;
  }

cbRemaining -= cbRead;
pTemp += cbRead;
}

*pcbRead = cbBuf - cbRemaining;

return TRUE;
}  // end ReceivesBytes

void cleanup()
{
   if (g_pInBuf)
      free (g_pInBuf);

   if (g_pOutBuf)
      free (g_pOutBuf);

   WSACleanup ();
   exit(0);
}