/* * Copyright (C) 2012-2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.nifty.ssl; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import org.apache.tomcat.jni.SSL; import org.apache.tomcat.jni.SessionTicketKey; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import java.io.File; import java.util.Arrays; public class OpenSslServerConfiguration extends SslServerConfiguration { public enum SSLVersion { TLS(SSL.SSL_PROTOCOL_TLS), // Server will accept all TLS versions TLS1_2(SSL.SSL_PROTOCOL_TLSV1_2); // Server will accept only TLS1.2. private final int id; SSLVersion(int id) { this.id = id; } public int getValue() { return id; } }; public enum SSLVerification { VERIFY_NONE(SSL.SSL_CVERIFY_NONE), // No client Certificate is required VERIFY_OPTIONAL(SSL.SSL_CVERIFY_OPTIONAL), // The client may present a valid Certificate VERIFY_REQUIRE(SSL.SSL_CVERIFY_REQUIRE), // The client has to present a valid Certificate VERIFY_OPTIONAL_NO_CA(SSL.SSL_CVERIFY_OPTIONAL_NO_CA); // The client's cert does not need to be verifiable. private final int id; SSLVerification(int id) { this.id = id; } public int getValue() { return id; } }; public static class Builder extends SslServerConfiguration.BuilderBase<Builder> { // Note: when adding new fields, make sure to update the initFromConfiguration() method below. public SessionTicketKey[] ticketKeys; // A string that can be used to separate tickets from different entities. public String sessionContext = "thrift"; public long sessionTimeoutSeconds = 86400; public long sessionCacheSize = 0; public boolean enableStatefulSessionCache = true; public int maxSslBufferBytes = 19267584; public boolean preallocateSslBuffer = true; public boolean threadLocalSslBuffer = false; public SSLVersion sslVersion = SSLVersion.TLS1_2; public Iterable<String> nextProtocols = ImmutableList.of("thrift"); public File clientCAFile; public SSLVerification sslVerification = SSLVerification.VERIFY_OPTIONAL; public Builder() { this.ciphers = SslDefaults.SERVER_DEFAULTS; } /** * Multiple session tickets can be set to allow for ticket rotation. * The first key is the active key used to encrypt tickets for new sessions. * Other ticket keys can be used to decrypt tickets and are not active keys. */ public Builder ticketKeys(SessionTicketKey[] ticketKeys) { this.ticketKeys = ticketKeys == null ? null : Arrays.copyOf(ticketKeys, ticketKeys.length); return this; } /** * Sets the next protocols which will be set to both ALPN as well as NPN. */ public Builder nextProtocols(Iterable<String> nextProtocols) { this.nextProtocols = ImmutableList.copyOf(nextProtocols); return this; } /** * Can be used to separate the tickets issued from different services * generated with the same key. */ public Builder sessionContext(String sessionContext) { this.sessionContext = sessionContext; return this; } public Builder sessionTimeoutSeconds(long sessionTimeoutSeconds) { this.sessionTimeoutSeconds = sessionTimeoutSeconds; return this; } public Builder sessionCacheSize(long sessionCacheSize) { this.sessionCacheSize = sessionCacheSize; return this; } public Builder sslVersion(SSLVersion sslVersion) { this.sslVersion = sslVersion; return this; } public Builder enableStatefulSessionCache(boolean enabled) { this.enableStatefulSessionCache = enabled; return this; } public Builder maxSslBufferBytes(int maxSslBufferBytes) { this.maxSslBufferBytes = maxSslBufferBytes; return this; } public Builder preallocateSslBuffer(boolean preallocateSslBuffer) { this.preallocateSslBuffer = preallocateSslBuffer; return this; } public Builder threadLocalSslBuffer(boolean threadLocalSslBuffer) { this.threadLocalSslBuffer = threadLocalSslBuffer; return this; } /** * Copies the state of an existing configration into this builder. * @param config the SSL configuration. * @return this builder. */ @Override public Builder initFromConfiguration(SslServerConfiguration config) { super.initFromConfiguration(config); if (config instanceof OpenSslServerConfiguration) { OpenSslServerConfiguration openSslConfig = (OpenSslServerConfiguration) config; ticketKeys(openSslConfig.ticketKeys); sessionContext(new String(openSslConfig.sessionContext)); sessionTimeoutSeconds(openSslConfig.sessionTimeoutSeconds); sessionCacheSize(openSslConfig.sessionCacheSize); enableStatefulSessionCache(openSslConfig.enableStatefulSessionCache); maxSslBufferBytes(openSslConfig.maxSslBufferBytes); preallocateSslBuffer(openSslConfig.preallocateSslBuffer); threadLocalSslBuffer(openSslConfig.threadLocalSslBuffer); sslVersion(openSslConfig.sslVersion); nextProtocols(openSslConfig.nextProtocols); clientCAFile(openSslConfig.clientCAFile); sslVerification(openSslConfig.sslVerification); } else { throw new IllegalArgumentException("Provided configuration is not an OpenSslServerConfiguration"); } return this; } public Builder clientCAFile(File clientCAFile) { this.clientCAFile = clientCAFile; return this; } public Builder sslVerification(SSLVerification sslVerification) { this.sslVerification = sslVerification; return this; } @Override protected SslServerConfiguration createServerConfiguration() { OpenSslServerConfiguration sslServerConfiguration = new OpenSslServerConfiguration(this); sslServerConfiguration.initializeServerContext(); return sslServerConfiguration; } } public final SessionTicketKey[] ticketKeys; // A string that can be used to separate tickets from different entities. public final byte[] sessionContext; public final long sessionTimeoutSeconds; public final long sessionCacheSize; public final boolean enableStatefulSessionCache; public final int maxSslBufferBytes; public final boolean preallocateSslBuffer; public final boolean threadLocalSslBuffer; public final SSLVersion sslVersion; public final Iterable<String> nextProtocols; public final File clientCAFile; public final SSLVerification sslVerification; private OpenSslServerConfiguration(Builder builder) { super(builder); this.ticketKeys = builder.ticketKeys; this.sessionContext = builder.sessionContext.getBytes(); this.sessionTimeoutSeconds = builder.sessionTimeoutSeconds; this.sessionCacheSize = builder.sessionCacheSize; this.enableStatefulSessionCache = builder.enableStatefulSessionCache; this.maxSslBufferBytes = builder.maxSslBufferBytes; this.preallocateSslBuffer = builder.preallocateSslBuffer; this.threadLocalSslBuffer = builder.threadLocalSslBuffer; this.sslVersion = builder.sslVersion; this.nextProtocols = builder.nextProtocols; this.clientCAFile = builder.clientCAFile; this.sslVerification = builder.sslVerification; } public static OpenSslServerConfiguration.Builder newBuilder() { return new OpenSslServerConfiguration.Builder(); } @Override protected SslHandlerFactory createSslHandlerFactory() { NettyTcNativeLoader.ensureAvailable(); try { NiftyOpenSslServerContext serverContext = new NiftyOpenSslServerContext(this); if (this.ticketKeys != null) { serverContext.setTicketKeys(this.ticketKeys); } serverContext.setSessionIdContext(this.sessionContext); serverContext.setSessionCacheTimeout(this.sessionTimeoutSeconds); return serverContext; } catch (Exception e) { throw Throwables.propagate(e); } } @Override public SslSession getSession(SSLEngine engine) throws SSLException { return OpenSslSessionHelper.getSession(engine); } }