Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/com/jcraft/jsch/HostKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ boolean isWildcardMatched(String _host) {
* @param hostname the hostname to test (e.g., {@code host.example.com})
* @return {@code true} if the hostname matches the pattern; {@code false} otherwise
*/
private boolean matchesWildcardPattern(String pattern, String hostname) {
static boolean matchesWildcardPattern(String pattern, String hostname) {
if (pattern == null || hostname == null) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* fails any other validation check. Throws specific subclasses of {@link JSchException}
* for different failure reasons.
*/
static void checkHostCertificate(Session session, OpenSshCertificate certificate)

Check failure on line 37 in src/main/java/com/jcraft/jsch/OpenSshCertificateHostKeyVerifier.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=mwiede_jsch&issues=AZ_Rn3pAVUAxMOfFlWiw&open=AZ_Rn3pAVUAxMOfFlWiw&pullRequest=1108
throws JSchException {

byte[] caPublicKeyByteArray = certificate.getSignatureKey();
Expand Down Expand Up @@ -107,7 +107,15 @@
// Convert to lowercase for principal matching (same as OpenSSH ssh_login())
principalName = principalName.toLowerCase(Locale.ROOT);

if (!principals.contains(principalName)) {
boolean principalMatched = false;
for (String principal : principals) {
if (HostKey.matchesWildcardPattern(principal, principalName)) {
principalMatched = true;
break;
}
}

if (!principalMatched) {
throw new JSchException("rejected HostKey: invalid principal '" + principalName
+ "', allowed principals: " + principals);
}
Expand Down
73 changes: 73 additions & 0 deletions src/test/java/com/jcraft/jsch/HostKeyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,77 @@
assertFalse(hostKey.isWildcardMatched("[host.example.com]:22"),
"Should not match different port");
}

@Test
public void testMatchesWildcardPattern_openSshVectors() {

Check warning on line 278 in src/test/java/com/jcraft/jsch/HostKeyTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce the number of assertions from 69 to less than 25.

See more on https://sonarcloud.io/project/issues?id=mwiede_jsch&issues=AaBIY2zpw585KMrT8Q5V&open=AaBIY2zpw585KMrT8Q5V&pullRequest=1108
assertTrue(HostKey.matchesWildcardPattern("*", ""));
assertTrue(HostKey.matchesWildcardPattern("**", ""));
assertTrue(HostKey.matchesWildcardPattern("***", ""));
assertFalse(HostKey.matchesWildcardPattern("?", ""));
assertFalse(HostKey.matchesWildcardPattern("*?", ""));
assertFalse(HostKey.matchesWildcardPattern("?*", ""));
assertFalse(HostKey.matchesWildcardPattern("**a*", ""));
assertTrue(HostKey.matchesWildcardPattern("?", "a"));
assertTrue(HostKey.matchesWildcardPattern("a?", "aa"));
assertTrue(HostKey.matchesWildcardPattern("*", "a"));
assertTrue(HostKey.matchesWildcardPattern("a*", "aa"));
assertTrue(HostKey.matchesWildcardPattern("?*", "aa"));
assertTrue(HostKey.matchesWildcardPattern("?a", "aa"));
assertTrue(HostKey.matchesWildcardPattern("*a", "aa"));
assertFalse(HostKey.matchesWildcardPattern("a?", "ba"));
assertFalse(HostKey.matchesWildcardPattern("a*", "ba"));
assertFalse(HostKey.matchesWildcardPattern("?a", "ab"));
assertFalse(HostKey.matchesWildcardPattern("*a", "ab"));
assertTrue(HostKey.matchesWildcardPattern("**", "aa"));
assertTrue(HostKey.matchesWildcardPattern("a***b", "ab"));
assertTrue(HostKey.matchesWildcardPattern("a***b", "axb"));
assertTrue(HostKey.matchesWildcardPattern("a***b", "axxb"));
assertFalse(HostKey.matchesWildcardPattern("a***b", "ax"));
assertTrue(HostKey.matchesWildcardPattern("a*b*b", "abbb"));
assertFalse(HostKey.matchesWildcardPattern("a*b*c", "abbb"));
assertFalse(HostKey.matchesWildcardPattern("a*a*a*a*b", "aaaaaaaaac"));
assertTrue(HostKey.matchesWildcardPattern("a*a*a*a*b", "aaaaaaaaab"));
assertTrue(HostKey.matchesWildcardPattern("*b", "ab"));
assertTrue(HostKey.matchesWildcardPattern("*a*", "ab"));
assertTrue(HostKey.matchesWildcardPattern("*a*b", "ab"));
assertFalse(HostKey.matchesWildcardPattern("*a*c", "ab"));
assertTrue(HostKey.matchesWildcardPattern("a?c", "abc"));
assertTrue(HostKey.matchesWildcardPattern("??c", "abc"));
assertTrue(HostKey.matchesWildcardPattern("???", "abc"));
assertFalse(HostKey.matchesWildcardPattern("a?d", "abc"));
assertFalse(HostKey.matchesWildcardPattern("???", "ab"));
assertTrue(HostKey.matchesWildcardPattern("ab*", "abc"));
assertTrue(HostKey.matchesWildcardPattern("ab*", "ab"));
assertFalse(HostKey.matchesWildcardPattern("ab*", "a"));
assertTrue(HostKey.matchesWildcardPattern("ab?", "abc"));
assertFalse(HostKey.matchesWildcardPattern("ab?", "ab"));
assertFalse(HostKey.matchesWildcardPattern("ab?", "abcd"));
assertTrue(HostKey.matchesWildcardPattern("?bc", "abc"));
assertTrue(HostKey.matchesWildcardPattern("?b*", "abc"));
assertFalse(HostKey.matchesWildcardPattern("?c", "abc"));
assertTrue(HostKey.matchesWildcardPattern("a*?c", "abc"));
assertFalse(HostKey.matchesWildcardPattern("a*?c", "ac"));
assertTrue(HostKey.matchesWildcardPattern("a*?c", "abbc"));
assertTrue(HostKey.matchesWildcardPattern("a?*c", "abc"));
assertFalse(HostKey.matchesWildcardPattern("a?*c", "ac"));
assertTrue(HostKey.matchesWildcardPattern("a?*c", "abbc"));
assertTrue(HostKey.matchesWildcardPattern("a*?*c", "abc"));
assertFalse(HostKey.matchesWildcardPattern("a*?*c", "ac"));
assertTrue(HostKey.matchesWildcardPattern("?*c", "abc"));
assertTrue(HostKey.matchesWildcardPattern("?*c", "ac"));
assertFalse(HostKey.matchesWildcardPattern("?*c", "c"));
assertTrue(HostKey.matchesWildcardPattern("*?c", "abc"));
assertTrue(HostKey.matchesWildcardPattern("*?c", "ac"));
assertFalse(HostKey.matchesWildcardPattern("*?c", "c"));
assertTrue(HostKey.matchesWildcardPattern("a?*", "abc"));
assertTrue(HostKey.matchesWildcardPattern("a?*", "ab"));
assertFalse(HostKey.matchesWildcardPattern("a?*", "a"));
assertTrue(HostKey.matchesWildcardPattern("a*?", "abc"));
assertTrue(HostKey.matchesWildcardPattern("a*?", "ab"));
assertFalse(HostKey.matchesWildcardPattern("a*?", "a"));
assertTrue(HostKey.matchesWildcardPattern("a*b", "abb"));
assertFalse(HostKey.matchesWildcardPattern("a*b", "abbc"));
assertTrue(HostKey.matchesWildcardPattern("*.example.com", "host.example.com"));
assertFalse(HostKey.matchesWildcardPattern("*.example.com", "example.com"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -136,6 +137,49 @@ public void testPrincipals_multipleValues() {
assertTrue(cert.getPrincipals().contains("10.0.0.1"), "Should contain IP");
}

@Test
public void testCheckHostCertificate_withWildcardPrincipal() throws Exception {
OpenSshCertificate certificate = parseCertificate(
"src/test/resources/certificates/host/ssh_host_ed25519_wildcard_key-cert.pub");
String caPublicKey =
new String(Util.fromFile("src/test/resources/certificates/ca/ca_jsch_key.pub"),
StandardCharsets.UTF_8).trim();
String knownHosts = "@cert-authority *.EXAMPLE.COM,*.EXAMPLE.ORG " + caPublicKey;

JSch jsch = new JSch();
jsch.setKnownHosts(new ByteArrayInputStream(knownHosts.getBytes(StandardCharsets.UTF_8)));

Session matchingSession = jsch.getSession("user", "HOST.EXAMPLE.COM");
assertDoesNotThrow(
() -> OpenSshCertificateHostKeyVerifier.checkHostCertificate(matchingSession, certificate));

Session nonMatchingSession = jsch.getSession("user", "HOST.EXAMPLE.ORG");
JSchException exception =
assertThrows(JSchException.class, () -> OpenSshCertificateHostKeyVerifier
.checkHostCertificate(nonMatchingSession, certificate));
assertTrue(exception.getMessage().contains("invalid principal"));
}

@Test
public void testCheckHostCertificate_withMatchingSubsequentWildcardPrincipal() throws Exception {
OpenSshCertificate certificate = parseCertificate(
"src/test/resources/certificates/host/ssh_host_ed25519_multiple_wildcard_key-cert.pub");
assertIterableEquals(Arrays.asList("unrelated.example.net", "*.example.com", "*.EXAMPLE.ORG"),
certificate.getPrincipals());

String caPublicKey =
new String(Util.fromFile("src/test/resources/certificates/ca/ca_jsch_key.pub"),
StandardCharsets.UTF_8).trim();
String knownHosts = "@cert-authority *.EXAMPLE.COM " + caPublicKey;

JSch jsch = new JSch();
jsch.setKnownHosts(new ByteArrayInputStream(knownHosts.getBytes(StandardCharsets.UTF_8)));

Session session = jsch.getSession("user", "HOST.EXAMPLE.COM");
assertDoesNotThrow(
() -> OpenSshCertificateHostKeyVerifier.checkHostCertificate(session, certificate));
}

// ==================== Tests for RSA CA signature algorithm (issue #1085) ====================

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFpe3qDi0EuP+jJN021xhhALIhGQc3IHKM2fgBqJfxaFAAAAIL8D5CmrW6ZsQPEihukmIUhSkk5orQF3jgD5ShekkS9vAAAAAAAAAAAAAAACAAAADXdpbGRjYXJkLWhvc3QAAAA7AAAAFXVucmVsYXRlZC5leGFtcGxlLm5ldAAAAA0qLmV4YW1wbGUuY29tAAAADSouRVhBTVBMRS5PUkcAAAAAAAAAAP//////////AAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACCk+kZHaz0pZsY+GYszU0oEitHruzVPS4Eu4SUgXD7BcAAAAFMAAAALc3NoLWVkMjU1MTkAAABASeBOGjdNX+24VnyeTUgBVDiavy1RNTud1blaBzMv29EGAQ5pGapNrJkTgnaqXT7U0OYb3w/iYTyMNVxeWYk6Dw== wildcard-host
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAINi/24/vK4FDRC6B+QUUgPu2wqE/n6yMdhIOGlIkWus5AAAAIL8D5CmrW6ZsQPEihukmIUhSkk5orQF3jgD5ShekkS9vAAAAAAAAAAAAAAACAAAADXdpbGRjYXJkLWhvc3QAAAAiAAAADSouZXhhbXBsZS5jb20AAAANKi5FWEFNUExFLk9SRwAAAAAAAAAA//////////8AAAAAAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIKT6RkdrPSlmxj4ZizNTSgSK0eu7NU9LgS7hJSBcPsFwAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAM5yx9xvrv0218umB3qmz+Cqj/vcnTkXilT4u8kbmD5g5DCbD6xSAzf56vEmJPHKy3EZAVi+PVmsLPdAd0LOoH wildcard-host