Skip to content

Commit

Permalink
Rework NtlmAuthentication
Browse files Browse the repository at this point in the history
* Correctly set AvFlags if NtlmChallenge contains MIC

Signed-off-by: Jeroen van Erp <[email protected]>

* Reworking and testing NTLM authentication

Signed-off-by: Jeroen van Erp <[email protected]>

* Make lift happy & continue refactoring

Signed-off-by: Jeroen van Erp <[email protected]>

* Made negotiate complete according to MS-NLMP

Signed-off-by: Jeroen van Erp <[email protected]>

* Fix test

Signed-off-by: Jeroen van Erp <[email protected]>

* Fix normal integration tests

Signed-off-by: Jeroen van Erp <[email protected]>

* More tests for NtlmFunctions

Signed-off-by: Jeroen van Erp <[email protected]>

* Fix configs

Signed-off-by: Jeroen van Erp <[email protected]>

* Correctly reset PredictableRandom between tests

Signed-off-by: Jeroen van Erp <[email protected]>

* Do not send time as part of clientTargetInfo

Signed-off-by: Jeroen van Erp <[email protected]>

* Fix some warnings

Signed-off-by: Jeroen van Erp <[email protected]>

* Attempt NtlmAuthenticate with raw NtlmChallenge TargetInfo

This is one of the differences found in the authentication between 0.10.0 and 0.11.0

Signed-off-by: Jeroen van Erp <[email protected]>

* Added trace logging to log Ntlm message flow

Signed-off-by: Jeroen van Erp <[email protected]>

* Revert "Attempt NtlmAuthenticate with raw NtlmChallenge TargetInfo"

This reverts commit 5d281dc.

* Fix domain/workstation mixup

Signed-off-by: Jeroen van Erp <[email protected]>

* Added NtlmConfig.integrity boolean

Signed-off-by: Jeroen van Erp <[email protected]>

* Don't render MIC in NtlmAuthenticate is integrity disabled

Signed-off-by: Jeroen van Erp <[email protected]>

* Correct offsets and configurably omit version from messages

Signed-off-by: Jeroen van Erp <[email protected]>

* Temporary fix don't send MsvAvFlags 0x02

Signed-off-by: Jeroen van Erp <[email protected]>

* Refactor TargetInformation to separate AvPairs

Signed-off-by: Jeroen van Erp <[email protected]>

* Fix JavaDoc warnings

Signed-off-by: Jeroen van Erp <[email protected]>

* Some more tests

Signed-off-by: Jeroen van Erp <[email protected]>

* Temporarily disabled NtlmConfig.integrity

Signed-off-by: Jeroen van Erp <[email protected]>

---------

Signed-off-by: Jeroen van Erp <[email protected]>
  • Loading branch information
hierynomus authored May 8, 2023
1 parent 6a2c0b3 commit 410e631
Show file tree
Hide file tree
Showing 53 changed files with 1,731 additions and 468 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ test-output/
*.pdf
.vscode/
.java-version

.metals
.bloop
6 changes: 3 additions & 3 deletions src/it/docker-image/smb.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
[global]
security = user
log level = 5
log file = /var/log/samba/smbd.log
max log size = 10000

