Windows C++ TLS 实现连接163邮箱

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

posted on 2020-10-09 16:06  thinkinc999  阅读(639)  评论(1编辑  收藏  举报

导航