1 /************************************************************************************ 2 Copyright (C) 2019 MariaDB 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Library General Public 6 License as published by the Free Software Foundation; either 7 version 2 of the License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public 15 License along with this library; if not see <http://www.gnu.org/licenses> 16 or write to the Free Software Foundation, Inc., 17 51 Franklin St., Fifth Floor, Boston, MA 02110, USA 18 19 *************************************************************************************/ 20 21 /* 22 This module contain X509 certificate handling on Windows. 23 PEM parsing, loading client certificate and key, server certificate validation 24 */ 25 26 /* 27 CERT_CHAIN_ENGINE_CONFIG has additional members in Windows 8.1 28 To allow client to be work on pre-8.1 Windows, compile 29 with corresponding _WIN32_WINNT 30 */ 31 #ifdef _WIN32_WINNT 32 #undef _WIN32_WINNT 33 #define _WIN32_WINNT 0x0601 34 #endif 35 36 #include "schannel_certs.h" 37 #include <malloc.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <winsock2.h> 41 #include <ws2tcpip.h> 42 #include <winhttp.h> 43 #include <assert.h> 44 #include "win32_errmsg.h" 45 46 /* 47 Return GetLastError(), or, if this unexpectedly gives success, 48 return ERROR_INTERNAL_ERROR. 49 50 Background - in several cases in this module we return GetLastError() 51 after an Windows function fails. However, we do not want the function to 52 return success, even if GetLastError() was suddenly 0. 53 */ 54 static DWORD get_last_error() 55 { 56 DWORD ret = GetLastError(); 57 if (ret) 58 return ret; 59 60 // We generally expect last error to be set API fails. 61 // thus the debug assertion- 62 assert(0); 63 return ERROR_INTERNAL_ERROR; 64 } 65 66 #define FAIL(...) \ 67 do{\ 68 status = get_last_error();\ 69 ma_format_win32_error(errmsg, errmsg_len, status, __VA_ARGS__);\ 70 goto cleanup;\ 71 } while (0) 72 73 /* 74 Load file into memory. Add null terminator at the end, so it will be a valid C string. 75 */ 76 static char* pem_file_to_string(const char* file, char* errmsg, size_t errmsg_len) 77 { 78 LARGE_INTEGER file_size; 79 size_t file_bufsize = 0; 80 size_t total_bytes_read = 0; 81 char* file_buffer = NULL; 82 SECURITY_STATUS status = SEC_E_OK; 83 84 HANDLE file_handle = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, 85 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 86 if (file_handle == INVALID_HANDLE_VALUE) 87 { 88 FAIL("failed to open file '%s'", file); 89 } 90 91 if (!GetFileSizeEx(file_handle, &file_size)) 92 { 93 FAIL("GetFileSizeEx failed on '%s'", file); 94 } 95 96 if (file_size.QuadPart > ULONG_MAX - 1) 97 { 98 SetLastError(SEC_E_INVALID_PARAMETER); 99 FAIL("file '%s' too large", file); 100 } 101 102 file_bufsize = (size_t)file_size.QuadPart; 103 file_buffer = (char*)LocalAlloc(0,file_bufsize + 1); 104 if (!file_buffer) 105 { 106 FAIL("LocalAlloc(0,%zu) failed", file_bufsize + 1); 107 } 108 109 while (total_bytes_read < file_bufsize) 110 { 111 DWORD bytes_to_read = (DWORD)(file_bufsize - total_bytes_read); 112 DWORD bytes_read = 0; 113 114 if (!ReadFile(file_handle, file_buffer + total_bytes_read, 115 bytes_to_read, &bytes_read, NULL)) 116 { 117 FAIL("ReadFile() failed to read file '%s'", file); 118 } 119 if (bytes_read == 0) 120 { 121 /* Premature EOF -- adjust the bufsize to the new value */ 122 file_bufsize = total_bytes_read; 123 } 124 else 125 { 126 total_bytes_read += bytes_read; 127 } 128 } 129 130 /* Null terminate the buffer */ 131 file_buffer[file_bufsize] = '\0'; 132 133 cleanup: 134 if (file_handle != INVALID_HANDLE_VALUE) 135 { 136 CloseHandle(file_handle); 137 } 138 if (status) 139 { 140 /* Some error happened. */ 141 LocalFree(file_buffer); 142 file_buffer = NULL; 143 } 144 return file_buffer; 145 } 146 147 148 // Structure for parsing BEGIN/END sections inside pem. 149 typedef struct _pem_type_desc 150 { 151 const char* begin_tag; 152 size_t begin_tag_len; 153 const char* end_tag; 154 size_t end_tag_len; 155 } pem_type_desc; 156 157 #define BEGIN_TAG(x) "-----BEGIN " x "-----" 158 #define END_TAG(x) "\n-----END " x "-----" 159 #define PEM_SECTION(tag) {BEGIN_TAG(tag), sizeof(BEGIN_TAG(tag))-1, END_TAG(tag), sizeof(END_TAG(tag))-1} 160 161 typedef enum { 162 PEM_TYPE_CERTIFICATE = 0, 163 PEM_TYPE_X509_CRL, 164 PEM_TYPE_RSA_PRIVATE_KEY, 165 PEM_TYPE_PRIVATE_KEY 166 } PEM_TYPE; 167 168 static const pem_type_desc pem_sections[] = { 169 PEM_SECTION("CERTIFICATE"), 170 PEM_SECTION("X509 CRL"), 171 PEM_SECTION("RSA PRIVATE KEY"), 172 PEM_SECTION("PRIVATE KEY") 173 }; 174 175 /* 176 Locate a substring in pem for given type, 177 e.g section between BEGIN CERTIFICATE and END CERTIFICATE 178 in PEMs base64 format, with header and footer. 179 180 output parameters 'begin' and 'end' are set upon return. 181 it is possible that functions returns 'begin' != NULL but 182 'end' = NULL. This is generally a format error, meaning that 183 the end tag was not found 184 */ 185 void pem_locate(char* pem_str, 186 PEM_TYPE type, 187 char** begin, 188 char** end) 189 { 190 *begin = NULL; 191 *end = NULL; 192 char c; 193 194 const pem_type_desc* desc = &pem_sections[type]; 195 *begin = strstr(pem_str, desc->begin_tag); 196 if (!(*begin)) 197 return; 198 199 // We expect newline after the 200 // begin tag, LF or CRLF 201 c = (*begin)[desc->begin_tag_len]; 202 203 if (c != '\r' && c != '\n') 204 { 205 *begin = NULL; 206 return; 207 } 208 209 *end = strstr(*begin + desc->begin_tag_len + 1, desc->end_tag); 210 if (!*end) 211 return; // error, end marker not found 212 213 (*end) += desc->end_tag_len; 214 return; 215 } 216 217 218 /* 219 Add certificates, or CRLs from a PEM file to Wincrypt store 220 */ 221 static SECURITY_STATUS add_certs_to_store( 222 HCERTSTORE trust_store, 223 const char* file, 224 PEM_TYPE type, 225 char* errmsg, 226 size_t errmsg_len) 227 { 228 char* file_buffer = NULL; 229 char* cur = NULL; 230 SECURITY_STATUS status = SEC_E_OK; 231 CRL_CONTEXT* crl_context = NULL; 232 CERT_CONTEXT* cert_context = NULL; 233 char* begin; 234 char* end; 235 236 file_buffer = pem_file_to_string(file, errmsg, errmsg_len); 237 if (!file_buffer) 238 goto cleanup; 239 240 for (cur = file_buffer; ; cur = end) 241 { 242 pem_locate(cur, type, &begin, &end); 243 244 if (!begin) 245 break; 246 247 if (!end) 248 { 249 SetLastError(SEC_E_INVALID_PARAMETER); 250 FAIL("Invalid PEM file '%s', missing end marker corresponding to begin marker '%s' at offset %zu", 251 file, pem_sections[type].begin_tag, (size_t)(begin - file_buffer)); 252 } 253 CERT_BLOB cert_blob; 254 void* context = NULL; 255 DWORD actual_content_type = 0; 256 257 cert_blob.pbData = (BYTE*)begin; 258 cert_blob.cbData = (DWORD)(end - begin); 259 if (!CryptQueryObject( 260 CERT_QUERY_OBJECT_BLOB, &cert_blob, 261 CERT_QUERY_CONTENT_FLAG_CERT | CERT_QUERY_CONTENT_FLAG_CRL, 262 CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type, 263 NULL, NULL, NULL, (const void**)&context)) 264 { 265 FAIL("failed to extract certificate from PEM file '%s'",file); 266 } 267 268 if (!context) 269 { 270 SetLastError(SEC_E_INTERNAL_ERROR); 271 FAIL("unexpected result from CryptQueryObject(),cert_context is NULL" 272 " after successful completion, file '%s'", 273 file); 274 } 275 276 if (actual_content_type == CERT_QUERY_CONTENT_CERT) 277 { 278 CERT_CONTEXT* cert_context = (CERT_CONTEXT*)context; 279 if (!CertAddCertificateContextToStore( 280 trust_store, cert_context, 281 CERT_STORE_ADD_ALWAYS, NULL)) 282 { 283 FAIL("CertAddCertificateContextToStore failed"); 284 } 285 } 286 else if (actual_content_type == CERT_QUERY_CONTENT_CRL) 287 { 288 CRL_CONTEXT* crl_context = (CRL_CONTEXT*)context; 289 if (!CertAddCRLContextToStore( 290 trust_store, crl_context, 291 CERT_STORE_ADD_ALWAYS, NULL)) 292 { 293 FAIL("CertAddCRLContextToStore() failed"); 294 } 295 } 296 } 297 cleanup: 298 LocalFree(file_buffer); 299 if (cert_context) 300 CertFreeCertificateContext(cert_context); 301 if (crl_context) 302 CertFreeCRLContext(crl_context); 303 return status; 304 } 305 306 /* 307 Add a directory to store, i.e try to load all files. 308 (extract certificates and add them to store) 309 310 @return 0 on success, error only if directory is invalid. 311 */ 312 SECURITY_STATUS add_dir_to_store(HCERTSTORE trust_store, const char* dir, 313 PEM_TYPE type, char* errmsg, size_t errmsg_len) 314 { 315 WIN32_FIND_DATAA ffd; 316 char path[MAX_PATH]; 317 char pattern[MAX_PATH]; 318 DWORD dwAttr; 319 HANDLE hFind = INVALID_HANDLE_VALUE; 320 SECURITY_STATUS status = SEC_E_OK; 321 322 if ((dwAttr = GetFileAttributes(dir)) == INVALID_FILE_ATTRIBUTES) 323 { 324 SetLastError(SEC_E_INVALID_PARAMETER); 325 FAIL("directory '%s' does not exist", dir); 326 } 327 if (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) 328 { 329 SetLastError(SEC_E_INVALID_PARAMETER); 330 FAIL("'%s' is not a directory", dir); 331 } 332 sprintf_s(pattern, sizeof(pattern), "%s\\*", dir); 333 hFind = FindFirstFile(pattern, &ffd); 334 if (hFind == INVALID_HANDLE_VALUE) 335 { 336 FAIL("FindFirstFile(%s) failed",pattern); 337 } 338 do 339 { 340 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 341 continue; 342 sprintf_s(path, sizeof(path), "%s\\%s", dir, ffd.cFileName); 343 344 // ignore error from add_certs_to_store(), not all file 345 // maybe PEM. 346 add_certs_to_store(trust_store, path, type, errmsg, 347 errmsg_len); 348 } while (FindNextFile(hFind, &ffd) != 0); 349 350 cleanup: 351 if (hFind != INVALID_HANDLE_VALUE) 352 FindClose(hFind); 353 354 return status; 355 } 356 357 /* Count certificates in store. */ 358 static int count_certificates(HCERTSTORE store) 359 { 360 int num_certs = 0; 361 PCCERT_CONTEXT c = NULL; 362 363 while ((c = CertEnumCertificatesInStore(store, c))) 364 num_certs++; 365 366 return num_certs; 367 } 368 369 /** 370 Creates certificate store with user defined CA chain and/or CRL. 371 Loads PEM certificate from files or directories. 372 373 If only CRLFile/CRLPath is defined, the "system" store is duplicated, 374 and new CRLs are added to it. 375 376 If CAFile/CAPAth is defined, then new empty store is created, and CAs 377 (and CRLs, if defined), are added to it. 378 379 The function throws an error, if none of the files in CAFile/CAPath have a valid certificate. 380 It is also an error if CRLFile does not exist. 381 */ 382 SECURITY_STATUS schannel_create_store( 383 const char* CAFile, 384 const char* CAPath, 385 const char* CRLFile, 386 const char* CRLPath, 387 HCERTSTORE* out_store, 388 char* errmsg, 389 size_t errmsg_len) 390 { 391 392 HCERTSTORE store = NULL; 393 HCERTSTORE system_store = NULL; 394 int status = SEC_E_OK; 395 396 *out_store = NULL; 397 if (!CAFile && !CAPath && !CRLFile && !CRLPath) 398 { 399 /* Nothing to do, caller will use default store*/ 400 *out_store = NULL; 401 return SEC_E_OK; 402 } 403 if (CAFile || CAPath) 404 { 405 /* Open the certificate store */ 406 store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, (HCRYPTPROV)NULL, 407 CERT_STORE_CREATE_NEW_FLAG, NULL); 408 if (!store) 409 { 410 FAIL("CertOpenStore failed for memory store"); 411 } 412 } 413 else if (CRLFile || CRLPath) 414 { 415 /* Only CRL was provided, copy system store, add revocation list to 416 * it. */ 417 system_store = 418 CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY)NULL, 419 CERT_SYSTEM_STORE_CURRENT_USER, L"MY"); 420 if (!system_store) 421 { 422 FAIL("CertOpenStore failed for system store"); 423 } 424 425 store = CertDuplicateStore(system_store); 426 if (!store) 427 { 428 FAIL("CertDuplicateStore failed"); 429 } 430 } 431 432 if (CAFile) 433 { 434 status = add_certs_to_store(store, CAFile, 435 PEM_TYPE_CERTIFICATE, errmsg, errmsg_len); 436 if (status) 437 goto cleanup; 438 } 439 if (CAPath) 440 { 441 status = add_dir_to_store(store, CAPath, 442 PEM_TYPE_CERTIFICATE, errmsg, errmsg_len); 443 if (status) 444 goto cleanup; 445 } 446 447 if ((CAFile || CAPath) && store && !count_certificates(store)) 448 { 449 SetLastError(SEC_E_INVALID_PARAMETER); 450 FAIL("no valid certificates were found, CAFile='%s', CAPath='%s'", 451 CAFile ? CAFile : "<not set>", CAPath ? CAPath : "<not set>"); 452 } 453 454 if (CRLFile) 455 { 456 status = add_certs_to_store(store, CRLFile, PEM_TYPE_X509_CRL, 457 errmsg, errmsg_len); 458 } 459 if (CRLPath) 460 { 461 status = add_dir_to_store(store, CRLPath, PEM_TYPE_X509_CRL, 462 errmsg, errmsg_len); 463 } 464 465 cleanup: 466 if (system_store) 467 CertCloseStore(system_store, 0); 468 if (status && store) 469 { 470 CertCloseStore(store, 0); 471 store = NULL; 472 } 473 *out_store = store; 474 return status; 475 } 476 477 /* 478 The main verification logic. 479 Taken almost completely from Windows 2003 Platform SDK 2003 480 (Samples\Security\SSPI\SSL\WebClient.c) 481 482 The only difference here is is usage of custom store 483 and chain engine. 484 */ 485 static SECURITY_STATUS VerifyServerCertificate( 486 PCCERT_CONTEXT pServerCert, 487 HCERTSTORE hStore, 488 LPWSTR pwszServerName, 489 DWORD dwRevocationCheckFlags, 490 DWORD dwVerifyFlags, 491 LPSTR errmsg, 492 size_t errmsg_len) 493 { 494 SSL_EXTRA_CERT_CHAIN_POLICY_PARA polExtra; 495 CERT_CHAIN_POLICY_PARA PolicyPara; 496 CERT_CHAIN_POLICY_STATUS PolicyStatus; 497 CERT_CHAIN_PARA ChainPara; 498 HCERTCHAINENGINE hChainEngine = NULL; 499 PCCERT_CHAIN_CONTEXT pChainContext = NULL; 500 LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH, 501 szOID_SERVER_GATED_CRYPTO, 502 szOID_SGC_NETSCAPE }; 503 DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR); 504 SECURITY_STATUS status = SEC_E_OK; 505 506 if (pServerCert == NULL) 507 { 508 SetLastError(SEC_E_WRONG_PRINCIPAL); 509 FAIL("Invalid parameter pServerCert passed to VerifyServerCertificate"); 510 } 511 512 ZeroMemory(&ChainPara, sizeof(ChainPara)); 513 ChainPara.cbSize = sizeof(ChainPara); 514 ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; 515 ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages; 516 ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; 517 518 if (hStore) 519 { 520 CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 }; 521 EngineConfig.cbSize = sizeof(EngineConfig); 522 EngineConfig.hExclusiveRoot = hStore; 523 if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine)) 524 { 525 FAIL("CertCreateCertificateChainEngine failed"); 526 } 527 } 528 529 if (!CertGetCertificateChain( 530 hChainEngine, 531 pServerCert, 532 NULL, 533 pServerCert->hCertStore, 534 &ChainPara, 535 dwRevocationCheckFlags, 536 NULL, 537 &pChainContext)) 538 { 539 FAIL("CertGetCertificateChain failed"); 540 goto cleanup; 541 } 542 543 // Validate certificate chain. 544 ZeroMemory(&polExtra, sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA)); 545 polExtra.cbStruct = sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA); 546 polExtra.dwAuthType = AUTHTYPE_SERVER; 547 polExtra.fdwChecks = dwVerifyFlags; 548 polExtra.pwszServerName = pwszServerName; 549 550 memset(&PolicyPara, 0, sizeof(PolicyPara)); 551 PolicyPara.cbSize = sizeof(PolicyPara); 552 PolicyPara.pvExtraPolicyPara = &polExtra; 553 554 memset(&PolicyStatus, 0, sizeof(PolicyStatus)); 555 PolicyStatus.cbSize = sizeof(PolicyStatus); 556 557 if (!CertVerifyCertificateChainPolicy( 558 CERT_CHAIN_POLICY_SSL, 559 pChainContext, 560 &PolicyPara, 561 &PolicyStatus)) 562 { 563 FAIL("CertVerifyCertificateChainPolicy failed"); 564 } 565 566 if (PolicyStatus.dwError) 567 { 568 SetLastError(PolicyStatus.dwError); 569 FAIL("Server certificate validation failed"); 570 } 571 572 cleanup: 573 if (hChainEngine) 574 { 575 CertFreeCertificateChainEngine(hChainEngine); 576 } 577 if (pChainContext) 578 { 579 CertFreeCertificateChain(pChainContext); 580 } 581 return status; 582 } 583 584 585 void schannel_free_store(HCERTSTORE store) 586 { 587 if (store) 588 CertCloseStore(store, 0); 589 } 590 591 592 /* 593 Verify server certificate against a wincrypt store 594 @return 0 - success, otherwise error occurred. 595 */ 596 SECURITY_STATUS schannel_verify_server_certificate( 597 const CERT_CONTEXT* cert, 598 HCERTSTORE store, 599 BOOL check_revocation, 600 const char* server_name, 601 BOOL check_server_name, 602 char* errmsg, 603 size_t errmsg_len) 604 { 605 SECURITY_STATUS status = SEC_E_OK; 606 wchar_t* wserver_name = NULL; 607 DWORD dwVerifyFlags; 608 DWORD dwRevocationFlags; 609 610 if (check_server_name) 611 { 612 int cchServerName = (int)strlen(server_name) + 1; 613 wserver_name = (wchar_t*)LocalAlloc(0,sizeof(wchar_t) * cchServerName); 614 if (!wserver_name) 615 { 616 FAIL("LocalAlloc() failed"); 617 } 618 if (MultiByteToWideChar(CP_UTF8, 0, server_name, cchServerName, wserver_name, cchServerName) < 0) 619 { 620 FAIL("MultiByteToWideChar() failed"); 621 } 622 } 623 624 dwVerifyFlags = 0; 625 dwRevocationFlags = 0; 626 if (check_revocation) 627 dwRevocationFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; 628 if (!check_server_name) 629 dwVerifyFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID; 630 631 status = VerifyServerCertificate(cert, store, wserver_name ? wserver_name : L"SERVER_NAME", 632 dwRevocationFlags, dwVerifyFlags, errmsg, errmsg_len); 633 634 cleanup: 635 LocalFree(wserver_name); 636 return status; 637 } 638 639 640 /* Attach private key (in PEM format) to client certificate */ 641 static SECURITY_STATUS load_private_key(CERT_CONTEXT* cert, char* private_key_str, size_t len, char* errmsg, size_t errmsg_len) 642 { 643 DWORD derlen = (DWORD)len; 644 BYTE* derbuf = NULL; 645 DWORD keyblob_len = 0; 646 BYTE* keyblob = NULL; 647 HCRYPTPROV hProv = 0; 648 HCRYPTKEY hKey = 0; 649 CERT_KEY_CONTEXT cert_key_context = { 0 }; 650 PCRYPT_PRIVATE_KEY_INFO pki = NULL; 651 DWORD pki_len = 0; 652 SECURITY_STATUS status = SEC_E_OK; 653 654 derbuf = LocalAlloc(0, derlen); 655 if (!derbuf) 656 { 657 FAIL("LocalAlloc failed"); 658 } 659 660 if (!CryptStringToBinaryA(private_key_str, (DWORD)len, CRYPT_STRING_BASE64HEADER, derbuf, &derlen, NULL, NULL)) 661 { 662 FAIL("Failed to convert BASE64 private key"); 663 } 664 665 /* 666 To accommodate for both "BEGIN PRIVATE KEY" vs "BEGIN RSA PRIVATE KEY" 667 sections in PEM, we try to decode with PKCS_PRIVATE_KEY_INFO first, 668 and, if it fails, with PKCS_RSA_PRIVATE_KEY flag. 669 */ 670 if (CryptDecodeObjectEx( 671 X509_ASN_ENCODING, 672 PKCS_PRIVATE_KEY_INFO, 673 derbuf, derlen, 674 CRYPT_DECODE_ALLOC_FLAG, 675 NULL, &pki, &pki_len)) 676 { 677 // convert private key info to RSA private key blob 678 if (!CryptDecodeObjectEx( 679 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 680 PKCS_RSA_PRIVATE_KEY, 681 pki->PrivateKey.pbData, 682 pki->PrivateKey.cbData, 683 CRYPT_DECODE_ALLOC_FLAG, 684 NULL, &keyblob, &keyblob_len)) 685 { 686 FAIL("Failed to parse private key"); 687 } 688 } 689 else if (!CryptDecodeObjectEx( 690 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 691 PKCS_RSA_PRIVATE_KEY, 692 derbuf, derlen, 693 CRYPT_DECODE_ALLOC_FLAG, NULL, 694 &keyblob, &keyblob_len)) 695 { 696 FAIL("Failed to parse private key"); 697 } 698 699 if (!CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) 700 { 701 FAIL("CryptAcquireContext failed"); 702 } 703 704 if (!CryptImportKey(hProv, keyblob, keyblob_len, 0, 0, (HCRYPTKEY*)&hKey)) 705 { 706 FAIL("CryptImportKey failed"); 707 } 708 cert_key_context.hCryptProv = hProv; 709 cert_key_context.dwKeySpec = AT_KEYEXCHANGE; 710 cert_key_context.cbSize = sizeof(cert_key_context); 711 712 /* assign private key to certificate context */ 713 if (!CertSetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, 714 CERT_STORE_NO_CRYPT_RELEASE_FLAG, 715 &cert_key_context)) 716 { 717 FAIL("CertSetCertificateContextProperty failed"); 718 } 719 720 cleanup: 721 LocalFree(derbuf); 722 LocalFree(keyblob); 723 LocalFree(pki); 724 if (hKey) 725 CryptDestroyKey(hKey); 726 if (status) 727 { 728 if (hProv) 729 CryptReleaseContext(hProv, 0); 730 } 731 return status; 732 } 733 734 /* 735 Given PEM strings for certificate and private key, 736 create a client certificate* 737 */ 738 static CERT_CONTEXT* create_client_certificate_mem( 739 char* cert_file_content, 740 char* key_file_content, 741 char* errmsg, 742 size_t errmsg_len) 743 { 744 CERT_CONTEXT* ctx = NULL; 745 char* begin; 746 char* end; 747 CERT_BLOB cert_blob; 748 DWORD actual_content_type = 0; 749 SECURITY_STATUS status = SEC_E_OK; 750 751 /* Parse certificate */ 752 pem_locate(cert_file_content, PEM_TYPE_CERTIFICATE, 753 &begin, &end); 754 755 if (!begin || !end) 756 { 757 SetLastError(SEC_E_INVALID_PARAMETER); 758 FAIL("Client certificate not found in PEM file"); 759 } 760 761 cert_blob.pbData = (BYTE*)begin; 762 cert_blob.cbData = (DWORD)(end - begin); 763 if (!CryptQueryObject( 764 CERT_QUERY_OBJECT_BLOB, &cert_blob, 765 CERT_QUERY_CONTENT_FLAG_CERT, 766 CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type, 767 NULL, NULL, NULL, (const void**)&ctx)) 768 { 769 FAIL("Can't parse client certficate"); 770 } 771 772 /* Parse key */ 773 PEM_TYPE types[] = { PEM_TYPE_RSA_PRIVATE_KEY, PEM_TYPE_PRIVATE_KEY }; 774 for (int i = 0; i < sizeof(types) / sizeof(types[0]); i++) 775 { 776 pem_locate(key_file_content, types[i], &begin, &end); 777 if (begin && end) 778 { 779 /* Assign key to certificate.*/ 780 status = load_private_key(ctx, begin, (end - begin), errmsg, errmsg_len); 781 goto cleanup; 782 } 783 } 784 785 if (!begin || !end) 786 { 787 SetLastError(SEC_E_INVALID_PARAMETER); 788 FAIL("Client private key not found in PEM"); 789 } 790 791 cleanup: 792 if (status && ctx) 793 { 794 CertFreeCertificateContext(ctx); 795 ctx = NULL; 796 } 797 return ctx; 798 } 799 800 801 /* Given cert and key, as PEM file names, create a client certificate */ 802 CERT_CONTEXT* schannel_create_cert_context(char* cert_file, char* key_file, char* errmsg, size_t errmsg_len) 803 { 804 CERT_CONTEXT* ctx = NULL; 805 char* key_file_content = NULL; 806 char* cert_file_content = NULL; 807 808 cert_file_content = pem_file_to_string(cert_file, errmsg, errmsg_len); 809 810 if (!cert_file_content) 811 goto cleanup; 812 813 if (cert_file == key_file) 814 { 815 key_file_content = cert_file_content; 816 } 817 else 818 { 819 key_file_content = pem_file_to_string(key_file, errmsg, errmsg_len); 820 if (!key_file_content) 821 goto cleanup; 822 } 823 824 ctx = create_client_certificate_mem(cert_file_content, key_file_content, errmsg, errmsg_len); 825 826 cleanup: 827 LocalFree(cert_file_content); 828 if (cert_file != key_file) 829 LocalFree(key_file_content); 830 831 return ctx; 832 } 833 834 /* 835 Free certificate, and all resources, created by schannel_create_cert_context() 836 */ 837 void schannel_free_cert_context(const CERT_CONTEXT* cert) 838 { 839 /* release provider handle which was acquires in load_private_key() */ 840 CERT_KEY_CONTEXT cert_key_context = { 0 }; 841 cert_key_context.cbSize = sizeof(cert_key_context); 842 DWORD cbData = sizeof(CERT_KEY_CONTEXT); 843 HCRYPTPROV hProv = 0; 844 845 if (CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &cert_key_context, &cbData)) 846 { 847 hProv = cert_key_context.hCryptProv; 848 } 849 CertFreeCertificateContext(cert); 850 if (hProv) 851 { 852 CryptReleaseContext(cert_key_context.hCryptProv, 0); 853 } 854 } 855