From 80dfff1b1a30f29b5d15693f494332aa7ccb7544 Mon Sep 17 00:00:00 2001 From: Felix Schumacher Date: Mon, 26 Jan 2009 15:31:41 +0100 Subject: [PATCH] jcifs-1.3.3 from tgz Sun Jan 25 14:31:31 EST 2009 jcifs-1.3.3 If a jcifs.netbios.wins property is not supplied, the default jcifs.resolveOrder is now LMHOSTS,DNS,BCAST whereas previously it was LMHOSTS,BCAST,DNS. This is much more likely to eliminate annoying 6 second timeouts waiting for BCAST queries to timeout. An "Invalid parameter" error would occur if the RC4 Cipher was not available for NTLMv2. The logic has been adjusted so that the correct error is thrown. Note that RC4 is only available in Java 1.5 update 7 and later. Thus JCIFS 1.3 requires that version or NTLMv2 must be disabled. The NTLMSSP classes could try to load Cp850 which is not available in the standard JRE. This has been fixed. Added setLength method to NdrBuffer class. --- README.txt | 18 ++++++++++ build.xml | 4 +-- examples/runtests.sh | 16 +++++---- src/jcifs/UniAddress.java | 8 ++--- src/jcifs/dcerpc/DcerpcBind.java | 2 ++ src/jcifs/dcerpc/DcerpcException.java | 8 ++--- src/jcifs/dcerpc/DcerpcHandle.java | 40 +++++++++++++++++++++-- src/jcifs/dcerpc/DcerpcMessage.java | 9 ++--- src/jcifs/dcerpc/DcerpcSecurityProvider.java | 27 +++++++++++++++ src/jcifs/dcerpc/ndr/NdrBuffer.java | 3 ++ src/jcifs/ntlmssp/NtlmMessage.java | 2 +- src/jcifs/ntlmssp/Type3Message.java | 47 ++++++++++++++++----------- src/jcifs/smb/NtlmContext.java | 2 +- src/jcifs/smb/NtlmPasswordAuthentication.java | 2 ++ src/jcifs/smb/SmbComSessionSetupAndX.java | 5 ++- src/jcifs/smb/SmbSession.java | 19 +++++++++-- 16 files changed, 164 insertions(+), 48 deletions(-) create mode 100644 src/jcifs/dcerpc/DcerpcSecurityProvider.java diff --git a/README.txt b/README.txt index ea8c302..253ae60 100644 --- a/README.txt +++ b/README.txt @@ -1,3 +1,21 @@ +Sun Jan 25 14:31:31 EST 2009 +jcifs-1.3.3 + +If a jcifs.netbios.wins property is not supplied, the default +jcifs.resolveOrder is now LMHOSTS,DNS,BCAST whereas previously it was +LMHOSTS,BCAST,DNS. This is much more likely to eliminate annoying 6 +second timeouts waiting for BCAST queries to timeout. + +An "Invalid parameter" error would occur if the RC4 Cipher was not +available for NTLMv2. The logic has been adjusted so that the correct +error is thrown. Note that RC4 is only available in Java 1.5 update 7 and +later. Thus JCIFS 1.3 requires that version or NTLMv2 must be disabled. + +The NTLMSSP classes could try to load Cp850 which is not available in +the standard JRE. This has been fixed. + +Added setLength method to NdrBuffer class. + Mon Dec 22 13:30:39 EST 2008 jcifs-1.3.2 diff --git a/build.xml b/build.xml index af58471..2e1674d 100644 --- a/build.xml +++ b/build.xml @@ -1,7 +1,7 @@ - - + + diff --git a/examples/runtests.sh b/examples/runtests.sh index c408eed..65239a7 100644 --- a/examples/runtests.sh +++ b/examples/runtests.sh @@ -1,8 +1,9 @@ #!/bin/sh -JAVA_HOME=/usr/local/java6 +#JAVA_HOME=/usr/local/java6 +JAVA_HOME=/usr/local/java CLASSPATH=../build:. -PROPERTIES=../user1.prp +PROPERTIES=../../user2.prp RUN="${JAVA_HOME}/bin/java -cp ${CLASSPATH} -Djcifs.properties=${PROPERTIES}" #SERVER=dc1.w.net @@ -10,17 +11,18 @@ RUN="${JAVA_HOME}/bin/java -cp ${CLASSPATH} -Djcifs.properties=${PROPERTIES}" #DIR=test # Domain-based DFS -#SHARE=root2 -#DIR=link2/test +SERVER=w.net +SHARE=root2 +DIR=link2/test # smb://fs1.w.net/DFSStandaloneRoot/DFSStandaloneLink/test/ # smb://dc1.w.net/root2/link2/test/ # smb://dc1.w.net/tmp/test/ # smb://dc3.x.net/tmp/test/ # Stand-alone DFS -SERVER=dc3.x.net -SHARE=tmp -DIR=test +#SERVER=dc3.x.net +#SHARE=tmp +#DIR=test WRITE_DIR=${DIR}/ SRC_DIR=${DIR}/Junk diff --git a/src/jcifs/UniAddress.java b/src/jcifs/UniAddress.java index a6f021b..d4638b3 100644 --- a/src/jcifs/UniAddress.java +++ b/src/jcifs/UniAddress.java @@ -78,14 +78,14 @@ public class UniAddress { if( nbns == null ) { resolveOrder = new int[3]; resolveOrder[0] = RESOLVER_LMHOSTS; - resolveOrder[1] = RESOLVER_BCAST; - resolveOrder[2] = RESOLVER_DNS; + resolveOrder[1] = RESOLVER_DNS; + resolveOrder[2] = RESOLVER_BCAST; } else { resolveOrder = new int[4]; resolveOrder[0] = RESOLVER_LMHOSTS; resolveOrder[1] = RESOLVER_WINS; - resolveOrder[2] = RESOLVER_BCAST; - resolveOrder[3] = RESOLVER_DNS; + resolveOrder[2] = RESOLVER_DNS; + resolveOrder[3] = RESOLVER_BCAST; } } else { int[] tmp = new int[4]; diff --git a/src/jcifs/dcerpc/DcerpcBind.java b/src/jcifs/dcerpc/DcerpcBind.java index 5e792a9..fd58ddc 100644 --- a/src/jcifs/dcerpc/DcerpcBind.java +++ b/src/jcifs/dcerpc/DcerpcBind.java @@ -44,6 +44,8 @@ public class DcerpcBind extends DcerpcMessage { DcerpcBinding binding; int max_xmit, max_recv; + public DcerpcBind() { + } DcerpcBind(DcerpcBinding binding, DcerpcHandle handle) { this.binding = binding; max_xmit = handle.max_xmit; diff --git a/src/jcifs/dcerpc/DcerpcException.java b/src/jcifs/dcerpc/DcerpcException.java index d8a2b7a..3bfcd7c 100644 --- a/src/jcifs/dcerpc/DcerpcException.java +++ b/src/jcifs/dcerpc/DcerpcException.java @@ -48,14 +48,14 @@ public class DcerpcException extends IOException implements DcerpcError, WinErro private int error; private Throwable rootCause; - DcerpcException(String msg) { - super(msg); - } DcerpcException(int error) { super(getMessageByDcerpcError(error)); this.error = error; } - DcerpcException(String msg, Throwable rootCause) { + public DcerpcException(String msg) { + super(msg); + } + public DcerpcException(String msg, Throwable rootCause) { super(msg); this.rootCause = rootCause; } diff --git a/src/jcifs/dcerpc/DcerpcHandle.java b/src/jcifs/dcerpc/DcerpcHandle.java index e8ac0c5..72bd37b 100644 --- a/src/jcifs/dcerpc/DcerpcHandle.java +++ b/src/jcifs/dcerpc/DcerpcHandle.java @@ -106,6 +106,7 @@ public abstract class DcerpcHandle implements DcerpcConstants { protected int max_xmit = 4280; protected int max_recv = max_xmit; protected int state = 0; + protected DcerpcSecurityProvider securityProvider = null; private static int call_id = 1; public static DcerpcHandle getHandle(String url, @@ -117,6 +118,16 @@ public abstract class DcerpcHandle implements DcerpcConstants { throw new DcerpcException("DCERPC transport not supported: " + url); } + public void bind() throws DcerpcException, IOException { + try { + state = 1; + DcerpcMessage bind = new DcerpcBind(binding, this); + sendrecv(bind); + } catch (IOException ioe) { + state = 0; + throw ioe; + } + } public void sendrecv(DcerpcMessage msg) throws DcerpcException, IOException { byte[] stub, frag; NdrBuffer buf, fbuf; @@ -124,9 +135,7 @@ public abstract class DcerpcHandle implements DcerpcConstants { DcerpcException de; if (state == 0) { - state = 1; - DcerpcMessage bind = new DcerpcBind(binding, this); - sendrecv(bind); + bind(); } isDirect = msg instanceof DcerpcBind; @@ -146,8 +155,14 @@ public abstract class DcerpcHandle implements DcerpcConstants { msg.encode(buf); + if (securityProvider != null) { + buf.setIndex(0); + securityProvider.wrap(buf); + } + tot = buf.getLength(); off = 0; + while (off < tot) { msg.call_id = call_id++; @@ -171,6 +186,14 @@ public abstract class DcerpcHandle implements DcerpcConstants { doReceiveFragment(stub, isDirect); buf.reset(); + buf.setIndex(8); + buf.setLength(buf.dec_ndr_short()); + + if (securityProvider != null) + securityProvider.unwrap(buf); + + buf.setIndex(0); + msg.decode_header(buf); off = 24; @@ -189,6 +212,13 @@ public abstract class DcerpcHandle implements DcerpcConstants { doReceiveFragment(frag, isDirect); fbuf.reset(); + fbuf.setIndex(8); + fbuf.setLength(fbuf.dec_ndr_short()); + + if (securityProvider != null) + securityProvider.unwrap(fbuf); + + fbuf.reset(); msg.decode_header(fbuf); stub_frag_len = msg.length - 24; @@ -213,6 +243,10 @@ public abstract class DcerpcHandle implements DcerpcConstants { throw de; } + public void setDcerpcSecurityProvider(DcerpcSecurityProvider securityProvider) + { + this.securityProvider = securityProvider; + } public String getServer() { if (this instanceof DcerpcPipeHandle) return ((DcerpcPipeHandle)this).pipe.getServer(); diff --git a/src/jcifs/dcerpc/DcerpcMessage.java b/src/jcifs/dcerpc/DcerpcMessage.java index e91a5e1..6edacd2 100644 --- a/src/jcifs/dcerpc/DcerpcMessage.java +++ b/src/jcifs/dcerpc/DcerpcMessage.java @@ -57,8 +57,9 @@ public abstract class DcerpcMessage extends NdrObject implements DcerpcConstants buf.enc_ndr_long(call_id); } void decode_header(NdrBuffer buf) throws NdrException { - buf.dec_ndr_small(); /* RPC version */ - buf.dec_ndr_small(); /* minor version */ + /* RPC major / minor version */ + if (buf.dec_ndr_small() != 5 || buf.dec_ndr_small() != 0) + throw new NdrException("DCERPC version not supported"); ptype = buf.dec_ndr_small(); flags = buf.dec_ndr_small(); if (buf.dec_ndr_long() != 0x00000010) /* Little-endian / ASCII / IEEE */ @@ -96,7 +97,7 @@ public abstract class DcerpcMessage extends NdrObject implements DcerpcConstants public void decode(NdrBuffer buf) throws NdrException { decode_header(buf); - if (ptype != 12 && ptype != 2 && ptype != 3) + if (ptype != 12 && ptype != 2 && ptype != 3 && ptype != 13) throw new NdrException("Unexpected ptype: " + ptype); if (ptype == 2 || ptype == 3) { /* Response or Fault */ @@ -104,7 +105,7 @@ public abstract class DcerpcMessage extends NdrObject implements DcerpcConstants buf.dec_ndr_short(); /* context id */ buf.dec_ndr_short(); /* cancel count */ } - if (ptype == 3) { /* Fault */ + if (ptype == 3 || ptype == 13) { /* Fault */ result = buf.dec_ndr_long(); } else { /* Bind_ack or Response */ decode_out(buf); diff --git a/src/jcifs/dcerpc/DcerpcSecurityProvider.java b/src/jcifs/dcerpc/DcerpcSecurityProvider.java new file mode 100644 index 0000000..1776683 --- /dev/null +++ b/src/jcifs/dcerpc/DcerpcSecurityProvider.java @@ -0,0 +1,27 @@ +/* jcifs msrpc client library in Java + * Copyright (C) 2009 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.dcerpc; + +import jcifs.dcerpc.ndr.NdrBuffer; + +public interface DcerpcSecurityProvider +{ + void wrap(NdrBuffer outgoing) throws DcerpcException; + void unwrap(NdrBuffer incoming) throws DcerpcException; +} diff --git a/src/jcifs/dcerpc/ndr/NdrBuffer.java b/src/jcifs/dcerpc/ndr/NdrBuffer.java index b08e1f4..5c96131 100644 --- a/src/jcifs/dcerpc/ndr/NdrBuffer.java +++ b/src/jcifs/dcerpc/ndr/NdrBuffer.java @@ -96,6 +96,9 @@ public class NdrBuffer { public int getLength() { return deferred.length; } + public void setLength(int length) { + deferred.length = length; + } public void advance(int n) { index += n; if ((index - start) > deferred.length) { diff --git a/src/jcifs/ntlmssp/NtlmMessage.java b/src/jcifs/ntlmssp/NtlmMessage.java index 17dc55d..4c68eed 100644 --- a/src/jcifs/ntlmssp/NtlmMessage.java +++ b/src/jcifs/ntlmssp/NtlmMessage.java @@ -34,7 +34,7 @@ public abstract class NtlmMessage implements NtlmFlags { (byte) 'S', (byte) 'S', (byte) 'P', (byte) 0 }; - private static final String OEM_ENCODING = Config.getProperty("jcifs.encoding", "Cp850"); + private static final String OEM_ENCODING = Config.DEFAULT_OEM_ENCODING; private int flags; diff --git a/src/jcifs/ntlmssp/Type3Message.java b/src/jcifs/ntlmssp/Type3Message.java index b548e9a..4a454ba 100644 --- a/src/jcifs/ntlmssp/Type3Message.java +++ b/src/jcifs/ntlmssp/Type3Message.java @@ -172,6 +172,8 @@ public class Type3Message extends NtlmMessage { RANDOM.nextBytes(clientChallenge); java.util.Arrays.fill(clientChallenge, 8, 24, (byte)0x00); +// NTLMv1 w/ NTLM2 session sec and key exch all been verified with a debug build of smbclient + byte[] responseKeyNT = NtlmPasswordAuthentication.nTOWFv1(password); byte[] ntlm2Response = NtlmPasswordAuthentication.getNTLM2Response(responseKeyNT, type2.getChallenge(), @@ -187,9 +189,11 @@ public class Type3Message extends NtlmMessage { MD4 md4 = new MD4(); md4.update(responseKeyNT); - HMACT64 hmac = new HMACT64(md4.digest()); + byte[] userSessionKey = md4.digest(); + + HMACT64 hmac = new HMACT64(userSessionKey); hmac.update(sessionNonce); - byte[] userSessionKey = hmac.digest(); // NTLM2 session key + byte[] ntlm2SessionKey = hmac.digest(); if ((getFlags() & NTLMSSP_NEGOTIATE_KEY_EXCH) != 0) { masterKey = new byte[16]; @@ -198,7 +202,7 @@ public class Type3Message extends NtlmMessage { byte[] exchangedKey = new byte[16]; try { Cipher rc4 = Cipher.getInstance("RC4"); - rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(userSessionKey, "RC4")); + rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(ntlm2SessionKey, "RC4")); rc4.update(masterKey, 0, 16, exchangedKey, 0); } catch (GeneralSecurityException gse) { throw new RuntimeException("", gse); @@ -206,7 +210,7 @@ public class Type3Message extends NtlmMessage { setSessionKey(exchangedKey); } else { - masterKey = userSessionKey; + masterKey = ntlm2SessionKey; setSessionKey(masterKey); } } @@ -231,25 +235,28 @@ public class Type3Message extends NtlmMessage { setNTResponse(getNTLMv2Response(type2, responseKeyNT, clientChallenge2)); if ((getFlags() & NTLMSSP_NEGOTIATE_SIGN) == NTLMSSP_NEGOTIATE_SIGN) { - masterKey = new byte[16]; - RANDOM.nextBytes(masterKey); - HMACT64 hmac = new HMACT64(responseKeyNT); hmac.update(ntResponse, 0, 16); // only first 16 bytes of ntResponse byte[] userSessionKey = hmac.digest(); - /* TODO: don't do this if NTLMSSP_NEGOTIATE_KEY_EXCH not set - */ - byte[] exchangedKey = new byte[16]; - try { - Cipher rc4 = Cipher.getInstance("RC4"); - rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(userSessionKey, "RC4")); - rc4.update(masterKey, 0, 16, exchangedKey, 0); - } catch (GeneralSecurityException gse) { - throw new RuntimeException("", gse); - } + if ((getFlags() & NTLMSSP_NEGOTIATE_KEY_EXCH) != 0) { + masterKey = new byte[16]; + RANDOM.nextBytes(masterKey); + + byte[] exchangedKey = new byte[16]; + try { + Cipher rc4 = Cipher.getInstance("RC4"); + rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(userSessionKey, "RC4")); + rc4.update(masterKey, 0, 16, exchangedKey, 0); + } catch (GeneralSecurityException gse) { + throw new RuntimeException("", gse); + } - setSessionKey(exchangedKey); + setSessionKey(exchangedKey); + } else { + masterKey = userSessionKey; + setSessionKey(masterKey); + } } break; @@ -662,17 +669,19 @@ public class Type3Message extends NtlmMessage { int workstationOffset = readULong(material, 48); int flags; String charset; + byte[] _sessionKey = null; if (lmResponseOffset == 52 || ntResponseOffset == 52 || domainOffset == 52 || userOffset == 52 || workstationOffset == 52) { flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_OEM; charset = getOEMEncoding(); } else { - setSessionKey(readSecurityBuffer(material, 52)); + _sessionKey = readSecurityBuffer(material, 52); flags = readULong(material, 60); charset = ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? "UnicodeLittleUnmarked" : getOEMEncoding(); } + setSessionKey(_sessionKey); setFlags(flags); setLMResponse(lmResponse); setNTResponse(ntResponse); diff --git a/src/jcifs/smb/NtlmContext.java b/src/jcifs/smb/NtlmContext.java index 04d761a..099753a 100644 --- a/src/jcifs/smb/NtlmContext.java +++ b/src/jcifs/smb/NtlmContext.java @@ -87,7 +87,7 @@ public class NtlmContext { state++; break; } catch (Exception e) { - throw new SmbException(e.getMessage()); + throw new SmbException(e.getMessage(), e); } default: throw new SmbException("Invalid state"); diff --git a/src/jcifs/smb/NtlmPasswordAuthentication.java b/src/jcifs/smb/NtlmPasswordAuthentication.java index 845407e..7556907 100644 --- a/src/jcifs/smb/NtlmPasswordAuthentication.java +++ b/src/jcifs/smb/NtlmPasswordAuthentication.java @@ -74,6 +74,8 @@ public final class NtlmPasswordAuthentication implements Principal, Serializable static String DEFAULT_PASSWORD; static final String BLANK = ""; + public static final NtlmPasswordAuthentication ANONYMOUS = new NtlmPasswordAuthentication("", "", ""); + static void initDefaults() { if (DEFAULT_DOMAIN != null) return; DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", "?"); diff --git a/src/jcifs/smb/SmbComSessionSetupAndX.java b/src/jcifs/smb/SmbComSessionSetupAndX.java index 04d3fdf..6547275 100644 --- a/src/jcifs/smb/SmbComSessionSetupAndX.java +++ b/src/jcifs/smb/SmbComSessionSetupAndX.java @@ -47,7 +47,10 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock { if (cred instanceof NtlmPasswordAuthentication) { NtlmPasswordAuthentication auth = (NtlmPasswordAuthentication)cred; - if (session.transport.server.encryptedPasswords) { + if (auth == NtlmPasswordAuthentication.ANONYMOUS) { + lmHash = new byte[0]; + ntHash = new byte[0]; + } else if (session.transport.server.encryptedPasswords) { lmHash = auth.getAnsiHash( session.transport.server.encryptionKey ); ntHash = auth.getUnicodeHash( session.transport.server.encryptionKey ); // prohibit HTTP auth attempts for the null session diff --git a/src/jcifs/smb/SmbSession.java b/src/jcifs/smb/SmbSession.java index fc0944e..5391d17 100644 --- a/src/jcifs/smb/SmbSession.java +++ b/src/jcifs/smb/SmbSession.java @@ -22,6 +22,7 @@ import java.util.Vector; import java.util.Enumeration; import java.net.InetAddress; import java.net.UnknownHostException; +import java.io.IOException; import jcifs.Config; import jcifs.UniAddress; import jcifs.netbios.NbtAddress; @@ -274,7 +275,10 @@ synchronized( transport() ) { do { switch (state) { case 10: /* NTLM */ - if ((transport.capabilities & SmbConstants.CAP_EXTENDED_SECURITY) == SmbConstants.CAP_EXTENDED_SECURITY) { + if (auth == NtlmPasswordAuthentication.ANONYMOUS) + transport.capabilities &= ~SmbConstants.CAP_EXTENDED_SECURITY; + + if (transport.hasCapability(SmbConstants.CAP_EXTENDED_SECURITY)) { state = 20; /* NTLMSSP */ break; } @@ -338,7 +342,18 @@ synchronized( transport() ) { break; } - token = nctx.initSecContext(token, 0, token.length); + try { + token = nctx.initSecContext(token, 0, token.length); + } catch (SmbException se) { + /* We must close the transport or the server will be expecting a + * Type3Message. Otherwise, when we send a Type1Message it will return + * "Invalid parameter". + */ + try { transport.disconnect(true); } catch (IOException ioe) {} + uid = 0; + throw se; + } + if (token != null) { request = new SmbComSessionSetupAndX(this, null, token); response = new SmbComSessionSetupAndXResponse(null); -- 2.11.0