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
PS:会笑的人,运气通常都会比别人好。
posted on 2020-10-09 16:06 thinkinc999 阅读(639) 评论(1) 编辑 收藏 举报