diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..846342af --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,33 @@ +image: java:8-jdk + +stages: + - build + - test + - deploy + +before_script: +# - echo `pwd` # debug +# - echo "$CI_BUILD_NAME, $CI_BUILD_REF_NAME $CI_BUILD_STAGE" # debug + - export GRADLE_USER_HOME=`pwd`/.gradle + +cache: + paths: + - .gradle/wrapper + - .gradle/caches + +build: + stage: build + script: + - ./gradlew assemble + artifacts: + paths: + - build/libs/*.jar + expire_in: 1 week + only: + - master + +test: + stage: test + script: + - ./gradlew check + diff --git a/src/it/java/SmbjTest.java b/src/it/java/SmbjTest.java index 5836db49..1201bbea 100644 --- a/src/it/java/SmbjTest.java +++ b/src/it/java/SmbjTest.java @@ -338,7 +338,6 @@ public void testRpc() throws IOException, SMBApiException, URISyntaxException { } } - //@Test public void manualTestNotify() throws IOException { diff --git a/src/it/resources/logback-test.xml b/src/it/resources/logback-test.xml index a12690b5..6054f851 100644 --- a/src/it/resources/logback-test.xml +++ b/src/it/resources/logback-test.xml @@ -28,6 +28,6 @@ - + diff --git a/src/main/java/com/hierynomus/msdtyp/ACL.java b/src/main/java/com/hierynomus/msdtyp/ACL.java index 1359bd0a..e7726e8d 100644 --- a/src/main/java/com/hierynomus/msdtyp/ACL.java +++ b/src/main/java/com/hierynomus/msdtyp/ACL.java @@ -15,12 +15,11 @@ */ package com.hierynomus.msdtyp; +import java.util.Arrays; import com.hierynomus.msdtyp.ace.ACE; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.Arrays; - /** * [MS-DTYP].pdf 2.4.5 ACL */ @@ -84,11 +83,11 @@ public int getAclSize() { @Override public String toString() { return "ACL{" + - "revision=" + revision + - ", aceCount=" + aceCount + - ", sidIdentifierAuthority=" + Arrays.toString(sidIdentifierAuthority) + - ", subAuthorities=" + Arrays.toString(subAuthorities) + - ", aces=" + Arrays.toString(aces) + - '}'; + "revision=" + revision + + ", aceCount=" + aceCount + + ", sidIdentifierAuthority=" + Arrays.toString(sidIdentifierAuthority) + + ", subAuthorities=" + Arrays.toString(subAuthorities) + + ", aces=" + Arrays.toString(aces) + + '}'; } } diff --git a/src/main/java/com/hierynomus/msdtyp/AccessMask.java b/src/main/java/com/hierynomus/msdtyp/AccessMask.java index 92a91cbe..06bc3b3f 100644 --- a/src/main/java/com/hierynomus/msdtyp/AccessMask.java +++ b/src/main/java/com/hierynomus/msdtyp/AccessMask.java @@ -19,7 +19,7 @@ /** * MS-DTYP 2.4.3 ACCESS_MASK - * + *

