int ssl_init(void) { /* init SSL before parsing configuration file */ SSL_load_error_strings(); SSL_library_init(); index_cli=SSL_get_ex_new_index(0, "cli index", NULL, NULL, NULL); index_opt=SSL_CTX_get_ex_new_index(0, "opt index", NULL, NULL, NULL); index_redirect=SSL_SESSION_get_ex_new_index(0, "redirect index", NULL, NULL, NULL); index_addr=SSL_SESSION_get_ex_new_index(0, "addr index", NULL, NULL, cb_free); if(index_cli<0 || index_opt<0 || index_redirect<0 || index_addr<0) { s_log(LOG_ERR, "Application specific data initialization failed"); return 1; } #ifndef OPENSSL_NO_ENGINE ENGINE_load_builtin_engines(); #endif #ifndef OPENSSL_NO_DH dh_params=get_dh2048(); if(!dh_params) { s_log(LOG_ERR, "Failed to get default DH parameters"); return 1; } #endif /* OPENSSL_NO_DH */ return 0; }
/* From OpenSSL's documentation: * * If "strong" primes were used to generate the DH parameters, it is * not strictly necessary to generate a new key for each handshake * but it does improve forward secrecy. * * -- gilles@ */ DH * get_dh(void) { #if defined(USE_DH1024) return get_dh1024(); #else return get_dh2048(); #endif }
NOEXPORT int init_dh(SERVICE_OPTIONS *section) { DH *dh=NULL; s_log(LOG_DEBUG, "DH initialization"); #ifdef HAVE_OSSL_ENGINE_H if(!section->engine) /* cert is a file and not an identifier */ #endif dh=read_dh(section->cert); if(!dh) dh=get_dh2048(); if(!dh) { s_log(LOG_NOTICE, "DH initialization failed"); return 1; /* FAILED */ } SSL_CTX_set_tmp_dh(section->ctx, dh); s_log(LOG_DEBUG, "DH initialized with %d-bit key", 8*DH_size(dh)); DH_free(dh); return 0; /* OK */ }
int ssl_setup(SSL_CTX **ctxp, struct pki *pki, int (*sni_cb)(SSL *,int *,void *), const char *ciphers) { DH *dh; SSL_CTX *ctx; uint8_t sid[SSL_MAX_SID_CTX_LENGTH]; ctx = ssl_ctx_create(pki->pki_name, pki->pki_cert, pki->pki_cert_len, ciphers); /* * Set session ID context to a random value. We don't support * persistent caching of sessions so it is OK to set a temporary * session ID context that is valid during run time. */ arc4random_buf(sid, sizeof(sid)); if (!SSL_CTX_set_session_id_context(ctx, sid, sizeof(sid))) goto err; if (sni_cb) SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb); if (pki->pki_dhparams_len == 0) dh = get_dh2048(); else dh = get_dh_from_memory(pki->pki_dhparams, pki->pki_dhparams_len); ssl_set_ephemeral_key_exchange(ctx, dh); DH_free(dh); SSL_CTX_set_ecdh_auto(ctx, 1); *ctxp = ctx; return 1; err: SSL_CTX_free(ctx); ssl_error("ssl_setup"); return 0; }
pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode ) { if (!ssl_initialized) { ssl_initialized = 1; SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); ssl_ex_data_index = SSL_get_ex_new_index( 0, (void *) "org.apache.qpid.proton.ssl", NULL, NULL, NULL); } pn_ssl_domain_t *domain = (pn_ssl_domain_t *) calloc(1, sizeof(pn_ssl_domain_t)); if (!domain) return NULL; domain->ref_count = 1; domain->mode = mode; // enable all supported protocol versions, then explicitly disable the // known vulnerable ones. This should allow us to use the latest version // of the TLS standard that the installed library supports. switch(mode) { case PN_SSL_MODE_CLIENT: domain->ctx = SSL_CTX_new(SSLv23_client_method()); // and TLSv1+ if (!domain->ctx) { ssl_log_error("Unable to initialize OpenSSL context."); free(domain); return NULL; } break; case PN_SSL_MODE_SERVER: domain->ctx = SSL_CTX_new(SSLv23_server_method()); // and TLSv1+ if (!domain->ctx) { ssl_log_error("Unable to initialize OpenSSL context."); free(domain); return NULL; } break; default: pn_transport_logf(NULL, "Invalid value for pn_ssl_mode_t: %d", mode); free(domain); return NULL; } const long reject_insecure = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; SSL_CTX_set_options(domain->ctx, reject_insecure); #ifdef SSL_OP_NO_COMPRESSION // Mitigate the CRIME vulnerability SSL_CTX_set_options(domain->ctx, SSL_OP_NO_COMPRESSION); #endif // by default, allow anonymous ciphers so certificates are not required 'out of the box' if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_ANONYMOUS )) { ssl_log_error("Failed to set cipher list to %s", CIPHERS_ANONYMOUS); pn_ssl_domain_free(domain); return NULL; } // ditto: by default do not authenticate the peer (can be done by SASL). if (pn_ssl_domain_set_peer_authentication( domain, PN_SSL_ANONYMOUS_PEER, NULL )) { pn_ssl_domain_free(domain); return NULL; } DH *dh = get_dh2048(); if (dh) { SSL_CTX_set_tmp_dh(domain->ctx, dh); DH_free(dh); SSL_CTX_set_options(domain->ctx, SSL_OP_SINGLE_DH_USE); } return domain; }
/** Initialize the SSL context. * \return pointer to SSL context object. */ SSL_CTX * ssl_init(char *private_key_file, char *ca_file, char *ca_dir, int req_client_cert) { const SSL_METHOD *meth; /* If this const gives you a warning, you're using an old version of OpenSSL. Walker, this means you! */ /* uint8_t context[128]; */ unsigned int reps = 1; pcg32_random_t rand_state; uint64_t seeds[2]; bool seeded = false; if (!bio_err) { if (!SSL_library_init()) return NULL; SSL_load_error_strings(); /* Error write context */ bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); } lock_file(stderr); fputs("Seeding OpenSSL random number pool.\n", stderr); unlock_file(stderr); while (!RAND_status()) { /* At this point, a system with /dev/urandom or a EGD file in the usual places will have enough entropy. Otherwise, be lazy and use random numbers until it's satisfied. */ uint32_t gibberish[8]; int n; if (!seeded) { generate_seed(seeds); pcg32_srandom_r(&rand_state, seeds[0], seeds[1]); seeded = 1; } for (n = 0; n < 8; n++) gibberish[n] = pcg32_random_r(&rand_state); RAND_seed(gibberish, sizeof gibberish); reps += 1; } lock_file(stderr); fprintf(stderr, "Seeded after %u %s.\n", reps, reps > 1 ? "cycles" : "cycle"); unlock_file(stderr); /* Set up SIGPIPE handler here? */ /* Create context */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L meth = TLS_server_method(); #else meth = SSLv23_server_method(); #endif ctx = SSL_CTX_new(meth); SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); /* Load keys/certs */ if (private_key_file && *private_key_file) { if (!SSL_CTX_use_certificate_chain_file(ctx, private_key_file)) { ssl_errordump("Unable to load server certificate - only anonymous " "ciphers supported."); } if (!SSL_CTX_use_PrivateKey_file(ctx, private_key_file, SSL_FILETYPE_PEM)) { ssl_errordump( "Unable to load private key - only anonymous ciphers supported."); } } /* Load trusted CAs */ if ((ca_file && *ca_file) || (ca_dir && *ca_dir)) { if (!SSL_CTX_load_verify_locations(ctx, (ca_file && *ca_file) ? ca_file : NULL, (ca_dir && *ca_dir) ? ca_dir : NULL)) { ssl_errordump("Unable to load CA certificates"); } { STACK_OF(X509_NAME) *certs = NULL; if (ca_file && *ca_file) certs = SSL_load_client_CA_file(ca_file); if (certs) SSL_CTX_set_client_CA_list(ctx, certs); if (req_client_cert) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, client_verify_callback); else SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, client_verify_callback); #if (OPENSSL_VERSION_NUMBER < 0x0090600fL) SSL_CTX_set_verify_depth(ctx, 1); #endif } } SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_ALL); SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); /* Set up DH key */ { DH *dh; dh = get_dh2048(); SSL_CTX_set_tmp_dh(ctx, dh); DH_free(dh); } #ifdef NID_X9_62_prime256v1 /* Set up ECDH key */ { EC_KEY *ecdh = NULL; ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); SSL_CTX_set_tmp_ecdh(ctx, ecdh); EC_KEY_free(ecdh); } #endif /* Set the cipher list to the usual default list, except that * we'll allow anonymous diffie-hellman, too. */ SSL_CTX_set_cipher_list(ctx, "ALL:ECDH:ADH:!LOW:!MEDIUM:@STRENGTH"); /* Set up session cache if we can */ /* strncpy((char *) context, MUDNAME, 128); SSL_CTX_set_session_id_context(ctx, context, strlen(context)); */ return ctx; }
pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode ) { if (!ssl_initialized) { ssl_initialized = 1; SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); ssl_ex_data_index = SSL_get_ex_new_index( 0, (void *) "org.apache.qpid.proton.ssl", NULL, NULL, NULL); } pn_ssl_domain_t *domain = (pn_ssl_domain_t *) calloc(1, sizeof(pn_ssl_domain_t)); if (!domain) return NULL; domain->ref_count = 1; domain->mode = mode; switch(mode) { case PN_SSL_MODE_CLIENT: domain->ctx = SSL_CTX_new(TLSv1_client_method()); if (!domain->ctx) { _log_ssl_error( "Unable to initialize OpenSSL context.\n"); free(domain); return NULL; } break; case PN_SSL_MODE_SERVER: domain->ctx = SSL_CTX_new(SSLv23_server_method()); if (!domain->ctx) { _log_ssl_error("Unable to initialize OpenSSL context.\n"); free(domain); return NULL; } SSL_CTX_set_options(domain->ctx, SSL_OP_NO_SSLv2); // v2 is insecure break; default: _log_error("Invalid valid for pn_ssl_mode_t: %d\n", mode); free(domain); return NULL; } // by default, allow anonymous ciphers so certificates are not required 'out of the box' if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_ANONYMOUS )) { _log_ssl_error("Failed to set cipher list to %s\n", CIPHERS_ANONYMOUS); pn_ssl_domain_free(domain); return NULL; } // ditto: by default do not authenticate the peer (can be done by SASL). if (pn_ssl_domain_set_peer_authentication( domain, PN_SSL_ANONYMOUS_PEER, NULL )) { pn_ssl_domain_free(domain); return NULL; } DH *dh = get_dh2048(); if (dh) { SSL_CTX_set_tmp_dh(domain->ctx, dh); DH_free(dh); SSL_CTX_set_options(domain->ctx, SSL_OP_SINGLE_DH_USE); } return domain; }
void SSLContextManager::addSSLContextConfig( const SSLContextConfig& ctxConfig, const SSLCacheOptions& cacheOptions, const TLSTicketKeySeeds* ticketSeeds, const folly::SocketAddress& vipAddress, const std::shared_ptr<SSLCacheProvider>& externalCache) { unsigned numCerts = 0; std::string commonName; std::string lastCertPath; std::unique_ptr<std::list<std::string>> subjectAltName; auto sslCtx = std::make_shared<SSLContext>(ctxConfig.sslVersion); for (const auto& cert : ctxConfig.certificates) { try { sslCtx->loadCertificate(cert.certPath.c_str()); } catch (const std::exception& ex) { // The exception isn't very useful without the certificate path name, // so throw a new exception that includes the path to the certificate. string msg = folly::to<string>("error loading SSL certificate ", cert.certPath, ": ", folly::exceptionStr(ex)); LOG(ERROR) << msg; throw std::runtime_error(msg); } // Verify that the Common Name and (if present) Subject Alternative Names // are the same for all the certs specified for the SSL context. numCerts++; X509* x509 = getX509(sslCtx->getSSLCtx()); auto guard = folly::makeGuard([x509] { X509_free(x509); }); auto cn = SSLUtil::getCommonName(x509); if (!cn) { throw std::runtime_error(folly::to<string>("Cannot get CN for X509 ", cert.certPath)); } auto altName = SSLUtil::getSubjectAltName(x509); VLOG(2) << "cert " << cert.certPath << " CN: " << *cn; if (altName) { altName->sort(); VLOG(2) << "cert " << cert.certPath << " SAN: " << flattenList(*altName); } else { VLOG(2) << "cert " << cert.certPath << " SAN: " << "{none}"; } if (numCerts == 1) { commonName = *cn; subjectAltName = std::move(altName); } else { if (commonName != *cn) { throw std::runtime_error(folly::to<string>("X509 ", cert.certPath, " does not have same CN as ", lastCertPath)); } if (altName == nullptr) { if (subjectAltName != nullptr) { throw std::runtime_error(folly::to<string>("X509 ", cert.certPath, " does not have same SAN as ", lastCertPath)); } } else { if ((subjectAltName == nullptr) || (*altName != *subjectAltName)) { throw std::runtime_error(folly::to<string>("X509 ", cert.certPath, " does not have same SAN as ", lastCertPath)); } } } lastCertPath = cert.certPath; // TODO t4438250 - Add ECDSA support to the crypto_ssl offload server // so we can avoid storing the ECDSA private key in the // address space of the Internet-facing process. For // now, if cert name includes "-EC" to denote elliptic // curve, we load its private key even if the server as // a whole has been configured for async crypto. if (ctxConfig.isLocalPrivateKey || (cert.certPath.find("-EC") != std::string::npos)) { // The private key lives in the same process // This needs to be called before loadPrivateKey(). if (!cert.passwordPath.empty()) { auto sslPassword = std::make_shared<PasswordInFile>(cert.passwordPath); sslCtx->passwordCollector(sslPassword); } try { sslCtx->loadPrivateKey(cert.keyPath.c_str()); } catch (const std::exception& ex) { // Throw an error that includes the key path, so the user can tell // which key had a problem. string msg = folly::to<string>("error loading private SSL key ", cert.keyPath, ": ", folly::exceptionStr(ex)); LOG(ERROR) << msg; throw std::runtime_error(msg); } } } if (!ctxConfig.isLocalPrivateKey) { enableAsyncCrypto(sslCtx); } // Let the server pick the highest performing cipher from among the client's // choices. // // Let's use a unique private key for all DH key exchanges. // // Because some old implementations choke on empty fragments, most SSL // applications disable them (it's part of SSL_OP_ALL). This // will improve performance and decrease write buffer fragmentation. sslCtx->setOptions(SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE | SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); // Configure SSL ciphers list if (!ctxConfig.tls11Ciphers.empty()) { // FIXME: create a dummy SSL_CTX for cipher testing purpose? It can // remove the ordering dependency // Test to see if the specified TLS1.1 ciphers are valid. Note that // these will be overwritten by the ciphers() call below. sslCtx->setCiphersOrThrow(ctxConfig.tls11Ciphers); } // Important that we do this *after* checking the TLS1.1 ciphers above, // since we test their validity by actually setting them. sslCtx->ciphers(ctxConfig.sslCiphers); // Use a fix DH param DH* dh = get_dh2048(); SSL_CTX_set_tmp_dh(sslCtx->getSSLCtx(), dh); DH_free(dh); const string& curve = ctxConfig.eccCurveName; if (!curve.empty()) { set_key_from_curve(sslCtx->getSSLCtx(), curve); } if (!ctxConfig.clientCAFile.empty()) { try { sslCtx->setVerificationOption(SSLContext::VERIFY_REQ_CLIENT_CERT); sslCtx->loadTrustedCertificates(ctxConfig.clientCAFile.c_str()); sslCtx->loadClientCAList(ctxConfig.clientCAFile.c_str()); } catch (const std::exception& ex) { string msg = folly::to<string>("error loading client CA", ctxConfig.clientCAFile, ": ", folly::exceptionStr(ex)); LOG(ERROR) << msg; throw std::runtime_error(msg); } } // - start - SSL session cache config // the internal cache never does what we want (per-thread-per-vip). // Disable it. SSLSessionCacheManager will set it appropriately. SSL_CTX_set_session_cache_mode(sslCtx->getSSLCtx(), SSL_SESS_CACHE_OFF); SSL_CTX_set_timeout(sslCtx->getSSLCtx(), cacheOptions.sslCacheTimeout.count()); std::unique_ptr<SSLSessionCacheManager> sessionCacheManager; if (ctxConfig.sessionCacheEnabled && cacheOptions.maxSSLCacheSize > 0 && cacheOptions.sslCacheFlushSize > 0) { sessionCacheManager = folly::make_unique<SSLSessionCacheManager>( cacheOptions.maxSSLCacheSize, cacheOptions.sslCacheFlushSize, sslCtx.get(), vipAddress, commonName, eventBase_, stats_, externalCache); } // - end - SSL session cache config std::unique_ptr<TLSTicketKeyManager> ticketManager = createTicketManagerHelper(sslCtx, ticketSeeds, ctxConfig, stats_); // finalize sslCtx setup by the individual features supported by openssl ctxSetupByOpensslFeature(sslCtx, ctxConfig); try { insert(sslCtx, std::move(sessionCacheManager), std::move(ticketManager), ctxConfig.isDefault); } catch (const std::exception& ex) { string msg = folly::to<string>("Error adding certificate : ", folly::exceptionStr(ex)); LOG(ERROR) << msg; throw std::runtime_error(msg); } }