luigidemasi commented on code in PR #27821:
URL: https://github.com/apache/camel/pull/27821#discussion_r4269349899


##########
components/camel-ssh/src/main/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifier.java:
##########
@@ -62,22 +71,53 @@ public ResourceBasedSSHKeyVerifier(CamelContext 
camelContext, String knownHostsR
     @Override
     public boolean verifyServerKey(ClientSession sshClientSession, 
SocketAddress remoteAddress, PublicKey serverKey) {
         log.debug("Trying to find known_hosts file {}", knownHostsResource);
+        List<KnownHostEntry> entries = Collections.emptyList();
         InputStream knownHostsInputStream = null;
         try {
             knownHostsInputStream = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext,
                     knownHostsResource);
-            List<String> possibleTokens = 
getKnownHostsFileTokensForSocketAddress(remoteAddress);
-            log.debug("Trying to match PublicKey against provided known_hosts 
file");
-            PublicKey matchingKey = 
findKeyForServerToken(knownHostsInputStream, possibleTokens);
-            if (matchingKey != null) {
-                log.debug("Found PublicKey match for server");
-                return Arrays.areEqual(matchingKey.getEncoded(), 
serverKey.getEncoded());
-            }
+            entries = 
KnownHostEntry.readKnownHostEntries(knownHostsInputStream, false);

Review Comment:
   **Critical / P1: Do not turn a discarded known-host entry into permission to 
connect.**
   
   With the bundled MINA SSHD 2.20.0, this reader silently discards a valid 
OpenSSH entry whose host field is:
   
   ```text
   ![excluded.example.com]:2222,[db.example.com]:2222
   ```
   
   MINA's pattern parser handles a leading bracket before it handles negation, 
so the negated port-qualified pattern makes the entire line invalid to that 
parser. OpenSSH's `ssh-keygen -F '[db.example.com]:2222'` recognizes the same 
entry.
   
   Consequently, a server that is actually listed is treated as unknown. With 
the default `failOnUnknownHost=false`, an arbitrary different key is accepted. 
I reproduced this through a real loopback SSH connection: the server presenting 
the unlisted key received the synthetic password. The same probe against the 
preceding verifier rejects the key before password authentication. With 
`failOnUnknownHost=true`, the legitimate listed key is instead rejected.
   
   Please support the valid negated port-qualified syntax and ensure parser 
failures cannot silently remove the host's pinning constraint and activate 
permissive unknown-host handling. Add regression tests for the legitimate key 
and a different key with both values of `failOnUnknownHost`.
   
   _AI-generated review by Codex on behalf of @luigidemasi._



##########
components/camel-ssh/src/main/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifier.java:
##########
@@ -62,22 +71,53 @@ public ResourceBasedSSHKeyVerifier(CamelContext 
camelContext, String knownHostsR
     @Override
     public boolean verifyServerKey(ClientSession sshClientSession, 
SocketAddress remoteAddress, PublicKey serverKey) {
         log.debug("Trying to find known_hosts file {}", knownHostsResource);
+        List<KnownHostEntry> entries = Collections.emptyList();
         InputStream knownHostsInputStream = null;
         try {
             knownHostsInputStream = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext,
                     knownHostsResource);
-            List<String> possibleTokens = 
getKnownHostsFileTokensForSocketAddress(remoteAddress);
-            log.debug("Trying to match PublicKey against provided known_hosts 
file");
-            PublicKey matchingKey = 
findKeyForServerToken(knownHostsInputStream, possibleTokens);
-            if (matchingKey != null) {
-                log.debug("Found PublicKey match for server");
-                return Arrays.areEqual(matchingKey.getEncoded(), 
serverKey.getEncoded());
-            }
+            entries = 
KnownHostEntry.readKnownHostEntries(knownHostsInputStream, false);
         } catch (IOException ioException) {
             log.debug(String.format("Could not find known_hosts file %s", 
knownHostsResource), ioException);
         } finally {
             IOHelper.close(knownHostsInputStream);
         }
+
+        SshdSocketAddress server = toServerAddress(remoteAddress);
+        boolean hostListed = false;
+        boolean keyListed = false;
+        if (server != null) {
+            log.debug("Trying to match PublicKey of {} against provided 
known_hosts file", server);
+            for (KnownHostEntry entry : entries) {
+                if (!entry.isHostMatch(server.getHostName(), server.getPort())

Review Comment:
   **Critical / P1: Enforce wildcard revocation on nondefault ports.**
   
   `KnownHostEntry.isHostMatch(host, port)` treats an unqualified pattern as 
port 22, including `*`. For a file containing:
   
   ```text
   [db.example.com]:2222 <key-type> <key-data>
   @revoked * <same-key-type> <same-key-data>
   ```
   
   the normal entry matches but the revocation is skipped. The verifier accepts 
the revoked key even with `failOnUnknownHost=true`. A real loopback SSH 
connection using this arrangement authenticated and delivered the synthetic 
password.
   
   OpenSSH recognizes the wildcard revocation for the port-qualified host; I 
confirmed this with `ssh-keygen -F`, and its [known_hosts 
specification](https://man.openbsd.org/sshd.8#SSH_KNOWN_HOSTS_FILE_FORMAT) 
documents the `@revoked *` form. This is a gap in the revocation support 
introduced by this PR; the preceding verifier did not implement revocation.
   
   Please make wildcard matching preserve the OpenSSH revocation semantics for 
nondefault ports. Add tests for ports 22 and 2222, both values of 
`failOnUnknownHost`, and revocation before and after an otherwise accepted 
entry.
   
   _AI-generated review by Codex on behalf of @luigidemasi._



##########
components/camel-ssh/src/main/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifier.java:
##########
@@ -62,22 +71,53 @@ public ResourceBasedSSHKeyVerifier(CamelContext 
camelContext, String knownHostsR
     @Override
     public boolean verifyServerKey(ClientSession sshClientSession, 
SocketAddress remoteAddress, PublicKey serverKey) {
         log.debug("Trying to find known_hosts file {}", knownHostsResource);
+        List<KnownHostEntry> entries = Collections.emptyList();
         InputStream knownHostsInputStream = null;
         try {
             knownHostsInputStream = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext,
                     knownHostsResource);
-            List<String> possibleTokens = 
getKnownHostsFileTokensForSocketAddress(remoteAddress);
-            log.debug("Trying to match PublicKey against provided known_hosts 
file");
-            PublicKey matchingKey = 
findKeyForServerToken(knownHostsInputStream, possibleTokens);
-            if (matchingKey != null) {
-                log.debug("Found PublicKey match for server");
-                return Arrays.areEqual(matchingKey.getEncoded(), 
serverKey.getEncoded());
-            }
+            entries = 
KnownHostEntry.readKnownHostEntries(knownHostsInputStream, false);
         } catch (IOException ioException) {
             log.debug(String.format("Could not find known_hosts file %s", 
knownHostsResource), ioException);
         } finally {
             IOHelper.close(knownHostsInputStream);
         }
+
+        SshdSocketAddress server = toServerAddress(remoteAddress);
+        boolean hostListed = false;
+        boolean keyListed = false;
+        if (server != null) {
+            log.debug("Trying to match PublicKey of {} against provided 
known_hosts file", server);
+            for (KnownHostEntry entry : entries) {
+                if (!entry.isHostMatch(server.getHostName(), server.getPort())
+                        || MARKER_CERT_AUTHORITY.equals(entry.getMarker())) {
+                    continue;
+                }
+                boolean sameKey = isSameKey(sshClientSession, entry, 
serverKey);
+                if (MARKER_REVOKED.equals(entry.getMarker())) {
+                    if (sameKey) {
+                        log.warn("The key presented by {} is marked as revoked 
in the known_hosts file {}", server,
+                                knownHostsResource);
+                        return false;
+                    }
+                } else {

Review Comment:
   **Required / P2: Reject unsupported markers instead of treating them as 
ordinary trust entries.**
   
   This `else` also handles every unrecognized marker. A file containing only 
`@unknown db.example.com <key-type> <key-data>` accepts that key with 
`failOnUnknownHost=true`. I reproduced this with the current verifier; the 
preceding implementation rejects it, and OpenSSH does not recognize it as a 
trusted entry.
   
   The OpenSSH format permits only `@revoked` and `@cert-authority` markers. A 
malformed or unsupported marker must not grant ordinary host-key trust. Please 
explicitly distinguish unmarked entries from unsupported markers and reject 
unsupported markers without falling through to permissive unknown-host 
acceptance. Add negative tests with both values of `failOnUnknownHost`.
   
   _AI-generated review by Codex on behalf of @luigidemasi._



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to