/* */ #include "WinTLSContext.h" #include #include "BufferedFile.h" #include "LogFactory.h" #include "Logger.h" #include "fmt.h" #include "message.h" #include "util.h" #ifndef SP_PROT_TLS1_1_CLIENT #define SP_PROT_TLS1_1_CLIENT 0x00000200 #endif #ifndef SP_PROT_TLS1_1_SERVER #define SP_PROT_TLS1_1_SERVER 0x00000100 #endif #ifndef SP_PROT_TLS1_2_CLIENT #define SP_PROT_TLS1_2_CLIENT 0x00000800 #endif #ifndef SP_PROT_TLS1_2_SERVER #define SP_PROT_TLS1_2_SERVER 0x00000400 #endif #ifndef SCH_USE_STRONG_CRYPTO #define SCH_USE_STRONG_CRYPTO 0x00400000 #endif #define WEAK_CIPHER_BITS 56 #define STRONG_CIPHER_BITS 128 namespace aria2 { WinTLSContext::WinTLSContext(TLSSessionSide side, TLSVersion ver) : side_(side), store_(0) { memset(&credentials_, 0, sizeof(credentials_)); credentials_.dwVersion = SCHANNEL_CRED_VERSION; credentials_.grbitEnabledProtocols = 0; if (side_ == TLS_CLIENT) { switch (ver) { case TLS_PROTO_SSL3: credentials_.grbitEnabledProtocols |= SP_PROT_SSL3_CLIENT; // fall through case TLS_PROTO_TLS10: credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_CLIENT; // fall through case TLS_PROTO_TLS11: credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; // fall through case TLS_PROTO_TLS12: credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; // fall through default: break; } } else { switch (ver) { case TLS_PROTO_SSL3: credentials_.grbitEnabledProtocols |= SP_PROT_SSL3_SERVER; // fall through case TLS_PROTO_TLS10: credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_SERVER; // fall through case TLS_PROTO_TLS11: credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_1_SERVER; // fall through case TLS_PROTO_TLS12: credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_2_SERVER; // fall through default: break; } } switch (ver) { case TLS_PROTO_SSL3: // User explicitly wanted SSLv3 and therefore weak ciphers. credentials_.dwMinimumCipherStrength = WEAK_CIPHER_BITS; break; default: // Strong protocol versions: Use a minimum strength, which might be later // refined using SCH_USE_STRONG_CRYPTO in the flags. credentials_.dwMinimumCipherStrength = STRONG_CIPHER_BITS; } setVerifyPeer(side_ == TLS_CLIENT); } TLSContext* TLSContext::make(TLSSessionSide side, TLSVersion ver) { return new WinTLSContext(side, ver); } WinTLSContext::~WinTLSContext() { if (store_) { ::CertCloseStore(store_, 0); store_ = 0; } } bool WinTLSContext::getVerifyPeer() const { return credentials_.dwFlags & SCH_CRED_AUTO_CRED_VALIDATION; } void WinTLSContext::setVerifyPeer(bool verify) { cred_.reset(); // Never automatically push any client or server certs. We'll do cert setup // ourselves. credentials_.dwFlags = SCH_CRED_NO_DEFAULT_CREDS; if (credentials_.dwMinimumCipherStrength > WEAK_CIPHER_BITS) { // Enable strong crypto if we already set a minimum cipher streams. // This might actually require even stronger algorithms, which is a good // thing. credentials_.dwFlags |= SCH_USE_STRONG_CRYPTO; } if (side_ != TLS_CLIENT || !verify) { // No verification for servers and if user explicitly requested it credentials_.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_NO_SERVERNAME_CHECK; return; } // Verify other side's cert chain. credentials_.dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_REVOCATION_CHECK_CHAIN | SCH_CRED_IGNORE_NO_REVOCATION_CHECK; } CredHandle* WinTLSContext::getCredHandle() { if (cred_) { return cred_.get(); } TimeStamp ts; cred_.reset(new CredHandle()); const CERT_CONTEXT* ctx = nullptr; if (store_) { ctx = ::CertEnumCertificatesInStore(store_, nullptr); if (!ctx) { throw DL_ABORT_EX("Failed to load certificate"); } credentials_.cCreds = 1; credentials_.paCred = &ctx; } else { credentials_.cCreds = 0; credentials_.paCred = nullptr; } SECURITY_STATUS status = ::AcquireCredentialsHandleW( nullptr, (SEC_WCHAR*)UNISP_NAME_W, side_ == TLS_CLIENT ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, nullptr, &credentials_, nullptr, nullptr, cred_.get(), &ts); if (ctx) { ::CertFreeCertificateContext(ctx); } if (status != SEC_E_OK) { cred_.reset(); throw DL_ABORT_EX("Failed to initialize WinTLS context handle"); } return cred_.get(); } bool WinTLSContext::addCredentialFile(const std::string& certfile, const std::string& keyfile) { std::stringstream ss; BufferedFile(certfile.c_str(), "rb").transfer(ss); auto data = ss.str(); CRYPT_DATA_BLOB blob = { (DWORD)data.length(), (BYTE*)data.c_str() }; if (!::PFXIsPFXBlob(&blob)) { A2_LOG_ERROR("Not a valid PKCS12 file"); return false; } HCERTSTORE store = ::PFXImportCertStore(&blob, L"", CRYPT_EXPORTABLE | CRYPT_USER_KEYSET); if (!store_) { store = ::PFXImportCertStore( &blob, nullptr, CRYPT_EXPORTABLE | CRYPT_USER_KEYSET); } if (!store) { A2_LOG_ERROR("Failed to import PKCS12 store"); return false; } auto ctx = ::CertEnumCertificatesInStore(store, nullptr); if (!ctx) { A2_LOG_ERROR("PKCS12 file does not contain certificates"); ::CertCloseStore(store, 0); return false; } ::CertFreeCertificateContext(ctx); if (store_) { ::CertCloseStore(store_, 0); } store_ = store; cred_.reset(); return true; } bool WinTLSContext::addTrustedCACertFile(const std::string& certfile) { A2_LOG_WARN("TLS CA bundle files are not supported. " "The system trust store will be used."); return false; } } // namespace aria2