diff --git a/src/main/java/com/hierynomus/ntlm/NtlmConfig.java b/src/main/java/com/hierynomus/ntlm/NtlmConfig.java new file mode 100644 index 00000000..cc9fa2e0 --- /dev/null +++ b/src/main/java/com/hierynomus/ntlm/NtlmConfig.java @@ -0,0 +1,81 @@ +/* + * 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; +import com.hierynomus.ntlm.messages.WindowsVersion.NtlmRevisionCurrent; +import com.hierynomus.ntlm.messages.WindowsVersion.ProductMajorVersion; +import com.hierynomus.ntlm.messages.WindowsVersion.ProductMinorVersion; + +public class NtlmConfig { + private WindowsVersion windowsVersion; + private String workstationName; + + 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; + } + + public WindowsVersion getWindowsVersion() { + return windowsVersion; + } + + public String getWorkstationName() { + return workstationName; + } + + public static class Builder { + private NtlmConfig config; + + public Builder() { + config = new NtlmConfig(); + config.windowsVersion = new WindowsVersion(ProductMajorVersion.WINDOWS_MAJOR_VERSION_6, ProductMinorVersion.WINDOWS_MINOR_VERSION_1, 7600, NtlmRevisionCurrent.NTLMSSP_REVISION_W2K3); + } + + 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) { + return this; + } + + public Builder withOmitVersion(boolean omitVersion) { + return this; + } + + public NtlmConfig build() { + return new NtlmConfig(config); + } + } +} diff --git a/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java b/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java index fc224cf5..dcc0080d 100644 --- a/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java +++ b/src/main/java/com/hierynomus/ntlm/messages/NtlmNegotiate.java @@ -18,7 +18,6 @@ import com.hierynomus.protocol.commons.Charsets; import com.hierynomus.protocol.commons.buffer.Buffer; -import java.util.EnumSet; import java.util.Set; import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.*; @@ -27,21 +26,9 @@ * [MS-NLMP].pdf 2.2.1.1 NEGOTIATE_MESSAGE */ public class NtlmNegotiate extends NtlmMessage { - public static final Set DEFAULT_FLAGS = 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 NtlmNegotiate() { - super(DEFAULT_FLAGS, null); + public NtlmNegotiate(Set negotiateFlags) { + super(negotiateFlags, null); } public void write(Buffer.PlainBuffer buffer) { diff --git a/src/main/java/com/hierynomus/smbj/SmbConfig.java b/src/main/java/com/hierynomus/smbj/SmbConfig.java index 57c805a3..40e8f29f 100644 --- a/src/main/java/com/hierynomus/smbj/SmbConfig.java +++ b/src/main/java/com/hierynomus/smbj/SmbConfig.java @@ -15,13 +15,31 @@ */ package com.hierynomus.smbj; +import static com.hierynomus.mssmb2.SMB2Dialect.SMB_2_0_2; +import static com.hierynomus.mssmb2.SMB2Dialect.SMB_2_1; +import static com.hierynomus.mssmb2.SMB2Dialect.SMB_3_0; +import static com.hierynomus.mssmb2.SMB2Dialect.SMB_3_0_2; +import static com.hierynomus.mssmb2.SMB2Dialect.SMB_3_1_1; + +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import javax.net.SocketFactory; + import com.hierynomus.mssmb2.SMB2Dialect; import com.hierynomus.mssmb2.SMB2GlobalCapability; +import com.hierynomus.ntlm.NtlmConfig; import com.hierynomus.protocol.commons.Factory; import com.hierynomus.protocol.commons.socket.ProxySocketFactory; import com.hierynomus.security.SecurityProvider; import com.hierynomus.security.bc.BCSecurityProvider; -import com.hierynomus.security.jce.JceSecurityProvider; import com.hierynomus.smb.SMBPacket; import com.hierynomus.smb.SMBPacketData; import com.hierynomus.smbj.auth.Authenticator; @@ -30,13 +48,6 @@ import com.hierynomus.smbj.transport.TransportLayerFactory; import com.hierynomus.smbj.transport.tcp.direct.DirectTcpTransportFactory; -import javax.net.SocketFactory; -import java.security.SecureRandom; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import static com.hierynomus.mssmb2.SMB2Dialect.*; - public final class SmbConfig { private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024; @@ -78,7 +89,7 @@ public final class SmbConfig { private long transactTimeout; private GSSContextConfig clientGSSContextConfig; private boolean encryptData; - private String workStationName; + private NtlmConfig ntlmConfig; private int soTimeout; @@ -87,7 +98,7 @@ public static SmbConfig createDefaultConfig() { } public static Builder builder() { - return new Builder() + Builder b = new Builder() .withClientGuid(UUID.randomUUID()) .withRandomProvider(new SecureRandom()) .withSecurityProvider(getDefaultSecurityProvider()) @@ -104,6 +115,8 @@ public static Builder builder() { .withTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) .withClientGSSContextConfig(GSSContextConfig.createDefaultConfig()) .withEncryptData(false); + + return b; } private static SecurityProvider getDefaultSecurityProvider() { @@ -152,7 +165,7 @@ private SmbConfig(SmbConfig other) { useMultiProtocolNegotiate = other.useMultiProtocolNegotiate; clientGSSContextConfig = other.clientGSSContextConfig; encryptData = other.encryptData; - workStationName = other.workStationName; + ntlmConfig = other.ntlmConfig; } public Random getRandomProvider() { @@ -235,8 +248,17 @@ public boolean isEncryptData() { return encryptData; } + /** + * Get the work station name to be used in the NTLM authentication. + * + * @deprecated Moved into getNtlmConfig().getWorkStationName() + */ public String getWorkStationName() { - return workStationName; + return getNtlmConfig().getWorkstationName(); + } + + public NtlmConfig getNtlmConfig() { + return ntlmConfig; } public Set getClientCapabilities() { @@ -255,9 +277,11 @@ public Set getClientCapabilities() { public static class Builder { private SmbConfig config; + private NtlmConfig.Builder ntlmConfigBuilder; Builder() { config = new SmbConfig(); + ntlmConfigBuilder = NtlmConfig.builder(); } public Builder withRandomProvider(Random random) { @@ -424,6 +448,8 @@ public SmbConfig build() { throw new IllegalStateException("If encryption is enabled, at least one dialect should be SMB3.x compatible"); } + config.ntlmConfig = ntlmConfigBuilder.build(); + return new SmbConfig(config); } @@ -450,9 +476,19 @@ public Builder withEncryptData(boolean encryptData) { return this; } + /** + * Set the workstation name to be used in the NTLM authentication. + * + * @deprecated Moved into + * withNtlmConfig(NtlmConfig.builder().withWorkstationName(..).build()) + */ public Builder withWorkStationName(String workStationName) { - config.workStationName = workStationName; + ntlmConfigBuilder.withWorkstationName(workStationName); return this; } + + public NtlmConfig.Builder withNtlmConfig() { + return ntlmConfigBuilder; + } } } diff --git a/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java b/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java index a04cd0f3..f93aa5a8 100644 --- a/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java +++ b/src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java @@ -15,21 +15,34 @@ */ package com.hierynomus.smbj.auth; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_56; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_128; import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_ALWAYS_SIGN; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_ANONYMOUS; +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_OEM_DOMAIN_SUPPLIED; +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED; import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_SEAL; 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; import java.io.IOException; import java.math.BigInteger; import java.util.EnumSet; +import java.util.HashSet; import java.util.Random; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hierynomus.asn1.types.primitive.ASN1ObjectIdentifier; import com.hierynomus.msdtyp.MsDataTypes; +import com.hierynomus.ntlm.NtlmConfig; import com.hierynomus.ntlm.av.AvId; import com.hierynomus.ntlm.av.AvPairFlags; import com.hierynomus.ntlm.functions.ComputedNtlmV2Response; @@ -39,12 +52,7 @@ import com.hierynomus.ntlm.messages.NtlmChallenge; import com.hierynomus.ntlm.messages.NtlmNegotiate; import com.hierynomus.ntlm.messages.NtlmNegotiateFlag; -import com.hierynomus.ntlm.messages.WindowsVersion; -import com.hierynomus.ntlm.messages.WindowsVersion.NtlmRevisionCurrent; -import com.hierynomus.ntlm.messages.WindowsVersion.ProductMajorVersion; -import com.hierynomus.ntlm.messages.WindowsVersion.ProductMinorVersion; import com.hierynomus.protocol.commons.ByteArrayUtils; -import com.hierynomus.protocol.commons.EnumWithValue; import com.hierynomus.protocol.commons.buffer.Buffer; import com.hierynomus.protocol.commons.buffer.Endian; import com.hierynomus.security.SecurityProvider; @@ -64,11 +72,12 @@ enum State { NEGOTIATE, AUTHENTICATE, COMPLETE }; private static final ASN1ObjectIdentifier NTLMSSP = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.2.2.10"); private SecurityProvider securityProvider; private Random random; - private String workStationName; private NtlmV2Functions functions; + private NtlmConfig config; // Context buildup private State state; + private Set negotiateFlags; private NtlmNegotiate negotiateMessage; public static class Factory implements com.hierynomus.protocol.commons.Factory.Named { @@ -116,7 +125,20 @@ public AuthenticateResponse authenticate(final AuthenticationContext context, fi private AuthenticateResponse doNegotiate(AuthenticationContext context, byte[] gssToken) throws SpnegoException { AuthenticateResponse response = new AuthenticateResponse(); - this.negotiateMessage = new NtlmNegotiate(); + this.negotiateFlags = 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); + + this.negotiateMessage = new NtlmNegotiate(negotiateFlags); logger.trace("Sending NTLM negotiate message: {}", this.negotiateMessage); response.setNegToken(negTokenInit(this.negotiateMessage)); return response; @@ -157,8 +179,6 @@ private AuthenticateResponse doAuthenticate(AuthenticationContext context, NtlmC // If NTLM v2 is used, KeyExchangeKey MUST be set to the given 128-bit // SessionBaseKey value. - WindowsVersion version = new WindowsVersion(ProductMajorVersion.WINDOWS_MAJOR_VERSION_6, - ProductMinorVersion.WINDOWS_MINOR_VERSION_1, 7600, NtlmRevisionCurrent.NTLMSSP_REVISION_W2K3); // MIC (16 bytes) provided if in AvPairType is key MsvAvFlags with value & // 0x00000002 is true AvPairFlags pair = serverNtlmChallenge.getTargetInfo() != null @@ -167,7 +187,7 @@ private AuthenticateResponse doAuthenticate(AuthenticationContext context, NtlmC if (pair != null && (pair.getValue() & 0x00000002) > 0) { // MIC should be calculated NtlmAuthenticate resp = new NtlmAuthenticate(new byte[0], ntlmv2Response, - context.getUsername(), context.getDomain(), workStationName, sessionkey, negotiateFlags, version, + context.getUsername(), context.getDomain(), config.getWorkstationName(), sessionkey, negotiateFlags, config.getWindowsVersion(), true); // TODO correct hash should be tested @@ -184,7 +204,7 @@ private AuthenticateResponse doAuthenticate(AuthenticationContext context, NtlmC return response; } else { NtlmAuthenticate resp = new NtlmAuthenticate(new byte[0], ntlmv2Response, - context.getUsername(), context.getDomain(), workStationName, sessionkey, negotiateFlags, version, + context.getUsername(), context.getDomain(), config.getWorkstationName(), sessionkey, negotiateFlags, config.getWindowsVersion(), false); response.setNegToken(negTokenTarg(resp)); return response; @@ -216,8 +236,9 @@ private byte[] negTokenTarg(NtlmAuthenticate resp) throws SpnegoException { public void init(SmbConfig config) { this.securityProvider = config.getSecurityProvider(); this.random = config.getRandomProvider(); - this.workStationName = config.getWorkStationName(); + this.config = config.getNtlmConfig(); this.state = State.NEGOTIATE; + this.negotiateFlags = new HashSet<>(); this.functions = new NtlmV2Functions(random, securityProvider); } diff --git a/src/test/groovy/com/hierynomus/spnego/NegTokenInitSpec.groovy b/src/test/groovy/com/hierynomus/spnego/NegTokenInitSpec.groovy index 87ff8d0f..0ac0eabd 100644 --- a/src/test/groovy/com/hierynomus/spnego/NegTokenInitSpec.groovy +++ b/src/test/groovy/com/hierynomus/spnego/NegTokenInitSpec.groovy @@ -22,6 +22,21 @@ import com.hierynomus.protocol.commons.buffer.Buffer import com.hierynomus.protocol.commons.buffer.Endian import spock.lang.Specification +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_NEGOTIATE_VERSION +import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_REQUEST_TARGET +import static com.hierynomus.ntlm.messages.WindowsVersion.NtlmRevisionCurrent.NTLMSSP_REVISION_W2K3 +import static com.hierynomus.ntlm.messages.WindowsVersion.ProductMajorVersion.WINDOWS_MAJOR_VERSION_6 +import static com.hierynomus.ntlm.messages.WindowsVersion.ProductMinorVersion.WINDOWS_MINOR_VERSION_1 + class NegTokenInitSpec extends Specification { def "should correctly decode GSS-API negInitToken"() { @@ -41,9 +56,19 @@ class NegTokenInitSpec extends Specification { def initToken = new NegTokenInit() def ntlmBuffer = new Buffer.PlainBuffer(Endian.LE) def spnegoBuffer = new Buffer.PlainBuffer(Endian.LE) + def flags = 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_REQUEST_TARGET, + NTLMSSP_NEGOTIATE_UNICODE) when: - new NtlmNegotiate().write(ntlmBuffer) + new NtlmNegotiate(flags).write(ntlmBuffer) initToken.addSupportedMech(new ASN1ObjectIdentifier("1.3.6.1.4.1.311.2.2.10")) initToken.setMechToken(ntlmBuffer.compactData) initToken.write(spnegoBuffer)