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:
parent
8f5207e6ca
commit
17c8a76efb
|
@ -66,4 +66,25 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</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>
|
</project>
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.eclipse.jetty.http3.frames.SettingsFrame;
|
||||||
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
|
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
|
||||||
import org.eclipse.jetty.http3.server.internal.HTTP3SessionServer;
|
import org.eclipse.jetty.http3.server.internal.HTTP3SessionServer;
|
||||||
import org.eclipse.jetty.quic.client.ClientQuicSession;
|
import org.eclipse.jetty.quic.client.ClientQuicSession;
|
||||||
|
import org.eclipse.jetty.quic.common.QuicErrorCode;
|
||||||
import org.eclipse.jetty.quic.common.QuicSession;
|
import org.eclipse.jetty.quic.common.QuicSession;
|
||||||
import org.junit.jupiter.api.Tag;
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -640,4 +641,24 @@ public class ClientServerTest extends AbstractClientServerTest
|
||||||
|
|
||||||
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>@{argLine}
|
<argLine>@{argLine}
|
||||||
${jetty.surefire.argLine}
|
${jetty.surefire.argLine}
|
||||||
--enable-native-access org.eclipse.jetty.quic.quiche.foreign</argLine>
|
--enable-native-access=ALL-UNNAMED</argLine>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|
|
@ -74,6 +74,12 @@ public class ClientQuicSession extends QuicSession
|
||||||
return new ClientProtocolSession(this);
|
return new ClientProtocolSession(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean validateNewlyEstablishedConnection()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection newConnection(QuicStreamEndPoint endPoint)
|
public Connection newConnection(QuicStreamEndPoint endPoint)
|
||||||
{
|
{
|
||||||
|
|
|
@ -319,6 +319,9 @@ public abstract class QuicSession extends ContainerLifeCycle
|
||||||
ProtocolSession protocol = protocolSession;
|
ProtocolSession protocol = protocolSession;
|
||||||
if (protocol == null)
|
if (protocol == null)
|
||||||
{
|
{
|
||||||
|
if (!validateNewlyEstablishedConnection())
|
||||||
|
return null;
|
||||||
|
|
||||||
protocolSession = protocol = createProtocolSession();
|
protocolSession = protocol = createProtocolSession();
|
||||||
addManaged(protocol);
|
addManaged(protocol);
|
||||||
}
|
}
|
||||||
|
@ -343,6 +346,11 @@ public abstract class QuicSession extends ContainerLifeCycle
|
||||||
|
|
||||||
protected abstract ProtocolSession createProtocolSession();
|
protected abstract ProtocolSession createProtocolSession();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the connection is valid, false otherwise.
|
||||||
|
*/
|
||||||
|
protected abstract boolean validateNewlyEstablishedConnection();
|
||||||
|
|
||||||
List<Long> getWritableStreamIds()
|
List<Long> getWritableStreamIds()
|
||||||
{
|
{
|
||||||
return quicheConnection.writableStreamIds();
|
return quicheConnection.writableStreamIds();
|
||||||
|
|
|
@ -80,6 +80,9 @@ public class PemExporter
|
||||||
try (OutputStream os = Files.newOutputStream(paths[1]))
|
try (OutputStream os = Files.newOutputStream(paths[1]))
|
||||||
{
|
{
|
||||||
Certificate[] certChain = keyStore.getCertificateChain(alias);
|
Certificate[] certChain = keyStore.getCertificateChain(alias);
|
||||||
|
if (certChain == null)
|
||||||
|
throw new IllegalArgumentException("Alias does not exist in key store: " + alias);
|
||||||
|
|
||||||
for (Certificate cert : certChain)
|
for (Certificate cert : certChain)
|
||||||
writeAsPEM(os, cert);
|
writeAsPEM(os, cert);
|
||||||
Files.setPosixFilePermissions(paths[1], Set.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE));
|
Files.setPosixFilePermissions(paths[1], Set.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE));
|
||||||
|
|
|
@ -71,6 +71,11 @@ public class ServerQuicConnection extends QuicConnection
|
||||||
return connector;
|
return connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerQuicConfiguration getQuicConfiguration()
|
||||||
|
{
|
||||||
|
return quicConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen()
|
public void onOpen()
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.CyclicTimeouts;
|
import org.eclipse.jetty.io.CyclicTimeouts;
|
||||||
import org.eclipse.jetty.io.RuntimeIOException;
|
import org.eclipse.jetty.io.RuntimeIOException;
|
||||||
import org.eclipse.jetty.quic.common.ProtocolSession;
|
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.QuicSession;
|
||||||
import org.eclipse.jetty.quic.common.QuicStreamEndPoint;
|
import org.eclipse.jetty.quic.common.QuicStreamEndPoint;
|
||||||
import org.eclipse.jetty.quic.quiche.QuicheConnection;
|
import org.eclipse.jetty.quic.quiche.QuicheConnection;
|
||||||
|
@ -46,7 +46,7 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp
|
||||||
private final Connector connector;
|
private final Connector connector;
|
||||||
private long expireNanoTime = Long.MAX_VALUE;
|
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);
|
super(executor, scheduler, bufferPool, quicheConnection, connection, remoteAddress);
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
|
@ -67,6 +67,18 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp
|
||||||
return new ServerProtocolSession(this);
|
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
|
@Override
|
||||||
public Connection newConnection(QuicStreamEndPoint endPoint)
|
public Connection newConnection(QuicStreamEndPoint endPoint)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue