Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
dkocher committed Apr 30, 2024
2 parents 222be2d + 09e2ca5 commit 8515bb6
Show file tree
Hide file tree
Showing 60 changed files with 2,806 additions and 509 deletions.
18 changes: 16 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.36.0
:sshj_version: 0.38.0
:source-highlighter: pygments

image:https://github.com/hierynomus/sshj/actions/workflows/gradle.yml/badge.svg[link="https://github.com/hierynomus/sshj/actions/workflows/gradle.yml"]
Expand All @@ -10,6 +10,8 @@ image:https://codecov.io/gh/hierynomus/sshj/branch/master/graph/badge.svg["codec
image:http://www.javadoc.io/badge/com.hierynomus/sshj.svg?color=blue["JavaDocs", link="http://www.javadoc.io/doc/com.hierynomus/sshj"]
image:https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj/badge.svg["Maven Central",link="https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj"]

WARNING: SSHJ versions up to and including 0.37.0 are vulnerable to https://nvd.nist.gov/vuln/detail/CVE-2023-48795[CVE-2023-48795 - Terrapin]. Please upgrade to 0.38.0 or higher.

To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :)

== Getting SSHJ
Expand Down Expand Up @@ -46,7 +48,7 @@ If your project is built using another build tool that uses the Maven Central re
In the `examples` directory, there is a separate Maven project that shows how the library can be used in some sample cases. If you want to run them, follow these guidelines:

. Install http://maven.apache.org/[Maven 2.2.1] or up.
. Clone the Overthere repository.
. Clone the SSHJ repository.
. Go into the `examples` directory and run the command `mvn eclipse:eclipse`.
. Import the `examples` project into Eclipse.
. Change the login details in the example classes (address, username and password) and run them!
Expand Down Expand Up @@ -108,6 +110,18 @@ Issue tracker: https://github.com/hierynomus/sshj/issues
Fork away!