* Its ok to find multiple names pointing to the same values, Since the * same access mask when applied to File, Folder or other object are just * named/called differently. diff --git a/src/main/java/com/hierynomus/msdtyp/MsDataTypes.java b/src/main/java/com/hierynomus/msdtyp/MsDataTypes.java index eea46258..9c19f20e 100644 --- a/src/main/java/com/hierynomus/msdtyp/MsDataTypes.java +++ b/src/main/java/com/hierynomus/msdtyp/MsDataTypes.java @@ -15,11 +15,10 @@ */ package com.hierynomus.msdtyp; -import com.hierynomus.protocol.commons.buffer.Buffer; -import com.hierynomus.protocol.commons.buffer.Endian; - import java.util.Date; import java.util.UUID; +import com.hierynomus.protocol.commons.buffer.Buffer; +import com.hierynomus.protocol.commons.buffer.Endian; /** * Utility class that can read and write data types from the [MS-DTYP].pdf specification document from buffers. @@ -28,7 +27,8 @@ public class MsDataTypes { public static final int NANO100_TO_MILLI = 10000; public static final long WINDOWS_TO_UNIX_EPOCH = 0x19DB1DED53E8000L; - private MsDataTypes() {} + private MsDataTypes() { + } /** * [MS-DTYP].pdf 2.3.4.2 GUID Packet representation diff --git a/src/main/java/com/hierynomus/msdtyp/SID.java b/src/main/java/com/hierynomus/msdtyp/SID.java index e491a59c..8086f778 100644 --- a/src/main/java/com/hierynomus/msdtyp/SID.java +++ b/src/main/java/com/hierynomus/msdtyp/SID.java @@ -20,6 +20,8 @@ import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; +import java.util.Arrays; + /** * [MS-DTYP].pdf 2.4.2 SecurityIdentifier SID */ @@ -134,4 +136,29 @@ public byte[] getSidIdentifierAuthority() { public long[] getSubAuthorities() { return subAuthorities; } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + SID sid = (SID) o; + + if (revision != sid.revision) + return false; + if (!Arrays.equals(sidIdentifierAuthority, sid.sidIdentifierAuthority)) + return false; + return Arrays.equals(subAuthorities, sid.subAuthorities); + + } + + @Override + public int hashCode() { + int result = (int) revision; + result = 31 * result + Arrays.hashCode(sidIdentifierAuthority); + result = 31 * result + Arrays.hashCode(subAuthorities); + return result; + } } diff --git a/src/main/java/com/hierynomus/msdtyp/SecurityDescriptor.java b/src/main/java/com/hierynomus/msdtyp/SecurityDescriptor.java index 9672de91..2b1de454 100644 --- a/src/main/java/com/hierynomus/msdtyp/SecurityDescriptor.java +++ b/src/main/java/com/hierynomus/msdtyp/SecurityDescriptor.java @@ -15,12 +15,11 @@ */ package com.hierynomus.msdtyp; +import java.util.EnumSet; import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - /** * [MS-DTYP].pdf 2.4.6 SecurityDescriptor */ @@ -35,7 +34,7 @@ public class SecurityDescriptor { public SecurityDescriptor() { } -// public SecurityDescriptor(EnumSet control, SID ownerSid, SID groupSid, ACL sacl, ACL dacl) { + // public SecurityDescriptor(EnumSet control, SID ownerSid, SID groupSid, ACL sacl, ACL dacl) { // this.control = control; // this.ownerSid = ownerSid; // this.groupSid = groupSid; @@ -120,12 +119,12 @@ public ACL getDacl() { @Override public String toString() { return "SecurityDescriptor{" + - "control=" + control + - ", ownerSid=" + ownerSid + - ", groupSid=" + groupSid + - ", sacl=" + sacl + - ", dacl=" + dacl + - '}'; + "control=" + control + + ", ownerSid=" + ownerSid + + ", groupSid=" + groupSid + + ", sacl=" + sacl + + ", dacl=" + dacl + + '}'; } // SecurityDescriptor Control bits diff --git a/src/main/java/com/hierynomus/msdtyp/ace/ACE.java b/src/main/java/com/hierynomus/msdtyp/ace/ACE.java index 04194468..8bc29d7f 100644 --- a/src/main/java/com/hierynomus/msdtyp/ace/ACE.java +++ b/src/main/java/com/hierynomus/msdtyp/ace/ACE.java @@ -117,10 +117,10 @@ public static ACE factory(SMBBuffer buffer) throws Buffer.BufferException { @Override public String toString() { return "ACE{" + - "aceHeader=" + aceHeader + - ", accessMask=" + EnumWithValue.EnumUtils.toEnumSet(accessMask, AccessMask.class) + - ", sid=" + sid + - '}'; + "aceHeader=" + aceHeader + + ", accessMask=" + EnumWithValue.EnumUtils.toEnumSet(accessMask, AccessMask.class) + + ", sid=" + sid + + '}'; } protected abstract void readMessage(SMBBuffer buffer) throws Buffer.BufferException; diff --git a/src/main/java/com/hierynomus/msdtyp/ace/AceHeader.java b/src/main/java/com/hierynomus/msdtyp/ace/AceHeader.java index feedc873..ef611589 100644 --- a/src/main/java/com/hierynomus/msdtyp/ace/AceHeader.java +++ b/src/main/java/com/hierynomus/msdtyp/ace/AceHeader.java @@ -15,12 +15,11 @@ */ package com.hierynomus.msdtyp.ace; +import java.util.EnumSet; import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toEnumSet; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.valueOf; @@ -73,9 +72,9 @@ public EnumSet getAceFlags() { @Override public String toString() { return "AceHeader{" + - "aceType=" + aceType + - ", aceFlags=" + aceFlags + - ", aceSize=" + aceSize + - '}'; + "aceType=" + aceType + + ", aceFlags=" + aceFlags + + ", aceSize=" + aceSize + + '}'; } } diff --git a/src/main/java/com/hierynomus/msdtyp/ace/AceType1.java b/src/main/java/com/hierynomus/msdtyp/ace/AceType1.java index ee1265de..cb6e9fc9 100644 --- a/src/main/java/com/hierynomus/msdtyp/ace/AceType1.java +++ b/src/main/java/com/hierynomus/msdtyp/ace/AceType1.java @@ -15,13 +15,12 @@ */ package com.hierynomus.msdtyp.ace; +import java.util.EnumSet; import com.hierynomus.msdtyp.AccessMask; import com.hierynomus.msdtyp.SID; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toLong; // Type 1 - Header/Mask/SID (ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_MANDATORY_LABEL_ACE, @@ -30,9 +29,10 @@ class AceType1 extends ACE { AceType1() { } + AceType1(AceType aceType, EnumSet aceFlags, EnumSet accessMask, SID sid) { super(new AceHeader(aceType, aceFlags, ACE.HEADER_STRUCTURE_SIZE + 4 + sid.byteCount()), - toLong(accessMask), sid); + toLong(accessMask), sid); } @Override diff --git a/src/main/java/com/hierynomus/msdtyp/ace/AceType2.java b/src/main/java/com/hierynomus/msdtyp/ace/AceType2.java index 08e04c10..ecd461f0 100644 --- a/src/main/java/com/hierynomus/msdtyp/ace/AceType2.java +++ b/src/main/java/com/hierynomus/msdtyp/ace/AceType2.java @@ -15,15 +15,14 @@ */ package com.hierynomus.msdtyp.ace; +import java.util.EnumSet; +import java.util.UUID; import com.hierynomus.msdtyp.AccessMask; import com.hierynomus.msdtyp.MsDataTypes; import com.hierynomus.msdtyp.SID; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; -import java.util.UUID; - import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toEnumSet; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toLong; @@ -35,9 +34,9 @@ class AceType2 extends ACE { private UUID inheritedObjectType; AceType2(AceType aceType, EnumSet aceFlags, EnumSet accessMask, - EnumSet flags, UUID objectType, UUID inheritedObjectType, SID sid) { + EnumSet flags, UUID objectType, UUID inheritedObjectType, SID sid) { super(new AceHeader(aceType, aceFlags, ACE.HEADER_STRUCTURE_SIZE + 4 + 4 + 16 + 16 + sid.byteCount()), - toLong(accessMask), sid); + toLong(accessMask), sid); this.flags = flags; this.objectType = objectType; this.inheritedObjectType = inheritedObjectType; @@ -83,9 +82,9 @@ protected void readMessage(SMBBuffer buffer) throws Buffer.BufferException { @Override public String toString() { return "AceType2{" + - "flags=" + flags + - ", objectType=" + objectType + - ", inheritedObjectType=" + inheritedObjectType + - "} " + super.toString(); + "flags=" + flags + + ", objectType=" + objectType + + ", inheritedObjectType=" + inheritedObjectType + + "} " + super.toString(); } } diff --git a/src/main/java/com/hierynomus/msdtyp/ace/AceType3.java b/src/main/java/com/hierynomus/msdtyp/ace/AceType3.java index 9076958a..c43febe5 100644 --- a/src/main/java/com/hierynomus/msdtyp/ace/AceType3.java +++ b/src/main/java/com/hierynomus/msdtyp/ace/AceType3.java @@ -15,14 +15,13 @@ */ package com.hierynomus.msdtyp.ace; +import java.util.Arrays; +import java.util.EnumSet; import com.hierynomus.msdtyp.AccessMask; import com.hierynomus.msdtyp.SID; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.Arrays; -import java.util.EnumSet; - import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toLong; // Type 3 - Header/Mask/SID/ApplicationData @@ -35,10 +34,10 @@ class AceType3 extends ACE { } AceType3(AceType aceType, EnumSet aceFlags, EnumSet accessMask, SID sid, byte[] - applicationData) { + applicationData) { super(new AceHeader(aceType, aceFlags, ACE.HEADER_STRUCTURE_SIZE + 4 + 4 + sid.byteCount() + - applicationData.length), - toLong(accessMask), sid); + applicationData.length), + toLong(accessMask), sid); this.applicationData = applicationData; } @@ -59,7 +58,7 @@ protected void readMessage(SMBBuffer buffer) throws Buffer.BufferException { @Override public String toString() { return "AceType3{" + - "applicationData=" + Arrays.toString(applicationData) + - "} " + super.toString(); + "applicationData=" + Arrays.toString(applicationData) + + "} " + super.toString(); } } diff --git a/src/main/java/com/hierynomus/msdtyp/ace/AceType4.java b/src/main/java/com/hierynomus/msdtyp/ace/AceType4.java index 9149c020..26467866 100644 --- a/src/main/java/com/hierynomus/msdtyp/ace/AceType4.java +++ b/src/main/java/com/hierynomus/msdtyp/ace/AceType4.java @@ -15,15 +15,14 @@ */ package com.hierynomus.msdtyp.ace; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.UUID; import com.hierynomus.msdtyp.AccessMask; import com.hierynomus.msdtyp.SID; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.UUID; - // Type 4 - Header/Mask/Flags/ObjectType/InheritedObjectType/Sid/ApplicationData // ACCESS_ALLOWED_CALLBACK_OBJECT_ACE, ACCESS_DENIED_CALLBACK_OBJECT_ACE, SYSTEM_AUDIT_OBJECT_ACE, // SYSTEM_AUDIT_CALLBACK_OBJECT_ACE, @@ -35,8 +34,8 @@ class AceType4 extends AceType2 { } AceType4(AceType aceType, EnumSet aceFlags, EnumSet accessMask, - EnumSet flags, UUID objectType, UUID inheritedObjectType, SID sid, byte[] - applicationData) { + EnumSet flags, UUID objectType, UUID inheritedObjectType, SID sid, byte[] + applicationData) { super(aceType, aceFlags, accessMask, flags, objectType, inheritedObjectType, sid); aceHeader.setAceSize(aceHeader.getAceSize() + applicationData.length); this.applicationData = applicationData; @@ -58,7 +57,7 @@ protected void readMessage(SMBBuffer buffer) throws Buffer.BufferException { @Override public String toString() { return "AceType4{" + - "applicationData=" + Arrays.toString(applicationData) + - "} " + super.toString(); + "applicationData=" + Arrays.toString(applicationData) + + "} " + super.toString(); } } diff --git a/src/main/java/com/hierynomus/msdtyp/ace/AceTypes.java b/src/main/java/com/hierynomus/msdtyp/ace/AceTypes.java index 15eafa88..9d71dac7 100644 --- a/src/main/java/com/hierynomus/msdtyp/ace/AceTypes.java +++ b/src/main/java/com/hierynomus/msdtyp/ace/AceTypes.java @@ -15,20 +15,34 @@ */ package com.hierynomus.msdtyp.ace; -import com.hierynomus.msdtyp.AccessMask; -import com.hierynomus.msdtyp.SID; - import java.util.EnumSet; import java.util.UUID; +import com.hierynomus.msdtyp.AccessMask; +import com.hierynomus.msdtyp.SID; -import static com.hierynomus.msdtyp.ace.AceType.*; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_ALLOWED_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_ALLOWED_CALLBACK_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_ALLOWED_OBJECT_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_DENIED_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_DENIED_CALLBACK_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.ACCESS_DENIED_OBJECT_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.SYSTEM_AUDIT_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.SYSTEM_AUDIT_CALLBACK_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.SYSTEM_AUDIT_OBJECT_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.SYSTEM_MANDATORY_LABEL_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE; +import static com.hierynomus.msdtyp.ace.AceType.SYSTEM_SCOPED_POLICY_ID_ACE_TYPE; /** * Factory methods for the different AceType objects. */ public class AceTypes { - private AceTypes() {} + private AceTypes() { + } /** * [MS-DTYP].pdf 2.4.4.2 ACCESS_ALLOWED_ACE @@ -89,8 +103,8 @@ public static ACE accessAllowedCallbackObjectAce(EnumSet aceFlags, Enu * [MS-DTYP].pdf 2.4.4.9 ACCESS_DENIED_CALLBACK_OBJECT_ACE */ public static ACE accessDeniedCallbackObjectAce(EnumSet aceFlags, EnumSet accessMask, - EnumSet flags, UUID objectType, UUID inheritedObjectType, - SID sid, byte[] applicationData) { + EnumSet flags, UUID objectType, UUID inheritedObjectType, + SID sid, byte[] applicationData) { return new AceType4(ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE, aceFlags, accessMask, flags, objectType, inheritedObjectType, sid, applicationData); } diff --git a/src/main/java/com/hierynomus/mserref/NtStatus.java b/src/main/java/com/hierynomus/mserref/NtStatus.java index 9ace152c..5a6e5587 100644 --- a/src/main/java/com/hierynomus/mserref/NtStatus.java +++ b/src/main/java/com/hierynomus/mserref/NtStatus.java @@ -19,7 +19,7 @@ /** * [MS-ERREF].pdf 2.3.1 NTSTATUS values - * + *

* Subset of the possible values which are useful for SMB2 communication */ public enum NtStatus implements EnumWithValue { @@ -28,6 +28,8 @@ public enum NtStatus implements EnumWithValue { STATUS_PENDING(0x00000103L), STATUS_BUFFER_OVERFLOW(0x80000005L), STATUS_END_OF_FILE(0xC0000011L), + STATUS_SHARING_VIOLATION(0xC0000043L), + STATUS_DELETE_PENDING(0xC0000056L), STATUS_FILE_IS_A_DIRECTORY(0xC00000BAL), STATUS_NETWORK_NAME_DELETED(0xC00000C9L), STATUS_INVALID_PARAMETER(0xC000000DL), @@ -39,8 +41,13 @@ public enum NtStatus implements EnumWithValue { STATUS_OBJECT_PATH_NOT_FOUND(0xC000003AL), STATUS_LOGON_FAILURE(0xC000006DL), STATUS_PASSWORD_EXPIRED(0xC0000071L), + STATUS_DISK_FULL(0xC000007FL), STATUS_INSUFFICIENT_RESOURCES(0xC000009AL), + STATUS_PIPE_NOT_AVAILABLE(0xC00000ACL), + STATUS_INVALID_PIPE_STATE(0xC00000ADL), + STATUS_PIPE_BUSY(0xC00000AEL), STATUS_NOT_SUPPORTED(0xC00000BBL), + STATUS_BAD_NETWORK_PATH(0xC00000BEL), STATUS_BAD_NETWORK_NAME(0xC00000CCL), STATUS_REQUEST_NOT_ACCEPTED(0xC00000D0L), STATUS_NET_WRITE_FAULT(0xC00000D2L), @@ -66,10 +73,12 @@ public enum NtStatus implements EnumWithValue { STATUS_CONNECTION_DISCONNECTED(0xC000020CL), STATUS_CONNECTION_RESET(0xC000020DL), STATUS_NOT_FOUND(0xC0000225L), + STATUS_PATH_NOT_COVERED(0xC0000257L), STATUS_RETRY(0xC000022DL), STATUS_DFS_UNAVAILABLE(0xC000026DL), STATUS_FILE_ENCRYPTED(0xC0000293L), STATUS_NETWORK_SESSION_EXPIRED(0xC000035CL), + STATUS_NO_MORE_FILES(0x80000006L), UNKNOWN(0xFFFFFFFFL); private long value; diff --git a/src/main/java/com/hierynomus/msfscc/fileinformation/FileInfo.java b/src/main/java/com/hierynomus/msfscc/fileinformation/FileInfo.java index abf34611..9cf996cd 100644 --- a/src/main/java/com/hierynomus/msfscc/fileinformation/FileInfo.java +++ b/src/main/java/com/hierynomus/msfscc/fileinformation/FileInfo.java @@ -58,9 +58,9 @@ public long getAccessMask() { @Override public String toString() { return "FileInfo{" + - "fileName='" + fileName + '\'' + - "fileSize='" + fileSize + '\'' + - ", fileAttributes=" + EnumWithValue.EnumUtils.toEnumSet(fileAttributes, FileAttributes.class) + - '}'; + "fileName='" + fileName + '\'' + + "fileSize='" + fileSize + '\'' + + ", fileAttributes=" + EnumWithValue.EnumUtils.toEnumSet(fileAttributes, FileAttributes.class) + + '}'; } } diff --git a/src/main/java/com/hierynomus/msfscc/fileinformation/FileInformationFactory.java b/src/main/java/com/hierynomus/msfscc/fileinformation/FileInformationFactory.java index fc618457..abbd3c49 100644 --- a/src/main/java/com/hierynomus/msfscc/fileinformation/FileInformationFactory.java +++ b/src/main/java/com/hierynomus/msfscc/fileinformation/FileInformationFactory.java @@ -15,15 +15,14 @@ */ package com.hierynomus.msfscc.fileinformation; -import com.hierynomus.msdtyp.MsDataTypes; -import com.hierynomus.msfscc.FileInformationClass; -import com.hierynomus.protocol.commons.buffer.Buffer; -import com.hierynomus.protocol.commons.buffer.Endian; - import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Date; import java.util.List; +import com.hierynomus.msdtyp.MsDataTypes; +import com.hierynomus.msfscc.FileInformationClass; +import com.hierynomus.protocol.commons.buffer.Buffer; +import com.hierynomus.protocol.commons.buffer.Endian; public class FileInformationFactory { @@ -44,9 +43,7 @@ public static byte[] getRenameInfo(boolean replaceIfExists, String newName) { * MS-FSCC 2.4.11 FileDispositionInformation for SMB2 */ public static byte[] getFileDispositionInfo(boolean deleteOnClose) { - Buffer.PlainBuffer fileDispBuf = new Buffer.PlainBuffer(Endian.LE); - fileDispBuf.putByte((byte) (deleteOnClose ? 1 : 0)); - return fileDispBuf.getCompactData(); + return deleteOnClose ? new byte[]{1} : new byte[]{0}; } /** @@ -58,16 +55,16 @@ public static byte[] getFileDispositionInfo(boolean deleteOnClose) { * @throws Buffer.BufferException */ public static List parseFileInformationList( - byte[] data, FileInformationClass fileInformationClass) - throws Buffer.BufferException { + byte[] data, FileInformationClass fileInformationClass) + throws Buffer.BufferException { Buffer.PlainBuffer buffer = new Buffer.PlainBuffer(data, Endian.LE); List _fileInfoList = new ArrayList<>(); int offsetStart = 0; int nextEntryOffset = offsetStart; long fileIndex = 0; - do { - nextEntryOffset = (int)buffer.readUInt32(); + do { + nextEntryOffset = (int) buffer.readUInt32(); fileIndex = buffer.readUInt32(); FileInfo fileInfo = null; switch (fileInformationClass) { @@ -92,7 +89,7 @@ public static List parseFileInformationList( /** * [MS-SMB2] 2.2.38 SMB2 QUERY_INFO Response, SMB2_0_INFO_FILE/FileAllInformation - * + *

* [MS-FSCC] 2.4.2 FileAllInformation */ public static FileInfo parseFileAllInformation(Buffer.PlainBuffer buffer) throws Buffer.BufferException { diff --git a/src/main/java/com/hierynomus/mssmb2/SMB2CreateDisposition.java b/src/main/java/com/hierynomus/mssmb2/SMB2CreateDisposition.java index 6b2b0029..b522a1b2 100644 --- a/src/main/java/com/hierynomus/mssmb2/SMB2CreateDisposition.java +++ b/src/main/java/com/hierynomus/mssmb2/SMB2CreateDisposition.java @@ -25,17 +25,29 @@ * For other files, this field MUST contain one of the following values. */ public enum SMB2CreateDisposition implements EnumWithValue { - /** If the file already exists, supersede it. Otherwise, create the file. This value SHOULD NOT be used for a printer object. */ + /** + * If the file already exists, supersede it. Otherwise, create the file. This value SHOULD NOT be used for a printer object. + */ FILE_SUPERSEDE(0x00000000L), - /** If the file already exists, return success; otherwise, fail the operation. MUST NOT be used for a printer object. */ + /** + * If the file already exists, return success; otherwise, fail the operation. MUST NOT be used for a printer object. + */ FILE_OPEN(0x00000001L), - /** If the file already exists, fail the operation; otherwise, create the file. */ + /** + * If the file already exists, fail the operation; otherwise, create the file. + */ FILE_CREATE(0x00000002L), - /** Open the file if it already exists; otherwise, create the file. This value SHOULD NOT be used for a printer object. */ + /** + * Open the file if it already exists; otherwise, create the file. This value SHOULD NOT be used for a printer object. + */ FILE_OPEN_IF(0x00000003L), - /** Overwrite the file if it already exists; otherwise, fail the operation. MUST NOT be used for a printer object. */ + /** + * Overwrite the file if it already exists; otherwise, fail the operation. MUST NOT be used for a printer object. + */ FILE_OVERWRITE(0x00000004L), - /** Overwrite the file if it already exists; otherwise, create the file. This value SHOULD NOT be used for a printer object. */ + /** + * Overwrite the file if it already exists; otherwise, create the file. This value SHOULD NOT be used for a printer object. + */ FILE_OVERWRITE_IF(0x00000005L); private long value; diff --git a/src/main/java/com/hierynomus/mssmb2/SMB2Dialect.java b/src/main/java/com/hierynomus/mssmb2/SMB2Dialect.java index 80e929c4..2a69fdd6 100644 --- a/src/main/java/com/hierynomus/mssmb2/SMB2Dialect.java +++ b/src/main/java/com/hierynomus/mssmb2/SMB2Dialect.java @@ -42,6 +42,7 @@ public boolean isSmb3x() { /** * Whether any of the dialects in the set is an SMB 3.x dialect. + * * @param dialects The supported dialects enumset. * @return true if there is (at least) one SMB 3.x dialect in the set. */ diff --git a/src/main/java/com/hierynomus/mssmb2/SMB2FileId.java b/src/main/java/com/hierynomus/mssmb2/SMB2FileId.java index e94053b8..b141e359 100644 --- a/src/main/java/com/hierynomus/mssmb2/SMB2FileId.java +++ b/src/main/java/com/hierynomus/mssmb2/SMB2FileId.java @@ -39,13 +39,13 @@ public void write(SMBBuffer buffer) { } public static SMB2FileId read(SMBBuffer buffer) throws Buffer.BufferException { - return new SMB2FileId(buffer.readRawBytes(8),buffer.readRawBytes(8)); + return new SMB2FileId(buffer.readRawBytes(8), buffer.readRawBytes(8)); } @Override public String toString() { return "SMB2FileId{" + - "persistentHandle=" + ByteArrayUtils.printHex(persistentHandle) + - '}'; + "persistentHandle=" + ByteArrayUtils.printHex(persistentHandle) + + '}'; } } diff --git a/src/main/java/com/hierynomus/mssmb2/SMB2Header.java b/src/main/java/com/hierynomus/mssmb2/SMB2Header.java index 2ade7a9c..ec9141fa 100644 --- a/src/main/java/com/hierynomus/mssmb2/SMB2Header.java +++ b/src/main/java/com/hierynomus/mssmb2/SMB2Header.java @@ -21,13 +21,14 @@ import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.isSet; -import static com.hierynomus.smbj.connection.NegotiatedProtocol.SINGLE_CREDIT_PAYLOAD_SIZE; /** * [MS-SMB2].pdf 2.2.1 SMB2 Packet Header */ public class SMB2Header { public static final int STRUCTURE_SIZE = 64; + public static final int SIGNATURE_OFFSET = 48; + public static final int SIGNATURE_SIZE = 16; private SMB2Dialect dialect; private int creditCharge = 1; @@ -63,12 +64,12 @@ public void writeTo(SMBBuffer buffer) { buffer.putUInt32(treeId); // TreeId (4 bytes) } buffer.putLong(sessionId); // SessionId (8 bytes) - buffer.putRawBytes(new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}); // Signature (16 bytes) + buffer.putRawBytes(new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}); // Signature (16 bytes) } private void writeChannelSequenceReserved(SMBBuffer buffer) { if (dialect.isSmb3x()) { - buffer.putRawBytes(new byte[] {0x0, 0x0}); // ChannelSequence (2 bytes) + buffer.putRawBytes(new byte[]{0x0, 0x0}); // ChannelSequence (2 bytes) buffer.putReserved(2); // Reserved (2 bytes) throw new UnsupportedOperationException("SMB 3.x not yet implemented"); } else { @@ -78,7 +79,7 @@ private void writeChannelSequenceReserved(SMBBuffer buffer) { /** * [MS-SMB2].pdf 3.2.4.1.2 Requesting Credits from the Server - * + *

* We should at least request the number of credits this request consumes, but we can request more (by calling {@link #setCreditRequest(int)}). */ private void writeCreditRequest(SMBBuffer buffer) { @@ -137,6 +138,10 @@ public void setDialect(SMB2Dialect dialect) { this.dialect = dialect; } + public boolean isFlagSet(SMB2MessageFlag flag) { + return isSet(this.flags, flag); + } + public void setFlag(SMB2MessageFlag flag) { this.flags |= flag.getValue(); } @@ -205,4 +210,23 @@ public void setNextCommandOffset(long nextCommandOffset) { public void setCreditCharge(int creditCharge) { this.creditCharge = creditCharge; } + + public String toString() { + return String.format( + "dialect=%s, creditCharge=%s, creditRequest=%s, creditResponse=%s, message=%s, messageId=%s, asyncId=%s, sessionId=%s, treeId=%s, status=%s, statusCode=%s, flags=%s, nextCommandOffset=%s", + dialect, + creditCharge, + creditRequest, + creditResponse, + message, + messageId, + asyncId, + sessionId, + treeId, + status, + statusCode, + flags, + nextCommandOffset); + + } } diff --git a/src/main/java/com/hierynomus/mssmb2/SMB2Packet.java b/src/main/java/com/hierynomus/mssmb2/SMB2Packet.java index 53b9fad5..2bf98c9d 100644 --- a/src/main/java/com/hierynomus/mssmb2/SMB2Packet.java +++ b/src/main/java/com/hierynomus/mssmb2/SMB2Packet.java @@ -22,6 +22,7 @@ public class SMB2Packet implements Packet { protected final SMB2Header header = new SMB2Header(); protected int structureSize; + SMBBuffer buffer; public SMB2Packet() { } @@ -54,6 +55,10 @@ public int getStructureSize() { return structureSize; } + public SMBBuffer getBuffer() { + return buffer; + } + public final void write(SMBBuffer buffer) { header.writeTo(buffer); writeTo(buffer); @@ -61,6 +66,7 @@ public final void write(SMBBuffer buffer) { /** * Write the message fields into the buffer, as specified in the [MS-SMB2].pdf specification. + * * @param buffer */ protected void writeTo(SMBBuffer buffer) { @@ -68,6 +74,7 @@ protected void writeTo(SMBBuffer buffer) { } public final SMB2Packet read(SMBBuffer buffer) throws Buffer.BufferException { + this.buffer = buffer; // remember the buffer we read it from header.readFrom(buffer); readMessage(buffer); return this; diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyRequest.java index 14562fef..f45dbead 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyRequest.java @@ -15,12 +15,11 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.EnumSet; import com.hierynomus.mssmb2.*; import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - /** * [MS-SMB2].pdf 2.2.35 SMB2 CHANGE_NOTIFY Request *

diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyResponse.java index b9a17246..a62499d1 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyResponse.java @@ -15,6 +15,9 @@ */ package com.hierynomus.mssmb2.messages; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import com.hierynomus.mserref.NtStatus; import com.hierynomus.msfscc.FileNotifyAction; import com.hierynomus.mssmb2.SMB2Packet; @@ -22,14 +25,11 @@ import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - /** * [MS-SMB2].pdf 2.2.36 SMB2 CHANGE_NOTIFY Response *

-\ */ + * \ + */ public class SMB2ChangeNotifyResponse extends SMB2Packet { List fileNotifyInfoList = new ArrayList<>(); @@ -49,7 +49,7 @@ protected void readMessage(SMBBuffer buffer) throws Buffer.BufferException { } private List readFileNotifyInfo(SMBBuffer buffer, int outputBufferOffset, int outBufferLength) - throws Buffer.BufferException { + throws Buffer.BufferException { List notifyInfoList = new ArrayList<>(); buffer.rpos(outputBufferOffset); int currentPos = buffer.rpos(); @@ -57,11 +57,11 @@ private List readFileNotifyInfo(SMBBuffer buffer, int outputBuff long fileNameLen = 0; String fileName = null; - do { - nextEntryOffset = (int)buffer.readUInt32(); + do { + nextEntryOffset = (int) buffer.readUInt32(); FileNotifyAction action = EnumWithValue.EnumUtils.valueOf(buffer.readUInt32(), FileNotifyAction.class, null); fileNameLen = buffer.readUInt32(); - fileName = buffer.readString(StandardCharsets.UTF_16LE, (int)fileNameLen/2); + fileName = buffer.readString(StandardCharsets.UTF_16LE, (int) fileNameLen / 2); notifyInfoList.add(new FileNotifyInfo(action, fileName)); currentPos += nextEntryOffset; buffer.rpos(currentPos); @@ -94,9 +94,9 @@ public String getFileName() { @Override public String toString() { return "FileNotifyInfo{" + - "action=" + action + - ", fileName='" + fileName + '\'' + - '}'; + "action=" + action + + ", fileName='" + fileName + '\'' + + '}'; } } } diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2Close.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2Close.java index 420ff7a4..cfe6cfbc 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2Close.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2Close.java @@ -15,6 +15,7 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.Date; import com.hierynomus.msdtyp.MsDataTypes; import com.hierynomus.mserref.NtStatus; import com.hierynomus.mssmb2.SMB2Dialect; @@ -24,8 +25,6 @@ import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.Date; - /** * [MS-SMB2].pdf 2.2.15 SMB2 CLOSE Request / 2.2.16 SMB2 CLOSE Response */ diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateRequest.java index f92068f5..c9c4ff63 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateRequest.java @@ -15,12 +15,11 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.EnumSet; import com.hierynomus.msfscc.FileAttributes; import com.hierynomus.mssmb2.*; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.ensureNotNull; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toLong; diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateResponse.java index edf5624e..b1703d49 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2CreateResponse.java @@ -15,6 +15,8 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.Date; +import java.util.EnumSet; import com.hierynomus.msdtyp.MsDataTypes; import com.hierynomus.mserref.NtStatus; import com.hierynomus.msfscc.FileAttributes; @@ -23,14 +25,10 @@ import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.Date; -import java.util.EnumSet; - import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toEnumSet; /** * [MS-SMB2].pdf 2.2.14 SMB2 CREATE Response - * */ public class SMB2CreateResponse extends SMB2Packet { diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2IoctlResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2IoctlResponse.java index 3a40f6c2..31bb50a5 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2IoctlResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2IoctlResponse.java @@ -26,7 +26,8 @@ /** * [MS-SMB2].pdf 2.2.32 SMB2 IOCTL Response *

-\ */ + * \ + */ public class SMB2IoctlResponse extends SMB2Packet { private SMB2IoctlRequest.ControlCode controlCode; @@ -50,9 +51,9 @@ protected void readMessage(SMBBuffer buffer) throws Buffer.BufferException { fileId = SMB2FileId.read(buffer); // FileId (16 bytes) int inputOffset = buffer.readUInt32AsInt(); // Input Offset (4 bytes) - int inputCount = buffer.readUInt32AsInt(); // Input Count (4 bytes) + int inputCount = buffer.readUInt32AsInt(); // Input Count (4 bytes) int outputOffset = buffer.readUInt32AsInt(); // Input Offset (4 bytes) - int outputCount = buffer.readUInt32AsInt(); // Input Count (4 bytes) + int outputCount = buffer.readUInt32AsInt(); // Input Count (4 bytes) buffer.skip(4); // Flags (4 bytes) buffer.skip(4); // Reserved2 (4 bytes) diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateRequest.java index 07c450de..507099f5 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateRequest.java @@ -15,15 +15,14 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.EnumSet; +import java.util.UUID; import com.hierynomus.msdtyp.MsDataTypes; import com.hierynomus.mssmb2.SMB2Dialect; import com.hierynomus.mssmb2.SMB2MessageCommandCode; import com.hierynomus.mssmb2.SMB2Packet; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; -import java.util.UUID; - /** * [MS-SMB2].pdf 2.2.3 SMB2 Negotiate */ @@ -34,6 +33,7 @@ public class SMB2NegotiateRequest extends SMB2Packet { /** * Request constructor. + * * @param dialects * @param clientGuid */ @@ -45,6 +45,7 @@ public SMB2NegotiateRequest(EnumSet dialects, UUID clientGuid) { /** * The Request packet + * * @param buffer */ @Override diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateResponse.java index f34230f8..ca0ca82c 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2NegotiateResponse.java @@ -15,15 +15,14 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.Date; +import java.util.UUID; import com.hierynomus.msdtyp.MsDataTypes; import com.hierynomus.mssmb2.SMB2Dialect; import com.hierynomus.mssmb2.SMB2Packet; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.Date; -import java.util.UUID; - /** * [MS-SMB2].pdf 2.2.4 SMB2 Negotiate Response */ diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryDirectoryRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryDirectoryRequest.java index 3a9ad8e1..24971714 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryDirectoryRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryDirectoryRequest.java @@ -15,13 +15,12 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.EnumSet; import com.hierynomus.msfscc.FileInformationClass; import com.hierynomus.mssmb2.*; import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - /** * [MS-SMB2].pdf 2.2.33 SMB2 QUERY DIRECTORY Request *

diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoRequest.java index ba92c570..395e5b61 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoRequest.java @@ -15,6 +15,7 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.EnumSet; import com.hierynomus.msdtyp.SecurityInformation; import com.hierynomus.msfscc.FileInformationClass; import com.hierynomus.msfscc.FileSysemInformationClass; @@ -22,8 +23,6 @@ import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - /** * [MS-SMB2].pdf 2.2.37 SMB2 QUERY_INFO Request */ diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoResponse.java index 4d670a9b..b09a3e6c 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2QueryInfoResponse.java @@ -23,7 +23,8 @@ /** * [MS-SMB2].pdf 2.2.38 SMB2 QUERY_INFO Response *

-\ */ + * \ + */ public class SMB2QueryInfoResponse extends SMB2Packet { byte[] outputBuffer; diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadRequest.java index 0bc7549c..3a2b9487 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadRequest.java @@ -23,7 +23,6 @@ /** * [MS-SMB2].pdf 2.2.19 SMB2 READ Request - * */ public class SMB2ReadRequest extends SMB2MultiCreditPacket { diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadResponse.java index c0c8eccd..1430aa4f 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ReadResponse.java @@ -22,7 +22,6 @@ /** * [MS-SMB2].pdf 2.2.20 SMB2 READ Response - * */ public class SMB2ReadResponse extends SMB2Packet { @@ -30,7 +29,7 @@ public class SMB2ReadResponse extends SMB2Packet { private byte[] data; public SMB2ReadResponse() { - super(); + super(); } diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ResponseMessageFactory.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ResponseMessageFactory.java index 15ae9cba..8d73eabd 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2ResponseMessageFactory.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2ResponseMessageFactory.java @@ -26,7 +26,7 @@ public class SMB2ResponseMessageFactory { public static SMB2Packet read(SMBBuffer buffer) throws Buffer.BufferException { // Check we see a valid header start - Check.ensureEquals(buffer.readRawBytes(4), new byte[] {(byte) 0xFE, 'S', 'M', 'B'}, "Could not find SMB2 Packet header"); + Check.ensureEquals(buffer.readRawBytes(4), new byte[]{(byte) 0xFE, 'S', 'M', 'B'}, "Could not find SMB2 Packet header"); // Skip until Command buffer.skip(8); SMB2MessageCommandCode command = SMB2MessageCommandCode.lookup(buffer.readUInt16()); diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2SessionSetup.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2SessionSetup.java index d498dbce..49be7c5b 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2SessionSetup.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2SessionSetup.java @@ -15,6 +15,7 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.EnumSet; import com.hierynomus.mserref.NtStatus; import com.hierynomus.mssmb2.SMB2Dialect; import com.hierynomus.mssmb2.SMB2Header; @@ -23,9 +24,6 @@ import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import com.hierynomus.smbj.common.SMBRuntimeException; - -import java.util.EnumSet; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toEnumSet; @@ -48,7 +46,7 @@ public SMB2SessionSetup() { public SMB2SessionSetup(SMB2Dialect negotiatedDialect, EnumSet securityMode) { super(25, negotiatedDialect, SMB2MessageCommandCode.SMB2_SESSION_SETUP); this.negotiatedDialect = negotiatedDialect; - this.securityMode = (byte)EnumWithValue.EnumUtils.toLong(securityMode); + this.securityMode = (byte) EnumWithValue.EnumUtils.toLong(securityMode); } @Override @@ -59,9 +57,11 @@ protected void writeTo(SMBBuffer buffer) { buffer.putUInt32(clientCapabilities & 0x01); // Capabilities (4 bytes) (only last byte can be set) buffer.putReserved4(); // Channel (4 bytes) buffer.putUInt16(SMB2Header.STRUCTURE_SIZE + 25 - 1); // SecurityBufferOffset (2 bytes) (header structure size + Session setup structure size - 1) - buffer.putUInt16(securityBuffer.length); // SecurityBufferLength (2 bytes) + buffer.putUInt16((securityBuffer != null) ? securityBuffer.length : 0); // SecurityBufferLength (2 bytes) buffer.putUInt64(previousSessionId); // PreviousSessionId (8 bytes) - buffer.putRawBytes(securityBuffer); // SecurityBuffer (variable) + if (securityBuffer != null) { + buffer.putRawBytes(securityBuffer); // SecurityBuffer (variable) + } } @Override @@ -71,7 +71,7 @@ protected void readMessage(SMBBuffer buffer) throws Buffer.BufferException { int securityBufferOffset = buffer.readUInt16(); // SecurityBufferOffset (2 bytes) int securityBufferLength = buffer.readUInt16(); // SecurityBufferLength (2 bytes) if (getHeader().getStatus() == NtStatus.STATUS_SUCCESS || - getHeader().getStatus() == NtStatus.STATUS_MORE_PROCESSING_REQUIRED) { + getHeader().getStatus() == NtStatus.STATUS_MORE_PROCESSING_REQUIRED) { securityBuffer = readSecurityBuffer(buffer, securityBufferOffset, securityBufferLength); // SecurityBuffer (variable) } } @@ -81,8 +81,10 @@ private byte[] readSecurityBuffer(SMBBuffer buffer, int securityBufferOffset, in // Just to be sure, we should already be there. buffer.rpos(securityBufferOffset); return buffer.readRawBytes(securityBufferLength); + } else { + return new byte[0]; +// throw new SMBRuntimeException("The SMB2 Session Setup response should contain a positive length security buffer"); } - throw new SMBRuntimeException("The SMB2 Session Setup response should contain a positive length security buffer"); } private void putFlags(SMBBuffer buffer) { diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2SetInfoRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2SetInfoRequest.java index 164d3d9c..9b234f4b 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2SetInfoRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2SetInfoRequest.java @@ -33,10 +33,10 @@ public class SMB2SetInfoRequest extends SMB2Packet { private final SecurityInformation securityInformation; public SMB2SetInfoRequest( - SMB2Dialect negotiatedDialect, long sessionId, long treeId, - SMB2InfoType infoType, SMB2FileId fileId, - FileInformationClass fileInfoClass, - SecurityInformation securityInformation, byte[] buffer + SMB2Dialect negotiatedDialect, long sessionId, long treeId, + SMB2InfoType infoType, SMB2FileId fileId, + FileInformationClass fileInfoClass, + SecurityInformation securityInformation, byte[] buffer ) { super(33, negotiatedDialect, SMB2MessageCommandCode.SMB2_SET_INFO, sessionId, treeId); this.fileId = fileId; @@ -47,7 +47,6 @@ public SMB2SetInfoRequest( } /** - * * @param smbBuffer */ @Override diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2TreeConnectResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2TreeConnectResponse.java index b4db2ac5..68d35950 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2TreeConnectResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2TreeConnectResponse.java @@ -15,19 +15,18 @@ */ package com.hierynomus.mssmb2.messages; +import java.util.EnumSet; import com.hierynomus.mserref.NtStatus; import com.hierynomus.mssmb2.SMB2Packet; import com.hierynomus.mssmb2.SMB2ShareCapabilities; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.smbj.common.SMBBuffer; -import java.util.EnumSet; - import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toEnumSet; /** * [MS-SMB2].pdf 2.2.10 SMB2 TREE_CONNECT Response - * + *

* TODO */ public class SMB2TreeConnectResponse extends SMB2Packet { @@ -38,7 +37,7 @@ public class SMB2TreeConnectResponse extends SMB2Packet { private long maximalAccess; public SMB2TreeConnectResponse() { - super(); + super(); } @@ -56,6 +55,7 @@ protected void readMessage(SMBBuffer buffer) throws Buffer.BufferException { /** * Whether the ShareType returned is SMB2_SHARE_TYPE_DISK (0x01) + * * @return true if the ShareType returned is SMB2_SHARE_TYPE_DISK (0x01) */ public boolean isDiskShare() { @@ -64,6 +64,7 @@ public boolean isDiskShare() { /** * Whether the ShareType returned is SMB2_SHARE_TYPE_PIPE (0x02) + * * @return true if the ShareType returned is SMB2_SHARE_TYPE_PIPE (0x02) */ public boolean isNamedPipe() { @@ -72,6 +73,7 @@ public boolean isNamedPipe() { /** * Whether the ShareType returned is SMB2_SHARE_TYPE_PRINT (0x03) + * * @return true if the ShareType returned is SMB2_SHARE_TYPE_PRINT (0x03) */ public boolean isPrinterShare() { diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteRequest.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteRequest.java index 8fee9cc9..81a4b8c3 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteRequest.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteRequest.java @@ -16,12 +16,11 @@ package com.hierynomus.mssmb2.messages; import com.hierynomus.mssmb2.*; -import com.hierynomus.smbj.io.ByteChunkProvider; import com.hierynomus.smbj.common.SMBBuffer; +import com.hierynomus.smbj.io.ByteChunkProvider; /** * [MS-SMB2].pdf 2.2.21 SMB2 Write Request - * */ public class SMB2WriteRequest extends SMB2MultiCreditPacket { diff --git a/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteResponse.java b/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteResponse.java index cb4c6a78..ce1d304d 100644 --- a/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteResponse.java +++ b/src/main/java/com/hierynomus/mssmb2/messages/SMB2WriteResponse.java @@ -22,14 +22,13 @@ /** * [MS-SMB2].pdf 2.2.22 SMB2 Write Response - * */ public class SMB2WriteResponse extends SMB2Packet { private long bytesWritten; public SMB2WriteResponse() { - super(); + super(); } diff --git a/src/main/java/com/hierynomus/ntlm/functions/NtlmFunctions.java b/src/main/java/com/hierynomus/ntlm/functions/NtlmFunctions.java index 934b517f..989f6ef1 100644 --- a/src/main/java/com/hierynomus/ntlm/functions/NtlmFunctions.java +++ b/src/main/java/com/hierynomus/ntlm/functions/NtlmFunctions.java @@ -15,18 +15,17 @@ */ package com.hierynomus.ntlm.functions; -import com.hierynomus.msdtyp.MsDataTypes; -import com.hierynomus.ntlm.NtlmException; -import com.hierynomus.protocol.commons.buffer.Buffer; -import com.hierynomus.protocol.commons.buffer.Endian; - -import javax.crypto.*; -import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.*; import java.util.Arrays; +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import com.hierynomus.msdtyp.MsDataTypes; +import com.hierynomus.ntlm.NtlmException; +import com.hierynomus.protocol.commons.buffer.Buffer; +import com.hierynomus.protocol.commons.buffer.Endian; /** * NTLM Helper functions @@ -155,7 +154,7 @@ public static byte[] LMOWFv1(String password, String username, String userDomain /** * [MS-NLMP].pdf 2.2.2.7 NTLM v2: NTLMv2_CLIENT_CHALLENGE - * + *

* 3.3.2 NTLM v2 Authentication * Set temp to ConcatenationOf(Responserversion, HiResponserversion, Z(6), Time, ClientChallenge, Z(4), ServerName, Z(4)) * @@ -170,8 +169,8 @@ public static byte[] getNTLMv2ClientChallenge(byte[] targetInformation) { long nowAsFileTime = MsDataTypes.nowAsFileTime(); byte[] l_targetInfo = (targetInformation == null) ? new byte[0] : targetInformation; Buffer.PlainBuffer ccBuf = new Buffer.PlainBuffer(Endian.LE); - ccBuf.putByte((byte)0x01); // RespType (1) - ccBuf.putByte((byte)0x01); // HiRespType (1) + ccBuf.putByte((byte) 0x01); // RespType (1) + ccBuf.putByte((byte) 0x01); // HiRespType (1) ccBuf.putUInt16(0); // Reserved1 (2) ccBuf.putUInt32(0); // Reserved2 (4) ccBuf.putLong(nowAsFileTime); // Timestamp (8) @@ -184,9 +183,8 @@ public static byte[] getNTLMv2ClientChallenge(byte[] targetInformation) { } /** - * * 3.3.2 NTLM v2 Authentication - * + *

* Set NTProofStr to HMAC_MD5(ResponseKeyNT, ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,temp)) * Set NtChallengeResponse to ConcatenationOf(NTProofStr, temp) * @@ -211,7 +209,7 @@ public static byte[] encryptRc4(byte[] key, byte[] val) throws NtlmException { try { Cipher c = getRC4Cipher(key); byte[] enc = c.doFinal(val); - return enc; + return enc; } catch (BadPaddingException | IllegalBlockSizeException e) { throw new NtlmException(e); } @@ -223,7 +221,9 @@ public static SecureRandom getRandom() { } public static void setRandom(SecureRandom sRandom) { - if (sRandom != null) secureRandom = sRandom; + if (sRandom != null) { + secureRandom = sRandom; + } } @@ -231,17 +231,17 @@ private static byte[] setupKey(byte[] key56) { byte[] key = new byte[8]; key[0] = (byte) ((key56[0] >> 1) & 0xff); key[1] = (byte) ((((key56[0] & 0x01) << 6) - | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff); + | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff); key[2] = (byte) ((((key56[1] & 0x03) << 5) - | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff); + | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff); key[3] = (byte) ((((key56[2] & 0x07) << 4) - | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff); + | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff); key[4] = (byte) ((((key56[3] & 0x0f) << 3) - | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff); + | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff); key[5] = (byte) ((((key56[4] & 0x1f) << 2) - | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff); + | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff); key[6] = (byte) ((((key56[5] & 0x3f) << 1) - | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff); + | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff); key[7] = (byte) (key56[6] & 0x7f); for (int i = 0; i < key.length; i++) { diff --git a/src/main/java/com/hierynomus/ntlm/messages/NtlmAuthenticate.java b/src/main/java/com/hierynomus/ntlm/messages/NtlmAuthenticate.java index 4cff6b8e..8a6878ea 100644 --- a/src/main/java/com/hierynomus/ntlm/messages/NtlmAuthenticate.java +++ b/src/main/java/com/hierynomus/ntlm/messages/NtlmAuthenticate.java @@ -15,12 +15,11 @@ */ package com.hierynomus.ntlm.messages; +import java.nio.charset.StandardCharsets; import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.protocol.commons.buffer.Endian; -import java.nio.charset.StandardCharsets; - import static com.hierynomus.ntlm.functions.NtlmFunctions.unicode; /** @@ -38,9 +37,9 @@ public class NtlmAuthenticate extends NtlmPacket { private long negotiateFlags; public NtlmAuthenticate( - byte[] lmResponse, byte[] ntResponse, - String userName, String domainName, String workstation, - byte[] encryptedRandomSessionKey, long negotiateFlags + byte[] lmResponse, byte[] ntResponse, + String userName, String domainName, String workstation, + byte[] encryptedRandomSessionKey, long negotiateFlags ) { super(); this.lmResponse = ensureNotNull(lmResponse); @@ -92,16 +91,17 @@ public void write(Buffer.PlainBuffer buffer) { /** * MS-NLMP 2.2.2.10 VERSION + * * @return */ public byte[] getVersion() { Buffer.PlainBuffer plainBuffer = new Buffer.PlainBuffer(Endian.LE); - plainBuffer.putByte((byte)0x06); // Major Version 6 - plainBuffer.putByte((byte)0x01); // Minor Version 1 + plainBuffer.putByte((byte) 0x06); // Major Version 6 + plainBuffer.putByte((byte) 0x01); // Minor Version 1 plainBuffer.putUInt16(7600); // Product Build 7600 - byte[] reserved = {(byte)0x00, (byte)0x00, (byte)0x00}; + byte[] reserved = {(byte) 0x00, (byte) 0x00, (byte) 0x00}; plainBuffer.putRawBytes(reserved); // Reserver 3 bytes - plainBuffer.putByte((byte)0x0F); // NTLM Revision Current + plainBuffer.putByte((byte) 0x0F); // NTLM Revision Current return plainBuffer.getCompactData(); } diff --git a/src/main/java/com/hierynomus/ntlm/messages/NtlmChallenge.java b/src/main/java/com/hierynomus/ntlm/messages/NtlmChallenge.java index bd4cffb0..6f7d294e 100644 --- a/src/main/java/com/hierynomus/ntlm/messages/NtlmChallenge.java +++ b/src/main/java/com/hierynomus/ntlm/messages/NtlmChallenge.java @@ -15,16 +15,15 @@ */ package com.hierynomus.ntlm.messages; -import com.hierynomus.msdtyp.MsDataTypes; -import com.hierynomus.protocol.commons.EnumWithValue; -import com.hierynomus.protocol.commons.buffer.Buffer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.nio.charset.StandardCharsets; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.hierynomus.msdtyp.MsDataTypes; +import com.hierynomus.protocol.commons.EnumWithValue; +import com.hierynomus.protocol.commons.buffer.Buffer; /** * [MS-NLMP].pdf 2.2.1.2 CHALLENGE_MESSAGE diff --git a/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java b/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java index 8caadd8e..7d4bb535 100644 --- a/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java +++ b/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java @@ -15,29 +15,38 @@ */ package com.hierynomus.ntlm.messages; -import com.hierynomus.protocol.commons.buffer.Buffer; - import java.nio.charset.StandardCharsets; import java.util.EnumSet; +import com.hierynomus.protocol.commons.buffer.Buffer; -import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.*; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.EnumUtils; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_128; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_56; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_ALWAYS_SIGN; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_KEY_EXCH; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_NTLM; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_SIGN; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_TARGET_INFO; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_UNICODE; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_REQUEST_TARGET; /** * [MS-NLMP].pdf 2.2.1.1 NEGOTIATE_MESSAGE */ public class NtlmNegotiate extends NtlmPacket { - public static final long DEFAULT_FLAGS = EnumUtils.toLong(EnumSet.of( - NTLMSSP_NEGOTIATE_56, - NTLMSSP_NEGOTIATE_128, - NTLMSSP_NEGOTIATE_TARGET_INFO, - NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, - NTLMSSP_NEGOTIATE_SIGN, - NTLMSSP_NEGOTIATE_ALWAYS_SIGN, - NTLMSSP_NEGOTIATE_KEY_EXCH, - NTLMSSP_NEGOTIATE_NTLM, - NTLMSSP_NEGOTIATE_NTLM, - NTLMSSP_REQUEST_TARGET, - NTLMSSP_NEGOTIATE_UNICODE)); + public static final long DEFAULT_FLAGS = EnumUtils.toLong(EnumSet.of( + NTLMSSP_NEGOTIATE_56, + NTLMSSP_NEGOTIATE_128, + NTLMSSP_NEGOTIATE_TARGET_INFO, + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, + NTLMSSP_NEGOTIATE_SIGN, + NTLMSSP_NEGOTIATE_ALWAYS_SIGN, + NTLMSSP_NEGOTIATE_KEY_EXCH, + NTLMSSP_NEGOTIATE_NTLM, + NTLMSSP_NEGOTIATE_NTLM, + NTLMSSP_REQUEST_TARGET, + NTLMSSP_NEGOTIATE_UNICODE)); private long flags = DEFAULT_FLAGS; diff --git a/src/main/java/com/hierynomus/protocol/commons/Base64.java b/src/main/java/com/hierynomus/protocol/commons/Base64.java index 60b505da..98780c8f 100644 --- a/src/main/java/com/hierynomus/protocol/commons/Base64.java +++ b/src/main/java/com/hierynomus/protocol/commons/Base64.java @@ -32,7 +32,7 @@ public class Base64 { * @since 1.3 */ public static class InputStream - extends java.io.FilterInputStream { + extends java.io.FilterInputStream { private final boolean encode; // Encoding or decoding private int position; // Current position in the buffer @@ -97,7 +97,7 @@ public InputStream(java.io.InputStream in, int options) { */ @Override public int read() - throws java.io.IOException { + throws java.io.IOException { // Do we need to get data? if (position < 0) @@ -194,7 +194,7 @@ else if (i == 0) */ @Override public int read(byte[] dest, int off, int len) - throws java.io.IOException { + throws java.io.IOException { int i; int b; for (i = 0; i < len; i++) { @@ -220,7 +220,7 @@ else if (i == 0) * @since 1.3 */ public static class OutputStream - extends java.io.FilterOutputStream { + extends java.io.FilterOutputStream { private final boolean encode; private int position; @@ -286,7 +286,7 @@ public OutputStream(java.io.OutputStream out, int options) { */ @Override public void close() - throws java.io.IOException { + throws java.io.IOException { // 1. Ensure that pending characters are written flush(); @@ -306,7 +306,7 @@ public void close() */ @Override public void flush() - throws java.io.IOException { + throws java.io.IOException { flushBase64(); super.flush(); } @@ -317,7 +317,7 @@ public void flush() * @throws java.io.IOException if there's an error. */ public void flushBase64() - throws java.io.IOException { + throws java.io.IOException { if (position > 0) if (encode) { out.write(encode3to4(b4, buffer, position, options)); @@ -346,7 +346,7 @@ public void resumeEncoding() { * @since 1.5.1 */ public void suspendEncoding() - throws java.io.IOException { + throws java.io.IOException { flushBase64(); suspendEncoding = true; } // end suspendEncoding @@ -361,7 +361,7 @@ public void suspendEncoding() */ @Override public void write(byte[] theBytes, int off, int len) - throws java.io.IOException { + throws java.io.IOException { // Encoding suspended? if (suspendEncoding) { super.out.write(theBytes, off, len); @@ -383,7 +383,7 @@ public void write(byte[] theBytes, int off, int len) */ @Override public void write(int theByte) - throws java.io.IOException { + throws java.io.IOException { // Encoding suspended? if (suspendEncoding) { super.out.write(theByte); @@ -494,40 +494,40 @@ else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) */ /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ private final static byte[] _STANDARD_ALPHABET = {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', - (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', - (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', - (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', - (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', - (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', - (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', - (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/'}; + (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', + (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', + (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', + (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', + (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', + (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', + (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/'}; /** * Translates a Base64 value to either its 6-bit reconstruction value or a negative number indicating some other * meaning. */ private final static byte[] _STANDARD_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal - // 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9, -9, -9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' - -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' - -9, -9, -9, -9 // Decimal 123 - 126 + // 0 - 8 + -5, -5, // Whitespace: Tab and Linefeed + -9, -9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 + -9, -9, -9, -9, -9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9, -9, -9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine + -9, -9, -9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9, -9, -9, // Decimal 62 - 64 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' + -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' + -9, -9, -9, -9 // Decimal 123 - 126 /* * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 @@ -550,43 +550,43 @@ else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) * bytes become "hyphen" and "underscore" instead of "plus" and "slash." */ private final static byte[] _URL_SAFE_ALPHABET = {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', - (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', - (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', - (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', - (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', - (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', - (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', - (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_'}; + (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', + (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', + (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', + (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', + (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', + (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', + (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_'}; /** * Used in decoding URL- and Filename-safe dialects of Base64. */ private final static byte[] _URL_SAFE_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal - // 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' - -9, -9, -9, -9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' - -9, -9, -9, -9 // Decimal 123 - 126 + // 0 - 8 + -5, -5, // Whitespace: Tab and Linefeed + -9, -9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 + -9, -9, -9, -9, -9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine + -9, -9, -9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9, -9, -9, // Decimal 62 - 64 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' + -9, -9, -9, -9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' + -9, -9, -9, -9 // Decimal 123 - 126 /* * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 @@ -608,43 +608,43 @@ else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html. */ private final static byte[] _ORDERED_ALPHABET = {(byte) '-', (byte) '0', (byte) '1', (byte) '2', (byte) '3', - (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', - (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', - (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', - (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c', - (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', - (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', - (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z'}; + (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', + (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', + (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', + (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c', + (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', + (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', + (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z'}; /** * Used in decoding the "ordered" dialect of Base64. */ private final static byte[] _ORDERED_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal - // 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M' - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z' - -9, -9, -9, -9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm' - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z' - -9, -9, -9, -9 // Decimal 123 - 126 + // 0 - 8 + -5, -5, // Whitespace: Tab and Linefeed + -9, -9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 + -9, -9, -9, -9, -9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine + -9, -9, -9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9, -9, -9, // Decimal 62 - 64 + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M' + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z' + -9, -9, -9, -9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm' + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z' + -9, -9, -9, -9 // Decimal 123 - 126 /* * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 @@ -696,21 +696,21 @@ public static byte[] decode(byte[] source) { * @since 1.3 */ public static byte[] decode(byte[] source, int off, int len, int options) - throws java.io.IOException { + throws java.io.IOException { // Lots of error checking and exception throwing if (source == null) throw new NullPointerException("Cannot decode null source array."); if (off < 0 || off + len > source.length) throw new IllegalArgumentException(String.format( - "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, - len)); + "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, + len)); if (len == 0) return new byte[0]; else if (len < 4) throw new IllegalArgumentException( - "Base64-encoded string must have at least four characters, but length specified was " + len); + "Base64-encoded string must have at least four characters, but length specified was " + len); byte[] DECODABET = getDecodabet(options); @@ -748,7 +748,7 @@ else if (len < 4) else // There's a bad input character in the Base64 stream. throw new java.io.IOException(String.format("Bad Base64 input character '%c' in array position %d", - source[i], i)); + source[i], i)); } // each input character byte[] out = new byte[outBuffPosn]; @@ -765,7 +765,7 @@ else if (len < 4) * @since 1.4 */ public static byte[] decode(String s) - throws java.io.IOException { + throws java.io.IOException { return decode(s, NO_OPTIONS); } @@ -780,7 +780,7 @@ public static byte[] decode(String s) * @since 1.4 */ public static byte[] decode(String s, int options) - throws java.io.IOException { + throws java.io.IOException { if (s == null) throw new NullPointerException("Input string was null."); @@ -854,7 +854,7 @@ public static byte[] decode(String s, int options) * @since 2.2 */ public static void decodeFileToFile(String infile, String outfile) - throws java.io.IOException { + throws java.io.IOException { byte[] decoded = Base64.decodeFromFile(infile); java.io.OutputStream out = null; @@ -884,7 +884,7 @@ public static void decodeFileToFile(String infile, String outfile) * @since 2.1 */ public static byte[] decodeFromFile(String filename) - throws java.io.IOException { + throws java.io.IOException { byte[] decodedData = null; Base64.InputStream bis = null; @@ -898,12 +898,12 @@ public static byte[] decodeFromFile(String filename) // Check for size of file if (file.length() > Integer.MAX_VALUE) throw new java.io.IOException("File is too big for this convenience method (" + file.length() - + " bytes)."); + + " bytes)."); buffer = new byte[(int) file.length()]; // Open a stream bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), - Base64.DECODE); + Base64.DECODE); // Read until done while ((numBytes = bis.read(buffer, length, 4096)) >= 0) @@ -938,7 +938,7 @@ public static byte[] decodeFromFile(String filename) * @since 2.1 */ public static void decodeToFile(String dataToDecode, String filename) - throws java.io.IOException { + throws java.io.IOException { Base64.OutputStream bos = null; try { @@ -969,8 +969,8 @@ public static void decodeToFile(String dataToDecode, String filename) * @since 1.5 */ public static Object decodeToObject(String encodedObject) - throws java.io.IOException, - java.lang.ClassNotFoundException { + throws java.io.IOException, + java.lang.ClassNotFoundException { // Decode and gunzip if necessary byte[] objBytes = decode(encodedObject); @@ -1092,7 +1092,7 @@ public static String encodeBytes(byte[] source) { * @since 2.0 */ public static String encodeBytes(byte[] source, int options) - throws java.io.IOException { + throws java.io.IOException { return encodeBytes(source, 0, source.length, options); } // end encodeBytes @@ -1148,7 +1148,7 @@ public static String encodeBytes(byte[] source, int off, int len) { * @since 2.0 */ public static String encodeBytes(byte[] source, int off, int len, int options) - throws java.io.IOException { + throws java.io.IOException { byte[] encoded = encodeBytesToBytes(source, off, len, options); // Return value according to relevant encoding. @@ -1197,7 +1197,7 @@ public static byte[] encodeBytesToBytes(byte[] source) { * @since 2.3.1 */ public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) - throws java.io.IOException { + throws java.io.IOException { if (source == null) throw new NullPointerException("Cannot serialize a null array."); @@ -1210,7 +1210,7 @@ public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int opt if (off + len > source.length) throw new IllegalArgumentException(String.format( - "Cannot have offset of %d and length of %d with array of length %d", off, len, source.length)); + "Cannot have offset of %d and length of %d with array of length %d", off, len, source.length)); // Compress? if ((options & GZIP) > 0) { @@ -1310,7 +1310,7 @@ public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int opt * @since 2.2 */ public static void encodeFileToFile(String infile, String outfile) - throws java.io.IOException { + throws java.io.IOException { String encoded = Base64.encodeFromFile(infile); java.io.OutputStream out = null; @@ -1340,7 +1340,7 @@ public static void encodeFileToFile(String infile, String outfile) * @since 2.1 */ public static String encodeFromFile(String filename) - throws java.io.IOException { + throws java.io.IOException { String encodedData = null; Base64.InputStream bis = null; @@ -1355,7 +1355,7 @@ public static String encodeFromFile(String filename) // Open a stream bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), - Base64.ENCODE); + Base64.ENCODE); // Read until done while ((numBytes = bis.read(buffer, length, 4096)) >= 0) @@ -1393,7 +1393,7 @@ public static String encodeFromFile(String filename) * @since 1.4 */ public static String encodeObject(java.io.Serializable serializableObject) - throws java.io.IOException { + throws java.io.IOException { return encodeObject(serializableObject, NO_OPTIONS); } // end encodeObject @@ -1425,7 +1425,7 @@ public static String encodeObject(java.io.Serializable serializableObject) * @since 2.0 */ public static String encodeObject(java.io.Serializable serializableObject, int options) - throws java.io.IOException { + throws java.io.IOException { if (serializableObject == null) throw new NullPointerException("Cannot serialize a null object."); @@ -1486,7 +1486,7 @@ public static String encodeObject(java.io.Serializable serializableObject, int o * @since 2.1 */ public static void encodeToFile(byte[] dataToEncode, String filename) - throws java.io.IOException { + throws java.io.IOException { if (dataToEncode == null) throw new NullPointerException("Data to encode was null."); @@ -1536,12 +1536,12 @@ private static int decode4to3(byte[] source, int srcOffset, byte[] destination, throw new NullPointerException("Destination array was null."); if (srcOffset < 0 || srcOffset + 3 >= source.length) throw new IllegalArgumentException(String.format( - "Source array with length %d cannot have offset of %d and still process four bytes.", - source.length, srcOffset)); + "Source array with length %d cannot have offset of %d and still process four bytes.", + source.length, srcOffset)); if (destOffset < 0 || destOffset + 2 >= destination.length) throw new IllegalArgumentException(String.format( - "Destination array with length %d cannot have offset of %d and still store three bytes.", - destination.length, destOffset)); + "Destination array with length %d cannot have offset of %d and still store three bytes.", + destination.length, destOffset)); byte[] DECODABET = getDecodabet(options); @@ -1563,7 +1563,7 @@ else if (source[srcOffset + 3] == EQUALS_SIGN) { // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); int outBuff = (DECODABET[source[srcOffset]] & 0xFF) << 18 | (DECODABET[source[srcOffset + 1]] & 0xFF) << 12 - | (DECODABET[source[srcOffset + 2]] & 0xFF) << 6; + | (DECODABET[source[srcOffset + 2]] & 0xFF) << 6; destination[destOffset] = (byte) (outBuff >>> 16); destination[destOffset + 1] = (byte) (outBuff >>> 8); @@ -1578,7 +1578,7 @@ else if (source[srcOffset + 3] == EQUALS_SIGN) { // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); int outBuff = (DECODABET[source[srcOffset]] & 0xFF) << 18 | (DECODABET[source[srcOffset + 1]] & 0xFF) << 12 - | (DECODABET[source[srcOffset + 2]] & 0xFF) << 6 | DECODABET[source[srcOffset + 3]] & 0xFF; + | (DECODABET[source[srcOffset + 2]] & 0xFF) << 6 | DECODABET[source[srcOffset + 3]] & 0xFF; destination[destOffset] = (byte) (outBuff >> 16); destination[destOffset + 1] = (byte) (outBuff >> 8); @@ -1639,8 +1639,8 @@ private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, // We have to shift left 24 in order to flush out the 1's that appear // when Java treats a value as negative that is cast from a byte to an int. int inBuff = (numSigBytes > 0 ? source[srcOffset] << 24 >>> 8 : 0) - | (numSigBytes > 1 ? source[srcOffset + 1] << 24 >>> 16 : 0) - | (numSigBytes > 2 ? source[srcOffset + 2] << 24 >>> 24 : 0); + | (numSigBytes > 1 ? source[srcOffset + 1] << 24 >>> 16 : 0) + | (numSigBytes > 2 ? source[srcOffset + 2] << 24 >>> 24 : 0); switch (numSigBytes) { case 3: diff --git a/src/main/java/com/hierynomus/protocol/commons/ByteArrayUtils.java b/src/main/java/com/hierynomus/protocol/commons/ByteArrayUtils.java index d67fcc33..3d1fccb0 100644 --- a/src/main/java/com/hierynomus/protocol/commons/ByteArrayUtils.java +++ b/src/main/java/com/hierynomus/protocol/commons/ByteArrayUtils.java @@ -15,7 +15,9 @@ */ package com.hierynomus.protocol.commons; -/** Utility functions for byte arrays. */ +/** + * Utility functions for byte arrays. + */ public class ByteArrayUtils { final static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; @@ -29,7 +31,6 @@ public class ByteArrayUtils { * @param a2 * @param a2Offset * @param length - * * @return true or false */ public static boolean equals(byte[] a1, final int a1Offset, byte[] a2, final int a2Offset, final int length) { @@ -48,7 +49,6 @@ public static boolean equals(byte[] a1, final int a1Offset, byte[] a2, final int * Get a hexadecimal representation of the full byte array, with each octet separated by a space. * * @param array - * * @return hex string, each octet delimited by a space */ public static String printHex(byte[] array) { @@ -62,7 +62,6 @@ public static String printHex(byte[] array) { * @param array * @param offset * @param len - * * @return hex string, each octet delimited by a space */ public static String printHex(byte[] array, int offset, int len) { @@ -81,7 +80,6 @@ public static String printHex(byte[] array, int offset, int len) { * Get the hexadecimal representation of a byte array. * * @param array - * * @return hex string */ public static String toHex(byte[] array) { @@ -95,7 +93,6 @@ public static String toHex(byte[] array) { * @param array * @param offset * @param len - * * @return hex string */ public static String toHex(byte[] array, int offset, int len) { diff --git a/src/main/java/com/hierynomus/protocol/commons/Factory.java b/src/main/java/com/hierynomus/protocol/commons/Factory.java index eb73d3e8..bd063332 100644 --- a/src/main/java/com/hierynomus/protocol/commons/Factory.java +++ b/src/main/java/com/hierynomus/protocol/commons/Factory.java @@ -32,7 +32,7 @@ public interface Factory { * @param type of object created by this factory */ interface Named - extends Factory { + extends Factory { /** * Utility functions diff --git a/src/main/java/com/hierynomus/protocol/commons/IOUtils.java b/src/main/java/com/hierynomus/protocol/commons/IOUtils.java index 91d9533b..9ae7deb6 100644 --- a/src/main/java/com/hierynomus/protocol/commons/IOUtils.java +++ b/src/main/java/com/hierynomus/protocol/commons/IOUtils.java @@ -15,11 +15,10 @@ */ package com.hierynomus.protocol.commons; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.Closeable; import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class IOUtils { diff --git a/src/main/java/com/hierynomus/protocol/commons/backport/Jdk7HttpProxySocket.java b/src/main/java/com/hierynomus/protocol/commons/backport/Jdk7HttpProxySocket.java index 9618ed24..c64982d5 100644 --- a/src/main/java/com/hierynomus/protocol/commons/backport/Jdk7HttpProxySocket.java +++ b/src/main/java/com/hierynomus/protocol/commons/backport/Jdk7HttpProxySocket.java @@ -55,7 +55,7 @@ private void connectHttpProxy(SocketAddress endpoint, int timeout) throws IOExce checkAndFlushProxyResponse(); } - private void checkAndFlushProxyResponse()throws IOException { + private void checkAndFlushProxyResponse() throws IOException { InputStream socketInput = getInputStream(); byte[] tmpBuffer = new byte[512]; int len = socketInput.read(tmpBuffer, 0, tmpBuffer.length); diff --git a/src/main/java/com/hierynomus/protocol/commons/buffer/Buffer.java b/src/main/java/com/hierynomus/protocol/commons/buffer/Buffer.java index 739bd4e4..25272b50 100644 --- a/src/main/java/com/hierynomus/protocol/commons/buffer/Buffer.java +++ b/src/main/java/com/hierynomus/protocol/commons/buffer/Buffer.java @@ -15,20 +15,19 @@ */ package com.hierynomus.protocol.commons.buffer; -import com.hierynomus.protocol.commons.ByteArrayUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.hierynomus.protocol.commons.ByteArrayUtils; public class Buffer> { private static final Logger logger = LoggerFactory.getLogger(Buffer.class); public static class BufferException - extends Exception { + extends Exception { public BufferException(String message) { super(message); @@ -179,7 +178,7 @@ public void wpos(int wpos) { * @throws BufferException If there are less than a bytes available */ protected void ensureAvailable(int a) - throws BufferException { + throws BufferException { if (available() < a) { throw new BufferException("Underflow"); } @@ -229,7 +228,7 @@ public byte[] getCompactData() { * @return the {@code true} or {@code false} value read */ public boolean readBoolean() - throws BufferException { + throws BufferException { return readByte() != 0; } @@ -249,7 +248,7 @@ public Buffer putBoolean(boolean b) { * @return the byte read */ public byte readByte() - throws BufferException { + throws BufferException { ensureAvailable(1); return data[rpos++]; } @@ -286,7 +285,7 @@ public byte[] readRawBytes(int length) throws BufferException { * @throws BufferException If the read operation would cause an underflow (less bytes available than array size) */ public void readRawBytes(byte[] buf) - throws BufferException { + throws BufferException { readRawBytes(buf, 0, buf.length); } @@ -299,7 +298,7 @@ public void readRawBytes(byte[] buf) * @throws BufferException If the read operation would cause an underflow (less than length bytes available) */ public void readRawBytes(byte[] buf, int offset, int length) - throws BufferException { + throws BufferException { ensureAvailable(length); System.arraycopy(data, rpos, buf, offset, length); rpos += length; @@ -530,8 +529,9 @@ public Buffer putUInt64(long uint64, Endian endianness) { /** * Writes a long in the buffer's endianness. - * + *

* Note: unlike a uint64, a long can be negative. + * * @param longVal * @return this */ @@ -541,8 +541,9 @@ public Buffer putLong(long longVal) { /** * Writes a long in the specified endianness. - * + *

* Note: unlike a uint64, a long can be negative or overflowed. + * * @param longVal * @return this */ diff --git a/src/main/java/com/hierynomus/protocol/commons/buffer/Endian.java b/src/main/java/com/hierynomus/protocol/commons/buffer/Endian.java index 5d151bb9..2d965512 100644 --- a/src/main/java/com/hierynomus/protocol/commons/buffer/Endian.java +++ b/src/main/java/com/hierynomus/protocol/commons/buffer/Endian.java @@ -41,7 +41,7 @@ public > void writeUInt16(Buffer buffer, int uint16) { public > int readUInt16(Buffer buffer) throws Buffer.BufferException { buffer.ensureAvailable(2); return buffer.data[buffer.rpos++] << 8 & 0xFF00 | - buffer.data[buffer.rpos++] & 0x00FF; + buffer.data[buffer.rpos++] & 0x00FF; } @Override @@ -58,8 +58,8 @@ public > void writeUInt24(Buffer buffer, int uint24) { public > int readUInt24(Buffer buffer) throws Buffer.BufferException { buffer.ensureAvailable(3); return buffer.data[buffer.rpos++] << 16 & 0xFF0000 | - buffer.data[buffer.rpos++] << 8 & 0x00FF00 | - buffer.data[buffer.rpos++] & 0x0000FF; + buffer.data[buffer.rpos++] << 8 & 0x00FF00 | + buffer.data[buffer.rpos++] & 0x0000FF; } @Override @@ -77,9 +77,9 @@ public > void writeUInt32(Buffer buffer, long uint32) { public > long readUInt32(Buffer buffer) throws Buffer.BufferException { buffer.ensureAvailable(4); return buffer.data[buffer.rpos++] << 24 & 0xFF000000L | - buffer.data[buffer.rpos++] << 16 & 0x00FF0000L | - buffer.data[buffer.rpos++] << 8 & 0x0000FF00L | - buffer.data[buffer.rpos++] & 0x000000FFL; + buffer.data[buffer.rpos++] << 16 & 0x00FF0000L | + buffer.data[buffer.rpos++] << 8 & 0x0000FF00L | + buffer.data[buffer.rpos++] & 0x000000FFL; } @Override @@ -155,7 +155,7 @@ public > void writeUInt16(Buffer buffer, int uint16) { public > int readUInt16(Buffer buffer) throws Buffer.BufferException { buffer.ensureAvailable(2); return buffer.data[buffer.rpos++] & 0x00FF | - buffer.data[buffer.rpos++] << 8 & 0xFF00; + buffer.data[buffer.rpos++] << 8 & 0xFF00; } @Override @@ -173,8 +173,8 @@ public > void writeUInt24(Buffer buffer, int uint24) { public > int readUInt24(Buffer buffer) throws Buffer.BufferException { buffer.ensureAvailable(3); return buffer.data[buffer.rpos++] & 0x0000FF | - buffer.data[buffer.rpos++] << 8 & 0x00FF00 | - buffer.data[buffer.rpos++] << 16 & 0xFF0000; + buffer.data[buffer.rpos++] << 8 & 0x00FF00 | + buffer.data[buffer.rpos++] << 16 & 0xFF0000; } @Override @@ -193,9 +193,9 @@ public > void writeUInt32(Buffer buffer, long uint32) { public > long readUInt32(Buffer buffer) throws Buffer.BufferException { buffer.ensureAvailable(4); return buffer.data[buffer.rpos++] & 0x000000FFL | - buffer.data[buffer.rpos++] << 8 & 0x0000FF00L | - buffer.data[buffer.rpos++] << 16 & 0x00FF0000L | - buffer.data[buffer.rpos++] << 24 & 0xFF000000L; + buffer.data[buffer.rpos++] << 8 & 0x0000FF00L | + buffer.data[buffer.rpos++] << 16 & 0x00FF0000L | + buffer.data[buffer.rpos++] << 24 & 0xFF000000L; } @Override diff --git a/src/main/java/com/hierynomus/protocol/commons/concurrent/Promise.java b/src/main/java/com/hierynomus/protocol/commons/concurrent/Promise.java index 88b012dd..a3286710 100644 --- a/src/main/java/com/hierynomus/protocol/commons/concurrent/Promise.java +++ b/src/main/java/com/hierynomus/protocol/commons/concurrent/Promise.java @@ -15,15 +15,14 @@ */ package com.hierynomus.protocol.commons.concurrent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Represents promised data of the parameterized type {@code V} and allows waiting on it. An exception may also be @@ -121,7 +120,7 @@ public void clear() { * @throws T in case another thread informs the promise of an error meanwhile */ public V retrieve() - throws T { + throws T { return tryRetrieve(0, TimeUnit.SECONDS); } @@ -134,7 +133,7 @@ public V retrieve() * @throws T in case another thread informs the promise of an error meanwhile, or the timeout expires */ public V retrieve(long timeout, TimeUnit unit) - throws T { + throws T { final V value = tryRetrieve(timeout, unit); if (value == null) throw wrapper.wrap(new TimeoutException("Timeout expired")); @@ -153,7 +152,7 @@ public V retrieve(long timeout, TimeUnit unit) * @throws T in case another thread informs the promise of an error meanwhile */ public V tryRetrieve(long timeout, TimeUnit unit) - throws T { + throws T { lock.lock(); try { if (pendingEx != null) diff --git a/src/main/java/com/hierynomus/protocol/commons/socket/ProxySocketFactory.java b/src/main/java/com/hierynomus/protocol/commons/socket/ProxySocketFactory.java index 985f17d0..375c691c 100644 --- a/src/main/java/com/hierynomus/protocol/commons/socket/ProxySocketFactory.java +++ b/src/main/java/com/hierynomus/protocol/commons/socket/ProxySocketFactory.java @@ -15,15 +15,14 @@ */ package com.hierynomus.protocol.commons.socket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.SocketFactory; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; +import javax.net.SocketFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ProxySocketFactory extends SocketFactory { private static final Logger logger = LoggerFactory.getLogger(ProxySocketFactory.class); diff --git a/src/main/java/com/hierynomus/protocol/commons/socket/SocketClient.java b/src/main/java/com/hierynomus/protocol/commons/socket/SocketClient.java index 78fe7655..4d04b649 100644 --- a/src/main/java/com/hierynomus/protocol/commons/socket/SocketClient.java +++ b/src/main/java/com/hierynomus/protocol/commons/socket/SocketClient.java @@ -15,14 +15,17 @@ */ package com.hierynomus.protocol.commons.socket; -import javax.net.SocketFactory; +import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; +import javax.net.SocketFactory; public abstract class SocketClient { + private static final int INITIAL_BUFFER_SIZE = 9000; // Size of a Jumbo frame. + private final int defaultPort; private Socket socket; @@ -115,7 +118,7 @@ protected OutputStream getOutputStream() { protected void onConnect() throws IOException { socket.setSoTimeout(soTimeout); input = socket.getInputStream(); - output = socket.getOutputStream(); + output = new BufferedOutputStream(socket.getOutputStream(), INITIAL_BUFFER_SIZE); } public int getRemotePort() { diff --git a/src/main/java/com/hierynomus/smbj/Config.java b/src/main/java/com/hierynomus/smbj/Config.java index 9f7f67d4..d83c7afa 100644 --- a/src/main/java/com/hierynomus/smbj/Config.java +++ b/src/main/java/com/hierynomus/smbj/Config.java @@ -15,11 +15,13 @@ */ package com.hierynomus.smbj; -import com.hierynomus.mssmb2.SMB2Dialect; - import java.util.EnumSet; +import java.util.List; import java.util.Random; import java.util.UUID; +import com.hierynomus.mssmb2.SMB2Dialect; +import com.hierynomus.protocol.commons.Factory; +import com.hierynomus.smbj.auth.Authenticator; public interface Config { @@ -27,5 +29,13 @@ public interface Config { EnumSet getSupportedDialects(); + List> getSupportedAuthenticators(); + UUID getClientGuid(); + + /** + * enforces message signing. When message signing is enforced a received message that is not signed properly + * will be dropped. + */ + boolean isStrictSigning(); } diff --git a/src/main/java/com/hierynomus/smbj/ConfigImpl.java b/src/main/java/com/hierynomus/smbj/ConfigImpl.java index 7166d935..4176879e 100644 --- a/src/main/java/com/hierynomus/smbj/ConfigImpl.java +++ b/src/main/java/com/hierynomus/smbj/ConfigImpl.java @@ -15,17 +15,21 @@ */ package com.hierynomus.smbj; -import com.hierynomus.mssmb2.SMB2Dialect; - import java.util.EnumSet; +import java.util.List; import java.util.Random; import java.util.UUID; +import com.hierynomus.mssmb2.SMB2Dialect; +import com.hierynomus.protocol.commons.Factory; +import com.hierynomus.smbj.auth.Authenticator; public class ConfigImpl implements Config { protected EnumSet dialects; + protected List> authenticators; protected Random random; protected UUID clientGuid; + protected boolean isStrictSigning; @Override public Random getRandomProvider() { @@ -41,4 +45,14 @@ public EnumSet getSupportedDialects() { public UUID getClientGuid() { return clientGuid; } + + @Override + public boolean isStrictSigning() { + return isStrictSigning; + } + + @Override + public List> getSupportedAuthenticators() { + return authenticators; + } } diff --git a/src/main/java/com/hierynomus/smbj/DefaultConfig.java b/src/main/java/com/hierynomus/smbj/DefaultConfig.java index 24cb7179..35fbcabd 100644 --- a/src/main/java/com/hierynomus/smbj/DefaultConfig.java +++ b/src/main/java/com/hierynomus/smbj/DefaultConfig.java @@ -15,17 +15,31 @@ */ package com.hierynomus.smbj; -import com.hierynomus.mssmb2.SMB2Dialect; - import java.security.SecureRandom; +import java.util.ArrayList; import java.util.EnumSet; import java.util.UUID; +import com.hierynomus.mssmb2.SMB2Dialect; +import com.hierynomus.protocol.commons.Factory; +import com.hierynomus.smbj.auth.Authenticator; +import com.hierynomus.smbj.auth.NtlmAuthenticator; +import com.hierynomus.smbj.auth.SpnegoAuthenticator; + public class DefaultConfig extends ConfigImpl { public DefaultConfig() { random = new SecureRandom(); dialects = EnumSet.of(SMB2Dialect.SMB_2_1, SMB2Dialect.SMB_2_0_2); clientGuid = UUID.randomUUID(); + isStrictSigning = false; //TODO change to true when we are more confident + registerDefaultAuthenticators(); + } + + private void registerDefaultAuthenticators() { + authenticators = new ArrayList>(); + // order is important. The authenticators listed first will be selected + authenticators.add(new SpnegoAuthenticator.Factory()); + authenticators.add(new NtlmAuthenticator.Factory()); } } diff --git a/src/main/java/com/hierynomus/smbj/ProgressListener.java b/src/main/java/com/hierynomus/smbj/ProgressListener.java index 76f5e5f4..a16a5a80 100644 --- a/src/main/java/com/hierynomus/smbj/ProgressListener.java +++ b/src/main/java/com/hierynomus/smbj/ProgressListener.java @@ -19,6 +19,7 @@ public interface ProgressListener { /** * Invoked when the progress of the API call changes. + * * @param numBytes the number of bytes completed. * @param totalBytes the total number of bytes. */ diff --git a/src/main/java/com/hierynomus/smbj/SMBClient.java b/src/main/java/com/hierynomus/smbj/SMBClient.java index 3dfd2dca..a6567562 100644 --- a/src/main/java/com/hierynomus/smbj/SMBClient.java +++ b/src/main/java/com/hierynomus/smbj/SMBClient.java @@ -15,13 +15,12 @@ */ package com.hierynomus.smbj; -import com.hierynomus.smbj.connection.Connection; -import com.hierynomus.smbj.event.SMBEventBus; -import com.hierynomus.smbj.transport.tcp.DirectTcpTransport; - import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import com.hierynomus.smbj.connection.Connection; +import com.hierynomus.smbj.event.SMBEventBus; +import com.hierynomus.smbj.transport.tcp.DirectTcpTransport; /** * Server Message Block Client API. @@ -49,6 +48,7 @@ public SMBClient(Config config) { /** * Connect to the host at

hostname
on the default port (445) + * * @param hostname The hostname to connect to. * @return An established connection. * @throws IOException If the connection could not be established. @@ -57,6 +57,17 @@ public Connection connect(String hostname) throws IOException { return getEstablishedOrConnect(hostname, DEFAULT_PORT); } + /** + * Connect to the host at
hostname
on the given port + * + * @param hostname The hostname to connect to. + * @param port The port to connect to + * @return An established connection. + * @throws IOException If the connection could not be established. + */ + public Connection connect(String hostname, int port) throws IOException { + return getEstablishedOrConnect(hostname, port); + } private Connection getEstablishedOrConnect(String hostname, int port) throws IOException { synchronized (this) { diff --git a/src/main/java/com/hierynomus/smbj/auth/Authenticator.java b/src/main/java/com/hierynomus/smbj/auth/Authenticator.java index a0ea9bc9..22c79f72 100644 --- a/src/main/java/com/hierynomus/smbj/auth/Authenticator.java +++ b/src/main/java/com/hierynomus/smbj/auth/Authenticator.java @@ -15,6 +15,13 @@ */ package com.hierynomus.smbj.auth; -interface Authenticator { +import java.io.IOException; -} \ No newline at end of file +import com.hierynomus.smbj.session.Session; + +public interface Authenticator { + + boolean supports(AuthenticationContext context); + + byte[] authenticate(AuthenticationContext context, byte[] gssToken, Session session) throws IOException; +} diff --git a/src/main/java/com/hierynomus/smbj/auth/GSSAuthenticationContext.java b/src/main/java/com/hierynomus/smbj/auth/GSSAuthenticationContext.java new file mode 100755 index 00000000..4eadc8e5 --- /dev/null +++ b/src/main/java/com/hierynomus/smbj/auth/GSSAuthenticationContext.java @@ -0,0 +1,36 @@ +/* + * Copyright (C)2016 - SMBJ 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.smbj.auth; + +import javax.security.auth.Subject; + +import org.ietf.jgss.GSSCredential; + +public class GSSAuthenticationContext extends AuthenticationContext { + Subject subject; + GSSCredential creds; + public GSSAuthenticationContext(String username, String domain, Subject subject, GSSCredential creds) { + super(username, "".toCharArray(), domain); + this.subject = subject; + this.creds = creds; + } + public Subject getSubject() { + return subject; + } + public GSSCredential getCreds() { + return creds; + } +} diff --git a/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java b/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java index 7be64bd5..1c66f1c5 100644 --- a/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java +++ b/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java @@ -15,9 +15,15 @@ */ package com.hierynomus.smbj.auth; -import com.hierynomus.mserref.NtStatus; -import com.hierynomus.mssmb2.messages.SMB2SessionSetup; -import com.hierynomus.ntlm.NtlmException; +import java.io.IOException; +import java.math.BigInteger; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.microsoft.MicrosoftObjectIdentifiers; +import org.bouncycastle.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.hierynomus.ntlm.functions.NtlmFunctions; import com.hierynomus.ntlm.messages.NtlmAuthenticate; import com.hierynomus.ntlm.messages.NtlmChallenge; @@ -26,28 +32,16 @@ import com.hierynomus.protocol.commons.ByteArrayUtils; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.protocol.commons.buffer.Endian; -import com.hierynomus.protocol.commons.concurrent.Futures; -import com.hierynomus.smbj.connection.Connection; -import com.hierynomus.smbj.transport.TransportException; +import com.hierynomus.smbj.session.Session; import com.hierynomus.spnego.NegTokenInit; import com.hierynomus.spnego.NegTokenTarg; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.microsoft.MicrosoftObjectIdentifiers; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.EnumSet; -import java.util.concurrent.Future; public class NtlmAuthenticator implements Authenticator { private static final Logger logger = LoggerFactory.getLogger(NtlmAuthenticator.class); private static final ASN1ObjectIdentifier NTLMSSP = MicrosoftObjectIdentifiers.microsoft.branch("2.2.10"); - public static class Factory implements com.hierynomus.protocol.commons.Factory.Named { + public static class Factory implements com.hierynomus.protocol.commons.Factory.Named { @Override public String getName() { // The OID for NTLMSSP @@ -60,65 +54,56 @@ public NtlmAuthenticator create() { } } - public long authenticate(Connection connection, AuthenticationContext context) throws TransportException { - try { - logger.info("Authenticating {} on {} using NTLM", context.getUsername(), connection.getRemoteHostname()); - EnumSet signingEnabled = EnumSet.of - (SMB2SessionSetup.SMB2SecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED); + private boolean initialized = false; + private boolean completed = false; - SMB2SessionSetup smb2SessionSetup = new SMB2SessionSetup(connection.getNegotiatedProtocol().getDialect(), signingEnabled); + @Override + public byte[] authenticate(final AuthenticationContext context, final byte[] gssToken, Session session) throws IOException { + if (completed) { + return null; + } else if (!initialized) { + logger.info("Initialized Authentication of {} using NTLM", context.getUsername()); NtlmNegotiate ntlmNegotiate = new NtlmNegotiate(); - byte[] asn1 = negTokenInit(ntlmNegotiate); - smb2SessionSetup.setSecurityBuffer(asn1); - Future future = connection.send(smb2SessionSetup); - SMB2SessionSetup receive = Futures.get(future, TransportException.Wrapper); - long sessionId = receive.getHeader().getSessionId(); - if (receive.getHeader().getStatus() == NtStatus.STATUS_MORE_PROCESSING_REQUIRED) { - logger.debug("More processing required for authentication of {}", context.getUsername()); - byte[] securityBuffer = receive.getSecurityBuffer(); - logger.debug("Received token: {}", ByteArrayUtils.printHex(securityBuffer)); - - NegTokenTarg negTokenTarg = new NegTokenTarg().read(securityBuffer); - BigInteger negotiationResult = negTokenTarg.getNegotiationResult(); - NtlmChallenge challenge = (NtlmChallenge) new NtlmChallenge().read(new Buffer.PlainBuffer(negTokenTarg.getResponseToken(), Endian.LE)); - logger.debug("Received NTLM challenge from: {}", challenge.getTargetName()); - - byte[] serverChallenge = challenge.getServerChallenge(); - byte[] responseKeyNT = NtlmFunctions.NTOWFv2(String.valueOf(context.getPassword()), context.getUsername(), context.getDomain()); - byte[] ntlmv2ClientChallenge = NtlmFunctions.getNTLMv2ClientChallenge(challenge.getTargetInfo()); - byte[] ntlmv2Response = NtlmFunctions.getNTLMv2Response(responseKeyNT, serverChallenge, ntlmv2ClientChallenge); - byte[] sessionkey = null; - - if (challenge.getNegotiateFlags().contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_SIGN)) { - byte[] userSessionKey = NtlmFunctions.hmac_md5( - responseKeyNT, ByteBuffer.wrap(ntlmv2Response, 0, 16).array()); - - if ((challenge.getNegotiateFlags().contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_KEY_EXCH))) { - byte[] masterKey = new byte[16]; - NtlmFunctions.getRandom().nextBytes(masterKey); - sessionkey = NtlmFunctions.encryptRc4(userSessionKey, masterKey); - } else { - sessionkey = userSessionKey; - } - } - - SMB2SessionSetup smb2SessionSetup2 = new SMB2SessionSetup(connection.getNegotiatedProtocol().getDialect(), signingEnabled); - smb2SessionSetup2.getHeader().setSessionId(sessionId); - //smb2SessionSetup2.getHeader().setCreditRequest(256); - - NtlmAuthenticate resp = new NtlmAuthenticate(new byte[0], ntlmv2Response, - context.getUsername(), context.getDomain(), null, sessionkey, NtlmNegotiate.DEFAULT_FLAGS); - asn1 = negTokenTarg(resp, negTokenTarg.getResponseToken()); - smb2SessionSetup2.setSecurityBuffer(asn1); - Future send = connection.send(smb2SessionSetup2); - SMB2SessionSetup setupResponse = Futures.get(send, TransportException.Wrapper); - if (setupResponse.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { - throw new NtlmException("Setup failed with " + setupResponse.getHeader().getStatus()); + initialized = true; + return negTokenInit(ntlmNegotiate); + } else { + logger.debug("Received token: {}", ByteArrayUtils.printHex(gssToken)); + + NegTokenTarg negTokenTarg = new NegTokenTarg().read(gssToken); + BigInteger negotiationResult = negTokenTarg.getNegotiationResult(); + NtlmChallenge challenge = null; + try { + challenge = (NtlmChallenge) new NtlmChallenge().read(new Buffer.PlainBuffer(negTokenTarg.getResponseToken(), Endian.LE)); + } catch (Buffer.BufferException e) { + throw new IOException(e); + } + logger.debug("Received NTLM challenge from: {}", challenge.getTargetName()); + + byte[] serverChallenge = challenge.getServerChallenge(); + byte[] responseKeyNT = NtlmFunctions.NTOWFv2(String.valueOf(context.getPassword()), context.getUsername(), context.getDomain()); + byte[] ntlmv2ClientChallenge = NtlmFunctions.getNTLMv2ClientChallenge(challenge.getTargetInfo()); + byte[] ntlmv2Response = NtlmFunctions.getNTLMv2Response(responseKeyNT, serverChallenge, ntlmv2ClientChallenge); + byte[] sessionkey = null; + + if (challenge.getNegotiateFlags().contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_SIGN)) { + byte[] userSessionKey = NtlmFunctions.hmac_md5(responseKeyNT, Arrays.copyOfRange(ntlmv2Response, 0, 16)); // first 16 bytes of ntlmv2Response is ntProofStr + if ((challenge.getNegotiateFlags().contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_KEY_EXCH))) { + byte[] masterKey = new byte[16]; + NtlmFunctions.getRandom().nextBytes(masterKey); + sessionkey = NtlmFunctions.encryptRc4(userSessionKey, masterKey); + session.setSigningKey(masterKey); + } else { + sessionkey = userSessionKey; + session.setSigningKey(sessionkey); } } - return sessionId; - } catch (IOException | Buffer.BufferException e) { - throw new TransportException(e); + + completed = true; + + // If NTLM v2 is used, KeyExchangeKey MUST be set to the given 128-bit SessionBaseKey value. + NtlmAuthenticate resp = new NtlmAuthenticate(new byte[0], ntlmv2Response, + context.getUsername(), context.getDomain(), null, sessionkey, NtlmNegotiate.DEFAULT_FLAGS); + return negTokenTarg(resp, negTokenTarg.getResponseToken()); } } @@ -144,4 +129,9 @@ private byte[] negTokenTarg(NtlmAuthenticate resp, byte[] responseToken) { return negTokenBuffer.getCompactData(); } + @Override + public boolean supports(AuthenticationContext context) { + return context.getClass().equals(AuthenticationContext.class); + } + } diff --git a/src/main/java/com/hierynomus/smbj/auth/SpnegoAuthenticator.java b/src/main/java/com/hierynomus/smbj/auth/SpnegoAuthenticator.java index 68f0363c..1cd2e205 100644 --- a/src/main/java/com/hierynomus/smbj/auth/SpnegoAuthenticator.java +++ b/src/main/java/com/hierynomus/smbj/auth/SpnegoAuthenticator.java @@ -15,8 +15,27 @@ */ package com.hierynomus.smbj.auth; +import java.io.IOException; +import java.security.Key; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Arrays; +import javax.security.auth.Subject; +import org.ietf.jgss.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.hierynomus.mssmb2.SMB2Header; +import com.hierynomus.protocol.commons.ByteArrayUtils; +import com.hierynomus.smbj.session.Session; +import com.hierynomus.smbj.transport.TransportException; +import com.sun.security.jgss.ExtendedGSSContext; +import com.sun.security.jgss.InquireType; + + public class SpnegoAuthenticator implements Authenticator { - public static class Factory implements com.hierynomus.protocol.commons.Factory.Named { + private static final Logger logger = LoggerFactory.getLogger(SpnegoAuthenticator.class); + + public static class Factory implements com.hierynomus.protocol.commons.Factory.Named { @Override public String getName() { @@ -26,30 +45,91 @@ public String getName() { @Override public SpnegoAuthenticator create() { - return null; + return new SpnegoAuthenticator(); + } + } + + private GSSContext gssContext; + + @Override + public byte[] authenticate(final AuthenticationContext context, final byte[] gssToken, final Session session) throws IOException { + final GSSAuthenticationContext gssAuthenticationContext = (GSSAuthenticationContext) context; + try { + return Subject.doAs(gssAuthenticationContext.getSubject(), new PrivilegedExceptionAction() { + public byte[] run() throws Exception { + return authenticateSession(gssAuthenticationContext, gssToken, session); + } + }); + } catch (PrivilegedActionException e) { + throw new TransportException(e); + } + } + + private byte[] authenticateSession(GSSAuthenticationContext context, byte[] gssToken, Session session) throws TransportException { + try { + logger.info("Authenticating {} on {} using SPNEGO", context.getUsername(), session.getConnection().getRemoteHostname()); + if (gssContext == null) { + // GSS context is not established yet because this is the first pass at authentication, + // so create the GSS context and attach it to our AuthenticationContext + if (gssToken == null) { + logger.error("GSS token is null, but should have been provided by the SMB negotiation response"); + } + + GSSManager gssManager = GSSManager.getInstance(); + Oid spnegoOid = new Oid("1.3.6.1.5.5.2"); //SPNEGO + + String service = "cifs"; + String hostName = session.getConnection().getRemoteHostname(); + GSSName serverName = gssManager.createName(service + "@" + hostName, GSSName.NT_HOSTBASED_SERVICE); + gssContext = gssManager.createContext(serverName, spnegoOid, context.getCreds(), GSSContext.DEFAULT_LIFETIME); + gssContext.requestMutualAuth(false); + // TODO fill in all the other options too + + } + + byte[] newToken = gssContext.initSecContext(gssToken, 0, gssToken.length); + if (newToken != null) { + logger.debug("Received token: {}", ByteArrayUtils.printHex(newToken)); + } + if (gssContext.isEstablished()) { + ExtendedGSSContext e = (ExtendedGSSContext) gssContext; + Key key = (Key) e.inquireSecContext(InquireType.KRB5_GET_SESSION_KEY); + if (key != null) { + // if a session key was negotiated, save it. + session.setSigningKey(adjustSessionKeyLength(key.getEncoded())); + } + } + return newToken; + } catch (GSSException e) { + throw new TransportException(e); } } /** - * Authenticate the user against - * @param gssToken - * @param context - * @return + * [MS-SMB2] 3.2.5.3.1 Handling a New Authentication + * Session.SessionKey MUST be set to the first 16 bytes of the cryptographic key queried from the + * GSS protocol for this authenticated context. If the cryptographic key is less than 16 bytes, + * it is right-padded with zero bytes. + * + * @param key session key from the GSS API + * @return key, truncated or padded to 16 bytes */ - public byte[] authenticate(byte[] gssToken, AuthenticationContext context) { - return null; + private byte[] adjustSessionKeyLength(byte[] key) { + byte[] newKey; + if (key.length > SMB2Header.SIGNATURE_SIZE) { + newKey = Arrays.copyOfRange(key, 0, SMB2Header.SIGNATURE_SIZE); + } else if (key.length < SMB2Header.SIGNATURE_SIZE) { + newKey = new byte[16]; + System.arraycopy(key, 0, newKey, 0, key.length); + Arrays.fill(newKey, key.length, SMB2Header.SIGNATURE_SIZE - 1, (byte) 0); + } else { + newKey = key; + } + return newKey; } - - public void authenticate(String username, String password, String domain) { -// try { -// GSSManager gssManager = GSSManager.getInstance(); -// Oid spnegoOid = new Oid("1.3.6.1.5.5.2"); -// GSSName serverName = gssManager.createName(, GSSName.NT_HOSTBASED_SERVICE, spnegoOid); -// GSSContext context = gssManager.createContext(serverName, spnegoOid, null, GSSContext.DEFAULT_LIFETIME); -// byte[] bytes = context.acceptSecContext(gssToken, 0, gssToken.length); -// } catch (GSSException e) { -// e.printStackTrace(); -// } + @Override + public boolean supports(AuthenticationContext context) { + return context.getClass().equals(GSSAuthenticationContext.class); } } diff --git a/src/main/java/com/hierynomus/smbj/common/MessageSigning.java b/src/main/java/com/hierynomus/smbj/common/MessageSigning.java new file mode 100755 index 00000000..22be4224 --- /dev/null +++ b/src/main/java/com/hierynomus/smbj/common/MessageSigning.java @@ -0,0 +1,113 @@ +/* + * Copyright (C)2016 - SMBJ 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.smbj.common; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import com.hierynomus.mssmb2.SMB2Header; + +public class MessageSigning { + + public static final String HMAC_SHA256_ALGORITHM = "HmacSHA256"; + + /** + * check that the signature field of the SMB message buffer is correct. + * + * @param buffer byte array containing the SMB message. + * @param len message length. Note that the buffer array might contain more + * bytes than the message length. The len parameter indicates how + * many bytes of the buffer array are in the message. + * @param signingKeySpec the session's signing key, a SecretKeySpec + * @return + */ + public static boolean validateSignature(byte[] buffer, int len, SecretKeySpec signingKeySpec) { + try { + byte[] signature = computeSignature(buffer, len, signingKeySpec); + + // are signatures identical? + for (int i = 0; i < SMB2Header.SIGNATURE_SIZE; i++) { + if (signature[i] != buffer[SMB2Header.SIGNATURE_OFFSET + i]) { + return false; + } + } + return true; + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + return false; // cannot check signature? + } + } + + /** + * sign a SMB message buffer + * + * @param buffer byte array containing the SMB message. + * @param len message length. Note that the buffer array might contain more + * bytes than the message length. The len parameter indicates how + * many bytes of the buffer array are in the message. + * @param signingKey the session's signing key. + * @throws InvalidKeyException + * @throws NoSuchAlgorithmException + */ + // [MS-SMB2] 3.1.4.1 Signing An Outgoing Message + // 1. The sender MUST zero out the 16-byte signature field in the SMB2 Header of the message to be sent + // prior to generating the signature. + // 2. If Connection.Dialect belongs to the SMB 3.x dialect family, the sender MUST compute a 16-byte hash + // using AES-128-CMAC over the entire message, beginning with the SMB2 Header from step 1, and using the + // key provided. The AES-128-CMAC is specified in [RFC4493]. If the message is part of a compounded chain, + // any padding at the end of the message MUST be used in the hash computation. The sender MUST copy the + // 16-byte hash into the signature field of the SMB2 header. + // 3. If Connection.Dialect is "2.0.2" or "2.1", the sender MUST compute a 32-byte hash using HMAC-SHA256 + // over the entire message, beginning with the SMB2 Header from step 1, and using the key provided. + // The HMAC-SHA256 is specified in [FIPS180-4] and [RFC2104]. If the message is part of a compounded + // chain, any padding at the end of the message MUST be used in the hash computation. The first + // 16 bytes (the high-order portion) of the hash MUST be copied (beginning with the first, most + // significant, byte) into the 16-byte signature field of the SMB2 Header. + public static void signBuffer(byte[] buffer, int len, SecretKeySpec signingKeySpec) throws InvalidKeyException, + NoSuchAlgorithmException { + byte[] signature = computeSignature(buffer, len, signingKeySpec); + System.arraycopy(signature, 0, buffer, SMB2Header.SIGNATURE_OFFSET, SMB2Header.SIGNATURE_SIZE); + } + + /** + * compute the HMAC signature on a SMB buffer. The calculation is performed + * as if the signature field is filled with zeros. + * + * @param buffer byte array containing the SMB message. + * @param len message length. Note that the buffer array might contain more + * bytes than the message length. The len parameter indicates how + * many bytes of the buffer array are in the message. + * @param signingKeySpec the session's signing key, a SecretKeySpec + * @throws InvalidKeyException + * @throws NoSuchAlgorithmException + */ + private static byte[] computeSignature(byte[] buffer, int len, SecretKeySpec signingKeySpec) throws NoSuchAlgorithmException, + InvalidKeyException { + if (len < SMB2Header.STRUCTURE_SIZE) { + throw new IllegalArgumentException("Buffer must be longer than 64 bytes"); + } + + Mac mac = Mac.getInstance(signingKeySpec.getAlgorithm()); + mac.init(signingKeySpec); + mac.update(buffer, 0, SMB2Header.SIGNATURE_OFFSET); + for (int i = 0; i < SMB2Header.SIGNATURE_SIZE; i++) { // pretend the signature bytes are zero + mac.update((byte) 0); + } + mac.update(buffer, SMB2Header.STRUCTURE_SIZE, len - SMB2Header.STRUCTURE_SIZE); + byte[] signature = mac.doFinal(); + return signature; + } +} diff --git a/src/main/java/com/hierynomus/smbj/common/SMBApiException.java b/src/main/java/com/hierynomus/smbj/common/SMBApiException.java index b94da0ae..435116b0 100644 --- a/src/main/java/com/hierynomus/smbj/common/SMBApiException.java +++ b/src/main/java/com/hierynomus/smbj/common/SMBApiException.java @@ -22,22 +22,26 @@ public class SMBApiException extends SMBRuntimeException { private final NtStatus status; private final SMB2MessageCommandCode failedCommand; + private long statusCode; - public SMBApiException(NtStatus status, SMB2MessageCommandCode failedCommand, String message) { + public SMBApiException(NtStatus status, long statusCode, SMB2MessageCommandCode failedCommand, String message) { super(message); this.status = status; + this.statusCode = statusCode; this.failedCommand = failedCommand; } - public SMBApiException(NtStatus status, SMB2MessageCommandCode failedCommand, Throwable t) { + public SMBApiException(NtStatus status, long statusCode, SMB2MessageCommandCode failedCommand, Throwable t) { super(t); this.status = status; + this.statusCode = statusCode; this.failedCommand = failedCommand; } public SMBApiException(SMB2Header header, String message) { super(message); this.status = header.getStatus(); + this.statusCode = header.getStatusCode(); this.failedCommand = header.getMessage(); } @@ -45,12 +49,16 @@ public NtStatus getStatus() { return status; } + public long getStatusCode() { + return statusCode; + } + public SMB2MessageCommandCode getFailedCommand() { return failedCommand; } @Override public String getMessage() { - return status + "(" + status.getValue() + "): " + super.getMessage(); + return status + "(" + status.getValue() + "/" + statusCode + "): " + super.getMessage(); } } diff --git a/src/main/java/com/hierynomus/smbj/common/SMBBuffer.java b/src/main/java/com/hierynomus/smbj/common/SMBBuffer.java index 1eabf00f..53f5b0c7 100644 --- a/src/main/java/com/hierynomus/smbj/common/SMBBuffer.java +++ b/src/main/java/com/hierynomus/smbj/common/SMBBuffer.java @@ -15,11 +15,10 @@ */ package com.hierynomus.smbj.common; -import com.hierynomus.protocol.commons.buffer.Buffer; -import com.hierynomus.protocol.commons.buffer.Endian; - import java.nio.charset.StandardCharsets; import java.util.Arrays; +import com.hierynomus.protocol.commons.buffer.Buffer; +import com.hierynomus.protocol.commons.buffer.Endian; public class SMBBuffer extends Buffer { private static final byte[] RESERVED_2 = new byte[]{0x0, 0x0}; diff --git a/src/main/java/com/hierynomus/smbj/common/SMBException.java b/src/main/java/com/hierynomus/smbj/common/SMBException.java index 09a83633..732629f8 100644 --- a/src/main/java/com/hierynomus/smbj/common/SMBException.java +++ b/src/main/java/com/hierynomus/smbj/common/SMBException.java @@ -15,9 +15,8 @@ */ package com.hierynomus.smbj.common; -import com.hierynomus.protocol.commons.concurrent.ExceptionWrapper; - import java.io.IOException; +import com.hierynomus.protocol.commons.concurrent.ExceptionWrapper; public class SMBException extends IOException { public static ExceptionWrapper Wrapper = new ExceptionWrapper() { diff --git a/src/main/java/com/hierynomus/smbj/connection/Connection.java b/src/main/java/com/hierynomus/smbj/connection/Connection.java index c5550457..43e02e0d 100644 --- a/src/main/java/com/hierynomus/smbj/connection/Connection.java +++ b/src/main/java/com/hierynomus/smbj/connection/Connection.java @@ -15,17 +15,36 @@ */ package com.hierynomus.smbj.connection; +import java.io.IOException; +import java.util.EnumSet; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; +import java.util.concurrent.Future; +import java.util.concurrent.locks.ReentrantLock; + +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.hierynomus.mserref.NtStatus; +import com.hierynomus.mssmb2.SMB2MessageCommandCode; import com.hierynomus.mssmb2.SMB2MessageFlag; import com.hierynomus.mssmb2.SMB2MultiCreditPacket; import com.hierynomus.mssmb2.SMB2Packet; import com.hierynomus.mssmb2.messages.SMB2NegotiateRequest; import com.hierynomus.mssmb2.messages.SMB2NegotiateResponse; +import com.hierynomus.mssmb2.messages.SMB2SessionSetup; +import com.hierynomus.protocol.commons.Factory; import com.hierynomus.protocol.commons.concurrent.Futures; import com.hierynomus.protocol.commons.socket.SocketClient; import com.hierynomus.smbj.Config; import com.hierynomus.smbj.auth.AuthenticationContext; -import com.hierynomus.smbj.auth.NtlmAuthenticator; +import com.hierynomus.smbj.auth.Authenticator; +import com.hierynomus.smbj.common.MessageSigning; +import com.hierynomus.smbj.common.SMBApiException; import com.hierynomus.smbj.common.SMBRuntimeException; import com.hierynomus.smbj.event.SMBEventBus; import com.hierynomus.smbj.event.SessionLoggedOff; @@ -37,19 +56,13 @@ import com.hierynomus.smbj.transport.tcp.DirectTcpPacketReader; import com.hierynomus.smbj.transport.tcp.DirectTcpTransport; import com.hierynomus.spnego.NegTokenInit; -import net.engio.mbassy.listener.Handler; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.util.NoSuchElementException; -import java.util.UUID; -import java.util.concurrent.Future; -import java.util.concurrent.locks.ReentrantLock; +import net.engio.mbassy.listener.Handler; +import static com.hierynomus.mssmb2.messages.SMB2SessionSetup.SMB2SecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.isSet; import static com.hierynomus.smbj.connection.NegotiatedProtocol.SINGLE_CREDIT_PAYLOAD_SIZE; +import static java.lang.String.format; /** * A connection to a server. @@ -91,7 +104,13 @@ protected void onConnect() throws IOException { @Override public void close() throws Exception { - connectionInfo.getSessionTable().closeRemainingSessions(); + for (Session session : connectionInfo.getSessionTable().activeSessions()) { + try { + session.close(); + } catch (IOException e) { + logger.warn("Exception while closing session {}", session.getSessionId(), e); + } + } packetReader.stop(); logger.info("Closed connection to {}", getRemoteHostname()); super.disconnect(); @@ -103,25 +122,83 @@ public void close() throws Exception { * @return a (new) Session that is authenticated for the user. */ public Session authenticate(AuthenticationContext authContext) { - // TODO hardcoded for now - NtlmAuthenticator.Factory factory = new NtlmAuthenticator.Factory(); try { NegTokenInit negTokenInit = new NegTokenInit().read(connectionInfo.getGssNegotiateToken()); - if (negTokenInit.getSupportedMechTypes().contains(new ASN1ObjectIdentifier(factory.getName()))) { - NtlmAuthenticator ntlmAuthenticator = factory.create(); - long sessionId = ntlmAuthenticator.authenticate(this, authContext); - logger.info("Successfully authenticated {} on {}, session is {}", authContext.getUsername(), getRemoteHostname(), sessionId); - Session session = new Session(sessionId, this, bus); - connectionInfo.getSessionTable().registerSession(sessionId, session); + Authenticator authenticator = getAuthenticator(negTokenInit.getSupportedMechTypes(), authContext); + Session session = new Session(0, this, bus, connectionInfo.isRequireSigning()); + SMB2SessionSetup receive = authenticationRound(authenticator, authContext, connectionInfo.getGssNegotiateToken(), session); + long sessionId = receive.getHeader().getSessionId(); + session.setSessionId(sessionId); + connectionInfo.getPreauthSessionTable().registerSession(sessionId, session); + try { + while (receive.getHeader().getStatus() == NtStatus.STATUS_MORE_PROCESSING_REQUIRED) { + logger.debug("More processing required for authentication of {} using {}", authContext.getUsername(), authenticator); + receive = authenticationRound(authenticator, authContext, receive.getSecurityBuffer(), session); + } + + if (receive.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { + throw new SMBApiException(receive.getHeader(), format("Authentication failed for '%s' using %s", authContext.getUsername(), authenticator)); + } + + if (receive.getSecurityBuffer() != null) { + // process the last received buffer + authenticator.authenticate(authContext, receive.getSecurityBuffer(), session); + } + logger.info("Successfully authenticated {} on {}, session is {}", authContext.getUsername(), getRemoteHostname(), session.getSessionId()); + connectionInfo.getSessionTable().registerSession(session.getSessionId(), session); return session; + } finally { + connectionInfo.getPreauthSessionTable().sessionClosed(sessionId); } } catch (IOException e) { throw new SMBRuntimeException(e); } - return null; } + private SMB2SessionSetup authenticationRound(Authenticator authenticator, AuthenticationContext authContext, byte[] inputToken, Session session) throws IOException { + byte[] securityContext = authenticator.authenticate(authContext, inputToken, session); + SMB2SessionSetup req = new SMB2SessionSetup(connectionInfo.getNegotiatedProtocol().getDialect(), EnumSet.of(SMB2_NEGOTIATE_SIGNING_ENABLED)); + req.setSecurityBuffer(securityContext); + req.getHeader().setSessionId(session.getSessionId()); + return sendAndReceive(req); + } + + private Authenticator getAuthenticator(List mechTypes, AuthenticationContext context) { + for (Factory.Named factory : config.getSupportedAuthenticators()) { + if (mechTypes.contains(new ASN1ObjectIdentifier(factory.getName()))) { + Authenticator authenticator = factory.create(); + if (authenticator.supports(context)) { + return authenticator; + } + } + } + throw new SMBRuntimeException("No authenticator is configured for the supported mechtypes: " + mechTypes); + } + + /** + * send a packet, unsigned. + * + * @param packet SMBPacket to send + * @return a Future to be used to retrieve the response packet + * @throws TransportException + */ public Future send(SMB2Packet packet) throws TransportException { + return send(packet, null); + } + + private T sendAndReceive(SMB2Packet packet) throws TransportException { + return Futures.get(this.send(packet), TransportException.Wrapper); + } + + /** + * send a packet, potentially signed + * + * @param packet SMBPacket to send + * @param signingKeySpec if null, do not sign the packet. Otherwise, the signingKey will be used to sign the packet. + * @return a Future to be used to retrieve the response packet + * @throws TransportException + */ + public Future send(SMB2Packet packet, SecretKeySpec signingKeySpec) throws TransportException { lock.lock(); try { int availableCredits = connectionInfo.getSequenceWindow().available(); @@ -152,7 +229,11 @@ public Future send(SMB2Packet packet) throws Transport Request request = new Request(packet.getHeader().getMessageId(), UUID.randomUUID(), packet); connectionInfo.getOutstandingRequests().registerOutstanding(request); - transport.write(packet); + if (signingKeySpec != null) { + transport.writeSigned(packet, signingKeySpec); + } else { + transport.write(packet); + } return request.getFuture(null); // TODO cancel callback } finally { lock.unlock(); @@ -218,6 +299,40 @@ public void handle(SMB2Packet packet) throws TransportException { return; } + if (packet.getHeader().getSessionId() != 0 && (packet.getHeader().getMessage() != SMB2MessageCommandCode.SMB2_SESSION_SETUP)) { + Session session = connectionInfo.getSessionTable().find(packet.getHeader().getSessionId()); + if (session == null) { + // check for a not-yet-authenticated session + session = connectionInfo.getPreauthSessionTable().find(packet.getHeader().getSessionId()); + if (session == null) { + logger.warn("Illegal request, no session matching the sessionId: {}", packet.getHeader().getSessionId()); + //TODO maybe tear down the connection? + return; + } + } + + // check packet signature. Drop the packet if it is not correct. + if (session.isSigningRequired()) { + if (packet.getHeader().isFlagSet(SMB2MessageFlag.SMB2_FLAGS_SIGNED)) { + packet.getBuffer().rpos(0); + if (!MessageSigning.validateSignature(packet.getBuffer().array(), packet.getBuffer().available(), session.getSigningKeySpec())) { + logger.warn("Invalid packet signature"); + if (config.isStrictSigning()) { + return; // drop the packet + } + } + } else { + logger.warn("Illegal request, session requires message signing, but the message is not signed."); + return; + } + } else { + if (packet.getHeader().isFlagSet(SMB2MessageFlag.SMB2_FLAGS_SIGNED)) { + logger.trace("Received a signed packet, but signing is not required on this session."); + // but this is OK, so we fall through. + } + } + } + // [MS-SMB2].pdf 3.2.5.1.8 Processing the Response connectionInfo.getOutstandingRequests().receivedResponseFor(messageId).getPromise().deliver(packet); } diff --git a/src/main/java/com/hierynomus/smbj/connection/ConnectionInfo.java b/src/main/java/com/hierynomus/smbj/connection/ConnectionInfo.java index 3af4e581..917e4685 100644 --- a/src/main/java/com/hierynomus/smbj/connection/ConnectionInfo.java +++ b/src/main/java/com/hierynomus/smbj/connection/ConnectionInfo.java @@ -15,12 +15,10 @@ */ package com.hierynomus.smbj.connection; -import com.hierynomus.mssmb2.messages.SMB2NegotiateResponse; -import com.hierynomus.protocol.commons.EnumWithValue; - import java.util.EnumSet; -import java.util.List; import java.util.UUID; +import com.hierynomus.mssmb2.messages.SMB2NegotiateResponse; +import com.hierynomus.protocol.commons.EnumWithValue; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toEnumSet; @@ -51,7 +49,7 @@ public long getValue() { // All SMB2 Dialect private SessionTable sessionTable = new SessionTable(); - private List preauthSessionTable; + private SessionTable preauthSessionTable = new SessionTable(); private OutstandingRequests outstandingRequests = new OutstandingRequests(); private SequenceWindow sequenceWindow; private byte[] gssNegotiateToken; @@ -99,6 +97,10 @@ SessionTable getSessionTable() { return sessionTable; } + public SessionTable getPreauthSessionTable() { + return preauthSessionTable; + } + public UUID getClientGuid() { return clientGuid; } diff --git a/src/main/java/com/hierynomus/smbj/connection/OutstandingRequests.java b/src/main/java/com/hierynomus/smbj/connection/OutstandingRequests.java index 10f75e6b..b019e74d 100644 --- a/src/main/java/com/hierynomus/smbj/connection/OutstandingRequests.java +++ b/src/main/java/com/hierynomus/smbj/connection/OutstandingRequests.java @@ -15,13 +15,12 @@ */ package com.hierynomus.smbj.connection; -import com.hierynomus.smbj.common.SMBRuntimeException; - import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.UUID; import java.util.concurrent.locks.ReentrantReadWriteLock; +import com.hierynomus.smbj.common.SMBRuntimeException; class OutstandingRequests { private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); diff --git a/src/main/java/com/hierynomus/smbj/connection/Request.java b/src/main/java/com/hierynomus/smbj/connection/Request.java index f58b8864..cd46e545 100644 --- a/src/main/java/com/hierynomus/smbj/connection/Request.java +++ b/src/main/java/com/hierynomus/smbj/connection/Request.java @@ -15,12 +15,6 @@ */ package com.hierynomus.smbj.connection; -import com.hierynomus.mssmb2.SMB2Packet; -import com.hierynomus.protocol.commons.concurrent.Promise; -import com.hierynomus.smbj.common.SMBRuntimeException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Date; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -29,6 +23,11 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.hierynomus.mssmb2.SMB2Packet; +import com.hierynomus.protocol.commons.concurrent.Promise; +import com.hierynomus.smbj.common.SMBRuntimeException; class Request { diff --git a/src/main/java/com/hierynomus/smbj/connection/SequenceWindow.java b/src/main/java/com/hierynomus/smbj/connection/SequenceWindow.java index 4d706b3b..9fee263b 100644 --- a/src/main/java/com/hierynomus/smbj/connection/SequenceWindow.java +++ b/src/main/java/com/hierynomus/smbj/connection/SequenceWindow.java @@ -15,11 +15,10 @@ */ package com.hierynomus.smbj.connection; -import com.hierynomus.smbj.common.SMBRuntimeException; - import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import com.hierynomus.smbj.common.SMBRuntimeException; /** * [MS-SMB2].pdf 3.2.4.1.6 Algorithm for Handling Available Message Sequence Numbers by the Client. diff --git a/src/main/java/com/hierynomus/smbj/connection/SessionTable.java b/src/main/java/com/hierynomus/smbj/connection/SessionTable.java index 8faccb5b..3ebe5b61 100644 --- a/src/main/java/com/hierynomus/smbj/connection/SessionTable.java +++ b/src/main/java/com/hierynomus/smbj/connection/SessionTable.java @@ -15,15 +15,12 @@ */ package com.hierynomus.smbj.connection; -import com.hierynomus.smbj.session.Session; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; +import java.util.*; import java.util.concurrent.locks.ReentrantLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.hierynomus.smbj.session.Session; class SessionTable { private static final Logger logger = LoggerFactory.getLogger(SessionTable.class); @@ -39,6 +36,15 @@ void registerSession(Long id, Session session) { } } + Session find(Long id) { + lock.lock(); + try { + return lookup.get(id); + } finally { + lock.unlock(); + } + } + Session sessionClosed(Long id) { lock.lock(); try { @@ -57,17 +63,10 @@ boolean isActive(Long id) { } } - void closeRemainingSessions() { + Collection activeSessions() { lock.lock(); try { - for (Long id : new HashSet<>(lookup.keySet())) { - Session session = lookup.get(id); - try { - session.close(); - } catch (IOException e) { - logger.error("Error closing session", e); - } - } + return new ArrayList<>(lookup.values()); } finally { lock.unlock(); } diff --git a/src/main/java/com/hierynomus/smbj/io/ByteChunkProvider.java b/src/main/java/com/hierynomus/smbj/io/ByteChunkProvider.java index 6bb478e2..1ea961f3 100644 --- a/src/main/java/com/hierynomus/smbj/io/ByteChunkProvider.java +++ b/src/main/java/com/hierynomus/smbj/io/ByteChunkProvider.java @@ -15,21 +15,21 @@ */ package com.hierynomus.smbj.io; -import com.hierynomus.protocol.commons.buffer.Buffer; -import com.hierynomus.smbj.common.SMBRuntimeException; - import java.io.IOException; import java.io.OutputStream; +import com.hierynomus.protocol.commons.buffer.Buffer; +import com.hierynomus.smbj.common.SMBRuntimeException; public abstract class ByteChunkProvider { - static final int CHUNK_SIZE = 64 * 1024; + protected static final int CHUNK_SIZE = 64 * 1024; - protected long offset = 0; + protected long offset; + protected int chunkSize = CHUNK_SIZE; public abstract boolean isAvailable(); public void writeChunk(OutputStream os) { - byte[] chunk = new byte[CHUNK_SIZE]; + byte[] chunk = new byte[chunkSize]; try { int size = getChunk(chunk); os.write(chunk, 0, size); @@ -40,7 +40,7 @@ public void writeChunk(OutputStream os) { } public void writeChunks(Buffer buffer, int nrChunks) { - byte[] chunk = new byte[CHUNK_SIZE]; + byte[] chunk = new byte[chunkSize]; for (int i = 0; i < nrChunks; i++) { try { int size = getChunk(chunk); @@ -53,7 +53,7 @@ public void writeChunks(Buffer buffer, int nrChunks) { } public void writeChunk(Buffer buffer) { - byte[] chunk = new byte[CHUNK_SIZE]; + byte[] chunk = new byte[chunkSize]; try { int size = getChunk(chunk); buffer.putRawBytes(chunk, 0, size); diff --git a/src/main/java/com/hierynomus/smbj/io/FileByteChunkProvider.java b/src/main/java/com/hierynomus/smbj/io/FileByteChunkProvider.java index db1a5566..598f85dc 100644 --- a/src/main/java/com/hierynomus/smbj/io/FileByteChunkProvider.java +++ b/src/main/java/com/hierynomus/smbj/io/FileByteChunkProvider.java @@ -15,9 +15,8 @@ */ package com.hierynomus.smbj.io; -import com.hierynomus.smbj.common.SMBRuntimeException; - import java.io.*; +import com.hierynomus.smbj.common.SMBRuntimeException; public class FileByteChunkProvider extends ByteChunkProvider { @@ -29,6 +28,24 @@ public FileByteChunkProvider(File file) throws FileNotFoundException { fis = new BufferedInputStream(new FileInputStream(file), CHUNK_SIZE); } + public FileByteChunkProvider(File file, long offset) throws IOException { + this.file = file; + fis = new BufferedInputStream(new FileInputStream(file), CHUNK_SIZE); + ensureSkipped(fis, offset); + this.offset = offset; + } + + private void ensureSkipped(final BufferedInputStream fis, final long offset) throws IOException { + long skipped = 0; + while (skipped < offset && fis.available() > 0) { + skipped += fis.skip(offset); + } + + if (skipped < offset) { + throw new IOException("Was unable to go to the requested offset of " + offset + " of file " + file); + } + } + @Override protected int getChunk(byte[] chunk) throws IOException { int count = 0; diff --git a/src/main/java/com/hierynomus/smbj/io/InputStreamByteChunkProvider.java b/src/main/java/com/hierynomus/smbj/io/InputStreamByteChunkProvider.java index cc143276..f6ca5944 100644 --- a/src/main/java/com/hierynomus/smbj/io/InputStreamByteChunkProvider.java +++ b/src/main/java/com/hierynomus/smbj/io/InputStreamByteChunkProvider.java @@ -15,10 +15,11 @@ */ package com.hierynomus.smbj.io; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; import com.hierynomus.smbj.common.SMBRuntimeException; -import java.io.*; - public class InputStreamByteChunkProvider extends ByteChunkProvider { private BufferedInputStream is; diff --git a/src/main/java/com/hierynomus/smbj/session/Session.java b/src/main/java/com/hierynomus/smbj/session/Session.java index 683de167..4f22eb3b 100644 --- a/src/main/java/com/hierynomus/smbj/session/Session.java +++ b/src/main/java/com/hierynomus/smbj/session/Session.java @@ -17,13 +17,16 @@ import java.io.IOException; import java.util.concurrent.Future; +import javax.crypto.spec.SecretKeySpec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.hierynomus.mssmb2.SMB2Packet; import com.hierynomus.mssmb2.SMB2ShareCapabilities; import com.hierynomus.mssmb2.messages.SMB2Logoff; import com.hierynomus.mssmb2.messages.SMB2TreeConnectRequest; import com.hierynomus.mssmb2.messages.SMB2TreeConnectResponse; import com.hierynomus.protocol.commons.concurrent.Futures; +import com.hierynomus.smbj.common.MessageSigning; import com.hierynomus.smbj.common.SMBApiException; import com.hierynomus.smbj.common.SMBRuntimeException; import com.hierynomus.smbj.common.SmbPath; @@ -41,16 +44,24 @@ */ public class Session implements AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(Session.class); - long sessionId; + private long sessionId; + + private SecretKeySpec signingKeySpec; + private boolean signingRequired; + private Connection connection; private SMBEventBus bus; private TreeConnectTable treeConnectTable = new TreeConnectTable(); - public Session(long sessionId, Connection connection, SMBEventBus bus) { + public Session(long sessionId, Connection connection, SMBEventBus bus, boolean signingRequired) { this.sessionId = sessionId; this.connection = connection; this.bus = bus; - bus.subscribe(this); + this.signingKeySpec = null; + this.signingRequired = signingRequired; + if (bus != null) { + bus.subscribe(this); + } } public long getSessionId() { @@ -84,9 +95,10 @@ private Share connectTree(final String shareName) { try { SMB2TreeConnectRequest smb2TreeConnectRequest = new SMB2TreeConnectRequest(connection.getNegotiatedProtocol().getDialect(), smbPath, sessionId); smb2TreeConnectRequest.getHeader().setCreditRequest(256); - Future send = connection.send(smb2TreeConnectRequest); + Future send = this.send(smb2TreeConnectRequest); SMB2TreeConnectResponse response = Futures.get(send, TransportException.Wrapper); if (response.getHeader().getStatus().isError()) { + logger.debug(response.getHeader().toString()); throw new SMBApiException(response.getHeader(), "Could not connect to " + smbPath); } @@ -114,28 +126,43 @@ private Share connectTree(final String shareName) { @Handler private void disconnectTree(TreeDisconnected disconnectEvent) { if (disconnectEvent.getSessionId() == sessionId) { - logger.debug("Notified of TreeDisconnected <<" + disconnectEvent.getTreeId() + ">>"); + logger.debug("Notified of TreeDisconnected <<{}>>", disconnectEvent.getTreeId()); treeConnectTable.closed(disconnectEvent.getTreeId()); } } public void logoff() throws TransportException { - logger.info("Logging off session " + sessionId + " from host " + connection.getRemoteHostname()); + logger.info("Logging off session {} from host {}", sessionId, connection.getRemoteHostname()); for (TreeConnect treeConnect : treeConnectTable.getOpenTreeConnects()) { try { treeConnect.getHandle().close(); } catch (IOException e) { - logger.error(String.format("Caught exception while closing TreeConnect with id: %s", treeConnect.getTreeId()), e); + logger.error("Caught exception while closing TreeConnect with id: {}", treeConnect.getTreeId(), e); } } SMB2Logoff logoff = new SMB2Logoff(connection.getNegotiatedProtocol().getDialect(), sessionId); - SMB2Logoff response = Futures.get(connection.send(logoff), TransportException.Wrapper); + SMB2Logoff response = Futures.get(this.send(logoff), TransportException.Wrapper); if (!response.getHeader().getStatus().isSuccess()) { throw new SMBApiException(response.getHeader(), "Could not logoff session <<" + sessionId + ">>"); } bus.publish(new SessionLoggedOff(sessionId)); } + public boolean isSigningRequired() { + return signingRequired; + } + + public void setSigningKey(byte[] signingKeyBytes) { + if (connection.getNegotiatedProtocol().getDialect().isSmb3x()) { + throw new IllegalStateException("Cannot set a signing key (yet) for SMB3.x"); + } else { + this.signingKeySpec = new SecretKeySpec(signingKeyBytes, MessageSigning.HMAC_SHA256_ALGORITHM); + } + } + + public SecretKeySpec getSigningKeySpec() { + return signingKeySpec; + } @Override public void close() throws IOException { @@ -145,4 +172,31 @@ public void close() throws IOException { public Connection getConnection() { return connection; } + + /** + * send a packet. The packet will be signed or not depending on the session's flags. + * + * @param packet SMBPacket to send + * @return a Future to be used to retrieve the response packet + * @throws TransportException + */ + public Future send(SMB2Packet packet) throws TransportException { + if (signingRequired && signingKeySpec == null) { + throw new TransportException("Message signing is required, but no signing key is negotiated"); + } + return connection.send(packet, signingKeySpec); + } + + public void setBus(SMBEventBus bus) { + if (this.bus != null) { + this.bus.unsubscribe(this); + this.bus = null; + } + this.bus = bus; + bus.subscribe(this); + } + + public void setSessionId(long sessionId) { + this.sessionId = sessionId; + } } diff --git a/src/main/java/com/hierynomus/smbj/session/TreeConnectTable.java b/src/main/java/com/hierynomus/smbj/session/TreeConnectTable.java index 42d6e161..9a17bef5 100644 --- a/src/main/java/com/hierynomus/smbj/session/TreeConnectTable.java +++ b/src/main/java/com/hierynomus/smbj/session/TreeConnectTable.java @@ -15,17 +15,16 @@ */ package com.hierynomus.smbj.session; -import com.hierynomus.smbj.share.TreeConnect; - import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; +import com.hierynomus.smbj.share.TreeConnect; /** * [MS-SMB2].pdf 3.2.1.3 Per Session - * + *

* A table of tree connects, as specified in section 3.2.1.4. The table MUST allow lookup by both TreeConnect.TreeConnectId and by share name. */ class TreeConnectTable { diff --git a/src/main/java/com/hierynomus/smbj/share/Directory.java b/src/main/java/com/hierynomus/smbj/share/Directory.java index 15417eb7..1c1c7382 100644 --- a/src/main/java/com/hierynomus/smbj/share/Directory.java +++ b/src/main/java/com/hierynomus/smbj/share/Directory.java @@ -15,6 +15,14 @@ */ package com.hierynomus.smbj.share; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.Future; + +import com.hierynomus.mssmb2.SMB2MessageCommandCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.mserref.NtStatus; import com.hierynomus.msfscc.FileInformationClass; import com.hierynomus.msfscc.fileinformation.FileInfo; @@ -28,12 +36,6 @@ import com.hierynomus.smbj.connection.Connection; import com.hierynomus.smbj.session.Session; import com.hierynomus.smbj.transport.TransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.EnumSet; -import java.util.List; -import java.util.concurrent.Future; public class Directory extends DiskEntry { @@ -46,28 +48,44 @@ public Directory(SMB2FileId fileId, TreeConnect treeConnect, String fileName) { public List list() throws TransportException, SMBApiException { Session session = treeConnect.getSession(); Connection connection = session.getConnection(); + int index = 0; + int newDataLength = -1; + List fileList = new ArrayList(); - // Query Directory Request - SMB2QueryDirectoryRequest qdr = new SMB2QueryDirectoryRequest(connection.getNegotiatedProtocol().getDialect(), + // Keep querying until we don't get new data + do { + // Query Directory Request + SMB2QueryDirectoryRequest qdr = new SMB2QueryDirectoryRequest(connection.getNegotiatedProtocol().getDialect(), session.getSessionId(), treeConnect.getTreeId(), getFileId(), FileInformationClass.FileIdBothDirectoryInformation, // FileInformationClass // .FileDirectoryInformation, - EnumSet.of(SMB2QueryDirectoryRequest.SMB2QueryDirectoryFlags.SMB2_REOPEN), - 0, null); - Future qdFuture = connection.send(qdr); + EnumSet.of(SMB2QueryDirectoryRequest.SMB2QueryDirectoryFlags.SMB2_INDEX_SPECIFIED), + index, null); + Future qdFuture = connection.send(qdr); - SMB2QueryDirectoryResponse qdResp = Futures.get(qdFuture, TransportException.Wrapper); + SMB2QueryDirectoryResponse qdResp = Futures.get(qdFuture, TransportException.Wrapper); - if (qdResp.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { - throw new SMBApiException(qdResp.getHeader(), "Query directory failed for " + fileName + "/" + fileId); - } - byte[] outputBuffer = qdResp.getOutputBuffer(); + if (qdResp.getHeader().getStatus() == NtStatus.STATUS_NO_MORE_FILES) { + newDataLength = 0; + } else { + if (qdResp.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { + throw new SMBApiException(qdResp.getHeader().getStatus(), + qdResp.getHeader().getStatusCode(), SMB2MessageCommandCode.SMB2_QUERY_DIRECTORY, + "Query directory failed for " + fileName + "/" + fileId); + } + byte[] outputBuffer = qdResp.getOutputBuffer(); + newDataLength = outputBuffer.length; + index += newDataLength; - try { - return FileInformationFactory.parseFileInformationList(outputBuffer, FileInformationClass.FileIdBothDirectoryInformation); - } catch (Buffer.BufferException e) { - throw new TransportException(e); - } + try { + fileList.addAll(FileInformationFactory.parseFileInformationList(outputBuffer, FileInformationClass.FileIdBothDirectoryInformation)); + } catch (Buffer.BufferException e) { + throw new TransportException(e); + } + } + } while(newDataLength > 65000); // Optimization for not making the last call which returns NO_MORE_FILES. + + return fileList; } public SMB2FileId getFileId() { diff --git a/src/main/java/com/hierynomus/smbj/share/DiskEntry.java b/src/main/java/com/hierynomus/smbj/share/DiskEntry.java index 6760f544..5d3f0613 100644 --- a/src/main/java/com/hierynomus/smbj/share/DiskEntry.java +++ b/src/main/java/com/hierynomus/smbj/share/DiskEntry.java @@ -15,11 +15,11 @@ */ package com.hierynomus.smbj.share; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.mssmb2.SMB2FileId; import com.hierynomus.smbj.common.SMBApiException; import com.hierynomus.smbj.transport.TransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; abstract class DiskEntry { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); diff --git a/src/main/java/com/hierynomus/smbj/share/DiskShare.java b/src/main/java/com/hierynomus/smbj/share/DiskShare.java index 17486810..ce5b2e27 100644 --- a/src/main/java/com/hierynomus/smbj/share/DiskShare.java +++ b/src/main/java/com/hierynomus/smbj/share/DiskShare.java @@ -15,6 +15,11 @@ */ package com.hierynomus.smbj.share; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.Future; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.msdtyp.AccessMask; import com.hierynomus.msdtyp.SecurityDescriptor; import com.hierynomus.msdtyp.SecurityInformation; @@ -41,18 +46,15 @@ import com.hierynomus.smbj.connection.Connection; import com.hierynomus.smbj.session.Session; import com.hierynomus.smbj.transport.TransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.EnumSet; -import java.util.List; -import java.util.concurrent.Future; import static com.hierynomus.msdtyp.AccessMask.FILE_READ_ATTRIBUTES; import static com.hierynomus.msdtyp.AccessMask.GENERIC_READ; import static com.hierynomus.msfscc.FileAttributes.FILE_ATTRIBUTE_DIRECTORY; import static com.hierynomus.msfscc.FileAttributes.FILE_ATTRIBUTE_NORMAL; -import static com.hierynomus.mssmb2.SMB2ShareAccess.*; +import static com.hierynomus.mssmb2.SMB2ShareAccess.EnumUtils; +import static com.hierynomus.mssmb2.SMB2ShareAccess.FILE_SHARE_DELETE; +import static com.hierynomus.mssmb2.SMB2ShareAccess.FILE_SHARE_READ; +import static com.hierynomus.mssmb2.SMB2ShareAccess.FILE_SHARE_WRITE; import static com.hierynomus.mssmb2.messages.SMB2QueryInfoRequest.SMB2QueryInfoType.SMB2_0_INFO_SECURITY; import static com.hierynomus.protocol.commons.EnumWithValue.EnumUtils.toLong; @@ -122,10 +124,10 @@ public DiskEntry getFile(String path) { * Get a handle to a directory in the given path */ public Directory openDirectory( - String path, - EnumSet accessMask, - EnumSet shareAccess, - SMB2CreateDisposition createDisposition) throws TransportException, SMBApiException { + String path, + EnumSet accessMask, + EnumSet shareAccess, + SMB2CreateDisposition createDisposition) throws TransportException, SMBApiException { logger.info("OpenDirectory {},{},{},{},{}", path, accessMask, shareAccess, createDisposition); SMB2FileId fileId = open(path, toLong(accessMask), EnumSet.of(FILE_ATTRIBUTE_DIRECTORY), shareAccess, createDisposition, EnumSet.of(SMB2CreateOptions.FILE_DIRECTORY_FILE)); @@ -136,7 +138,7 @@ public Directory openDirectory( /** * Get a handle to a file */ - public File openFile( String path, EnumSet accessMask, SMB2CreateDisposition createDisposition) throws TransportException, SMBApiException { + public File openFile(String path, EnumSet accessMask, SMB2CreateDisposition createDisposition) throws TransportException, SMBApiException { logger.info("OpenFile {},{},{}", path, accessMask, createDisposition); SMB2FileId fileId = open(path, toLong(accessMask), null, EnumSet.of(FILE_SHARE_READ), createDisposition, EnumSet.of(SMB2CreateOptions.FILE_NON_DIRECTORY_FILE)); @@ -365,13 +367,13 @@ private String makePath(String first, String... more) { } private void deleteCommon(String path, SMB2CreateRequest smb2CreateRequest) - throws TransportException, SMBApiException { + throws TransportException, SMBApiException { Session session = treeConnect.getSession(); Connection connection = session.getConnection(); // TODO Use Compounding - Future sendFuture = connection.send(smb2CreateRequest); + Future sendFuture = session.send(smb2CreateRequest); SMB2CreateResponse response = Futures.get(sendFuture, TransportException.Wrapper); if (response.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { @@ -390,7 +392,7 @@ private void deleteCommon(String path, SMB2CreateRequest smb2CreateRequest) SMB2SetInfoResponse setInfoResponse = Futures.get(setInfoFuture, TransportException.Wrapper); if (setInfoResponse.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { - throw new SMBApiException(response.getHeader(), "SetInfo failed for " + path); + throw new SMBApiException(setInfoResponse.getHeader(), "SetInfo failed for " + path); } } finally { SMB2Close closeReq = new SMB2Close(connection.getNegotiatedProtocol().getDialect(), @@ -399,7 +401,7 @@ private void deleteCommon(String path, SMB2CreateRequest smb2CreateRequest) SMB2Close closeResponse = Futures.get(closeFuture, TransportException.Wrapper); if (closeResponse.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { - throw new SMBApiException(response.getHeader(), "Close failed for " + path); + throw new SMBApiException(closeResponse.getHeader(), "Close failed for " + path); } } @@ -431,11 +433,11 @@ private boolean exists(String path, EnumSet createOptions) th } private byte[] queryInfoCommon( - String path, - SMB2QueryInfoRequest.SMB2QueryInfoType infoType, - EnumSet securityInfo, - FileInformationClass fileInformationClass) - throws SMBApiException { + String path, + SMB2QueryInfoRequest.SMB2QueryInfoType infoType, + EnumSet securityInfo, + FileInformationClass fileInformationClass) + throws SMBApiException { SMB2FileId fileId = null; try { @@ -478,7 +480,7 @@ private byte[] queryInfoCommon( fileId, infoType, fileInformationClass, fileSysemInformationClass, null, securityInfo); try { - Future qiResponseFuture = connection.send(qreq); + Future qiResponseFuture = session.send(qreq); SMB2QueryInfoResponse qresp = Futures.get(qiResponseFuture, SMBRuntimeException.Wrapper); if (qresp.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { diff --git a/src/main/java/com/hierynomus/smbj/share/File.java b/src/main/java/com/hierynomus/smbj/share/File.java index 54383d2a..c404e6c1 100644 --- a/src/main/java/com/hierynomus/smbj/share/File.java +++ b/src/main/java/com/hierynomus/smbj/share/File.java @@ -15,6 +15,12 @@ */ package com.hierynomus.smbj.share; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Future; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.mserref.NtStatus; import com.hierynomus.mssmb2.SMB2FileId; import com.hierynomus.mssmb2.messages.SMB2WriteRequest; @@ -26,13 +32,6 @@ import com.hierynomus.smbj.io.ByteChunkProvider; import com.hierynomus.smbj.session.Session; import com.hierynomus.smbj.transport.TransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.Future; public class File extends DiskEntry { @@ -52,7 +51,7 @@ public void write(ByteChunkProvider provider, ProgressListener progressListener) logger.debug("Writing to {} from offset {}", this.fileName, provider.getOffset()); SMB2WriteRequest wreq = new SMB2WriteRequest(connection.getNegotiatedProtocol().getDialect(), getFileId(), session.getSessionId(), treeConnect.getTreeId(), provider, connection.getNegotiatedProtocol().getMaxWriteSize()); - Future writeFuture = connection.send(wreq); + Future writeFuture = session.send(wreq); SMB2WriteResponse wresp = Futures.get(writeFuture, TransportException.Wrapper); if (wresp.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { throw new SMBApiException(wresp.getHeader(), "Write failed for " + this); @@ -100,8 +99,8 @@ public OutputStream getOutputStream(final ProgressListener listener) { @Override public String toString() { return "File{" + - "fileId=" + fileId + - ", fileName='" + fileName + '\'' + - '}'; + "fileId=" + fileId + + ", fileName='" + fileName + '\'' + + '}'; } } diff --git a/src/main/java/com/hierynomus/smbj/share/FileInputStream.java b/src/main/java/com/hierynomus/smbj/share/FileInputStream.java index 551ec0b7..b92f90a4 100644 --- a/src/main/java/com/hierynomus/smbj/share/FileInputStream.java +++ b/src/main/java/com/hierynomus/smbj/share/FileInputStream.java @@ -15,6 +15,11 @@ */ package com.hierynomus.smbj.share; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.Future; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.mserref.NtStatus; import com.hierynomus.mssmb2.SMB2FileId; import com.hierynomus.mssmb2.messages.SMB2ReadRequest; @@ -25,12 +30,6 @@ import com.hierynomus.smbj.connection.Connection; import com.hierynomus.smbj.session.Session; import com.hierynomus.smbj.transport.TransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.Future; public class FileInputStream extends InputStream { @@ -121,6 +120,6 @@ private void loadBuffer() throws IOException { private Future sendRequest() throws IOException { SMB2ReadRequest rreq = new SMB2ReadRequest(connection.getNegotiatedProtocol(), fileId, session.getSessionId(), treeConnect.getTreeId(), offset); - return connection.send(rreq); + return session.send(rreq); } } diff --git a/src/main/java/com/hierynomus/smbj/share/FileOutputStream.java b/src/main/java/com/hierynomus/smbj/share/FileOutputStream.java index 55c4e3ce..9384a26a 100644 --- a/src/main/java/com/hierynomus/smbj/share/FileOutputStream.java +++ b/src/main/java/com/hierynomus/smbj/share/FileOutputStream.java @@ -15,6 +15,11 @@ */ package com.hierynomus.smbj.share; +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.Future; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.mserref.NtStatus; import com.hierynomus.mssmb2.SMB2FileId; import com.hierynomus.mssmb2.messages.SMB2WriteRequest; @@ -26,12 +31,6 @@ import com.hierynomus.smbj.io.ByteChunkProvider; import com.hierynomus.smbj.session.Session; import com.hierynomus.smbj.transport.TransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.Future; public class FileOutputStream extends OutputStream { @@ -98,7 +97,7 @@ public void flush() throws IOException { private void sendWriteRequest() throws TransportException { SMB2WriteRequest wreq = new SMB2WriteRequest(connection.getNegotiatedProtocol().getDialect(), fileId, session.getSessionId(), treeConnect.getTreeId(), provider, connection.getNegotiatedProtocol().getMaxWriteSize()); - Future writeFuture = connection.send(wreq); + Future writeFuture = session.send(wreq); SMB2WriteResponse wresp = Futures.get(writeFuture, TransportException.Wrapper); if (wresp.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { throw new SMBApiException(wresp.getHeader(), "Write failed for " + this); diff --git a/src/main/java/com/hierynomus/smbj/share/Share.java b/src/main/java/com/hierynomus/smbj/share/Share.java index e8b6ef3f..6e37f392 100644 --- a/src/main/java/com/hierynomus/smbj/share/Share.java +++ b/src/main/java/com/hierynomus/smbj/share/Share.java @@ -15,6 +15,12 @@ */ package com.hierynomus.smbj.share; +import java.io.IOException; +import java.util.EnumSet; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.mserref.NtStatus; import com.hierynomus.msfscc.FileAttributes; import com.hierynomus.mssmb2.SMB2CreateDisposition; @@ -31,13 +37,6 @@ import com.hierynomus.smbj.connection.Connection; import com.hierynomus.smbj.session.Session; import com.hierynomus.smbj.transport.TransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; public class Share implements AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(Share.class); @@ -71,15 +70,14 @@ public SMB2FileId open( String path, long accessMask, EnumSet fileAttributes, EnumSet shareAccess, SMB2CreateDisposition createDisposition, EnumSet createOptions) - throws SMBApiException { + throws SMBApiException { logger.info("open {},{}", path); Session session = treeConnect.getSession(); - Connection connection = session.getConnection(); SMB2CreateRequest cr = openFileRequest( - treeConnect, path, accessMask, shareAccess, fileAttributes, createDisposition, createOptions); + treeConnect, path, accessMask, shareAccess, fileAttributes, createDisposition, createOptions); try { - Future responseFuture = connection.send(cr); + Future responseFuture = session.send(cr); SMB2CreateResponse cresponse = Futures.get(responseFuture, SMBRuntimeException.Wrapper); if (cresponse.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { throw new SMBApiException(cresponse.getHeader(), "Create failed for " + path); @@ -94,31 +92,32 @@ public SMB2FileId open( protected static SMB2CreateRequest openFileRequest( - TreeConnect treeConnect, String path, - long accessMask, - EnumSet shareAccess, - EnumSet fileAttributes, - SMB2CreateDisposition createDisposition, - EnumSet createOptions) { + TreeConnect treeConnect, String path, + long accessMask, + EnumSet shareAccess, + EnumSet fileAttributes, + SMB2CreateDisposition createDisposition, + EnumSet createOptions) { Session session = treeConnect.getSession(); SMB2CreateRequest cr = new SMB2CreateRequest( session.getConnection().getNegotiatedProtocol().getDialect(), - session.getSessionId(), treeConnect.getTreeId(), - accessMask, - fileAttributes, - shareAccess, - createDisposition, - createOptions, path); + session.getSessionId(), treeConnect.getTreeId(), + accessMask, + fileAttributes, + shareAccess, + createDisposition, + createOptions, path); return cr; } public void close(SMB2FileId fileId) throws TransportException, SMBApiException { - Connection connection = treeConnect.getSession().getConnection(); + Session session = treeConnect.getSession(); + Connection connection = session.getConnection(); SMB2Close closeReq = new SMB2Close( connection.getNegotiatedProtocol().getDialect(), treeConnect.getSession().getSessionId(), treeConnect.getTreeId(), fileId); - Future closeFuture = connection.send(closeReq); + Future closeFuture = session.send(closeReq); SMB2Close closeResp = Futures.get(closeFuture, TransportException.Wrapper); if (closeResp.getHeader().getStatus() != NtStatus.STATUS_SUCCESS) { diff --git a/src/main/java/com/hierynomus/smbj/share/TreeConnect.java b/src/main/java/com/hierynomus/smbj/share/TreeConnect.java index 64fb392d..067bdd1a 100644 --- a/src/main/java/com/hierynomus/smbj/share/TreeConnect.java +++ b/src/main/java/com/hierynomus/smbj/share/TreeConnect.java @@ -15,6 +15,8 @@ */ package com.hierynomus.smbj.share; +import java.util.EnumSet; +import java.util.concurrent.Future; import com.hierynomus.mssmb2.SMB2Packet; import com.hierynomus.mssmb2.SMB2ShareCapabilities; import com.hierynomus.mssmb2.messages.SMB2TreeDisconnect; @@ -27,9 +29,6 @@ import com.hierynomus.smbj.session.Session; import com.hierynomus.smbj.transport.TransportException; -import java.util.EnumSet; -import java.util.concurrent.Future; - /** * */ @@ -64,7 +63,7 @@ Connection getConnection() { void close(Share share) throws TransportException { SMB2TreeDisconnect disconnect = new SMB2TreeDisconnect(connection.getNegotiatedProtocol().getDialect(), session.getSessionId(), treeId); - Future send = connection.send(disconnect); + Future send = session.send(disconnect); SMB2Packet smb2Packet = Futures.get(send, TransportException.Wrapper); if (!smb2Packet.getHeader().getStatus().isSuccess()) { throw new SMBApiException(smb2Packet.getHeader(), "Error closing connection to " + smbPath); diff --git a/src/main/java/com/hierynomus/smbj/transport/BaseTransport.java b/src/main/java/com/hierynomus/smbj/transport/BaseTransport.java index 7578e3e9..e4b70c51 100644 --- a/src/main/java/com/hierynomus/smbj/transport/BaseTransport.java +++ b/src/main/java/com/hierynomus/smbj/transport/BaseTransport.java @@ -15,22 +15,25 @@ */ package com.hierynomus.smbj.transport; -import com.hierynomus.mssmb2.SMB2Packet; -import com.hierynomus.smbj.common.SMBBuffer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.concurrent.locks.ReentrantLock; +import javax.crypto.spec.SecretKeySpec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.hierynomus.mssmb2.SMB2MessageFlag; +import com.hierynomus.mssmb2.SMB2Packet; +import com.hierynomus.smbj.common.MessageSigning; +import com.hierynomus.smbj.common.SMBBuffer; public abstract class BaseTransport implements TransportLayer { private final Logger logger = LoggerFactory.getLogger(this.getClass()); protected InputStream in; protected OutputStream out; - private final ReentrantLock writeLock = new ReentrantLock(); @Override @@ -57,5 +60,32 @@ public void write(SMB2Packet packet) throws TransportException { } } + @Override + public void writeSigned(SMB2Packet packet, SecretKeySpec signingKeySpec) throws TransportException { + writeLock.lock(); + try { + try { + SMBBuffer buffer = new SMBBuffer(); + packet.getHeader().setFlag(SMB2MessageFlag.SMB2_FLAGS_SIGNED); // set the SMB2_FLAGS_SIGNED flag + packet.write(buffer); + + signBuffer(buffer, signingKeySpec); + + logger.trace("Writing packet << {} >>, sequence number << {} >>", packet.getHeader().getMessage(), packet.getSequenceNumber()); + doWrite(buffer); + out.flush(); + } catch (IOException | InvalidKeyException | NoSuchAlgorithmException ioe) { + throw new TransportException(ioe); + } + } finally { + writeLock.unlock(); + } + } + protected abstract void doWrite(SMBBuffer packetData) throws IOException; + + + private static void signBuffer(SMBBuffer buffer, SecretKeySpec signingKeySpec) throws InvalidKeyException, NoSuchAlgorithmException { + MessageSigning.signBuffer(buffer.array(), buffer.available(), signingKeySpec); + } } diff --git a/src/main/java/com/hierynomus/smbj/transport/PacketReader.java b/src/main/java/com/hierynomus/smbj/transport/PacketReader.java index 27fefb00..15213f6d 100644 --- a/src/main/java/com/hierynomus/smbj/transport/PacketReader.java +++ b/src/main/java/com/hierynomus/smbj/transport/PacketReader.java @@ -15,13 +15,12 @@ */ package com.hierynomus.smbj.transport; -import com.hierynomus.protocol.Packet; -import com.hierynomus.smbj.common.SMBRuntimeException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.InputStream; import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.hierynomus.protocol.Packet; +import com.hierynomus.smbj.common.SMBRuntimeException; public abstract class PacketReader

> implements Runnable { private static final Logger logger = LoggerFactory.getLogger(PacketReader.class); @@ -65,6 +64,7 @@ private void readPacket() throws TransportException { /** * Read the actual SMB2 Packet from the {@link InputStream} + * * @return the read SMB2Packet * @throws TransportException */ diff --git a/src/main/java/com/hierynomus/smbj/transport/TransportLayer.java b/src/main/java/com/hierynomus/smbj/transport/TransportLayer.java index de39ee0c..dcaf9512 100644 --- a/src/main/java/com/hierynomus/smbj/transport/TransportLayer.java +++ b/src/main/java/com/hierynomus/smbj/transport/TransportLayer.java @@ -15,16 +15,16 @@ */ package com.hierynomus.smbj.transport; -import com.hierynomus.mssmb2.SMB2Packet; - import java.io.InputStream; import java.io.OutputStream; +import javax.crypto.spec.SecretKeySpec; +import com.hierynomus.mssmb2.SMB2Packet; public interface TransportLayer { /** * Initialize the Transport layer. - * + *

* This is called directly after a connection has been established. */ void init(InputStream in, OutputStream out); @@ -32,14 +32,24 @@ public interface TransportLayer { /** * The default port for the specified SMB transport layer. * - * @return the default port + * @return the default port */ int getDefaultPort(); /** * Write the packet to the transport. + * * @param packet The packet to write. * @return The sequence number of the packet. */ void write(SMB2Packet packet) throws TransportException; + + /** + * Write the packet to the transport, signed using the given signing key. + * + * @param packet The packet to write. + * @param signingKeySpec a SecretKeySpec to use while signing. If null, no signing should be done. + * @return The sequence number of the packet. + */ + void writeSigned(SMB2Packet packet, SecretKeySpec signingKeySpec) throws TransportException; } diff --git a/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpPacketReader.java b/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpPacketReader.java index baf61b22..d353b0e9 100644 --- a/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpPacketReader.java +++ b/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpPacketReader.java @@ -15,6 +15,8 @@ */ package com.hierynomus.smbj.transport.tcp; +import java.io.IOException; +import java.io.InputStream; import com.hierynomus.mssmb2.SMB2Packet; import com.hierynomus.mssmb2.messages.SMB2ResponseMessageFactory; import com.hierynomus.protocol.Packet; @@ -25,9 +27,6 @@ import com.hierynomus.smbj.transport.PacketReceiver; import com.hierynomus.smbj.transport.TransportException; -import java.io.IOException; -import java.io.InputStream; - public class DirectTcpPacketReader extends PacketReader { public DirectTcpPacketReader(InputStream in, PacketReceiver handler) { diff --git a/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpTransport.java b/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpTransport.java index 7f88e0dd..560004e2 100644 --- a/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpTransport.java +++ b/src/main/java/com/hierynomus/smbj/transport/tcp/DirectTcpTransport.java @@ -15,13 +15,12 @@ */ package com.hierynomus.smbj.transport.tcp; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.hierynomus.smbj.common.SMBBuffer; import com.hierynomus.smbj.transport.BaseTransport; import com.hierynomus.smbj.transport.TransportLayer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; /** * A transport layer to do SMB2 over Direct TCP/IP. diff --git a/src/main/java/com/hierynomus/spnego/NegTokenInit.java b/src/main/java/com/hierynomus/spnego/NegTokenInit.java index 7ccabd83..1ca395fb 100644 --- a/src/main/java/com/hierynomus/spnego/NegTokenInit.java +++ b/src/main/java/com/hierynomus/spnego/NegTokenInit.java @@ -15,15 +15,14 @@ */ package com.hierynomus.spnego; -import com.hierynomus.protocol.commons.buffer.Buffer; -import com.hierynomus.protocol.commons.buffer.Endian; -import com.hierynomus.smbj.common.SMBRuntimeException; -import org.bouncycastle.asn1.*; - import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; +import org.bouncycastle.asn1.*; +import com.hierynomus.protocol.commons.buffer.Buffer; +import com.hierynomus.protocol.commons.buffer.Endian; +import com.hierynomus.smbj.common.SMBRuntimeException; import static com.hierynomus.spnego.ObjectIdentifiers.SPNEGO; @@ -102,7 +101,7 @@ public NegTokenInit read(byte[] bytes) throws IOException { } private NegTokenInit read(Buffer buffer) throws IOException { - try(ASN1InputStream is = new ASN1InputStream(buffer.asInputStream())) { + try (ASN1InputStream is = new ASN1InputStream(buffer.asInputStream())) { ASN1Primitive applicationSpecific = is.readObject(); if (!(applicationSpecific instanceof BERApplicationSpecific || applicationSpecific instanceof DERApplicationSpecific)) { throw new SpnegoException("Incorrect GSS-API ASN.1 token received, expected to find an [APPLICATION 0], not: " + applicationSpecific); diff --git a/src/main/java/com/hierynomus/spnego/NegTokenTarg.java b/src/main/java/com/hierynomus/spnego/NegTokenTarg.java index 876cc254..2d1612a2 100644 --- a/src/main/java/com/hierynomus/spnego/NegTokenTarg.java +++ b/src/main/java/com/hierynomus/spnego/NegTokenTarg.java @@ -15,13 +15,12 @@ */ package com.hierynomus.spnego; +import java.io.IOException; +import java.math.BigInteger; +import org.bouncycastle.asn1.*; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.protocol.commons.buffer.Endian; import com.hierynomus.smbj.common.SMBRuntimeException; -import org.bouncycastle.asn1.*; - -import java.io.IOException; -import java.math.BigInteger; /** * This class can encode and decode the SPNEGO negTokenInit Token. diff --git a/src/main/java/com/hierynomus/spnego/SpnegoToken.java b/src/main/java/com/hierynomus/spnego/SpnegoToken.java index 67ffa610..774d7d6d 100644 --- a/src/main/java/com/hierynomus/spnego/SpnegoToken.java +++ b/src/main/java/com/hierynomus/spnego/SpnegoToken.java @@ -15,13 +15,12 @@ */ package com.hierynomus.spnego; -import com.hierynomus.protocol.commons.buffer.Buffer; +import java.io.IOException; +import java.util.Enumeration; import org.bouncycastle.asn1.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Enumeration; +import com.hierynomus.protocol.commons.buffer.Buffer; import static com.hierynomus.spnego.ObjectIdentifiers.SPNEGO; @@ -58,8 +57,8 @@ protected void parseSpnegoToken(ASN1Encodable spnegoToken) throws IOException { } Enumeration tokenObjects = ((ASN1Sequence) negToken).getObjects(); - while(tokenObjects.hasMoreElements()) { - ASN1Encodable asn1Encodable = (ASN1Encodable)tokenObjects.nextElement(); + while (tokenObjects.hasMoreElements()) { + ASN1Encodable asn1Encodable = (ASN1Encodable) tokenObjects.nextElement(); if (!(asn1Encodable instanceof ASN1TaggedObject)) { throw new SpnegoException("Expected an ASN.1 TaggedObject as " + tokenName + " contents, not: " + asn1Encodable); } diff --git a/src/test/groovy/com/hierynomus/msdtyp/SIDTest.groovy b/src/test/groovy/com/hierynomus/msdtyp/SIDTest.groovy index 65f8ce5b..99297230 100644 --- a/src/test/groovy/com/hierynomus/msdtyp/SIDTest.groovy +++ b/src/test/groovy/com/hierynomus/msdtyp/SIDTest.groovy @@ -23,4 +23,14 @@ class SIDTest extends Specification { expect: SID.EVERYONE.toString() == "S-1-1-0" } + + def "SID identity"() { + given: + SID s1 = new SID((byte) 1, [0, 0, 0, 0, 0, 1] as byte[], [0] as long[]); + SID s2 = new SID((byte) 1, [0, 0, 0, 0, 0, 1] as byte[], [0] as long[]); + + expect: + s1 == s2 + s1.hashCode() == s2.hashCode() + } } diff --git a/src/test/groovy/com/hierynomus/mssmb2/SMB2NotifyResponseTest.groovy b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyResponseTest.groovy similarity index 96% rename from src/test/groovy/com/hierynomus/mssmb2/SMB2NotifyResponseTest.groovy rename to src/test/groovy/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyResponseTest.groovy index e4c0a608..4a770b1f 100644 --- a/src/test/groovy/com/hierynomus/mssmb2/SMB2NotifyResponseTest.groovy +++ b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2ChangeNotifyResponseTest.groovy @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hierynomus.mssmb2 +package com.hierynomus.mssmb2.messages import com.hierynomus.mssmb2.messages.SMB2ChangeNotifyResponse import com.hierynomus.smbj.common.SMBBuffer @@ -21,7 +21,7 @@ import spock.lang.Specification import javax.xml.bind.DatatypeConverter -class SMB2NotifyResponseTest extends Specification { +class SMB2ChangeNotifyResponseTest extends Specification { def "should parse notifications"() { given: diff --git a/src/test/groovy/com/hierynomus/mssmb2/SMB2CreateResponseTest.groovy b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2CreateResponseTest.groovy similarity index 97% rename from src/test/groovy/com/hierynomus/mssmb2/SMB2CreateResponseTest.groovy rename to src/test/groovy/com/hierynomus/mssmb2/messages/SMB2CreateResponseTest.groovy index a36bf1fb..0c3a11a6 100644 --- a/src/test/groovy/com/hierynomus/mssmb2/SMB2CreateResponseTest.groovy +++ b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2CreateResponseTest.groovy @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hierynomus.mssmb2 +package com.hierynomus.mssmb2.messages import com.hierynomus.mssmb2.messages.SMB2CreateResponse import com.hierynomus.smbj.common.SMBBuffer diff --git a/src/test/groovy/com/hierynomus/mssmb2/SMB2QueryDirectoryResponseTest.groovy b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2QueryDirectoryResponseTest.groovy similarity index 99% rename from src/test/groovy/com/hierynomus/mssmb2/SMB2QueryDirectoryResponseTest.groovy rename to src/test/groovy/com/hierynomus/mssmb2/messages/SMB2QueryDirectoryResponseTest.groovy index 32cb8655..cc1598e5 100644 --- a/src/test/groovy/com/hierynomus/mssmb2/SMB2QueryDirectoryResponseTest.groovy +++ b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2QueryDirectoryResponseTest.groovy @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hierynomus.mssmb2 +package com.hierynomus.mssmb2.messages import com.hierynomus.msfscc.FileInformationClass import com.hierynomus.msfscc.fileinformation.FileInformationFactory diff --git a/src/test/groovy/com/hierynomus/mssmb2/SMB2ReadResponseTest.groovy b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2ReadResponseTest.groovy similarity index 99% rename from src/test/groovy/com/hierynomus/mssmb2/SMB2ReadResponseTest.groovy rename to src/test/groovy/com/hierynomus/mssmb2/messages/SMB2ReadResponseTest.groovy index b4b73563..8d7ffac2 100644 --- a/src/test/groovy/com/hierynomus/mssmb2/SMB2ReadResponseTest.groovy +++ b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2ReadResponseTest.groovy @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hierynomus.mssmb2 +package com.hierynomus.mssmb2.messages import com.hierynomus.mserref.NtStatus import com.hierynomus.mssmb2.messages.SMB2ReadResponse diff --git a/src/test/groovy/com/hierynomus/mssmb2/SMB2TreeConnectResponseTest.groovy b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2TreeConnectResponseTest.groovy similarity index 94% rename from src/test/groovy/com/hierynomus/mssmb2/SMB2TreeConnectResponseTest.groovy rename to src/test/groovy/com/hierynomus/mssmb2/messages/SMB2TreeConnectResponseTest.groovy index d130f5d1..680e0b07 100644 --- a/src/test/groovy/com/hierynomus/mssmb2/SMB2TreeConnectResponseTest.groovy +++ b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2TreeConnectResponseTest.groovy @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hierynomus.mssmb2 +package com.hierynomus.mssmb2.messages +import com.hierynomus.mssmb2.SMB2ShareCapabilities import com.hierynomus.mssmb2.messages.SMB2TreeConnectResponse import com.hierynomus.smbj.common.SMBBuffer import spock.lang.Specification diff --git a/src/test/groovy/com/hierynomus/mssmb2/SMB2WriteResponseTest.groovy b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2WriteResponseTest.groovy similarity index 97% rename from src/test/groovy/com/hierynomus/mssmb2/SMB2WriteResponseTest.groovy rename to src/test/groovy/com/hierynomus/mssmb2/messages/SMB2WriteResponseTest.groovy index 99da05f6..4f26c4ec 100644 --- a/src/test/groovy/com/hierynomus/mssmb2/SMB2WriteResponseTest.groovy +++ b/src/test/groovy/com/hierynomus/mssmb2/messages/SMB2WriteResponseTest.groovy @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hierynomus.mssmb2 +package com.hierynomus.mssmb2.messages import com.hierynomus.mssmb2.messages.SMB2WriteResponse import com.hierynomus.smbj.common.SMBBuffer diff --git a/src/test/groovy/com/hierynomus/smbj/io/FileByteChunkProviderTest.groovy b/src/test/groovy/com/hierynomus/smbj/io/FileByteChunkProviderTest.groovy index b3668634..be7d411d 100644 --- a/src/test/groovy/com/hierynomus/smbj/io/FileByteChunkProviderTest.groovy +++ b/src/test/groovy/com/hierynomus/smbj/io/FileByteChunkProviderTest.groovy @@ -67,6 +67,24 @@ class FileByteChunkProviderTest extends Specification { provider.isAvailable() } + def "should start at provided offset"() { + given: + def file = getFileWithRandomData(ByteChunkProvider.CHUNK_SIZE) + def provider = new FileByteChunkProvider(file, 100) + def baos = new ByteArrayOutputStream() + + when: + provider.writeChunk(baos) + + then: + def tmpBytes = new byte[ByteChunkProvider.CHUNK_SIZE - 100] + System.arraycopy(file.bytes, 100, tmpBytes, 0, tmpBytes.length) + baos.toByteArray() == tmpBytes + provider.offset == ByteChunkProvider.CHUNK_SIZE + !provider.isAvailable() + + } + private def getFileWithRandomData(int size) { def bytes = new byte[size] new Random().nextBytes(bytes)