From: Felix Schumacher Date: Wed, 6 Aug 2008 14:08:56 +0000 (+0200) Subject: jcifs-0.7.15 from tgz X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=666d1cb42b5e1a0f304e155dcc5064c86ae38539;p=jcifs_without_docs.git jcifs-0.7.15 from tgz Thu Oct 23 01:18:29 EDT 2003 jcifs-0.7.15 released The name service code has been modified to return a different response with each 0x1C domain controller query. The range of servers is limited to the top jcifs.netbios.lookupRespLimit entries in the NetBIOS response. The effect that this will have is that the NtlmHttpFilter and NtlmServlet will rotate through domain controllers when authenticating WWW clients. Also, because different transports are used, fewer sessions will be multiplexed over each resulting in an increase in scalability proportional to the number of domain controllers used. This behavior can be turned off by setting the jcifs.http.loadBalance property to false (the default is true). The jcifs.smb.client.ssnLimit property default value has been changed to 250 from 100. Mon Oct 6 23:53:28 EDT 2003 jcifs-0.7.14 released Eric's LMv2 patch has been merged. There is some uncertainty surrounding the signing of SMBs if GUEST credentials are negotiated. On a related note; the "Concurrent modification" issue has been resolved. The oversight with this one was that it was believed that only a thead calling into the transport should ever trigger tryClose() which is not exactly true. The transport thread can call tryClose but prior to adding verify() calls it was rare and only happend when the transport reached a state where no further operations could move forward anyway. So rather than syncronize tryClose, I have changed verify to simply return a boolean value which set's the new ServerMessageBlock.verifyFailed member. This is then examined by the calling thread who will tryClose (with the transport locked) and throw the "Unverifyable signature" if verifyFailed. Finally there has been a straight forward but potentially dangerous change in SmbFile. All list operations now build a list of names or files using an ArrayList. This assisted in fixing another problem where NetServerEnum2 responses were observed to return server names of 0 length. This caused ArrayIndexOutOfBounds exceptions. These entries are now ignored. --- diff --git a/CHANGES.txt b/CHANGES.txt index def84bd..9eb88ad 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,42 @@ +Thu Oct 23 01:18:29 EDT 2003 + +jcifs-0.7.15 released + +The name service code has been modified to return a different response with +each 0x1C domain controller query. The range of servers is limited to the +top jcifs.netbios.lookupRespLimit entries in the NetBIOS response. The +effect that this will have is that the NtlmHttpFilter and NtlmServlet will +rotate through domain controllers when authenticating WWW clients. Also, +because different transports are used, fewer sessions will be multiplexed +over each resulting in an increase in scalability proportional to the +number of domain controllers used. This behavior can be turned off by +setting the jcifs.http.loadBalance property to false (the default is true). + +The jcifs.smb.client.ssnLimit property default value has been changed to +250 from 100. + +Mon Oct 6 23:53:28 EDT 2003 + +jcifs-0.7.14 released + +Eric's LMv2 patch has been merged. There is some uncertainty surrounding +the signing of SMBs if GUEST credentials are negotiated. On a related note; +the "Concurrent modification" issue has been resolved. The oversight with +this one was that it was believed that only a thead calling into the +transport should ever trigger tryClose() which is not exactly true. The +transport thread can call tryClose but prior to adding verify() calls it +was rare and only happend when the transport reached a state where no +further operations could move forward anyway. So rather than syncronize +tryClose, I have changed verify to simply return a boolean value which +set's the new ServerMessageBlock.verifyFailed member. This is then examined +by the calling thread who will tryClose (with the transport locked) and +throw the "Unverifyable signature" if verifyFailed. Finally there has been +a straight forward but potentially dangerous change in SmbFile. All list +operations now build a list of names or files using an ArrayList. This +assisted in fixing another problem where NetServerEnum2 responses were +observed to return server names of 0 length. This caused +ArrayIndexOutOfBounds exceptions. These entries are now ignored. + Wed Sep 17 21:21:31 EDT 2003 JCIFS now supports SMB signing. If a server requires SMB signing (Windows diff --git a/README.txt b/README.txt index 05acc07..ce37c7a 100644 --- a/README.txt +++ b/README.txt @@ -1,3 +1,19 @@ +Thu Oct 23 01:18:29 EDT 2003 + +jcifs-0.7.15 released + +The NTLM HTTP Authentication behavoir has been modified to permit load +balancing between many domain controllers. The jcifs.smb.client.ssnLimit +property default value has also been changed to 250 from 100. + +Mon Oct 6 23:53:28 EDT 2003 + +jcifs-0.7.14 released + +Eric's latest signing patch has been merged, a few adjustments have been +made to eliminate a concurrency issue, and a fix for browse servers that +return zero length server names has been incorporated. + Wed Sep 17 21:21:31 EDT 2003 SMB signing is now supported and there have been some adjustments to diff --git a/build.xml b/build.xml index f7caea3..18c3c97 100644 --- a/build.xml +++ b/build.xml @@ -66,24 +66,24 @@ - + - + - - - + + + - + - + diff --git a/examples/VerifyGuest.java b/examples/VerifyGuest.java new file mode 100644 index 0000000..9e78d3f --- /dev/null +++ b/examples/VerifyGuest.java @@ -0,0 +1,28 @@ +import jcifs.netbios.NbtAddress; +import jcifs.smb.*; +import jcifs.util.Log; +import java.util.Date; + +public class VerifyGuest { + + public static void list( SmbFile dir ) { + try { + long t1 = System.currentTimeMillis(); + SmbFile[] files = dir.listFiles(); + long t2 = System.currentTimeMillis() - t1; + + for( int i = 0; i < files.length; i++ ) { + System.out.print( " " + files[i].getName() ); + } + System.out.println(); + System.out.println( files.length + " files in " + t2 + "ms" ); + } catch( Exception e ) { + e.printStackTrace(); + } + } + + public static void main( String[] argv ) throws Exception { + list( new SmbFile( "smb://miallen2/" )); + list( new SmbFile( "smb://miallen2/pub/", new NtlmPasswordAuthentication( "dom", "user", "pass" ))); + } +} diff --git a/src/jcifs/UniAddress.java b/src/jcifs/UniAddress.java index 1622613..92d7b24 100644 --- a/src/jcifs/UniAddress.java +++ b/src/jcifs/UniAddress.java @@ -291,7 +291,13 @@ public class UniAddress { Object addr; String calledName; - UniAddress( Object addr ) { + /** + * Wrap an InetAddress or NbtAddress. + */ + public UniAddress( Object addr ) { + if( addr == null ) { + throw new IllegalArgumentException(); + } this.addr = addr; } diff --git a/src/jcifs/http/NtlmHttpFilter.java b/src/jcifs/http/NtlmHttpFilter.java index 350fec3..3cbff62 100644 --- a/src/jcifs/http/NtlmHttpFilter.java +++ b/src/jcifs/http/NtlmHttpFilter.java @@ -31,6 +31,7 @@ import jcifs.smb.SmbSession; import jcifs.smb.NtlmPasswordAuthentication; import jcifs.smb.SmbAuthException; import jcifs.util.Base64; +import jcifs.netbios.NbtAddress; /** * This servlet Filter can be used to negotiate password hashes with @@ -47,6 +48,8 @@ public class NtlmHttpFilter implements Filter { private String domainController; + private boolean loadBalance; + private boolean enableBasic; private boolean insecureBasic; @@ -70,7 +73,10 @@ public class NtlmHttpFilter implements Filter { } defaultDomain = Config.getProperty("jcifs.smb.client.domain"); domainController = Config.getProperty( "jcifs.http.domainController" ); - if( domainController == null ) domainController = defaultDomain; + if( domainController == null ) { + domainController = defaultDomain; + loadBalance = Config.getBoolean( "jcifs.http.loadBalance", true ); + } enableBasic = Boolean.valueOf( Config.getProperty("jcifs.http.enableBasic")).booleanValue(); insecureBasic = Boolean.valueOf( @@ -96,7 +102,11 @@ public class NtlmHttpFilter implements Filter { if( msg != null && (msg.startsWith( "NTLM " ) || (offerBasic && msg.startsWith("Basic ")))) { - dc = UniAddress.getByName( domainController, true ); + if( loadBalance ) { + dc = new UniAddress( NbtAddress.getByName( domainController, 0x1C, null )); + } else { + dc = UniAddress.getByName( domainController, true ); + } if (msg.startsWith("NTLM ")) { byte[] challenge = SmbSession.getChallenge( dc ); if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) { diff --git a/src/jcifs/http/NtlmServlet.java b/src/jcifs/http/NtlmServlet.java index 96fe897..19d7efc 100644 --- a/src/jcifs/http/NtlmServlet.java +++ b/src/jcifs/http/NtlmServlet.java @@ -43,6 +43,8 @@ import jcifs.smb.SmbSession; import jcifs.util.Base64; +import jcifs.netbios.NbtAddress; + /** * This servlet may be used with pre-2.3 servlet containers * to protect content with NTLM HTTP Authentication. Servlets that @@ -61,6 +63,8 @@ public abstract class NtlmServlet extends HttpServlet { private String domainController; + private boolean loadBalance; + private boolean enableBasic; private boolean insecureBasic; @@ -85,7 +89,10 @@ public abstract class NtlmServlet extends HttpServlet { } defaultDomain = Config.getProperty("jcifs.smb.client.domain"); domainController = Config.getProperty("jcifs.http.domainController"); - if (domainController == null) domainController = defaultDomain; + if( domainController == null ) { + domainController = defaultDomain; + loadBalance = Config.getBoolean( "jcifs.http.loadBalance", true ); + } enableBasic = Boolean.valueOf( Config.getProperty("jcifs.http.enableBasic")).booleanValue(); insecureBasic = Boolean.valueOf( @@ -96,12 +103,17 @@ public abstract class NtlmServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + UniAddress dc; boolean offerBasic = enableBasic && (insecureBasic || request.isSecure()); String msg = request.getHeader("Authorization"); if (msg != null && (msg.startsWith("NTLM ") || (offerBasic && msg.startsWith("Basic ")))) { - UniAddress dc = UniAddress.getByName(domainController, true); + if( loadBalance ) { + dc = new UniAddress( NbtAddress.getByName( domainController, 0x1C, null )); + } else { + dc = UniAddress.getByName( domainController, true ); + } NtlmPasswordAuthentication ntlm; if (msg.startsWith("NTLM ")) { byte[] challenge = SmbSession.getChallenge(dc); diff --git a/src/jcifs/netbios/NameServicePacket.java b/src/jcifs/netbios/NameServicePacket.java index 086e073..3d4d2cf 100644 --- a/src/jcifs/netbios/NameServicePacket.java +++ b/src/jcifs/netbios/NameServicePacket.java @@ -53,6 +53,10 @@ abstract class NameServicePacket { static final int AUTHORITY_OFFSET = 8; static final int ADDITIONAL_OFFSET = 10; + static final int LOOKUP_RESP_LIMIT = jcifs.Config.getInt( "jcifs.netbios.lookupRespLimit", 5 ); + + static int addrIndex = 0; + static void writeInt2( int val, byte[] dst, int dstIndex ) { dst[dstIndex++] = (byte)(( val >> 8 ) & 0xFF ); dst[dstIndex] = (byte)( val & 0xFF ); @@ -199,6 +203,8 @@ abstract class NameServicePacket { } int readResourceRecordWireFormat( byte[] src, int srcIndex ) { int start = srcIndex; + int end; + if(( src[srcIndex] & 0xC0 ) == 0xC0 ) { recordName = questionName; // label string pointer to questionName srcIndex += 2; @@ -213,8 +219,20 @@ abstract class NameServicePacket { srcIndex += 4; rDataLength = readInt2( src, srcIndex ); srcIndex += 2; - readRDataWireFormat( src, srcIndex ); - srcIndex += rDataLength; + + end = srcIndex + rDataLength; + for( int i = 0; srcIndex < end; i++ ) { + srcIndex += readRDataWireFormat( src, srcIndex ); + if( i == addrIndex ) { + addrIndex++; + if( addrIndex == LOOKUP_RESP_LIMIT ) { + addrIndex = 0; + } + return end - start; + } + } + addrIndex = 0; + return srcIndex - start; } diff --git a/src/jcifs/smb/ServerMessageBlock.java b/src/jcifs/smb/ServerMessageBlock.java index f216d5d..fe46815 100644 --- a/src/jcifs/smb/ServerMessageBlock.java +++ b/src/jcifs/smb/ServerMessageBlock.java @@ -263,6 +263,7 @@ abstract class ServerMessageBlock { boolean useUnicode, received; long responseTimeout = 1; int verifySequence; + boolean verifyFailed; ServerMessageBlock() { flags = (byte)( FLAGS_PATH_NAMES_CASELESS | FLAGS_PATH_NAMES_CANONICALIZED ); diff --git a/src/jcifs/smb/SmbFile.java b/src/jcifs/smb/SmbFile.java index add39d9..d3d4f2d 100644 --- a/src/jcifs/smb/SmbFile.java +++ b/src/jcifs/smb/SmbFile.java @@ -25,6 +25,7 @@ import java.net.UnknownHostException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import jcifs.util.Config; import jcifs.UniAddress; import jcifs.netbios.NbtAddress; @@ -1143,6 +1144,7 @@ The shareAccess parameter controls what permissions other clients have */ public String[] list() throws SmbException { + ArrayList list = new ArrayList(); connect0(); @@ -1158,11 +1160,12 @@ The shareAccess parameter controls what permissions other clients have response.status, response.toString() ); } - String[] ret = new String[response.entriesReturned]; for( int i = 0; i < response.entriesReturned; i++ ) { - ret[i] = response.results[i].name; + if( response.results[i].name.length() > 0 ) { + list.add( response.results[i].name ); + } } - return ret; + return (String[])list.toArray( new String[list.size()] ); } else if( share == null ) { if( getType() == TYPE_WORKGROUP ) { NetServerEnum2Response response = new NetServerEnum2Response(); @@ -1174,12 +1177,13 @@ The shareAccess parameter controls what permissions other clients have throw new SmbException( SmbException.ERRRAP, response.status ); } - String[] ret = new String[response.entriesReturned]; for( int i = 0; i < response.entriesReturned; i++ ) { - ret[i] = response.results[i].name; + if( response.results[i].name.length() > 0 ) { + list.add( response.results[i].name ); + } } - return ret; + return (String[])list.toArray( new String[list.size()] ); } else { NetShareEnumResponse response = new NetShareEnumResponse(); sendTransaction( new NetShareEnum(), response ); @@ -1189,21 +1193,20 @@ The shareAccess parameter controls what permissions other clients have throw new SmbException( SmbException.ERRRAP, response.status ); } - String[] ret = new String[response.entriesReturned]; for( int i = 0; i < response.entriesReturned; i++ ) { - ret[i] = response.results[i].netName; + list.add( response.results[i].netName ); } - return ret; + return (String[])list.toArray( new String[list.size()] ); } } else { return list( unc ); } } String[] list( String dirPath ) throws SmbException { - int sid, count, i, j; - String[] results; + int sid, count, i; String filename; + ArrayList list = new ArrayList(); Log.println( Log.WARNINGS, "smb find warning", " find with path=" + dirPath ); @@ -1213,23 +1216,19 @@ The shareAccess parameter controls what permissions other clients have sid = response.sid; count = response.searchCount; - j = 0; - - results = new String[Math.max( 16, count )]; int h1 = new String( "." ).hashCode(); int h2 = new String( ".." ).hashCode(); - i = 0; - while( j < count ) { - filename = response.results[i++].filename; + + for( i = 0; i < count; i++ ) { + filename = response.results[i].filename; if( filename.length() < 3 ) { int h = filename.hashCode(); if( h == h1 || h == h2 ) { - count--; continue; } } - results[j++] = filename; + list.add( filename ); } /* only difference between first2 and next2 @@ -1241,36 +1240,23 @@ The shareAccess parameter controls what permissions other clients have response.reset(); sendTransaction( new Trans2FindNext2( sid, response.resumeKey, response.lastName ), response ); - count += response.searchCount; + count = response.searchCount; - if( count > results.length ) { - String[] tmp = results; - results = new String[Math.max( results.length * 2, count )]; - System.arraycopy( tmp, 0, results, 0, j ); - } - i = 0; - while( j < count ) { - filename = response.results[i++].filename; + for( i = 0; i < count; i++ ) { + filename = response.results[i].filename; if( filename.length() < 3 ) { int h = filename.hashCode(); if( h == h1 || h == h2 ) { - count--; continue; } } - results[j++] = filename; + list.add( filename ); } } send( new SmbComFindClose2( sid ), blank_resp ); - if( results.length != count ) { - String[] tmp = results; - results = new String[count]; - System.arraycopy( tmp, 0, results, 0, count ); - } - - return results; + return (String[])list.toArray( new String[list.size()] ); } /** @@ -1333,6 +1319,8 @@ The shareAccess parameter controls what permissions other clients have */ public SmbFile[] listFiles( String wildcard ) throws SmbException { + ArrayList list = new ArrayList(); + if( url.toString().lastIndexOf( '/' ) != ( url.toString().length() - 1 )) { throw new SmbException( SmbException.ERRCLI, SmbException.ERRlistFiles, url.toString() + " directory must end with '/'" ); @@ -1352,18 +1340,19 @@ The shareAccess parameter controls what permissions other clients have response.status, response.toString() ); } - SmbFile[] ret = new SmbFile[response.entriesReturned]; for( int i = 0; i < response.entriesReturned; i++ ) { - ret[i] = new SmbFile( this, + if( response.results[i].name.length() > 0 ) { + list.add( new SmbFile( this, response.results[i].name, TYPE_WORKGROUP, ATTR_READONLY | ATTR_DIRECTORY, 0L, - 0L ); + 0L )); + } } //System.err.println( "ret=" + ret.length + ",ret[0]=" + ret[0] + ",name=" + response.results[0].name ); - return ret; + return (SmbFile[])list.toArray(new SmbFile[list.size()]); } else if( share == null ) { if( getType() == TYPE_WORKGROUP ) { NetServerEnum2Response response = new NetServerEnum2Response(); @@ -1375,17 +1364,18 @@ The shareAccess parameter controls what permissions other clients have throw new SmbException( SmbException.ERRRAP, response.status ); } - SmbFile[] ret = new SmbFile[response.entriesReturned]; for( int i = 0; i < response.entriesReturned; i++ ) { - ret[i] = new SmbFile( this, + if( response.results[i].name.length() > 0 ) { + list.add( new SmbFile( this, response.results[i].name, TYPE_SERVER, ATTR_READONLY | ATTR_DIRECTORY, 0L, - 0L ); + 0L )); + } } - return ret; + return (SmbFile[])list.toArray(new SmbFile[list.size()]); } else { NetShareEnumResponse response = new NetShareEnumResponse(); sendTransaction( new NetShareEnum(), response ); @@ -1395,7 +1385,6 @@ The shareAccess parameter controls what permissions other clients have throw new SmbException( SmbException.ERRRAP, response.status ); } - SmbFile[] ret = new SmbFile[response.entriesReturned]; for( int i = 0; i < response.entriesReturned; i++ ) { int shareType = response.results[i].type; switch( shareType ) { @@ -1409,14 +1398,14 @@ The shareAccess parameter controls what permissions other clients have shareType = TYPE_SHARE; break; } - ret[i] = new SmbFile( this, + list.add( new SmbFile( this, response.results[i].netName, shareType, ATTR_READONLY | ATTR_DIRECTORY, 0L, - 0L ); + 0L )); } - return ret; + return (SmbFile[])list.toArray(new SmbFile[list.size()]); } } else { return listFiles( getUncPath0(), wildcard ); @@ -1430,9 +1419,9 @@ The shareAccess parameter controls what permissions other clients have } } SmbFile[] listFiles( String dirPath, String wildcard ) throws SmbException { - int sid, count, i, j; - SmbFile[] results; + int sid, count, i; String base, filename; + ArrayList list = new ArrayList(); Log.println( Log.WARNINGS, "smb find warning", " find with path=" + dirPath ); @@ -1447,66 +1436,51 @@ The shareAccess parameter controls what permissions other clients have sid = response.sid; count = response.searchCount; - j = 0; - - results = new SmbFile[Math.max( 16, count )]; int h1 = new String( "." ).hashCode(); int h2 = new String( ".." ).hashCode(); - i = 0; try { - while( j < count ) { + for( i = 0; i < count; i++ ) { filename = response.results[i].filename; if( filename.length() < 3 ) { int h = filename.hashCode(); if( h == h1 || h == h2 ) { - count--; - i++; continue; } } - results[j++] = new SmbFile( this, + list.add( new SmbFile( this, filename, TYPE_FILESYSTEM, response.results[i].extFileAttributes, response.results[i].lastWriteTime, - response.results[i].endOfFile ); - i++; + response.results[i].endOfFile )); } - + /* only difference between first2 and next2 * responses is subCommand so let's recycle */ response.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2; - + while( response.isEndOfSearch == false && response.searchCount > 0 ) { response.reset(); sendTransaction( new Trans2FindNext2( sid, response.resumeKey, response.lastName ), response ); - count += response.searchCount; - - if( count > results.length ) { - SmbFile[] tmp = results; - results = new SmbFile[Math.max( results.length * 2, count )]; - System.arraycopy( tmp, 0, results, 0, j ); - } - i = 0; - while( j < count ) { + count = response.searchCount; + + for( i = 0; i < count; i++ ) { filename = response.results[i].filename; if( filename.length() < 3 ) { int h = filename.hashCode(); if( h == h1 || h == h2 ) { - count--; continue; } } - results[j++] = new SmbFile( this, + list.add( new SmbFile( this, filename, TYPE_FILESYSTEM, response.results[i].extFileAttributes, response.results[i].lastWriteTime, - response.results[i].endOfFile ); - i++; + response.results[i].endOfFile )); } } } catch( UnknownHostException uhe ) { @@ -1519,13 +1493,7 @@ The shareAccess parameter controls what permissions other clients have send( new SmbComFindClose2( sid ), blank_resp ); - if( results.length != count ) { - SmbFile[] tmp = results; - results = new SmbFile[count]; - System.arraycopy( tmp, 0, results, 0, count ); - } - - return results; + return (SmbFile[])list.toArray(new SmbFile[list.size()]); } /** * Changes the name of the file this SmbFile represents to the name diff --git a/src/jcifs/smb/SmbSession.java b/src/jcifs/smb/SmbSession.java index e3dbda3..71b6771 100644 --- a/src/jcifs/smb/SmbSession.java +++ b/src/jcifs/smb/SmbSession.java @@ -117,9 +117,12 @@ synchronized( transport ) { transport.negotiate(); - if( transport.useSigning && transport.macSigningKey == null ) { - /* The first SMB_COM_SESSION_SETUP_ANX - * generates the signing key */ + if( transport.useSigning && transport.macSigningKey == null && + NtlmPasswordAuthentication.NULL != auth && + NtlmPasswordAuthentication.NULL.equals( auth ) == false ) { + // NtlmPasswordAuthentication.GUEST.equals( auth ) == false ) { + /* The first SMB_COM_SESSION_SETUP_ANX with creds other than + * null or guest generates the signing key */ transport.initSigning( auth ); } diff --git a/src/jcifs/smb/SmbTransport.java b/src/jcifs/smb/SmbTransport.java index 2fa76af..609295e 100644 --- a/src/jcifs/smb/SmbTransport.java +++ b/src/jcifs/smb/SmbTransport.java @@ -1,5 +1,6 @@ /* jcifs smb client library in Java * Copyright (C) 2000 "Michael B. Allen" + * "Eric Glass" * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,6 +35,7 @@ import java.net.UnknownHostException; import java.security.MessageDigest; +import java.util.Arrays; import java.util.Vector; import java.util.LinkedList; import java.util.ListIterator; @@ -42,20 +44,22 @@ import java.util.Hashtable; class SmbTransport implements Runnable { + private static final byte[] LMV2_CROSSDOMAIN_KEY = new byte[16]; + private static final int DEFAULT_MAX_MPX_COUNT = 10; private static final int DEFAULT_RESPONSE_TIMEOUT = 10000; - private static final int DEFAULT_SO_TIMEOUT = 15000; - private static final int PUSHBACK_BUF_SIZE = 64; + private static final int DEFAULT_SO_TIMEOUT = 15000; + private static final int PUSHBACK_BUF_SIZE = 64; private static final int DEFAULT_RCV_BUF_SIZE = 60416; private static final int DEFAULT_SND_BUF_SIZE = 5000; - private static final int DEFAULT_SSN_LIMIT = 100; + private static final int DEFAULT_SSN_LIMIT = 250; private static final int LM_COMPATIBILITY = Config.getInt("jcifs.smb.lmCompatibility", 0); - static final int MID_OFFSET = 30; + static final int MID_OFFSET = 30; static final int HEADER_LENGTH = 32; - static final int FLAGS_OFFSET = 9; + static final int FLAGS_OFFSET = 9; static final int FLAGS_RESPONSE = 0x80; private NbtSocket socket; // should become UniSocket? @@ -68,7 +72,7 @@ class SmbTransport implements Runnable { private Thread thread; private Object outLock; - private MessageDigest signingDigest; + private MessageDigest signingDigest; private static Vector connections = new Vector(); private static MpxControl mpxCtrl = new MpxControl(); @@ -81,8 +85,8 @@ private static byte[] rcv_buf = new byte[0xFFFF]; UniAddress address; - byte[] macSigningKey; - int signSequence; + byte[] macSigningKey; + int signSequence; int port, rcv_buf_size, snd_buf_size; @@ -179,7 +183,7 @@ private static byte[] rcv_buf = new byte[0xFFFF]; } useSigning = Config.getBoolean("jcifs.smb.client.signingPreferred", false); if( useSigning ) { - client.flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES; + client.flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES; } soTimeout = Config.getInt( "jcifs.smb.client.soTimeout", DEFAULT_SO_TIMEOUT ); @@ -400,7 +404,7 @@ synchronized( rcv_buf ) { if( response.errorCode != 0 || e.hasMoreElements() == false ) { ((SmbComTransactionResponse)response).hasMore = false; if( useSigning ) { - verify(rcv_buf, 0, response.length, response.verifySequence); + response.verifyFailed = verify(rcv_buf, 0, response.length, response.verifySequence); } response.notify(); } else { @@ -418,7 +422,7 @@ synchronized( rcv_buf ) { } Log.printHexDump( "smb received", rcv_buf, 0, response.length ); if( useSigning ) { - verify(rcv_buf, 0, response.length, response.verifySequence); + response.verifyFailed = verify(rcv_buf, 0, response.length, response.verifySequence); } response.notify(); @@ -437,13 +441,15 @@ synchronized( rcv_buf ) { responseTable.size() + " pending requests" ); } } catch( IOException ioe ) { - tryClose( true ); + synchronized( this ) { + tryClose( true ); + } Log.printStackTrace( "exception reading from socket input: " + address, ioe ); } } } - void initSigning(NtlmPasswordAuthentication auth) throws SmbException { + void initSigning(NtlmPasswordAuthentication auth) throws SmbException { if( auth.hashesExternal ) { if( server.signaturesRequired ) { throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, @@ -472,10 +478,13 @@ synchronized( rcv_buf ) { case 3: case 4: case 5: - // NTLMv2 will have a key; LMv2 doesn't (just uses an - // empty user session key). So if/when we start doing - // NTLMv2, this will change. macSigningKey = new byte[16]; + try { + auth.getUserSessionKey(server.encryptionKey, macSigningKey, 0); + } catch (Exception ex) { + Log.printStackTrace("Unable to calculate MAC signing key.", ex); + macSigningKey = null; + } break; default: macSigningKey = new byte[40]; @@ -491,17 +500,17 @@ synchronized( rcv_buf ) { } } - /** - * Performs MAC signing of the SMB. This is done as follows. - * The signature field of the SMB is overwritted with the sequence number; - * The MD5 digest of the MAC signing key + the entire SMB is taken; - * The first 8 bytes of this are placed in the signature field. - * - * @param data The data. - * @param offset The starting offset at which the SMB header begins. - * @param length The length of the SMB data starting at offset. - */ - private void sign(byte[] data, int offset, int length) { + /** + * Performs MAC signing of the SMB. This is done as follows. + * The signature field of the SMB is overwritted with the sequence number; + * The MD5 digest of the MAC signing key + the entire SMB is taken; + * The first 8 bytes of this are placed in the signature field. + * + * @param data The data. + * @param offset The starting offset at which the SMB header begins. + * @param length The length of the SMB data starting at offset. + */ + private void sign(byte[] data, int offset, int length) { if (macSigningKey == null) return; try { signingDigest.update(macSigningKey); @@ -515,18 +524,18 @@ synchronized( rcv_buf ) { } finally { signSequence += 2; } - } - - /** - * Performs MAC signature verification. This calculates the signature - * of the SMB and compares it to the signature field on the SMB itself. - * - * @param data The data. - * @param offset The starting offset at which the SMB header begins. - * @param length The length of the SMB data starting at offset. - */ - private void verify(byte[] data, int offset, int length, int verifySequence) throws IOException { - if (macSigningKey == null) return; + } + + /** + * Performs MAC signature verification. This calculates the signature + * of the SMB and compares it to the signature field on the SMB itself. + * + * @param data The data. + * @param offset The starting offset at which the SMB header begins. + * @param length The length of the SMB data starting at offset. + */ + private boolean verify(byte[] data, int offset, int length, int verifySequence) throws IOException { + if (macSigningKey == null) return false; signingDigest.update(macSigningKey); int index = offset; signingDigest.update(data, index, ServerMessageBlock.SIGNATURE_OFFSET); @@ -539,10 +548,30 @@ synchronized( rcv_buf ) { byte[] signature = signingDigest.digest(); for (int i = 0; i < 8; i++) { if (signature[i] != data[offset + ServerMessageBlock.SIGNATURE_OFFSET + i]) { - throw new IOException("Unverifiable signature."); + // can this just use == ? + if (Arrays.equals(LMV2_CROSSDOMAIN_KEY, macSigningKey)) return true; + signingDigest.update(LMV2_CROSSDOMAIN_KEY); + index = offset; + signingDigest.update(data, index, ServerMessageBlock.SIGNATURE_OFFSET); + index += ServerMessageBlock.SIGNATURE_OFFSET; + ServerMessageBlock.writeInt4(verifySequence, sequence, 0); + signingDigest.update(sequence); + index += 8; + signingDigest.update(data, index, length - ServerMessageBlock.SIGNATURE_OFFSET - 8); + signature = signingDigest.digest(); + for (i = 0; i < 8; i++) { + if (signature[i] != data[offset + ServerMessageBlock.SIGNATURE_OFFSET + i]) { + return true; + } + } + // verified using LMv2 cross-domain MAC key; start using that. + macSigningKey = LMV2_CROSSDOMAIN_KEY; + break; } } - } + + return false; + } void send( ServerMessageBlock request, ServerMessageBlock response ) throws SmbException { @@ -559,11 +588,11 @@ synchronized( rcv_buf ) { synchronized( outLock ) { ensureOpen(); synchronized( snd_buf ) { - int length = request.writeWireFormat(snd_buf, 0); - if( useSigning ) { - sign(snd_buf, 0, length); - } - out.write(snd_buf, 0, length); + int length = request.writeWireFormat(snd_buf, 0); + if( useSigning ) { + sign(snd_buf, 0, length); + } + out.write(snd_buf, 0, length); out.flush(); Log.printMessageData( "smb sent", request ); @@ -597,12 +626,12 @@ synchronized( snd_buf ) { synchronized( outLock ) { ensureOpen(); synchronized( snd_buf ) { - int length = request.writeWireFormat(snd_buf, 0); - if( useSigning ) { + int length = request.writeWireFormat(snd_buf, 0); + if( useSigning ) { response.verifySequence = signSequence + 1; sign(snd_buf, 0, length); - } - out.write(snd_buf, 0, length); + } + out.write(snd_buf, 0, length); out.flush(); Log.printMessageData( "smb sent", request ); @@ -634,6 +663,11 @@ synchronized( snd_buf ) { throw new SmbException( SmbException.ERRCLI, SmbException.ERRserverTimeout, address ); + } else if( response.verifyFailed ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRioe, + "Unverifiable signature." ); } switch( response.errorCode & 0xFF ) { case SmbException.SUCCESS: @@ -691,12 +725,12 @@ synchronized( snd_buf ) { synchronized( outLock ) { ensureOpen(); synchronized(snd_buf) { - int length = request.writeWireFormat(snd_buf, 0); - if( useSigning ) { + int length = request.writeWireFormat(snd_buf, 0); + if( useSigning ) { response.verifySequence = signSequence + 1; - sign(snd_buf, 0, length); - } - out.write(snd_buf, 0, length); + sign(snd_buf, 0, length); + } + out.write(snd_buf, 0, length); out.flush(); Log.printMessageData( "smb sent", request ); @@ -741,12 +775,12 @@ synchronized(snd_buf) { synchronized( outLock ) { ensureOpen(); synchronized( snd_buf ) { - int length = request.writeWireFormat(snd_buf, 0); - if( useSigning ) { + int length = request.writeWireFormat(snd_buf, 0); + if( useSigning ) { response.verifySequence = signSequence + 1; - sign(snd_buf, 0, length); - } - out.write(snd_buf, 0, length); + sign(snd_buf, 0, length); + } + out.write(snd_buf, 0, length); out.flush(); Log.printMessageData( "smb sent", request ); @@ -778,6 +812,11 @@ synchronized( snd_buf ) { throw new SmbException( SmbException.ERRCLI, SmbException.ERRserverTimeout, address ); + } else if( response.verifyFailed ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRioe, + "Unverifiable signature." ); } switch( response.errorCode & 0xFF ) { case SmbException.SUCCESS: @@ -821,8 +860,8 @@ synchronized( snd_buf ) { * Negotiate Protocol Request / Response */ - // reset MAC signing - macSigningKey = null; + // reset MAC signing + macSigningKey = null; SmbComNegotiateResponse response = new SmbComNegotiateResponse(); send( new SmbComNegotiate(), response ); @@ -832,14 +871,14 @@ synchronized( snd_buf ) { throw new SmbException( SmbException.ERRCLI, SmbException.ERRbadDialect ); } - server.securityMode = response.securityMode; - server.security = response.security; + server.securityMode = response.securityMode; + server.security = response.security; server.encryptedPasswords = response.encryptedPasswords; server.signaturesEnabled = response.signaturesEnabled; server.signaturesRequired = response.signaturesRequired; - if (server.signaturesRequired || (server.signaturesEnabled && useSigning)) { + if (server.signaturesRequired || (server.signaturesEnabled && useSigning)) { useSigning = true; - client.flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES; + client.flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES; if (signingDigest == null) { try { @@ -849,24 +888,24 @@ synchronized( snd_buf ) { return; } } - } else { + } else { useSigning = false; - client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES; + client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES; } - negotiatedDialectIndex = response.dialectIndex;; - server.maxMpxCount = response.maxMpxCount; - negotiatedMaxMpxCount = client.maxMpxCount < server.maxMpxCount ? + negotiatedDialectIndex = response.dialectIndex;; + server.maxMpxCount = response.maxMpxCount; + negotiatedMaxMpxCount = client.maxMpxCount < server.maxMpxCount ? client.maxMpxCount : server.maxMpxCount; - mpxCtrl.maxMpxCount = negotiatedMaxMpxCount < 1 ? + mpxCtrl.maxMpxCount = negotiatedMaxMpxCount < 1 ? 1 : negotiatedMaxMpxCount; - server.maxNumberVcs = response.maxNumberVcs; - server.maxBufferSize = response.maxBufferSize; - negotiatedMaxBufferSize = client.maxBufferSize < server.maxBufferSize ? + server.maxNumberVcs = response.maxNumberVcs; + server.maxBufferSize = response.maxBufferSize; + negotiatedMaxBufferSize = client.maxBufferSize < server.maxBufferSize ? client.maxBufferSize : server.maxBufferSize; - server.maxRawSize = response.maxRawSize; - server.sessionKey = response.sessionKey; - server.capabilities = response.capabilities; - negotiatedCapabilities = client.capabilities & server.capabilities; + server.maxRawSize = response.maxRawSize; + server.sessionKey = response.sessionKey; + server.capabilities = response.capabilities; + negotiatedCapabilities = client.capabilities & server.capabilities; if(( negotiatedCapabilities & ServerMessageBlock.CAP_UNICODE ) == 0 ) { // server doesn't want unicode if( Config.getBoolean( "jcifs.smb.client.useUnicode", false )) { @@ -878,11 +917,11 @@ synchronized( snd_buf ) { client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE; } } - server.serverTime = response.serverTime; - server.serverTimeZone = response.serverTimeZone; + server.serverTime = response.serverTime; + server.serverTimeZone = response.serverTimeZone; server.encryptionKeyLength = response.encryptionKeyLength; - server.encryptionKey = response.encryptionKey; - server.oemDomainName = response.oemDomainName; + server.encryptionKey = response.encryptionKey; + server.oemDomainName = response.oemDomainName; } public String toString() { String ret = "SmbTransport[address=" + address; @@ -907,16 +946,16 @@ synchronized( snd_buf ) { mpxCtrl.releaseMid( mid ); } - static class MpxControl { + static class MpxControl { MpxListNode first, last; - class MpxListNode { - int i; - MpxListNode next; + class MpxListNode { + int i; + MpxListNode next; MpxListNode( int id ) { i = id; next = null; } - } + } int nextMid, mpxCount, maxMpxCount; MpxControl() { @@ -948,15 +987,15 @@ synchronized( snd_buf ) { mpxCount--; notify(); } - synchronized boolean contains( int i ) { + synchronized boolean contains( int i ) { for( MpxListNode tmp = first; tmp != null; tmp = tmp.next ) { if( tmp.i == i ) { return true; } } return false; - } - synchronized void add( int i ) { + } + synchronized void add( int i ) { if( contains( i )) { return; } @@ -965,8 +1004,8 @@ synchronized( snd_buf ) { } else { last = last.next = new MpxListNode( i ); } - } - synchronized void remove( int i ) { + } + synchronized void remove( int i ) { if( first == null ) { return; } else if( first == last && first.i == i ) { @@ -988,6 +1027,6 @@ synchronized( snd_buf ) { } prev = tmp; } - } - } + } + } } diff --git a/src/jcifs/smb/SmbTransport.java.bak b/src/jcifs/smb/SmbTransport.java.bak deleted file mode 100644 index d9501e4..0000000 --- a/src/jcifs/smb/SmbTransport.java.bak +++ /dev/null @@ -1,832 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2000 "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.smb; - -import jcifs.netbios.NbtSocket; -import jcifs.netbios.NbtException; -import jcifs.netbios.NbtAddress; -import jcifs.UniAddress; -import jcifs.Config; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PushbackInputStream; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Vector; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.Enumeration; -import java.util.Hashtable; - -class SmbTransport implements Runnable { - - private static final int DEFAULT_MAX_MPX_COUNT = 10; - private static final int DEFAULT_RESPONSE_TIMEOUT = 10000; - private static final int DEFAULT_SO_TIMEOUT = 15000; - private static final int PUSHBACK_BUF_SIZE = 64; - private static final int DEFAULT_RCV_BUF_SIZE = 60416; - private static final int DEFAULT_SND_BUF_SIZE = 5000; - private static final int DEFAULT_SSN_LIMIT = 100; - - static final int MID_OFFSET = 30; - static final int HEADER_LENGTH = 32; - static final int FLAGS_OFFSET = 9; - static final int FLAGS_RESPONSE = 0x80; - - private NbtSocket socket; // should become UniSocket? - private InputStream in; - private OutputStream out; - private int localPort, soTimeout, responseTimeout; - private long closeTime; - private InetAddress localAddr; - private Hashtable responseTable; - private Thread thread; - private Object outLock; - - private static Vector connections = new Vector(); - private static MpxControl mpxCtrl = new MpxControl(); - -private static byte[] snd_buf = new byte[0xFFFF]; -private static byte[] rcv_buf = new byte[0xFFFF]; - - LinkedList sessions; - boolean negotiated, useUnicode; - - UniAddress address; - int port, rcv_buf_size, snd_buf_size; - - int negotiatedDialectIndex; - int negotiatedMaxMpxCount; - int negotiatedMaxBufferSize; - int negotiatedCapabilities; - - int ssnLimit = Config.getInt( "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT ); - - ClientProperties client = new ClientProperties(); - ServerProperties server = new ServerProperties(); - - class ClientProperties { - int maxMpxCount = Config.getInt( "jcifs.smb.client.maxMpxCount", - DEFAULT_MAX_MPX_COUNT ); - int maxBufferSize = Config.getInt( "jcifs.smb.client.snd_buf_size", - DEFAULT_SND_BUF_SIZE ); - int sessionKey = 0x00000000; - /* NT 4 Workstation client capabilities 0x00D4 - * we don't do NT Status Codes or Level II oplocks - * but we could do raw and mpx - */ - int flags2 = Config.getInt( "jcifs.smb.client.flags2", - ServerMessageBlock.FLAGS2_LONG_FILENAMES | - ServerMessageBlock.FLAGS2_UNICODE ); - int capabilities = Config.getInt( "jcifs.smb.client.capabilities", - ServerMessageBlock.CAP_UNICODE | ServerMessageBlock.CAP_NT_SMBS ); - String nativeOs = Config.getProperty( "jcifs.smb.client.nativeOs", - System.getProperty( "os.name" )); - String nativeLanMan = Config.getProperty( "jcifs.smb.client.nativeLanMan", "jCIFS" ); - int vcNumber = 1; - } - class ServerProperties { - String tconHostName; - - byte flags; - int flags2; - int maxMpxCount; - int maxBufferSize; - int sessionKey; - // NT 4 Workstation is 0x43FD - int capabilities; - String oemDomainName; - String primaryDomain; - String nativeOs; - String nativeLanMan; - - int securityMode; - int security; - boolean encryptedPasswords; - boolean signaturesEnabled; - boolean signaturesRequired; - int maxNumberVcs; - int maxRawSize; - long serverTime; - int serverTimeZone; - int encryptionKeyLength; - byte[] encryptionKey; - } - - static synchronized SmbTransport getSmbTransport( UniAddress address, int port ) { - return getSmbTransport( address, port, - Config.getInetAddress( "jcifs.smb.client.laddr", null ), - Config.getInt( "jcifs.smb.client.lport", 0 )); - } - static synchronized SmbTransport getSmbTransport( UniAddress address, int port, - InetAddress localAddr, int localPort ) { - SmbTransport conn; - for( Enumeration e = connections.elements(); e.hasMoreElements(); ) { - conn = (SmbTransport)e.nextElement(); - if( conn.matches( address, port, localAddr, localPort )) { - return conn; - } - } - - conn = new SmbTransport( address, port, localAddr, localPort ); - connections.addElement( conn ); - return conn; - } - - SmbTransport( UniAddress address, int port, InetAddress localAddr, int localPort ) { - this.address = address; - this.port = port; - this.localAddr = localAddr; - this.localPort = localPort; - - useUnicode = Config.getBoolean( "jcifs.smb.client.useUnicode", true ); - if( useUnicode == false ) { - client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE; - client.capabilities &= 0xFFFF ^ ServerMessageBlock.CAP_UNICODE; - } - if( Config.getBoolean( "jcifs.smb.client.useNTSmbs", false ) == true ) { - client.capabilities |= ServerMessageBlock.CAP_NT_SMBS; - } - - soTimeout = Config.getInt( "jcifs.smb.client.soTimeout", DEFAULT_SO_TIMEOUT ); - responseTimeout = Config.getInt( "jcifs.smb.client.responseTimeout", DEFAULT_RESPONSE_TIMEOUT ); - rcv_buf_size = Config.getInt( "jcifs.smb.client.rcv_buf_size", DEFAULT_RCV_BUF_SIZE ); - snd_buf_size = client.maxBufferSize; - sessions = new LinkedList(); - responseTable = new Hashtable(); - outLock = new Object(); - negotiated = false; - } - - synchronized SmbSession getSmbSession() { - return getSmbSession( new NtlmPasswordAuthentication( null, null, null )); - } - synchronized SmbSession getSmbSession( NtlmPasswordAuthentication auth ) { - SmbSession ssn; - - ListIterator iter = sessions.listIterator(); - while( iter.hasNext() ) { - ssn = (SmbSession)iter.next(); - if( ssn.matches( auth )) { - ssn.auth = auth; - return ssn; - } - } - ssn = new SmbSession( this, auth ); - sessions.add( ssn ); - - if( sessions.size() > ssnLimit ) { - int nclose = sessions.size() / 10; - SmbSession s; - - while( nclose-- > 0 ) { - s = (SmbSession)sessions.removeFirst(); - s.logoff( false ); - } - } - - return ssn; - } - boolean matches( UniAddress address, int port, InetAddress localAddr, int localPort ) { - InetAddress defaultLocal = null; - try { - defaultLocal = InetAddress.getLocalHost(); - } catch( UnknownHostException uhe ) { - } - int p1 = ( port == 0 || port == 139 ) ? 0 : port; - int p2 = ( this.port == 0 || this.port == 139 ) ? 0 : this.port; - InetAddress la1 = localAddr == null ? defaultLocal : localAddr; - InetAddress la2 = this.localAddr == null ? defaultLocal : this.localAddr; - return address.equals( this.address ) && - p1 == p2 && - la1.equals( la2 ) && - localPort == this.localPort; - } - boolean hasCapability( int cap ) throws SmbException { - negotiate(); - return (negotiatedCapabilities & cap) == cap; - } - void ensureOpen() throws IOException { - if( socket == null ) { - Object obj; - NbtAddress naddr; - String calledName; - - /* Hack to convert InetAddress to NbtAddress until we properly - * abstract NetBIOSless transport with some kind of "UniSocket". - */ - obj = address.getAddress(); - if( obj instanceof NbtAddress ) { - naddr = (NbtAddress)obj; - } else { - try { - naddr = NbtAddress.getByName( ((InetAddress)obj).getHostAddress() ); - } catch( UnknownHostException uhe ) { - naddr = null; // never happen - } - } - - calledName = address.firstCalledName(); - do { - try { - socket = new NbtSocket( naddr, calledName, port, localAddr, localPort ); - break; - } catch( NbtException ne ) { - if( ne.errorClass == NbtException.ERR_SSN_SRVC && - ( ne.errorCode == NbtException.CALLED_NOT_PRESENT || - ne.errorCode == NbtException.NOT_LISTENING_CALLED )) { - Log.println( Log.WARNINGS, "smb warning", ne.getMessage() ); - } else { - throw ne; - } - } - } while(( calledName = address.nextCalledName()) != null ); - - if( calledName == null ) { - throw new IOException( "Failed to establish session with " + address ); - } - - /* Save the calledName for using on SMB_COM_TREE_CONNECT - */ - if( calledName == NbtAddress.SMBSERVER_NAME ) { - server.tconHostName = address.getHostAddress(); - } else { - server.tconHostName = calledName; - } - - if( Config.getBoolean( "jcifs.smb.client.tcpNoDelay", false )) { - socket.setTcpNoDelay( true ); - } - in = new PushbackInputStream( socket.getInputStream(), PUSHBACK_BUF_SIZE ); - out = socket.getOutputStream(); - thread = new Thread( this, "JCIFS-SmbTransport" ); - thread.setDaemon( true ); - thread.start(); - } - } - void tryClose( boolean inError ) { - SmbSession ssn; - - if( socket == null ) { - inError = true; - } - - ListIterator iter = sessions.listIterator(); - while( iter.hasNext() ) { - ssn = (SmbSession)iter.next(); - ssn.logoff( inError ); - } - if( socket != null ) { - try { - socket.close(); - } catch( IOException ioe ) { - } - } - in = null; - out = null; - socket = null; - negotiated = false; - thread = null; - responseTable.clear(); - } - public void run() { - int mid, l, i, n, m; - ServerMessageBlock response; - int magic[] = { 0xFF, 'S', 'M', 'B' }; - - while( thread == Thread.currentThread() ) { - try { - socket.setSoTimeout( soTimeout ); - - m = 0; - while( m < 4 ) { - if(( i = in.read() ) < 0 ) { - return; - } - if(( i & 0xFF ) == magic[m] ) { - m++; - } else if(( i & 0xFF ) == 0xFF ) { - m = 1; - } else { - m = 0; - } - } - -synchronized( rcv_buf ) { - rcv_buf[0] = (byte)0xFF; - rcv_buf[1] = (byte)'S'; - rcv_buf[2] = (byte)'M'; - rcv_buf[3] = (byte)'B'; - - if( in.read( rcv_buf, 4, HEADER_LENGTH - 4 ) != ( HEADER_LENGTH - 4 )) { - /* Read on a netbios SocketInputStream does not - * return until len bytes have been read or the stream is - * closed. This must be the latter case. - */ - break; - } - ((PushbackInputStream)in).unread( rcv_buf, 0, HEADER_LENGTH ); - if( rcv_buf[0] != (byte)0xFF || - rcv_buf[1] != (byte)'S' || - rcv_buf[2] != (byte)'M' || - rcv_buf[3] != (byte)'B' ) { - Log.println( Log.WARNINGS, "smb warning", - "bad smb header, purging session message" ); - in.skip( in.available() ); - continue; - } - if(( rcv_buf[FLAGS_OFFSET] & FLAGS_RESPONSE ) == FLAGS_RESPONSE ) { - mid = ServerMessageBlock.readInt2( rcv_buf, MID_OFFSET ); - - response = (ServerMessageBlock)responseTable.get( new Integer( mid )); - if( response == null ) { - Log.println( Log.WARNINGS, "smb warning", - " no handler for mid=" + mid + ", purging session message" ); - in.skip( in.available() ); - continue; - } - synchronized( response ) { - response.useUnicode = useUnicode; - Log.println( Log.DEBUGGING, "smb transport warning", - " new data read from socket" ); - - if( response instanceof SmbComTransactionResponse ) { - Enumeration e = (Enumeration)response; - if( e.hasMoreElements() ) { - e.nextElement(); - } else { - Log.println( Log.WARNINGS, "smb warning", - "more responses to transaction than expected" ); - continue; - } - response.readWireFormat( in, rcv_buf, 0 ); - response.received = true; - Log.printMessageData( "smb received", response ); - - if( response.errorCode != 0 || e.hasMoreElements() == false ) { - ((SmbComTransactionResponse)response).hasMore = false; - response.notify(); - } else { - ensureOpen(); - } - } else { - response.readWireFormat( in, rcv_buf, 0 ); - response.received = true; - - Log.printMessageData( "smb received", response ); - ServerMessageBlock smb = response; - while( smb instanceof AndXServerMessageBlock && - ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) { - Log.printMessageData( "smb andx data", smb ); - } - Log.printHexDump( "smb received", rcv_buf, 0, response.length ); - - response.notify(); - } - } - } else { - // it's a request(break oplock) - } -} - } catch( InterruptedIOException iioe ) { - if( responseTable.size() == 0 ) { - tryClose( false ); - } else { - Log.println( Log.WARNINGS, "smb warning", - " soTimeout has occured but there are " + - responseTable.size() + " pending requests" ); - } - } catch( IOException ioe ) { - tryClose( true ); - Log.printStackTrace( "exception reading from socket input: " + address, ioe ); - } - } - } - void send( ServerMessageBlock request, - ServerMessageBlock response ) throws SmbException { - Integer mid = null; - - negotiate(); - - request.flags2 = client.flags2; - request.mid = aquireMid(); - request.useUnicode = useUnicode; - - if( response == null ) { - try { - synchronized( outLock ) { - ensureOpen(); -synchronized( snd_buf ) { - out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); - out.flush(); - - Log.printMessageData( "smb sent", request ); - ServerMessageBlock smb = request; - while( smb instanceof AndXServerMessageBlock && - ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) { - Log.printMessageData( "smb andx data", smb ); - } - Log.printHexDump( "smb sent", snd_buf, 0, request.length ); -} - } - } catch( IOException ioe ) { - tryClose( true ); - throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); - } finally { - releaseMid( request.mid ); - } - - return; - } - - // now for the normal case where response is not null - - try { - synchronized( response ) { - - response.received = false; - mid = new Integer( request.mid ); - responseTable.put( mid, response ); - - synchronized( outLock ) { - ensureOpen(); -synchronized( snd_buf ) { - out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); - out.flush(); - - Log.printMessageData( "smb sent", request ); - ServerMessageBlock smb = request; - while( smb instanceof AndXServerMessageBlock && - ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) { - Log.printMessageData( "smb andx data", smb ); - } - Log.printHexDump( "smb sent", snd_buf, 0, request.length ); -} - } - - // default it 1 so that 0 can be used as forever - response.wait( response.responseTimeout == 1 ? - responseTimeout : response.responseTimeout ); - } - } catch( InterruptedException ie ) { - tryClose( true ); - } catch( IOException ioe ) { - tryClose( true ); - throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); - } finally { - responseTable.remove( mid ); - releaseMid( request.mid ); - } - - if( response.received == false ) { - tryClose( true ); - throw new SmbException( SmbException.ERRCLI, - SmbException.ERRserverTimeout, - address ); - } - switch( response.errorCode & 0xFF ) { - case SmbException.SUCCESS: - break; - case SmbException.ERRDOS: - switch(( response.errorCode >> 16 ) & 0xFFFF ) { - case SmbException.ERRnoaccess: - throw new SmbAuthException( response.errorCode ); - } - throw new SmbException( response.errorCode ); - case SmbException.ERRSRV: - switch(( response.errorCode >> 16 ) & 0xFFFF ) { - case SmbException.ERRbadpw: - case SmbException.ERRaccess: - case SmbException.ERRaccountExpired: - case SmbException.ERRbadClient: - case SmbException.ERRbadLogonTime: - case SmbException.ERRpasswordExpired: - throw new SmbAuthException( response.errorCode ); - } - default: - throw new SmbException( response.errorCode ); - } - - } - void sendTransaction( SmbComTransaction request, - SmbComTransactionResponse response ) throws SmbException { - Integer mid = null; - - negotiate(); - - request.flags2 = client.flags2; - request.mid = aquireMid(); - mid = new Integer( request.mid ); - request.useUnicode = useUnicode; - request.maxBufferSize = negotiatedMaxBufferSize; - response.received = false; - response.hasMore = true; - response.isPrimary = true; - - try { - request.txn_buf = BufferCache.getBuffer(); - response.txn_buf = BufferCache.getBuffer(); - - request.nextElement(); - if( request.hasMoreElements() ) { - // multi-part request - - SmbComBlankResponse interimResponse = new SmbComBlankResponse(); - - synchronized( interimResponse ) { - - responseTable.put( mid, interimResponse ); - - synchronized( outLock ) { - ensureOpen(); -synchronized(snd_buf) { - out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); - out.flush(); - - Log.printMessageData( "smb sent", request ); - Log.printHexDump( "smb sent", snd_buf, 0, request.length ); -} - } - - interimResponse.wait( responseTimeout ); - - if( interimResponse.received == false ) { - throw new SmbException( SmbException.ERRserverTimeout ); - } - switch( interimResponse.errorCode & 0xFF ) { - case SmbException.SUCCESS: - break; - case SmbException.ERRDOS: - switch(( response.errorCode >> 16 ) & 0xFFFF ) { - case SmbException.ERRnoaccess: - throw new SmbAuthException( response.errorCode ); - } - throw new SmbException( response.errorCode ); - case SmbException.ERRSRV: - switch(( interimResponse.errorCode >> 16 ) & 0xFFFF ) { - case SmbException.ERRbadpw: - case SmbException.ERRaccess: - case SmbException.ERRaccountExpired: - case SmbException.ERRbadClient: - case SmbException.ERRbadLogonTime: - case SmbException.ERRpasswordExpired: - throw new SmbAuthException( interimResponse.errorCode ); - } - default: - throw new SmbException( interimResponse.errorCode ); - } - } - request.nextElement(); - } - - synchronized( response ) { - responseTable.put( mid, response ); - do { - synchronized( outLock ) { - ensureOpen(); -synchronized( snd_buf ) { - out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); - out.flush(); - - Log.printMessageData( "smb sent", request ); - Log.printHexDump( "smb sent", snd_buf, 0, request.length ); -} - } - } while( request.hasMoreElements() && request.nextElement() != null ); - - do { - // default it 1 so that 0 can be used as forever - response.received = false; - response.wait( response.responseTimeout == 1 ? responseTimeout : response.responseTimeout ); - } while( response.received && response.hasMoreElements() ); - } - } catch( InterruptedException ie ) { - tryClose( true ); - } catch( IOException ioe ) { - tryClose( true ); - throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); - } finally { - responseTable.remove( mid ); - releaseMid( request.mid ); - BufferCache.releaseBuffer( request.txn_buf ); - BufferCache.releaseBuffer( response.txn_buf ); - } - - if( response.received == false ) { - tryClose( true ); - throw new SmbException( SmbException.ERRCLI, - SmbException.ERRserverTimeout, - address ); - } - switch( response.errorCode & 0xFF ) { - case SmbException.SUCCESS: - break; - case SmbException.ERRDOS: - switch(( response.errorCode >> 16 ) & 0xFFFF ) { - case SmbException.ERRnoaccess: - throw new SmbAuthException( response.errorCode ); - } - throw new SmbException( response.errorCode ); - case SmbException.ERRSRV: - switch(( response.errorCode >> 16 ) & 0xFFFF ) { - case SmbException.ERRbadpw: - case SmbException.ERRaccess: - case SmbException.ERRaccountExpired: - case SmbException.ERRbadClient: - case SmbException.ERRbadLogonTime: - case SmbException.ERRpasswordExpired: - throw new SmbAuthException( response.errorCode ); - } - default: - throw new SmbException( response.errorCode ); - } - } - synchronized void negotiate() throws SmbException { - - if( negotiated ) { - return; - } - /* we must set this here rather than later because this - * calls send() which calls negotiate so we don't want to - * end up in a loop. The alternative would be to inline - * the send routine here but this seems okay for now - */ - negotiated = true; - - Log.println( Log.WARNINGS, "smb negotiation warning", - " requesting negotiation with " + address ); - - /* - * Negotiate Protocol Request / Response - */ - - SmbComNegotiateResponse response = new SmbComNegotiateResponse(); - send( new SmbComNegotiate(), response ); - - if( response.dialectIndex > 10 ) { - tryClose( true ); - throw new SmbException( SmbException.ERRCLI, SmbException.ERRbadDialect ); - } - - server.securityMode = response.securityMode; - server.security = response.security; - server.encryptedPasswords = response.encryptedPasswords; - server.signaturesEnabled = response.signaturesEnabled; - server.signaturesRequired = response.signaturesRequired; - negotiatedDialectIndex = response.dialectIndex;; - server.maxMpxCount = response.maxMpxCount; - negotiatedMaxMpxCount = client.maxMpxCount < server.maxMpxCount ? - client.maxMpxCount : server.maxMpxCount; - mpxCtrl.maxMpxCount = negotiatedMaxMpxCount < 1 ? - 1 : negotiatedMaxMpxCount; - server.maxNumberVcs = response.maxNumberVcs; - server.maxBufferSize = response.maxBufferSize; - negotiatedMaxBufferSize = client.maxBufferSize < server.maxBufferSize ? - client.maxBufferSize : server.maxBufferSize; - server.maxRawSize = response.maxRawSize; - server.sessionKey = response.sessionKey; - server.capabilities = response.capabilities; - negotiatedCapabilities = client.capabilities & server.capabilities; - if(( negotiatedCapabilities & ServerMessageBlock.CAP_UNICODE ) == 0 ) { - // server doesn't want unicode - if( Config.getBoolean( "jcifs.smb.client.useUnicode", false )) { - // force unicode - negotiatedCapabilities |= ServerMessageBlock.CAP_UNICODE; - } else { - // not explicitly set to true so flip unicode off as server requests - useUnicode = false; - client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE; - } - } - server.serverTime = response.serverTime; - server.serverTimeZone = response.serverTimeZone; - server.encryptionKeyLength = response.encryptionKeyLength; - server.encryptionKey = response.encryptionKey; - server.oemDomainName = response.oemDomainName; - } - public String toString() { - String ret = "SmbTransport[address=" + address; - if( socket == null ) { - ret += ",port=,localAddr=,localPort=]"; - } else { - ret += ",port=" + socket.getPort() + - ",localAddr=" + socket.getLocalAddress() + - ",localPort=" + socket.getLocalPort() + "]"; - } - return ret; - } - - static int aquireMid() throws SmbException { - try { - return mpxCtrl.aquireMid(); - } catch( InterruptedException ie ) { - throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ie.getMessage() ); - } - } - static void releaseMid( int mid ) { - mpxCtrl.releaseMid( mid ); - } - - static class MpxControl { - MpxListNode first, last; - class MpxListNode { - int i; - MpxListNode next; - MpxListNode( int id ) { - i = id; - next = null; - } - } - int nextMid, mpxCount, maxMpxCount; - - MpxControl() { - nextMid = 0; - mpxCount = 0; - maxMpxCount = 1; - first = last = null; - } - - synchronized void clear() { - nextMid = 0; - mpxCount = 0; - maxMpxCount = 1; - first = last = null; - } - synchronized int aquireMid() throws InterruptedException { - while( mpxCount >= maxMpxCount ) { - wait(); - } - if(( ++nextMid % 0xFFFF ) == 0 ) { - nextMid = 1; - } - add( nextMid ); - mpxCount++; - return nextMid; - } - synchronized void releaseMid( int i ) { - remove( i ); - mpxCount--; - notify(); - } - synchronized boolean contains( int i ) { - for( MpxListNode tmp = first; tmp != null; tmp = tmp.next ) { - if( tmp.i == i ) { - return true; - } - } - return false; - } - synchronized void add( int i ) { - if( contains( i )) { - return; - } - if( first == null ) { - first = last = new MpxListNode( i ); - } else { - last = last.next = new MpxListNode( i ); - } - } - synchronized void remove( int i ) { - if( first == null ) { - return; - } else if( first == last && first.i == i ) { - first = last = null; - return; - } - MpxListNode tmp, prev; - for( tmp = prev = first; tmp != null; tmp = tmp.next ) { - if( tmp.i == i ) { - if( tmp == first ) { - first = first.next; - } else if( tmp == last ) { - last = prev; - last.next = null; - } else { - prev.next = tmp.next; - } - return; - } - prev = tmp; - } - } - } -}