+Sat Oct 25 17:45:51 EDT 2008
+jcifs-1.3.0 released
+
+NTLMv2 has been FULLY implemented and is now the default. Signatures
+without and without various NTLMSSP flags (e.g. NTLMSSP_NEGOTIATE_NTLM2)
+have been tested with Windows 2003 and Windows 2000.
+
+New default values are:
+
+ jcifs.lmCompatibility = 3
+ jcifs.smb.client.useExtendedSecurity = true
+
+Note: The NTLM HTTP Filter does not and can never support NTLMv2 as it
+uses the main-in-the-middle technique which is specifically thwarted by
+factoring the NTLMSSP TargetInformation block into the computed hashes. A
+proper NTLMv2 HTTP authentication filter would require NETLOGON RPCs (or
+possibly some kind of Kerberos digest authentication like Heimdal uses).
+
Sun Oct 19 23:25:45 EDT 2008
jcifs-1.2.25
<project name="jcifs" default="usage" basedir=".">
- <property name="version" value="1.2.25"/>
- <property name="reldate" value="Oct 20, 2008"/>
+ <property name="version" value="1.3.0"/>
+ <property name="reldate" value="Oct 26, 2008"/>
<target name="usage">
<echo>
#!/bin/sh
-JAVA_HOME=/usr/local/java
+JAVA_HOME=/usr/local/java6
CLASSPATH=../build:.
-PROPERTIES=../../user2.prp
+PROPERTIES=../user1.prp
RUN="${JAVA_HOME}/bin/java -cp ${CLASSPATH} -Djcifs.properties=${PROPERTIES}"
#SERVER=dc1.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=fs1.w.net
-SHARE=standalone
-DIR=sub1/dc1tmp/test
+SERVER=dc3.x.net
+SHARE=tmp
+DIR=test
WRITE_DIR=${DIR}/
SRC_DIR=${DIR}/Junk
String name;
int level;
- /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min.
+ /* Set jcifs properties we know we want; soTimeout and cachePolicy to 30min.
*/
- Config.setProperty( "jcifs.smb.client.soTimeout", "300000" );
+ Config.setProperty( "jcifs.smb.client.soTimeout", "1800000" );
Config.setProperty( "jcifs.netbios.cachePolicy", "1200" );
+ /* The Filter can only work with NTLMv1 as it uses a man-in-the-middle
+ * techinque that NTLMv2 specifically thwarts. A real NTLM Filter would
+ * need to do a NETLOGON RPC or possibly some kind of Kerberos based digest
+ * authentication like Hiemdal supposedly has.
+ */
+ Config.setProperty( "jcifs.smb.lmCompatibility", "0" );
Enumeration e = filterConfig.getInitParameterNames();
while( e.hasMoreElements() ) {
}
Type2Message type2 = (Type2Message) message;
message = new Type3Message(type2, password, domain, user,
- Type3Message.getDefaultWorkstation());
+ Type3Message.getDefaultWorkstation(), 0);
}
return message;
}
if( RO == null || RO.length() == 0 ) {
/* No resolveOrder has been specified, use the
- * default which is LMHOSTS,WINS,BCAST,DNS or just
+ * default which is LMHOSTS,DNS,WINS,BCAST
* LMHOSTS,BCAST,DNS if jcifs.netbios.wins has not
* been specified.
*/
+++ /dev/null
-/* jcifs smb client library in Java
- * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
- *
- * 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.netbios;
-
-import java.net.*;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.util.HashMap;
-import java.util.StringTokenizer;
-import jcifs.Config;
-import jcifs.util.Hexdump;
-import jcifs.util.LogStream;
-
-class NameServiceClient implements Runnable {
-
- static final int DEFAULT_SO_TIMEOUT = 5000;
- static final int DEFAULT_RCV_BUF_SIZE = 576;
- static final int DEFAULT_SND_BUF_SIZE = 576;
- static final int NAME_SERVICE_UDP_PORT = 137;
- static final int DEFAULT_RETRY_COUNT = 2;
- static final int DEFAULT_RETRY_TIMEOUT = 3000;
-
- static final int RESOLVER_LMHOSTS = 1;
- static final int RESOLVER_BCAST = 2;
- static final int RESOLVER_WINS = 3;
-
- private static final int SND_BUF_SIZE = Config.getInt( "jcifs.netbios.snd_buf_size", DEFAULT_SND_BUF_SIZE );
- private static final int RCV_BUF_SIZE = Config.getInt( "jcifs.netbios.rcv_buf_size", DEFAULT_RCV_BUF_SIZE );
- private static final int SO_TIMEOUT = Config.getInt( "jcifs.netbios.soTimeout", DEFAULT_SO_TIMEOUT );
- private static final int RETRY_COUNT = Config.getInt( "jcifs.netbios.retryCount", DEFAULT_RETRY_COUNT );
- private static final int RETRY_TIMEOUT = Config.getInt( "jcifs.netbios.retryTimeout", DEFAULT_RETRY_TIMEOUT);
- private static final int LPORT = Config.getInt( "jcifs.netbios.lport", 0 );
- private static final InetAddress LADDR = Config.getInetAddress( "jcifs.netbios.laddr", null );
- private static final String RO = Config.getProperty( "jcifs.resolveOrder" );
-
- private static LogStream log = LogStream.getInstance();
-
- private final Object LOCK = new Object();
-
- private int lport, closeTimeout;
- private byte[] snd_buf, rcv_buf;
- private DatagramSocket socket;
- private DatagramPacket in, out;
- private HashMap responseTable = new HashMap();
- private Thread thread;
- private int nextNameTrnId = 0;
- private int[] resolveOrder;
-
- InetAddress laddr, baddr;
-
- NameServiceClient() {
- this( LPORT, LADDR );
- }
- NameServiceClient( int lport, InetAddress laddr ) {
- this.lport = lport;
- this.laddr = laddr;
-
- try {
- baddr = Config.getInetAddress( "jcifs.netbios.baddr",
- InetAddress.getByName( "255.255.255.255" ));
- } catch( UnknownHostException uhe ) {
- }
-
- snd_buf = new byte[SND_BUF_SIZE];
- rcv_buf = new byte[RCV_BUF_SIZE];
- out = new DatagramPacket( snd_buf, SND_BUF_SIZE, baddr, NAME_SERVICE_UDP_PORT );
- in = new DatagramPacket( rcv_buf, RCV_BUF_SIZE );
-
- if( RO == null || RO.length() == 0 ) {
-
- /* No resolveOrder has been specified, use the
- * default which is LMHOSTS,WINS,BCAST,DNS or just
- * LMHOSTS,BCAST,DNS if jcifs.netbios.wins has not
- * been specified.
- */
-
- if( NbtAddress.getWINSAddress() == null ) {
- resolveOrder = new int[2];
- resolveOrder[0] = RESOLVER_LMHOSTS;
- resolveOrder[1] = RESOLVER_BCAST;
- } else {
- resolveOrder = new int[3];
- resolveOrder[0] = RESOLVER_LMHOSTS;
- resolveOrder[1] = RESOLVER_WINS;
- resolveOrder[2] = RESOLVER_BCAST;
- }
- } else {
- int[] tmp = new int[3];
- StringTokenizer st = new StringTokenizer( RO, "," );
- int i = 0;
- while( st.hasMoreTokens() ) {
- String s = st.nextToken().trim();
- if( s.equalsIgnoreCase( "LMHOSTS" )) {
- tmp[i++] = RESOLVER_LMHOSTS;
- } else if( s.equalsIgnoreCase( "WINS" )) {
- if( NbtAddress.getWINSAddress() == null ) {
- if( log.level > 1 ) {
- log.println( "NetBIOS resolveOrder specifies WINS however the " +
- "jcifs.netbios.wins property has not been set" );
- }
- continue;
- }
- tmp[i++] = RESOLVER_WINS;
- } else if( s.equalsIgnoreCase( "BCAST" )) {
- tmp[i++] = RESOLVER_BCAST;
- } else if( s.equalsIgnoreCase( "DNS" )) {
- ; // skip
- } else if( log.level > 1 ) {
- log.println( "unknown resolver method: " + s );
- }
- }
- resolveOrder = new int[i];
- System.arraycopy( tmp, 0, resolveOrder, 0, i );
- }
- }
-
- int getNextNameTrnId() {
- if(( ++nextNameTrnId & 0xFFFF ) == 0 ) {
- nextNameTrnId = 1;
- }
- return nextNameTrnId;
- }
- void ensureOpen( int timeout ) throws IOException {
- closeTimeout = 0;
- if( SO_TIMEOUT != 0 ) {
- closeTimeout = Math.max( SO_TIMEOUT, timeout );
- }
- // If socket is still good, the new closeTimeout will
- // be ignored; see tryClose comment.
- if( socket == null ) {
- socket = new DatagramSocket( lport, laddr );
- thread = new Thread( this, "JCIFS-NameServiceClient" );
- thread.setDaemon( true );
- thread.start();
- }
- }
- void tryClose() {
- synchronized( LOCK ) {
-
- /* Yes, there is the potential to drop packets
- * because we might close the socket during a
- * request. However the chances are slim and the
- * retry code should ensure the overall request
- * is serviced. The alternative complicates things
- * more than I think is worth it.
- */
-
- if( socket != null ) {
- socket.close();
- socket = null;
- }
- thread = null;
- responseTable.clear();
- }
- }
- public void run() {
- int nameTrnId;
- NameServicePacket response;
-
- try {
- while( thread == Thread.currentThread() ) {
- in.setLength( RCV_BUF_SIZE );
-
- socket.setSoTimeout( closeTimeout );
- socket.receive( in );
-
- if( log.level > 3 )
- log.println( "NetBIOS: new data read from socket" );
-
- nameTrnId = NameServicePacket.readNameTrnId( rcv_buf, 0 );
- response = (NameServicePacket)responseTable.get( new Integer( nameTrnId ));
- if( response == null || response.received ) {
- continue;
- }
- synchronized( response ) {
- response.readWireFormat( rcv_buf, 0 );
- response.received = true;
-
- if( log.level > 3 ) {
- log.println( response );
- Hexdump.hexdump( log, rcv_buf, 0, in.getLength() );
- }
-
- response.notify();
- }
- }
- } catch(SocketTimeoutException ste) {
- } catch( Exception ex ) {
- if( log.level > 2 )
- ex.printStackTrace( log );
- } finally {
- tryClose();
- }
- }
- void send( NameServicePacket request, NameServicePacket response,
- int timeout ) throws IOException {
- Integer nid = null;
- int max = NbtAddress.NBNS.length;
-
- if (max == 0)
- max = 1; /* No WINs, try only bcast addr */
-
- synchronized( response ) {
- while (max-- > 0) {
- try {
- synchronized( LOCK ) {
- request.nameTrnId = getNextNameTrnId();
- nid = new Integer( request.nameTrnId );
-
- out.setAddress( request.addr );
- out.setLength( request.writeWireFormat( snd_buf, 0 ));
- response.received = false;
-
- responseTable.put( nid, response );
- ensureOpen( timeout + 1000 );
- socket.send( out );
-
- if( log.level > 3 ) {
- log.println( request );
- Hexdump.hexdump( log, snd_buf, 0, out.getLength() );
- }
- }
-
- response.wait( timeout );
-
- if (response.received)
- return;
-
- } catch( InterruptedException ie ) {
- } finally {
- responseTable.remove( nid );
- }
-
- if (NbtAddress.isWINS( request.addr ) == false)
- break;
-
- /* Message was sent to WINS but
- * failed to receive response.
- * Try a different WINS server.
- */
- if (request.addr == NbtAddress.getWINSAddress())
- NbtAddress.switchWINS();
- request.addr = NbtAddress.getWINSAddress();
- }
- }
- }
-
- NbtAddress[] getAllByName( Name name, InetAddress addr )
- throws UnknownHostException {
- int n;
- NameQueryRequest request = new NameQueryRequest( name );
- NameQueryResponse response = new NameQueryResponse();
-
- request.addr = addr != null ? addr : NbtAddress.getWINSAddress();
- request.isBroadcast = request.addr == null;
-
- if( request.isBroadcast ) {
- request.addr = baddr;
- n = RETRY_COUNT;
- } else {
- request.isBroadcast = false;
- n = 1;
- }
-
- do {
- try {
- send( request, response, RETRY_TIMEOUT );
- } catch( IOException ioe ) {
- if( log.level > 1 )
- ioe.printStackTrace( log );
- throw new UnknownHostException( name.name );
- }
-
- if( response.received && response.resultCode == 0 ) {
- return response.addrEntry;
- }
- } while( --n > 0 && request.isBroadcast );
-
- throw new UnknownHostException( name.name );
- }
- NbtAddress getByName( Name name, InetAddress addr )
- throws UnknownHostException {
- int n;
- NameQueryRequest request = new NameQueryRequest( name );
- NameQueryResponse response = new NameQueryResponse();
-
- if( addr != null ) { /* UniAddress calls always use this
- * because it specifies addr
- */
- request.addr = addr; /* if addr ends with 255 flag it bcast */
- request.isBroadcast = (addr.getAddress()[3] == (byte)0xFF);
-
- n = RETRY_COUNT;
- do {
- try {
- send( request, response, RETRY_TIMEOUT );
- } catch( IOException ioe ) {
- if( log.level > 1 )
- ioe.printStackTrace( log );
- throw new UnknownHostException( name.name );
- }
-
- if( response.received && response.resultCode == 0 ) {
- int last = response.addrEntry.length - 1;
- response.addrEntry[last].hostName.srcHashCode = addr.hashCode();
- return response.addrEntry[last];
- }
- } while( --n > 0 && request.isBroadcast );
-
- throw new UnknownHostException( name.name );
- }
-
- /* If a target address to query was not specified explicitly
- * with the addr parameter we fall into this resolveOrder routine.
- */
-
- for( int i = 0; i < resolveOrder.length; i++ ) {
- try {
- switch( resolveOrder[i] ) {
- case RESOLVER_LMHOSTS:
- NbtAddress ans = Lmhosts.getByName( name );
- if( ans != null ) {
- ans.hostName.srcHashCode = 0; // just has to be different
- // from other methods
- return ans;
- }
- break;
- case RESOLVER_WINS:
- case RESOLVER_BCAST:
- if( resolveOrder[i] == RESOLVER_WINS &&
- name.name != NbtAddress.MASTER_BROWSER_NAME &&
- name.hexCode != 0x1d ) {
- request.addr = NbtAddress.getWINSAddress();
- request.isBroadcast = false;
- } else {
- request.addr = baddr;
- request.isBroadcast = true;
- }
-
- n = RETRY_COUNT;
- while( n-- > 0 ) {
- try {
- send( request, response, RETRY_TIMEOUT );
- } catch( IOException ioe ) {
- if( log.level > 1 )
- ioe.printStackTrace( log );
- throw new UnknownHostException( name.name );
- }
- if( response.received && response.resultCode == 0 ) {
-
-/* Before we return, in anticipation of this address being cached we must
- * augment the addresses name's hashCode to distinguish those resolved by
- * Lmhosts, WINS, or BCAST. Otherwise a failed query from say WINS would
- * get pulled out of the cache for a BCAST on the same name.
- */
- response.addrEntry[0].hostName.srcHashCode =
- request.addr.hashCode();
- return response.addrEntry[0];
- } else if( resolveOrder[i] == RESOLVER_WINS ) {
- /* If WINS reports negative, no point in retry
- */
- break;
- }
- }
- break;
- }
- } catch( IOException ioe ) {
- }
- }
- throw new UnknownHostException( name.name );
- }
- NbtAddress[] getNodeStatus( NbtAddress addr ) throws UnknownHostException {
- int n, srcHashCode;
- NodeStatusRequest request;
- NodeStatusResponse response;
-
- response = new NodeStatusResponse( addr );
- request = new NodeStatusRequest(
- new Name( NbtAddress.ANY_HOSTS_NAME, 0x00, null));
- request.addr = addr.getInetAddress();
-
- n = RETRY_COUNT;
- while( n-- > 0 ) {
- try {
- send( request, response, RETRY_TIMEOUT );
- } catch( IOException ioe ) {
- if( log.level > 1 )
- ioe.printStackTrace( log );
- throw new UnknownHostException( addr.toString() );
- }
- if( response.received && response.resultCode == 0 ) {
-
- /* For name queries resolved by different sources (e.g. WINS,
- * BCAST, Node Status) we need to augment the hashcode generated
- * for the addresses hostname or failed lookups for one type will
- * be cached and cause other types to fail even though they may
- * not be the authority for the name. For example, if a WINS lookup
- * for FOO fails and caches unknownAddress for FOO, a subsequent
- * lookup for FOO using BCAST should not fail because of that
- * name cached from WINS.
- *
- * So, here we apply the source addresses hashCode to each name to
- * make them specific to who resolved the name.
- */
-
- srcHashCode = request.addr.hashCode();
- for( int i = 0; i < response.addressArray.length; i++ ) {
- response.addressArray[i].hostName.srcHashCode = srcHashCode;
- }
- return response.addressArray;
- }
- }
- throw new UnknownHostException( addr.hostName.name );
- }
-}
*/
public Type1Message(int flags, String suppliedDomain,
String suppliedWorkstation) {
- setFlags(flags);
+ setFlags(getDefaultFlags() | flags);
setSuppliedDomain(suppliedDomain);
+ if (suppliedWorkstation == null)
+ suppliedWorkstation = getDefaultWorkstation();
setSuppliedWorkstation(suppliedWorkstation);
}
import jcifs.netbios.NbtAddress;
import jcifs.smb.NtlmPasswordAuthentication;
+import jcifs.util.HMACT64;
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.GeneralSecurityException;
+import jcifs.util.MD4;
/**
* Represents an NTLMSSP Type-3 message.
private String workstation;
- private byte[] sessionKey;
+ private byte[] masterKey = null;
+ private byte[] sessionKey = null;
static {
DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |
defaultWorkstation = NbtAddress.getLocalHost().getHostName();
} catch (UnknownHostException ex) { }
DEFAULT_WORKSTATION = defaultWorkstation;
- LM_COMPATIBILITY = Config.getInt("jcifs.smb.lmCompatibility", 0);
+ LM_COMPATIBILITY = Config.getInt("jcifs.smb.lmCompatibility", 3);
}
/**
* taking place.
*/
public Type3Message(Type2Message type2, String password, String domain,
- String user, String workstation) {
- setFlags(getDefaultFlags(type2));
+ String user, String workstation, int flags) {
+ setFlags(flags | getDefaultFlags(type2));
+ if (workstation == null)
+ workstation = getDefaultWorkstation();
+ setWorkstation(workstation);
setDomain(domain);
setUser(user);
- setWorkstation(workstation);
+
switch (LM_COMPATIBILITY) {
case 0:
case 1:
- setLMResponse(getLMResponse(type2, password));
- setNTResponse(getNTResponse(type2, password));
+ if ((getFlags() & NTLMSSP_NEGOTIATE_NTLM2) == 0) {
+ setLMResponse(getLMResponse(type2, password));
+ setNTResponse(getNTResponse(type2, password));
+ } else {
+ // NTLM2 Session Response
+
+ byte[] clientChallenge = new byte[24];
+ RANDOM.nextBytes(clientChallenge);
+ java.util.Arrays.fill(clientChallenge, 8, 24, (byte)0x00);
+
+ byte[] responseKeyNT = NtlmPasswordAuthentication.nTOWFv1(password);
+ byte[] ntlm2Response = NtlmPasswordAuthentication.getNTLM2Response(responseKeyNT,
+ type2.getChallenge(),
+ clientChallenge);
+
+ setLMResponse(clientChallenge);
+ setNTResponse(ntlm2Response);
+
+ if ((getFlags() & NTLMSSP_NEGOTIATE_SIGN) == NTLMSSP_NEGOTIATE_SIGN) {
+ byte[] sessionNonce = new byte[16];
+ System.arraycopy(type2.getChallenge(), 0, sessionNonce, 0, 8);
+ System.arraycopy(clientChallenge, 0, sessionNonce, 8, 8);
+
+ MD4 md4 = new MD4();
+ md4.update(responseKeyNT);
+ HMACT64 hmac = new HMACT64(md4.digest());
+ hmac.update(sessionNonce);
+ byte[] userSessionKey = hmac.digest(); // NTLM2 session key
+
+ 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);
+ } else {
+ masterKey = userSessionKey;
+ setSessionKey(masterKey);
+ }
+ }
+ }
break;
case 2:
byte[] nt = getNTResponse(type2, password);
case 3:
case 4:
case 5:
+ byte[] responseKeyNT = NtlmPasswordAuthentication.nTOWFv2(domain, user, password);
+
byte[] clientChallenge = new byte[8];
RANDOM.nextBytes(clientChallenge);
- setLMResponse(getLMv2Response(type2, domain, user, password,
- clientChallenge));
- /*
- setNTResponse(getNTLMv2Response(type2, domain, user, password,
- clientChallenge));
- */
+ setLMResponse(getLMv2Response(type2, domain, user, password, clientChallenge));
+
+ byte[] clientChallenge2 = new byte[8];
+ RANDOM.nextBytes(clientChallenge2);
+ 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);
+ }
+
+ setSessionKey(exchangedKey);
+ }
+
break;
default:
setLMResponse(getLMResponse(type2, password));
}
/**
+ * The real session key if the regular session key is actually
+ * the encrypted version used for key exchange.
+ *
+ * @return A <code>byte[]</code> containing the session key.
+ */
+ public byte[] getMasterKey() {
+ return masterKey;
+ }
+
+ /**
* Returns the session key.
*
* @return A <code>byte[]</code> containing the session key.
return NtlmPasswordAuthentication.getLMv2Response(domain, user,
password, type2.getChallenge(), clientChallenge);
}
+ public static byte[] getNTLMv2Response(Type2Message type2,
+ byte[] responseKeyNT,
+ byte[] clientChallenge) {
+ if (type2 == null || responseKeyNT == null || clientChallenge == null) {
+ return null;
+ }
+ return NtlmPasswordAuthentication.getNTLMv2Response(responseKeyNT,
+ type2.getChallenge(),
+ clientChallenge,
+ System.currentTimeMillis(),
+ type2.getTargetInformation());
+ }
/**
* Constructs the NT response to the given Type-2 message using
protected CacheEntry referrals = null;
public HashMap getTrustedDomains(NtlmPasswordAuthentication auth) throws SmbAuthException {
- if (DISABLED)
+ if (DISABLED || auth.domain == "?")
return null;
if (_domains != null && System.currentTimeMillis() > _domains.expiration) {
if (dr.length == 1)
return dr[0];
} catch (IOException ioe) {
- if (log.level >= 3)
+ if (log.level >= 4)
ioe.printStackTrace(log);
if (strictView && ioe instanceof SmbAuthException) {
throw (SmbAuthException)ioe;
public static final int NT_STATUS_NOT_FOUND = 0xC0000225;
public static final int NT_STATUS_ACCOUNT_LOCKED_OUT = 0xC0000234;
public static final int NT_STATUS_PATH_NOT_COVERED = 0xC0000257;
+ public static final int NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED = 0xC0000279;
static final int[] NT_STATUS_CODES = {
NT_STATUS_OK,
NT_STATUS_NOT_FOUND,
NT_STATUS_ACCOUNT_LOCKED_OUT,
NT_STATUS_PATH_NOT_COVERED,
+ NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED,
};
static final String[] NT_STATUS_MESSAGES = {
"NT_STATUS_NOT_FOUND",
"The referenced account is currently locked out and may not be logged on to.",
"The remote system is not reachable by the transport.",
+ "NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED",
};
}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2008 "Michael B. Allen" <jcifs at samba dot org>
+ *
+ * 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.smb;
+
+import java.io.IOException;
+import java.security.*;
+import jcifs.ntlmssp.*;
+
+public class NtlmContext {
+
+ NtlmPasswordAuthentication auth;
+ int ntlmsspFlags;
+ String workstation;
+ boolean isEstablished = false;
+ byte[] serverChallenge = null;
+ byte[] signingKey = null;
+ int state = 1;
+
+ public NtlmContext(NtlmPasswordAuthentication auth, boolean doSigning) {
+ this.auth = auth;
+ this.ntlmsspFlags = ntlmsspFlags |
+ NtlmFlags.NTLMSSP_REQUEST_TARGET |
+ NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
+ NtlmFlags.NTLMSSP_NEGOTIATE_128;
+ if (doSigning) {
+ this.ntlmsspFlags |= NtlmFlags.NTLMSSP_NEGOTIATE_SIGN |
+ NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
+ NtlmFlags.NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+ this.workstation = Type1Message.getDefaultWorkstation();
+ }
+
+ public boolean isEstablished() {
+ return isEstablished;
+ }
+ public byte[] getServerChallenge()
+ {
+ return serverChallenge;
+ }
+ public byte[] getSigningKey()
+ {
+ return signingKey;
+ }
+
+ public byte[] initSecContext(byte[] token, int offset, int len) throws SmbException {
+ switch (state) {
+ case 1:
+ Type1Message msg1 = new Type1Message(ntlmsspFlags, auth.getDomain(), workstation);
+ token = msg1.toByteArray();
+ state++;
+ break;
+ case 2:
+ try {
+ Type2Message msg2 = new Type2Message(token);
+
+ serverChallenge = msg2.getChallenge();
+ ntlmsspFlags &= msg2.getFlags();
+
+ Type3Message msg3 = new Type3Message(msg2,
+ auth.getPassword(),
+ auth.getDomain(),
+ auth.getUsername(),
+ workstation,
+ ntlmsspFlags);
+ token = msg3.toByteArray();
+
+ if ((ntlmsspFlags & NtlmFlags.NTLMSSP_NEGOTIATE_SIGN) != 0)
+ signingKey = msg3.getMasterKey();
+
+ isEstablished = true;
+ state++;
+ break;
+ } catch (Exception e) {
+ throw new SmbException(e.getMessage());
+ }
+ default:
+ throw new SmbException("Invalid state");
+ }
+ return token;
+ }
+}
import java.io.UnsupportedEncodingException;
import java.io.Serializable;
import java.security.Principal;
+import java.security.MessageDigest;
+import java.security.GeneralSecurityException;
import java.util.Random;
import java.util.Arrays;
import jcifs.Config;
public final class NtlmPasswordAuthentication implements Principal, Serializable {
private static final int LM_COMPATIBILITY =
- Config.getInt("jcifs.smb.lmCompatibility", 0);
+ Config.getInt("jcifs.smb.lmCompatibility", 3);
private static final Random RANDOM = new Random();
(byte)0x4b, (byte)0x47, (byte)0x53, (byte)0x21,
(byte)0x40, (byte)0x23, (byte)0x24, (byte)0x25
};
+ /* Accepts key multiple of 7
+ * Returns enc multiple of 8
+ * Multiple is the same like: 21 byte key gives 24 byte result
+ */
private static void E( byte[] key, byte[] data, byte[] e ) {
byte[] key7 = new byte[7];
byte[] e8 = new byte[8];
return null;
}
}
+ public static byte[] getNTLM2Response(byte[] nTOWFv1,
+ byte[] serverChallenge,
+ byte[] clientChallenge)
+ {
+ byte[] sessionHash = new byte[8];
+
+ try {
+ MessageDigest md5;
+ md5 = MessageDigest.getInstance("MD5");
+ md5.update(serverChallenge);
+ md5.update(clientChallenge, 0, 8);
+ System.arraycopy(md5.digest(), 0, sessionHash, 0, 8);
+ } catch (GeneralSecurityException gse) {
+ if (log.level > 0)
+ gse.printStackTrace(log);
+ throw new RuntimeException("MD5", gse);
+ }
+
+ byte[] key = new byte[21];
+ System.arraycopy(nTOWFv1, 0, key, 0, 16);
+ byte[] ntResponse = new byte[24];
+ E(key, sessionHash, ntResponse);
+
+ return ntResponse;
+ }
+ public static byte[] nTOWFv1(String password)
+ {
+ try {
+ MD4 md4 = new MD4();
+ md4.update(password.getBytes("UnicodeLittleUnmarked"));
+ return md4.digest();
+ } catch (UnsupportedEncodingException uee) {
+ throw new RuntimeException(uee.getMessage());
+ }
+ }
+ public static byte[] nTOWFv2(String domain, String username, String password)
+ {
+ try {
+ MD4 md4 = new MD4();
+ md4.update(password.getBytes("UnicodeLittleUnmarked"));
+ HMACT64 hmac = new HMACT64(md4.digest());
+ hmac.update(username.toUpperCase().getBytes("UnicodeLittleUnmarked"));
+ hmac.update(domain.toUpperCase().getBytes("UnicodeLittleUnmarked"));
+ return hmac.digest();
+ } catch (UnsupportedEncodingException uee) {
+ throw new RuntimeException(uee.getMessage());
+ }
+ }
+ static byte[] computeResponse(byte[] responseKey,
+ byte[] serverChallenge,
+ byte[] clientData,
+ int offset,
+ int length)
+ {
+ HMACT64 hmac = new HMACT64(responseKey);
+ hmac.update(serverChallenge);
+ hmac.update(clientData, offset, length);
+ byte[] mac = hmac.digest();
+ byte[] ret = new byte[mac.length + clientData.length];
+ System.arraycopy(mac, 0, ret, 0, mac.length);
+ System.arraycopy(clientData, 0, ret, mac.length, clientData.length);
+ return ret;
+ }
+ public static byte[] getLMv2Response(
+ byte[] responseKeyLM,
+ byte[] serverChallenge,
+ byte[] clientChallenge)
+ {
+ return NtlmPasswordAuthentication.computeResponse(responseKeyLM,
+ serverChallenge,
+ clientChallenge,
+ 0,
+ clientChallenge.length);
+ }
+ public static byte[] getNTLMv2Response(
+ byte[] responseKeyNT,
+ byte[] serverChallenge,
+ byte[] clientChallenge,
+ long time,
+ byte[] targetInfo)
+ {
+ byte[] temp = new byte[28 + targetInfo.length];
+
+ Encdec.enc_uint32le(0x00000101, temp, 0); // Header
+ Encdec.enc_uint32le(0x00000000, temp, 4); // Reserved
+ Encdec.enc_uint64le((time + SmbConstants.MILLISECONDS_BETWEEN_1970_AND_1601) * 10000L, temp, 8);
+ System.arraycopy(clientChallenge, 0, temp, 16, 8);
+ Encdec.enc_uint32le(0x00000000, temp, 24); // Unknown
+ System.arraycopy(targetInfo, 0, temp, 28, targetInfo.length);
+
+ return NtlmPasswordAuthentication.computeResponse(responseKeyNT,
+ serverChallenge,
+ temp,
+ 0,
+ temp.length);
+ }
static final NtlmPasswordAuthentication NULL =
new NtlmPasswordAuthentication( "", "", "" );
}
}
+ public byte[] getSigningKey(byte[] challenge) throws SmbException
+ {
+ switch (LM_COMPATIBILITY) {
+ case 0:
+ case 1:
+ case 2:
+ byte[] signingKey = new byte[40];
+ getUserSessionKey(challenge, signingKey, 0);
+ System.arraycopy(getUnicodeHash(challenge), 0, signingKey, 16, 24);
+ return signingKey;
+ case 3:
+ case 4:
+ case 5:
+ /* This code is only called if extended security is not on. This will
+ * all be cleaned up an normalized in JCIFS 2.x.
+ */
+ throw new SmbException("NTLMv2 requires extended security (jcifs.smb.client.useExtendedSecurity must be true if jcifs.smb.lmCompatibility >= 3)");
+ }
+ return null;
+ }
+
/**
* Returns the effective user session key.
*
* @param offset The offset in the destination array at which the
* session key will start.
*/
- void getUserSessionKey(byte[] challenge, byte[] dest, int offset)
- throws Exception {
+ void getUserSessionKey(byte[] challenge, byte[] dest, int offset) throws SmbException {
if (hashesExternal) return;
- MD4 md4 = new MD4();
- md4.update(password.getBytes("UnicodeLittleUnmarked"));
- switch (LM_COMPATIBILITY) {
- case 0:
- case 1:
- case 2:
- md4.update(md4.digest());
- md4.digest(dest, offset, 16);
- break;
- case 3:
- case 4:
- case 5:
- if( clientChallenge == null ) {
- clientChallenge = new byte[8];
- RANDOM.nextBytes( clientChallenge );
- }
+ try {
+ MD4 md4 = new MD4();
+ md4.update(password.getBytes("UnicodeLittleUnmarked"));
+ switch (LM_COMPATIBILITY) {
+ case 0:
+ case 1:
+ case 2:
+ md4.update(md4.digest());
+ md4.digest(dest, offset, 16);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ if( clientChallenge == null ) {
+ clientChallenge = new byte[8];
+ RANDOM.nextBytes( clientChallenge );
+ }
- HMACT64 hmac = new HMACT64(md4.digest());
- hmac.update(username.toUpperCase().getBytes(
- "UnicodeLittleUnmarked"));
- hmac.update(domain.toUpperCase().getBytes(
- "UnicodeLittleUnmarked"));
- byte[] ntlmv2Hash = hmac.digest();
- hmac = new HMACT64(ntlmv2Hash);
- hmac.update(challenge);
- hmac.update(clientChallenge);
- HMACT64 userKey = new HMACT64(ntlmv2Hash);
- userKey.update(hmac.digest());
- userKey.digest(dest, offset, 16);
- break;
- default:
- md4.update(md4.digest());
- md4.digest(dest, offset, 16);
- break;
- }
+ HMACT64 hmac = new HMACT64(md4.digest());
+ hmac.update(username.toUpperCase().getBytes(
+ "UnicodeLittleUnmarked"));
+ hmac.update(domain.toUpperCase().getBytes(
+ "UnicodeLittleUnmarked"));
+ byte[] ntlmv2Hash = hmac.digest();
+ hmac = new HMACT64(ntlmv2Hash);
+ hmac.update(challenge);
+ hmac.update(clientChallenge);
+ HMACT64 userKey = new HMACT64(ntlmv2Hash);
+ userKey.update(hmac.digest());
+ userKey.digest(dest, offset, 16);
+ break;
+ default:
+ md4.update(md4.digest());
+ md4.digest(dest, offset, 16);
+ break;
+ }
+ } catch (Exception e) {
+ throw new SmbException("", e);
+ }
}
/**
flags2,
tid, pid, uid, mid,
wordCount, byteCount;
- boolean useUnicode, received;
+ boolean useUnicode, received, extendedSecurity;
long responseTimeout = 1;
int signSeq;
boolean verifyFailed;
}
return str;
}
+ String readString(byte[] src, int srcIndex, int srcEnd, int maxLen, boolean useUnicode) {
+ int len = 0;
+ String str = null;
+ try {
+ if (useUnicode) {
+ // Unicode requires word alignment
+ if (((srcIndex - headerStart) % 2) != 0) {
+ srcIndex++;
+ }
+ for (len = 0; (srcIndex + len + 1) < srcEnd; len += 2) {
+ if (src[srcIndex + len] == (byte)0x00 && src[srcIndex + len + 1] == (byte)0x00) {
+ break;
+ }
+ if (len > maxLen) {
+ if (log.level > 0)
+ Hexdump.hexdump(System.err, src, srcIndex, maxLen < 128 ? maxLen + 8 : 128);
+ throw new RuntimeException("zero termination not found");
+ }
+ }
+ str = new String(src, srcIndex, len, "UnicodeLittleUnmarked");
+ } else {
+ for (len = 0; srcIndex < srcEnd; len++) {
+ if (src[srcIndex + len] == (byte)0x00) {
+ break;
+ }
+ if (len > maxLen) {
+ if (log.level > 0)
+ Hexdump.hexdump(System.err, src, srcIndex, maxLen < 128 ? maxLen + 8 : 128);
+ throw new RuntimeException("zero termination not found");
+ }
+ }
+ str = new String(src, srcIndex, len, OEM_ENCODING);
+ }
+ } catch( UnsupportedEncodingException uee ) {
+ if( log.level > 1 )
+ uee.printStackTrace( log );
+ }
+ return str;
+ }
int stringWireLength( String str, int offset ) {
int len = str.length() + 1;
if( useUnicode ) {
private MessageDigest digest;
private byte[] macSigningKey;
+ private boolean bypass = false;
private int updates;
private int signSequence;
+ public SigningDigest(byte[] macSigningKey, boolean bypass) throws SmbException {
+ try {
+ digest = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException ex) {
+ if( log.level > 0 )
+ ex.printStackTrace( log );
+ throw new SmbException( "MD5", ex );
+ }
+
+ this.macSigningKey = macSigningKey;
+ this.bypass = bypass;
+ this.updates = 0;
+ this.signSequence = 0;
+
+ if( log.level >= 5 ) {
+ log.println("macSigningKey:");
+ Hexdump.hexdump( log, macSigningKey, 0, macSigningKey.length );
+ }
+ }
+
public SigningDigest( SmbTransport transport,
NtlmPasswordAuthentication auth ) throws SmbException {
try {
ServerMessageBlock.writeInt4(signSequence, data, index);
update(data, offset, length);
System.arraycopy(digest(), 0, data, index, 8);
+ if (bypass) {
+ bypass = false;
+ System.arraycopy("BSRSPYL ".getBytes(), 0, data, index, 8);
+ }
} catch (Exception ex) {
if( log.level > 0 )
ex.printStackTrace( log );
int bufferIndex ) {
int start = bufferIndex;
- server.encryptionKey = new byte[server.encryptionKeyLength];
- System.arraycopy( buffer, bufferIndex,
- server.encryptionKey, 0, server.encryptionKeyLength );
- bufferIndex += server.encryptionKeyLength;
- if( byteCount > server.encryptionKeyLength ) {
- int len = 0;
- try {
- if(( flags2 & FLAGS2_UNICODE ) == FLAGS2_UNICODE ) {
- while( buffer[bufferIndex + len] != (byte)0x00 ||
- buffer[bufferIndex + len + 1] != (byte)0x00 ) {
- len += 2;
- if( len > 256 ) {
- throw new RuntimeException( "zero termination not found" );
+ if ((server.capabilities & FLAGS2_EXTENDED_SECURITY_NEGOTIATION) == 0) {
+ server.encryptionKey = new byte[server.encryptionKeyLength];
+ System.arraycopy( buffer, bufferIndex,
+ server.encryptionKey, 0, server.encryptionKeyLength );
+ bufferIndex += server.encryptionKeyLength;
+ if( byteCount > server.encryptionKeyLength ) {
+ int len = 0;
+// TODO: we can use new string routine here
+ try {
+ if(( flags2 & FLAGS2_UNICODE ) == FLAGS2_UNICODE ) {
+ while( buffer[bufferIndex + len] != (byte)0x00 ||
+ buffer[bufferIndex + len + 1] != (byte)0x00 ) {
+ len += 2;
+ if( len > 256 ) {
+ throw new RuntimeException( "zero termination not found" );
+ }
}
- }
- server.oemDomainName = new String( buffer, bufferIndex,
- len, "UnicodeLittleUnmarked" );
- } else {
- while( buffer[bufferIndex + len] != (byte)0x00 ) {
- len++;
- if( len > 256 ) {
- throw new RuntimeException( "zero termination not found" );
+ server.oemDomainName = new String( buffer, bufferIndex,
+ len, "UnicodeLittleUnmarked" );
+ } else {
+ while( buffer[bufferIndex + len] != (byte)0x00 ) {
+ len++;
+ if( len > 256 ) {
+ throw new RuntimeException( "zero termination not found" );
+ }
}
+ server.oemDomainName = new String( buffer, bufferIndex,
+ len, ServerMessageBlock.OEM_ENCODING );
}
- server.oemDomainName = new String( buffer, bufferIndex,
- len, ServerMessageBlock.OEM_ENCODING );
+ } catch( UnsupportedEncodingException uee ) {
+ if( log.level > 1 )
+ uee.printStackTrace( log );
}
- } catch( UnsupportedEncodingException uee ) {
- if( log.level > 1 )
- uee.printStackTrace( log );
+ bufferIndex += len;
+ } else {
+ server.oemDomainName = new String();
}
- bufferIndex += len;
} else {
- server.oemDomainName = new String();
+ server.guid = new byte[16];
+ System.arraycopy(buffer, bufferIndex, server.guid, 0, 16);
+ // ignore SPNEGO token for now ...
}
return bufferIndex - start;
",serverTimeZone=" + server.serverTimeZone +
",encryptionKeyLength=" + server.encryptionKeyLength +
",byteCount=" + byteCount +
- ",encryptionKey=0x" + Hexdump.toHexString( server.encryptionKey,
- 0,
- server.encryptionKeyLength * 2 ) +
",oemDomainName=" + server.oemDomainName + "]" );
}
}
private static final boolean DISABLE_PLAIN_TEXT_PASSWORDS =
Config.getBoolean( "jcifs.smb.client.disablePlainTextPasswords", true );
- private byte[] accountPassword, unicodePassword;
- private int passwordLength, unicodePasswordLength;
+ private byte[] lmHash, ntHash, blob = null;
private int sessionKey;
private String accountName, primaryDomain;
SmbSession session;
- NtlmPasswordAuthentication auth;
+ Object cred;
- SmbComSessionSetupAndX( SmbSession session, ServerMessageBlock andx ) throws SmbException {
+ SmbComSessionSetupAndX( SmbSession session, ServerMessageBlock andx, Object cred ) throws SmbException {
super( andx );
command = SMB_COM_SESSION_SETUP_ANDX;
this.session = session;
- this.auth = session.auth;
- if( auth.hashesExternal &&
- Arrays.equals(auth.challenge, session.transport.server.encryptionKey) == false ) {
- throw new SmbAuthException( SmbException.NT_STATUS_ACCESS_VIOLATION );
- }
- }
+ this.cred = cred;
- int getBatchLimit( byte command ) {
- return command == SMB_COM_TREE_CONNECT_ANDX ? BATCH_LIMIT : 0;
- }
- int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) {
- int start = dstIndex;
+ sessionKey = session.transport.sessionKey;
- if( session.transport.server.security == SECURITY_USER &&
- ( auth.hashesExternal || auth.password.length() > 0 )) {
- if( session.transport.server.encryptedPasswords ) {
- accountPassword = auth.getAnsiHash( session.transport.server.encryptionKey );
- passwordLength = accountPassword.length;
- unicodePassword = auth.getUnicodeHash( session.transport.server.encryptionKey );
- unicodePasswordLength = unicodePassword.length;
- // prohibit HTTP auth attempts for the null session
- if (unicodePasswordLength == 0 && passwordLength == 0) {
- throw new RuntimeException("Null setup prohibited.");
+ if (session.transport.server.security == SECURITY_USER) {
+ if (cred instanceof NtlmPasswordAuthentication) {
+ NtlmPasswordAuthentication auth = (NtlmPasswordAuthentication)cred;
+
+ 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
+ if (lmHash.length == 0 && ntHash.length == 0) {
+ throw new RuntimeException("Null setup prohibited.");
+ }
+ } else if( DISABLE_PLAIN_TEXT_PASSWORDS ) {
+ throw new RuntimeException( "Plain text passwords are disabled" );
+ } else if( useUnicode ) {
+ // plain text
+ String password = auth.getPassword();
+ lmHash = new byte[0];
+ ntHash = new byte[(password.length() + 1) * 2];
+ writeString( password, ntHash, 0 );
+ } else {
+ // plain text
+ String password = auth.getPassword();
+ lmHash = new byte[(password.length() + 1) * 2];
+ ntHash = new byte[0];
+ writeString( password, lmHash, 0 );
}
- } else if( DISABLE_PLAIN_TEXT_PASSWORDS ) {
- throw new RuntimeException( "Plain text passwords are disabled" );
- } else if( useUnicode ) {
- // plain text
- String password = auth.getPassword();
- accountPassword = new byte[0];
- passwordLength = 0;
- unicodePassword = new byte[(password.length() + 1) * 2];
- unicodePasswordLength = writeString( password, unicodePassword, 0 );
+ accountName = auth.username;
+ if (useUnicode)
+ accountName = accountName.toUpperCase();
+ primaryDomain = auth.domain.toUpperCase();
+ } else if (cred instanceof byte[]) {
+ blob = (byte[])cred;
} else {
- // plain text
- String password = auth.getPassword();
- accountPassword = new byte[(password.length() + 1) * 2];
- passwordLength = writeString( password, accountPassword, 0 );
- unicodePassword = new byte[0];
- unicodePasswordLength = 0;
+ throw new SmbException("Unsupported credential type");
}
} else {
- // no password in session setup
- passwordLength = unicodePasswordLength = 0;
+ throw new SmbException("Unsupported");
}
+ }
- sessionKey = session.transport.sessionKey;
+ int getBatchLimit( byte command ) {
+ return command == SMB_COM_TREE_CONNECT_ANDX ? BATCH_LIMIT : 0;
+ }
+ int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) {
+ int start = dstIndex;
writeInt2( session.transport.snd_buf_size, dst, dstIndex );
dstIndex += 2;
dstIndex += 2;
writeInt4( sessionKey, dst, dstIndex );
dstIndex += 4;
- writeInt2( passwordLength, dst, dstIndex );
- dstIndex += 2;
- writeInt2( unicodePasswordLength, dst, dstIndex );
- dstIndex += 2;
+ if (blob != null) {
+ writeInt2( blob.length, dst, dstIndex );
+ dstIndex += 2;
+ } else {
+ writeInt2( lmHash.length, dst, dstIndex );
+ dstIndex += 2;
+ writeInt2( ntHash.length, dst, dstIndex );
+ dstIndex += 2;
+ }
dst[dstIndex++] = (byte)0x00;
dst[dstIndex++] = (byte)0x00;
dst[dstIndex++] = (byte)0x00;
int writeBytesWireFormat( byte[] dst, int dstIndex ) {
int start = dstIndex;
- accountName = useUnicode ? auth.username : auth.username.toUpperCase();
- primaryDomain = auth.domain.toUpperCase();
-
- if( session.transport.server.security == SECURITY_USER &&
- ( auth.hashesExternal || auth.password.length() > 0 )) {
- System.arraycopy( accountPassword, 0, dst, dstIndex, passwordLength );
- dstIndex += passwordLength;
- if (session.transport.server.encryptedPasswords == false && useUnicode) {
- /* Align Unicode plain text password manually
- */
- if ((( dstIndex - headerStart ) % 2 ) != 0 ) {
- dst[dstIndex++] = (byte)'\0';
- }
- }
- System.arraycopy( unicodePassword, 0, dst, dstIndex, unicodePasswordLength );
- dstIndex += unicodePasswordLength;
+ if (blob != null) {
+ System.arraycopy(blob, 0, dst, dstIndex, blob.length );
+ dstIndex += blob.length;
+ } else {
+ System.arraycopy( lmHash, 0, dst, dstIndex, lmHash.length );
+ dstIndex += lmHash.length;
+ System.arraycopy( ntHash, 0, dst, dstIndex, ntHash.length );
+ dstIndex += ntHash.length;
+
+ dstIndex += writeString( accountName, dst, dstIndex );
+ dstIndex += writeString( primaryDomain, dst, dstIndex );
}
-
- dstIndex += writeString( accountName, dst, dstIndex );
- dstIndex += writeString( primaryDomain, dst, dstIndex );
dstIndex += writeString( session.transport.NATIVE_OS, dst, dstIndex );
dstIndex += writeString( session.transport.NATIVE_LANMAN, dst, dstIndex );
",maxMpxCount=" + session.transport.maxMpxCount +
",VC_NUMBER=" + session.transport.VC_NUMBER +
",sessionKey=" + sessionKey +
- ",passwordLength=" + passwordLength +
- ",unicodePasswordLength=" + unicodePasswordLength +
+ ",lmHash.length=" + (lmHash == null ? 0 : lmHash.length) +
+ ",ntHash.length=" + (ntHash == null ? 0 : ntHash.length) +
",capabilities=" + session.transport.capabilities +
",accountName=" + accountName +
",primaryDomain=" + primaryDomain +
private String primaryDomain = "";
boolean isLoggedInAsGuest;
+ byte[] blob = null;
SmbComSessionSetupAndXResponse( ServerMessageBlock andx ) {
super( andx );
return 0;
}
int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) {
+ int start = bufferIndex;
isLoggedInAsGuest = ( buffer[bufferIndex] & 0x01 ) == 0x01 ? true : false;
- return 2;
+ bufferIndex += 2;
+ if (extendedSecurity) {
+ int blobLength = readInt2(buffer, bufferIndex);
+ bufferIndex += 2;
+ blob = new byte[blobLength];
+ }
+ return bufferIndex - start;
}
int readBytesWireFormat( byte[] buffer, int bufferIndex ) {
int start = bufferIndex;
+ if (extendedSecurity) {
+ System.arraycopy(buffer, bufferIndex, blob, 0, blob.length);
+ bufferIndex += blob.length;
+ }
nativeOs = readString( buffer, bufferIndex );
bufferIndex += stringWireLength( nativeOs, bufferIndex );
- nativeLanMan = readString( buffer, bufferIndex );
+ nativeLanMan = readString( buffer, bufferIndex, start + byteCount, 255, useUnicode );
bufferIndex += stringWireLength( nativeLanMan, bufferIndex );
-
- if( useUnicode ) {
- int len;
-
- if((( bufferIndex - headerStart ) % 2 ) != 0 ) {
- bufferIndex++;
- }
-
- len = 0;
- while( buffer[bufferIndex + len] != (byte)0x00 ) {
- len += 2;
- if( len > 256 ) {
- throw new RuntimeException( "zero termination not found" );
- }
- }
- try {
- primaryDomain = new String( buffer, bufferIndex, len, "UnicodeLittle" );
- } catch( UnsupportedEncodingException uee ) {
- if( log.level > 1 )
- uee.printStackTrace( log );
- }
- bufferIndex += len;
- } else {
- primaryDomain = readString( buffer, bufferIndex );
- bufferIndex += stringWireLength( primaryDomain, bufferIndex );
+ if (!extendedSecurity) {
+ primaryDomain = readString(buffer, bufferIndex, start + byteCount, 255, useUnicode);
+ bufferIndex += stringWireLength(primaryDomain, bufferIndex);
}
return bufferIndex - start;
static final boolean USE_NTSTATUS = Config.getBoolean( "jcifs.smb.client.useNtStatus", true );
static final boolean SIGNPREF = Config.getBoolean("jcifs.smb.client.signingPreferred", false );
static final boolean USE_NTSMBS = Config.getBoolean( "jcifs.smb.client.useNTSmbs", true );
- static final boolean USE_EXTSEC = Config.getBoolean( "jcifs.smb.client.useExtendedSecurity", false );
+ static final boolean USE_EXTSEC = Config.getBoolean( "jcifs.smb.client.useExtendedSecurity", true );
static final String NETBIOS_HOSTNAME = Config.getProperty( "jcifs.netbios.hostname", null );
- static final int LM_COMPATIBILITY = Config.getInt( "jcifs.smb.lmCompatibility", 0);
+ static final int LM_COMPATIBILITY = Config.getInt( "jcifs.smb.lmCompatibility", 3);
static final int FLAGS_NONE = 0x00;
static final int FLAGS_LOCK_AND_READ_WRITE_AND_UNLOCK = 0x01;
static final int CAP_LOCK_AND_READ = 0x0100;
static final int CAP_NT_FIND = 0x0200;
static final int CAP_DFS = 0x1000;
+ static final int CAP_EXTENDED_SECURITY = 0x80000000;
// file attribute encoding
static final int ATTR_READONLY = 0x01;
import jcifs.Config;
import jcifs.UniAddress;
import jcifs.netbios.NbtAddress;
+import jcifs.util.MD4;
/**
* The class represents a user's session established with an SMB/CIFS
}
void sessionSetup( ServerMessageBlock andx,
ServerMessageBlock andxResponse ) throws SmbException {
+ NtlmContext nctx = null;
SmbException ex = null;
+ SmbComSessionSetupAndX request;
+ SmbComSessionSetupAndXResponse response;
+ byte[] token = new byte[0];
+ int state = 10;
synchronized( transport() ) {
if( sessionSetup ) {
if( transport.log.level >= 4 )
transport.log.println( "sessionSetup: accountName=" + auth.username + ",primaryDomain=" + auth.domain );
- SmbComSessionSetupAndX request = new SmbComSessionSetupAndX( this, andx );
- SmbComSessionSetupAndXResponse response = new SmbComSessionSetupAndXResponse( andxResponse );
+ do {
+ switch (state) {
+ case 10: /* NTLM */
+ if ((transport.capabilities & SmbConstants.CAP_EXTENDED_SECURITY) == SmbConstants.CAP_EXTENDED_SECURITY) {
+ state = 20; /* NTLMSSP */
+ break;
+ }
- /* Create SMB signature digest if necessary
- * Only the first SMB_COM_SESSION_SETUP_ANX with non-null or
- * blank password initializes signing.
- */
- if (transport.isSignatureSetupRequired( auth )) {
- if( auth.hashesExternal && NtlmPasswordAuthentication.DEFAULT_PASSWORD != NtlmPasswordAuthentication.BLANK ) {
- /* preauthentication
- */
- transport.getSmbSession( NtlmPasswordAuthentication.DEFAULT ).getSmbTree( LOGON_SHARE, null ).treeConnect( null, null );
- } else {
- request.digest = new SigningDigest( transport, auth );
- }
- }
+ request = new SmbComSessionSetupAndX( this, andx, auth );
+ response = new SmbComSessionSetupAndXResponse( andxResponse );
+
+ /* Create SMB signature digest if necessary
+ * Only the first SMB_COM_SESSION_SETUP_ANX with non-null or
+ * blank password initializes signing.
+ */
+ if (transport.isSignatureSetupRequired( auth )) {
+ if( auth.hashesExternal && NtlmPasswordAuthentication.DEFAULT_PASSWORD != NtlmPasswordAuthentication.BLANK ) {
+ /* preauthentication
+ */
+ transport.getSmbSession( NtlmPasswordAuthentication.DEFAULT ).getSmbTree( LOGON_SHARE, null ).treeConnect( null, null );
+ } else {
+ byte[] signingKey = auth.getSigningKey(transport.server.encryptionKey);
+ request.digest = new SigningDigest(signingKey, false);
+ }
+ }
- request.auth = auth;
+ request.auth = auth;
- try {
- transport.send( request, response );
- } catch (SmbAuthException sae) {
- throw sae;
- } catch (SmbException se) {
- ex = se;
- }
+ try {
+ transport.send( request, response );
+ } catch (SmbAuthException sae) {
+ throw sae;
+ } catch (SmbException se) {
+ ex = se;
+ }
- if( response.isLoggedInAsGuest &&
- "GUEST".equalsIgnoreCase( auth.username ) == false) {
- throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
- }
+ if( response.isLoggedInAsGuest &&
+ "GUEST".equalsIgnoreCase( auth.username ) == false) {
+ throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
+ }
- uid = response.uid;
- sessionSetup = true;
+ if (ex != null)
+ throw ex;
- if( request.digest != null ) {
- /* success - install the signing digest */
- transport.digest = request.digest;
- }
+ uid = response.uid;
+
+ if( request.digest != null ) {
+ /* success - install the signing digest */
+ transport.digest = request.digest;
+ }
+
+ sessionSetup = true;
+ state = 0;
+
+ break;
+ case 20:
+ if (nctx == null) {
+ boolean doSigning = (transport.flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES) != 0;
+ nctx = new NtlmContext(auth, doSigning);
+ }
- if (ex != null)
- throw ex;
+ if (nctx.isEstablished()) {
+ sessionSetup = true;
+ state = 0;
+ break;
+ }
+
+ token = nctx.initSecContext(token, 0, token.length);
+ if (token != null) {
+ request = new SmbComSessionSetupAndX(this, null, token);
+ response = new SmbComSessionSetupAndXResponse(null);
+
+ if (transport.isSignatureSetupRequired( auth )) {
+ byte[] signingKey = nctx.getSigningKey();
+ if (signingKey != null)
+ request.digest = new SigningDigest(signingKey, true);
+ }
+
+ request.uid = uid;
+ uid = 0;
+
+ try {
+ transport.send( request, response );
+ } catch (SmbAuthException sae) {
+ throw sae;
+ } catch (SmbException se) {
+ ex = se;
+ /* Apparently once a successfull NTLMSSP login occurs, the
+ * server will return "Access denied" even if a logoff is
+ * sent. Unfortunately calling disconnect() doesn't always
+ * actually shutdown the connection before other threads
+ * have committed themselves (e.g. InterruptTest example).
+ */
+ try { transport.disconnect(true); } catch (Exception e) {}
+ }
+
+ if( response.isLoggedInAsGuest &&
+ "GUEST".equalsIgnoreCase( auth.username ) == false) {
+ throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
+ }
+
+ if (ex != null)
+ throw ex;
+
+ uid = response.uid;
+
+ if (request.digest != null) {
+ /* success - install the signing digest */
+ transport.digest = request.digest;
+ }
+
+ token = response.blob;
+ }
+
+ break;
+ default:
+ throw new SmbException("Unexpected session setup state: " + state);
+ }
+ } while (state != 0);
}
}
void logoff( boolean inError ) {
}
if( !inError && transport.server.security != ServerMessageBlock.SECURITY_SHARE ) {
-
/*
* Logoff And X Request / Response
*/
-
+
SmbComLogoffAndX request = new SmbComLogoffAndX( null );
request.uid = uid;
try {
transport.send( request, null );
} catch( SmbException se ) {
}
+ uid = 0;
}
sessionSetup = false;
int serverTimeZone;
int encryptionKeyLength;
byte[] encryptionKey;
+ byte[] guid;
}
InetAddress localAddr;
if( resp.dialectIndex > 10 ) {
throw new SmbException( "This client does not support the negotiated dialect." );
}
- if (server.encryptionKeyLength != 8 && LM_COMPATIBILITY == 0) {
- throw new SmbException("Encryption key length is not 8 as expected. This could indicate that the server requires NTLMv2. JCIFS does not fully support NTLMv2 but you can try setting jcifs.smb.lmCompatibility = 3.");
+ if ((server.capabilities & CAP_EXTENDED_SECURITY) != CAP_EXTENDED_SECURITY &&
+ server.encryptionKeyLength != 8 &&
+ LM_COMPATIBILITY == 0) {
+ throw new SmbException("Unexpected encryption key length: " + server.encryptionKeyLength);
}
/* Adjust negotiated values */
if (maxMpxCount < 1) maxMpxCount = 1;
snd_buf_size = Math.min( snd_buf_size, server.maxBufferSize );
capabilities &= server.capabilities;
+ if ((server.capabilities & CAP_EXTENDED_SECURITY) == CAP_EXTENDED_SECURITY)
+ capabilities |= CAP_EXTENDED_SECURITY; // & doesn't copy high bit
+
if ((capabilities & ServerMessageBlock.CAP_UNICODE) == 0) {
// server doesn't want unicode
if (FORCE_UNICODE) {
protected void doRecv( Response response ) throws IOException {
ServerMessageBlock resp = (ServerMessageBlock)response;
resp.useUnicode = useUnicode;
+ resp.extendedSecurity = (capabilities & CAP_EXTENDED_SECURITY) == CAP_EXTENDED_SECURITY;
synchronized (BUF) {
System.arraycopy( sbuf, 0, BUF, 0, 4 + HEADER_LENGTH );
throw drs[0];
case 0x80000005: /* STATUS_BUFFER_OVERFLOW */
break; /* normal for DCERPC named pipes */
+ case NtStatus.NT_STATUS_MORE_PROCESSING_REQUIRED:
+ break; /* normal for NTLMSSP */
default:
throw new SmbException( resp.errorCode, null );
}