jcifs-1.3.0 from tgz
authorFelix Schumacher <p0354740@isib001.(none)>
Thu, 6 Nov 2008 12:19:14 +0000 (13:19 +0100)
committerFelix Schumacher <p0354740@isib001.(none)>
Thu, 6 Nov 2008 12:19:14 +0000 (13:19 +0100)
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).

21 files changed:
README.txt
build.xml
examples/runtests.sh
src/jcifs/http/NtlmHttpFilter.java
src/jcifs/http/NtlmHttpURLConnection.java
src/jcifs/netbios/NameServiceClient.java
src/jcifs/netbios/NameServiceClient.java-2008-02-05 [deleted file]
src/jcifs/ntlmssp/Type1Message.java
src/jcifs/ntlmssp/Type3Message.java
src/jcifs/smb/Dfs.java
src/jcifs/smb/NtStatus.java
src/jcifs/smb/NtlmContext.java [new file with mode: 0644]
src/jcifs/smb/NtlmPasswordAuthentication.java
src/jcifs/smb/ServerMessageBlock.java
src/jcifs/smb/SigningDigest.java
src/jcifs/smb/SmbComNegotiateResponse.java
src/jcifs/smb/SmbComSessionSetupAndX.java
src/jcifs/smb/SmbComSessionSetupAndXResponse.java
src/jcifs/smb/SmbConstants.java
src/jcifs/smb/SmbSession.java
src/jcifs/smb/SmbTransport.java

index 217f899..8ae86df 100644 (file)
@@ -1,3 +1,21 @@
+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
 
index 3cbd385..3d1a159 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -1,7 +1,7 @@
 <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>
index 4c83ba3..c408eed 100644 (file)
@@ -1,8 +1,8 @@
 #!/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
@@ -13,10 +13,14 @@ RUN="${JAVA_HOME}/bin/java -cp ${CLASSPATH} -Djcifs.properties=${PROPERTIES}"
 #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
index fd67008..d4a5b28 100644 (file)
@@ -60,10 +60,16 @@ public class NtlmHttpFilter implements Filter {
         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() ) {
index a539ecc..dcd8adc 100644 (file)
@@ -564,7 +564,7 @@ public class NtlmHttpURLConnection extends HttpURLConnection {
             }
             Type2Message type2 = (Type2Message) message;
             message = new Type3Message(type2, password, domain, user,
-                    Type3Message.getDefaultWorkstation());
+                    Type3Message.getDefaultWorkstation(), 0);
         }
         return message;
     }
