Advertisement
Guest User

TLSclient.c

a guest
Feb 26th, 2015
526
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 72.67 KB | None | 0 0
  1. //
  2. // MS Visual C: cl -Os TLSclient.c
  3. //
  4.  
  5. // This C example is designed as more of a guide than a library to be plugged into an application
  6. // That module required a couple of major re-writes and is available upon request
  7. // The Basic example has tips to the direction you should take
  8. // This will work with connections on port 587 that upgrade a plain text session to an encrypted session with STARTTLS as covered here.
  9.  
  10. // TLSclient.c - SSPI Schannel gmail TLS connection example
  11.  
  12. // #define DYNAMIC_LOAD   // dinamic load of SSPI
  13.  
  14. #define SECURITY_WIN32
  15. #define IO_BUFFER_SIZE      0x10000
  16. #define DLL_NAME            TEXT("Secur32.dll")
  17. #define NT4_DLL_NAME        TEXT("Security.dll")
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <windows.h>
  22. #include <winsock.h>
  23. #include <wincrypt.h>
  24. #include <wintrust.h>
  25. #include <schannel.h>
  26. #include <security.h>
  27. #include <sspi.h>
  28.  
  29. #pragma comment(lib, "WSock32.Lib")
  30. #pragma comment(lib, "Crypt32.Lib")
  31. #pragma comment(lib, "user32.lib")
  32. #ifndef DYNAMIC_LOAD
  33. #pragma comment(lib, "secur32.lib")
  34. #endif
  35. //#pragma comment(lib, "MSVCRTD.lib")
  36.  
  37.  
  38. #define S_PROTO_ID SP_PROT_SSL3TLS1_X // 0=default; SP_PROT_TLS1; SP_PROT_PCT1; SP_PROT_SSL2; SP_PROT_SSL3;
  39. #define K_PROTO_ID 0                  // 0=default; CALG_DH_EPHEM; CALG_RSA_KEYX;
  40.  
  41. // Globals.
  42. BOOL    fVerbose        = FALSE; // FALSE; // TRUE;
  43. BOOL    fHexDump        = FALSE;
  44. BOOL    fDoTLS          = FALSE;
  45. BOOL    fNoCertErr      = FALSE;
  46.  
  47. INT     iPortNumber     = 465;              // gmail SSL
  48. LPSTR   pszServerName   = "smtp.gmail.com"; // server name or IP
  49. LPSTR   pszUser         = NULL;             // if specified, search certificate in "MY" store
  50.  
  51. DWORD   dwProtocol      = S_PROTO_ID;
  52. ALG_ID  aiKeyExch       = K_PROTO_ID;
  53.  
  54. BOOL    fUseProxy       = FALSE;
  55. LPSTR   pszProxyServer  = "proxy";
  56. INT     iProxyPort      = 80;
  57.  
  58. HCERTSTORE hMyCertStore = NULL;
  59. #ifdef DYNAMIC_LOAD
  60. HMODULE g_hSecurity            = NULL;
  61. #endif
  62.  
  63. SCHANNEL_CRED SchannelCred;
  64. PSecurityFunctionTableA g_pSSPI;
  65.  
  66.  
  67.  
  68. /*****************************************************************************/
  69. static void DisplayWinVerifyTrustError(DWORD Status)
  70. {
  71.     LPSTR pszName = NULL;
  72.  
  73.     switch(Status)
  74.     {
  75.             case CERT_E_EXPIRED:                pszName = "CERT_E_EXPIRED";                 break;
  76.             case CERT_E_VALIDITYPERIODNESTING:  pszName = "CERT_E_VALIDITYPERIODNESTING";   break;
  77.             case CERT_E_ROLE:                   pszName = "CERT_E_ROLE";                    break;
  78.             case CERT_E_PATHLENCONST:           pszName = "CERT_E_PATHLENCONST";            break;
  79.             case CERT_E_CRITICAL:               pszName = "CERT_E_CRITICAL";                break;
  80.             case CERT_E_PURPOSE:                pszName = "CERT_E_PURPOSE";                 break;
  81.             case CERT_E_ISSUERCHAINING:         pszName = "CERT_E_ISSUERCHAINING";          break;
  82.             case CERT_E_MALFORMED:              pszName = "CERT_E_MALFORMED";               break;
  83.             case CERT_E_UNTRUSTEDROOT:          pszName = "CERT_E_UNTRUSTEDROOT";           break;
  84.             case CERT_E_CHAINING:               pszName = "CERT_E_CHAINING";                break;
  85.             case TRUST_E_FAIL:                  pszName = "TRUST_E_FAIL";                   break;
  86.             case CERT_E_REVOKED:                pszName = "CERT_E_REVOKED";                 break;
  87.             case CERT_E_UNTRUSTEDTESTROOT:      pszName = "CERT_E_UNTRUSTEDTESTROOT";       break;
  88.             case CERT_E_REVOCATION_FAILURE:     pszName = "CERT_E_REVOCATION_FAILURE";      break;
  89.             case CERT_E_CN_NO_MATCH:            pszName = "CERT_E_CN_NO_MATCH";             break;
  90.             case CERT_E_WRONG_USAGE:            pszName = "CERT_E_WRONG_USAGE";             break;
  91.             default:                            pszName = "unknown";                        break;
  92.     }
  93.     printf("Error 0x%x (%s) returned by CertVerifyCertificateChainPolicy!\n", Status, pszName);
  94. }
  95.  
  96.  
  97. /*****************************************************************************/
  98. static void DisplayWinSockError(DWORD ErrCode)
  99. {
  100.     LPSTR pszName = NULL; // http://www.sockets.com/err_lst1.htm#WSANO_DATA
  101.  
  102.     switch(ErrCode) // http://msdn.microsoft.com/en-us/library/ms740668(VS.85).aspx
  103.     {
  104.             case     10035:  pszName = "WSAEWOULDBLOCK    "; break;
  105.             case     10036:  pszName = "WSAEINPROGRESS    "; break;
  106.             case     10037:  pszName = "WSAEALREADY       "; break;
  107.             case     10038:  pszName = "WSAENOTSOCK       "; break;
  108.             case     10039:  pszName = "WSAEDESTADDRREQ   "; break;
  109.             case     10040:  pszName = "WSAEMSGSIZE       "; break;
  110.             case     10041:  pszName = "WSAEPROTOTYPE     "; break;
  111.             case     10042:  pszName = "WSAENOPROTOOPT    "; break;
  112.             case     10043:  pszName = "WSAEPROTONOSUPPORT"; break;
  113.             case     10044:  pszName = "WSAESOCKTNOSUPPORT"; break;
  114.             case     10045:  pszName = "WSAEOPNOTSUPP     "; break;
  115.             case     10046:  pszName = "WSAEPFNOSUPPORT   "; break;
  116.             case     10047:  pszName = "WSAEAFNOSUPPORT   "; break;
  117.             case     10048:  pszName = "WSAEADDRINUSE     "; break;
  118.             case     10049:  pszName = "WSAEADDRNOTAVAIL  "; break;
  119.             case     10050:  pszName = "WSAENETDOWN       "; break;
  120.             case     10051:  pszName = "WSAENETUNREACH    "; break;
  121.             case     10052:  pszName = "WSAENETRESET      "; break;
  122.             case     10053:  pszName = "WSAECONNABORTED   "; break;
  123.             case     10054:  pszName = "WSAECONNRESET     "; break;
  124.             case     10055:  pszName = "WSAENOBUFS        "; break;
  125.             case     10056:  pszName = "WSAEISCONN        "; break;
  126.             case     10057:  pszName = "WSAENOTCONN       "; break;
  127.             case     10058:  pszName = "WSAESHUTDOWN      "; break;
  128.             case     10059:  pszName = "WSAETOOMANYREFS   "; break;
  129.             case     10060:  pszName = "WSAETIMEDOUT      "; break;
  130.             case     10061:  pszName = "WSAECONNREFUSED   "; break;
  131.             case     10062:  pszName = "WSAELOOP          "; break;
  132.             case     10063:  pszName = "WSAENAMETOOLONG   "; break;
  133.             case     10064:  pszName = "WSAEHOSTDOWN      "; break;
  134.             case     10065:  pszName = "WSAEHOSTUNREACH   "; break;
  135.             case     10066:  pszName = "WSAENOTEMPTY      "; break;
  136.             case     10067:  pszName = "WSAEPROCLIM       "; break;
  137.             case     10068:  pszName = "WSAEUSERS         "; break;
  138.             case     10069:  pszName = "WSAEDQUOT         "; break;
  139.             case     10070:  pszName = "WSAESTALE         "; break;
  140.             case     10071:  pszName = "WSAEREMOTE        "; break;
  141.             case     10091:  pszName = "WSASYSNOTREADY    "; break;
  142.             case     10092:  pszName = "WSAVERNOTSUPPORTED"; break;
  143.             case     10093:  pszName = "WSANOTINITIALISED "; break;
  144.             case     11001:  pszName = "WSAHOST_NOT_FOUND "; break;
  145.             case     11002:  pszName = "WSATRY_AGAIN      "; break;
  146.             case     11003:  pszName = "WSANO_RECOVERY    "; break;
  147.             case     11004:  pszName = "WSANO_DATA        "; break;
  148.     }
  149.     printf("Error 0x%x (%s)\n", ErrCode, pszName);
  150. }
  151.  
  152. /*****************************************************************************/
  153. static void DisplaySECError(DWORD ErrCode)
  154. {
  155.     LPSTR pszName = NULL; // WinError.h
  156.  
  157.     switch(ErrCode)
  158.     {
  159.             case     SEC_E_BUFFER_TOO_SMALL:
  160.                 pszName = "SEC_E_BUFFER_TOO_SMALL - The message buffer is too small. Used with the Digest SSP.";
  161.                 break;
  162.  
  163.             case     SEC_E_CRYPTO_SYSTEM_INVALID:
  164.                 pszName = "SEC_E_CRYPTO_SYSTEM_INVALID - The cipher chosen for the security context is not supported. Used with the Digest SSP.";
  165.                 break;
  166.             case     SEC_E_INCOMPLETE_MESSAGE:
  167.                 pszName = "SEC_E_INCOMPLETE_MESSAGE - The data in the input buffer is incomplete. The application needs to read more data from the server and call DecryptMessage (General) again.";
  168.                 break;
  169.  
  170.             case     SEC_E_INVALID_HANDLE:
  171.                 pszName = "SEC_E_INVALID_HANDLE - A context handle that is not valid was specified in the phContext parameter. Used with the Digest and Schannel SSPs.";
  172.                 break;
  173.  
  174.             case     SEC_E_INVALID_TOKEN:
  175.                 pszName = "SEC_E_INVALID_TOKEN - The buffers are of the wrong type or no buffer of type SECBUFFER_DATA was found. Used with the Schannel SSP.";
  176.                 break;
  177.                
  178.             case     SEC_E_MESSAGE_ALTERED:
  179.                 pszName = "SEC_E_MESSAGE_ALTERED - The message has been altered. Used with the Digest and Schannel SSPs.";
  180.                 break;
  181.                
  182.             case     SEC_E_OUT_OF_SEQUENCE:
  183.                 pszName = "SEC_E_OUT_OF_SEQUENCE - The message was not received in the correct sequence.";
  184.                 break;
  185.                
  186.             case     SEC_E_QOP_NOT_SUPPORTED:
  187.                 pszName = "SEC_E_QOP_NOT_SUPPORTED - Neither confidentiality nor integrity are supported by the security context. Used with the Digest SSP.";
  188.                 break;
  189.                
  190.             case     SEC_I_CONTEXT_EXPIRED:
  191.                 pszName = "SEC_I_CONTEXT_EXPIRED - The message sender has finished using the connection and has initiated a shutdown.";
  192.                 break;
  193.                
  194.             case     SEC_I_RENEGOTIATE:
  195.                 pszName = "SEC_I_RENEGOTIATE - The remote party requires a new handshake sequence or the application has just initiated a shutdown.";
  196.                 break;
  197.                
  198.             case     SEC_E_ENCRYPT_FAILURE:
  199.                 pszName = "SEC_E_ENCRYPT_FAILURE - The specified data could not be encrypted.";
  200.                 break;
  201.                
  202.             case     SEC_E_DECRYPT_FAILURE:
  203.                 pszName = "SEC_E_DECRYPT_FAILURE - The specified data could not be decrypted.";
  204.                 break;
  205.  
  206.     }
  207.     printf("Error 0x%x %s \n", ErrCode, pszName);
  208. }
  209.  
  210.  
  211.  
  212. /*****************************************************************************/
  213. static void DisplayCertChain( PCCERT_CONTEXT  pServerCert, BOOL fLocal )
  214. {
  215.     CHAR szName[1000];
  216.     PCCERT_CONTEXT pCurrentCert, pIssuerCert;
  217.     DWORD dwVerificationFlags;
  218.  
  219.     printf("\n");
  220.  
  221.     // display leaf name
  222.     if( !CertNameToStr( pServerCert->dwCertEncodingType,
  223.                                                 &pServerCert->pCertInfo->Subject,
  224.                                                 CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
  225.                                                 szName, sizeof(szName) ) )
  226.     { printf("**** Error 0x%x building subject name\n", GetLastError()); }
  227.  
  228.     if(fLocal) printf("Client subject: %s\n", szName);
  229.     else printf("Server subject: %s\n", szName);
  230.  
  231.     if( !CertNameToStr( pServerCert->dwCertEncodingType,
  232.                                                 &pServerCert->pCertInfo->Issuer,
  233.                                                 CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
  234.                                                 szName, sizeof(szName) ) )
  235.     { printf("**** Error 0x%x building issuer name\n", GetLastError()); }
  236.  
  237.     if(fLocal) printf("Client issuer: %s\n", szName);
  238.     else printf("Server issuer: %s\n\n", szName);
  239.  
  240.  
  241.     // display certificate chain
  242.     pCurrentCert = pServerCert;
  243.     while(pCurrentCert != NULL)
  244.     {
  245.         dwVerificationFlags = 0;
  246.         pIssuerCert = CertGetIssuerCertificateFromStore( pServerCert->hCertStore, pCurrentCert, NULL, &dwVerificationFlags );
  247.         if(pIssuerCert == NULL)
  248.         {
  249.             if(pCurrentCert != pServerCert) CertFreeCertificateContext(pCurrentCert);
  250.             break;
  251.         }
  252.  
  253.         if( !CertNameToStr( pIssuerCert->dwCertEncodingType,
  254.                                                         &pIssuerCert->pCertInfo->Subject,
  255.                                                         CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
  256.                                                         szName, sizeof(szName) ) )
  257.         { printf("**** Error 0x%x building subject name\n", GetLastError()); }
  258.  
  259.         printf("CA subject: %s\n", szName);
  260.  
  261.         if( !CertNameToStr( pIssuerCert->dwCertEncodingType,
  262.                                                         &pIssuerCert->pCertInfo->Issuer,
  263.                                                         CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
  264.                                                         szName, sizeof(szName) ) )
  265.         { printf("**** Error 0x%x building issuer name\n", GetLastError()); }
  266.  
  267.         printf("CA issuer: %s\n\n", szName);
  268.  
  269.         if(pCurrentCert != pServerCert) CertFreeCertificateContext(pCurrentCert);
  270.         pCurrentCert = pIssuerCert;
  271.         pIssuerCert = NULL;
  272.     }
  273. }
  274.  
  275. /*****************************************************************************/
  276. static void DisplayConnectionInfo( CtxtHandle *phContext )
  277. {
  278.     char  szInfo[2048];
  279.     WORD  s;
  280.  
  281.     SECURITY_STATUS Status;
  282.     SecPkgContext_ConnectionInfo ConnectionInfo;
  283.     SecPkgContext_KeyInfo KeyInfo;
  284.    
  285.     Status = g_pSSPI->QueryContextAttributesA( phContext, SECPKG_ATTR_CONNECTION_INFO, (PVOID)&ConnectionInfo );
  286.     if(Status != SEC_E_OK) { printf("Error 0x%x querying connection info\n", Status); return; }
  287.  
  288.     Status = g_pSSPI->QueryContextAttributesA( phContext, SECPKG_ATTR_KEY_INFO, (PVOID)&KeyInfo );
  289.     if(Status != SEC_E_OK) { printf("Error 0x%x querying key info\n", Status); return; }
  290.    
  291.  
  292.     // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549%28v=vs.85%29.aspx    
  293.    
  294.     switch(ConnectionInfo.dwProtocol)
  295.     {
  296.       case SP_PROT_TLS1_2_CLIENT:
  297.         sprintf(szInfo, "TLSv1.2");
  298.         break;
  299.       case SP_PROT_TLS1_1_CLIENT:
  300.         sprintf(szInfo, "TLSv1.1");
  301.         break;
  302.       case SP_PROT_TLS1_CLIENT:
  303.         sprintf(szInfo, "TLSv1");
  304.         break;
  305.       case SP_PROT_SSL3_CLIENT:
  306.         sprintf(szInfo, "SSLv3");
  307.         break;
  308.       case SP_PROT_SSL2_CLIENT:
  309.         sprintf(szInfo, "SSLv2");
  310.         break;
  311.       case SP_PROT_PCT1_CLIENT:
  312.         sprintf(szInfo, "PCTv1");
  313.         break;
  314.       default:
  315.         sprintf(szInfo, "0x%x", ConnectionInfo.dwProtocol);
  316.         break;
  317.     }
  318.     printf("%s ", szInfo);
  319.  
  320.     switch(ConnectionInfo.aiExch)
  321.     {
  322.       case CALG_RSA_KEYX:
  323.       case CALG_RSA_SIGN:
  324.           sprintf(szInfo, "RSA");
  325.           break;
  326.       case CALG_KEA_KEYX:
  327.           sprintf(szInfo, "KEA");
  328.           break;
  329.       case CALG_DH_EPHEM:
  330.           sprintf(szInfo, "DHE");
  331.           break;
  332.       case CALG_ECDH:
  333.           sprintf(szInfo, "ECDH");
  334.           break;
  335.       case CALG_ECDSA:
  336.           sprintf(szInfo, "ECDSA");
  337.           break;
  338.       case 0xAE06:
  339.           sprintf(szInfo, "ECDHE");
  340.           break;
  341.       default:
  342.           sprintf(szInfo, "0x%x", ConnectionInfo.aiExch);
  343.     }
  344.     printf("%s_%s", szInfo, KeyInfo.sSignatureAlgorithmName);
  345.     /*
  346.     printf("k=%s (%d bits), ", szInfo, ConnectionInfo.dwExchStrength);
  347.     printf("s=%s, ", KeyInfo.sSignatureAlgorithmName);
  348.     */
  349.    
  350.     switch(ConnectionInfo.aiCipher)
  351.     {
  352.       case CALG_RC2:
  353.         sprintf(szInfo, "RC2");
  354.         break;
  355.       case CALG_RC4:
  356.         sprintf(szInfo, "RC4");
  357.         break;
  358.       case CALG_DES:
  359.         sprintf(szInfo, "DES");
  360.         break;
  361.       case CALG_3DES:
  362.         sprintf(szInfo, "3DES");
  363.         break;
  364.       case CALG_AES_128:
  365.         sprintf(szInfo, "AES128");
  366.         break;
  367.       case CALG_AES_256:
  368.         sprintf(szInfo, "AES256");
  369.         break;
  370.       default:
  371.         sprintf(szInfo, "0x%x", ConnectionInfo.aiCipher);
  372.         break;
  373.     }
  374.     printf("_%s", szInfo);
  375.     //printf("e=%s (%d bits), ", szInfo, ConnectionInfo.dwCipherStrength);
  376.  
  377.     switch(ConnectionInfo.aiHash)
  378.     {
  379.       case CALG_MD5:
  380.         sprintf(szInfo, "MD5");
  381.         break;
  382.       case CALG_SHA:
  383.         sprintf(szInfo, "SHA");
  384.         break;
  385.       case CALG_SHA_256:
  386.         sprintf(szInfo, "SHA256");
  387.         break;
  388.       case CALG_SHA_384:
  389.         sprintf(szInfo, "SHA384");
  390.         break;
  391.       case CALG_SHA_512:
  392.         sprintf(szInfo, "SHA512");
  393.         break;
  394.       case CALG_HMAC:        
  395.         sprintf(szInfo, "HMAC");
  396.         break;
  397.       default:
  398.         sprintf(szInfo, "0x%x", ConnectionInfo.aiHash);
  399.     }
  400.     printf("_%s", szInfo);
  401.    
  402.     printf(" (k=%d, e=%d, c=%d, h=%d)\n", KeyInfo.KeySize, ConnectionInfo.dwExchStrength, ConnectionInfo.dwCipherStrength, ConnectionInfo.dwHashStrength);
  403.    
  404.     //printf("h=%s (%d bits)\n", szInfo, ConnectionInfo.dwHashStrength);
  405.     printf("\n");
  406. }
  407.  
  408.  
  409. /*****************************************************************************/
  410. static void PrintHexDump( DWORD length, PBYTE buffer )
  411. {
  412.     DWORD i,count,index;
  413.     CHAR rgbDigits[]="0123456789abcdef";
  414.     CHAR rgbLine[100];
  415.     char cbLine;
  416.  
  417.     for(index = 0; length; length -= count, buffer += count, index += count)
  418.     {
  419.         count = (length > 16) ? 16:length;
  420.         sprintf(rgbLine, "%4.4x  ",index);
  421.         cbLine = 6;
  422.  
  423.         for(i=0;i<count;i++)
  424.         {
  425.             rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4];
  426.             rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f];
  427.             if(i == 7) rgbLine[cbLine++] = ':';
  428.             else rgbLine[cbLine++] = ' ';
  429.         }
  430.         for(; i < 16; i++)
  431.         {
  432.             rgbLine[cbLine++] = ' ';
  433.             rgbLine[cbLine++] = ' ';
  434.             rgbLine[cbLine++] = ' ';
  435.         }
  436.         rgbLine[cbLine++] = ' ';
  437.  
  438.         for(i = 0; i < count; i++)
  439.         {
  440.             if(buffer[i] < 32 || buffer[i] > 126 || buffer[i] == '%') rgbLine[cbLine++] = '.';
  441.             else rgbLine[cbLine++] = buffer[i];
  442.         }
  443.         rgbLine[cbLine++] = 0;
  444.         printf("%s\n", rgbLine);
  445.     }
  446. }
  447.  
  448. /*****************************************************************************/
  449. static void PrintText( DWORD length, PBYTE buffer ) // handle unprintable charaters
  450. {
  451.     int i; //
  452.  
  453.     // printf("\n"); // "length = %d bytes \n", length);
  454.     for( i = 0; i < (int)length; i++ )
  455.     {
  456.       if( buffer[i] == 10 || buffer[i] == 13 ) printf("%c", (char)buffer[i]);
  457.       else if( buffer[i] < 32 || buffer[i] > 126 || buffer[i] == '%' ) printf("%c", '.');
  458.       else printf("%c", (char)buffer[i]);
  459.     }
  460.     // printf("\n");
  461. }
  462.  
  463.  
  464.  
  465. /*****************************************************************************/
  466. static void WriteDataToFile( PSTR pszData, PBYTE pbData, DWORD cbData )
  467. {
  468.     FILE *file;
  469.  
  470.     file = fopen(pszData, "wb");
  471.     if(file == NULL)
  472.     { printf("**** Error opening file '%s'\n", pszData); return; }
  473.  
  474.     if(fwrite(pbData, 1, cbData, file) != cbData)
  475.     { printf("**** Error writing to file\n"); return; }
  476.  
  477.     fclose(file);
  478. }
  479.  
  480. /*****************************************************************************/
  481. BOOL LoadSecurityLibrary( void ) // load SSPI.DLL, set up a special table - PSecurityFunctionTable
  482. {
  483. #ifdef DYNAMIC_LOAD  
  484.     INIT_SECURITY_INTERFACE pInitSecurityInterface;
  485. //  QUERY_CREDENTIALS_ATTRIBUTES_FN pQueryCredentialsAttributes;
  486.     OSVERSIONINFO VerInfo;
  487.     UCHAR lpszDLL[MAX_PATH];
  488.  
  489.  
  490.     //  Find out which security DLL to use, depending on
  491.     //  whether we are on Win2K, NT or Win9x
  492.     VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  493.     if ( !GetVersionEx (&VerInfo) ) return FALSE;
  494.  
  495.     if ( VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT  &&  VerInfo.dwMajorVersion == 4 )
  496.     {
  497.         strcpy(lpszDLL, NT4_DLL_NAME); // NT4_DLL_NAME TEXT("Security.dll")
  498.     }
  499.     else if ( VerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
  500.               VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
  501.     {
  502.         strcpy(lpszDLL, DLL_NAME); // DLL_NAME TEXT("Secur32.dll")
  503.     }
  504.     else
  505.         { printf( "System not recognized\n" ); return FALSE; }
  506.  
  507.  
  508.     //  Load Security DLL
  509.     g_hSecurity = LoadLibrary(lpszDLL);
  510.     if(g_hSecurity == NULL) { printf( "Error 0x%x loading %s.\n", GetLastError(), lpszDLL ); return FALSE; }
  511.  
  512.     pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress( g_hSecurity, "InitSecurityInterfaceA" );
  513.     if(pInitSecurityInterface == NULL) { printf( "Error 0x%x reading InitSecurityInterface entry point.\n", GetLastError() ); return FALSE; }
  514.  
  515.     g_pSSPI = pInitSecurityInterface(); // call InitSecurityInterfaceA(void);
  516. #else
  517.     g_pSSPI = InitSecurityInterface(); // call InitSecurityInterfaceA(void);    
  518. #endif  
  519.     if(g_pSSPI == NULL) { printf("Error 0x%x reading security interface.\n", GetLastError()); return FALSE; }
  520.     return TRUE; // and PSecurityFunctionTable
  521. }
  522.  
  523.  
  524. /*****************************************************************************/
  525. void UnloadSecurityLibrary(void)
  526. {
  527. #ifdef DYNAMIC_LOAD  
  528.     FreeLibrary(g_hSecurity);
  529.     g_hSecurity = NULL;
  530. #endif    
  531. }
  532.  
  533.  
  534. /*****************************************************************************/
  535. static DWORD VerifyServerCertificate( PCCERT_CONTEXT pServerCert, PSTR pszServerName, DWORD dwCertFlags )
  536. {
  537.     HTTPSPolicyCallbackData  polHttps;
  538.     CERT_CHAIN_POLICY_PARA   PolicyPara;
  539.     CERT_CHAIN_POLICY_STATUS PolicyStatus;
  540.     CERT_CHAIN_PARA          ChainPara;
  541.     PCCERT_CHAIN_CONTEXT     pChainContext = NULL;
  542.     DWORD                                         cchServerName, Status;
  543.     LPSTR rgszUsages[]     = { szOID_PKIX_KP_SERVER_AUTH,
  544.                                szOID_SERVER_GATED_CRYPTO,
  545.                                szOID_SGC_NETSCAPE };
  546.  
  547.     DWORD cUsages          = sizeof(rgszUsages) / sizeof(LPSTR);
  548.  
  549.     PWSTR   pwszServerName = NULL;
  550.  
  551.  
  552.     if(pServerCert == NULL)
  553.     { Status = SEC_E_WRONG_PRINCIPAL; goto cleanup; }
  554.  
  555.     // Convert server name to unicode.
  556.     if(pszServerName == NULL || strlen(pszServerName) == 0)
  557.     { Status = SEC_E_WRONG_PRINCIPAL; goto cleanup; }
  558.  
  559.     cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, NULL, 0);
  560.     pwszServerName = LocalAlloc(LMEM_FIXED, cchServerName * sizeof(WCHAR));
  561.     if(pwszServerName == NULL)
  562.     { Status = SEC_E_INSUFFICIENT_MEMORY; goto cleanup; }
  563.  
  564.     cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, pwszServerName, cchServerName);
  565.     if(cchServerName == 0)
  566.     { Status = SEC_E_WRONG_PRINCIPAL; goto cleanup; }
  567.  
  568.  
  569.     // Build certificate chain.
  570.     ZeroMemory(&ChainPara, sizeof(ChainPara));
  571.     ChainPara.cbSize = sizeof(ChainPara);
  572.     ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
  573.     ChainPara.RequestedUsage.Usage.cUsageIdentifier     = cUsages;
  574.     ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
  575.  
  576.     if( !CertGetCertificateChain( NULL,
  577.                                                                     pServerCert,
  578.                                                                     NULL,
  579.                                                                     pServerCert->hCertStore,
  580.                                                                     &ChainPara,
  581.                                                                     0,
  582.                                                                     NULL,
  583.                                                                     &pChainContext ) )
  584.     {
  585.         Status = GetLastError();
  586.         printf("Error 0x%x returned by CertGetCertificateChain!\n", Status);
  587.         goto cleanup;
  588.     }
  589.  
  590.  
  591.     // Validate certificate chain.
  592.     ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
  593.     polHttps.cbStruct           = sizeof(HTTPSPolicyCallbackData);
  594.     polHttps.dwAuthType         = AUTHTYPE_SERVER;
  595.     polHttps.fdwChecks          = dwCertFlags;
  596.     polHttps.pwszServerName     = pwszServerName;
  597.  
  598.     memset(&PolicyPara, 0, sizeof(PolicyPara));
  599.     PolicyPara.cbSize            = sizeof(PolicyPara);
  600.     PolicyPara.pvExtraPolicyPara = &polHttps;
  601.  
  602.     memset(&PolicyStatus, 0, sizeof(PolicyStatus));
  603.     PolicyStatus.cbSize = sizeof(PolicyStatus);
  604.  
  605.     if( !CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
  606.                                            pChainContext,
  607.                                            &PolicyPara,
  608.                                            &PolicyStatus ) )
  609.     {
  610.       Status = GetLastError();
  611.       printf("Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", Status);
  612.       if (!fNoCertErr) goto cleanup;
  613.     }
  614.  
  615.     if(PolicyStatus.dwError)
  616.     {
  617.       Status = PolicyStatus.dwError;
  618.       DisplayWinVerifyTrustError(Status);
  619.       if (!fNoCertErr) goto cleanup;
  620.     }
  621.  
  622.     Status = SEC_E_OK;
  623.  
  624.  
  625. cleanup:
  626.     if(pChainContext)  CertFreeCertificateChain(pChainContext);
  627.     if(pwszServerName) LocalFree(pwszServerName);
  628.  
  629.     return Status;
  630. }
  631.  
  632.  
  633. /*****************************************************************************/
  634. static SECURITY_STATUS CreateCredentials( LPSTR pszUser, PCredHandle phCreds )  
  635. { //                                                in                     out
  636.     TimeStamp        tsExpiry;
  637.     SECURITY_STATUS  Status;
  638.     DWORD            cSupportedAlgs = 0;
  639.     ALG_ID           rgbSupportedAlgs[16];
  640.     PCCERT_CONTEXT   pCertContext = NULL;
  641.  
  642.  
  643.     // Open the "MY" certificate store, where IE stores client certificates.
  644.         // Windows maintains 4 stores -- MY, CA, ROOT, SPC.
  645.     if(hMyCertStore == NULL)
  646.     {
  647.         hMyCertStore = CertOpenSystemStore(0, "MY");
  648.         if(!hMyCertStore)
  649.         {
  650.             printf( "**** Error 0x%x returned by CertOpenSystemStore\n", GetLastError() );
  651.             return SEC_E_NO_CREDENTIALS;
  652.         }
  653.     }
  654.  
  655.  
  656.     // If a user name is specified, then attempt to find a client
  657.     // certificate. Otherwise, just create a NULL credential.
  658.     if(NULL != pszUser)
  659.     {
  660.         // Find client certificate. Note that this sample just searches for a
  661.         // certificate that contains the user name somewhere in the subject name.
  662.         // A real application should be a bit less casual.
  663.         pCertContext = CertFindCertificateInStore( hMyCertStore,                     // hCertStore
  664.                                                    X509_ASN_ENCODING,             // dwCertEncodingType
  665.                                                    0,                                             // dwFindFlags
  666.                                                    CERT_FIND_SUBJECT_STR_A,// dwFindType
  667.                                                    pszUser,                         // *pvFindPara
  668.                                                    NULL );                                 // pPrevCertContext
  669.  
  670.  
  671.         if(pCertContext == NULL)
  672.         {
  673.             printf("**** Error 0x%x returned by CertFindCertificateInStore\n", GetLastError());
  674.                         if( GetLastError() == CRYPT_E_NOT_FOUND ) printf("CRYPT_E_NOT_FOUND - property doesn't exist\n");
  675.                         return SEC_E_NO_CREDENTIALS;
  676.         }
  677.     }
  678.  
  679.  
  680.     // Build Schannel credential structure. Currently, this sample only
  681.     // specifies the protocol to be used (and optionally the certificate,
  682.     // of course). Real applications may wish to specify other parameters as well.
  683.     ZeroMemory( &SchannelCred, sizeof(SchannelCred) );
  684.  
  685.     SchannelCred.dwVersion  = SCHANNEL_CRED_VERSION;
  686.     if(pCertContext)
  687.     {
  688.         SchannelCred.cCreds     = 1;
  689.         SchannelCred.paCred     = &pCertContext;
  690.     }
  691.  
  692.     SchannelCred.grbitEnabledProtocols = dwProtocol;
  693.  
  694.     if(aiKeyExch) rgbSupportedAlgs[cSupportedAlgs++] = aiKeyExch;
  695.  
  696.     if(cSupportedAlgs)
  697.     {
  698.         SchannelCred.cSupportedAlgs    = cSupportedAlgs;
  699.         SchannelCred.palgSupportedAlgs = rgbSupportedAlgs;
  700.     }
  701.  
  702.     SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
  703.  
  704.     // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
  705.     // this sample verifies the server certificate manually.
  706.     // Applications that expect to run on WinNT, Win9x, or WinME
  707.     // should specify this flag and also manually verify the server
  708.     // certificate. Applications running on newer versions of Windows can
  709.     // leave off this flag, in which case the InitializeSecurityContext
  710.     // function will validate the server certificate automatically.
  711.     SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
  712.  
  713.  
  714.     // Create an SSPI credential.
  715.     Status = g_pSSPI->AcquireCredentialsHandleA( NULL,                 // Name of principal    
  716.                                                                                                  UNISP_NAME_A,         // Name of package
  717.                                                                                                  SECPKG_CRED_OUTBOUND, // Flags indicating use
  718.                                                                                                  NULL,                 // Pointer to logon ID
  719.                                                                                                  &SchannelCred,        // Package specific data
  720.                                                                                                  NULL,                 // Pointer to GetKey() func
  721.                                                                                                  NULL,                 // Value to pass to GetKey()
  722.                                                                                                  phCreds,              // (out) Cred Handle
  723.                                                                                                  &tsExpiry );          // (out) Lifetime (optional)
  724.  
  725.     if(Status != SEC_E_OK) printf("**** Error 0x%x returned by AcquireCredentialsHandle\n", Status);
  726.  
  727.     // cleanup: Free the certificate context. Schannel has already made its own copy.
  728.     if(pCertContext) CertFreeCertificateContext(pCertContext);
  729.  
  730.     return Status;
  731. }
  732.  
  733. static int skread(SOCKET sock, char *pBuff, int *nLen)
  734. {
  735.   int nRes;
  736.  
  737.   memset(pBuff, '\0', *nLen);
  738.   Sleep(150);
  739.   nRes = recv(sock, pBuff, *nLen, 0);
  740.   if (SOCKET_ERROR == nRes)
  741.   {
  742.     printf("**** Error %d receiving message from proxy\n", WSAGetLastError());
  743.     DisplayWinSockError( WSAGetLastError() );
  744.     return WSAGetLastError();
  745.   }
  746.   *nLen = nRes;
  747.   return(0);
  748. }
  749.  
  750. static int skwrite(SOCKET sock, char *pBuff, int *nLen)
  751. {
  752.   int nRes;
  753.  
  754.   nRes = send(sock, pBuff, *nLen, 0);
  755.   if (SOCKET_ERROR == nRes)
  756.   {
  757.     printf("**** Error %d receiving message from proxy\n", WSAGetLastError());
  758.     DisplayWinSockError( WSAGetLastError() );
  759.     return WSAGetLastError();
  760.   }
  761.   *nLen = nRes;
  762.   return(0);
  763. }
  764.  
  765.  
  766. /*****************************************************************************/
  767. static INT ConnectToServer( LPSTR pszServerName, INT iPortNumber, SOCKET * pSocket )      
  768. { //                                    in                in                 out
  769.     SOCKET Socket;
  770.     struct sockaddr_in sin;
  771.     struct hostent *hp;
  772.  
  773.  
  774.     Socket = socket(PF_INET, SOCK_STREAM, 0);
  775.     if(Socket == INVALID_SOCKET)
  776.     {
  777.         printf("**** Error %d creating socket\n", WSAGetLastError());
  778.               DisplayWinSockError( WSAGetLastError() );
  779.         return WSAGetLastError();
  780.     }
  781.  
  782.  
  783.     if(fUseProxy)
  784.     {
  785.         sin.sin_family = AF_INET;
  786.         sin.sin_port = ntohs((u_short)iProxyPort);
  787.         if((hp = gethostbyname(pszProxyServer)) == NULL)
  788.         {
  789.           printf("**** Error %d returned by gethostbyname using Proxy\n", WSAGetLastError());
  790.                     DisplayWinSockError( WSAGetLastError() );
  791.           return WSAGetLastError();
  792.         }
  793.         else
  794.           memcpy(&sin.sin_addr, hp->h_addr, 4);
  795.     }
  796.  
  797.     else // No proxy used
  798.     {
  799.         sin.sin_family = AF_INET;
  800.         sin.sin_port = htons((u_short)iPortNumber);
  801.         if((hp = gethostbyname(pszServerName)) == NULL)
  802.         {
  803.           printf("**** Error returned by gethostbyname\n");
  804.                     DisplayWinSockError( WSAGetLastError() );
  805.           return WSAGetLastError();
  806.         }
  807.         else
  808.           memcpy(&sin.sin_addr, hp->h_addr, 4);
  809.     }
  810.  
  811.  
  812.     if(connect(Socket, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
  813.     {
  814.         printf( "**** Error %d connecting to \"%s\" (%s)\n",  WSAGetLastError(), pszServerName,  inet_ntoa(sin.sin_addr) );
  815.         closesocket(Socket);
  816.                 DisplayWinSockError( WSAGetLastError() );
  817.         return WSAGetLastError();
  818.     }
  819.  
  820.  
  821.     if(fUseProxy)
  822.     {
  823.         BYTE  pbMessage[200];
  824.         DWORD cbMessage;
  825.  
  826.         // Build message for proxy server
  827.         strcpy(pbMessage, "CONNECT ");
  828.         strcat(pbMessage, pszServerName);
  829.         strcat(pbMessage, ":");
  830.          _itoa(iPortNumber, pbMessage + strlen(pbMessage), 10);
  831.         strcat(pbMessage, " HTTP/1.0\r\nUser-Agent: webclient\r\n\r\n");
  832.         cbMessage = (DWORD)strlen(pbMessage);
  833.  
  834.         // Send message to proxy server
  835.         if(send(Socket, pbMessage, cbMessage, 0) == SOCKET_ERROR)
  836.         {
  837.           printf("**** Error %d sending message to proxy!\n", WSAGetLastError());
  838.                     DisplayWinSockError( WSAGetLastError() );
  839.                     return WSAGetLastError();
  840.         }
  841.  
  842.         // Receive message from proxy server
  843.         cbMessage = recv(Socket, pbMessage, 200, 0);
  844.         if(cbMessage == SOCKET_ERROR)
  845.         {
  846.           printf("**** Error %d receiving message from proxy\n", WSAGetLastError());
  847.                     DisplayWinSockError( WSAGetLastError() );
  848.                     return WSAGetLastError();
  849.         }
  850.         // this sample is limited but in normal use it
  851.         // should continue to receive until CR LF CR LF is received
  852.     }
  853.     *pSocket = Socket;
  854.  
  855.     if (fDoTLS) {
  856.       BYTE  pbMessage[2048];
  857.       DWORD cbMessage;
  858.       int   rc, size;
  859.      
  860.       size = 2047;
  861.       if (0 != (rc = skread(Socket, pbMessage, &size))) return(rc);
  862.       printf("%s", pbMessage);
  863.      
  864.       strcpy(pbMessage, "EHLO example.com\r\n");
  865.       printf("%s", pbMessage);
  866.       size = strlen(pbMessage);
  867.       if (0 != (rc = skwrite(Socket, pbMessage, &size))) return(rc);
  868.       size = 2047;
  869.       if (0 != (rc = skread(Socket, pbMessage, &size))) return(rc);
  870.       printf("%s", pbMessage);
  871.      
  872.       strcpy(pbMessage, "STARTTLS\r\n");
  873.       printf("%s", pbMessage);
  874.       size = strlen(pbMessage);
  875.       if (0 != (rc = skwrite(Socket, pbMessage, &size))) return(rc);
  876.       size = 2047;
  877.       if (0 != (rc = skread(Socket, pbMessage, &size))) return(rc);
  878.       printf("%s", pbMessage);
  879.     }
  880.    
  881.    
  882.   return SEC_E_OK;
  883. }
  884.  
  885. /*****************************************************************************/
  886. static LONG DisconnectFromServer( SOCKET Socket, PCredHandle phCreds, CtxtHandle * phContext )
  887. {
  888.         PBYTE                    pbMessage;
  889.     DWORD                    dwType, dwSSPIFlags, dwSSPIOutFlags, cbMessage, cbData, Status;
  890.     SecBufferDesc OutBuffer;
  891.     SecBuffer     OutBuffers[1];
  892.     TimeStamp     tsExpiry;
  893.  
  894.  
  895.     dwType = SCHANNEL_SHUTDOWN; // Notify schannel that we are about to close the connection.
  896.  
  897.     OutBuffers[0].pvBuffer   = &dwType;
  898.     OutBuffers[0].BufferType = SECBUFFER_TOKEN;
  899.     OutBuffers[0].cbBuffer   = sizeof(dwType);
  900.  
  901.     OutBuffer.cBuffers  = 1;
  902.     OutBuffer.pBuffers  = OutBuffers;
  903.     OutBuffer.ulVersion = SECBUFFER_VERSION;
  904.  
  905.     Status = g_pSSPI->ApplyControlToken(phContext, &OutBuffer);
  906.         if(FAILED(Status)) { printf("**** Error 0x%x returned by ApplyControlToken\n", Status); goto cleanup; }
  907.  
  908.  
  909.     // Build an SSL close notify message.
  910.     dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
  911.                   ISC_REQ_REPLAY_DETECT     |
  912.                   ISC_REQ_CONFIDENTIALITY   |
  913.                   ISC_RET_EXTENDED_ERROR    |
  914.                   ISC_REQ_ALLOCATE_MEMORY   |
  915.                   ISC_REQ_STREAM;
  916.  
  917.     OutBuffers[0].pvBuffer   = NULL;
  918.     OutBuffers[0].BufferType = SECBUFFER_TOKEN;
  919.     OutBuffers[0].cbBuffer   = 0;
  920.  
  921.     OutBuffer.cBuffers  = 1;
  922.     OutBuffer.pBuffers  = OutBuffers;
  923.     OutBuffer.ulVersion = SECBUFFER_VERSION;
  924.  
  925.     Status = g_pSSPI->InitializeSecurityContextA( phCreds,
  926.                                                                                                     phContext,
  927.                                                                                                     NULL,
  928.                                                                                                     dwSSPIFlags,
  929.                                                                                                     0,
  930.                                                                                                     SECURITY_NATIVE_DREP,
  931.                                                                                                     NULL,
  932.                                                                                                     0,
  933.                                                                                                     phContext,
  934.                                                                                                     &OutBuffer,
  935.                                                                                                     &dwSSPIOutFlags,
  936.                                                                                                     &tsExpiry );
  937.  
  938.     if(FAILED(Status)) { printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status); goto cleanup; }
  939.  
  940.     pbMessage = OutBuffers[0].pvBuffer;
  941.     cbMessage = OutBuffers[0].cbBuffer;
  942.  
  943.  
  944.     // Send the close notify message to the server.
  945.     if(pbMessage != NULL && cbMessage != 0)
  946.     {
  947.         cbData = send(Socket, pbMessage, cbMessage, 0);
  948.         if(cbData == SOCKET_ERROR || cbData == 0)
  949.         {
  950.             Status = WSAGetLastError();
  951.                         printf("**** Error %d sending close notify\n", Status);
  952.                       DisplayWinSockError( WSAGetLastError() );
  953.             goto cleanup;
  954.         }
  955.         printf("Sending Close Notify\n");
  956.         if (fVerbose) printf("%d bytes of handshake data sent\n", cbData);
  957.         if (fHexDump) { PrintHexDump(cbData, pbMessage); printf("\n"); }
  958.         g_pSSPI->FreeContextBuffer(pbMessage); // Free output buffer.
  959.     }
  960.    
  961.  
  962. cleanup:
  963.     g_pSSPI->DeleteSecurityContext(phContext); // Free the security context.
  964.     closesocket(Socket); // Close the socket.
  965.  
  966.     return Status;
  967. }
  968.  
  969.  
  970.  
  971. /*****************************************************************************/
  972. static void GetNewClientCredentials( CredHandle *phCreds, CtxtHandle *phContext )
  973. {
  974.  
  975.     CredHandle                                            hCreds;
  976.     SecPkgContext_IssuerListInfoEx    IssuerListInfo;
  977.     PCCERT_CHAIN_CONTEXT                        pChainContext;
  978.     CERT_CHAIN_FIND_BY_ISSUER_PARA    FindByIssuerPara;
  979.     PCCERT_CONTEXT                                    pCertContext;
  980.     TimeStamp                                                tsExpiry;
  981.     SECURITY_STATUS                                    Status;
  982.  
  983.  
  984.     // Read list of trusted issuers from schannel.
  985.     Status = g_pSSPI->QueryContextAttributesA( phContext, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo );
  986.         if(Status != SEC_E_OK) { printf("Error 0x%x querying issuer list info\n", Status); return; }
  987.  
  988.     // Enumerate the client certificates.
  989.     ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
  990.  
  991.     FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
  992.     FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
  993.     FindByIssuerPara.dwKeySpec = 0;
  994.     FindByIssuerPara.cIssuer   = IssuerListInfo.cIssuers;
  995.     FindByIssuerPara.rgIssuer  = IssuerListInfo.aIssuers;
  996.  
  997.     pChainContext = NULL;
  998.  
  999.     while(TRUE)
  1000.     {   // Find a certificate chain.
  1001.         pChainContext = CertFindChainInStore( hMyCertStore,
  1002.                                               X509_ASN_ENCODING,
  1003.                                               0,
  1004.                                               CERT_CHAIN_FIND_BY_ISSUER,
  1005.                                               &FindByIssuerPara,
  1006.                                               pChainContext );
  1007.                 if(pChainContext == NULL) { printf("Error 0x%x finding cert chain\n", GetLastError()); break; }
  1008.  
  1009.                 printf("\ncertificate chain found\n");
  1010.  
  1011.         // Get pointer to leaf certificate context.
  1012.         pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
  1013.  
  1014.         // Create schannel credential.
  1015.         SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
  1016.         SchannelCred.cCreds = 1;
  1017.         SchannelCred.paCred = &pCertContext;
  1018.  
  1019.         Status = g_pSSPI->AcquireCredentialsHandleA(  NULL,                   // Name of principal
  1020.                                                                                                             UNISP_NAME_A,           // Name of package
  1021.                                                                                                             SECPKG_CRED_OUTBOUND,   // Flags indicating use
  1022.                                                                                                             NULL,                   // Pointer to logon ID
  1023.                                                                                                             &SchannelCred,          // Package specific data
  1024.                                                                                                             NULL,                   // Pointer to GetKey() func
  1025.                                                                                                             NULL,                   // Value to pass to GetKey()
  1026.                                                                                                             &hCreds,                // (out) Cred Handle
  1027.                                                                                                             &tsExpiry );            // (out) Lifetime (optional)
  1028.  
  1029.                 if(Status != SEC_E_OK) {printf("**** Error 0x%x returned by AcquireCredentialsHandle\n", Status); continue;}
  1030.  
  1031.                 printf("\nnew schannel credential created\n");
  1032.  
  1033.         g_pSSPI->FreeCredentialsHandle(phCreds); // Destroy the old credentials.
  1034.  
  1035.         *phCreds = hCreds;
  1036.  
  1037.     }
  1038. }
  1039.    
  1040. /*****************************************************************************/
  1041. static SECURITY_STATUS ClientHandshakeLoop( SOCKET          Socket,         // in
  1042.                                                                                         PCredHandle     phCreds,        // in
  1043.                                                                                         CtxtHandle *    phContext,      // in, out
  1044.                                                                                         BOOL            fDoInitialRead, // in
  1045.                                                                                         SecBuffer *     pExtraData )    // out
  1046.  
  1047. {
  1048.  
  1049.       SecBufferDesc   OutBuffer, InBuffer;
  1050.     SecBuffer       InBuffers[2], OutBuffers[1];
  1051.     DWORD           dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
  1052.     TimeStamp       tsExpiry;
  1053.     SECURITY_STATUS scRet;
  1054.     PUCHAR          IoBuffer;
  1055.     BOOL            fDoRead;
  1056.  
  1057.  
  1058.     dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   | ISC_REQ_REPLAY_DETECT     | ISC_REQ_CONFIDENTIALITY   |
  1059.                   ISC_RET_EXTENDED_ERROR    | ISC_REQ_ALLOCATE_MEMORY   | ISC_REQ_STREAM;
  1060.  
  1061.  
  1062.     // Allocate data buffer.
  1063.     IoBuffer = LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
  1064.     if(IoBuffer == NULL) { printf("**** Out of memory (1)\n"); return SEC_E_INTERNAL_ERROR; }
  1065.     cbIoBuffer = 0;
  1066.     fDoRead = fDoInitialRead;
  1067.  
  1068.  
  1069.  
  1070.     // Loop until the handshake is finished or an error occurs.
  1071.     scRet = SEC_I_CONTINUE_NEEDED;
  1072.  
  1073.     while( scRet == SEC_I_CONTINUE_NEEDED        ||
  1074.            scRet == SEC_E_INCOMPLETE_MESSAGE     ||
  1075.            scRet == SEC_I_INCOMPLETE_CREDENTIALS )
  1076.    {
  1077.         if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) // Read data from server.
  1078.         {
  1079.             if(fDoRead)
  1080.             {
  1081.                 cbData = recv(Socket, IoBuffer + cbIoBuffer, IO_BUFFER_SIZE - cbIoBuffer, 0 );
  1082.                 if(cbData == SOCKET_ERROR)
  1083.                 {
  1084.                     printf("**** Error %d reading data from server\n", WSAGetLastError());
  1085.                     scRet = SEC_E_INTERNAL_ERROR;
  1086.                     break;
  1087.                 }
  1088.                 else if(cbData == 0)
  1089.                 {
  1090.                     printf("**** Server unexpectedly disconnected\n");
  1091.                     scRet = SEC_E_INTERNAL_ERROR;
  1092.                     break;
  1093.                 }
  1094.                 if (fVerbose) printf("%d bytes of handshake data received\n", cbData);
  1095.                 if (fHexDump) { PrintHexDump(cbData, IoBuffer + cbIoBuffer); printf("\n"); }
  1096.                 cbIoBuffer += cbData;
  1097.             }
  1098.             else
  1099.               fDoRead = TRUE;
  1100.         }
  1101.  
  1102.  
  1103.  
  1104.         // Set up the input buffers. Buffer 0 is used to pass in data
  1105.         // received from the server. Schannel will consume some or all
  1106.         // of this. Leftover data (if any) will be placed in buffer 1 and
  1107.         // given a buffer type of SECBUFFER_EXTRA.
  1108.         InBuffers[0].pvBuffer   = IoBuffer;
  1109.         InBuffers[0].cbBuffer   = cbIoBuffer;
  1110.         InBuffers[0].BufferType = SECBUFFER_TOKEN;
  1111.  
  1112.         InBuffers[1].pvBuffer   = NULL;
  1113.         InBuffers[1].cbBuffer   = 0;
  1114.         InBuffers[1].BufferType = SECBUFFER_EMPTY;
  1115.  
  1116.         InBuffer.cBuffers       = 2;
  1117.         InBuffer.pBuffers       = InBuffers;
  1118.         InBuffer.ulVersion      = SECBUFFER_VERSION;
  1119.  
  1120.  
  1121.         // Set up the output buffers. These are initialized to NULL
  1122.         // so as to make it less likely we'll attempt to free random
  1123.         // garbage later.
  1124.         OutBuffers[0].pvBuffer  = NULL;
  1125.         OutBuffers[0].BufferType= SECBUFFER_TOKEN;
  1126.         OutBuffers[0].cbBuffer  = 0;
  1127.  
  1128.         OutBuffer.cBuffers      = 1;
  1129.         OutBuffer.pBuffers      = OutBuffers;
  1130.         OutBuffer.ulVersion     = SECBUFFER_VERSION;
  1131.  
  1132.  
  1133.         // Call InitializeSecurityContext.
  1134.         scRet = g_pSSPI->InitializeSecurityContextA(  phCreds,
  1135.                                                                                                             phContext,
  1136.                                                                                                             NULL,
  1137.                                                                                                             dwSSPIFlags,
  1138.                                                                                                             0,
  1139.                                                                                                             SECURITY_NATIVE_DREP,
  1140.                                                                                                             &InBuffer,
  1141.                                                                                                             0,
  1142.                                                                                                             NULL,
  1143.                                                                                                             &OutBuffer,
  1144.                                                                                                             &dwSSPIOutFlags,
  1145.                                                                                                             &tsExpiry );
  1146.  
  1147.  
  1148.         // If InitializeSecurityContext was successful (or if the error was
  1149.         // one of the special extended ones), send the contends of the output
  1150.         // buffer to the server.
  1151.         if(scRet == SEC_E_OK                ||
  1152.            scRet == SEC_I_CONTINUE_NEEDED   ||
  1153.            FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
  1154.         {
  1155.             if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
  1156.             {
  1157.                 cbData = send(Socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0 );
  1158.                 if(cbData == SOCKET_ERROR || cbData == 0)
  1159.                 {
  1160.                     printf( "**** Error %d sending data to server (2)\n",  WSAGetLastError() );
  1161.                                         DisplayWinSockError( WSAGetLastError() );
  1162.                     g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
  1163.                     g_pSSPI->DeleteSecurityContext(phContext);
  1164.                     return SEC_E_INTERNAL_ERROR;
  1165.                 }
  1166.                 if (fVerbose) printf("%d bytes of handshake data sent\n", cbData);
  1167.                 if (fHexDump) { PrintHexDump(cbData, OutBuffers[0].pvBuffer); printf("\n"); }
  1168.  
  1169.                 // Free output buffer.
  1170.                 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
  1171.                 OutBuffers[0].pvBuffer = NULL;
  1172.             }
  1173.         }
  1174.  
  1175.  
  1176.  
  1177.         // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
  1178.         // then we need to read more data from the server and try again.
  1179.         if(scRet == SEC_E_INCOMPLETE_MESSAGE) continue;
  1180.  
  1181.  
  1182.         // If InitializeSecurityContext returned SEC_E_OK, then the
  1183.         // handshake completed successfully.
  1184.         if(scRet == SEC_E_OK)
  1185.         {
  1186.             // If the "extra" buffer contains data, this is encrypted application
  1187.             // protocol layer stuff. It needs to be saved. The application layer
  1188.             // will later decrypt it with DecryptMessage.
  1189.             // printf("Handshake was successful\n");
  1190.  
  1191.             if(InBuffers[1].BufferType == SECBUFFER_EXTRA)
  1192.             {
  1193.                 pExtraData->pvBuffer = LocalAlloc( LMEM_FIXED, InBuffers[1].cbBuffer );
  1194.                 if(pExtraData->pvBuffer == NULL) { printf("**** Out of memory (2)\n"); return SEC_E_INTERNAL_ERROR; }
  1195.  
  1196.                 MoveMemory( pExtraData->pvBuffer,
  1197.                             IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
  1198.                             InBuffers[1].cbBuffer );
  1199.  
  1200.                 pExtraData->cbBuffer   = InBuffers[1].cbBuffer;
  1201.                 pExtraData->BufferType = SECBUFFER_TOKEN;
  1202.  
  1203.                 if (fVerbose) printf( "%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer );
  1204.             }
  1205.             else
  1206.             {
  1207.                 pExtraData->pvBuffer   = NULL;
  1208.                 pExtraData->cbBuffer   = 0;
  1209.                 pExtraData->BufferType = SECBUFFER_EMPTY;
  1210.             }
  1211.             break; // Bail out to quit
  1212.         }
  1213.  
  1214.  
  1215.  
  1216.         // Check for fatal error.
  1217.         if(FAILED(scRet)) { printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet); break; }
  1218.  
  1219.         // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
  1220.         // then the server just requested client authentication.
  1221.         if(scRet == SEC_I_INCOMPLETE_CREDENTIALS)
  1222.         {
  1223.             // Busted. The server has requested client authentication and
  1224.             // the credential we supplied didn't contain a client certificate.
  1225.             // This function will read the list of trusted certificate
  1226.             // authorities ("issuers") that was received from the server
  1227.             // and attempt to find a suitable client certificate that
  1228.             // was issued by one of these. If this function is successful,
  1229.             // then we will connect using the new certificate. Otherwise,
  1230.             // we will attempt to connect anonymously (using our current credentials).
  1231.             GetNewClientCredentials(phCreds, phContext);
  1232.  
  1233.             // Go around again.
  1234.             fDoRead = FALSE;
  1235.             scRet = SEC_I_CONTINUE_NEEDED;
  1236.             continue;
  1237.         }
  1238.  
  1239.         // Copy any leftover data from the "extra" buffer, and go around again.
  1240.         if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
  1241.         {
  1242.             MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
  1243.             cbIoBuffer = InBuffers[1].cbBuffer;
  1244.         }
  1245.         else
  1246.           cbIoBuffer = 0;
  1247.     }
  1248.  
  1249.     // Delete the security context in the case of a fatal error.
  1250.     if(FAILED(scRet)) g_pSSPI->DeleteSecurityContext(phContext);
  1251.     LocalFree(IoBuffer);
  1252.  
  1253.     return scRet;
  1254. }
  1255.  
  1256.  
  1257. /*****************************************************************************/
  1258. static SECURITY_STATUS PerformClientHandshake( SOCKET          Socket,        // in
  1259.                                                                                              PCredHandle     phCreds,       // in
  1260.                                                                                              LPSTR           pszServerName, // in
  1261.                                                                                              CtxtHandle *    phContext,     // out
  1262.                                                                                              SecBuffer *     pExtraData )   // out
  1263. {
  1264.  
  1265.     SecBufferDesc   OutBuffer;
  1266.     SecBuffer       OutBuffers[1];
  1267.     DWORD           dwSSPIFlags, dwSSPIOutFlags, cbData;
  1268.     TimeStamp       tsExpiry;
  1269.     SECURITY_STATUS scRet;
  1270.  
  1271.  
  1272.     dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   | ISC_REQ_REPLAY_DETECT     | ISC_REQ_CONFIDENTIALITY   |
  1273.                   ISC_RET_EXTENDED_ERROR    | ISC_REQ_ALLOCATE_MEMORY   | ISC_REQ_STREAM;
  1274.  
  1275.  
  1276.     //  Initiate a ClientHello message and generate a token.
  1277.     OutBuffers[0].pvBuffer   = NULL;
  1278.     OutBuffers[0].BufferType = SECBUFFER_TOKEN;
  1279.     OutBuffers[0].cbBuffer   = 0;
  1280.  
  1281.     OutBuffer.cBuffers  = 1;
  1282.     OutBuffer.pBuffers  = OutBuffers;
  1283.     OutBuffer.ulVersion = SECBUFFER_VERSION;
  1284.  
  1285.     scRet = g_pSSPI->InitializeSecurityContextA(  phCreds,
  1286.                                                                                                     NULL,
  1287.                                                                                                     pszServerName,
  1288.                                                                                                     dwSSPIFlags,
  1289.                                                                                                     0,
  1290.                                                                                                     SECURITY_NATIVE_DREP,
  1291.                                                                                                     NULL,
  1292.                                                                                                     0,
  1293.                                                                                                     phContext,
  1294.                                                                                                     &OutBuffer,
  1295.                                                                                                     &dwSSPIOutFlags,
  1296.                                                                                                     &tsExpiry );
  1297.  
  1298.     if(scRet != SEC_I_CONTINUE_NEEDED) { printf("**** Error %d returned by InitializeSecurityContext (1)\n", scRet); return scRet; }
  1299.  
  1300.     // Send response to server if there is one.
  1301.     if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
  1302.     {
  1303.         cbData = send( Socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0 );
  1304.         if( cbData == SOCKET_ERROR || cbData == 0 )
  1305.         {
  1306.             printf("**** Error %d sending data to server (1)\n", WSAGetLastError());
  1307.             g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
  1308.             g_pSSPI->DeleteSecurityContext(phContext);
  1309.             return SEC_E_INTERNAL_ERROR;
  1310.         }
  1311.         if (fVerbose) printf("%d bytes of handshake data sent\n", cbData);
  1312.         if (fHexDump) { PrintHexDump(cbData, OutBuffers[0].pvBuffer); printf("\n"); }
  1313.         g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); // Free output buffer.
  1314.         OutBuffers[0].pvBuffer = NULL;
  1315.     }
  1316.  
  1317.     return ClientHandshakeLoop(Socket, phCreds, phContext, TRUE, pExtraData);
  1318. }
  1319.  
  1320.  
  1321.  
  1322. /*****************************************************************************/
  1323. static DWORD EncryptSend( SOCKET Socket, CtxtHandle * phContext, PBYTE pbIoBuffer, SecPkgContext_StreamSizes Sizes )
  1324. // http://msdn.microsoft.com/en-us/library/aa375378(VS.85).aspx
  1325. // The encrypted message is encrypted in place, overwriting the original contents of its buffer.
  1326. {
  1327.     SECURITY_STATUS    scRet;            // unsigned long cbBuffer;    // Size of the buffer, in bytes
  1328.     SecBufferDesc        Message;        // unsigned long BufferType;  // Type of the buffer (below)
  1329.     SecBuffer                Buffers[4];    // void    SEC_FAR * pvBuffer;   // Pointer to the buffer
  1330.     DWORD                        cbMessage, cbData;
  1331.     PBYTE                        pbMessage;
  1332.  
  1333.  
  1334.     pbMessage = pbIoBuffer + Sizes.cbHeader; // Offset by "header size"
  1335.     cbMessage = (DWORD)strlen(pbMessage);
  1336.     if (fVerbose) printf("Sending %d bytes of plaintext: ", cbMessage);
  1337.     PrintText(cbMessage, pbMessage);
  1338.     if (fHexDump) { PrintHexDump(cbMessage, pbMessage); printf("\n"); }
  1339.  
  1340.  
  1341.         // Encrypt the HTTP request.
  1342.     Buffers[0].pvBuffer     = pbIoBuffer;                                // Pointer to buffer 1
  1343.     Buffers[0].cbBuffer     = Sizes.cbHeader;                        // length of header
  1344.     Buffers[0].BufferType   = SECBUFFER_STREAM_HEADER;    // Type of the buffer
  1345.  
  1346.     Buffers[1].pvBuffer     = pbMessage;                                // Pointer to buffer 2
  1347.     Buffers[1].cbBuffer     = cbMessage;                                // length of the message
  1348.     Buffers[1].BufferType   = SECBUFFER_DATA;                        // Type of the buffer
  1349.                                                                                            
  1350.     Buffers[2].pvBuffer     = pbMessage + cbMessage;        // Pointer to buffer 3
  1351.     Buffers[2].cbBuffer     = Sizes.cbTrailer;                    // length of the trailor
  1352.     Buffers[2].BufferType   = SECBUFFER_STREAM_TRAILER;    // Type of the buffer
  1353.  
  1354.         Buffers[3].pvBuffer     = SECBUFFER_EMPTY;                    // Pointer to buffer 4
  1355.     Buffers[3].cbBuffer     = SECBUFFER_EMPTY;                    // length of buffer 4
  1356.     Buffers[3].BufferType   = SECBUFFER_EMPTY;                    // Type of the buffer 4
  1357.  
  1358.  
  1359.     Message.ulVersion       = SECBUFFER_VERSION;    // Version number
  1360.     Message.cBuffers        = 4;                                    // Number of buffers - must contain four SecBuffer structures.
  1361.     Message.pBuffers        = Buffers;                        // Pointer to array of buffers
  1362.     scRet = g_pSSPI->EncryptMessage(phContext, 0, &Message, 0); // must contain four SecBuffer structures.
  1363.     if(FAILED(scRet)) { printf("**** Error 0x%x returned by EncryptMessage\n", scRet); return scRet; }
  1364.  
  1365.  
  1366.     // Send the encrypted data to the server.                                            len                                                                         flags
  1367.     cbData = send( Socket, pbIoBuffer,    Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer,    0 );
  1368.  
  1369.     if (fVerbose) printf("%d bytes of encrypted data sent\n", cbData);
  1370.     if (fHexDump) { PrintHexDump(cbData, pbIoBuffer); printf("\n"); }
  1371.  
  1372.     return cbData; // send( Socket, pbIoBuffer,    Sizes.cbHeader + strlen(pbMessage) + Sizes.cbTrailer,  0 );
  1373.  
  1374. }
  1375.  
  1376.  
  1377. /*****************************************************************************/
  1378. static SECURITY_STATUS ReadDecrypt( SOCKET Socket, PCredHandle phCreds, CtxtHandle * phContext, PBYTE pbIoBuffer, DWORD    cbIoBufferLength )
  1379.  
  1380. // calls recv() - blocking socket read
  1381. // http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx
  1382.  
  1383. // The encrypted message is decrypted in place, overwriting the original contents of its buffer.
  1384. // http://msdn.microsoft.com/en-us/library/aa375211(VS.85).aspx
  1385.  
  1386. {
  1387.   SecBuffer                ExtraBuffer;
  1388.   SecBuffer                *pDataBuffer, *pExtraBuffer;
  1389.  
  1390.   SECURITY_STATUS    scRet;            // unsigned long cbBuffer;    // Size of the buffer, in bytes
  1391.   SecBufferDesc        Message;        // unsigned long BufferType;  // Type of the buffer (below)
  1392.   SecBuffer                Buffers[4];    // void    SEC_FAR * pvBuffer;   // Pointer to the buffer
  1393.  
  1394.   DWORD                        cbIoBuffer, cbData, length;
  1395.     PBYTE                        buff;
  1396.   int i;
  1397.  
  1398.  
  1399.  
  1400.     // Read data from server until done.
  1401.     cbIoBuffer = 0;
  1402.         scRet = 0;
  1403.     while(TRUE) // Read some data.
  1404.     {
  1405.                 if( cbIoBuffer == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE ) // get the data
  1406.         {
  1407.             cbData = recv(Socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0);
  1408.             if(cbData == SOCKET_ERROR)
  1409.             {
  1410.                                 printf("**** Error %d reading data from server\n", WSAGetLastError());
  1411.                                 scRet = SEC_E_INTERNAL_ERROR;
  1412.                                 break;
  1413.             }
  1414.             else if(cbData == 0) // Server disconnected.
  1415.             {
  1416.                 if(cbIoBuffer)
  1417.                 {
  1418.                     printf("**** Server unexpectedly disconnected\n");
  1419.                     scRet = SEC_E_INTERNAL_ERROR;
  1420.                     return scRet;
  1421.                 }
  1422.                 else
  1423.                   break; // All Done
  1424.             }
  1425.             else // success
  1426.             {
  1427.                 if (fVerbose) printf("%d bytes of (encrypted) application data received\n", cbData);
  1428.                 if (fHexDump) { PrintHexDump(cbData, pbIoBuffer + cbIoBuffer); printf("\n"); }
  1429.                 cbIoBuffer += cbData;
  1430.             }
  1431.         }
  1432.  
  1433.  
  1434.         // Decrypt the received data.
  1435.         Buffers[0].pvBuffer     = pbIoBuffer;
  1436.         Buffers[0].cbBuffer     = cbIoBuffer;
  1437.         Buffers[0].BufferType   = SECBUFFER_DATA;  // Initial Type of the buffer 1
  1438.                 Buffers[1].BufferType   = SECBUFFER_EMPTY; // Initial Type of the buffer 2
  1439.                 Buffers[2].BufferType   = SECBUFFER_EMPTY; // Initial Type of the buffer 3
  1440.                 Buffers[3].BufferType   = SECBUFFER_EMPTY; // Initial Type of the buffer 4
  1441.  
  1442.                 Message.ulVersion       = SECBUFFER_VERSION;    // Version number
  1443.                 Message.cBuffers        = 4;                                    // Number of buffers - must contain four SecBuffer structures.
  1444.                 Message.pBuffers        = Buffers;                        // Pointer to array of buffers
  1445.         scRet = g_pSSPI->DecryptMessage(phContext, &Message, 0, NULL);
  1446.         if( scRet == SEC_I_CONTEXT_EXPIRED ) break; // Server signalled end of session
  1447. //      if( scRet == SEC_E_INCOMPLETE_MESSAGE - Input buffer has partial encrypted record, read more
  1448.         if( scRet != SEC_E_OK &&
  1449.             scRet != SEC_I_RENEGOTIATE &&
  1450.             scRet != SEC_I_CONTEXT_EXPIRED )
  1451.                         { printf("**** DecryptMessage ");
  1452.                             DisplaySECError((DWORD)scRet);
  1453.                             return scRet; }
  1454.  
  1455.  
  1456.  
  1457.         // Locate data and (optional) extra buffers.
  1458.         pDataBuffer  = NULL;
  1459.         pExtraBuffer = NULL;
  1460.         for(i = 1; i < 4; i++)
  1461.         {
  1462.             if( pDataBuffer  == NULL && Buffers[i].BufferType == SECBUFFER_DATA  ) pDataBuffer  = &Buffers[i];
  1463.             if( pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA ) pExtraBuffer = &Buffers[i];
  1464.         }
  1465.  
  1466.  
  1467.         // Display the decrypted data.
  1468.         if(pDataBuffer)
  1469.         {
  1470.                     length = pDataBuffer->cbBuffer;
  1471.                     if( length ) // check if last two chars are CR LF
  1472.                     {
  1473.                         buff = pDataBuffer->pvBuffer; // printf( "n-2= %d, n-1= %d \n", buff[length-2], buff[length-1] );
  1474.                         if (fVerbose) printf("Decrypted data: %d bytes\n", length);
  1475.                         PrintText( length, buff );
  1476.                         if (fHexDump) { PrintHexDump(length, buff); printf("\n"); }
  1477.                         if( buff[length-2] == 13 && buff[length-1] == 10 ) break; // printf("Found CRLF\n");
  1478.                     }
  1479.         }
  1480.  
  1481.  
  1482.  
  1483.         // Move any "extra" data to the input buffer.
  1484.         if(pExtraBuffer)
  1485.         {
  1486.             MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
  1487.             cbIoBuffer = pExtraBuffer->cbBuffer; // printf("cbIoBuffer= %d  \n", cbIoBuffer);
  1488.         }
  1489.         else
  1490.           cbIoBuffer = 0;
  1491.  
  1492.  
  1493.                 // The server wants to perform another handshake sequence.
  1494.         if(scRet == SEC_I_RENEGOTIATE)
  1495.         {
  1496.             printf("Server requested renegotiate!\n");
  1497.             scRet = ClientHandshakeLoop( Socket, phCreds, phContext, FALSE, &ExtraBuffer);
  1498.             if(scRet != SEC_E_OK) return scRet;
  1499.  
  1500.             if(ExtraBuffer.pvBuffer) // Move any "extra" data to the input buffer.
  1501.             {
  1502.                 MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
  1503.                 cbIoBuffer = ExtraBuffer.cbBuffer;
  1504.             }
  1505.         }
  1506.     } // Loop till CRLF is found at the end of the data
  1507.  
  1508.     return SEC_E_OK;
  1509. }
  1510.  
  1511.  
  1512.        
  1513. /*****************************************************************************/
  1514. static SECURITY_STATUS SMTPsession( SOCKET          Socket,     // in
  1515.                                                                 PCredHandle     phCreds,    // in
  1516.                                                                 CtxtHandle *    phContext)  // in
  1517. {
  1518.         SecPkgContext_StreamSizes Sizes;            // unsigned long cbBuffer;    // Size of the buffer, in bytes
  1519.     SECURITY_STATUS                        scRet;            // unsigned long BufferType;  // Type of the buffer (below)        
  1520.     PBYTE                                            pbIoBuffer; // void    SEC_FAR * pvBuffer;   // Pointer to the buffer
  1521.     DWORD                                            cbIoBufferLength, cbData;
  1522.  
  1523.  
  1524.     // Read stream encryption properties.
  1525.     scRet = g_pSSPI->QueryContextAttributesA( phContext, SECPKG_ATTR_STREAM_SIZES, &Sizes );
  1526.     if(scRet != SEC_E_OK)
  1527.     { printf("**** Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet); return scRet; }
  1528.  
  1529.  
  1530.     // Create a buffer.
  1531.     cbIoBufferLength = Sizes.cbHeader  +  Sizes.cbMaximumMessage  +  Sizes.cbTrailer;
  1532.     pbIoBuffer       = LocalAlloc(LMEM_FIXED, cbIoBufferLength);
  1533.     if(pbIoBuffer == NULL) { printf("**** Out of memory (2)\n"); return SEC_E_INTERNAL_ERROR; }
  1534.  
  1535.     if (!fDoTLS) {
  1536.       // Receive a Response
  1537.       scRet = ReadDecrypt( Socket, phCreds, phContext, pbIoBuffer, cbIoBufferLength );
  1538.       if( scRet != SEC_E_OK ) return scRet;
  1539.     }
  1540.  
  1541.     // Build the request - must be < maximum message size
  1542.     sprintf( pbIoBuffer+Sizes.cbHeader, "%s",  "EHLO example.com\r\n" ); // message begins after the header
  1543.  
  1544.     // Send a request.
  1545.     cbData = EncryptSend( Socket, phContext, pbIoBuffer, Sizes );
  1546.     if(cbData == SOCKET_ERROR || cbData == 0)
  1547.     { printf("**** Error %d sending data to server (3)\n",  WSAGetLastError()); return SEC_E_INTERNAL_ERROR; }  
  1548.  
  1549.  
  1550.     // Receive a Response
  1551.         scRet = ReadDecrypt( Socket, phCreds, phContext, pbIoBuffer, cbIoBufferLength );
  1552.     if( scRet != SEC_E_OK ) return scRet;
  1553.  
  1554.     // Build the request - must be < maximum message size
  1555.     sprintf( pbIoBuffer+Sizes.cbHeader, "%s",  "QUIT\r\n" ); // message begins after the header
  1556.  
  1557.  
  1558.     // Send a request.
  1559.     cbData = EncryptSend( Socket, phContext, pbIoBuffer, Sizes );
  1560.     if(cbData == SOCKET_ERROR || cbData == 0)
  1561.     { printf("**** Error %d sending data to server (3)\n",  WSAGetLastError()); return SEC_E_INTERNAL_ERROR; }  
  1562.  
  1563.  
  1564.     // Receive a Response
  1565.         scRet = ReadDecrypt( Socket, phCreds, phContext, pbIoBuffer, cbIoBufferLength );
  1566.     if( scRet != SEC_E_OK ) return scRet;
  1567.  
  1568.  
  1569.     return SEC_E_OK;
  1570. }
  1571.  
  1572.  
  1573. /*****************************************************************************/
  1574. void _cdecl main( int argc, char *argv[] )
  1575. {
  1576.     WSADATA WsaData;
  1577.     SOCKET  Socket = INVALID_SOCKET;
  1578.  
  1579.     CredHandle hClientCreds;
  1580.     CtxtHandle hContext;
  1581.     BOOL fCredsInitialized   = FALSE;
  1582.     BOOL fContextInitialized = FALSE;
  1583.  
  1584.     SecBuffer  ExtraData;
  1585.     SECURITY_STATUS Status;
  1586.  
  1587.     PCCERT_CONTEXT pRemoteCertContext = NULL;
  1588.  
  1589.     int i;
  1590.     char *p;
  1591.     for (i=1; i<argc; i++) {
  1592.       if (NULL != (p=strchr(argv[i], ':'))) {
  1593.         *p++ = '\0';
  1594.         pszServerName = argv[i];
  1595.         iPortNumber = atoi(p);
  1596.       } else {
  1597.         if (0 == stricmp(argv[i], "-v")) fVerbose = TRUE;
  1598.         else if (0 == stricmp(argv[i], "-t")) fDoTLS = TRUE;
  1599.         else if (0 == stricmp(argv[i], "-n")) fNoCertErr = TRUE;
  1600.         else if (0 == stricmp(argv[i], "-x")) fHexDump = TRUE;
  1601.         else if (0 == stricmp(argv[i], "-h")) {
  1602.           printf("TLSclient [-option ... -option] [host:port]\n\n");
  1603.           printf("option     meaning\n");
  1604.           printf("---------  -------------------------------------\n");
  1605.           printf("-v         verbose, more messages\n");
  1606.           printf("-t         use STARTTLS to start the session\n");
  1607.           printf("-n         ignore server certificate errors\n");
  1608.           printf("-x         hex dump of traffic data\n");
  1609.           printf("-h         show this help text\n");
  1610.           printf("host:port  connect to host on given port\n");
  1611.           printf("---------  -------------------------------------\n\n");
  1612.           printf("examples:  TLSclient -t smtp.gmail.com:25\n");
  1613.           printf("           TLSclient mail.gmx.com:465\n");
  1614.           printf("\n");
  1615.           return;
  1616.         }
  1617.       }
  1618.     }
  1619.  
  1620.     if( !LoadSecurityLibrary() )
  1621.     { printf("Error initializing the security library\n"); goto cleanup; } //
  1622. printf("----- SSPI Initialized\n");
  1623.  
  1624.  
  1625.     // Initialize the WinSock subsystem.
  1626.     if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR) // Winsock.h
  1627.     { printf("Error %d returned by WSAStartup\n", GetLastError()); goto cleanup; } //
  1628. printf("----- WinSock Initialized\n");
  1629.  
  1630.  
  1631.     // Create credentials.
  1632.     if(CreateCredentials(pszUser, &hClientCreds))
  1633.     { printf("Error creating credentials\n"); goto cleanup; }
  1634.     fCredsInitialized = TRUE; //
  1635. printf("----- Credentials Initialized\n");
  1636.  
  1637.  
  1638.     // Connect to server.
  1639. printf("----- Connecting To Server\n");
  1640.     if(ConnectToServer(pszServerName, iPortNumber, &Socket))
  1641.     { printf("Error connecting to server\n"); goto cleanup; } //
  1642. printf("----- Connected To Server\n");
  1643.  
  1644.  
  1645.  
  1646.     // Perform handshake
  1647.     if( PerformClientHandshake( Socket, &hClientCreds, pszServerName, &hContext, &ExtraData ) )
  1648.     { printf("Error performing handshake\n"); goto cleanup; }
  1649.     fContextInitialized = TRUE; //
  1650. printf("----- Client Handshake Performed\n");
  1651.  
  1652.  
  1653.     // Authenticate server's credentials. Get server's certificate.
  1654.     Status = g_pSSPI->QueryContextAttributesA( &hContext, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext );
  1655.     if(Status != SEC_E_OK)
  1656.         { printf("Error 0x%x querying remote certificate\n", Status); goto cleanup; } //
  1657. printf("----- Server Credentials Authenticated \n");
  1658.  
  1659.  
  1660.     // Display server certificate chain.
  1661.     DisplayCertChain( pRemoteCertContext, FALSE ); //
  1662. printf("----- Certificate Chain Displayed \n");
  1663.  
  1664.  
  1665.     // Attempt to validate server certificate.
  1666.     Status = VerifyServerCertificate( pRemoteCertContext, pszServerName, 0 );
  1667.         if(Status) { printf("**** Error 0x%x authenticating server credentials!\n", Status); goto cleanup; }
  1668.         // The server certificate did not validate correctly. At this point, we cannot tell
  1669.         // if we are connecting to the correct server, or if we are connecting to a
  1670.         // "man in the middle" attack server - Best to just abort the connection.
  1671. printf("----- Server Certificate Verified\n");
  1672.  
  1673.  
  1674.  
  1675.     // Free the server certificate context.
  1676.     CertFreeCertificateContext(pRemoteCertContext);
  1677.     pRemoteCertContext = NULL; //
  1678. printf("----- Server certificate context released \n");
  1679.  
  1680.  
  1681.     // Display connection info.
  1682. printf("----- Secure Connection Info\n\n");
  1683.     DisplayConnectionInfo(&hContext); //
  1684.  
  1685.  
  1686. printf("----- Running encrypted SMTP session\n");
  1687.     // Send Request, recover response. LPSTR pszRequest = "EHLO";
  1688.     if( SMTPsession( Socket, &hClientCreds, &hContext ) )
  1689.     { printf("Error SMTP Session \n"); goto cleanup; } //
  1690. printf("----- SMTP session Complete \n");
  1691.  
  1692.  
  1693.     // Send a close_notify alert to the server and close down the connection.
  1694.     if(DisconnectFromServer(Socket, &hClientCreds, &hContext))
  1695.     { printf("Error disconnecting from server\n"); goto cleanup; }
  1696.     fContextInitialized = FALSE;
  1697.     Socket = INVALID_SOCKET; //
  1698. printf("----- Disconnected From Server\n");
  1699.  
  1700.  
  1701.  
  1702.  
  1703. cleanup: //
  1704. printf("----- Begin Cleanup\n");
  1705.  
  1706.     // Free the server certificate context.
  1707.     if(pRemoteCertContext)
  1708.     {
  1709.         CertFreeCertificateContext(pRemoteCertContext);
  1710.         pRemoteCertContext = NULL;
  1711.     }
  1712.  
  1713.     // Free SSPI context handle.
  1714.     if(fContextInitialized)
  1715.     {
  1716.         g_pSSPI->DeleteSecurityContext(&hContext);
  1717.         fContextInitialized = FALSE;
  1718.     }
  1719.  
  1720.     // Free SSPI credentials handle.
  1721.     if(fCredsInitialized)
  1722.     {
  1723.         g_pSSPI->FreeCredentialsHandle(&hClientCreds);
  1724.         fCredsInitialized = FALSE;
  1725.     }
  1726.  
  1727.     // Close socket.
  1728.     if(Socket != INVALID_SOCKET) closesocket(Socket);
  1729.  
  1730.     // Shutdown WinSock subsystem.
  1731.     WSACleanup();
  1732.  
  1733.     // Close "MY" certificate store.
  1734.     if(hMyCertStore) CertCloseStore(hMyCertStore, 0);
  1735.  
  1736.     UnloadSecurityLibrary();
  1737.  
  1738.  
  1739. printf("----- All Done ----- \n");
  1740.  
  1741. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement