diff --git a/core/pva/src/main/java/org/epics/pva/client/ValidationHandler.java b/core/pva/src/main/java/org/epics/pva/client/ValidationHandler.java index 28beda9bf9..6761a9e0b7 100644 --- a/core/pva/src/main/java/org/epics/pva/client/ValidationHandler.java +++ b/core/pva/src/main/java/org/epics/pva/client/ValidationHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 Oak Ridge National Laboratory. + * Copyright (c) 2019-2023 Oak Ridge National Laboratory. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,6 +14,7 @@ import java.util.List; import org.epics.pva.common.CommandHandler; +import org.epics.pva.common.PVAAuth; import org.epics.pva.common.PVAHeader; import org.epics.pva.data.PVASize; import org.epics.pva.data.PVAString; @@ -50,10 +51,11 @@ public void handleCommand(final ClientTCPHandler tcp, final ByteBuffer buffer) t // Support "ca" authorization, fall back to anonymouse final ClientAuthentication authentication; - if (auth.contains("ca")) + if (auth.contains(PVAAuth.CA)) authentication = ClientAuthentication.CA; else authentication = ClientAuthentication.Anonymous; + // TODO PVAAuth.X509 tcp.handleValidationRequest(server_receive_buffer_size, server_introspection_registry_max_size, authentication); diff --git a/core/pva/src/main/java/org/epics/pva/common/PVAAuth.java b/core/pva/src/main/java/org/epics/pva/common/PVAAuth.java index 0d340ff9de..bb93b30477 100644 --- a/core/pva/src/main/java/org/epics/pva/common/PVAAuth.java +++ b/core/pva/src/main/java/org/epics/pva/common/PVAAuth.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 Oak Ridge National Laboratory. + * Copyright (c) 2019-2023 Oak Ridge National Laboratory. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -18,4 +18,7 @@ public class PVAAuth /** CA authentication based on user name and host */ public static String CA = "ca"; + + /**Authentication based on 'Common Name' in certificate */ + public static String X509 = "x509"; } diff --git a/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java b/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java index 979be131ed..148bc2b345 100644 --- a/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java +++ b/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java @@ -39,8 +39,28 @@ abstract class ServerAuth public static ServerAuth decode(final ServerTCPHandler tcp, final ByteBuffer buffer) throws Exception { final String auth = PVAString.decodeString(buffer); + + if (buffer.remaining() < 1) + throw new Exception("Missing authentication detail for '" + auth + "'"); + + final PVATypeRegistry types = tcp.getClientTypes(); + final PVAData type = types.decodeType("", buffer); + PVAStructure info = null; + if (type instanceof PVAStructure) + { + info = (PVAStructure) type; + info.decode(types, buffer); + } + if (PVAAuth.CA.equals(auth)) - return new CAServerAuth(tcp, buffer); + return new CAServerAuth(info); + + if (info != null) + throw new Exception("Expected no authentication detail for '" + auth + "' but got " + info); + + if (PVAAuth.X509.equals(auth)) + return new X509ServerAuth(); + return Anonymous; } @@ -63,20 +83,8 @@ private static class CAServerAuth extends ServerAuth { private String user, host; - public CAServerAuth(final ServerTCPHandler tcp, final ByteBuffer buffer) throws Exception + public CAServerAuth(final PVAStructure info) throws Exception { - final PVATypeRegistry types = tcp.getClientTypes(); - - if (buffer.remaining() < 1) - throw new Exception("Missing 'ca' authentication info"); - - final PVAData data = types.decodeType("", buffer); - if (! (data instanceof PVAStructure)) - throw new Exception("Expected structure for 'ca' authentication info, got " + data); - - final PVAStructure info = (PVAStructure) data; - info.decode(types, buffer); - PVAString element = info.get("user"); if (element == null) throw new Exception("Missing 'ca' authentication 'user', got " + info); @@ -91,9 +99,7 @@ public CAServerAuth(final ServerTCPHandler tcp, final ByteBuffer buffer) throws @Override public boolean hasWriteAccess(final String channel) { - // TODO Implement access security based on `acf` type config file - // if (! channel.contains("demo")) - // return false; + // TODO Implement access security based on `acf` type config file, checking channel for user and host return true; } @@ -103,4 +109,31 @@ public String toString() return "ca(" + user + "@" + host + ")"; } } + + + private static class X509ServerAuth extends ServerAuth + { + private String user, host; + + public X509ServerAuth() throws Exception + { + // TODO Get user from certificate, host from TCP?? + user = "TODO"; + host = "TODO"; + } + + @Override + public boolean hasWriteAccess(final String channel) + { + // TODO Implement access security based on `acf` type config file, checking channel for user and host + return true; + } + + @Override + public String toString() + { + return "x509(" + user + "@" + host + ")"; + } + } + } diff --git a/core/pva/src/main/java/org/epics/pva/server/ServerTCPHandler.java b/core/pva/src/main/java/org/epics/pva/server/ServerTCPHandler.java index 2971c827e0..f54bd8a998 100644 --- a/core/pva/src/main/java/org/epics/pva/server/ServerTCPHandler.java +++ b/core/pva/src/main/java/org/epics/pva/server/ServerTCPHandler.java @@ -14,7 +14,10 @@ import java.nio.ByteBuffer; import java.util.logging.Level; +import javax.net.ssl.SSLSocket; + import org.epics.pva.common.CommandHandlers; +import org.epics.pva.common.PVAAuth; import org.epics.pva.common.PVAHeader; import org.epics.pva.common.RequestEncoder; import org.epics.pva.common.SearchResponse; @@ -78,9 +81,10 @@ public ServerTCPHandler(final PVAServer server, final Socket client) throws Exce submit((version, buffer) -> { logger.log(Level.FINE, () -> "Sending Validation Request"); - PVAHeader.encodeMessageHeader(buffer, - PVAHeader.FLAG_SERVER, - PVAHeader.CMD_CONNECTION_VALIDATION, 4+2+1+PVAString.getEncodedSize("anonymous") + PVAString.getEncodedSize("ca")); + + final int size_offset = buffer.position() + PVAHeader.HEADER_OFFSET_PAYLOAD_SIZE; + PVAHeader.encodeMessageHeader(buffer, PVAHeader.FLAG_SERVER, PVAHeader.CMD_CONNECTION_VALIDATION, 4+2+1); + final int payload_start = buffer.position(); // int serverReceiveBufferSize; buffer.putInt(receive_buffer.capacity()); @@ -88,13 +92,17 @@ public ServerTCPHandler(final PVAServer server, final Socket client) throws Exce // short serverIntrospectionRegistryMaxSize; buffer.putShort(Short.MAX_VALUE); - // string[] authNZ; - PVASize.encodeSize(1, buffer); + // On secure connection server supports "x509" + boolean support_x509 = (client instanceof SSLSocket); + + // string[] authNZ; listing most secure at end + PVASize.encodeSize(support_x509 ? 3 : 2, buffer); + PVAString.encodeString(PVAAuth.ANONYMOUS, buffer); + PVAString.encodeString(PVAAuth.CA, buffer); + if (support_x509) + PVAString.encodeString(PVAAuth.X509, buffer); - // TODO ServerAuthentication - PVAString.encodeString("ca", buffer); - PVAString.encodeString("anonymous", buffer); - // TODO "x509" + buffer.putInt(size_offset, buffer.position() - payload_start); }); }