in java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java [462:666]
public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) throws KeyManagementException {
if (initialized) {
log.warn(sm.getString("openssl.doubleInit"));
return;
}
boolean success;
Exception cause = null;
try (var localArena = Arena.ofConfined()) {
if (sslHostConfig.getInsecureRenegotiation()) {
openssl_h_Compatibility.SSL_CTX_set_options(state.sslCtx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION());
} else {
openssl_h_Compatibility.SSL_CTX_clear_options(state.sslCtx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION());
}
// Use server's preference order for ciphers (rather than
// client's)
if (sslHostConfig.getHonorCipherOrder()) {
openssl_h_Compatibility.SSL_CTX_set_options(state.sslCtx, SSL_OP_CIPHER_SERVER_PREFERENCE());
} else {
openssl_h_Compatibility.SSL_CTX_clear_options(state.sslCtx, SSL_OP_CIPHER_SERVER_PREFERENCE());
}
// Disable compression if requested
if (sslHostConfig.getDisableCompression()) {
openssl_h_Compatibility.SSL_CTX_set_options(state.sslCtx, SSL_OP_NO_COMPRESSION());
} else {
openssl_h_Compatibility.SSL_CTX_clear_options(state.sslCtx, SSL_OP_NO_COMPRESSION());
}
// Disable TLS Session Tickets (RFC4507) to protect perfect forward secrecy
if (sslHostConfig.getDisableSessionTickets()) {
openssl_h_Compatibility.SSL_CTX_set_options(state.sslCtx, SSL_OP_NO_TICKET());
} else {
openssl_h_Compatibility.SSL_CTX_clear_options(state.sslCtx, SSL_OP_NO_TICKET());
}
// List the ciphers that the client is permitted to negotiate
if (minTlsVersion <= TLS1_2_VERSION()) {
if (SSL_CTX_set_cipher_list(state.sslCtx,
localArena.allocateFrom(sslHostConfig.getCiphers())) <= 0) {
log.warn(sm.getString("engine.failedCipherList", sslHostConfig.getCiphers()));
}
}
// Check if the ciphers have been changed from the defaults
if (maxTlsVersion >= TLS1_3_VERSION() && (sslHostConfig.getCiphers() != SSLHostConfig.DEFAULT_TLS_CIPHERS)) {
if (SSL_CTX_set_ciphersuites(state.sslCtx,
localArena.allocateFrom(sslHostConfig.getCiphers())) <= 0) {
log.warn(sm.getString("engine.failedCipherSuite", sslHostConfig.getCiphers()));
}
}
// If there is no certificate file must be using a KeyStore so a KeyManager is required.
// If there is a certificate file a KeyManager is helpful but not strictly necessary.
certificate.setCertificateKeyManager(
OpenSSLUtil.chooseKeyManager(kms, certificate.getCertificateFile() == null));
success = addCertificate(certificate, localArena);
// Client certificate verification
int value = switch (sslHostConfig.getCertificateVerification()) {
case NONE -> SSL_VERIFY_NONE();
case OPTIONAL -> SSL_VERIFY_PEER();
case OPTIONAL_NO_CA -> OPTIONAL_NO_CA;
case REQUIRED -> SSL_VERIFY_FAIL_IF_NO_PEER_CERT();
};
// Set int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) callback
SSL_CTX_set_verify(state.sslCtx, value,
SSL_CTX_set_verify$callback.allocate(new OpenSSLEngine.VerifyCallback(), contextArena));
// Trust and certificate verification
if (tms != null) {
// Client certificate verification based on custom trust managers
x509TrustManager = chooseTrustManager(tms);
SSL_CTX_set_cert_verify_callback(state.sslCtx,
SSL_CTX_set_cert_verify_callback$cb.allocate(new CertVerifyCallback(x509TrustManager), contextArena), state.sslCtx);
// Pass along the DER encoded certificates of the accepted client
// certificate issuers, so that their subjects can be presented
// by the server during the handshake to allow the client choosing
// an acceptable certificate
for (X509Certificate caCert : x509TrustManager.getAcceptedIssuers()) {
var rawCACertificate = localArena.allocateFrom(ValueLayout.JAVA_BYTE, caCert.getEncoded());
var rawCACertificatePointer = localArena.allocateFrom(ValueLayout.ADDRESS, rawCACertificate);
var x509CACert = d2i_X509(MemorySegment.NULL, rawCACertificatePointer, rawCACertificate.byteSize());
if (MemorySegment.NULL.equals(x509CACert)) {
logLastError("openssl.errorLoadingCertificate");
} else if (SSL_CTX_add_client_CA(state.sslCtx, x509CACert) <= 0) {
logLastError("openssl.errorAddingCertificate");
} else if (log.isDebugEnabled()) {
log.debug(sm.getString("openssl.addedClientCaCert", caCert.toString()));
}
}
} else {
// Client certificate verification based on trusted CA files and dirs
MemorySegment caCertificateFileNative = sslHostConfig.getCaCertificateFile() != null
? localArena.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile())) : MemorySegment.NULL;
MemorySegment caCertificatePathNative = sslHostConfig.getCaCertificatePath() != null
? localArena.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())) : MemorySegment.NULL;
if ((sslHostConfig.getCaCertificateFile() != null || sslHostConfig.getCaCertificatePath() != null)
&& SSL_CTX_load_verify_locations(state.sslCtx,
caCertificateFileNative, caCertificatePathNative) <= 0) {
logLastError("openssl.errorConfiguringLocations");
} else {
var caCerts = SSL_CTX_get_client_CA_list(state.sslCtx);
if (MemorySegment.NULL.equals(caCerts)) {
caCerts = SSL_load_client_CA_file(caCertificateFileNative);
if (!MemorySegment.NULL.equals(caCerts)) {
SSL_CTX_set_client_CA_list(state.sslCtx, caCerts);
}
} else {
// OpenSSL might crash here when passing null on some platforms
if (MemorySegment.NULL.equals(caCertificateFileNative)
|| (SSL_add_file_cert_subjects_to_stack(caCerts, caCertificateFileNative) <= 0)) {
caCerts = MemorySegment.NULL;
}
}
if (MemorySegment.NULL.equals(caCerts)) {
log.warn(sm.getString("openssl.noCACerts"));
}
}
}
if (negotiableProtocols != null && !negotiableProtocols.isEmpty()) {
SSL_CTX_set_alpn_select_cb(state.sslCtx,
SSL_CTX_set_alpn_select_cb$cb.allocate(new ALPNSelectCallback(negotiableProtocols), contextArena), state.sslCtx);
}
// Log any non fatal init errors
String errMessage = OpenSSLLibrary.getLastError();
while (errMessage != null) {
log.info(sm.getString("openssl.errorInit", errMessage));
errMessage = OpenSSLLibrary.getLastError();
}
// Apply OpenSSLConfCmd if used
OpenSSLConf openSslConf = sslHostConfig.getOpenSslConf();
if (openSslConf != null && !MemorySegment.NULL.equals(state.confCtx)) {
// Check OpenSSLConfCmd if used
if (log.isTraceEnabled()) {
log.trace(sm.getString("openssl.checkConf"));
}
try {
if (!checkConf(openSslConf)) {
log.error(sm.getString("openssl.errCheckConf"));
}
} catch (Exception e) {
log.error(sm.getString("openssl.errCheckConf"), e);
}
if (log.isTraceEnabled()) {
log.trace(sm.getString("openssl.applyConf"));
}
try {
if (!applyConf(openSslConf)) {
log.error(sm.getString("openssl.errApplyConf"));
}
} catch (Exception e) {
log.error(sm.getString("openssl.errApplyConf"), e);
}
// Reconfigure the enabled protocols
long opts = openssl_h_Compatibility.SSL_CTX_get_options(state.sslCtx);
List<String> enabled = new ArrayList<>();
// Seems like there is no way to explicitly disable SSLv2Hello
// in OpenSSL so it is always enabled
enabled.add(Constants.SSL_PROTO_SSLv2Hello);
if ((opts & SSL_OP_NO_TLSv1()) == 0) {
enabled.add(Constants.SSL_PROTO_TLSv1);
}
if ((opts & SSL_OP_NO_TLSv1_1()) == 0) {
enabled.add(Constants.SSL_PROTO_TLSv1_1);
}
if ((opts & SSL_OP_NO_TLSv1_2()) == 0) {
enabled.add(Constants.SSL_PROTO_TLSv1_2);
}
if ((opts & SSL_OP_NO_TLSv1_3()) == 0) {
enabled.add(Constants.SSL_PROTO_TLSv1_3);
}
if ((opts & SSL_OP_NO_SSLv2()) == 0) {
enabled.add(Constants.SSL_PROTO_SSLv2);
}
if ((opts & SSL_OP_NO_SSLv3()) == 0) {
enabled.add(Constants.SSL_PROTO_SSLv3);
}
sslHostConfig.setEnabledProtocols(
enabled.toArray(new String[0]));
// Reconfigure the enabled ciphers
sslHostConfig.setEnabledCiphers(getCiphers(state.sslCtx));
}
sessionContext = new OpenSSLSessionContext(this);
// If client authentication is being used, OpenSSL requires that
// this is set so always set it in case an app is configured to
// require it
sessionContext.setSessionIdContext(DEFAULT_SESSION_ID_CONTEXT);
sslHostConfig.setOpenSslContext(Long.valueOf(state.sslCtx.address()));
initialized = true;
} catch (Exception e) {
cause = e;
success = false;
}
if (!success) {
destroy();
throw new KeyManagementException(sm.getString("openssl.errorSSLCtxInit"), cause);
}
}