== Release history
SSHJ 0.38.0 (2024-01-02)::
* Mitigated CVE-2023-48795 - Terrapin
* Merged https://github.com/hierynomus/sshj/pull/917[#917]: Implement OpenSSH strict key exchange extension
* Merged https://github.com/hierynomus/sshj/pull/903[#903]: Fix for writing known hosts key string
* Merged https://github.com/hierynomus/sshj/pull/913[#913]: Prevent remote port forwarding buffers to grow without bounds
* Moved tests to JUnit5
* Merged https://github.com/hierynomus/sshj/pull/827[#827]: Fallback to [email protected] extension if available
* Merged https://github.com/hierynomus/sshj/pull/904[#904]: Add ChaCha20-Poly1305 support for OpenSSH keys
SSHJ 0.37.0 (2023-10-11)::
* Merged https://github.com/hierynomus/sshj/pull/899[#899]: Add support for AES-GCM OpenSSH private keys
* Merged https://github.com/hierynomus/sshj/pull/901[#901]: Fix ZLib compression bug
* Merged https://github.com/hierynomus/sshj/pull/898[#898]: Improved malformed file handling for OpenSSH private keys
SSHJ 0.36.0 (2023-09-04)::
* Rewrote Integration tests to JUnit5
* Merged https://github.com/hierynomus/sshj/pull/851[#851]: Fix race condition in key exchange causing intermittent SSH_MSG_UNIMPLEMENTED
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ compileJava {

configurations.implementation.transitive = false

def bouncycastleVersion = "1.75"
def bouncycastleVersion = "1.78"
def sshdVersion = "2.10.0"

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<groupId>com.hierynomus</groupId>
<artifactId>sshj-examples</artifactId>
<packaging>jar</packaging>
<version>0.33.0</version>
<version>0.37.0</version>

<name>sshj-examples</name>
<description>Examples for SSHv2 library for Java</description>
Expand Down
6 changes: 5 additions & 1 deletion src/itest/java/com/hierynomus/sshj/SshdContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,9 @@ public static Builder defaultBuilder() {
.withFileFromString("sshd_config", sshdConfig.build());
}

@Override
public void accept(@NotNull DockerfileBuilder builder) {
builder.from("alpine:3.18.3");
builder.from("alpine:3.19.0");
builder.run("apk add --no-cache openssh");
builder.expose(22);
builder.copy("entrypoint.sh", "/entrypoint.sh");
Expand Down Expand Up @@ -214,6 +215,9 @@ public SshdContainer(@NotNull Future<String> future) {
case STDERR:
logger().info("sshd stderr: {}", outputFrame.getUtf8String().stripTrailing());
break;
case END:
break;

}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.hierynomus.sshj.SshdContainer;
import com.hierynomus.sshj.SshdContainer.SshdConfigBuilder;

import net.schmizz.sshj.Config;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.testcontainers.junit.jupiter.Testcontainers;

import com.hierynomus.sshj.SshdContainer;
import com.hierynomus.sshj.transport.mac.Macs;

import net.schmizz.sshj.Config;
import net.schmizz.sshj.DefaultConfig;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.kex;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.hierynomus.sshj.SshdContainer;
import net.schmizz.keepalive.KeepAlive;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.transport.TransportException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.slf4j.LoggerFactory;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Testcontainers
class StrictKeyExchangeTest {

@Container
private static final SshdContainer sshd = new SshdContainer();

private final List<Logger> watchedLoggers = new ArrayList<>();
private final ListAppender<ILoggingEvent> logWatcher = new ListAppender<>();

@BeforeEach
void setUpLogWatcher() {
logWatcher.start();
setUpLogger("net.schmizz.sshj.transport.Decoder");
setUpLogger("net.schmizz.sshj.transport.Encoder");
setUpLogger("net.schmizz.sshj.transport.KeyExchanger");
}

@AfterEach
void tearDown() {
watchedLoggers.forEach(Logger::detachAndStopAllAppenders);
}

private void setUpLogger(String className) {
Logger logger = ((Logger) LoggerFactory.getLogger(className));
logger.addAppender(logWatcher);
watchedLoggers.add(logger);
}

private static Stream<Arguments> strictKeyExchange() {
Config defaultConfig = new DefaultConfig();
Config heartbeaterConfig = new DefaultConfig();
heartbeaterConfig.setKeepAliveProvider(new KeepAliveProvider() {
@Override
public KeepAlive provide(ConnectionImpl connection) {
return new HotLoopHeartbeater(connection);
}
});
return Stream.of(defaultConfig, heartbeaterConfig).map(Arguments::of);
}

@MethodSource
@ParameterizedTest
void strictKeyExchange(Config config) throws Throwable {
try (SSHClient client = sshd.getConnectedClient(config)) {
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1");
assertTrue(client.isAuthenticated());
}
List<String> keyExchangerLogs = getLogs("KeyExchanger");
assertThat(keyExchangerLogs).contains(
"Initiating key exchange",
"Sending SSH_MSG_KEXINIT",
"Received SSH_MSG_KEXINIT",
"Enabling strict key exchange extension"
);
List<String> decoderLogs = getLogs("Decoder").stream()
.map(log -> log.split(":")[0])
.collect(Collectors.toList());
assertThat(decoderLogs).startsWith(
"Received packet #0",
"Received packet #1",
"Received packet #2",
"Received packet #0",
"Received packet #1",
"Received packet #2",
"Received packet #3"
);
List<String> encoderLogs = getLogs("Encoder").stream()
.map(log -> log.split(":")[0])
.collect(Collectors.toList());
assertThat(encoderLogs).startsWith(
"Encoding packet #0",
"Encoding packet #1",
"Encoding packet #2",
"Encoding packet #0",
"Encoding packet #1",
"Encoding packet #2",
"Encoding packet #3"
);
}

private List<String> getLogs(String className) {
return logWatcher.list.stream()
.filter(event -> event.getLoggerName().endsWith(className))
.map(ILoggingEvent::getFormattedMessage)
.collect(Collectors.toList());
}

private static class HotLoopHeartbeater extends KeepAlive {

HotLoopHeartbeater(ConnectionImpl conn) {
super(conn, "sshj-Heartbeater");
}

@Override
public boolean isEnabled() {
return true;
}

@Override
protected void doKeepAlive() throws TransportException {
conn.getTransport().write(new SSHPacket(Message.IGNORE));
}

}

}
3 changes: 0 additions & 3 deletions src/main/java/com/hierynomus/sshj/key/KeyAlgorithms.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@
import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;

import java.util.Arrays;
import java.util.List;

public class KeyAlgorithms {

public static Factory SSHRSA() { return new Factory("ssh-rsa", new SignatureRSA.FactorySSHRSA(), KeyType.RSA); }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.sftp;

import com.hierynomus.sshj.sftp.RemoteResourceSelector.Result;
import net.schmizz.sshj.sftp.RemoteResourceFilter;

public class RemoteResourceFilterConverter {

public static RemoteResourceSelector selectorFrom(RemoteResourceFilter filter) {
if (filter == null) {
return RemoteResourceSelector.ALL;
}

return resource -> filter.accept(resource) ? Result.ACCEPT : Result.CONTINUE;
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/hierynomus/sshj/sftp/RemoteResourceSelector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.sftp;

import net.schmizz.sshj.sftp.RemoteResourceInfo;

public interface RemoteResourceSelector {
public static RemoteResourceSelector ALL = new RemoteResourceSelector() {
@Override
public Result select(RemoteResourceInfo resource) {
return Result.ACCEPT;
}
};

enum Result {
/**
* Accept the remote resource and add it to the result.
*/
ACCEPT,

/**
* Do not add the remote resource to the result and continue with the next.
*/
CONTINUE,

/**
* Do not add the remote resource to the result and stop further execution.
*/
BREAK;
}

/**
* Decide whether the remote resource should be included in the result and whether execution should continue.
*/
Result select(RemoteResourceInfo resource);
}
Loading

0 comments on commit 8515bb6

Please sign in to comment.