-Wed Aug 15 12:47:55 EDT 2007
-jcifs-1.2.17 released
+Wed Feb 6 00:05:42 EST 2008
+jcifs-1.2.18e released
-A try/catch that was accedentally added in a previous release has been
-removed.
+The SID.getServerSid() method could fail with NetApp servers due to a
+"generic" mask values. The mask has been changed to 0x00000001 which
+corresponds to an LsaOpenPolicy mask of POLICY_VIEW_LOCAL_INFORMATION.
-Thu Aug 2 12:00:00 EDT 2007
-jcifs-1.2.16 released
+The LsaPolicyHandle class would not throw an error if the LsarOpenPolicy2
+call failed. This has been fixed.
-With this release, JCIFS now supports domain-based DFS. With
-domain-based DFS, clients access DFS roots under the DNS domain name
-like \\example.com\dfs\foo so that users do not need to remember server
-names. However, for clients to work with these DFS roots they have to
-be prepared to connect to each domain controller as necessary to find
-the target share and successfully authenticate. JCIFS now includes this
-retry logic. JCIFS will also do something that it seems even Windows
-clients do not do - if you list the shares of a domain (e.g. (new
-SmbFile("smb://example.com/")).listFiles()), JCIFS will build a merged
-list of all shares on all domain controllers.
+The SmbFile constructor could inappropriately URL decode the authority
+component of SMB URLs.
-Note that these changes are fairly significant. Whenever JCIFS tries to
-connect to a server this new logic is used. So if anyone notices anything
-out of the ordinary please report it to the JCIFS mailaing list.
+The NTLM HTTP Filter documentation has been updated.
-Mon Jul 16 13:26:26 EDT 2007
-jcifs-1.2.15 released
+An Invalid state: 4 error has been fixed.
-This release includes some significant changes. Most of these changes
-are related to NetApp compatibility. The changes in this release are:
+A NetBIOS name service issue caused by Jetdirect printers has been fixed.
-An SMB signing failure related to DFS has been fixed.
+An ArrayIndexOutOfBounds exception in the SmbException class has been
+fixed.
+
+A NullPointerException in SmbSession.getChallengeForDomain() has been
+fixed.
+
+A NullPointerException in NbtAddress related to hosts without adequate
+localhost address configuration has been fixed.
+
+An ArrayIndexOutOfBounds exception could be thrown if a server requires
+NTLMv2. This exception has been replaced with a more informative one.
+
+The SmbSessionSetup constructor will now compare the challenge and
+encryptionKey using Arrays.equals instead of == to satisfy unforseen
+use-cases that otherwise trigger an NT_STATUS_ACCESS_VIOLATION.
+
+If a share was unshared while JCIFS was in the middle of reading files
+from it, the transport could enter an error state from which it could
+not immediately recover if the share was restored. A small change to
+SmbTransport.doRecv() fixes this problem.
+
+Tue Jun 26 16:11:31 EDT 2007
The DCERPC bind did not exactly mimic Windows which uses
SMB_COM_{WRITE,READ}_ANDX. We were using TransactNmPipe throughout which
a NetApp server. JCIFS now implements the bind exactly like Windows to
help ensure compatibility with other servers.
-Other changes related to NetApp compatibility include falling back to
-SamrConnect2 if an DCERPC_FAULT_OP_RNG_ERROR error occurs, more closely
-mimicing the SMB_COM_NT_CREATE_ANDX "extended" response, adjusting various
-RPC handle operation access masks, uncommenting some padding code that
-was commented out for what appeared to be a NetWare problem, disabling
-some logic to used port 139 if the jcifs.netbios.hostname was set and
-finally adding code to include LsarQosInfo structures in the MSRPC bind.
-
-Some new error code information has been added.
-
-Constants for common SIDs have been added to the SID class.
-
-The SID.getGroupMemberSids() method will now return an empty SID array
-if the SID is not of type SID_TYPE_DOM_GRP or SID_TYPE_ALIAS.
-
A minor performance flaw in the DCERPC code was found and fixed.
Wed Jun 20 13:09:10 EDT 2007
<project name="jcifs" default="usage" basedir=".">
- <property name="version" value="1.2.17"/>
- <property name="reldate" value="Aug 15, 2007"/>
+ <property name="version" value="1.2.18e"/>
+ <property name="reldate" value="Feb 6, 2008"/>
<target name="usage">
<echo>
--- /dev/null
+import java.util.*;
+import jcifs.smb.*;
+
+public class GetLocalGroupMemberSidsFromURL {
+
+ public static void main( String[] argv ) throws Exception {
+ if (argv.length < 1) {
+ System.err.println("usage: GetLocalGroupMemberSidsFromURL <smburl>");
+ System.exit(1);
+ }
+
+ SmbFile file = new SmbFile(argv[0]);
+ String server = file.getServer();
+ NtlmPasswordAuthentication auth = (NtlmPasswordAuthentication)file.getPrincipal();
+ ACE[] security = file.getSecurity(true);
+
+ SID serverSid = SID.getServerSid(server, auth);
+
+ for (int ai = 0; ai < security.length; ai++) {
+ ACE ace = security[ai];
+ SID sid = ace.getSID();
+
+ if (sid.getType() == SID.SID_TYPE_ALIAS && serverSid.equals(sid.getDomainSid())) {
+ System.out.println(sid.toString() + " (" + sid.toDisplayString() + ") members:");
+
+ SID[] mems = sid.getGroupMemberSids(server, auth, SID.SID_FLAG_RESOLVE_SIDS);
+ for (int mi = 0; mi < mems.length; mi++) {
+ SID mem = mems[mi];
+ System.out.println(" " + mem.getType() + " " + mem.toDisplayString());
+ }
+ }
+ }
+ }
+}
--- /dev/null
+import java.util.*;
+import jcifs.smb.*;
+
+public class GetServerSidFromURL {
+
+ public static void main( String[] argv ) throws Exception {
+ if (argv.length < 1) {
+ System.err.println("usage: GetServerSidFromURL <smburl>");
+ System.exit(1);
+ }
+
+ SmbFile file = new SmbFile(argv[0]);
+ String server = file.getServer();
+ NtlmPasswordAuthentication auth = (NtlmPasswordAuthentication)file.getPrincipal();
+
+ SID serverSid = SID.getServerSid(server, auth);
+
+ System.out.println(serverSid);
+ }
+}
import java.io.IOException;
+import jcifs.smb.SmbException;
+
import jcifs.dcerpc.*;
public class LsaPolicyHandle extends rpc.policy_handle {
server = "\\\\";
MsrpcLsarOpenPolicy2 rpc = new MsrpcLsarOpenPolicy2(server, access, this);
handle.sendrecv(rpc);
+ if (rpc.retval != 0)
+ throw new SmbException(rpc.retval, false);
}
public void close() throws IOException {
--- /dev/null
+/* jcifs msrpc client library in Java
+ * Copyright (C) 2007 "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.dcerpc.msrpc;
+
+import jcifs.smb.*;
+import jcifs.dcerpc.*;
+import jcifs.dcerpc.ndr.*;
+
+public class MsrpcQueryInformationPolicy extends lsarpc.LsarQueryInformationPolicy {
+
+ public MsrpcQueryInformationPolicy(LsaPolicyHandle policyHandle,
+ short level,
+ NdrObject info) {
+ super(policyHandle, level, info);
+ ptype = 0;
+ flags = DCERPC_FIRST_FRAG | DCERPC_LAST_FRAG;
+ }
+}
+++ /dev/null
-/* jcifs msrpc client library in Java
- * Copyright (C) 2007 "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.dcerpc.msrpc;
-
-import jcifs.smb.*;
-import jcifs.dcerpc.*;
-import jcifs.dcerpc.ndr.*;
-
-public class MsrpcQueryInformationPolicy2 extends lsarpc.LsarQueryInformationPolicy2 {
-
- public MsrpcQueryInformationPolicy2(LsaPolicyHandle policyHandle,
- short level,
- NdrObject info) {
- super(policyHandle, level, info);
- ptype = 0;
- flags = DCERPC_FIRST_FRAG | DCERPC_LAST_FRAG;
- }
-}
}
}
- response.wait( timeout );
+ long start = System.currentTimeMillis();
+ while (timeout > 0) {
+ response.wait( timeout );
+
+ /* JetDirect printer can respond to regular broadcast query
+ * with node status so we need to check to make sure that
+ * the record type matches the question type and if not,
+ * loop around and try again.
+ */
+ if (response.received && request.questionType == response.recordType)
+ return;
- if (response.received)
- return;
+ response.received = false;
+ timeout -= System.currentTimeMillis() - start;
+ }
} catch( InterruptedException ie ) {
} finally {
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.netbios;
+
+import java.net.*;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import jcifs.Config;
+import jcifs.util.Hexdump;
+import jcifs.util.LogStream;
+
+class NameServiceClient implements Runnable {
+
+ static final int DEFAULT_SO_TIMEOUT = 5000;
+ static final int DEFAULT_RCV_BUF_SIZE = 576;
+ static final int DEFAULT_SND_BUF_SIZE = 576;
+ static final int NAME_SERVICE_UDP_PORT = 137;
+ static final int DEFAULT_RETRY_COUNT = 2;
+ static final int DEFAULT_RETRY_TIMEOUT = 3000;
+
+ static final int RESOLVER_LMHOSTS = 1;
+ static final int RESOLVER_BCAST = 2;
+ static final int RESOLVER_WINS = 3;
+
+ private static final int SND_BUF_SIZE = Config.getInt( "jcifs.netbios.snd_buf_size", DEFAULT_SND_BUF_SIZE );
+ private static final int RCV_BUF_SIZE = Config.getInt( "jcifs.netbios.rcv_buf_size", DEFAULT_RCV_BUF_SIZE );
+ private static final int SO_TIMEOUT = Config.getInt( "jcifs.netbios.soTimeout", DEFAULT_SO_TIMEOUT );
+ private static final int RETRY_COUNT = Config.getInt( "jcifs.netbios.retryCount", DEFAULT_RETRY_COUNT );
+ private static final int RETRY_TIMEOUT = Config.getInt( "jcifs.netbios.retryTimeout", DEFAULT_RETRY_TIMEOUT);
+ private static final int LPORT = Config.getInt( "jcifs.netbios.lport", 0 );
+ private static final InetAddress LADDR = Config.getInetAddress( "jcifs.netbios.laddr", null );
+ private static final String RO = Config.getProperty( "jcifs.resolveOrder" );
+
+ private static LogStream log = LogStream.getInstance();
+
+ private final Object LOCK = new Object();
+
+ private int lport, closeTimeout;
+ private byte[] snd_buf, rcv_buf;
+ private DatagramSocket socket;
+ private DatagramPacket in, out;
+ private HashMap responseTable = new HashMap();
+ private Thread thread;
+ private int nextNameTrnId = 0;
+ private int[] resolveOrder;
+
+ InetAddress laddr, baddr;
+
+ NameServiceClient() {
+ this( LPORT, LADDR );
+ }
+ NameServiceClient( int lport, InetAddress laddr ) {
+ this.lport = lport;
+ this.laddr = laddr;
+
+ try {
+ baddr = Config.getInetAddress( "jcifs.netbios.baddr",
+ InetAddress.getByName( "255.255.255.255" ));
+ } catch( UnknownHostException uhe ) {
+ }
+
+ snd_buf = new byte[SND_BUF_SIZE];
+ rcv_buf = new byte[RCV_BUF_SIZE];
+ out = new DatagramPacket( snd_buf, SND_BUF_SIZE, baddr, NAME_SERVICE_UDP_PORT );
+ in = new DatagramPacket( rcv_buf, RCV_BUF_SIZE );
+
+ if( RO == null || RO.length() == 0 ) {
+
+ /* No resolveOrder has been specified, use the
+ * default which is LMHOSTS,WINS,BCAST,DNS or just
+ * LMHOSTS,BCAST,DNS if jcifs.netbios.wins has not
+ * been specified.
+ */
+
+ if( NbtAddress.getWINSAddress() == null ) {
+ resolveOrder = new int[2];
+ resolveOrder[0] = RESOLVER_LMHOSTS;
+ resolveOrder[1] = RESOLVER_BCAST;
+ } else {
+ resolveOrder = new int[3];
+ resolveOrder[0] = RESOLVER_LMHOSTS;
+ resolveOrder[1] = RESOLVER_WINS;
+ resolveOrder[2] = RESOLVER_BCAST;
+ }
+ } else {
+ int[] tmp = new int[3];
+ StringTokenizer st = new StringTokenizer( RO, "," );
+ int i = 0;
+ while( st.hasMoreTokens() ) {
+ String s = st.nextToken().trim();
+ if( s.equalsIgnoreCase( "LMHOSTS" )) {
+ tmp[i++] = RESOLVER_LMHOSTS;
+ } else if( s.equalsIgnoreCase( "WINS" )) {
+ if( NbtAddress.getWINSAddress() == null ) {
+ if( log.level > 1 ) {
+ log.println( "NetBIOS resolveOrder specifies WINS however the " +
+ "jcifs.netbios.wins property has not been set" );
+ }
+ continue;
+ }
+ tmp[i++] = RESOLVER_WINS;
+ } else if( s.equalsIgnoreCase( "BCAST" )) {
+ tmp[i++] = RESOLVER_BCAST;
+ } else if( s.equalsIgnoreCase( "DNS" )) {
+ ; // skip
+ } else if( log.level > 1 ) {
+ log.println( "unknown resolver method: " + s );
+ }
+ }
+ resolveOrder = new int[i];
+ System.arraycopy( tmp, 0, resolveOrder, 0, i );
+ }
+ }
+
+ int getNextNameTrnId() {
+ if(( ++nextNameTrnId & 0xFFFF ) == 0 ) {
+ nextNameTrnId = 1;
+ }
+ return nextNameTrnId;
+ }
+ void ensureOpen( int timeout ) throws IOException {
+ closeTimeout = 0;
+ if( SO_TIMEOUT != 0 ) {
+ closeTimeout = Math.max( SO_TIMEOUT, timeout );
+ }
+ // If socket is still good, the new closeTimeout will
+ // be ignored; see tryClose comment.
+ if( socket == null ) {
+ socket = new DatagramSocket( lport, laddr );
+ thread = new Thread( this, "JCIFS-NameServiceClient" );
+ thread.setDaemon( true );
+ thread.start();
+ }
+ }
+ void tryClose() {
+ synchronized( LOCK ) {
+
+ /* Yes, there is the potential to drop packets
+ * because we might close the socket during a
+ * request. However the chances are slim and the
+ * retry code should ensure the overall request
+ * is serviced. The alternative complicates things
+ * more than I think is worth it.
+ */
+
+ if( socket != null ) {
+ socket.close();
+ socket = null;
+ }
+ thread = null;
+ responseTable.clear();
+ }
+ }
+ public void run() {
+ int nameTrnId;
+ NameServicePacket response;
+
+ try {
+ while( thread == Thread.currentThread() ) {
+ in.setLength( RCV_BUF_SIZE );
+
+ socket.setSoTimeout( closeTimeout );
+ socket.receive( in );
+
+ if( log.level > 3 )
+ log.println( "NetBIOS: new data read from socket" );
+
+ nameTrnId = NameServicePacket.readNameTrnId( rcv_buf, 0 );
+ response = (NameServicePacket)responseTable.get( new Integer( nameTrnId ));
+ if( response == null || response.received ) {
+ continue;
+ }
+ synchronized( response ) {
+ response.readWireFormat( rcv_buf, 0 );
+ response.received = true;
+
+ if( log.level > 3 ) {
+ log.println( response );
+ Hexdump.hexdump( log, rcv_buf, 0, in.getLength() );
+ }
+
+ response.notify();
+ }
+ }
+ } catch(SocketTimeoutException ste) {
+ } catch( Exception ex ) {
+ if( log.level > 2 )
+ ex.printStackTrace( log );
+ } finally {
+ tryClose();
+ }
+ }
+ void send( NameServicePacket request, NameServicePacket response,
+ int timeout ) throws IOException {
+ Integer nid = null;
+ int max = NbtAddress.NBNS.length;
+
+ if (max == 0)
+ max = 1; /* No WINs, try only bcast addr */
+
+ synchronized( response ) {
+ while (max-- > 0) {
+ try {
+ synchronized( LOCK ) {
+ request.nameTrnId = getNextNameTrnId();
+ nid = new Integer( request.nameTrnId );
+
+ out.setAddress( request.addr );
+ out.setLength( request.writeWireFormat( snd_buf, 0 ));
+ response.received = false;
+
+ responseTable.put( nid, response );
+ ensureOpen( timeout + 1000 );
+ socket.send( out );
+
+ if( log.level > 3 ) {
+ log.println( request );
+ Hexdump.hexdump( log, snd_buf, 0, out.getLength() );
+ }
+ }
+
+ response.wait( timeout );
+
+ if (response.received)
+ return;
+
+ } catch( InterruptedException ie ) {
+ } finally {
+ responseTable.remove( nid );
+ }
+
+ if (NbtAddress.isWINS( request.addr ) == false)
+ break;
+
+ /* Message was sent to WINS but
+ * failed to receive response.
+ * Try a different WINS server.
+ */
+ if (request.addr == NbtAddress.getWINSAddress())
+ NbtAddress.switchWINS();
+ request.addr = NbtAddress.getWINSAddress();
+ }
+ }
+ }
+
+ NbtAddress[] getAllByName( Name name, InetAddress addr )
+ throws UnknownHostException {
+ int n;
+ NameQueryRequest request = new NameQueryRequest( name );
+ NameQueryResponse response = new NameQueryResponse();
+
+ request.addr = addr != null ? addr : NbtAddress.getWINSAddress();
+ request.isBroadcast = request.addr == null;
+
+ if( request.isBroadcast ) {
+ request.addr = baddr;
+ n = RETRY_COUNT;
+ } else {
+ request.isBroadcast = false;
+ n = 1;
+ }
+
+ do {
+ try {
+ send( request, response, RETRY_TIMEOUT );
+ } catch( IOException ioe ) {
+ if( log.level > 1 )
+ ioe.printStackTrace( log );
+ throw new UnknownHostException( name.name );
+ }
+
+ if( response.received && response.resultCode == 0 ) {
+ return response.addrEntry;
+ }
+ } while( --n > 0 && request.isBroadcast );
+
+ throw new UnknownHostException( name.name );
+ }
+ NbtAddress getByName( Name name, InetAddress addr )
+ throws UnknownHostException {
+ int n;
+ NameQueryRequest request = new NameQueryRequest( name );
+ NameQueryResponse response = new NameQueryResponse();
+
+ if( addr != null ) { /* UniAddress calls always use this
+ * because it specifies addr
+ */
+ request.addr = addr; /* if addr ends with 255 flag it bcast */
+ request.isBroadcast = (addr.getAddress()[3] == (byte)0xFF);
+
+ n = RETRY_COUNT;
+ do {
+ try {
+ send( request, response, RETRY_TIMEOUT );
+ } catch( IOException ioe ) {
+ if( log.level > 1 )
+ ioe.printStackTrace( log );
+ throw new UnknownHostException( name.name );
+ }
+
+ if( response.received && response.resultCode == 0 ) {
+ int last = response.addrEntry.length - 1;
+ response.addrEntry[last].hostName.srcHashCode = addr.hashCode();
+ return response.addrEntry[last];
+ }
+ } while( --n > 0 && request.isBroadcast );
+
+ throw new UnknownHostException( name.name );
+ }
+
+ /* If a target address to query was not specified explicitly
+ * with the addr parameter we fall into this resolveOrder routine.
+ */
+
+ for( int i = 0; i < resolveOrder.length; i++ ) {
+ try {
+ switch( resolveOrder[i] ) {
+ case RESOLVER_LMHOSTS:
+ NbtAddress ans = Lmhosts.getByName( name );
+ if( ans != null ) {
+ ans.hostName.srcHashCode = 0; // just has to be different
+ // from other methods
+ return ans;
+ }
+ break;
+ case RESOLVER_WINS:
+ case RESOLVER_BCAST:
+ if( resolveOrder[i] == RESOLVER_WINS &&
+ name.name != NbtAddress.MASTER_BROWSER_NAME &&
+ name.hexCode != 0x1d ) {
+ request.addr = NbtAddress.getWINSAddress();
+ request.isBroadcast = false;
+ } else {
+ request.addr = baddr;
+ request.isBroadcast = true;
+ }
+
+ n = RETRY_COUNT;
+ while( n-- > 0 ) {
+ try {
+ send( request, response, RETRY_TIMEOUT );
+ } catch( IOException ioe ) {
+ if( log.level > 1 )
+ ioe.printStackTrace( log );
+ throw new UnknownHostException( name.name );
+ }
+ if( response.received && response.resultCode == 0 ) {
+
+/* Before we return, in anticipation of this address being cached we must
+ * augment the addresses name's hashCode to distinguish those resolved by
+ * Lmhosts, WINS, or BCAST. Otherwise a failed query from say WINS would
+ * get pulled out of the cache for a BCAST on the same name.
+ */
+ response.addrEntry[0].hostName.srcHashCode =
+ request.addr.hashCode();
+ return response.addrEntry[0];
+ } else if( resolveOrder[i] == RESOLVER_WINS ) {
+ /* If WINS reports negative, no point in retry
+ */
+ break;
+ }
+ }
+ break;
+ }
+ } catch( IOException ioe ) {
+ }
+ }
+ throw new UnknownHostException( name.name );
+ }
+ NbtAddress[] getNodeStatus( NbtAddress addr ) throws UnknownHostException {
+ int n, srcHashCode;
+ NodeStatusRequest request;
+ NodeStatusResponse response;
+
+ response = new NodeStatusResponse( addr );
+ request = new NodeStatusRequest(
+ new Name( NbtAddress.ANY_HOSTS_NAME, 0x00, null));
+ request.addr = addr.getInetAddress();
+
+ n = RETRY_COUNT;
+ while( n-- > 0 ) {
+ try {
+ send( request, response, RETRY_TIMEOUT );
+ } catch( IOException ioe ) {
+ if( log.level > 1 )
+ ioe.printStackTrace( log );
+ throw new UnknownHostException( addr.toString() );
+ }
+ if( response.received && response.resultCode == 0 ) {
+
+ /* For name queries resolved by different sources (e.g. WINS,
+ * BCAST, Node Status) we need to augment the hashcode generated
+ * for the addresses hostname or failed lookups for one type will
+ * be cached and cause other types to fail even though they may
+ * not be the authority for the name. For example, if a WINS lookup
+ * for FOO fails and caches unknownAddress for FOO, a subsequent
+ * lookup for FOO using BCAST should not fail because of that
+ * name cached from WINS.
+ *
+ * So, here we apply the source addresses hashCode to each name to
+ * make them specific to who resolved the name.
+ */
+
+ srcHashCode = request.addr.hashCode();
+ for( int i = 0; i < response.addressArray.length; i++ ) {
+ response.addressArray[i].hostName.srcHashCode = srcHashCode;
+ }
+ return response.addressArray;
+ }
+ }
+ throw new UnknownHostException( addr.hostName.name );
+ }
+}
try {
localInetAddress = InetAddress.getLocalHost();
} catch( UnknownHostException uhe ) {
+ /* Java cannot determine the localhost. This is basically a config
+ * issue on the host. There's not much we can do about it. Just
+ * to suppress NPEs that would result we can create a possibly bogus
+ * address. Pretty sure the below cannot actually thrown a UHE tho.
+ */
+ try {
+ localInetAddress = InetAddress.getByName("127.0.0.1");
+ } catch( UnknownHostException ignored ) {
+ }
}
}
{ 0x08bf0002, 0xC0000193 },
{ 0x08c00002, 0xC0000070 },
{ 0x08c10002, 0xC000006f },
- { 0x08c20002, 0xC0000071 }
+ { 0x08c20002, 0xC0000071 },
};
/* These aren't really used by jCIFS -- the map above is used
"This user account has expired.",
"The user is not allowed to log on from this workstation.",
"The user is not allowed to log on at this time.",
- "The password of this user has expired."
+ "The password of this user has expired.",
};
}
import java.net.URLStreamHandler;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
-
import java.io.PrintStream;
public class Handler extends URLStreamHandler {
static final URLStreamHandler SMB_HANDLER = new Handler();
- static String unescape( String str ) throws NumberFormatException, UnsupportedEncodingException {
- char ch;
- int i, j, state, len;
- char[] out;
- byte[] b = new byte[1];
-
- if( str == null ) {
- return null;
- }
-
- len = str.length();
- out = new char[len];
- state = 0;
- for( i = j = 0; i < len; i++ ) {
- switch( state ) {
- case 0:
- ch = str.charAt( i );
- if( ch == '%' ) {
- state = 1;
- } else {
- out[j++] = ch;
- }
- break;
- case 1:
- /* Get ASCII hex value and convert to platform dependant
- * encoding like EBCDIC perhaps
- */
- b[0] = (byte)(Integer.parseInt( str.substring( i, i + 2 ), 16 ) & 0xFF);
- out[j++] = (new String( b, 0, 1, "ASCII" )).charAt( 0 );
- i++;
- state = 0;
- }
- }
-
- return new String( out, 0, j );
- }
-
protected int getDefaultPort() {
return SmbConstants.DEFAULT_PORT;
}
}
protected void parseURL( URL u, String spec, int start, int limit ) {
String host = u.getHost();
- String userinfo, path, ref;
+ String path, ref;
int port;
if( spec.equals( "smb://" )) {
limit += 2;
}
super.parseURL( u, spec, start, limit );
- userinfo = u.getUserInfo();
path = u.getPath();
ref = u.getRef();
- try {
- userinfo = unescape( userinfo );
- } catch( UnsupportedEncodingException uee ) {
- }
if (ref != null) {
path += '#' + ref;
}
port = getDefaultPort();
}
setURL( u, "smb", u.getHost(), port,
- u.getAuthority(), userinfo,
+ u.getAuthority(), u.getUserInfo(),
path, u.getQuery(), null );
}
}
domain = username = password = null;
if( userInfo != null ) {
+ try {
+ userInfo = unescape( userInfo );
+ } catch( UnsupportedEncodingException uee ) {
+ }
int i, u, end;
char c;
public String toString() {
return getName();
}
+
+ static String unescape( String str ) throws NumberFormatException, UnsupportedEncodingException {
+ char ch;
+ int i, j, state, len;
+ char[] out;
+ byte[] b = new byte[1];
+
+ if( str == null ) {
+ return null;
+ }
+
+ len = str.length();
+ out = new char[len];
+ state = 0;
+ for( i = j = 0; i < len; i++ ) {
+ switch( state ) {
+ case 0:
+ ch = str.charAt( i );
+ if( ch == '%' ) {
+ state = 1;
+ } else {
+ out[j++] = ch;
+ }
+ break;
+ case 1:
+ /* Get ASCII hex value and convert to platform dependant
+ * encoding like EBCDIC perhaps
+ */
+ b[0] = (byte)(Integer.parseInt( str.substring( i, i + 2 ), 16 ) & 0xFF);
+ out[j++] = (new String( b, 0, 1, "ASCII" )).charAt( 0 );
+ i++;
+ state = 0;
+ }
+ }
+
+ return new String( out, 0, j );
+ }
+
}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <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.UnsupportedEncodingException;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Random;
+import java.util.Arrays;
+import jcifs.Config;
+import jcifs.util.*;
+
+/**
+ * This class stores and encrypts NTLM user credentials. The default
+ * credentials are retrieved from the <tt>jcifs.smb.client.domain</tt>,
+ * <tt>jcifs.smb.client.username</tt>, and <tt>jcifs.smb.client.password</tt>
+ * properties.
+ * <p>
+ * Read <a href="../../../authhandler.html">jCIFS Exceptions and
+ * NtlmAuthenticator</a> for related information.
+ */
+
+public final class NtlmPasswordAuthentication implements Principal, Serializable {
+
+ private static final int LM_COMPATIBILITY =
+ Config.getInt("jcifs.smb.lmCompatibility", 0);
+
+ private static final Random RANDOM = new Random();
+
+ private static LogStream log = LogStream.getInstance();
+
+ // KGS!@#$%
+ private static final byte[] S8 = {
+ (byte)0x4b, (byte)0x47, (byte)0x53, (byte)0x21,
+ (byte)0x40, (byte)0x23, (byte)0x24, (byte)0x25
+ };
+ private static void E( byte[] key, byte[] data, byte[] e ) {
+ byte[] key7 = new byte[7];
+ byte[] e8 = new byte[8];
+
+ for( int i = 0; i < key.length / 7; i++ ) {
+ System.arraycopy( key, i * 7, key7, 0, 7 );
+ DES des = new DES( key7 );
+ des.encrypt( data, e8 );
+ System.arraycopy( e8, 0, e, i * 8, 8 );
+ }
+ }
+
+ static String DEFAULT_DOMAIN;
+ static String DEFAULT_USERNAME;
+ static String DEFAULT_PASSWORD;
+ static final String BLANK = "";
+
+ static void initDefaults() {
+ if (DEFAULT_DOMAIN != null) return;
+ DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", "?");
+ DEFAULT_USERNAME = Config.getProperty("jcifs.smb.client.username", "GUEST");
+ DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password", BLANK);
+ }
+
+/**
+ * Generate the ANSI DES hash for the password associated with these credentials.
+ */
+ static public byte[] getPreNTLMResponse( String password, byte[] challenge ) {
+ byte[] p14 = new byte[14];
+ byte[] p21 = new byte[21];
+ byte[] p24 = new byte[24];
+ byte[] passwordBytes;
+ try {
+ passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.OEM_ENCODING );
+ } catch( UnsupportedEncodingException uee ) {
+ throw new RuntimeException("Try setting jcifs.encoding=US-ASCII", uee);
+ }
+ int passwordLength = passwordBytes.length;
+
+ // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM
+ if( passwordLength > 14) {
+ passwordLength = 14;
+ }
+ System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );
+ E( p14, S8, p21);
+ E( p21, challenge, p24);
+ return p24;
+ }
+/**
+ * Generate the Unicode MD4 hash for the password associated with these credentials.
+ */
+ static public byte[] getNTLMResponse( String password, byte[] challenge ) {
+ byte[] uni = null;
+ byte[] p21 = new byte[21];
+ byte[] p24 = new byte[24];
+
+ try {
+ uni = password.getBytes( "UnicodeLittleUnmarked" );
+ } catch( UnsupportedEncodingException uee ) {
+ if( log.level > 0 )
+ uee.printStackTrace( log );
+ }
+ MD4 md4 = new MD4();
+ md4.update( uni );
+ try {
+ md4.digest(p21, 0, 16);
+ } catch (Exception ex) {
+ if( log.level > 0 )
+ ex.printStackTrace( log );
+ }
+ E( p21, challenge, p24 );
+ return p24;
+ }
+
+ /**
+ * Creates the LMv2 response for the supplied information.
+ *
+ * @param domain The domain in which the username exists.
+ * @param user The username.
+ * @param password The user's password.
+ * @param challenge The server challenge.
+ * @param clientChallenge The client challenge (nonce).
+ */
+ public static byte[] getLMv2Response(String domain, String user,
+ String password, byte[] challenge, byte[] clientChallenge) {
+ try {
+ byte[] hash = new byte[16];
+ byte[] response = new byte[24];
+ MD4 md4 = new MD4();
+ md4.update(password.getBytes("UnicodeLittleUnmarked"));
+ HMACT64 hmac = new HMACT64(md4.digest());
+ hmac.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked"));
+ hmac.update(domain.toUpperCase().getBytes("UnicodeLittleUnmarked"));
+ hmac = new HMACT64(hmac.digest());
+ hmac.update(challenge);
+ hmac.update(clientChallenge);
+ hmac.digest(response, 0, 16);
+ System.arraycopy(clientChallenge, 0, response, 16, 8);
+ return response;
+ } catch (Exception ex) {
+ if( log.level > 0 )
+ ex.printStackTrace( log );
+ return null;
+ }
+ }
+
+ static final NtlmPasswordAuthentication NULL =
+ new NtlmPasswordAuthentication( "", "", "" );
+ static final NtlmPasswordAuthentication GUEST =
+ new NtlmPasswordAuthentication( "?", "GUEST", "" );
+ static final NtlmPasswordAuthentication DEFAULT =
+ new NtlmPasswordAuthentication( null );
+
+ String domain;
+ String username;
+ String password;
+ byte[] ansiHash;
+ byte[] unicodeHash;
+ boolean hashesExternal = false;
+ byte[] clientChallenge = null;
+ byte[] challenge = null;
+
+/**
+ * Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo
+ * component of an SMB URL like "<tt>domain;user:pass</tt>". This constructor
+ * is used internally be jCIFS when parsing SMB URLs.
+ */
+
+ public NtlmPasswordAuthentication( String userInfo ) {
+ domain = username = password = null;
+
+ if( userInfo != null ) {
+ int i, u, end;
+ char c;
+
+ end = userInfo.length();
+ for( i = 0, u = 0; i < end; i++ ) {
+ c = userInfo.charAt( i );
+ if( c == ';' ) {
+ domain = userInfo.substring( 0, i );
+ u = i + 1;
+ } else if( c == ':' ) {
+ password = userInfo.substring( i + 1 );
+ break;
+ }
+ }
+ username = userInfo.substring( u, i );
+ }
+
+ initDefaults();
+
+ if( domain == null ) this.domain = DEFAULT_DOMAIN;
+ if( username == null ) this.username = DEFAULT_USERNAME;
+ if( password == null ) this.password = DEFAULT_PASSWORD;
+ }
+/**
+ * Create an <tt>NtlmPasswordAuthentication</tt> object from a
+ * domain, username, and password. Parameters that are <tt>null</tt>
+ * will be substituted with <tt>jcifs.smb.client.domain</tt>,
+ * <tt>jcifs.smb.client.username</tt>, <tt>jcifs.smb.client.password</tt>
+ * property values.
+ */
+ public NtlmPasswordAuthentication( String domain, String username, String password ) {
+ this.domain = domain;
+ this.username = username;
+ this.password = password;
+
+ initDefaults();
+
+ if( domain == null ) this.domain = DEFAULT_DOMAIN;
+ if( username == null ) this.username = DEFAULT_USERNAME;
+ if( password == null ) this.password = DEFAULT_PASSWORD;
+ }
+/**
+ * Create an <tt>NtlmPasswordAuthentication</tt> object with raw password
+ * hashes. This is used exclusively by the <tt>jcifs.http.NtlmSsp</tt>
+ * class which is in turn used by NTLM HTTP authentication functionality.
+ */
+ public NtlmPasswordAuthentication( String domain, String username,
+ byte[] challenge, byte[] ansiHash, byte[] unicodeHash ) {
+ if( domain == null || username == null ||
+ ansiHash == null || unicodeHash == null ) {
+ throw new IllegalArgumentException( "External credentials cannot be null" );
+ }
+ this.domain = domain;
+ this.username = username;
+ this.password = null;
+ this.challenge = challenge;
+ this.ansiHash = ansiHash;
+ this.unicodeHash = unicodeHash;
+ hashesExternal = true;
+ }
+
+/**
+ * Returns the domain.
+ */
+ public String getDomain() {
+ return domain;
+ }
+/**
+ * Returns the username.
+ */
+ public String getUsername() {
+ return username;
+ }
+/**
+ * Returns the password in plain text or <tt>null</tt> if the raw password
+ * hashes were used to construct this <tt>NtlmPasswordAuthentication</tt>
+ * object which will be the case when NTLM HTTP Authentication is
+ * used. There is no way to retrieve a users password in plain text unless
+ * it is supplied by the user at runtime.
+ */
+ public String getPassword() {
+ return password;
+ }
+/**
+ * Return the domain and username in the format:
+ * <tt>domain\\username</tt>. This is equivalent to <tt>toString()</tt>.
+ */
+ public String getName() {
+ boolean d = domain.length() > 0 && domain.equals( "?" ) == false;
+ return d ? domain + "\\" + username : username;
+ }
+
+/**
+ * Computes the 24 byte ANSI password hash given the 8 byte server challenge.
+ */
+ public byte[] getAnsiHash( byte[] challenge ) {
+ if( hashesExternal ) {
+ return ansiHash;
+ }
+ switch (LM_COMPATIBILITY) {
+ case 0:
+ case 1:
+ return getPreNTLMResponse( password, challenge );
+ case 2:
+ return getNTLMResponse( password, challenge );
+ case 3:
+ case 4:
+ case 5:
+ if( clientChallenge == null ) {
+ clientChallenge = new byte[8];
+ RANDOM.nextBytes( clientChallenge );
+ }
+ return getLMv2Response(domain, username, password, challenge,
+ clientChallenge);
+ default:
+ return getPreNTLMResponse( password, challenge );
+ }
+ }
+/**
+ * Computes the 24 byte Unicode password hash given the 8 byte server challenge.
+ */
+ public byte[] getUnicodeHash( byte[] challenge ) {
+ if( hashesExternal ) {
+ return unicodeHash;
+ }
+ switch (LM_COMPATIBILITY) {
+ case 0:
+ case 1:
+ case 2:
+ return getNTLMResponse( password, challenge );
+ case 3:
+ case 4:
+ case 5:
+ /*
+ if( clientChallenge == null ) {
+ clientChallenge = new byte[8];
+ RANDOM.nextBytes( clientChallenge );
+ }
+ return getNTLMv2Response(domain, username, password, null,
+ challenge, clientChallenge);
+ */
+ return new byte[0];
+ default:
+ return getNTLMResponse( password, challenge );
+ }
+ }
+
+ /**
+ * Returns the effective user session key.
+ *
+ * @param challenge The server challenge.
+ * @return A <code>byte[]</code> containing the effective user session key,
+ * used in SMB MAC signing and NTLMSSP signing and sealing.
+ */
+ public byte[] getUserSessionKey(byte[] challenge) {
+ if (hashesExternal) return null;
+ byte[] key = new byte[16];
+ try {
+ getUserSessionKey(challenge, key, 0);
+ } catch (Exception ex) {
+ if( log.level > 0 )
+ ex.printStackTrace( log );
+ }
+ return key;
+ }
+
+ /**
+ * Calculates the effective user session key.
+ *
+ * @param challenge The server challenge.
+ * @param dest The destination array in which the user session key will be
+ * placed.
+ * @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 {
+ 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 );
+ }
+
+ 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;
+ }
+ }
+
+/**
+ * Compares two <tt>NtlmPasswordAuthentication</tt> objects for
+ * equality. Two <tt>NtlmPasswordAuthentication</tt> objects are equal if
+ * their caseless domain and username fields are equal and either both hashes are external and they are equal or both internally supplied passwords are equal. If one <tt>NtlmPasswordAuthentication</tt> object has external hashes (meaning negotiated via NTLM HTTP Authentication) and the other does not they will not be equal. This is technically not correct however the server 8 byte challage would be required to compute and compare the password hashes but that it not available with this method.
+ */
+ public boolean equals( Object obj ) {
+ if( obj instanceof NtlmPasswordAuthentication ) {
+ NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj;
+ if( ntlm.domain.toUpperCase().equals( domain.toUpperCase() ) &&
+ ntlm.username.toUpperCase().equals( username.toUpperCase() )) {
+ if( hashesExternal && ntlm.hashesExternal ) {
+ return Arrays.equals( ansiHash, ntlm.ansiHash ) &&
+ Arrays.equals( unicodeHash, ntlm.unicodeHash );
+ /* This still isn't quite right. If one npa object does not have external
+ * hashes and the other does then they will not be considered equal even
+ * though they may be.
+ */
+ } else if( !hashesExternal && password.equals( ntlm.password )) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+/**
+ * Return the upcased username hash code.
+ */
+ public int hashCode() {
+ return getName().toUpperCase().hashCode();
+ }
+/**
+ * Return the domain and username in the format:
+ * <tt>domain\\username</tt>. This is equivalent to <tt>getName()</tt>.
+ */
+ public String toString() {
+ return getName();
+ }
+}
+
DcerpcHandle handle = null;
LsaPolicyHandle policyHandle = null;
lsarpc.LsarDomainInfo info = new lsarpc.LsarDomainInfo();
- MsrpcQueryInformationPolicy2 rpc;
+ MsrpcQueryInformationPolicy rpc;
try {
handle = DcerpcHandle.getHandle("ncacn_np:" + server +
"[\\PIPE\\lsarpc]", auth);
- policyHandle = new LsaPolicyHandle(handle, null, 0x02000000);
- rpc = new MsrpcQueryInformationPolicy2(policyHandle,
+ // NetApp doesn't like the 'generic' access mask values
+ policyHandle = new LsaPolicyHandle(handle, null, 0x00000001);
+ rpc = new MsrpcQueryInformationPolicy(policyHandle,
(short)lsarpc.POLICY_INFO_ACCOUNT_DOMAIN,
info);
handle.sendrecv(rpc);
* To filter 0 len updates and for debugging
*/
-public class SigningDigest {
-
- private static final int LM_COMPATIBILITY = Config.getInt( "jcifs.smb.lmCompatibility", 0);
+public class SigningDigest implements SmbConstants {
static LogStream log = LogStream.getInstance();
package jcifs.smb;
+import java.util.Arrays;
import jcifs.Config;
class SmbComSessionSetupAndX extends AndXServerMessageBlock {
command = SMB_COM_SESSION_SETUP_ANDX;
this.session = session;
this.auth = session.auth;
- if( auth.hashesExternal && auth.challenge != session.transport.server.encryptionKey ) {
+ if( auth.hashesExternal &&
+ Arrays.equals(auth.challenge, session.transport.server.encryptionKey) == false ) {
throw new SmbAuthException( SmbException.NT_STATUS_ACCESS_VIOLATION );
}
}
static final boolean USE_EXTSEC = Config.getBoolean( "jcifs.smb.client.useExtendedSecurity", false );
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 FLAGS_NONE = 0x00;
static final int FLAGS_LOCK_AND_READ_WRITE_AND_UNLOCK = 0x01;
static String getMessageByCode( int errcode ) {
if(( errcode & 0xC0000000 ) == 0xC0000000 ) {
int min = 0;
- int max = NT_STATUS_CODES.length;
+ int max = NT_STATUS_CODES.length - 1;
while( max >= min ) {
int mid = (min + max) / 2;
+ /* Note there's a signedness error here because 0xC0000000 based values are
+ * negative so it with NT_STATUS_SUCCESS (0) the binary search will not be
+ * performed properly. The effect is that the code at index 1 is never found
+ * (NT_STATUS_UNSUCCESSFUL). We should just factor out NT_STATUS_SUCCESS
+ * as a special case (which it is).
+ */
if( errcode > NT_STATUS_CODES[mid] ) {
min = mid + 1;
} else if( errcode < NT_STATUS_CODES[mid] ) {
}
} else {
int min = 0;
- int max = DOS_ERROR_CODES.length;
+ int max = DOS_ERROR_CODES.length - 1;
while( max >= min ) {
int mid = (min + max) / 2;
return errcode;
} else {
int min = 0;
- int max = DOS_ERROR_CODES.length;
+ int max = DOS_ERROR_CODES.length - 1;
while( max >= min ) {
int mid = (min + max) / 2;
}
static String getMessageByWinerrCode( int errcode ) {
int min = 0;
- int max = WINERR_CODES.length;
+ int max = WINERR_CODES.length - 1;
while( max >= min ) {
int mid = (min + max) / 2;
do {
if (dc_list_expiration < now) {
- dc_list_expiration = now + CACHE_POLICY * 1000L;
NbtAddress[] list = NbtAddress.getAllByName( DOMAIN, 0x1C, null, null );
+ dc_list_expiration = now + CACHE_POLICY * 1000L;
if (list != null && list.length > 0) {
dc_list = list;
} else { /* keep using the old list */
transport.send( request, response );
}
}
-int x = 0;
void sessionSetup( ServerMessageBlock andx,
ServerMessageBlock andxResponse ) throws SmbException {
+ SmbException ex = null;
+
synchronized( transport() ) {
if( sessionSetup ) {
return;
}
request.auth = auth;
- transport.send( request, response );
+
+ try {
+ transport.send( request, response );
+ } catch (SmbAuthException sae) {
+ throw sae;
+ } catch (SmbException se) {
+ ex = se;
+ }
if( response.isLoggedInAsGuest &&
"GUEST".equalsIgnoreCase( auth.username ) == false) {
/* success - install the signing digest */
transport.digest = request.digest;
}
+
+ if (ex != null)
+ throw ex;
}
}
void logoff( boolean inError ) {
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.smb;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import jcifs.Config;
+import jcifs.UniAddress;
+import jcifs.netbios.NbtAddress;
+
+/**
+ * The class represents a user's session established with an SMB/CIFS
+ * server. This class is used internally to the jCIFS library however
+ * applications may wish to authenticate aribrary user credentials
+ * with the <tt>logon</tt> method. It is noteworthy that jCIFS does not
+ * support DCE/RPC at this time and therefore does not use the NETLOGON
+ * procedure. Instead, it simply performs a "tree connect" to IPC$ using
+ * the supplied credentials. This is only a subset of the NETLOGON procedure
+ * but is achives the same effect.
+
+Note that it is possible to change the resource against which clients
+are authenticated to be something other than <tt>IPC$</tt> using the
+<tt>jcifs.smb.client.logonShare</tt> property. This can be used to
+provide simple group based access control. For example, one could setup
+the NTLM HTTP Filter with the <tt>jcifs.smb.client.domainController</tt>
+init parameter set to the name of the server used for authentication. On
+that host, create a share called JCIFSAUTH and adjust the access control
+list for that share to permit only the clients that should have access to
+the target website. Finally, set the <tt>jcifs.smb.client.logonShare</tt>
+to JCIFSAUTH. This should restrict access to only those clients that have
+access to the JCIFSAUTH share. The access control on that share can be
+changed without changing init parameters or reinitializing the webapp.
+ */
+
+public final class SmbSession {
+
+ private static final String LOGON_SHARE =
+ Config.getProperty( "jcifs.smb.client.logonShare", null );
+ private static final int LOOKUP_RESP_LIMIT =
+ Config.getInt( "jcifs.netbios.lookupRespLimit", 3 );
+ private static final String DOMAIN =
+ Config.getProperty("jcifs.smb.client.domain", null);
+ private static final String USERNAME =
+ Config.getProperty("jcifs.smb.client.username", null);
+ private static final int CACHE_POLICY =
+ Config.getInt( "jcifs.netbios.cachePolicy", 60 * 10 ) * 60; /* 10 hours */
+
+ static NbtAddress[] dc_list = null;
+ static long dc_list_expiration;
+ static int dc_list_counter;
+
+ private static NtlmChallenge interrogate( NbtAddress addr ) throws SmbException {
+ UniAddress dc = new UniAddress( addr );
+ SmbTransport trans = SmbTransport.getSmbTransport( dc, 0 );
+ if (USERNAME == null) {
+ trans.connect();
+ if (SmbTransport.log.level >= 3)
+ SmbTransport.log.println(
+ "Default credentials (jcifs.smb.client.username/password)" +
+ " not specified. SMB signing may not work propertly." +
+ " Skipping DC interrogation." );
+ } else {
+ SmbSession ssn = trans.getSmbSession( NtlmPasswordAuthentication.DEFAULT );
+ ssn.getSmbTree( LOGON_SHARE, null ).treeConnect( null, null );
+ }
+ return new NtlmChallenge( trans.server.encryptionKey, dc );
+ }
+ public static NtlmChallenge getChallengeForDomain()
+ throws SmbException, UnknownHostException {
+ if( DOMAIN == null ) {
+ throw new SmbException( "A domain was not specified" );
+ }
+ synchronized (DOMAIN) {
+ long now = System.currentTimeMillis();
+int retry = 1;
+
+do {
+ if (dc_list_expiration < now) {
+ dc_list_expiration = now + CACHE_POLICY * 1000L;
+ NbtAddress[] list = NbtAddress.getAllByName( DOMAIN, 0x1C, null, null );
+ if (list != null && list.length > 0) {
+ dc_list = list;
+ } else { /* keep using the old list */
+ dc_list_expiration = now + 1000 * 60 * 15; /* 15 min */
+ if (SmbTransport.log.level >= 2) {
+ SmbTransport.log.println( "Failed to retrieve DC list from WINS" );
+ }
+ }
+ }
+
+ int max = Math.min( dc_list.length, LOOKUP_RESP_LIMIT );
+ for (int j = 0; j < max; j++) {
+ int i = dc_list_counter++ % max;
+ if (dc_list[i] != null) {
+ try {
+ return interrogate( dc_list[i] );
+ } catch (SmbException se) {
+ if (SmbTransport.log.level >= 2) {
+ SmbTransport.log.println( "Failed validate DC: " + dc_list[i] );
+ if (SmbTransport.log.level > 2)
+ se.printStackTrace( SmbTransport.log );
+ }
+ }
+ dc_list[i] = null;
+ }
+ }
+
+/* No DCs found, for retieval of list by expiring it and retry.
+ */
+ dc_list_expiration = 0;
+} while (retry-- > 0);
+
+ dc_list_expiration = now + 1000 * 60 * 15; /* 15 min */
+ }
+
+ throw new UnknownHostException(
+ "Failed to negotiate with a suitable domain controller for " + DOMAIN );
+ }
+
+ public static byte[] getChallenge( UniAddress dc )
+ throws SmbException, UnknownHostException {
+ return getChallenge(dc, 0);
+ }
+
+ public static byte[] getChallenge( UniAddress dc, int port )
+ throws SmbException, UnknownHostException {
+ SmbTransport trans = SmbTransport.getSmbTransport( dc, port );
+ trans.connect();
+ return trans.server.encryptionKey;
+ }
+/**
+ * Authenticate arbitrary credentials represented by the
+ * <tt>NtlmPasswordAuthentication</tt> object against the domain controller
+ * specified by the <tt>UniAddress</tt> parameter. If the credentials are
+ * not accepted, an <tt>SmbAuthException</tt> will be thrown. If an error
+ * occurs an <tt>SmbException</tt> will be thrown. If the credentials are
+ * valid, the method will return without throwing an exception. See the
+ * last <a href="../../../faq.html">FAQ</a> question.
+ *
+ * See also the <tt>jcifs.smb.client.logonShare</tt> property.
+ */
+ public static void logon( UniAddress dc,
+ NtlmPasswordAuthentication auth ) throws SmbException {
+ logon(dc, 0, auth);
+ }
+
+ public static void logon( UniAddress dc, int port,
+ NtlmPasswordAuthentication auth ) throws SmbException {
+ SmbTree tree = SmbTransport.getSmbTransport( dc, port ).getSmbSession( auth ).getSmbTree( LOGON_SHARE, null );
+ if( LOGON_SHARE == null ) {
+ tree.treeConnect( null, null );
+ } else {
+ Trans2FindFirst2 req = new Trans2FindFirst2( "\\", "*", SmbFile.ATTR_DIRECTORY );
+ Trans2FindFirst2Response resp = new Trans2FindFirst2Response();
+ tree.send( req, resp );
+ }
+ }
+
+ private int uid;
+ private Vector trees;
+ private boolean sessionSetup;
+ // Transport parameters allows trans to be removed from CONNECTIONS
+ private UniAddress address;
+ private int port, localPort;
+ private InetAddress localAddr;
+
+ SmbTransport transport = null;
+ NtlmPasswordAuthentication auth;
+ long expiration;
+
+ SmbSession( UniAddress address, int port,
+ InetAddress localAddr, int localPort,
+ NtlmPasswordAuthentication auth ) {
+ this.address = address;
+ this.port = port;
+ this.localAddr = localAddr;
+ this.localPort = localPort;
+ this.auth = auth;
+ trees = new Vector();
+ }
+
+ synchronized SmbTree getSmbTree( String share, String service ) {
+ SmbTree t;
+
+ if( share == null ) {
+ share = "IPC$";
+ }
+ for( Enumeration e = trees.elements(); e.hasMoreElements(); ) {
+ t = (SmbTree)e.nextElement();
+ if( t.matches( share, service )) {
+ return t;
+ }
+ }
+ t = new SmbTree( this, share, service );
+ trees.addElement( t );
+ return t;
+ }
+ boolean matches( NtlmPasswordAuthentication auth ) {
+ return this.auth == auth || this.auth.equals( auth );
+ }
+ synchronized SmbTransport transport() {
+ if( transport == null ) {
+ transport = SmbTransport.getSmbTransport( address, port, localAddr, localPort );
+ }
+ return transport;
+ }
+ void send( ServerMessageBlock request,
+ ServerMessageBlock response ) throws SmbException {
+ if( response != null ) {
+ response.received = false;
+ }
+
+ synchronized(transport.setupDiscoLock) {
+ expiration = System.currentTimeMillis() + SmbTransport.SO_TIMEOUT;
+ sessionSetup( request, response );
+ if( response != null && response.received ) {
+ return;
+ }
+ request.uid = uid;
+ request.auth = auth;
+ transport.send( request, response );
+ }
+ }
+int x = 0;
+ void sessionSetup( ServerMessageBlock andx,
+ ServerMessageBlock andxResponse ) throws SmbException {
+synchronized( transport() ) {
+ if( sessionSetup ) {
+ return;
+ }
+
+ transport.connect();
+
+ /*
+ * Session Setup And X Request / Response
+ */
+
+ 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 );
+
+ /* 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.auth = auth;
+ transport.send( request, response );
+
+ if( response.isLoggedInAsGuest &&
+ "GUEST".equalsIgnoreCase( auth.username ) == false) {
+ throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
+ }
+
+ uid = response.uid;
+ sessionSetup = true;
+
+ if( request.digest != null ) {
+ /* success - install the signing digest */
+ transport.digest = request.digest;
+ }
+}
+ }
+ void logoff( boolean inError ) {
+synchronized( transport() ) {
+ if( sessionSetup == false ) {
+ return;
+ }
+
+ for( Enumeration e = trees.elements(); e.hasMoreElements(); ) {
+ SmbTree t = (SmbTree)e.nextElement();
+ t.treeDisconnect( inError );
+ }
+
+ if( !inError && transport.server.security != ServerMessageBlock.SECURITY_SHARE ) {
+
+ /*
+ * Logoff And X Request / Response
+ */
+
+ SmbComLogoffAndX request = new SmbComLogoffAndX( null );
+ request.uid = uid;
+ try {
+ transport.send( request, null );
+ } catch( SmbException se ) {
+ }
+ }
+
+ sessionSetup = false;
+}
+ }
+ public String toString() {
+ return "SmbSession[accountName=" + auth.username +
+ ",primaryDomain=" + auth.domain +
+ ",uid=" + uid +
+ ",sessionSetup=" + sessionSetup + "]";
+ }
+}
try {
connect( RESPONSE_TIMEOUT );
} catch( IOException ioe ) {
- throw new SmbException( "", ioe );
+ throw new SmbException( ioe.getMessage(), ioe );
}
return (capabilities & cap) == cap;
}
try {
super.connect( RESPONSE_TIMEOUT );
} catch( TransportException te ) {
- throw new SmbException( "", te );
+ throw new SmbException( te.getMessage(), te );
}
}
protected void doConnect() throws IOException {
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.");
+ }
/* Adjust negotiated values */
if (size < (HEADER_LENGTH + 1) || (4 + size) > rcv_buf_size ) {
throw new IOException( "Invalid payload size: " + size );
}
- if (resp.command == ServerMessageBlock.SMB_COM_READ_ANDX) {
+ int errorCode = Encdec.dec_uint32le( BUF, 9 ) & 0xFFFFFFFF;
+ if (resp.command == ServerMessageBlock.SMB_COM_READ_ANDX && errorCode == 0) {
SmbComReadAndXResponse r = (SmbComReadAndXResponse)resp;
int off = HEADER_LENGTH;
/* WordCount thru dataOffset always 27 */
} catch( SmbException se ) {
throw se;
} catch( IOException ioe ) {
- throw new SmbException( "", ioe );
+ throw new SmbException( ioe.getMessage(), ioe );
}
checkStatus( request, response );
} finally {
/* This guarantees that we leave in a valid state
*/
- if (state != 0 && state != 3) {
+ if (state != 0 && state != 3 && state != 4) {
if (log.level >= 1)
log.println("Invalid state: " + state);
state = 0;