Skip to content

Deprecate TOFU, add TRUST_ALL and remove E_NON_LOCAL #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 1, 2016
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.neo4j.driver.internal.spi.ConnectionPool;
import org.neo4j.driver.internal.util.Clock;
import org.neo4j.driver.v1.AccessMode;
import org.neo4j.driver.v1.Config;
import org.neo4j.driver.v1.Logging;
import org.neo4j.driver.v1.Session;
import org.neo4j.driver.v1.exceptions.ClientException;
Expand All @@ -34,6 +35,16 @@

public class RoutingDriver extends BaseDriver
{
// Verify that a security plan is compatible with this driver, throwing an exception if not
private static SecurityPlan verifiedSecurityPlan( SecurityPlan securityPlan )
{
if ( !securityPlan.isRoutingCompatible() )
{
throw new IllegalArgumentException( "The chosen security plan is not compatible with a routing driver" );
}
return securityPlan;
}

private final LoadBalancer loadBalancer;

public RoutingDriver(
Expand All @@ -45,7 +56,7 @@ public RoutingDriver(
Clock clock,
Logging logging )
{
super( contract, securityPlan, logging );
super( contract, verifiedSecurityPlan( securityPlan ), logging );
this.loadBalancer = new LoadBalancer( settings, clock, log, connections, seedAddress );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,23 +136,4 @@ public int port()
return port;
}

/**
* Determine whether or not this address refers to the local machine. This
* will generally be true for "localhost" or "127.x.x.x".
*
* @return true if local, false otherwise
*/
public boolean isLocal()
{
try
{
// confirmed to work as desired with both "localhost" and "127.x.x.x"
return InetAddress.getByName( host ).isLoopbackAddress();
}
catch ( UnknownHostException e )
{
// if it's unknown, it's not local so we can safely return false
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ public static ByteChannel create( BoltServerAddress address, SecurityPlan securi

if (securityPlan.requiresEncryption())
{
channel = new TLSSocketChannel( address, securityPlan, soChannel, logger );
channel = TLSSocketChannel.create( address, securityPlan, soChannel, logger );
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@
*/
public class SecurityPlan
{
public static SecurityPlan forSignedCertificates( File certFile )
public static SecurityPlan forAllCertificates() throws GeneralSecurityException, IOException
{
SSLContext sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( new KeyManager[0], new TrustManager[]{new TrustAllTrustManager()}, null );

return new SecurityPlan( true, sslContext, true );
}

public static SecurityPlan forCustomCASignedCertificates( File certFile )
throws GeneralSecurityException, IOException
{
// A certificate file is specified so we will load the certificates in the file
Expand All @@ -59,44 +67,53 @@ public static SecurityPlan forSignedCertificates( File certFile )
SSLContext sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( new KeyManager[0], trustManagerFactory.getTrustManagers(), null );

return new SecurityPlan( true, sslContext);
return new SecurityPlan( true, sslContext, true );
}

public static SecurityPlan forSystemCertificates() throws NoSuchAlgorithmException, KeyStoreException
public static SecurityPlan forSystemCASignedCertificates() throws NoSuchAlgorithmException, KeyStoreException
{
return new SecurityPlan( true, SSLContext.getDefault() );
return new SecurityPlan( true, SSLContext.getDefault(), true );
}


@Deprecated
public static SecurityPlan forTrustOnFirstUse( File knownHosts, BoltServerAddress address, Logger logger )
throws IOException, KeyManagementException, NoSuchAlgorithmException
{
SSLContext sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( new KeyManager[0], new TrustManager[]{new TrustOnFirstUseTrustManager( address, knownHosts, logger )}, null );

return new SecurityPlan( true, sslContext);
return new SecurityPlan( true, sslContext, false );
}

public static SecurityPlan insecure()
{
return new SecurityPlan( false, null );
return new SecurityPlan( false, null, true );
}

private final boolean requiresEncryption;
private final SSLContext sslContext;
private final boolean routingCompatible;

private SecurityPlan( boolean requiresEncryption, SSLContext sslContext)
private SecurityPlan( boolean requiresEncryption, SSLContext sslContext, boolean routingCompatible )
{
this.requiresEncryption = requiresEncryption;
this.sslContext = sslContext;
this.routingCompatible = routingCompatible;
}

public boolean requiresEncryption()
{
return requiresEncryption;
}

public boolean isRoutingCompatible()
{
return routingCompatible;
}

public SSLContext sslContext() {return sslContext;}
public SSLContext sslContext()
{
return sslContext;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.security.GeneralSecurityException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
Expand All @@ -32,9 +29,6 @@
import org.neo4j.driver.internal.net.BoltServerAddress;
import org.neo4j.driver.v1.Logger;
import org.neo4j.driver.internal.util.BytePrinter;
import org.neo4j.driver.internal.util.BytePrinter;
import org.neo4j.driver.v1.Config.TrustStrategy;
import org.neo4j.driver.v1.Logger;
import org.neo4j.driver.v1.exceptions.ClientException;

import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
Expand Down Expand Up @@ -67,32 +61,23 @@ public class TLSSocketChannel implements ByteChannel

private static final ByteBuffer DUMMY_BUFFER = ByteBuffer.allocate( 0 );

public TLSSocketChannel( BoltServerAddress address, SecurityPlan securityPlan, ByteChannel channel, Logger logger )
throws GeneralSecurityException, IOException
{
this( channel, logger, createSSLEngine( address, securityPlan.sslContext() ) );
}

public TLSSocketChannel( ByteChannel channel, Logger logger, SSLEngine sslEngine ) throws GeneralSecurityException, IOException
public static TLSSocketChannel create( BoltServerAddress address, SecurityPlan securityPlan, ByteChannel channel, Logger logger )
throws IOException
{
this(channel, logger, sslEngine,
ByteBuffer.allocate( sslEngine.getSession().getApplicationBufferSize() ),
ByteBuffer.allocate( sslEngine.getSession().getPacketBufferSize() ),
ByteBuffer.allocate( sslEngine.getSession().getApplicationBufferSize() ),
ByteBuffer.allocate( sslEngine.getSession().getPacketBufferSize() ) );
SSLEngine sslEngine = securityPlan.sslContext().createSSLEngine( address.host(), address.port() );
sslEngine.setUseClientMode( true );
return new TLSSocketChannel( channel, logger, sslEngine );
}

TLSSocketChannel( ByteChannel channel, Logger logger, SSLEngine sslEngine,
ByteBuffer plainIn, ByteBuffer cipherIn, ByteBuffer plainOut, ByteBuffer cipherOut )
throws GeneralSecurityException, IOException
public TLSSocketChannel( ByteChannel channel, Logger logger, SSLEngine sslEngine ) throws IOException
{
this.logger = logger;
this.channel = channel;
this.sslEngine = sslEngine;
this.plainIn = plainIn;
this.cipherIn = cipherIn;
this.plainOut = plainOut;
this.cipherOut = cipherOut;
this.plainIn = ByteBuffer.allocate( sslEngine.getSession().getApplicationBufferSize() );
this.cipherIn = ByteBuffer.allocate( sslEngine.getSession().getPacketBufferSize() );
this.plainOut = ByteBuffer.allocate( sslEngine.getSession().getApplicationBufferSize() );
this.cipherOut = ByteBuffer.allocate( sslEngine.getSession().getPacketBufferSize() );
runHandshake();
}

Expand Down Expand Up @@ -353,18 +338,6 @@ static int bufferCopy( ByteBuffer from, ByteBuffer to )
return maxTransfer;
}

/**
* Create SSLEngine with the SSLContext just created.
* @param address the host to connect to
* @param sslContext the current ssl context
*/
private static SSLEngine createSSLEngine( BoltServerAddress address, SSLContext sslContext )
{
SSLEngine sslEngine = sslContext.createSSLEngine( address.host(), address.port() );
sslEngine.setUseClientMode( true );
return sslEngine;
}

@Override
public int read( ByteBuffer dst ) throws IOException
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* 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 org.neo4j.driver.internal.security;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;

public class TrustAllTrustManager implements X509TrustManager
{
public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException
{
throw new CertificateException( "All client connections to this client are forbidden." );
}

public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException
{
// all fine, pass through
}

public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}
36 changes: 27 additions & 9 deletions driver/src/main/java/org/neo4j/driver/v1/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@
import org.neo4j.driver.internal.net.pooling.PoolSettings;
import org.neo4j.driver.v1.util.Immutable;

import static java.lang.System.getProperty;
import static org.neo4j.driver.v1.Config.TrustStrategy.trustOnFirstUse;
import static org.neo4j.driver.v1.Config.TrustStrategy.trustAllCertificates;

/**
* A configuration class to config driver properties.
Expand Down Expand Up @@ -168,9 +167,8 @@ public static class ConfigBuilder
private Logging logging = new JULogging( Level.INFO );
private int maxIdleConnectionPoolSize = PoolSettings.DEFAULT_MAX_IDLE_CONNECTION_POOL_SIZE;
private long idleTimeBeforeConnectionTest = PoolSettings.DEFAULT_IDLE_TIME_BEFORE_CONNECTION_TEST;
private EncryptionLevel encryptionLevel = EncryptionLevel.REQUIRED_NON_LOCAL;
private TrustStrategy trustStrategy = trustOnFirstUse(
new File( getProperty( "user.home" ), ".neo4j" + File.separator + "known_hosts" ) );
private EncryptionLevel encryptionLevel = EncryptionLevel.REQUIRED;
private TrustStrategy trustStrategy = trustAllCertificates();
private RetryLogic retryLogic = RetryLogic.DEFAULT_RETRY_LOGIC;
private int routingFailureLimit = 1;
private long routingRetryDelayMillis = 5_000;
Expand Down Expand Up @@ -371,10 +369,6 @@ public enum EncryptionLevel
/** With this level, the driver will only connect to the server if it can do it without encryption. */
NONE,

/** With this level, the driver will only connect to the server without encryption if local but with
* encryption otherwise. */
REQUIRED_NON_LOCAL,

/** With this level, the driver will only connect to the server it if can do it with encryption. */
REQUIRED
}
Expand All @@ -386,10 +380,16 @@ public static class TrustStrategy
{
public enum Strategy
{
@Deprecated
TRUST_ON_FIRST_USE,

@Deprecated
TRUST_SIGNED_CERTIFICATES,

TRUST_ALL_CERTIFICATES,

TRUST_CUSTOM_CA_SIGNED_CERTIFICATES,

TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
}

Expand Down Expand Up @@ -445,11 +445,26 @@ public static TrustStrategy trustCustomCertificateSignedBy( File certFile )
return new TrustStrategy( Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES, certFile );
}

/**
*
* @return
*/
public static TrustStrategy trustSystemCertificates()
{
return new TrustStrategy( Strategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES );
}

/**
*
* @return
*
* @since 1.1
*/
public static TrustStrategy trustAllCertificates()
{
return new TrustStrategy( Strategy.TRUST_ALL_CERTIFICATES );
}

/**
* Automatically trust a Neo4j instance the first time we see it - but fail to connect if its encryption certificate ever changes.
* This is similar to the mechanism used in SSH, and protects against man-in-the-middle attacks that occur after the initial setup of your application.
Expand All @@ -463,7 +478,10 @@ public static TrustStrategy trustSystemCertificates()
*
* @param knownHostsFile a file where known certificates are stored.
* @return an authentication config
*
* @deprecated in 1.1 in favour of {@link #trustAllCertificates()}
*/
@Deprecated
public static TrustStrategy trustOnFirstUse( File knownHostsFile )
{
return new TrustStrategy( Strategy.TRUST_ON_FIRST_USE, knownHostsFile );
Expand Down
Loading