Skip to content

Commit

Permalink
#11996 deny connection creation for clients missing needed cert
Browse files Browse the repository at this point in the history
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
  • Loading branch information
lorban committed Jul 8, 2024
1 parent 7f77bc6 commit e04b66d
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ public long newStreamId(StreamType streamType)
return getQuicSession().newStreamId(streamType);
}

@Override
public void disconnect(long code, String reason)
{
super.disconnect(code, reason);
session.getListener().onDisconnect(session, code, reason);
}

@Override
protected void onStart()
{
Expand Down
21 changes: 21 additions & 0 deletions jetty-core/jetty-http3/jetty-http3-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,25 @@
</dependency>
</dependencies>

<profiles>
<profile>
<id>enable-foreign</id>
<activation>
<jdk>[22,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}
${jetty.surefire.argLine}
--enable-native-access=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -63,34 +63,41 @@ public class AbstractClientServerTest

protected void start(Handler handler) throws Exception
{
ServerQuicConfiguration quicConfiguration = newServerQuicConfiguration();
ServerQuicConfiguration quicConfiguration = newServerQuicConfiguration(false);
prepareServer(quicConfiguration, new HTTP3ServerConnectionFactory(quicConfiguration));
server.setHandler(handler);
server.start();
startClient();
}

private ServerQuicConfiguration newServerQuicConfiguration()
protected void start(Session.Server.Listener listener) throws Exception
{
SslContextFactory.Server sslServer = new SslContextFactory.Server();
sslServer.setKeyStorePath("src/test/resources/keystore.p12");
sslServer.setKeyStorePassword("storepwd");
return new ServerQuicConfiguration(sslServer, workDir.getEmptyPathDir());
startServer(false, listener);
startClient();
}

protected void start(Session.Server.Listener listener) throws Exception
protected void start(boolean needClientAuth, Session.Server.Listener listener) throws Exception
{
startServer(listener);
startServer(needClientAuth, listener);
startClient();
}

protected void startServer(Session.Server.Listener listener) throws Exception
private void startServer(boolean needClientAuth, Session.Server.Listener listener) throws Exception
{
ServerQuicConfiguration quicConfiguration = newServerQuicConfiguration();
ServerQuicConfiguration quicConfiguration = newServerQuicConfiguration(needClientAuth);
prepareServer(quicConfiguration, new RawHTTP3ServerConnectionFactory(quicConfiguration, listener));
server.start();
}

private ServerQuicConfiguration newServerQuicConfiguration(boolean needClientAuth)
{
SslContextFactory.Server sslServer = new SslContextFactory.Server();
sslServer.setNeedClientAuth(needClientAuth);
sslServer.setKeyStorePath("src/test/resources/keystore.p12");
sslServer.setKeyStorePassword("storepwd");
return new ServerQuicConfiguration(sslServer, workDir.getEmptyPathDir());
}

private void prepareServer(ServerQuicConfiguration quicConfiguration, ConnectionFactory serverConnectionFactory)
{
QueuedThreadPool serverThreads = new QueuedThreadPool();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@

package org.eclipse.jetty.http3.tests;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

Expand All @@ -39,6 +41,7 @@
import org.eclipse.jetty.http3.server.internal.HTTP3SessionServer;
import org.eclipse.jetty.quic.client.ClientQuicSession;
import org.eclipse.jetty.quic.common.QuicSession;
import org.eclipse.jetty.quic.server.ServerQuicSession;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -47,9 +50,11 @@
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

public class ClientServerTest extends AbstractClientServerTest
{
Expand All @@ -58,7 +63,7 @@ public void testConnectTriggersSettingsFrame() throws Exception
{
CountDownLatch serverPrefaceLatch = new CountDownLatch(1);
CountDownLatch serverSettingsLatch = new CountDownLatch(1);
start(new Session.Server.Listener()
start(true, new Session.Server.Listener()
{
@Override
public Map<Long, Long> onPreface(Session session)
Expand Down Expand Up @@ -640,4 +645,40 @@ public void onResponse(Stream.Client stream, HeadersFrame frame)

assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
}

@Test
public void testMissingNeededClientCertDeniesConnection() throws Exception
{
AtomicReference<HTTP3SessionServer> serverSessionRef = new AtomicReference<>();
CountDownLatch serverDisconnectLatch = new CountDownLatch(1);
start(true, new Session.Server.Listener()
{
@Override
public void onDisconnect(Session session, long error, String reason)
{
serverSessionRef.set((HTTP3SessionServer)session);
serverDisconnectLatch.countDown();
}
});

try
{
newSession(new Session.Client.Listener() {});
fail("expected ExecutionException");
}
catch (ExecutionException ex)
{
assertInstanceOf(IOException.class, ex.getCause());
}

assertTrue(serverDisconnectLatch.await(5, TimeUnit.SECONDS));

HTTP3SessionServer serverSession = serverSessionRef.get();
assertTrue(serverSession.isClosed());
assertTrue(serverSession.getStreams().isEmpty());
ServerQuicSession serverQuicSession = serverSession.getProtocolSession().getQuicSession();
// While HTTP/3 is completely closed, QUIC may still be exchanging packets, so we need to await().
await().atMost(3, TimeUnit.SECONDS).until(() -> serverQuicSession.getQuicStreamEndPoints().isEmpty());
await().atMost(3, TimeUnit.SECONDS).until(() -> serverQuicSession.getQuicConnection().getQuicSessions().isEmpty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ public QuicStreamEndPoint getOrCreateStreamEndPoint(long streamId, Consumer<Quic
return session.getOrCreateStreamEndPoint(streamId, consumer);
}

public void disconnect(long code, String reason)
{
session.outwardClose(code, reason);
}

protected void processWritableStreams()
{
List<Long> writableStreamIds = session.getWritableStreamIds();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
import org.eclipse.jetty.io.CyclicTimeouts;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RetainableByteBuffer;
import org.eclipse.jetty.quic.common.ProtocolSession;
import org.eclipse.jetty.quic.common.QuicConfiguration;
import org.eclipse.jetty.quic.common.QuicConnection;
import org.eclipse.jetty.quic.common.QuicErrorCode;
import org.eclipse.jetty.quic.common.QuicSession;
import org.eclipse.jetty.quic.quiche.QuicheConfig;
import org.eclipse.jetty.quic.quiche.QuicheConnection;
Expand Down Expand Up @@ -114,6 +116,20 @@ protected QuicSession createSession(SocketAddress remoteAddress, ByteBuffer ciph
}
else
{
if (quicConfiguration.getSslContextFactory().getNeedClientAuth())
{
byte[] peerCertificate = quicheConnection.getPeerCertificate();
if (peerCertificate == null)
{
ServerQuicSession session = newQuicSession(remoteAddress, quicheConnection);
ProtocolSession protocolSession = session.createProtocolSession();
protocolSession.disconnect(QuicErrorCode.CONNECTION_REFUSED.code(), "missing_client_cert");
// Send the response packet(s) that disconnect() generated.
session.flush();
return null;
}
}

ServerQuicSession session = newQuicSession(remoteAddress, quicheConnection);
// Send the response packet(s) that tryAccept() generated.
session.flush();
Expand Down

0 comments on commit e04b66d

Please sign in to comment.