Author: markt Date: Fri Jun 19 20:10:23 2015 New Revision: 1686497 URL: http://svn.apache.org/r1686497 Log: Complete the OpenSSL configuration plumbing for multiple certificates per virtual host
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/AprEndpoint.java tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfig.java tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java Modified: tomcat/trunk/java/org/apache/tomcat/util/net/AprEndpoint.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/AprEndpoint.java?rev=1686497&r1=1686496&r2=1686497&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/AprEndpoint.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/AprEndpoint.java Fri Jun 19 20:10:23 2015 @@ -369,180 +369,189 @@ public class AprEndpoint extends Abstrac for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) { for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) { - if (SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateFile()) == null) { + if (SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()) == null) { // This is required throw new Exception(sm.getString("endpoint.apr.noSslCertFile")); } + } + if (sslHostConfig.getCertificates().size() > 2) { + // TODO: Can this limitation be removed? + throw new Exception(sm.getString("endpoint.apr.tooManyCertFiles")); + } - // SSL protocol - int value = SSL.SSL_PROTOCOL_NONE; - if (sslHostConfig.getProtocols().size() == 0) { - // Native fallback used if protocols="" - value = SSL.SSL_PROTOCOL_ALL; - } else { - for (String protocol : sslHostConfig.getProtocols()) { - if (Constants.SSL_PROTO_SSLv2Hello.equalsIgnoreCase(protocol)) { - // NO-OP. OpenSSL always supports SSLv2Hello - } else if (Constants.SSL_PROTO_SSLv2.equalsIgnoreCase(protocol)) { - value |= SSL.SSL_PROTOCOL_SSLV2; - } else if (Constants.SSL_PROTO_SSLv3.equalsIgnoreCase(protocol)) { - value |= SSL.SSL_PROTOCOL_SSLV3; - } else if (Constants.SSL_PROTO_TLSv1.equalsIgnoreCase(protocol)) { - value |= SSL.SSL_PROTOCOL_TLSV1; - } else if (Constants.SSL_PROTO_TLSv1_1.equalsIgnoreCase(protocol)) { - value |= SSL.SSL_PROTOCOL_TLSV1_1; - } else if (Constants.SSL_PROTO_TLSv1_2.equalsIgnoreCase(protocol)) { - value |= SSL.SSL_PROTOCOL_TLSV1_2; - } else { - // Protocol not recognized, fail to start as it is safer than - // continuing with the default which might enable more than the - // is required - throw new Exception(sm.getString( - "endpoint.apr.invalidSslProtocol", protocol)); - } + // SSL protocol + int value = SSL.SSL_PROTOCOL_NONE; + if (sslHostConfig.getProtocols().size() == 0) { + // Native fallback used if protocols="" + value = SSL.SSL_PROTOCOL_ALL; + } else { + for (String protocol : sslHostConfig.getProtocols()) { + if (Constants.SSL_PROTO_SSLv2Hello.equalsIgnoreCase(protocol)) { + // NO-OP. OpenSSL always supports SSLv2Hello + } else if (Constants.SSL_PROTO_SSLv2.equalsIgnoreCase(protocol)) { + value |= SSL.SSL_PROTOCOL_SSLV2; + } else if (Constants.SSL_PROTO_SSLv3.equalsIgnoreCase(protocol)) { + value |= SSL.SSL_PROTOCOL_SSLV3; + } else if (Constants.SSL_PROTO_TLSv1.equalsIgnoreCase(protocol)) { + value |= SSL.SSL_PROTOCOL_TLSV1; + } else if (Constants.SSL_PROTO_TLSv1_1.equalsIgnoreCase(protocol)) { + value |= SSL.SSL_PROTOCOL_TLSV1_1; + } else if (Constants.SSL_PROTO_TLSv1_2.equalsIgnoreCase(protocol)) { + value |= SSL.SSL_PROTOCOL_TLSV1_2; + } else { + // Protocol not recognized, fail to start as it is safer than + // continuing with the default which might enable more than the + // is required + throw new Exception(sm.getString( + "endpoint.apr.invalidSslProtocol", protocol)); } } + } - // Create SSL Context - long ctx = 0; - try { - ctx = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER); - } catch (Exception e) { - // If the sslEngine is disabled on the AprLifecycleListener - // there will be an Exception here but there is no way to check - // the AprLifecycleListener settings from here - throw new Exception( - sm.getString("endpoint.apr.failSslContextMake"), e); - } - - boolean legacyRenegSupported = false; - try { - legacyRenegSupported = SSL.hasOp(SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); - if (legacyRenegSupported) - if (sslHostConfig.getInsecureRenegotiation()) { - SSLContext.setOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); - } else { - SSLContext.clearOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); - } - } catch (UnsatisfiedLinkError e) { - // Ignore - } - if (!legacyRenegSupported) { - // OpenSSL does not support unsafe legacy renegotiation. - log.warn(sm.getString("endpoint.warn.noInsecureReneg", - SSL.versionString())); - } - - // Use server's preference order for ciphers (rather than - // client's) - boolean orderCiphersSupported = false; - try { - orderCiphersSupported = SSL.hasOp(SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); - if (orderCiphersSupported) { - if (sslHostConfig.getHonorCipherOrder()) { - SSLContext.setOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); - } else { - SSLContext.clearOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); - } + // Create SSL Context + long ctx = 0; + try { + ctx = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER); + } catch (Exception e) { + // If the sslEngine is disabled on the AprLifecycleListener + // there will be an Exception here but there is no way to check + // the AprLifecycleListener settings from here + throw new Exception( + sm.getString("endpoint.apr.failSslContextMake"), e); + } + + boolean legacyRenegSupported = false; + try { + legacyRenegSupported = SSL.hasOp(SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + if (legacyRenegSupported) + if (sslHostConfig.getInsecureRenegotiation()) { + SSLContext.setOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + } else { + SSLContext.clearOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); } - } catch (UnsatisfiedLinkError e) { - // Ignore - } - if (!orderCiphersSupported) { - // OpenSSL does not support ciphers ordering. - log.warn(sm.getString("endpoint.warn.noHonorCipherOrder", - SSL.versionString())); - } - - // Disable compression if requested - boolean disableCompressionSupported = false; - try { - disableCompressionSupported = SSL.hasOp(SSL.SSL_OP_NO_COMPRESSION); - if (disableCompressionSupported) { - if (sslHostConfig.getDisableCompression()) { - SSLContext.setOptions(ctx, SSL.SSL_OP_NO_COMPRESSION); - } else { - SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_COMPRESSION); - } + } catch (UnsatisfiedLinkError e) { + // Ignore + } + if (!legacyRenegSupported) { + // OpenSSL does not support unsafe legacy renegotiation. + log.warn(sm.getString("endpoint.warn.noInsecureReneg", + SSL.versionString())); + } + + // Use server's preference order for ciphers (rather than + // client's) + boolean orderCiphersSupported = false; + try { + orderCiphersSupported = SSL.hasOp(SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); + if (orderCiphersSupported) { + if (sslHostConfig.getHonorCipherOrder()) { + SSLContext.setOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); + } else { + SSLContext.clearOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); } - } catch (UnsatisfiedLinkError e) { - // Ignore } - if (!disableCompressionSupported) { - // OpenSSL does not support ciphers ordering. - log.warn(sm.getString("endpoint.warn.noDisableCompression", - SSL.versionString())); - } - - // Disable TLS Session Tickets (RFC4507) to protect perfect forward secrecy - boolean disableSessionTicketsSupported = false; - try { - disableSessionTicketsSupported = SSL.hasOp(SSL.SSL_OP_NO_TICKET); - if (disableSessionTicketsSupported) { - if (sslHostConfig.getDisableSessionTickets()) { - SSLContext.setOptions(ctx, SSL.SSL_OP_NO_TICKET); - } else { - SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_TICKET); - } + } catch (UnsatisfiedLinkError e) { + // Ignore + } + if (!orderCiphersSupported) { + // OpenSSL does not support ciphers ordering. + log.warn(sm.getString("endpoint.warn.noHonorCipherOrder", + SSL.versionString())); + } + + // Disable compression if requested + boolean disableCompressionSupported = false; + try { + disableCompressionSupported = SSL.hasOp(SSL.SSL_OP_NO_COMPRESSION); + if (disableCompressionSupported) { + if (sslHostConfig.getDisableCompression()) { + SSLContext.setOptions(ctx, SSL.SSL_OP_NO_COMPRESSION); + } else { + SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_COMPRESSION); } - } catch (UnsatisfiedLinkError e) { - // Ignore } - if (!disableSessionTicketsSupported) { - // OpenSSL is too old to support TLS Session Tickets. - log.warn(sm.getString("endpoint.warn.noDisableSessionTickets", - SSL.versionString())); + } catch (UnsatisfiedLinkError e) { + // Ignore + } + if (!disableCompressionSupported) { + // OpenSSL does not support ciphers ordering. + log.warn(sm.getString("endpoint.warn.noDisableCompression", + SSL.versionString())); + } + + // Disable TLS Session Tickets (RFC4507) to protect perfect forward secrecy + boolean disableSessionTicketsSupported = false; + try { + disableSessionTicketsSupported = SSL.hasOp(SSL.SSL_OP_NO_TICKET); + if (disableSessionTicketsSupported) { + if (sslHostConfig.getDisableSessionTickets()) { + SSLContext.setOptions(ctx, SSL.SSL_OP_NO_TICKET); + } else { + SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_TICKET); + } } + } catch (UnsatisfiedLinkError e) { + // Ignore + } + if (!disableSessionTicketsSupported) { + // OpenSSL is too old to support TLS Session Tickets. + log.warn(sm.getString("endpoint.warn.noDisableSessionTickets", + SSL.versionString())); + } - // List the ciphers that the client is permitted to negotiate - SSLContext.setCipherSuite(ctx, sslHostConfig.getCiphers()); - // Load Server key and certificate + // List the ciphers that the client is permitted to negotiate + SSLContext.setCipherSuite(ctx, sslHostConfig.getCiphers()); + // Load Server key and certificate + // TODO: Confirm assumption that idx is not specific to + // key/certificate type + int idx = 0; + for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) { SSLContext.setCertificate(ctx, - SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateFile()), - SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateKeyFile()), - certificate.getCertificateKeyPassword(), SSL.SSL_AIDX_RSA); - // Support Client Certificates - SSLContext.setCACertificate(ctx, - SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()), - SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())); - // Set revocation - SSLContext.setCARevocation(ctx, - SSLHostConfig.adjustRelativePath( - sslHostConfig.getCertificateRevocationListFile()), - SSLHostConfig.adjustRelativePath( - sslHostConfig.getCertificateRevocationListPath())); - // Client certificate verification - switch (sslHostConfig.getCertificateVerification()) { - case NONE: - value = SSL.SSL_CVERIFY_NONE; - break; - case OPTIONAL: - value = SSL.SSL_CVERIFY_OPTIONAL; - break; - case OPTIONAL_NO_CA: - value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA; - break; - case REQUIRED: - value = SSL.SSL_CVERIFY_REQUIRE; - break; - } - SSLContext.setVerify(ctx, value, sslHostConfig.getCertificateVerificationDepth()); - // For now, sendfile is not supported with SSL - if (getUseSendfile()) { - setUseSendfileInternal(false); - if (useSendFileSet) { - log.warn(sm.getString("endpoint.apr.noSendfileWithSSL")); - } + SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), + SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), + certificate.getCertificateKeyPassword(), idx++); + } + // Support Client Certificates + SSLContext.setCACertificate(ctx, + SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()), + SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())); + // Set revocation + SSLContext.setCARevocation(ctx, + SSLHostConfig.adjustRelativePath( + sslHostConfig.getCertificateRevocationListFile()), + SSLHostConfig.adjustRelativePath( + sslHostConfig.getCertificateRevocationListPath())); + // Client certificate verification + switch (sslHostConfig.getCertificateVerification()) { + case NONE: + value = SSL.SSL_CVERIFY_NONE; + break; + case OPTIONAL: + value = SSL.SSL_CVERIFY_OPTIONAL; + break; + case OPTIONAL_NO_CA: + value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA; + break; + case REQUIRED: + value = SSL.SSL_CVERIFY_REQUIRE; + break; + } + SSLContext.setVerify(ctx, value, sslHostConfig.getCertificateVerificationDepth()); + // For now, sendfile is not supported with SSL + if (getUseSendfile()) { + setUseSendfileInternal(false); + if (useSendFileSet) { + log.warn(sm.getString("endpoint.apr.noSendfileWithSSL")); } + } - if (negotiableProtocols.size() > 0) { - byte[] protocols = buildAlpnConfig(negotiableProtocols); - if (SSLContext.setALPN(ctx, protocols, protocols.length) != 0) { - log.warn(sm.getString("endpoint.alpn.fail", negotiableProtocols)); - } + if (negotiableProtocols.size() > 0) { + byte[] protocols = buildAlpnConfig(negotiableProtocols); + if (SSLContext.setALPN(ctx, protocols, protocols.length) != 0) { + log.warn(sm.getString("endpoint.alpn.fail", negotiableProtocols)); } - sslHostConfig.setSslContext(Long.valueOf(ctx)); } + sslHostConfig.setSslContext(Long.valueOf(ctx)); } SSLHostConfig defaultSSLHostConfig = sslHostConfigs.get(getDefaultSSLHostConfigName()); Long defaultSSLContext = (Long) defaultSSLHostConfig.getSslContext(); Modified: tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties?rev=1686497&r1=1686496&r2=1686497&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties Fri Jun 19 20:10:23 2015 @@ -71,6 +71,7 @@ endpoint.apr.pollAddInvalid=Invalid atte endpoint.apr.pollError=Poller failed with error [{0}] : [{1}] endpoint.apr.pollMergeEvents=Merge poller event [{1}] for socket [{0}] to create merged event [{2}] endpoint.apr.pollUnknownEvent=A socket was returned from the poller with an unrecognized event [{0}] +endpoint.apr.tooManyCertFiles=More certificate files were configured than the AprEndpoint can handle endpoint.apr.remoteport=APR socket [{0}] opened with remote port [{1}] endpoint.jsse.noSslContext=No SSLContext could be found for the host name [{0}] endpoint.nio.selectorCloseFail=Failed to close selector when closing the poller Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfig.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfig.java?rev=1686497&r1=1686496&r2=1686497&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfig.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfig.java Fri Jun 19 20:10:23 2015 @@ -86,8 +86,6 @@ public class SSLHostConfig { private String truststoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider"); private String truststoreType = System.getProperty("javax.net.ssl.trustStoreType"); // OpenSSL - private String certificateFile; - private String certificateKeyFile; private String certificateRevocationListPath; private String caCertificateFile; private String caCertificatePath; @@ -496,25 +494,18 @@ public class SSLHostConfig { // ------------------------------- OpenSSL specific configuration properties - public void setCertificateFile(String certificateFile) { - setProperty("certificateFile", Type.OPENSSL); - this.certificateFile = certificateFile; - } - + // TODO: These certificate setters can be removed once it is no longer + // necessary to support the old configuration attributes (Tomcat 10?). - public String getCertificateFile() { - return certificateFile; + public void setCertificateFile(String certificateFile) { + registerDefaultCertificate(); + defaultCertificate.setCertificateFile(certificateFile); } public void setCertificateKeyFile(String certificateKeyFile) { - setProperty("certificateKeyFile", Type.OPENSSL); - this.certificateKeyFile = certificateKeyFile; - } - - - public String getCertificateKeyFile() { - return certificateKeyFile; + registerDefaultCertificate(); + defaultCertificate.setCertificateKeyFile(certificateKeyFile); } Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java?rev=1686497&r1=1686496&r2=1686497&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java Fri Jun 19 20:10:23 2015 @@ -38,6 +38,10 @@ public class SSLHostConfigCertificate { private String certificateKeystoreProvider = DEFAULT_KEYSTORE_PROVIDER; private String certificateKeystoreType = DEFAULT_KEYSTORE_TYPE; + // OpenSSL + private String certificateFile; + private String certificateKeyFile; + public SSLHostConfigCertificate(SSLHostConfig sslHostConfig, Type type) { this.sslHostConfig = sslHostConfig; @@ -126,6 +130,29 @@ public class SSLHostConfigCertificate { // OpenSSL + public void setCertificateFile(String certificateFile) { + sslHostConfig.setProperty( + "Certificate.certificateFile", SSLHostConfig.Type.OPENSSL); + this.certificateFile = certificateFile; + } + + + public String getCertificateFile() { + return certificateFile; + } + + + public void setCertificateKeyFile(String certificateKeyFile) { + sslHostConfig.setProperty( + "Certificate.certificateKeyFile", SSLHostConfig.Type.OPENSSL); + this.certificateKeyFile = certificateKeyFile; + } + + + public String getCertificateKeyFile() { + return certificateKeyFile; + } + // Nested types @@ -133,7 +160,6 @@ public class SSLHostConfigCertificate { UNDEFINED, RSA, DSA, - EC, - DH + ECC } } Modified: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java?rev=1686497&r1=1686496&r2=1686497&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java Fri Jun 19 20:10:23 2015 @@ -114,7 +114,7 @@ public class OpenSSLContext implements o aprPool = Pool.create(0); boolean success = false; try { - if (SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateFile()) == null) { + if (SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()) == null) { // This is required throw new Exception(netSm.getString("endpoint.apr.noSslCertFile")); } @@ -307,8 +307,8 @@ public class OpenSSLContext implements o SSLContext.setCipherSuite(ctx, ciphers); // Load Server key and certificate SSLContext.setCertificate(ctx, - SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateFile()), - SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateKeyFile()), + SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), + SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), certificate.getCertificateKeyPassword(), SSL.SSL_AIDX_RSA); // Support Client Certificates SSLContext.setCACertificate(ctx, Modified: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java?rev=1686497&r1=1686496&r2=1686497&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java Fri Jun 19 20:10:23 2015 @@ -48,8 +48,8 @@ public class OpenSSLUtil implements SSLU @Override public KeyManager[] getKeyManagers() throws Exception { KeyManager[] managers = { - new OpenSSLKeyManager(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateFile()), - SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateKeyFile())) + new OpenSSLKeyManager(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), + SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile())) }; return managers; } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org