Deny HTTP/3 connection creation for clients missing cert when needClientAuth is true (#12014)

#11996 deny connection creation for clients missing needed cert

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2024-07-10 23:17:14 +02:00 committed by GitHub
parent 8f5207e6ca
commit 17c8a76efb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 79 additions and 3 deletions

View File

@ -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>

View File

@ -38,6 +38,7 @@ import org.eclipse.jetty.http3.frames.SettingsFrame;
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
import org.eclipse.jetty.http3.server.internal.HTTP3SessionServer;
import org.eclipse.jetty.quic.client.ClientQuicSession;
import org.eclipse.jetty.quic.common.QuicErrorCode;
import org.eclipse.jetty.quic.common.QuicSession;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@ -640,4 +641,24 @@ public class ClientServerTest extends AbstractClientServerTest
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testMissingNeededClientCertificateDeniesConnection() throws Exception
{
start(new Session.Server.Listener() {});
connector.getQuicConfiguration().getSslContextFactory().setNeedClientAuth(true);
CountDownLatch latch = new CountDownLatch(1);
newSession(new Session.Client.Listener()
{
@Override
public void onDisconnect(Session session, long error, String reason)
{
assertEquals(QuicErrorCode.CONNECTION_REFUSED.code(), error);
assertEquals("missing_client_certificate_chain", reason);
latch.countDown();
}
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
}

View File

@ -73,7 +73,7 @@
<configuration>
<argLine>@{argLine}
${jetty.surefire.argLine}
--enable-native-access org.eclipse.jetty.quic.quiche.foreign</argLine>
--enable-native-access=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>

View File

@ -74,6 +74,12 @@ public class ClientQuicSession extends QuicSession
return new ClientProtocolSession(this);
}
@Override
protected boolean validateNewlyEstablishedConnection()
{
return true;
}
@Override
public Connection newConnection(QuicStreamEndPoint endPoint)
{

View File

@ -319,6 +319,9 @@ public abstract class QuicSession extends ContainerLifeCycle
ProtocolSession protocol = protocolSession;
if (protocol == null)
{
if (!validateNewlyEstablishedConnection())
return null;
protocolSession = protocol = createProtocolSession();
addManaged(protocol);
}
@ -343,6 +346,11 @@ public abstract class QuicSession extends ContainerLifeCycle
protected abstract ProtocolSession createProtocolSession();
/**
* @return true if the connection is valid, false otherwise.
*/
protected abstract boolean validateNewlyEstablishedConnection();
List<Long> getWritableStreamIds()
{
return quicheConnection.writableStreamIds();

View File

@ -80,6 +80,9 @@ public class PemExporter
try (OutputStream os = Files.newOutputStream(paths[1]))
{
Certificate[] certChain = keyStore.getCertificateChain(alias);
if (certChain == null)
throw new IllegalArgumentException("Alias does not exist in key store: " + alias);
for (Certificate cert : certChain)
writeAsPEM(os, cert);
Files.setPosixFilePermissions(paths[1], Set.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE));

View File

@ -71,6 +71,11 @@ public class ServerQuicConnection extends QuicConnection
return connector;
}
ServerQuicConfiguration getQuicConfiguration()
{
return quicConfiguration;
}
@Override
public void onOpen()
{

View File

@ -25,7 +25,7 @@ import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.CyclicTimeouts;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.quic.common.ProtocolSession;
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.common.QuicStreamEndPoint;
import org.eclipse.jetty.quic.quiche.QuicheConnection;
@ -46,7 +46,7 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp
private final Connector connector;
private long expireNanoTime = Long.MAX_VALUE;
public ServerQuicSession(Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress, Connector connector)
public ServerQuicSession(Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, QuicheConnection quicheConnection, ServerQuicConnection connection, SocketAddress remoteAddress, Connector connector)
{
super(executor, scheduler, bufferPool, quicheConnection, connection, remoteAddress);
this.connector = connector;
@ -67,6 +67,18 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp
return new ServerProtocolSession(this);
}
@Override
protected boolean validateNewlyEstablishedConnection()
{
if (getQuicConnection().getQuicConfiguration().getSslContextFactory().getNeedClientAuth() && getPeerCertificates() == null)
{
outwardClose(QuicErrorCode.CONNECTION_REFUSED.code(), "missing_client_certificate_chain");
flush();
return false;
}
return true;
}
@Override
public Connection newConnection(QuicStreamEndPoint endPoint)
{