index 80f6ab9..7d79254 100644 (file)
@@ -84,7 +84,7 @@ class NameServiceClient implements Runnable {
         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.
              */
diff --git a/src/jcifs/netbios/NameServiceClient.java-2008-02-05 b/src/jcifs/netbios/NameServiceClient.java-2008-02-05
deleted file mode 100644 (file)
index 00f4d3a..0000000
+++ /dev/null
@@ -1,431 +0,0 @@
-/* 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 );
-    }
-}
index 2c5c829..754575b 100644 (file)
@@ -71,8 +71,10 @@ public class Type1Message extends NtlmMessage {
      */
     public Type1Message(int flags, String suppliedDomain,
             String suppliedWorkstation) {
-        setFlags(flags);
+        setFlags(getDefaultFlags() | flags);
         setSuppliedDomain(suppliedDomain);
+        if (suppliedWorkstation == null)
+            suppliedWorkstation = getDefaultWorkstation();
         setSuppliedWorkstation(suppliedWorkstation);
     }
 
index 83a5762..5ad1b22 100644 (file)
@@ -30,6 +30,11 @@ import jcifs.Config;
 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.
@@ -60,7 +65,8 @@ public class Type3Message extends NtlmMessage {
 
     private String workstation;
 
-    private byte[] sessionKey;
+    private byte[] masterKey = null;
+    private byte[] sessionKey = null;
 
     static {
         DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |
@@ -75,7 +81,7 @@ public class Type3Message extends NtlmMessage {
             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);
     }
 
     /**
@@ -143,16 +149,66 @@ public class Type3Message extends NtlmMessage {
      * 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);
@@ -162,14 +218,38 @@ public class Type3Message extends NtlmMessage {
         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));
@@ -299,6 +379,16 @@ public class Type3Message extends NtlmMessage {
     }
 
     /**
+     * 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.
@@ -485,6 +575,18 @@ public class Type3Message extends NtlmMessage {
         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
index 1c0af43..5778981 100644 (file)
@@ -50,7 +50,7 @@ public class Dfs {
     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) {
@@ -125,7 +125,7 @@ public class Dfs {
             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;
index 1df7e48..02aebf9 100644 (file)
@@ -82,6 +82,7 @@ public interface NtStatus {
     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,
@@ -142,6 +143,7 @@ public interface NtStatus {
         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 = {
@@ -203,6 +205,7 @@ public interface NtStatus {
         "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",
     };
 }
 
diff --git a/src/jcifs/smb/NtlmContext.java b/src/jcifs/smb/NtlmContext.java
new file mode 100644 (file)
index 0000000..04d761a
--- /dev/null
@@ -0,0 +1,97 @@
+/* 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;
+    }
+}
index c7b977d..fddff77 100644 (file)
@@ -22,6 +22,8 @@ package jcifs.smb;
 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;
@@ -40,7 +42,7 @@ import jcifs.util.*;
 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();
 
@@ -51,6 +53,10 @@ public final class NtlmPasswordAuthentication implements Principal, Serializable
         (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];
@@ -156,6 +162,102 @@ public final class NtlmPasswordAuthentication implements Principal, Serializable
             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( "", "", "" );
@@ -335,6 +437,27 @@ public final class NtlmPasswordAuthentication implements Principal, Serializable
         }
     }
 
+    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.
      * 
@@ -363,44 +486,47 @@ public final class NtlmPasswordAuthentication implements Principal, Serializable
      * @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);
+        }
     } 
 
 /**
index 875dc08..fcb4b99 100644 (file)
@@ -176,7 +176,7 @@ abstract class ServerMessageBlock extends Response implements Request, SmbConsta
         flags2,
         tid, pid, uid, mid,
         wordCount, byteCount;
-    boolean useUnicode, received;
+    boolean useUnicode, received, extendedSecurity;
     long responseTimeout = 1;
     int signSeq;
     boolean verifyFailed;
@@ -267,6 +267,45 @@ Hexdump.hexdump( System.err, src, srcIndex, maxLen < 128 ? maxLen + 8 : 128 );
         }
         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 ) {
index ae32161..0bde148 100644 (file)
@@ -17,9 +17,30 @@ public class SigningDigest implements SmbConstants {
 
     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 {
@@ -114,6 +135,10 @@ public class SigningDigest implements SmbConstants {
             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 );
index d846e60..9b2d770 100644 (file)
@@ -66,40 +66,47 @@ class SmbComNegotiateResponse extends ServerMessageBlock {
                                     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;
@@ -122,9 +129,6 @@ class SmbComNegotiateResponse extends ServerMessageBlock {
             ",serverTimeZone="      + server.serverTimeZone +
             ",encryptionKeyLength=" + server.encryptionKeyLength +
             ",byteCount="           + byteCount +
-            ",encryptionKey=0x"     + Hexdump.toHexString( server.encryptionKey,
-                                                0,
-                                                server.encryptionKeyLength * 2 ) +
             ",oemDomainName="       + server.oemDomainName + "]" );
     }
 }
index 99b3b51..04d3fdf 100644 (file)
@@ -28,65 +28,66 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock {
     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;
@@ -96,10 +97,15 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock {
         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;
@@ -112,26 +118,18 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock {
     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 );
 
@@ -150,8 +148,8 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock {
             ",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 +
index 37748c7..7f7555d 100644 (file)
@@ -27,6 +27,7 @@ class SmbComSessionSetupAndXResponse extends AndXServerMessageBlock {
     private String primaryDomain = "";
 
     boolean isLoggedInAsGuest;
+    byte[] blob = null;
 
     SmbComSessionSetupAndXResponse( ServerMessageBlock andx ) {
         super( andx );
@@ -39,41 +40,30 @@ class SmbComSessionSetupAndXResponse extends AndXServerMessageBlock {
         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;
index 439b634..a003582 100644 (file)
@@ -26,10 +26,10 @@ interface SmbConstants {
     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;
@@ -62,6 +62,7 @@ interface SmbConstants {
     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;
index 67551d0..fc0944e 100644 (file)
@@ -25,6 +25,7 @@ import java.net.UnknownHostException;
 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
@@ -249,7 +250,12 @@ do {
     }
     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 ) {
@@ -265,48 +271,125 @@ synchronized( transport() ) {
         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 ) {
@@ -321,17 +404,17 @@ synchronized( transport() ) {
         }
 
         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;
index e49853a..6677656 100644 (file)
@@ -82,6 +82,7 @@ public class SmbTransport extends Transport implements SmbConstants {
         int serverTimeZone;
         int encryptionKeyLength;
         byte[] encryptionKey;
+        byte[] guid;
     }
 
     InetAddress localAddr;
@@ -314,8 +315,10 @@ public class SmbTransport extends Transport implements SmbConstants {
         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 */
@@ -330,6 +333,9 @@ public class SmbTransport extends Transport implements SmbConstants {
         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) {
@@ -444,6 +450,7 @@ public class SmbTransport extends Transport implements SmbConstants {
     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 );
@@ -523,6 +530,8 @@ public class SmbTransport extends Transport implements SmbConstants {
                 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 );
         }