load printers = no
printcap name = /dev/null
Expand All @@ -14,9 +17,6 @@ server string = %h server (Samba, Ubuntu)
dns proxy = no
interfaces = 192.168.2.0/24 eth0
bind interfaces only = yes
log file = /var/log/samba/log.%m
max log size = 1000
syslog = 0
panic action = /usr/share/samba/panic-action %d
server role = standalone server
passdb backend = tdbsam
Expand Down
8 changes: 5 additions & 3 deletions src/it/groovy/com/hierynomus/smbj/IntegrationTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.hierynomus.mssmb2.SMB2Dialect
import com.hierynomus.mssmb2.SMB2ShareAccess
import com.hierynomus.security.bc.BCSecurityProvider
import com.hierynomus.smbj.auth.AuthenticationContext
import com.hierynomus.smbj.connection.Connection
import com.hierynomus.smbj.share.Directory
import com.hierynomus.smbj.share.DiskShare
import com.hierynomus.smbj.share.File
Expand All @@ -38,11 +39,12 @@ class IntegrationTest extends Specification {
static final def FOLDER_THAT_DOES_NOT_EXIST = "foo"


def config = SmbConfig.builder().withDialects(SMB2Dialect.SMB_3_0).withEncryptData(true).withSigningRequired(true).withMultiProtocolNegotiate(true).withDfsEnabled(true).withSecurityProvider(new BCSecurityProvider()).build()
def client = new SMBClient(config)
def connection = _
def config = SmbConfig.builder().withDialects(SMB2Dialect.SMB_3_0).withEncryptData(true).withSigningRequired(true).withMultiProtocolNegotiate(true).withDfsEnabled(true).withSecurityProvider(new BCSecurityProvider())
def client = _
Connection connection = null

def setup() {
client = new SMBClient(config.build())
connection = client.connect(IP)
}

Expand Down
2 changes: 1 addition & 1 deletion src/it/resources/logback-test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
<appender-ref ref="STDOUT"/>
</root>

<logger name="com.hierynomus.smbj" level="debug"/>
<logger name="com.hierynomus.smbj" level="trace"/>

</configuration>
2 changes: 1 addition & 1 deletion src/main/java/com/hierynomus/msdfsc/DomainCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* computer joined to a domain. This cache contains a list of trusted domains in
* both NetBIOS and fully qualified domain name forms, in addition to a list of
* DC host names for each domain. Conceptually, this is an array of tuples of
* the form <DomainName, DCHint, DCList>. Cache lookup involves finding a
* the form {@code <DomainName, DCHint, DCList>}. Cache lookup involves finding a
* DomainCache entry with a matching DomainName. This can be used to check for a
* valid domain name or to find a DC host name for a given domain name. DCHint
* identifies a DC host name from DCList that is the DC that was last
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/com/hierynomus/msdfsc/ReferralCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
* A hit on a ReferralCache entry indicates that the path in a name resolution
* operation is a DFS Root, DFS link, or a SYSVOL/NETLOGON share. A
* ReferralCache entry conceptually contains entries indexed by a DFS path
* prefix, DFSPathPrefix. An entry is a tuple of the form <DFSPathPrefix,
* RootOrLink, Interlink, TTL, TargetFailback, TargetHint, TargetList>.
* prefix, DFSPathPrefix. An entry is a tuple of the form {@code <DFSPathPrefix,
* RootOrLink, Interlink, TTL, TargetFailback, TargetHint, TargetList>}.
* DFSPathPrefix is the DFS path that corresponds to a DFS root or a DFS link,
* and is the same as the string pointed to by the DFSPathOffset of a
* DFS_REFERRAL_V2, DFS_REFERRAL_V3 or DFS_REFERRAL_V4 referral entry.
Expand All @@ -50,9 +50,9 @@
* ReferralCache entry refresh operation while permitting the use of the
* ReferralCache entry; the hard time-out limit can be used to fail any
* operation using the ReferralCache entry if all attempts to refresh it
* fail.<4> TargetHint identifies a target in TargetList that was last
* fail.&lt;4&gt; TargetHint identifies a target in TargetList that was last
* successfully used by the DFS client. TargetList consists of tuples of the
* form <TargetPath, TargetSetBoundary>, where TargetPath is the string pointed
* form {@code <TargetPath, TargetSetBoundary>}, where TargetPath is the string pointed
* to by the NetworkAddressOffset field (as specified in sections 2.2.5.2,
* 2.2.5.3, and 2.2.5.4). TargetSetBoundary is only present in V4 referrals and
* reflects the value from the TargetSetBoundary of the referral entry (as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ public static <F extends FileInformation> FileInformation.Decoder<F> getDecoder(
}

/**
* [MS-SMB2] 2.2.34 SMB2 QUERY_DIRECTORY Response for FileInformationClass->FileIdBothDirectoryInformation
* [MS-SMB2] 2.2.34 SMB2 QUERY_DIRECTORY Response for FileInformationClass-&gt;FileIdBothDirectoryInformation
*
* @param data
* @param decoder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* [MS-SMB2] 2.2.42 SMB2 COMPRESSION_TRANSFORM_HEADER
* <p>
* The SMB2 COMPRESSION_TRANSFORM_HEADER is used by the client or server when sending compressed messages.
* This optional header is only valid for the SMB 3.1.1 dialect<73>.
* This optional header is only valid for the SMB 3.1.1 dialect&lt;73&gt;.
*/
public class SMB2CompressionTransformHeader implements SMBHeader {
private static final byte[] COMPRESSED_PROTOCOL_ID = {(byte) 0xFC, 'S', 'M', 'B'};
Expand Down
93 changes: 93 additions & 0 deletions src/main/java/com/hierynomus/ntlm/NtlmConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.ntlm;

import com.hierynomus.ntlm.messages.WindowsVersion;

public class NtlmConfig {
private WindowsVersion windowsVersion;
private String workstationName;
private boolean integrity;
private boolean omitVersion;

public static NtlmConfig defaultConfig() {
return builder().build();
}

public static Builder builder() {
return new Builder();
}

private NtlmConfig() {
}

private NtlmConfig(NtlmConfig other) {
this.windowsVersion = other.windowsVersion;
this.workstationName = other.workstationName;
this.integrity = other.integrity;
this.omitVersion = other.omitVersion;
}

public WindowsVersion getWindowsVersion() {
return windowsVersion;
}

public String getWorkstationName() {
return workstationName;
}

public boolean isIntegrityEnabled() {
return integrity;
}

public boolean isOmitVersion() {
return omitVersion;
}

public static class Builder {
private NtlmConfig config;

public Builder() {
config = new NtlmConfig();
config.integrity = false; // TODO temporarily disabled until we can figure out why it fails (probably mechListMIC in NegTokenTarg)
config.omitVersion = false;
}

public Builder withWindowsVersion(WindowsVersion windowsVersion) {
config.windowsVersion = windowsVersion;
return this;
}

public Builder withWorkstationName(String workstationName) {
config.workstationName = workstationName;
return this;
}

public Builder withIntegrity(boolean integrity) {
config.integrity = integrity;
return this;
}

public Builder withOmitVersion(boolean omitVersion) {
config.omitVersion = omitVersion;
return this;
}

public NtlmConfig build() {
return new NtlmConfig(config);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.ntlm.messages;
package com.hierynomus.ntlm.av;

import com.hierynomus.protocol.commons.EnumWithValue;

public enum AvId implements EnumWithValue<AvId> {
MsvAvEOL(0x00L),
MsvAvNbComputerName(0x01L),
MsvAvNdDomainName(0x02L),
MsvAvNbDomainName(0x02L),
MsvAvDnsComputerName(0x03L),
MsvAvDnsDomainName(0x04L),
MsvAvDnsTreeName(0x05L),
MsvAvFlags(0x06L),
MsvAvTimestamp(0x07L),
MsvAvSingleHost(0x08L),
MsvAvTargetName(0x09L),
MsvChannelBindings(0x0AL);
MsvAvChannelBindings(0x0AL);

private final long value;

Expand Down
54 changes: 54 additions & 0 deletions src/main/java/com/hierynomus/ntlm/av/AvPair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.ntlm.av;

import com.hierynomus.protocol.commons.buffer.Buffer;

public abstract class AvPair<T> {

protected AvId avId;
protected T value;

public AvPair(AvId avId) {
this.avId = avId;
}

public AvPair(AvId avId, T value) {
this.avId = avId;
this.value = value;
}

public abstract void write(Buffer<?> buffer);

public abstract AvPair<T> read(Buffer<?> buffer) throws Buffer.BufferException;

public AvId getAvId() {
return avId;
}

public T getValue() {
return value;
}

@Override
public String toString() {
return "AvPair{" +
"avId=" + avId.name() +
", value=" + value +
'}';
}

}
40 changes: 40 additions & 0 deletions src/main/java/com/hierynomus/ntlm/av/AvPairChannelBindings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.ntlm.av;

import com.hierynomus.protocol.commons.buffer.Buffer;
import com.hierynomus.protocol.commons.buffer.Buffer.BufferException;

public class AvPairChannelBindings extends AvPair<byte[]> {

AvPairChannelBindings() {
super(AvId.MsvAvChannelBindings);
}

@Override
public void write(Buffer<?> buffer) {
buffer.putUInt16((int) this.avId.getValue()); // AvId (2 bytes)
buffer.putUInt16(value.length); // AvLen (2 bytes)
buffer.putRawBytes(value); // Value (AvLen bytes)
}

@Override
public AvPair<byte[]> read(Buffer<?> buffer) throws BufferException {
int length = buffer.readUInt16(); // AvLen (2 bytes)
this.value = buffer.readRawBytes(length); // Value (AvLen bytes)
return this;
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/hierynomus/ntlm/av/AvPairEnd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.ntlm.av;

import com.hierynomus.protocol.commons.buffer.Buffer;
import com.hierynomus.protocol.commons.buffer.Buffer.BufferException;

public class AvPairEnd extends AvPair<Void> {

public AvPairEnd() {
super(AvId.MsvAvEOL);
}

@Override
public void write(Buffer<?> buffer) {
buffer.putUInt16((int) this.avId.getValue()); // AvId (2 bytes)
buffer.putUInt16(0); // AvLen (2 bytes)
}

@Override
public AvPair<Void> read(Buffer<?> buffer) throws BufferException {
buffer.readUInt16(); // AvLen (2 bytes)
return this;
}
}
Loading

0 comments on commit 410e631

Please sign in to comment.