diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 0785c75c..d61d0491 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -29,4 +29,4 @@ jobs:
key: ${{ runner.os }}-java${{ matrix.java }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-java${{ matrix.java }}-m2
- name: Run Tests
- run: mvn test -Dgpg.skip -Dmaven.javadoc.skip=true -B -V -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
+ run: mvn verify -Dgpg.skip -Dmaven.javadoc.skip=true -B -V -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
diff --git a/README.md b/README.md
index b60e8bca..8f2bcda3 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ Add to your pom.xml:
com.rapid7.client
dcerpc
- 0.10.0
+ 0.11.0
```
@@ -83,7 +83,8 @@ try (final Connection smbConnection = smbClient.connect("aaa.bbb.ccc.ddd")) {
final RPCTransport transport = SMBTransportFactories.SRVSVC.getTransport(session);
final ServerService serverService = new ServerService(transport);
- final List shares = serverService.getShares();
+ // Get shares at information level 0
+ final List shares = serverService.getShares0();
for (final NetShareInfo0 share : shares) {
System.out.println(share);
}
diff --git a/pom.xml b/pom.xml
index fd10175b..a1a3b1ee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,12 +48,14 @@
3.4
18.0
1.3
- 4.13.1
+ 5.7.2
1.10.19
- 0.10.0
+ 0.11.1
6.11
- 1.7
- 1.7
+ 1.60
+ 1.16.0
+ 1.8
+ 1.8
UTF-8
@@ -79,6 +81,11 @@
smbj
${thirdparty.smbj.version}
+
+ org.bouncycastle
+ bcprov-jdk15on
+ ${thirdparty.bouncycastle.version}
+
@@ -88,12 +95,6 @@
${thirdparty.hamcrest.version}
test
-
- junit
- junit
- ${thirdparty.junit.version}
- test
-
org.mockito
mockito-core
@@ -106,6 +107,49 @@
${thirdparty.testng.version}
test
+
+ org.testcontainers
+ testcontainers
+ ${thirdparty.testcontainers.version}
+ test
+
+
+ org.testcontainers
+ junit-jupiter
+ ${thirdparty.testcontainers.version}
+ test
+
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.30
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${thirdparty.junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${thirdparty.junit.version}
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ ${thirdparty.junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${thirdparty.junit.version}
+ test
+
@@ -122,8 +166,56 @@
org.apache.maven.plugins
maven-surefire-plugin
- 2.20.1
+ 2.22.2
+
+ maven-failsafe-plugin
+ 3.0.0-M5
+
+ src/integration-test/java
+
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.1.0
+
+
+ add-test-source
+ generate-test-sources
+
+ add-test-source
+
+
+
+
+
+
+
+
+ add-test-resource
+ generate-test-resources
+
+ add-test-resource
+
+
+
+
+ src/integration-test/resources
+
+
+
+
+
+
diff --git a/src/integration-test/java/IntegrationTestsIT.java b/src/integration-test/java/IntegrationTestsIT.java
new file mode 100644
index 00000000..2523c0f3
--- /dev/null
+++ b/src/integration-test/java/IntegrationTestsIT.java
@@ -0,0 +1,92 @@
+import com.rapid7.client.dcerpc.msrrp.RegistryService;
+import com.rapid7.client.dcerpc.mssrvs.ServerService;
+import com.rapid7.client.dcerpc.transport.RPCTransport;
+import com.rapid7.client.dcerpc.transport.SMBTransportFactories;
+import com.hierynomus.mssmb2.SMB2Dialect;
+import com.hierynomus.security.bc.BCSecurityProvider;
+import com.hierynomus.smbj.SMBClient;
+import com.hierynomus.smbj.SmbConfig;
+import com.hierynomus.smbj.auth.AuthenticationContext;
+import com.hierynomus.smbj.connection.Connection;
+import com.hierynomus.smbj.session.Session;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@Testcontainers
+class IntegrationTestsIT
+{
+ private static final Path DOCKER_BUILD_CONTEXT = Paths.get("src", "integration-test", "resources", "docker-image");
+
+ @Container
+ private static final GenericContainer> sambaContainer = new GenericContainer(
+ new ImageFromDockerfile()
+ .withFileFromPath(".", DOCKER_BUILD_CONTEXT))
+ .withExposedPorts(445);
+
+ @ParameterizedTest
+ @MethodSource("testWinRegDoesKeyExistForEachSupportedSMBVersionArgs")
+ @DisplayName("Test registry service key exists function for different SMB protocols")
+ void testWinRegDoesKeyExistForEachSupportedSMBVersion(String keyPath, boolean shouldExist, SMB2Dialect dialect)
+ throws IOException
+ {
+ final SmbConfig smbConfig = SmbConfig.builder().withSecurityProvider(new BCSecurityProvider()).withDialects(dialect).build();
+ final SMBClient smbClient = new SMBClient(smbConfig);
+ try (final Connection smbConnection = smbClient.connect("localhost", sambaContainer.getMappedPort(445))) {
+ final AuthenticationContext smbAuthenticationContext = new AuthenticationContext("smbj", "smbj".toCharArray(), "");
+ final Session session = smbConnection.authenticate(smbAuthenticationContext);
+
+ final RPCTransport transport = SMBTransportFactories.WINREG.getTransport(session);
+ final RegistryService registryService = new RegistryService(transport);
+
+ assertEquals(dialect, smbConnection.getNegotiatedProtocol().getDialect());
+ assertEquals(shouldExist, registryService.doesKeyExist("HKLM", keyPath));
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = SMB2Dialect.class, names = {"SMB_2_0_2", "SMB_2_1", "SMB_3_0", "SMB_3_0_2", "SMB_3_1_1"})
+ @DisplayName("Test service service enumerates shares for different SMB protocols")
+ void testSRVSVCReturnsSharesForEachSupportedSMBVersion(SMB2Dialect dialect)
+ throws IOException
+ {
+ final SmbConfig smbConfig = SmbConfig.builder().withSecurityProvider(new BCSecurityProvider()).withDialects(dialect).build();
+ final SMBClient smbClient = new SMBClient(smbConfig);
+ try (final Connection smbConnection = smbClient.connect("localhost", sambaContainer.getMappedPort(445))) {
+ final AuthenticationContext smbAuthenticationContext = new AuthenticationContext("smbj", "smbj".toCharArray(), "");
+ final Session session = smbConnection.authenticate(smbAuthenticationContext);
+
+ final RPCTransport transport = SMBTransportFactories.SRVSVC.getTransport(session);
+ final ServerService serverService = new ServerService(transport);
+
+ assertEquals(dialect, smbConnection.getNegotiatedProtocol().getDialect());
+ assertEquals(5, serverService.getShares0().size());
+ }
+ }
+
+ static Stream testWinRegDoesKeyExistForEachSupportedSMBVersionArgs() {
+ return Stream.of(
+ Arguments.of("Software", true, SMB2Dialect.SMB_3_1_1),
+ Arguments.of("not_exist", false, SMB2Dialect.SMB_3_1_1),
+ Arguments.of("Software", true, SMB2Dialect.SMB_3_0_2),
+ Arguments.of("not_exist", false, SMB2Dialect.SMB_3_0_2),
+ Arguments.of("Software", true, SMB2Dialect.SMB_3_0),
+ Arguments.of("not_exist", false, SMB2Dialect.SMB_3_0),
+ Arguments.of("Software", true, SMB2Dialect.SMB_2_1),
+ Arguments.of("not_exist", false, SMB2Dialect.SMB_2_1),
+ Arguments.of("Software", true, SMB2Dialect.SMB_2_0_2),
+ Arguments.of("not_exist", false, SMB2Dialect.SMB_2_0_2)
+ );
+ }
+}
diff --git a/src/integration-test/resources/docker-image/Dockerfile b/src/integration-test/resources/docker-image/Dockerfile
new file mode 100644
index 00000000..17f60944
--- /dev/null
+++ b/src/integration-test/resources/docker-image/Dockerfile
@@ -0,0 +1,23 @@
+FROM alpine:3.7
+
+RUN apk update && apk add --no-cache tini samba samba-common-tools supervisor bash
+
+ENV SMB_USER smbj
+ENV SMB_PASSWORD smbj
+
+COPY smb.conf /etc/samba/smb.conf
+COPY supervisord.conf /etc/supervisord.conf
+COPY entrypoint.sh /entrypoint.sh
+ADD public /opt/samba/share
+
+RUN mkdir -p /opt/samba/readonly /opt/samba/user /opt/samba/dfs && \
+ chmod 777 /opt/samba/readonly /opt/samba/user /opt/samba/dfs && \
+ adduser -s /bin/false "$SMB_USER" -D $SMB_PASSWORD && \
+ (echo "$SMB_PASSWORD"; echo "$SMB_PASSWORD" ) | pdbedit -a -u "$SMB_USER" && \
+ chmod ugo+x /entrypoint.sh
+
+EXPOSE 137/udp 138/udp 139 445
+
+ENTRYPOINT ["/sbin/tini", "/entrypoint.sh"]
+CMD ["supervisord"]
+
diff --git a/src/integration-test/resources/docker-image/entrypoint.sh b/src/integration-test/resources/docker-image/entrypoint.sh
new file mode 100644
index 00000000..7b930174
--- /dev/null
+++ b/src/integration-test/resources/docker-image/entrypoint.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+set -e
+
+: "${SMB_USER:=smbuser}"
+: "${SMB_PASSWORD:=smbpassword}"
+#
+#for netdev in /sys/class/net/*; do
+# netdev=${netdev##*/}
+# if [[ "$netdev" != "lo" ]]; then
+# break
+# fi
+#done
+#subnet=$(ip addr show "$netdev" | sed -n 's/.*inet \([0-9\.]*\/[0-9]*\) .*/\1/p')
+#ip_address=${subnet%%/*}
+
+ip_address="127.0.0.1"
+
+# Create DFS links
+# - /public -> public share
+# - /user -> user share
+# - /firstfail-public -> first listed server fails, second -> public share
+ln -s "msdfs:${ip_address}\\public" /opt/samba/dfs/public
+ln -s "msdfs:${ip_address}\\user" /opt/samba/dfs/user
+ln -s "msdfs:192.0.2.1\\notthere,${ip_address}\\public" /opt/samba/dfs/firstfail-public
+
+exec "$@"
diff --git a/src/integration-test/resources/docker-image/public/folder/do_not_remove b/src/integration-test/resources/docker-image/public/folder/do_not_remove
new file mode 100644
index 00000000..e69de29b
diff --git a/src/integration-test/resources/docker-image/public/test.txt b/src/integration-test/resources/docker-image/public/test.txt
new file mode 100644
index 00000000..137d409d
--- /dev/null
+++ b/src/integration-test/resources/docker-image/public/test.txt
@@ -0,0 +1 @@
+Hi there!
diff --git a/src/integration-test/resources/docker-image/smb.conf b/src/integration-test/resources/docker-image/smb.conf
new file mode 100644
index 00000000..6bde6f30
--- /dev/null
+++ b/src/integration-test/resources/docker-image/smb.conf
@@ -0,0 +1,61 @@
+[global]
+security = user
+
+load printers = no
+printcap name = /dev/null
+printing = bsd
+
+unix charset = UTF-8
+dos charset = CP932
+
+workgroup = WORKGROUP
+
+server string = %h server (Samba, Ubuntu)
+dns proxy = no
+interfaces = 192.168.2.0/24 eth0
+bind interfaces only = yes
+log file = /var/log/samba/log.%m
+max log size = 1000
+syslog = 0
+panic action = /usr/share/samba/panic-action %d
+server role = standalone server
+passdb backend = tdbsam
+obey pam restrictions = yes
+unix password sync = yes
+passwd program = /usr/bin/passwd %u
+passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
+pam password change = yes
+map to guest = Bad User
+usershare allow guests = yes
+host msdfs = yes
+
+[public]
+path = /opt/samba/share
+writable = yes
+printable = no
+public = yes
+guest only = yes
+create mode = 0777
+directory mode = 0777
+
+[readonly]
+path = /opt/samba/readonly
+writable = no
+printable = no
+public = no
+
+[user]
+path = /opt/samba/user
+writable = yes
+printable = no
+public = no
+create mode = 0777
+directory mode = 0777
+
+[dfs]
+path = /opt/samba/dfs
+writable = no
+printable = no
+public = yes
+guest ok = yes
+msdfs root = yes
diff --git a/src/integration-test/resources/docker-image/supervisord.conf b/src/integration-test/resources/docker-image/supervisord.conf
new file mode 100644
index 00000000..d9582786
--- /dev/null
+++ b/src/integration-test/resources/docker-image/supervisord.conf
@@ -0,0 +1,14 @@
+[supervisord]
+nodaemon=true
+/* user=root */
+loglevel=info
+
+[program:smbd]
+/* command=/usr/sbin/smbd -i --daemon --foreground --log-stdout */
+command=/usr/sbin/smbd --daemon --foreground --log-stdout
+redirect_stderr=true
+
+[program:nmbd]
+/* command=/usr/sbin/nmbd -i --daemon --foreground --log-stdout */
+command=/usr/sbin/nmbd --daemon --foreground --log-stdout
+redirect_stderr=true
diff --git a/src/main/java/com/rapid7/helper/smbj/io/SMB2Exception.java b/src/main/java/com/rapid7/helper/smbj/io/SMB2Exception.java
index c164f1d8..bbdeb8a3 100644
--- a/src/main/java/com/rapid7/helper/smbj/io/SMB2Exception.java
+++ b/src/main/java/com/rapid7/helper/smbj/io/SMB2Exception.java
@@ -20,7 +20,7 @@
import java.io.IOException;
import com.hierynomus.mserref.NtStatus;
-import com.hierynomus.mssmb2.SMB2Header;
+import com.hierynomus.mssmb2.SMB2PacketHeader;
import com.hierynomus.mssmb2.SMB2MessageCommandCode;
@SuppressWarnings("serial")
@@ -29,7 +29,7 @@ public class SMB2Exception extends IOException {
private final SMB2MessageCommandCode failedCommand;
private final long statusCode;
- public SMB2Exception(final SMB2Header header, final String message) {
+ public SMB2Exception(final SMB2PacketHeader header, final String message) {
super(String.format("%s returned %s (%d/%d): %s", header.getMessage(), header.getStatusCode(), header.getStatusCode(), header.getStatusCode(), message));
status = NtStatus.valueOf(header.getStatusCode());
diff --git a/src/main/java/com/rapid7/helper/smbj/io/SMB2SessionMessage.java b/src/main/java/com/rapid7/helper/smbj/io/SMB2SessionMessage.java
index bebd5e82..2d2b4bb0 100644
--- a/src/main/java/com/rapid7/helper/smbj/io/SMB2SessionMessage.java
+++ b/src/main/java/com/rapid7/helper/smbj/io/SMB2SessionMessage.java
@@ -28,8 +28,9 @@
import java.util.concurrent.TimeoutException;
import com.hierynomus.mserref.NtStatus;
import com.hierynomus.mssmb2.SMB2Dialect;
-import com.hierynomus.mssmb2.SMB2Header;
+import com.hierynomus.mssmb2.SMB2PacketHeader;
import com.hierynomus.mssmb2.SMB2Packet;
+import com.hierynomus.smbj.SmbConfig;
import com.hierynomus.smbj.session.Session;
public abstract class SMB2SessionMessage {
@@ -38,11 +39,11 @@ public abstract class SMB2SessionMessage {
private final long sessionID;
private final long timeout;
- public SMB2SessionMessage(final Session session) {
+ public SMB2SessionMessage(final Session session, final SmbConfig config) {
dialect = session.getConnection().getNegotiatedProtocol().getDialect();
this.session = session;
sessionID = session.getSessionId();
- timeout = session.getConnection().getConfig().getTransactTimeout();
+ timeout = config.getTransactTimeout();
}
public SMB2Dialect getDialect() {
@@ -93,7 +94,7 @@ public T sendAndRead(final SMB2Packet packet, final EnumS
throws IOException {
final Future future = send(packet);
final T responsePacket = read(future);
- final SMB2Header responseHeader = responsePacket.getHeader();
+ final SMB2PacketHeader responseHeader = responsePacket.getHeader();
final NtStatus responseStatus = NtStatus.valueOf(responseHeader.getStatusCode());
if (!ok.contains(responseStatus)) {
throw new SMB2Exception(responseHeader, "expected=" + ok);
diff --git a/src/main/java/com/rapid7/helper/smbj/share/NamedPipe.java b/src/main/java/com/rapid7/helper/smbj/share/NamedPipe.java
index ffc44f82..66d8b9ce 100644
--- a/src/main/java/com/rapid7/helper/smbj/share/NamedPipe.java
+++ b/src/main/java/com/rapid7/helper/smbj/share/NamedPipe.java
@@ -48,7 +48,7 @@ public class NamedPipe extends SMB2SessionMessage implements Closeable {
private final int writeBufferSize;
public NamedPipe(final Session session, final PipeShare share, final String name) throws IOException {
- super(session);
+ super(session, share.getTreeConnect().getConfig());
this.share = share;
@@ -56,9 +56,9 @@ public NamedPipe(final Session session, final PipeShare share, final String name
final SMB2CreateResponse createResponse = sendAndRead(createRequest, EnumSet.of(NtStatus.STATUS_SUCCESS));
fileID = createResponse.getFileId();
- transactBufferSize = Math.min(session.getConnection().getConfig().getTransactBufferSize(), session.getConnection().getNegotiatedProtocol().getMaxTransactSize());
- readBufferSize = Math.min(session.getConnection().getConfig().getReadBufferSize(), session.getConnection().getNegotiatedProtocol().getMaxReadSize());
- writeBufferSize = Math.min(session.getConnection().getConfig().getWriteBufferSize(), session.getConnection().getNegotiatedProtocol().getMaxWriteSize());
+ transactBufferSize = Math.min(share.getTreeConnect().getConfig().getTransactBufferSize(), session.getConnection().getNegotiatedProtocol().getMaxTransactSize());
+ readBufferSize = Math.min(share.getTreeConnect().getConfig().getReadBufferSize(), session.getConnection().getNegotiatedProtocol().getMaxReadSize());
+ writeBufferSize = Math.min(share.getTreeConnect().getConfig().getWriteBufferSize(), session.getConnection().getNegotiatedProtocol().getMaxWriteSize());
}
public byte[] transact(final byte[] inBuffer) throws IOException {