+Wed Sep 17 21:21:31 EDT 2003
+
+JCIFS now supports SMB signing. If a server requires SMB signing (Windows
+2003 requires it by default) or if the server supports signing and the
+jcifs.smb.client.signingPreferred property is true signing will be
+negotiated and used. Signing does not work with NTLM HTTP authentication
+because the original password hashes are required to generate the MAC
+signing key. There have also been some adjustments to SmbFile.hashCode.
+Previously jCIFS did not precisely exhibit the behavior described in the
+comments for hashCode() and equals().
+
+Mon Sep 1 19:04:11 EDT 2003
+
+CIFS permits multiple users to be logged in over the same transport
+(socket). JCIFS takes advantage of this and reuses existing transports
+whenever possible. It was reported that a user using the NTLM Servlet
+Filter was getting ERRSRV/90 which means "Too many Uids active on this
+session". So it looks like the limit is ~150 sessions per transport. I have
+added code to logoff 1/10th of the sessions if the
+jcifs.smb.client.ssnLimit propery is exceeded (default 100).
+
Thu Jul 10 22:07:09 EDT 2003
Support for LMv2 authentication has been added. See the Overview page in
+Wed Sep 17 21:21:31 EDT 2003
+
+SMB signing is now supported and there have been some adjustments to
+SmbFile.hashCode() and SmbFile.equals()
+
+Mon Sep 1 19:18:52 EDT 2003
+
+Added code to logoff 1/10th of all sessions over a transport if
+jcifs.smb.client.ssnLimit is reached (fix for ERRSVR/90 errors).
+
Thu Jul 10 22:07:09 EDT 2003
Support for LMv2 authentication has been added.
<target name="jar" depends="smb">
<copy file="src/jcifs/util/mime.map" tofile="build/jcifs/util/mime.map" overwrite="yes"/>
<copy file="src/jcifs/http/ne.css" tofile="build/jcifs/http/ne.css" overwrite="yes"/>
- <jar jarfile="jcifs-0.7.11.jar" basedir="build"/>
+ <jar jarfile="jcifs-0.7.13.jar" basedir="build"/>
</target>
<target name="tgz">
- <copy todir="dist_tmp/jcifs_0.7.11">
+ <copy todir="dist_tmp/jcifs_0.7.13">
<fileset dir="." excludes="ant,**/.*,build,jcifs.prp,**/*.tgz,**/*.zip"/>
</copy>
- <tar tarfile="jcifs-0.7.11.tar" basedir="dist_tmp"/>
- <gzip src="jcifs-0.7.11.tar" zipfile="jcifs-0.7.11.tgz"/>
- <delete file="jcifs-0.7.11.tar"/>
+ <tar tarfile="jcifs-0.7.13.tar" basedir="dist_tmp"/>
+ <gzip src="jcifs-0.7.13.tar" zipfile="jcifs-0.7.13.tgz"/>
+ <delete file="jcifs-0.7.13.tar"/>
<delete dir="dist_tmp"/>
</target>
<target name="zip">
- <copy todir="dist_tmp/jcifs_0.7.11">
+ <copy todir="dist_tmp/jcifs_0.7.13">
<fileset dir="." excludes="ant,**/.*,build,jcifs.prp,**/*.tgz,**/*.zip"/>
</copy>
<fixcrlf srcdir="dist_tmp" cr="add" tab="remove" tablength="4" excludes="**/*.jar,**/*.exe"/>
- <zip zipfile="jcifs-0.7.11.zip" basedir="dist_tmp"/>
+ <zip zipfile="jcifs-0.7.13.zip" basedir="dist_tmp"/>
<delete dir="dist_tmp"/>
</target>
--- /dev/null
+import jcifs.smb.SmbFile;
+
+public class Equals {
+
+ public static void main( String argv[] ) throws Exception {
+
+ SmbFile f1 = new SmbFile( argv[0] );
+ SmbFile f2 = new SmbFile( argv[1] );
+ System.err.println( f1.equals( f2 ));
+ }
+}
+
import jcifs.util.HMACT64;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
-import java.security.SecureRandom;
+import java.util.Random;
import java.util.Arrays;
import jcifs.Config;
private static final String DEFAULT_PASSWORD =
Config.getProperty("jcifs.smb.client.password", "");
- private static final SecureRandom random = new SecureRandom();
+ private static final Random RANDOM = new Random();
// KGS!@#$%
static final byte[] S8 = {
byte[] ansiHash;
byte[] unicodeHash;
boolean hashesExternal = false;
+ byte[] clientChallenge = null;
/**
* Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo
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.
*/
case 3:
case 4:
case 5:
- byte[] clientChallenge = new byte[8];
- random.nextBytes(clientChallenge);
+ if( clientChallenge == null ) {
+ clientChallenge = new byte[8];
+ RANDOM.nextBytes( clientChallenge );
+ }
return getLMv2Response(domain, username, password, challenge,
clientChallenge);
default:
case 4:
case 5:
/*
- byte[] clientChallenge = new byte[8];
- random.nextBytes(clientChallenge);
+ if( clientChallenge == null ) {
+ clientChallenge = new byte[8];
+ RANDOM.nextBytes( clientChallenge );
+ }
return getNTLMv2Response(domain, username, password, null,
challenge, clientChallenge);
*/
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) {
+ Log.printStackTrace("Unable to calculate user session key.", ex);
+ }
+ 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
static final int CMD_OFFSET = 4;
static final int ERROR_CODE_OFFSET = 5;
static final int FLAGS_OFFSET = 9;
+ static final int SIGNATURE_OFFSET = 14;
static final int TID_OFFSET = 24;
static final int HEADER_LENGTH = 32;
wordCount, byteCount;
boolean useUnicode, received;
long responseTimeout = 1;
+ int verifySequence;
ServerMessageBlock() {
flags = (byte)( FLAGS_PATH_NAMES_CASELESS | FLAGS_PATH_NAMES_CANONICALIZED );
+++ /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 jcifs.Config;
-import java.io.IOException;
-import java.io.InputStream;
-
-class SmbComSessionSetupAndX extends AndXServerMessageBlock {
-
- static final int BATCH_LIMIT =
- Config.getInt( "jcifs.smb.client.SessionSetupAndX.TreeConnectAndX", 1 );
-
- byte[] accountPassword, unicodePassword;
- int passwordLength, unicodePasswordLength;
- int negotiatedMaxBufferSize,
- negotiatedMaxMpxCount,
- vcNumber,
- sessionKey;
- String accountName,
- primaryDomain,
- nativeOs,
- nativeLanMan;
-
- SmbSession session;
-
- SmbComSessionSetupAndX( SmbSession session, ServerMessageBlock andx ) {
- super( andx );
- command = SMB_COM_SESSION_SETUP_ANDX;
- this.session = session;
- }
-
- int getBatchLimit( byte command ) {
- return command == SMB_COM_TREE_CONNECT_ANDX ? BATCH_LIMIT : 0;
- }
- int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) {
- int start = dstIndex;
-
- if( session.transport.server.security == SECURITY_USER &&
- ( session.auth.hashesExternal ||
- session.auth.password.length() > 0 )) {
- if( session.transport.server.encryptedPasswords ) {
- // encrypted
- accountPassword = session.auth.getAnsiHash( session.transport.server.encryptionKey );
- unicodePassword = session.auth.getUnicodeHash( session.transport.server.encryptionKey );
- // fix for NTLMv2 or empty NTLM/LM
- passwordLength = accountPassword.length;
- unicodePasswordLength = unicodePassword.length;
- } else if( Config.getBoolean( "jcifs.smb.client.disablePlainTextPasswords", true )) {
- throw new RuntimeException( "Plain text passwords are disabled" );
- } else if( useUnicode ) {
- // plain text
- String password = session.auth.getPassword();
- accountPassword = new byte[0];
- passwordLength = 0;
- unicodePassword = new byte[(password.length() + 1) * 2];
- unicodePasswordLength = writeString( password, unicodePassword, 0 );
- } else {
- // plain text
- String password = session.auth.getPassword();
- accountPassword = new byte[(password.length() + 1) * 2];
- passwordLength = writeString( password, accountPassword, 0 );
- unicodePassword = new byte[0];
- unicodePasswordLength = 0;
- }
- } else {
- // no password in session setup
- passwordLength = unicodePasswordLength = 0;
- }
-
- negotiatedMaxBufferSize = session.transport.negotiatedMaxBufferSize;
- negotiatedMaxMpxCount = session.transport.negotiatedMaxMpxCount;
- vcNumber = session.transport.client.vcNumber;
- sessionKey = session.transport.client.sessionKey;
-
- writeInt2( negotiatedMaxBufferSize, dst, dstIndex );
- dstIndex += 2;
- writeInt2( negotiatedMaxMpxCount, dst, dstIndex );
- dstIndex += 2;
- writeInt2( vcNumber, dst, dstIndex );
- dstIndex += 2;
- writeInt4( sessionKey, dst, dstIndex );
- dstIndex += 4;
- writeInt2( passwordLength, dst, dstIndex );
- dstIndex += 2;
- writeInt2( unicodePasswordLength, dst, dstIndex );
- dstIndex += 2;
- dst[dstIndex++] = (byte)0x00;
- dst[dstIndex++] = (byte)0x00;
- dst[dstIndex++] = (byte)0x00;
- dst[dstIndex++] = (byte)0x00;
- writeInt4( session.transport.client.capabilities, dst, dstIndex );
- dstIndex += 4;
-
- return dstIndex - start;
- }
- int writeBytesWireFormat( byte[] dst, int dstIndex ) {
- int start = dstIndex;
-
- accountName = session.auth.username.toUpperCase();
- primaryDomain = session.auth.domain.toUpperCase();
- nativeOs = session.transport.client.nativeOs;
- nativeLanMan = session.transport.client.nativeLanMan;
-
- if( session.transport.server.security == SECURITY_USER &&
- ( session.auth.hashesExternal ||
- session.auth.password.length() > 0 )) {
- System.arraycopy( accountPassword, 0, dst, dstIndex, passwordLength );
- dstIndex += passwordLength;
- System.arraycopy( unicodePassword, 0, dst, dstIndex, unicodePasswordLength );
- dstIndex += unicodePasswordLength;
- }
- if( useUnicode ) {
- // at least NT 4 observed needing this only with unicode
- dst[dstIndex++] = (byte)'\0';
- }
-
- dstIndex += writeString( accountName, dst, dstIndex );
- dstIndex += writeString( primaryDomain, dst, dstIndex );
- dstIndex += writeString( nativeOs, dst, dstIndex );
-/*
- // at least NT 4 observed with 2 zero bytes here
- dst[dstIndex++] = (byte)0x00;
- dst[dstIndex++] = (byte)0x00;
-This still isn't quite right.
-*/
- dstIndex += writeString( nativeLanMan, dst, dstIndex );
-
- return dstIndex - start;
- }
- int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) {
- return 0;
- }
- int readBytesWireFormat( byte[] buffer, int bufferIndex ) {
- return 0;
- }
- int readBytesDirectWireFormat( InputStream in, int byteCount ) throws IOException {
- return 0;
- }
- public String toString() {
- String result = new String( "SmbComSessionSetupAndX[" +
- super.toString() +
- ",maxBufferSize=" + negotiatedMaxBufferSize +
- ",maxMpxCount=" + negotiatedMaxMpxCount +
- ",vcNumber=" + vcNumber +
- ",sessionKey=" + sessionKey +
- ",passwordLength=" + passwordLength +
- ",unicodePasswordLength=" + unicodePasswordLength +
- ",capabilities=" + session.transport.client.capabilities +
- ",accountName=" + accountName +
- ",primaryDomain=" + primaryDomain +
- ",nativeOs=" + nativeOs +
- ",nativeLanMan=" + nativeLanMan + "]" );
- return result;
- }
-}
*/
public static final int ERRfilexists = 80;
/**
+ * Too many Uids active on this session
+ */
+ public static final int ERRtoomanyuids = 90;
+/**
* The pipe has been ended
*/
public static final int ERRbrokenpipe = 109;
* The device is not ready
*/
public static final int ERRnotready = 21;
+/**
+ * General failure
+ */
+ public static final int ERRgeneral = 31;
// RAP transaction status codes
public static final int NERR_Success = 0;
case ERRpasswordExpired:
result += "The password of the user has expired";
break;
+ case ERRtoomanyuids:
+ result += "Too many Uids active on this session";
+ break;
default:
result += "No description available [ERRSRV/" + errorCode + "]";
}
case ERRnotready:
result += "The device is not ready";
break;
+ case ERRgeneral:
+ result += "General failure";
+ break;
default:
result += "No description available [ERRHRD/" + errorCode + "]";
}
*/
public int hashCode() {
- return url.hashCode();
+ int hash;
+ try {
+ hash = getAddress().hashCode();
+ } catch( UnknownHostException uhe ) {
+ hash = getServer().toUpperCase().hashCode();
+ }
+ getUncPath0();
+ return hash + canon.toUpperCase().hashCode();
}
/**
*/
public boolean equals( Object obj ) {
-//HERE
return obj instanceof SmbFile && obj.hashCode() == hashCode();
}
return;
}
+ transport.negotiate();
+
+ if( transport.useSigning && transport.macSigningKey == null ) {
+ /* The first SMB_COM_SESSION_SETUP_ANX
+ * generates the signing key */
+ transport.initSigning( auth );
+ }
+
Log.println( Log.WARNINGS, "smb session setup warning",
" requesting session with accountName=" + auth.username +
",primaryDomain=" + auth.domain );
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
+
+import java.security.MessageDigest;
+
import java.util.Vector;
+import java.util.LinkedList;
+import java.util.ListIterator;
import java.util.Enumeration;
import java.util.Hashtable;
private static final int PUSHBACK_BUF_SIZE = 64;
private static final int DEFAULT_RCV_BUF_SIZE = 60416;
private static final int DEFAULT_SND_BUF_SIZE = 5000;
+ private static final int DEFAULT_SSN_LIMIT = 100;
+
+ private static final int LM_COMPATIBILITY =
+ Config.getInt("jcifs.smb.lmCompatibility", 0);
static final int MID_OFFSET = 30;
static final int HEADER_LENGTH = 32;
private Thread thread;
private Object outLock;
+ private MessageDigest signingDigest;
+
private static Vector connections = new Vector();
private static MpxControl mpxCtrl = new MpxControl();
private static byte[] snd_buf = new byte[0xFFFF];
private static byte[] rcv_buf = new byte[0xFFFF];
- Vector sessions;
- boolean negotiated, useUnicode;
+ LinkedList sessions;
+ boolean negotiated, useUnicode, useSigning;
UniAddress address;
+
+ byte[] macSigningKey;
+ int signSequence;
+
int port, rcv_buf_size, snd_buf_size;
int negotiatedDialectIndex;
int negotiatedMaxBufferSize;
int negotiatedCapabilities;
+ int ssnLimit = Config.getInt( "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT );
+
ClientProperties client = new ClientProperties();
ServerProperties server = new ServerProperties();
* but we could do raw and mpx
*/
int flags2 = Config.getInt( "jcifs.smb.client.flags2",
- ServerMessageBlock.FLAGS2_LONG_FILENAMES |
- ServerMessageBlock.FLAGS2_UNICODE );
+ ServerMessageBlock.FLAGS2_LONG_FILENAMES | ServerMessageBlock.FLAGS2_UNICODE );
int capabilities = Config.getInt( "jcifs.smb.client.capabilities",
ServerMessageBlock.CAP_UNICODE | ServerMessageBlock.CAP_NT_SMBS );
String nativeOs = Config.getProperty( "jcifs.smb.client.nativeOs",
if( Config.getBoolean( "jcifs.smb.client.useNTSmbs", false ) == true ) {
client.capabilities |= ServerMessageBlock.CAP_NT_SMBS;
}
+ useSigning = Config.getBoolean("jcifs.smb.client.signingPreferred", false);
+ if( useSigning ) {
+ client.flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
+ }
soTimeout = Config.getInt( "jcifs.smb.client.soTimeout", DEFAULT_SO_TIMEOUT );
responseTimeout = Config.getInt( "jcifs.smb.client.responseTimeout", DEFAULT_RESPONSE_TIMEOUT );
rcv_buf_size = Config.getInt( "jcifs.smb.client.rcv_buf_size", DEFAULT_RCV_BUF_SIZE );
snd_buf_size = client.maxBufferSize;
- sessions = new Vector();
+ sessions = new LinkedList();
responseTable = new Hashtable();
outLock = new Object();
negotiated = false;
synchronized SmbSession getSmbSession( NtlmPasswordAuthentication auth ) {
SmbSession ssn;
- for( Enumeration e = sessions.elements(); e.hasMoreElements(); ) {
- ssn = (SmbSession)e.nextElement();
+ ListIterator iter = sessions.listIterator();
+ while( iter.hasNext() ) {
+ ssn = (SmbSession)iter.next();
if( ssn.matches( auth )) {
ssn.auth = auth;
return ssn;
}
}
ssn = new SmbSession( this, auth );
-//System.err.println( "SESSION+: " + ssn + "\n " + sessions.toString() );
- sessions.addElement( ssn );
+ sessions.add( ssn );
+
+ if( sessions.size() > ssnLimit ) {
+ int nclose = sessions.size() / 10;
+ SmbSession s;
+
+ while( nclose-- > 0 ) {
+ s = (SmbSession)sessions.removeFirst();
+ s.logoff( false );
+ }
+ }
+
return ssn;
}
boolean matches( UniAddress address, int port, InetAddress localAddr, int localPort ) {
if( socket == null ) {
inError = true;
}
- for( Enumeration e = sessions.elements(); e.hasMoreElements(); ) {
- ssn = (SmbSession)e.nextElement();
+
+ ListIterator iter = sessions.listIterator();
+ while( iter.hasNext() ) {
+ ssn = (SmbSession)iter.next();
ssn.logoff( inError );
}
if( socket != null ) {
if( response.errorCode != 0 || e.hasMoreElements() == false ) {
((SmbComTransactionResponse)response).hasMore = false;
+ if( useSigning ) {
+ verify(rcv_buf, 0, response.length, response.verifySequence);
+ }
response.notify();
} else {
ensureOpen();
Log.printMessageData( "smb andx data", smb );
}
Log.printHexDump( "smb received", rcv_buf, 0, response.length );
+ if( useSigning ) {
+ verify(rcv_buf, 0, response.length, response.verifySequence);
+ }
response.notify();
}
}
}
}
+
+ void initSigning(NtlmPasswordAuthentication auth) throws SmbException {
+ if( auth.hashesExternal ) {
+ if( server.signaturesRequired ) {
+ throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe,
+ "Signing is required by the server but passwords are external." );
+ }
+ useSigning = false;
+ return;
+ }
+
+ signSequence = 0;
+
+ switch (LM_COMPATIBILITY) {
+ case 0:
+ case 1:
+ case 2:
+ macSigningKey = new byte[40];
+ try {
+ auth.getUserSessionKey(server.encryptionKey, macSigningKey, 0);
+ System.arraycopy(auth.getUnicodeHash(server.encryptionKey),
+ 0, macSigningKey, 16, 24);
+ } catch (Exception ex) {
+ Log.printStackTrace("Unable to calculate MAC signing key.", ex);
+ macSigningKey = null;
+ }
+ break;
+ case 3:
+ case 4:
+ case 5:
+ // NTLMv2 will have a key; LMv2 doesn't (just uses an
+ // empty user session key). So if/when we start doing
+ // NTLMv2, this will change.
+ macSigningKey = new byte[16];
+ break;
+ default:
+ macSigningKey = new byte[40];
+ try {
+ auth.getUserSessionKey(server.encryptionKey, macSigningKey, 0);
+ System.arraycopy(auth.getUnicodeHash(server.encryptionKey),
+ 0, macSigningKey, 16, 24);
+ } catch (Exception ex) {
+ Log.printStackTrace("Unable to calculate MAC signing key.", ex);
+ macSigningKey = null;
+ }
+ break;
+ }
+ }
+
+ /**
+ * Performs MAC signing of the SMB. This is done as follows.
+ * The signature field of the SMB is overwritted with the sequence number;
+ * The MD5 digest of the MAC signing key + the entire SMB is taken;
+ * The first 8 bytes of this are placed in the signature field.
+ *
+ * @param data The data.
+ * @param offset The starting offset at which the SMB header begins.
+ * @param length The length of the SMB data starting at offset.
+ */
+ private void sign(byte[] data, int offset, int length) {
+ if (macSigningKey == null) return;
+ try {
+ signingDigest.update(macSigningKey);
+ int index = offset + ServerMessageBlock.SIGNATURE_OFFSET;
+ for (int i = 0; i < 8; i++) data[index + i] = 0;
+ ServerMessageBlock.writeInt4(signSequence, data, index);
+ signingDigest.update(data, offset, length);
+ System.arraycopy(signingDigest.digest(), 0, data, index, 8);
+ } catch (Exception ex) {
+ Log.printStackTrace("Error signing SMB.", ex);
+ } finally {
+ signSequence += 2;
+ }
+ }
+
+ /**
+ * Performs MAC signature verification. This calculates the signature
+ * of the SMB and compares it to the signature field on the SMB itself.
+ *
+ * @param data The data.
+ * @param offset The starting offset at which the SMB header begins.
+ * @param length The length of the SMB data starting at offset.
+ */
+ private void verify(byte[] data, int offset, int length, int verifySequence) throws IOException {
+ if (macSigningKey == null) return;
+ signingDigest.update(macSigningKey);
+ int index = offset;
+ signingDigest.update(data, index, ServerMessageBlock.SIGNATURE_OFFSET);
+ index += ServerMessageBlock.SIGNATURE_OFFSET;
+ byte[] sequence = new byte[8];
+ ServerMessageBlock.writeInt4(verifySequence, sequence, 0);
+ signingDigest.update(sequence);
+ index += 8;
+ signingDigest.update(data, index, length - ServerMessageBlock.SIGNATURE_OFFSET - 8);
+ byte[] signature = signingDigest.digest();
+ for (int i = 0; i < 8; i++) {
+ if (signature[i] != data[offset + ServerMessageBlock.SIGNATURE_OFFSET + i]) {
+ throw new IOException("Unverifiable signature.");
+ }
+ }
+ }
+
void send( ServerMessageBlock request,
ServerMessageBlock response ) throws SmbException {
Integer mid = null;
synchronized( outLock ) {
ensureOpen();
synchronized( snd_buf ) {
- out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ int length = request.writeWireFormat(snd_buf, 0);
+ if( useSigning ) {
+ sign(snd_buf, 0, length);
+ }
+ out.write(snd_buf, 0, length);
out.flush();
Log.printMessageData( "smb sent", request );
synchronized( outLock ) {
ensureOpen();
synchronized( snd_buf ) {
- out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ int length = request.writeWireFormat(snd_buf, 0);
+ if( useSigning ) {
+ response.verifySequence = signSequence + 1;
+ sign(snd_buf, 0, length);
+ }
+ out.write(snd_buf, 0, length);
out.flush();
Log.printMessageData( "smb sent", request );
synchronized( outLock ) {
ensureOpen();
synchronized(snd_buf) {
- out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ int length = request.writeWireFormat(snd_buf, 0);
+ if( useSigning ) {
+ response.verifySequence = signSequence + 1;
+ sign(snd_buf, 0, length);
+ }
+ out.write(snd_buf, 0, length);
out.flush();
Log.printMessageData( "smb sent", request );
synchronized( outLock ) {
ensureOpen();
synchronized( snd_buf ) {
- out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ int length = request.writeWireFormat(snd_buf, 0);
+ if( useSigning ) {
+ response.verifySequence = signSequence + 1;
+ sign(snd_buf, 0, length);
+ }
+ out.write(snd_buf, 0, length);
out.flush();
Log.printMessageData( "smb sent", request );
* Negotiate Protocol Request / Response
*/
+ // reset MAC signing
+ macSigningKey = null;
+
SmbComNegotiateResponse response = new SmbComNegotiateResponse();
send( new SmbComNegotiate(), response );
server.encryptedPasswords = response.encryptedPasswords;
server.signaturesEnabled = response.signaturesEnabled;
server.signaturesRequired = response.signaturesRequired;
+ if (server.signaturesRequired || (server.signaturesEnabled && useSigning)) {
+ useSigning = true;
+ client.flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
+
+ if (signingDigest == null) {
+ try {
+ signingDigest = MessageDigest.getInstance("MD5");
+ } catch (Exception ex) {
+ Log.printStackTrace("Unable to obtain MD5 digest.", ex);
+ return;
+ }
+ }
+ } else {
+ useSigning = false;
+ client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
+ }
negotiatedDialectIndex = response.dialectIndex;;
server.maxMpxCount = response.maxMpxCount;
negotiatedMaxMpxCount = client.maxMpxCount < server.maxMpxCount ?
--- /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 jcifs.netbios.NbtSocket;
+import jcifs.netbios.NbtException;
+import jcifs.netbios.NbtAddress;
+import jcifs.UniAddress;
+import jcifs.Config;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Vector;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+class SmbTransport implements Runnable {
+
+ private static final int DEFAULT_MAX_MPX_COUNT = 10;
+ private static final int DEFAULT_RESPONSE_TIMEOUT = 10000;
+ private static final int DEFAULT_SO_TIMEOUT = 15000;
+ private static final int PUSHBACK_BUF_SIZE = 64;
+ private static final int DEFAULT_RCV_BUF_SIZE = 60416;
+ private static final int DEFAULT_SND_BUF_SIZE = 5000;
+ private static final int DEFAULT_SSN_LIMIT = 100;
+
+ static final int MID_OFFSET = 30;
+ static final int HEADER_LENGTH = 32;
+ static final int FLAGS_OFFSET = 9;
+ static final int FLAGS_RESPONSE = 0x80;
+
+ private NbtSocket socket; // should become UniSocket?
+ private InputStream in;
+ private OutputStream out;
+ private int localPort, soTimeout, responseTimeout;
+ private long closeTime;
+ private InetAddress localAddr;
+ private Hashtable responseTable;
+ private Thread thread;
+ private Object outLock;
+
+ private static Vector connections = new Vector();
+ private static MpxControl mpxCtrl = new MpxControl();
+
+private static byte[] snd_buf = new byte[0xFFFF];
+private static byte[] rcv_buf = new byte[0xFFFF];
+
+ LinkedList sessions;
+ boolean negotiated, useUnicode;
+
+ UniAddress address;
+ int port, rcv_buf_size, snd_buf_size;
+
+ int negotiatedDialectIndex;
+ int negotiatedMaxMpxCount;
+ int negotiatedMaxBufferSize;
+ int negotiatedCapabilities;
+
+ int ssnLimit = Config.getInt( "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT );
+
+ ClientProperties client = new ClientProperties();
+ ServerProperties server = new ServerProperties();
+
+ class ClientProperties {
+ int maxMpxCount = Config.getInt( "jcifs.smb.client.maxMpxCount",
+ DEFAULT_MAX_MPX_COUNT );
+ int maxBufferSize = Config.getInt( "jcifs.smb.client.snd_buf_size",
+ DEFAULT_SND_BUF_SIZE );
+ int sessionKey = 0x00000000;
+ /* NT 4 Workstation client capabilities 0x00D4
+ * we don't do NT Status Codes or Level II oplocks
+ * but we could do raw and mpx
+ */
+ int flags2 = Config.getInt( "jcifs.smb.client.flags2",
+ ServerMessageBlock.FLAGS2_LONG_FILENAMES |
+ ServerMessageBlock.FLAGS2_UNICODE );
+ int capabilities = Config.getInt( "jcifs.smb.client.capabilities",
+ ServerMessageBlock.CAP_UNICODE | ServerMessageBlock.CAP_NT_SMBS );
+ String nativeOs = Config.getProperty( "jcifs.smb.client.nativeOs",
+ System.getProperty( "os.name" ));
+ String nativeLanMan = Config.getProperty( "jcifs.smb.client.nativeLanMan", "jCIFS" );
+ int vcNumber = 1;
+ }
+ class ServerProperties {
+ String tconHostName;
+
+ byte flags;
+ int flags2;
+ int maxMpxCount;
+ int maxBufferSize;
+ int sessionKey;
+ // NT 4 Workstation is 0x43FD
+ int capabilities;
+ String oemDomainName;
+ String primaryDomain;
+ String nativeOs;
+ String nativeLanMan;
+
+ int securityMode;
+ int security;
+ boolean encryptedPasswords;
+ boolean signaturesEnabled;
+ boolean signaturesRequired;
+ int maxNumberVcs;
+ int maxRawSize;
+ long serverTime;
+ int serverTimeZone;
+ int encryptionKeyLength;
+ byte[] encryptionKey;
+ }
+
+ static synchronized SmbTransport getSmbTransport( UniAddress address, int port ) {
+ return getSmbTransport( address, port,
+ Config.getInetAddress( "jcifs.smb.client.laddr", null ),
+ Config.getInt( "jcifs.smb.client.lport", 0 ));
+ }
+ static synchronized SmbTransport getSmbTransport( UniAddress address, int port,
+ InetAddress localAddr, int localPort ) {
+ SmbTransport conn;
+ for( Enumeration e = connections.elements(); e.hasMoreElements(); ) {
+ conn = (SmbTransport)e.nextElement();
+ if( conn.matches( address, port, localAddr, localPort )) {
+ return conn;
+ }
+ }
+
+ conn = new SmbTransport( address, port, localAddr, localPort );
+ connections.addElement( conn );
+ return conn;
+ }
+
+ SmbTransport( UniAddress address, int port, InetAddress localAddr, int localPort ) {
+ this.address = address;
+ this.port = port;
+ this.localAddr = localAddr;
+ this.localPort = localPort;
+
+ useUnicode = Config.getBoolean( "jcifs.smb.client.useUnicode", true );
+ if( useUnicode == false ) {
+ client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE;
+ client.capabilities &= 0xFFFF ^ ServerMessageBlock.CAP_UNICODE;
+ }
+ if( Config.getBoolean( "jcifs.smb.client.useNTSmbs", false ) == true ) {
+ client.capabilities |= ServerMessageBlock.CAP_NT_SMBS;
+ }
+
+ soTimeout = Config.getInt( "jcifs.smb.client.soTimeout", DEFAULT_SO_TIMEOUT );
+ responseTimeout = Config.getInt( "jcifs.smb.client.responseTimeout", DEFAULT_RESPONSE_TIMEOUT );
+ rcv_buf_size = Config.getInt( "jcifs.smb.client.rcv_buf_size", DEFAULT_RCV_BUF_SIZE );
+ snd_buf_size = client.maxBufferSize;
+ sessions = new LinkedList();
+ responseTable = new Hashtable();
+ outLock = new Object();
+ negotiated = false;
+ }
+
+ synchronized SmbSession getSmbSession() {
+ return getSmbSession( new NtlmPasswordAuthentication( null, null, null ));
+ }
+ synchronized SmbSession getSmbSession( NtlmPasswordAuthentication auth ) {
+ SmbSession ssn;
+
+ ListIterator iter = sessions.listIterator();
+ while( iter.hasNext() ) {
+ ssn = (SmbSession)iter.next();
+ if( ssn.matches( auth )) {
+ ssn.auth = auth;
+ return ssn;
+ }
+ }
+ ssn = new SmbSession( this, auth );
+ sessions.add( ssn );
+
+ if( sessions.size() > ssnLimit ) {
+ int nclose = sessions.size() / 10;
+ SmbSession s;
+
+ while( nclose-- > 0 ) {
+ s = (SmbSession)sessions.removeFirst();
+ s.logoff( false );
+ }
+ }
+
+ return ssn;
+ }
+ boolean matches( UniAddress address, int port, InetAddress localAddr, int localPort ) {
+ InetAddress defaultLocal = null;
+ try {
+ defaultLocal = InetAddress.getLocalHost();
+ } catch( UnknownHostException uhe ) {
+ }
+ int p1 = ( port == 0 || port == 139 ) ? 0 : port;
+ int p2 = ( this.port == 0 || this.port == 139 ) ? 0 : this.port;
+ InetAddress la1 = localAddr == null ? defaultLocal : localAddr;
+ InetAddress la2 = this.localAddr == null ? defaultLocal : this.localAddr;
+ return address.equals( this.address ) &&
+ p1 == p2 &&
+ la1.equals( la2 ) &&
+ localPort == this.localPort;
+ }
+ boolean hasCapability( int cap ) throws SmbException {
+ negotiate();
+ return (negotiatedCapabilities & cap) == cap;
+ }
+ void ensureOpen() throws IOException {
+ if( socket == null ) {
+ Object obj;
+ NbtAddress naddr;
+ String calledName;
+
+ /* Hack to convert InetAddress to NbtAddress until we properly
+ * abstract NetBIOSless transport with some kind of "UniSocket".
+ */
+ obj = address.getAddress();
+ if( obj instanceof NbtAddress ) {
+ naddr = (NbtAddress)obj;
+ } else {
+ try {
+ naddr = NbtAddress.getByName( ((InetAddress)obj).getHostAddress() );
+ } catch( UnknownHostException uhe ) {
+ naddr = null; // never happen
+ }
+ }
+
+ calledName = address.firstCalledName();
+ do {
+ try {
+ socket = new NbtSocket( naddr, calledName, port, localAddr, localPort );
+ break;
+ } catch( NbtException ne ) {
+ if( ne.errorClass == NbtException.ERR_SSN_SRVC &&
+ ( ne.errorCode == NbtException.CALLED_NOT_PRESENT ||
+ ne.errorCode == NbtException.NOT_LISTENING_CALLED )) {
+ Log.println( Log.WARNINGS, "smb warning", ne.getMessage() );
+ } else {
+ throw ne;
+ }
+ }
+ } while(( calledName = address.nextCalledName()) != null );
+
+ if( calledName == null ) {
+ throw new IOException( "Failed to establish session with " + address );
+ }
+
+ /* Save the calledName for using on SMB_COM_TREE_CONNECT
+ */
+ if( calledName == NbtAddress.SMBSERVER_NAME ) {
+ server.tconHostName = address.getHostAddress();
+ } else {
+ server.tconHostName = calledName;
+ }
+
+ if( Config.getBoolean( "jcifs.smb.client.tcpNoDelay", false )) {
+ socket.setTcpNoDelay( true );
+ }
+ in = new PushbackInputStream( socket.getInputStream(), PUSHBACK_BUF_SIZE );
+ out = socket.getOutputStream();
+ thread = new Thread( this, "JCIFS-SmbTransport" );
+ thread.setDaemon( true );
+ thread.start();
+ }
+ }
+ void tryClose( boolean inError ) {
+ SmbSession ssn;
+
+ if( socket == null ) {
+ inError = true;
+ }
+
+ ListIterator iter = sessions.listIterator();
+ while( iter.hasNext() ) {
+ ssn = (SmbSession)iter.next();
+ ssn.logoff( inError );
+ }
+ if( socket != null ) {
+ try {
+ socket.close();
+ } catch( IOException ioe ) {
+ }
+ }
+ in = null;
+ out = null;
+ socket = null;
+ negotiated = false;
+ thread = null;
+ responseTable.clear();
+ }
+ public void run() {
+ int mid, l, i, n, m;
+ ServerMessageBlock response;
+ int magic[] = { 0xFF, 'S', 'M', 'B' };
+
+ while( thread == Thread.currentThread() ) {
+ try {
+ socket.setSoTimeout( soTimeout );
+
+ m = 0;
+ while( m < 4 ) {
+ if(( i = in.read() ) < 0 ) {
+ return;
+ }
+ if(( i & 0xFF ) == magic[m] ) {
+ m++;
+ } else if(( i & 0xFF ) == 0xFF ) {
+ m = 1;
+ } else {
+ m = 0;
+ }
+ }
+
+synchronized( rcv_buf ) {
+ rcv_buf[0] = (byte)0xFF;
+ rcv_buf[1] = (byte)'S';
+ rcv_buf[2] = (byte)'M';
+ rcv_buf[3] = (byte)'B';
+
+ if( in.read( rcv_buf, 4, HEADER_LENGTH - 4 ) != ( HEADER_LENGTH - 4 )) {
+ /* Read on a netbios SocketInputStream does not
+ * return until len bytes have been read or the stream is
+ * closed. This must be the latter case.
+ */
+ break;
+ }
+ ((PushbackInputStream)in).unread( rcv_buf, 0, HEADER_LENGTH );
+ if( rcv_buf[0] != (byte)0xFF ||
+ rcv_buf[1] != (byte)'S' ||
+ rcv_buf[2] != (byte)'M' ||
+ rcv_buf[3] != (byte)'B' ) {
+ Log.println( Log.WARNINGS, "smb warning",
+ "bad smb header, purging session message" );
+ in.skip( in.available() );
+ continue;
+ }
+ if(( rcv_buf[FLAGS_OFFSET] & FLAGS_RESPONSE ) == FLAGS_RESPONSE ) {
+ mid = ServerMessageBlock.readInt2( rcv_buf, MID_OFFSET );
+
+ response = (ServerMessageBlock)responseTable.get( new Integer( mid ));
+ if( response == null ) {
+ Log.println( Log.WARNINGS, "smb warning",
+ " no handler for mid=" + mid + ", purging session message" );
+ in.skip( in.available() );
+ continue;
+ }
+ synchronized( response ) {
+ response.useUnicode = useUnicode;
+ Log.println( Log.DEBUGGING, "smb transport warning",
+ " new data read from socket" );
+
+ if( response instanceof SmbComTransactionResponse ) {
+ Enumeration e = (Enumeration)response;
+ if( e.hasMoreElements() ) {
+ e.nextElement();
+ } else {
+ Log.println( Log.WARNINGS, "smb warning",
+ "more responses to transaction than expected" );
+ continue;
+ }
+ response.readWireFormat( in, rcv_buf, 0 );
+ response.received = true;
+ Log.printMessageData( "smb received", response );
+
+ if( response.errorCode != 0 || e.hasMoreElements() == false ) {
+ ((SmbComTransactionResponse)response).hasMore = false;
+ response.notify();
+ } else {
+ ensureOpen();
+ }
+ } else {
+ response.readWireFormat( in, rcv_buf, 0 );
+ response.received = true;
+
+ Log.printMessageData( "smb received", response );
+ ServerMessageBlock smb = response;
+ while( smb instanceof AndXServerMessageBlock &&
+ ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) {
+ Log.printMessageData( "smb andx data", smb );
+ }
+ Log.printHexDump( "smb received", rcv_buf, 0, response.length );
+
+ response.notify();
+ }
+ }
+ } else {
+ // it's a request(break oplock)
+ }
+}
+ } catch( InterruptedIOException iioe ) {
+ if( responseTable.size() == 0 ) {
+ tryClose( false );
+ } else {
+ Log.println( Log.WARNINGS, "smb warning",
+ " soTimeout has occured but there are " +
+ responseTable.size() + " pending requests" );
+ }
+ } catch( IOException ioe ) {
+ tryClose( true );
+ Log.printStackTrace( "exception reading from socket input: " + address, ioe );
+ }
+ }
+ }
+ void send( ServerMessageBlock request,
+ ServerMessageBlock response ) throws SmbException {
+ Integer mid = null;
+
+ negotiate();
+
+ request.flags2 = client.flags2;
+ request.mid = aquireMid();
+ request.useUnicode = useUnicode;
+
+ if( response == null ) {
+ try {
+ synchronized( outLock ) {
+ ensureOpen();
+synchronized( snd_buf ) {
+ out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ out.flush();
+
+ Log.printMessageData( "smb sent", request );
+ ServerMessageBlock smb = request;
+ while( smb instanceof AndXServerMessageBlock &&
+ ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) {
+ Log.printMessageData( "smb andx data", smb );
+ }
+ Log.printHexDump( "smb sent", snd_buf, 0, request.length );
+}
+ }
+ } catch( IOException ioe ) {
+ tryClose( true );
+ throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() );
+ } finally {
+ releaseMid( request.mid );
+ }
+
+ return;
+ }
+
+ // now for the normal case where response is not null
+
+ try {
+ synchronized( response ) {
+
+ response.received = false;
+ mid = new Integer( request.mid );
+ responseTable.put( mid, response );
+
+ synchronized( outLock ) {
+ ensureOpen();
+synchronized( snd_buf ) {
+ out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ out.flush();
+
+ Log.printMessageData( "smb sent", request );
+ ServerMessageBlock smb = request;
+ while( smb instanceof AndXServerMessageBlock &&
+ ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) {
+ Log.printMessageData( "smb andx data", smb );
+ }
+ Log.printHexDump( "smb sent", snd_buf, 0, request.length );
+}
+ }
+
+ // default it 1 so that 0 can be used as forever
+ response.wait( response.responseTimeout == 1 ?
+ responseTimeout : response.responseTimeout );
+ }
+ } catch( InterruptedException ie ) {
+ tryClose( true );
+ } catch( IOException ioe ) {
+ tryClose( true );
+ throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() );
+ } finally {
+ responseTable.remove( mid );
+ releaseMid( request.mid );
+ }
+
+ if( response.received == false ) {
+ tryClose( true );
+ throw new SmbException( SmbException.ERRCLI,
+ SmbException.ERRserverTimeout,
+ address );
+ }
+ switch( response.errorCode & 0xFF ) {
+ case SmbException.SUCCESS:
+ break;
+ case SmbException.ERRDOS:
+ switch(( response.errorCode >> 16 ) & 0xFFFF ) {
+ case SmbException.ERRnoaccess:
+ throw new SmbAuthException( response.errorCode );
+ }
+ throw new SmbException( response.errorCode );
+ case SmbException.ERRSRV:
+ switch(( response.errorCode >> 16 ) & 0xFFFF ) {
+ case SmbException.ERRbadpw:
+ case SmbException.ERRaccess:
+ case SmbException.ERRaccountExpired:
+ case SmbException.ERRbadClient:
+ case SmbException.ERRbadLogonTime:
+ case SmbException.ERRpasswordExpired:
+ throw new SmbAuthException( response.errorCode );
+ }
+ default:
+ throw new SmbException( response.errorCode );
+ }
+
+ }
+ void sendTransaction( SmbComTransaction request,
+ SmbComTransactionResponse response ) throws SmbException {
+ Integer mid = null;
+
+ negotiate();
+
+ request.flags2 = client.flags2;
+ request.mid = aquireMid();
+ mid = new Integer( request.mid );
+ request.useUnicode = useUnicode;
+ request.maxBufferSize = negotiatedMaxBufferSize;
+ response.received = false;
+ response.hasMore = true;
+ response.isPrimary = true;
+
+ try {
+ request.txn_buf = BufferCache.getBuffer();
+ response.txn_buf = BufferCache.getBuffer();
+
+ request.nextElement();
+ if( request.hasMoreElements() ) {
+ // multi-part request
+
+ SmbComBlankResponse interimResponse = new SmbComBlankResponse();
+
+ synchronized( interimResponse ) {
+
+ responseTable.put( mid, interimResponse );
+
+ synchronized( outLock ) {
+ ensureOpen();
+synchronized(snd_buf) {
+ out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ out.flush();
+
+ Log.printMessageData( "smb sent", request );
+ Log.printHexDump( "smb sent", snd_buf, 0, request.length );
+}
+ }
+
+ interimResponse.wait( responseTimeout );
+
+ if( interimResponse.received == false ) {
+ throw new SmbException( SmbException.ERRserverTimeout );
+ }
+ switch( interimResponse.errorCode & 0xFF ) {
+ case SmbException.SUCCESS:
+ break;
+ case SmbException.ERRDOS:
+ switch(( response.errorCode >> 16 ) & 0xFFFF ) {
+ case SmbException.ERRnoaccess:
+ throw new SmbAuthException( response.errorCode );
+ }
+ throw new SmbException( response.errorCode );
+ case SmbException.ERRSRV:
+ switch(( interimResponse.errorCode >> 16 ) & 0xFFFF ) {
+ case SmbException.ERRbadpw:
+ case SmbException.ERRaccess:
+ case SmbException.ERRaccountExpired:
+ case SmbException.ERRbadClient:
+ case SmbException.ERRbadLogonTime:
+ case SmbException.ERRpasswordExpired:
+ throw new SmbAuthException( interimResponse.errorCode );
+ }
+ default:
+ throw new SmbException( interimResponse.errorCode );
+ }
+ }
+ request.nextElement();
+ }
+
+ synchronized( response ) {
+ responseTable.put( mid, response );
+ do {
+ synchronized( outLock ) {
+ ensureOpen();
+synchronized( snd_buf ) {
+ out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 ));
+ out.flush();
+
+ Log.printMessageData( "smb sent", request );
+ Log.printHexDump( "smb sent", snd_buf, 0, request.length );
+}
+ }
+ } while( request.hasMoreElements() && request.nextElement() != null );
+
+ do {
+ // default it 1 so that 0 can be used as forever
+ response.received = false;
+ response.wait( response.responseTimeout == 1 ? responseTimeout : response.responseTimeout );
+ } while( response.received && response.hasMoreElements() );
+ }
+ } catch( InterruptedException ie ) {
+ tryClose( true );
+ } catch( IOException ioe ) {
+ tryClose( true );
+ throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() );
+ } finally {
+ responseTable.remove( mid );
+ releaseMid( request.mid );
+ BufferCache.releaseBuffer( request.txn_buf );
+ BufferCache.releaseBuffer( response.txn_buf );
+ }
+
+ if( response.received == false ) {
+ tryClose( true );
+ throw new SmbException( SmbException.ERRCLI,
+ SmbException.ERRserverTimeout,
+ address );
+ }
+ switch( response.errorCode & 0xFF ) {
+ case SmbException.SUCCESS:
+ break;
+ case SmbException.ERRDOS:
+ switch(( response.errorCode >> 16 ) & 0xFFFF ) {
+ case SmbException.ERRnoaccess:
+ throw new SmbAuthException( response.errorCode );
+ }
+ throw new SmbException( response.errorCode );
+ case SmbException.ERRSRV:
+ switch(( response.errorCode >> 16 ) & 0xFFFF ) {
+ case SmbException.ERRbadpw:
+ case SmbException.ERRaccess:
+ case SmbException.ERRaccountExpired:
+ case SmbException.ERRbadClient:
+ case SmbException.ERRbadLogonTime:
+ case SmbException.ERRpasswordExpired:
+ throw new SmbAuthException( response.errorCode );
+ }
+ default:
+ throw new SmbException( response.errorCode );
+ }
+ }
+ synchronized void negotiate() throws SmbException {
+
+ if( negotiated ) {
+ return;
+ }
+ /* we must set this here rather than later because this
+ * calls send() which calls negotiate so we don't want to
+ * end up in a loop. The alternative would be to inline
+ * the send routine here but this seems okay for now
+ */
+ negotiated = true;
+
+ Log.println( Log.WARNINGS, "smb negotiation warning",
+ " requesting negotiation with " + address );
+
+ /*
+ * Negotiate Protocol Request / Response
+ */
+
+ SmbComNegotiateResponse response = new SmbComNegotiateResponse();
+ send( new SmbComNegotiate(), response );
+
+ if( response.dialectIndex > 10 ) {
+ tryClose( true );
+ throw new SmbException( SmbException.ERRCLI, SmbException.ERRbadDialect );
+ }
+
+ server.securityMode = response.securityMode;
+ server.security = response.security;
+ server.encryptedPasswords = response.encryptedPasswords;
+ server.signaturesEnabled = response.signaturesEnabled;
+ server.signaturesRequired = response.signaturesRequired;
+ negotiatedDialectIndex = response.dialectIndex;;
+ server.maxMpxCount = response.maxMpxCount;
+ negotiatedMaxMpxCount = client.maxMpxCount < server.maxMpxCount ?
+ client.maxMpxCount : server.maxMpxCount;
+ mpxCtrl.maxMpxCount = negotiatedMaxMpxCount < 1 ?
+ 1 : negotiatedMaxMpxCount;
+ server.maxNumberVcs = response.maxNumberVcs;
+ server.maxBufferSize = response.maxBufferSize;
+ negotiatedMaxBufferSize = client.maxBufferSize < server.maxBufferSize ?
+ client.maxBufferSize : server.maxBufferSize;
+ server.maxRawSize = response.maxRawSize;
+ server.sessionKey = response.sessionKey;
+ server.capabilities = response.capabilities;
+ negotiatedCapabilities = client.capabilities & server.capabilities;
+ if(( negotiatedCapabilities & ServerMessageBlock.CAP_UNICODE ) == 0 ) {
+ // server doesn't want unicode
+ if( Config.getBoolean( "jcifs.smb.client.useUnicode", false )) {
+ // force unicode
+ negotiatedCapabilities |= ServerMessageBlock.CAP_UNICODE;
+ } else {
+ // not explicitly set to true so flip unicode off as server requests
+ useUnicode = false;
+ client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE;
+ }
+ }
+ server.serverTime = response.serverTime;
+ server.serverTimeZone = response.serverTimeZone;
+ server.encryptionKeyLength = response.encryptionKeyLength;
+ server.encryptionKey = response.encryptionKey;
+ server.oemDomainName = response.oemDomainName;
+ }
+ public String toString() {
+ String ret = "SmbTransport[address=" + address;
+ if( socket == null ) {
+ ret += ",port=,localAddr=,localPort=]";
+ } else {
+ ret += ",port=" + socket.getPort() +
+ ",localAddr=" + socket.getLocalAddress() +
+ ",localPort=" + socket.getLocalPort() + "]";
+ }
+ return ret;
+ }
+
+ static int aquireMid() throws SmbException {
+ try {
+ return mpxCtrl.aquireMid();
+ } catch( InterruptedException ie ) {
+ throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ie.getMessage() );
+ }
+ }
+ static void releaseMid( int mid ) {
+ mpxCtrl.releaseMid( mid );
+ }
+
+ static class MpxControl {
+ MpxListNode first, last;
+ class MpxListNode {
+ int i;
+ MpxListNode next;
+ MpxListNode( int id ) {
+ i = id;
+ next = null;
+ }
+ }
+ int nextMid, mpxCount, maxMpxCount;
+
+ MpxControl() {
+ nextMid = 0;
+ mpxCount = 0;
+ maxMpxCount = 1;
+ first = last = null;
+ }
+
+ synchronized void clear() {
+ nextMid = 0;
+ mpxCount = 0;
+ maxMpxCount = 1;
+ first = last = null;
+ }
+ synchronized int aquireMid() throws InterruptedException {
+ while( mpxCount >= maxMpxCount ) {
+ wait();
+ }
+ if(( ++nextMid % 0xFFFF ) == 0 ) {
+ nextMid = 1;
+ }
+ add( nextMid );
+ mpxCount++;
+ return nextMid;
+ }
+ synchronized void releaseMid( int i ) {
+ remove( i );
+ mpxCount--;
+ notify();
+ }
+ synchronized boolean contains( int i ) {
+ for( MpxListNode tmp = first; tmp != null; tmp = tmp.next ) {
+ if( tmp.i == i ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ synchronized void add( int i ) {
+ if( contains( i )) {
+ return;
+ }
+ if( first == null ) {
+ first = last = new MpxListNode( i );
+ } else {
+ last = last.next = new MpxListNode( i );
+ }
+ }
+ synchronized void remove( int i ) {
+ if( first == null ) {
+ return;
+ } else if( first == last && first.i == i ) {
+ first = last = null;
+ return;
+ }
+ MpxListNode tmp, prev;
+ for( tmp = prev = first; tmp != null; tmp = tmp.next ) {
+ if( tmp.i == i ) {
+ if( tmp == first ) {
+ first = first.next;
+ } else if( tmp == last ) {
+ last = prev;
+ last.next = null;
+ } else {
+ prev.next = tmp.next;
+ }
+ return;
+ }
+ prev = tmp;
+ }
+ }
+ }
+}
+++ /dev/null
-/* Encodes and decodes to and from Base64 notation.\r
- * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.util;\r
-\r
-public class Base64 {\r
-\r
- private static final String ALPHABET =\r
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
-\r
- /**\r
- * Base-64 encodes the supplied block of data. Line wrapping is not\r
- * applied on output.\r
- *\r
- * @param bytes The block of data that is to be Base-64 encoded.\r
- * @return A <code>String</code> containing the encoded data.\r
- */\r
- public static String encode(byte[] bytes) {\r
- int length = bytes.length;\r
- if (length == 0) return "";\r
- StringBuffer buffer =\r
- new StringBuffer((int) Math.ceil((double) length / 3d) * 4);\r
- int remainder = length % 3;\r
- length -= remainder;\r
- int block;\r
- int i = 0;\r
- while (i < length) {\r
- block = ((bytes[i++] & 0xff) << 16) | ((bytes[i++] & 0xff) << 8) |\r
- (bytes[i++] & 0xff);\r
- buffer.append(ALPHABET.charAt(block >>> 18));\r
- buffer.append(ALPHABET.charAt((block >>> 12) & 0x3f));\r
- buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));\r
- buffer.append(ALPHABET.charAt(block & 0x3f));\r
- }\r
- if (remainder == 0) return buffer.toString();\r
- if (remainder == 1) {\r
- block = (bytes[i] & 0xff) << 4;\r
- buffer.append(ALPHABET.charAt(block >>> 6));\r
- buffer.append(ALPHABET.charAt(block & 0x3f));\r
- buffer.append("==");\r
- return buffer.toString();\r
- }\r
- block = (((bytes[i++] & 0xff) << 8) | ((bytes[i]) & 0xff)) << 2;\r
- buffer.append(ALPHABET.charAt(block >>> 12));\r
- buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));\r
- buffer.append(ALPHABET.charAt(block & 0x3f));\r
- buffer.append("=");\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Decodes the supplied Base-64 encoded string.\r
- *\r
- * @param string The Base-64 encoded string that is to be decoded.\r
- * @return A <code>byte[]</code> containing the decoded data block.\r
- */\r
- public static byte[] decode(String string) {\r
- int length = string.length();\r
- if (length == 0) return new byte[0];\r
- int pad = (string.charAt(length - 2) == '=') ? 2 :\r
- (string.charAt(length - 1) == '=') ? 1 : 0;\r
- int size = length * 3 / 4 - pad;\r
- byte[] buffer = new byte[size];\r
- int block;\r
- int i = 0;\r
- int index = 0;\r
- while (i < length) {\r
- block = (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 18 |\r
- (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 12 |\r
- (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 6 |\r
- (ALPHABET.indexOf(string.charAt(i++)) & 0xff);\r
- buffer[index++] = (byte) (block >>> 16);\r
- if (index < size) buffer[index++] = (byte) ((block >>> 8) & 0xff);\r
- if (index < size) buffer[index++] = (byte) (block & 0xff);\r
- }\r
- return buffer;\r
- }\r
-\r
-}\r
+++ /dev/null
-/* HMACT64 keyed hashing algorithm\r
- * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.util;\r
-\r
-import java.security.MessageDigest;\r
-\r
-/**\r
- * This is an implementation of the HMACT64 keyed hashing algorithm.\r
- * HMACT64 is defined by Luke Leighton as a modified HMAC-MD5 (RFC 2104)\r
- * in which the key is truncated at 64 bytes (rather than being hashed\r
- * via MD5).\r
- */ \r
-public class HMACT64 extends MessageDigest implements Cloneable {\r
-\r
- private static final int BLOCK_LENGTH = 64;\r
-\r
- private static final byte IPAD = (byte) 0x36;\r
-\r
- private static final byte OPAD = (byte) 0x5c;\r
-\r
- private MessageDigest md5;\r
-\r
- private byte[] ipad = new byte[BLOCK_LENGTH];\r
-\r
- private byte[] opad = new byte[BLOCK_LENGTH];\r
-\r
- /**\r
- * Creates an HMACT64 instance which uses the given secret key material.\r
- *\r
- * @param key The key material to use in hashing.\r
- */\r
- public HMACT64(byte[] key) {\r
- super("HMACT64");\r
- int length = Math.min(key.length, BLOCK_LENGTH);\r
- for (int i = 0; i < length; i++) {\r
- ipad[i] = (byte) (key[i] ^ IPAD);\r
- opad[i] = (byte) (key[i] ^ OPAD);\r
- }\r
- for (int i = length; i < BLOCK_LENGTH; i++) {\r
- ipad[i] = IPAD;\r
- opad[i] = OPAD;\r
- }\r
- try {\r
- md5 = MessageDigest.getInstance("MD5");\r
- } catch (Exception ex) {\r
- throw new IllegalStateException(ex.getMessage());\r
- }\r
- engineReset();\r
- }\r
-\r
- private HMACT64(HMACT64 hmac) throws CloneNotSupportedException {\r
- super("HMACT64");\r
- this.ipad = hmac.ipad;\r
- this.opad = hmac.opad;\r
- this.md5 = (MessageDigest) hmac.md5.clone();\r
- }\r
-\r
- public Object clone() {\r
- try {\r
- return new HMACT64(this);\r
- } catch (CloneNotSupportedException ex) {\r
- throw new IllegalStateException(ex.getMessage());\r
- }\r
- }\r
-\r
- protected byte[] engineDigest() {\r
- byte[] digest = md5.digest();\r
- md5.update(opad);\r
- return md5.digest(digest);\r
- }\r
-\r
- protected int engineDigest(byte[] buf, int offset, int len) {\r
- byte[] digest = md5.digest();\r
- md5.update(opad);\r
- md5.update(digest);\r
- try {\r
- return md5.digest(buf, offset, len);\r
- } catch (Exception ex) {\r
- throw new IllegalStateException();\r
- }\r
- }\r
-\r
- protected int engineGetDigestLength() {\r
- return md5.getDigestLength();\r
- }\r
-\r
- protected void engineReset() {\r
- md5.reset();\r
- md5.update(ipad);\r
- }\r
-\r
- protected void engineUpdate(byte b) {\r
- md5.update(b);\r
- }\r
-\r
- protected void engineUpdate(byte[] input, int offset, int len) {\r
- md5.update(input, offset, len);\r
- }\r
-\r
-}\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Jason Pugsley" <jcifs at samba dot org>\r
- * "skeetz" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.http;\r
-\r
-import java.io.*;\r
-import java.util.Enumeration;\r
-import java.net.UnknownHostException;\r
-import javax.servlet.*;\r
-import javax.servlet.http.*;\r
-import jcifs.*;\r
-import jcifs.smb.SmbSession;\r
-import jcifs.smb.NtlmPasswordAuthentication;\r
-import jcifs.smb.SmbAuthException;\r
-import jcifs.util.Base64;\r
-\r
-/**\r
- * This servlet Filter can be used to negotiate password hashes with\r
- * MSIE clients using NTLM SSP. This is similar to <tt>Authentication:\r
- * BASIC</tt> but weakly encrypted and without requiring the user to re-supply\r
- * authentication credentials.\r
- * <p>\r
- * Read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a> for complete details.\r
- */\r
-\r
-public class NtlmHttpFilter implements Filter {\r
-\r
- private String defaultDomain;\r
-\r
- private String domainController;\r
-\r
- private boolean enableBasic;\r
-\r
- private boolean insecureBasic;\r
-\r
- private String realm;\r
-\r
- public void init( FilterConfig filterConfig ) throws ServletException {\r
- String name;\r
-\r
- /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min.\r
- */\r
- Config.setProperty( "jcifs.smb.client.soTimeout", "300000" );\r
- Config.setProperty( "jcifs.netbios.cachePolicy", "600" );\r
-\r
- Enumeration e = filterConfig.getInitParameterNames();\r
- while( e.hasMoreElements() ) {\r
- name = (String)e.nextElement();\r
- if( name.startsWith( "jcifs." )) {\r
- Config.setProperty( name, filterConfig.getInitParameter( name ));\r
- }\r
- }\r
- defaultDomain = Config.getProperty("jcifs.smb.client.domain");\r
- domainController = Config.getProperty( "jcifs.http.domainController" );\r
- if( domainController == null ) domainController = defaultDomain;\r
- enableBasic = Boolean.valueOf(\r
- Config.getProperty("jcifs.http.enableBasic")).booleanValue();\r
- insecureBasic = Boolean.valueOf(\r
- Config.getProperty("jcifs.http.insecureBasic")).booleanValue();\r
- realm = Config.getProperty("jcifs.http.basicRealm");\r
- if (realm == null) realm = "jCIFS";\r
- }\r
- public void destroy() {\r
- }\r
- public void doFilter( ServletRequest request,\r
- ServletResponse response,\r
- FilterChain chain ) throws IOException, ServletException {\r
- HttpServletRequest req;\r
- HttpServletResponse resp;\r
- UniAddress dc;\r
- String msg;\r
-\r
- NtlmPasswordAuthentication ntlm = null;\r
- req = (HttpServletRequest)request;\r
- resp = (HttpServletResponse)response;\r
- msg = req.getHeader( "Authorization" );\r
- boolean offerBasic = enableBasic && (insecureBasic || req.isSecure());\r
-\r
- if( msg != null && (msg.startsWith( "NTLM " ) ||\r
- (offerBasic && msg.startsWith("Basic ")))) {\r
- dc = UniAddress.getByName( domainController, true );\r
- if (msg.startsWith("NTLM ")) {\r
- byte[] challenge = SmbSession.getChallenge( dc );\r
- if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) {\r
- return;\r
- }\r
- } else {\r
- String auth = new String(Base64.decode(msg.substring(6)),\r
- "US-ASCII");\r
- int index = auth.indexOf(':');\r
- String user = (index != -1) ? auth.substring(0, index) : auth;\r
- String password = (index != -1) ? auth.substring(index + 1) :\r
- "";\r
- index = user.indexOf('\\');\r
- if (index == -1) index = user.indexOf('/');\r
- String domain = (index != -1) ? user.substring(0, index) :\r
- defaultDomain;\r
- user = (index != -1) ? user.substring(index + 1) : user;\r
- ntlm = new NtlmPasswordAuthentication(domain, user, password);\r
- }\r
- try {\r
- SmbSession.logon( dc, ntlm );\r
- } catch( SmbAuthException sae ) {\r
- resp.setHeader( "WWW-Authenticate", "NTLM" );\r
- if (offerBasic) {\r
- resp.addHeader( "WWW-Authenticate", "Basic realm=\"" +\r
- realm + "\"");\r
- }\r
- resp.setHeader( "Connection", "close" );\r
- resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );\r
- resp.flushBuffer();\r
- return;\r
- }\r
- req.getSession().setAttribute( "NtlmHttpAuth", ntlm );\r
- } else {\r
- HttpSession ssn = req.getSession(false);\r
- if (ssn == null || (ntlm = (NtlmPasswordAuthentication)\r
- ssn.getAttribute("NtlmHttpAuth")) == null) {\r
- resp.setHeader( "WWW-Authenticate", "NTLM" );\r
- if (offerBasic) {\r
- resp.addHeader( "WWW-Authenticate", "Basic realm=\"" +\r
- realm + "\"");\r
- }\r
- resp.setHeader( "Connection", "close" );\r
- resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );\r
- resp.flushBuffer();\r
- return;\r
- }\r
- }\r
-\r
- chain.doFilter( new NtlmHttpServletRequest( req, ntlm ), response );\r
- }\r
-\r
- // Added by cgross to work with weblogic 6.1.\r
- public void setFilterConfig( FilterConfig f ) {\r
- try {\r
- init( f );\r
- } catch( Exception e ) {\r
- e.printStackTrace();\r
- }\r
- }\r
- public FilterConfig getFilterConfig() {\r
- return null;\r
- }\r
-}\r
-\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.http;\r
-\r
-import java.io.InputStream;\r
-import java.io.IOException;\r
-import java.io.OutputStream;\r
-\r
-import java.net.Authenticator;\r
-import java.net.HttpURLConnection;\r
-import java.net.PasswordAuthentication;\r
-import java.net.ProtocolException;\r
-import java.net.URL;\r
-import java.net.URLDecoder;\r
-\r
-import java.security.Permission;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import jcifs.Config;\r
-\r
-import jcifs.ntlmssp.NtlmFlags;\r
-import jcifs.ntlmssp.NtlmMessage;\r
-import jcifs.ntlmssp.Type1Message;\r
-import jcifs.ntlmssp.Type2Message;\r
-import jcifs.ntlmssp.Type3Message;\r
-\r
-import jcifs.util.Base64;\r
-\r
-/**\r
- * Wraps an <code>HttpURLConnection</code> to provide NTLM authentication\r
- * services.\r
- *\r
- * Please read <a href="../../../httpclient.html">Using jCIFS NTLM Authentication for HTTP Connections</a>.\r
- */\r
-public class NtlmHttpURLConnection extends HttpURLConnection {\r
-\r
- private static final int MAX_REDIRECTS =\r
- Integer.parseInt(System.getProperty("http.maxRedirects", "20"));\r
-\r
- private static final int LM_COMPATIBILITY =\r
- Config.getInt("jcifs.smb.lmCompatibility", 0);\r
-\r
- private static final String DEFAULT_DOMAIN;\r
-\r
- private HttpURLConnection connection;\r
-\r
- private Map requestProperties;\r
-\r
- private Map headerFields;\r
-\r
- private String authProperty;\r
-\r
- private String method;\r
-\r
- static {\r
- String domain = System.getProperty("http.auth.ntlm.domain");\r
- if (domain == null) domain = Type3Message.getDefaultDomain();\r
- DEFAULT_DOMAIN = domain;\r
- }\r
-\r
- public NtlmHttpURLConnection(HttpURLConnection connection) {\r
- super(connection.getURL());\r
- this.connection = connection;\r
- requestProperties = new HashMap();\r
- }\r
-\r
- public void connect() throws IOException {\r
- if (connected) return;\r
- doConnect();\r
- connected = true;\r
- }\r
-\r
- public URL getURL() {\r
- return connection.getURL();\r
- }\r
-\r
- public int getContentLength() {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getContentLength();\r
- }\r
-\r
- public String getContentType() {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getContentType();\r
- }\r
-\r
- public String getContentEncoding() {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getContentEncoding();\r
- }\r
-\r
- public long getExpiration() {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getExpiration();\r
- }\r
-\r
- public long getDate() {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getDate();\r
- }\r
-\r
- public long getLastModified() {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getLastModified();\r
- }\r
-\r
- public String getHeaderField(String header) {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getHeaderField(header);\r
- }\r
-\r
- private synchronized Map getHeaderFields0() {\r
- if (headerFields != null) return headerFields;\r
- Map map = new HashMap();\r
- String key = connection.getHeaderFieldKey(0);\r
- String value = connection.getHeaderField(0);\r
- for (int i = 1; key != null || value != null; i++) {\r
- List values = (List) map.get(key);\r
- if (values == null) {\r
- values = new ArrayList();\r
- map.put(key, values);\r
- }\r
- values.add(value);\r
- key = connection.getHeaderFieldKey(i);\r
- value = connection.getHeaderField(i);\r
- }\r
- Iterator entries = map.entrySet().iterator();\r
- while (entries.hasNext()) {\r
- Map.Entry entry = (Map.Entry) entries.next();\r
- entry.setValue(Collections.unmodifiableList((List)\r
- entry.getValue()));\r
- }\r
- return (headerFields = Collections.unmodifiableMap(map));\r
- }\r
-\r
- public Map getHeaderFields() {\r
- synchronized (this) {\r
- if (headerFields != null) return headerFields;\r
- }\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return getHeaderFields0();\r
- }\r
-\r
- public int getHeaderFieldInt(String header, int def) {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getHeaderFieldInt(header, def);\r
- }\r
-\r
- public long getHeaderFieldDate(String header, long def) {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getHeaderFieldDate(header, def);\r
- }\r
-\r
- public String getHeaderFieldKey(int index) {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getHeaderFieldKey(index);\r
- }\r
-\r
- public String getHeaderField(int index) {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getHeaderField(index);\r
- }\r
-\r
- public Object getContent() throws IOException {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getContent();\r
- }\r
-\r
- public Object getContent(Class[] classes) throws IOException {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getContent(classes);\r
- }\r
-\r
- public Permission getPermission() throws IOException {\r
- return connection.getPermission();\r
- }\r
-\r
- public InputStream getInputStream() throws IOException {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getInputStream();\r
- }\r
-\r
- public OutputStream getOutputStream() throws IOException {\r
- try {\r
- connect();\r
- } catch (IOException ex) { }\r
- return connection.getOutputStream();\r
- }\r
-\r
- public String toString() {\r
- return connection.toString();\r
- }\r
-\r
- public void setDoInput(boolean doInput) {\r
- connection.setDoInput(doInput);\r
- this.doInput = doInput;\r
- }\r
-\r
- public boolean getDoInput() {\r
- return connection.getDoInput();\r
- }\r
-\r
- public void setDoOutput(boolean doOutput) {\r
- connection.setDoOutput(doOutput);\r
- this.doOutput = doOutput;\r
- }\r
-\r
- public boolean getDoOutput() {\r
- return connection.getDoOutput();\r
- }\r
-\r
- public void setAllowUserInteraction(boolean allowUserInteraction) {\r
- connection.setAllowUserInteraction(allowUserInteraction);\r
- this.allowUserInteraction = allowUserInteraction;\r
- }\r
-\r
- public boolean getAllowUserInteraction() {\r
- return connection.getAllowUserInteraction();\r
- }\r
-\r
- public void setUseCaches(boolean useCaches) {\r
- connection.setUseCaches(useCaches);\r
- this.useCaches = useCaches;\r
- }\r
-\r
- public boolean getUseCaches() {\r
- return connection.getUseCaches();\r
- }\r
-\r
- public void setIfModifiedSince(long ifModifiedSince) {\r
- connection.setIfModifiedSince(ifModifiedSince);\r
- this.ifModifiedSince = ifModifiedSince;\r
- }\r
-\r
- public long getIfModifiedSince() {\r
- return connection.getIfModifiedSince();\r
- }\r
-\r
- public boolean getDefaultUseCaches() {\r
- return connection.getDefaultUseCaches();\r
- }\r
-\r
- public void setDefaultUseCaches(boolean defaultUseCaches) {\r
- connection.setDefaultUseCaches(defaultUseCaches);\r
- }\r
-\r
- public void setRequestProperty(String key, String value) {\r
- if (key == null) throw new NullPointerException();\r
- List values = new ArrayList();\r
- values.add(value);\r
- boolean found = false;\r
- synchronized (requestProperties) {\r
- Iterator entries = requestProperties.entrySet().iterator();\r
- while (entries.hasNext()) {\r
- Map.Entry entry = (Map.Entry) entries.next();\r
- if (key.equalsIgnoreCase((String) entry.getKey())) {\r
- entry.setValue(value);\r
- found = true;\r
- break;\r
- }\r
- }\r
- if (!found) requestProperties.put(key, values);\r
- }\r
- connection.setRequestProperty(key, value);\r
- }\r
-\r
- public void addRequestProperty(String key, String value) {\r
- if (key == null) throw new NullPointerException();\r
- List values = null;\r
- synchronized (requestProperties) {\r
- Iterator entries = requestProperties.entrySet().iterator();\r
- while (entries.hasNext()) {\r
- Map.Entry entry = (Map.Entry) entries.next();\r
- if (key.equalsIgnoreCase((String) entry.getKey())) {\r
- values = (List) entry.getValue();\r
- values.add(value);\r
- break;\r
- }\r
- }\r
- if (values == null) {\r
- values = new ArrayList();\r
- values.add(value);\r
- requestProperties.put(key, values);\r
- }\r
- }\r
- // 1.3-compatible.\r
- StringBuffer buffer = new StringBuffer();\r
- Iterator propertyValues = values.iterator();\r
- while (propertyValues.hasNext()) {\r
- buffer.append(propertyValues.next());\r
- if (propertyValues.hasNext()) {\r
- buffer.append(", ");\r
- }\r
- }\r
- connection.setRequestProperty(key, buffer.toString());\r
- }\r
-\r
- public String getRequestProperty(String key) {\r
- return connection.getRequestProperty(key);\r
- }\r
-\r
- public Map getRequestProperties() {\r
- Map map = new HashMap();\r
- synchronized (requestProperties) {\r
- Iterator entries = requestProperties.entrySet().iterator();\r
- while (entries.hasNext()) {\r
- Map.Entry entry = (Map.Entry) entries.next();\r
- map.put(entry.getKey(),\r
- Collections.unmodifiableList((List) entry.getValue()));\r
- }\r
- }\r
- return Collections.unmodifiableMap(map);\r
- }\r
-\r
- public void setInstanceFollowRedirects(boolean instanceFollowRedirects) {\r
- connection.setInstanceFollowRedirects(instanceFollowRedirects);\r
- }\r
-\r
- public boolean getInstanceFollowRedirects() {\r
- return connection.getInstanceFollowRedirects();\r
- }\r
-\r
- public void setRequestMethod(String requestMethod)\r
- throws ProtocolException {\r
- connection.setRequestMethod(requestMethod);\r
- }\r
-\r
- public String getRequestMethod() {\r
- return connection.getRequestMethod();\r
- }\r
-\r
- public int getResponseCode() throws IOException {\r
- return connection.getResponseCode();\r
- }\r
-\r
- public String getResponseMessage() throws IOException {\r
- return connection.getResponseMessage();\r
- }\r
-\r
- public void disconnect() {\r
- connection.disconnect();\r
- connected = false;\r
- }\r
-\r
- public boolean usingProxy() {\r
- return connection.usingProxy();\r
- }\r
-\r
- public InputStream getErrorStream() {\r
- return connection.getErrorStream();\r
- }\r
-\r
- private int parseResponseCode() throws IOException {\r
- try {\r
- String response = connection.getHeaderField(0);\r
- int index = response.indexOf(' ');\r
- while (response.charAt(index) == ' ') index++;\r
- return Integer.parseInt(response.substring(index, index + 3));\r
- } catch (Exception ex) {\r
- throw new IOException(ex.getMessage());\r
- }\r
- }\r
-\r
- private synchronized void doConnect() throws IOException {\r
- connection.connect();\r
- int response = parseResponseCode();\r
- if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
- return;\r
- }\r
- Type1Message type1 = (Type1Message) attemptNegotiation(response);\r
- if (type1 == null) return; // no NTLM\r
- int attempt = 0;\r
- while (attempt < MAX_REDIRECTS) {\r
- connection.setRequestProperty(authProperty, method + ' ' +\r
- Base64.encode(type1.toByteArray()));\r
- connection.connect(); // send type 1\r
- response = parseResponseCode();\r
- if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
- return;\r
- }\r
- Type3Message type3 = (Type3Message) attemptNegotiation(response);\r
- if (type3 == null) return;\r
- connection.setRequestProperty(authProperty, method + ' ' +\r
- Base64.encode(type3.toByteArray()));\r
- connection.connect(); // send type 3\r
- response = parseResponseCode();\r
- if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
- return;\r
- }\r
- attempt++;\r
- if (attempt < MAX_REDIRECTS) reconnect();\r
- }\r
- throw new IOException("Unable to negotiate NTLM authentication.");\r
- }\r
-\r
- private NtlmMessage attemptNegotiation(int response) throws IOException {\r
- authProperty = null;\r
- method = null;\r
- InputStream errorStream = connection.getErrorStream();\r
- if (errorStream != null && errorStream.available() != 0) {\r
- int count;\r
- byte[] buf = new byte[1024];\r
- while ((count = errorStream.read(buf, 0, 1024)) != -1);\r
- }\r
- String authHeader;\r
- if (response == HTTP_UNAUTHORIZED) {\r
- authHeader = "WWW-Authenticate";\r
- authProperty = "Authorization";\r
- } else {\r
- authHeader = "Proxy-Authenticate";\r
- authProperty = "Proxy-Authorization";\r
- }\r
- String authorization = null;\r
- List methods = (List) getHeaderFields0().get(authHeader);\r
- if (methods == null) return null;\r
- Iterator iterator = methods.iterator();\r
- while (iterator.hasNext()) {\r
- String authMethod = (String) iterator.next();\r
- if (authMethod.startsWith("NTLM")) {\r
- if (authMethod.length() == 4) {\r
- method = "NTLM";\r
- break;\r
- }\r
- if (authMethod.indexOf(' ') != 4) continue;\r
- method = "NTLM";\r
- authorization = authMethod.substring(5).trim();\r
- break;\r
- } else if (authMethod.startsWith("Negotiate")) {\r
- if (authMethod.length() == 9) {\r
- method = "Negotiate";\r
- break;\r
- }\r
- if (authMethod.indexOf(' ') != 9) continue;\r
- method = "Negotiate";\r
- authorization = authMethod.substring(10).trim();\r
- break;\r
- }\r
- }\r
- if (method == null) return null;\r
- NtlmMessage message = (authorization != null) ?\r
- new Type2Message(Base64.decode(authorization)) : null;\r
- reconnect();\r
- if (message == null) {\r
- message = new Type1Message();\r
- if (LM_COMPATIBILITY > 2) {\r
- message.setFlag(NtlmFlags.NTLMSSP_REQUEST_TARGET, true);\r
- }\r
- } else {\r
- String domain = DEFAULT_DOMAIN;\r
- String user = Type3Message.getDefaultUser();\r
- String password = Type3Message.getDefaultPassword();\r
- String userInfo = url.getUserInfo();\r
- if (userInfo != null) {\r
- userInfo = URLDecoder.decode(userInfo);\r
- int index = userInfo.indexOf(':');\r
- user = (index != -1) ? userInfo.substring(0, index) : userInfo;\r
- if (index != -1) password = userInfo.substring(index + 1);\r
- index = user.indexOf('\\');\r
- if (index == -1) index = user.indexOf('/');\r
- domain = (index != -1) ? user.substring(0, index) : domain;\r
- user = (index != -1) ? user.substring(index + 1) : user;\r
- }\r
- if (user == null) {\r
- try {\r
- URL url = getURL();\r
- String protocol = url.getProtocol();\r
- int port = url.getPort();\r
- if (port == -1) {\r
- port = "https".equalsIgnoreCase(protocol) ? 443 : 80;\r
- }\r
- PasswordAuthentication auth =\r
- Authenticator.requestPasswordAuthentication(null,\r
- port, protocol, "", method);\r
- if (auth != null) {\r
- user = auth.getUserName();\r
- password = new String(auth.getPassword());\r
- }\r
- } catch (Exception ex) { }\r
- }\r
- Type2Message type2 = (Type2Message) message;\r
- message = new Type3Message(type2, password, domain, user,\r
- Type3Message.getDefaultWorkstation());\r
- }\r
- return message;\r
- }\r
-\r
- private void reconnect() throws IOException {\r
- connection = (HttpURLConnection) connection.getURL().openConnection();\r
- headerFields = null;\r
- synchronized (requestProperties) {\r
- Iterator properties = requestProperties.entrySet().iterator();\r
- while (properties.hasNext()) {\r
- Map.Entry property = (Map.Entry) properties.next();\r
- String key = (String) property.getKey();\r
- StringBuffer value = new StringBuffer();\r
- Iterator values = ((List) property.getValue()).iterator();\r
- while (values.hasNext()) {\r
- value.append(values.next());\r
- if (values.hasNext()) value.append(", ");\r
- }\r
- connection.setRequestProperty(key, value.toString());\r
- }\r
- }\r
- connection.setAllowUserInteraction(allowUserInteraction);\r
- connection.setDoInput(doInput);\r
- connection.setDoOutput(doOutput);\r
- connection.setIfModifiedSince(ifModifiedSince);\r
- connection.setUseCaches(useCaches);\r
- }\r
-}\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.ntlmssp;\r
-\r
-import jcifs.Config;\r
-\r
-/**\r
- * Abstract superclass for all NTLMSSP messages.\r
- */\r
-public abstract class NtlmMessage implements NtlmFlags {\r
-\r
- /**\r
- * The NTLMSSP "preamble".\r
- */\r
- protected static final byte[] NTLMSSP_SIGNATURE = new byte[] {\r
- (byte) 'N', (byte) 'T', (byte) 'L', (byte) 'M',\r
- (byte) 'S', (byte) 'S', (byte) 'P', (byte) 0\r
- };\r
-\r
- private static final String OEM_ENCODING =\r
- Config.getProperty("jcifs.smb.client.codepage",\r
- Config.getProperty("jcifs.encoding",\r
- System.getProperty("file.encoding")));\r
-\r
- private int flags;\r
-\r
- /**\r
- * Returns the flags currently in use for this message.\r
- *\r
- * @return An <code>int</code> containing the flags in use for this\r
- * message.\r
- */\r
- public int getFlags() {\r
- return flags;\r
- }\r
-\r
- /**\r
- * Sets the flags for this message.\r
- *\r
- * @param flags The flags for this message.\r
- */\r
- public void setFlags(int flags) {\r
- this.flags = flags;\r
- }\r
-\r
- /**\r
- * Returns the status of the specified flag.\r
- *\r
- * @param flag The flag to test (i.e., <code>NTLMSSP_NEGOTIATE_OEM</code>).\r
- * @return A <code>boolean</code> indicating whether the flag is set.\r
- */\r
- public boolean getFlag(int flag) {\r
- return (getFlags() & flag) != 0;\r
- }\r
-\r
- /**\r
- * Sets or clears the specified flag.\r
- * \r
- * @param flag The flag to set/clear (i.e.,\r
- * <code>NTLMSSP_NEGOTIATE_OEM</code>).\r
- * @param value Indicates whether to set (<code>true</code>) or\r
- * clear (<code>false</code>) the specified flag.\r
- */\r
- public void setFlag(int flag, boolean value) {\r
- setFlags(value ? (getFlags() | flag) :\r
- (getFlags() & (0xffffffff ^ flag)));\r
- }\r
-\r
- static int readULong(byte[] src, int index) {\r
- return (src[index] & 0xff) |\r
- ((src[index + 1] & 0xff) << 8) |\r
- ((src[index + 2] & 0xff) << 16) |\r
- ((src[index + 3] & 0xff) << 24);\r
- }\r
-\r
- static int readUShort(byte[] src, int index) {\r
- return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);\r
- }\r
-\r
- static byte[] readSecurityBuffer(byte[] src, int index) {\r
- int length = readUShort(src, index);\r
- int offset = readULong(src, index + 4);\r
- byte[] buffer = new byte[length];\r
- System.arraycopy(src, offset, buffer, 0, length);\r
- return buffer;\r
- }\r
-\r
- static void writeULong(byte[] dest, int offset, int ulong) {\r
- dest[offset] = (byte) (ulong & 0xff);\r
- dest[offset + 1] = (byte) (ulong >> 8 & 0xff);\r
- dest[offset + 2] = (byte) (ulong >> 16 & 0xff);\r
- dest[offset + 3] = (byte) (ulong >> 24 & 0xff);\r
- }\r
-\r
- static void writeUShort(byte[] dest, int offset, int ushort) {\r
- dest[offset] = (byte) (ushort & 0xff);\r
- dest[offset + 1] = (byte) (ushort >> 8 & 0xff);\r
- }\r
-\r
- static void writeSecurityBuffer(byte[] dest, int offset, int bodyOffset,\r
- byte[] src) {\r
- int length = (src != null) ? src.length : 0;\r
- if (length == 0) return;\r
- writeUShort(dest, offset, length);\r
- writeUShort(dest, offset + 2, length);\r
- writeULong(dest, offset + 4, bodyOffset);\r
- System.arraycopy(src, 0, dest, bodyOffset, length);\r
- }\r
-\r
- static String getOEMEncoding() {\r
- return OEM_ENCODING;\r
- }\r
-\r
- /**\r
- * Returns the raw byte representation of this message.\r
- *\r
- * @return A <code>byte[]</code> containing the raw message material.\r
- */\r
- public abstract byte[] toByteArray();\r
-\r
-}\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.smb;\r
-\r
-import jcifs.util.DES;\r
-import jcifs.util.MD4;\r
-import jcifs.util.HMACT64;\r
-import java.io.UnsupportedEncodingException;\r
-import java.security.Principal;\r
-import java.security.SecureRandom;\r
-import java.util.Arrays;\r
-import jcifs.Config;\r
-\r
-/**\r
- * This class stores and encrypts NTLM user credentials. The default\r
- * credentials are retrieved from the <tt>jcifs.smb.client.domain</tt>,\r
- * <tt>jcifs.smb.client.username</tt>, and <tt>jcifs.smb.client.password</tt>\r
- * properties.\r
- * <p>\r
- * Read <a href="../../../authhandler.html">jCIFS Exceptions and\r
- * NtlmAuthenticator</a> for related information.\r
- */\r
-\r
-public final class NtlmPasswordAuthentication implements Principal {\r
-\r
- private static final int LM_COMPATIBILITY =\r
- Config.getInt("jcifs.smb.lmCompatibility", 0);\r
-\r
- private static final String DEFAULT_DOMAIN =\r
- Config.getProperty("jcifs.smb.client.domain", "?");\r
-\r
- private static final String DEFAULT_USERNAME =\r
- Config.getProperty("jcifs.smb.client.username", "GUEST");\r
-\r
- private static final String DEFAULT_PASSWORD =\r
- Config.getProperty("jcifs.smb.client.password", "");\r
-\r
- private static final SecureRandom random = new SecureRandom();\r
-\r
- // KGS!@#$%\r
- static final byte[] S8 = {\r
- (byte)0x4b, (byte)0x47, (byte)0x53, (byte)0x21,\r
- (byte)0x40, (byte)0x23, (byte)0x24, (byte)0x25\r
- };\r
- static void E( byte[] key, byte[] data, byte[] e ) {\r
- byte[] key7 = new byte[7];\r
- byte[] e8 = new byte[8];\r
-\r
- for( int i = 0; i < key.length / 7; i++ ) {\r
- System.arraycopy( key, i * 7, key7, 0, 7 );\r
- DES des = new DES( key7 );\r
- des.encrypt( data, e8 );\r
- System.arraycopy( e8, 0, e, i * 8, 8 );\r
- }\r
- }\r
-/**\r
- * Generate the ANSI DES hash for the password associated with these credentials.\r
- */\r
- static public byte[] getPreNTLMResponse( String password, byte[] challenge ) {\r
- byte[] p14 = new byte[14];\r
- byte[] p21 = new byte[21];\r
- byte[] p24 = new byte[24];\r
- byte[] passwordBytes;\r
- try {\r
- passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.encoding );\r
- } catch( UnsupportedEncodingException uee ) {\r
- return null;\r
- }\r
- int passwordLength = passwordBytes.length;\r
-\r
- // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM\r
- if( passwordLength > 14) {\r
- passwordLength = 14;\r
- }\r
- System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );\r
- E( p14, S8, p21);\r
- E( p21, challenge, p24);\r
- return p24;\r
- }\r
-/**\r
- * Generate the Unicode MD4 hash for the password associated with these credentials.\r
- */\r
- static public byte[] getNTLMResponse( String password, byte[] challenge ) {\r
- byte[] uni = null;\r
- byte[] p21 = new byte[21];\r
- byte[] p24 = new byte[24];\r
-\r
- try {\r
- uni = password.getBytes( "UnicodeLittleUnmarked" );\r
- } catch( UnsupportedEncodingException uee ) {\r
- Log.printStackTrace( "password encryption exception", uee );\r
- }\r
- MD4 md4 = new MD4();\r
- md4.update( uni );\r
- try {\r
- md4.digest(p21, 0, 16);\r
- } catch (Exception ex) {\r
- Log.printStackTrace( "digest exception", ex );\r
- }\r
- E( p21, challenge, p24 );\r
- return p24;\r
- }\r
-\r
- /**\r
- * Creates the LMv2 response for the supplied information.\r
- *\r
- * @param domain The domain in which the username exists.\r
- * @param user The username.\r
- * @param password The user's password.\r
- * @param challenge The server challenge.\r
- * @param clientChallenge The client challenge (nonce). \r
- */ \r
- public static byte[] getLMv2Response(String domain, String user,\r
- String password, byte[] challenge, byte[] clientChallenge) {\r
- try {\r
- byte[] hash = new byte[16];\r
- byte[] response = new byte[24];\r
- MD4 md4 = new MD4();\r
- md4.update(password.getBytes("UnicodeLittleUnmarked"));\r
- HMACT64 hmac = new HMACT64(md4.digest());\r
- hmac.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked"));\r
- hmac.update(domain.toUpperCase().getBytes("UnicodeLittleUnmarked"));\r
- hmac = new HMACT64(hmac.digest());\r
- hmac.update(challenge);\r
- hmac.update(clientChallenge);\r
- hmac.digest(response, 0, 16);\r
- System.arraycopy(clientChallenge, 0, response, 16, 8);\r
- return response;\r
- } catch (Exception ex) {\r
- Log.printStackTrace("Error creating LMv2 response", ex);\r
- return null;\r
- }\r
- }\r
-\r
- static final NtlmPasswordAuthentication NULL =\r
- new NtlmPasswordAuthentication( "", "", "" );\r
- static final NtlmPasswordAuthentication GUEST =\r
- new NtlmPasswordAuthentication( "?", "GUEST", "" );\r
-\r
- String domain;\r
- String username;\r
- String password;\r
- byte[] ansiHash;\r
- byte[] unicodeHash;\r
- boolean hashesExternal = false;\r
-\r
-/**\r
- * Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo\r
- * component of an SMB URL like "<tt>domain;user:pass</tt>". This constructor\r
- * is used internally be jCIFS when parsing SMB URLs.\r
- */\r
-\r
- public NtlmPasswordAuthentication( String userInfo ) {\r
- domain = username = password = null;\r
-\r
- if( userInfo != null ) {\r
- int i, u, end;\r
- char c;\r
-\r
- end = userInfo.length();\r
- for( i = 0, u = 0; i < end; i++ ) {\r
- c = userInfo.charAt( i );\r
- if( c == ';' ) {\r
- domain = userInfo.substring( 0, i );\r
- u = i + 1;\r
- } else if( c == ':' ) {\r
- password = userInfo.substring( i + 1 );\r
- break;\r
- }\r
- }\r
- username = userInfo.substring( u, i );\r
- }\r
-\r
- if( domain == null ) this.domain = DEFAULT_DOMAIN;\r
- if( username == null ) this.username = DEFAULT_USERNAME;\r
- if( password == null ) this.password = DEFAULT_PASSWORD;\r
- }\r
-/**\r
- * Create an <tt>NtlmPasswordAuthentication</tt> object from a\r
- * domain, username, and password. Parameters that are <tt>null</tt>\r
- * will be substituted with <tt>jcifs.smb.client.domain</tt>,\r
- * <tt>jcifs.smb.client.username</tt>, <tt>jcifs.smb.client.password</tt>\r
- * property values.\r
- */\r
- public NtlmPasswordAuthentication( String domain, String username, String password ) {\r
- this.domain = domain;\r
- this.username = username;\r
- this.password = password;\r
- if( domain == null ) this.domain = DEFAULT_DOMAIN;\r
- if( username == null ) this.username = DEFAULT_USERNAME;\r
- if( password == null ) this.password = DEFAULT_PASSWORD;\r
- }\r
-/**\r
- * Create an <tt>NtlmPasswordAuthentication</tt> object with raw password\r
- * hashes. This is used exclusively by the <tt>jcifs.http.NtlmSsp</tt>\r
- * class which is in turn used by NTLM HTTP authentication functionality.\r
- */\r
- public NtlmPasswordAuthentication( String domain, String username,\r
- byte[] ansiHash, byte[] unicodeHash ) {\r
- if( domain == null || username == null ||\r
- ansiHash == null || unicodeHash == null ) {\r
- throw new IllegalArgumentException( "External credentials cannot null" );\r
- }\r
- this.domain = domain;\r
- this.username = username;\r
- this.password = null;\r
- this.ansiHash = ansiHash;\r
- this.unicodeHash = unicodeHash;\r
- hashesExternal = true;\r
- }\r
-\r
-/**\r
- * Returns the domain.\r
- */\r
- public String getDomain() {\r
- return domain;\r
- }\r
-/**\r
- * Returns the username.\r
- */\r
- public String getUsername() {\r
- return username;\r
- }\r
-/**\r
- * Returns the password in plain text or <tt>null</tt> if the raw password\r
- * hashes were used to construct this <tt>NtlmPasswordAuthentication</tt>\r
- * object which will be the case when NTLM HTTP Authentication is\r
- * used. There is no way to retrieve a users password in plain text unless\r
- * it is supplied by the user at runtime.\r
- */\r
- public String getPassword() {\r
- return password;\r
- }\r
-/**\r
- * Return the domain and username in the format:\r
- * <tt>domain\\username</tt>. This is equivalent to <tt>toString()</tt>.\r
- */\r
- public String getName() {\r
- boolean d = domain.length() > 0 && domain.equals( "?" ) == false;\r
- return d ? domain + "\\" + username : username;\r
- }\r
-/**\r
- * Computes the 24 byte ANSI password hash given the 8 byte server challenge.\r
- */\r
- public byte[] getAnsiHash( byte[] challenge ) {\r
- if( hashesExternal ) {\r
- return ansiHash;\r
- }\r
- switch (LM_COMPATIBILITY) {\r
- case 0:\r
- case 1:\r
- return getPreNTLMResponse( password, challenge );\r
- case 2:\r
- return getNTLMResponse( password, challenge );\r
- case 3:\r
- case 4:\r
- case 5:\r
- byte[] clientChallenge = new byte[8];\r
- random.nextBytes(clientChallenge);\r
- return getLMv2Response(domain, username, password, challenge,\r
- clientChallenge);\r
- default:\r
- return getPreNTLMResponse( password, challenge );\r
- }\r
- }\r
-/**\r
- * Computes the 24 byte Unicode password hash given the 8 byte server challenge.\r
- */\r
- public byte[] getUnicodeHash( byte[] challenge ) {\r
- if( hashesExternal ) {\r
- return unicodeHash;\r
- }\r
- switch (LM_COMPATIBILITY) {\r
- case 0:\r
- case 1:\r
- case 2:\r
- return getNTLMResponse( password, challenge );\r
- case 3:\r
- case 4:\r
- case 5:\r
- /*\r
- byte[] clientChallenge = new byte[8];\r
- random.nextBytes(clientChallenge);\r
- return getNTLMv2Response(domain, username, password, null,\r
- challenge, clientChallenge);\r
- */\r
- return new byte[0];\r
- default:\r
- return getNTLMResponse( password, challenge );\r
- }\r
- }\r
-/**\r
- * Compares two <tt>NtlmPasswordAuthentication</tt> objects for\r
- * equality. Two <tt>NtlmPasswordAuthentication</tt> objects are equal if\r
- * 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.\r
- */\r
- public boolean equals( Object obj ) {\r
- if( obj instanceof NtlmPasswordAuthentication ) {\r
- NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj;\r
- if( ntlm.domain.toUpperCase().equals( domain.toUpperCase() ) &&\r
- ntlm.username.toUpperCase().equals( username.toUpperCase() )) {\r
- if( hashesExternal && ntlm.hashesExternal ) {\r
- return Arrays.equals( ansiHash, ntlm.ansiHash ) &&\r
- Arrays.equals( unicodeHash, ntlm.unicodeHash );\r
- /* This still isn't quite right. If one npa object does not have external\r
- * hashes and the other does then they will not be considered equal even\r
- * though they may be.\r
- */\r
- } else if( !hashesExternal && password.equals( ntlm.password )) {\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
-\r
-\r
-/**\r
- * Return the upcased username hash code.\r
- */\r
- public int hashCode() {\r
- return getName().toUpperCase().hashCode();\r
- }\r
-/**\r
- * Return the domain and username in the format:\r
- * <tt>domain\\username</tt>. This is equivalent to <tt>getName()</tt>.\r
- */\r
- public String toString() {\r
- return getName();\r
- }\r
-}\r
-\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.http;\r
-\r
-import java.io.IOException;\r
-\r
-import java.net.UnknownHostException;\r
-\r
-import java.util.Enumeration;\r
-\r
-import javax.servlet.ServletConfig;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.UnavailableException;\r
-\r
-import javax.servlet.http.HttpSession;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import jcifs.Config;\r
-import jcifs.UniAddress;\r
-\r
-import jcifs.smb.NtlmPasswordAuthentication;\r
-import jcifs.smb.SmbAuthException;\r
-import jcifs.smb.SmbSession;\r
-\r
-import jcifs.util.Base64;\r
-\r
-/**\r
- * This servlet may be used with pre-2.3 servlet containers\r
- * to protect content with NTLM HTTP Authentication. Servlets that\r
- * extend this abstract base class may be authenticatied against an SMB\r
- * server or domain controller depending on how the\r
- * <tt>jcifs.smb.client.domain</tt> or <tt>jcifs.http.domainController</tt>\r
- * properties are be specified. <b>With later containers the\r
- * <tt>NtlmHttpFilter</tt> should be used/b>. For custom NTLM HTTP Authentication schemes the <tt>NtlmSsp</tt> may be used.\r
- * <p>\r
- * Read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a> related information.\r
- */\r
-\r
-public abstract class NtlmServlet extends HttpServlet {\r
-\r
- private String defaultDomain;\r
-\r
- private String domainController;\r
-\r
- private boolean enableBasic;\r
-\r
- private boolean insecureBasic;\r
-\r
- private String realm;\r
-\r
- public void init(ServletConfig config) throws ServletException {\r
- super.init(config);\r
-\r
- /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min.\r
- */\r
- Config.setProperty( "jcifs.smb.client.soTimeout", "300000" );\r
- Config.setProperty( "jcifs.netbios.cachePolicy", "600" );\r
-\r
- Enumeration e = config.getInitParameterNames();\r
- String name;\r
- while (e.hasMoreElements()) {\r
- name = (String) e.nextElement();\r
- if (name.startsWith("jcifs.")) {\r
- Config.setProperty(name, config.getInitParameter(name));\r
- }\r
- }\r
- defaultDomain = Config.getProperty("jcifs.smb.client.domain");\r
- domainController = Config.getProperty("jcifs.http.domainController");\r
- if (domainController == null) domainController = defaultDomain;\r
- enableBasic = Boolean.valueOf(\r
- Config.getProperty("jcifs.http.enableBasic")).booleanValue();\r
- insecureBasic = Boolean.valueOf(\r
- Config.getProperty("jcifs.http.insecureBasic")).booleanValue();\r
- realm = Config.getProperty("jcifs.http.basicRealm");\r
- if (realm == null) realm = "jCIFS";\r
- }\r
-\r
- protected void service(HttpServletRequest request,\r
- HttpServletResponse response) throws ServletException, IOException {\r
- boolean offerBasic = enableBasic &&\r
- (insecureBasic || request.isSecure());\r
- String msg = request.getHeader("Authorization");\r
- if (msg != null && (msg.startsWith("NTLM ") ||\r
- (offerBasic && msg.startsWith("Basic ")))) {\r
- UniAddress dc = UniAddress.getByName(domainController, true);\r
- NtlmPasswordAuthentication ntlm;\r
- if (msg.startsWith("NTLM ")) {\r
- byte[] challenge = SmbSession.getChallenge(dc);\r
- ntlm = NtlmSsp.authenticate(request, response, challenge);\r
- if (ntlm == null) return;\r
- } else {\r
- String auth = new String(Base64.decode(msg.substring(6)),\r
- "US-ASCII");\r
- int index = auth.indexOf(':');\r
- String user = (index != -1) ? auth.substring(0, index) : auth;\r
- String password = (index != -1) ? auth.substring(index + 1) :\r
- "";\r
- index = user.indexOf('\\');\r
- if (index == -1) index = user.indexOf('/');\r
- String domain = (index != -1) ? user.substring(0, index) :\r
- defaultDomain;\r
- user = (index != -1) ? user.substring(index + 1) : user;\r
- ntlm = new NtlmPasswordAuthentication(domain, user, password);\r
- }\r
- try {\r
- SmbSession.logon(dc, ntlm);\r
- } catch (SmbAuthException sae) {\r
- response.setHeader("WWW-Authenticate", "NTLM");\r
- if (offerBasic) {\r
- response.addHeader("WWW-Authenticate", "Basic realm=\"" +\r
- realm + "\"");\r
- }\r
- response.setHeader("Connection", "close");\r
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
- response.flushBuffer();\r
- return;\r
- }\r
- HttpSession ssn = request.getSession();\r
- ssn.setAttribute("NtlmHttpAuth", ntlm);\r
- ssn.setAttribute( "ntlmdomain", ntlm.getDomain() );\r
- ssn.setAttribute( "ntlmuser", ntlm.getUsername() );\r
- } else {\r
- HttpSession ssn = request.getSession(false);\r
- if (ssn == null || ssn.getAttribute("NtlmHttpAuth") == null) {\r
- response.setHeader("WWW-Authenticate", "NTLM");\r
- if (offerBasic) {\r
- response.addHeader("WWW-Authenticate", "Basic realm=\"" +\r
- realm + "\"");\r
- }\r
- response.setHeader("Connection", "close");\r
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
- response.flushBuffer();\r
- return;\r
- }\r
- }\r
- super.service(request, response);\r
- }\r
-}\r
-\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- * "Jason Pugsley" <jcifs at samba dot org>\r
- * "skeetz" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.http;\r
-\r
-import java.io.IOException;\r
-\r
-import javax.servlet.ServletException;\r
-\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import jcifs.smb.NtlmPasswordAuthentication;\r
-\r
-import jcifs.util.Base64;\r
-\r
-import jcifs.ntlmssp.NtlmFlags;\r
-import jcifs.ntlmssp.Type1Message;\r
-import jcifs.ntlmssp.Type2Message;\r
-import jcifs.ntlmssp.Type3Message;\r
-\r
-/**\r
- * This class is used internally by <tt>NtlmHttpFilter</tt>,\r
- * <tt>NtlmServlet</tt>, and <tt>NetworkExplorer</tt> to negiotiate password\r
- * hashes via NTLM SSP with MSIE. It might also be used directly by servlet\r
- * containers to incorporate similar functionality.\r
- * <p>\r
- * How NTLMSSP is used in conjunction with HTTP and MSIE clients is\r
- * described in an <A HREF="http://www.innovation.ch/java/ntlm.html">NTLM\r
- * Authentication Scheme for HTTP</A>. <p> Also, read <a\r
- * href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and\r
- * the Network Explorer Servlet</a> related information.\r
- */\r
-\r
-public class NtlmSsp implements NtlmFlags {\r
-\r
- /**\r
- * Calls the static {@link #authenticate(HttpServletRequest,\r
- * HttpServletResponse, byte[])} method to perform NTLM authentication\r
- * for the specified servlet request.\r
- *\r
- * @param req The request being serviced.\r
- * @param resp The response.\r
- * @param challenge The domain controller challenge.\r
- * @throws IOException If an IO error occurs.\r
- * @throws ServletException If an error occurs.\r
- */\r
- public NtlmPasswordAuthentication doAuthentication(\r
- HttpServletRequest req, HttpServletResponse resp, byte[] challenge)\r
- throws IOException, ServletException {\r
- return authenticate(req, resp, challenge);\r
- }\r
-\r
- /**\r
- * Performs NTLM authentication for the servlet request.\r
- *\r
- * @param req The request being serviced.\r
- * @param resp The response.\r
- * @param challenge The domain controller challenge.\r
- * @throws IOException If an IO error occurs.\r
- * @throws ServletException If an error occurs.\r
- */\r
- public static NtlmPasswordAuthentication authenticate(\r
- HttpServletRequest req, HttpServletResponse resp, byte[] challenge)\r
- throws IOException, ServletException {\r
- String msg = req.getHeader("Authorization");\r
- if (msg != null && msg.startsWith("NTLM ")) {\r
- byte[] src = Base64.decode(msg.substring(5));\r
- if (src[8] == 1) {\r
- Type1Message type1 = new Type1Message(src);\r
- Type2Message type2 = new Type2Message(type1, challenge, null);\r
- msg = Base64.encode(type2.toByteArray());\r
- resp.setHeader( "WWW-Authenticate", "NTLM " + msg );\r
- } else if (src[8] == 3) {\r
- Type3Message type3 = new Type3Message(src);\r
- byte[] lmResponse = type3.getLMResponse();\r
- if (lmResponse == null) lmResponse = new byte[0];\r
- byte[] ntResponse = type3.getNTResponse();\r
- if (ntResponse == null) ntResponse = new byte[0];\r
- return new NtlmPasswordAuthentication(type3.getDomain(),\r
- type3.getUser(), lmResponse, ntResponse);\r
- }\r
- } else {\r
- resp.setHeader("WWW-Authenticate", "NTLM");\r
- resp.setHeader("Connection", "close");\r
- }\r
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
- resp.setContentLength( 0 );\r
- resp.flushBuffer();\r
- return null;\r
- }\r
-\r
-}\r
-\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.ntlmssp;\r
-\r
-import java.io.IOException;\r
-\r
-import java.net.UnknownHostException;\r
-\r
-import jcifs.netbios.NbtAddress;\r
-\r
-import jcifs.Config;\r
-\r
-/**\r
- * Represents an NTLMSSP Type-1 message.\r
- */\r
-public class Type1Message extends NtlmMessage {\r
-\r
- private static final int DEFAULT_FLAGS;\r
-\r
- private static final String DEFAULT_DOMAIN;\r
-\r
- private static final String DEFAULT_WORKSTATION;\r
-\r
- private String suppliedDomain;\r
-\r
- private String suppliedWorkstation;\r
-\r
- static {\r
- DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
- (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
- NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
- DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
- String defaultWorkstation = null;\r
- try {\r
- defaultWorkstation = NbtAddress.getLocalHost().getHostName();\r
- } catch (UnknownHostException ex) { }\r
- DEFAULT_WORKSTATION = defaultWorkstation;\r
- }\r
-\r
- /**\r
- * Creates a Type-1 message using default values from the current\r
- * environment.\r
- */\r
- public Type1Message() {\r
- this(getDefaultFlags(), getDefaultDomain(), getDefaultWorkstation());\r
- }\r
-\r
- /**\r
- * Creates a Type-1 message with the specified parameters.\r
- *\r
- * @param flags The flags to apply to this message.\r
- * @param suppliedDomain The supplied authentication domain.\r
- * @param suppliedWorkstation The supplied workstation name.\r
- */\r
- public Type1Message(int flags, String suppliedDomain,\r
- String suppliedWorkstation) {\r
- setFlags(flags);\r
- setSuppliedDomain(suppliedDomain);\r
- setSuppliedWorkstation(suppliedWorkstation);\r
- }\r
-\r
- /**\r
- * Creates a Type-1 message using the given raw Type-1 material.\r
- *\r
- * @param material The raw Type-1 material used to construct this message.\r
- * @throws IOException If an error occurs while parsing the material.\r
- */\r
- public Type1Message(byte[] material) throws IOException {\r
- parse(material);\r
- }\r
-\r
- /**\r
- * Returns the supplied authentication domain.\r
- *\r
- * @return A <code>String</code> containing the supplied domain.\r
- */\r
- public String getSuppliedDomain() {\r
- return suppliedDomain;\r
- }\r
-\r
- /**\r
- * Sets the supplied authentication domain for this message.\r
- *\r
- * @param suppliedDomain The supplied domain for this message.\r
- */\r
- public void setSuppliedDomain(String suppliedDomain) {\r
- this.suppliedDomain = suppliedDomain;\r
- }\r
-\r
- /**\r
- * Returns the supplied workstation name.\r
- * \r
- * @return A <code>String</code> containing the supplied workstation name.\r
- */\r
- public String getSuppliedWorkstation() {\r
- return suppliedWorkstation;\r
- }\r
-\r
- /**\r
- * Sets the supplied workstation name for this message.\r
- * \r
- * @param suppliedWorkstation The supplied workstation for this message.\r
- */\r
- public void setSuppliedWorkstation(String suppliedWorkstation) {\r
- this.suppliedWorkstation = suppliedWorkstation;\r
- }\r
-\r
- public byte[] toByteArray() {\r
- try {\r
- String suppliedDomain = getSuppliedDomain();\r
- String suppliedWorkstation = getSuppliedWorkstation();\r
- int flags = getFlags();\r
- boolean hostInfo = false;\r
- byte[] domain = new byte[0];\r
- if (suppliedDomain != null && suppliedDomain.length() != 0) {\r
- hostInfo = true;\r
- flags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;\r
- domain = suppliedDomain.toUpperCase().getBytes(\r
- getOEMEncoding());\r
- } else {\r
- flags &= (NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED ^ 0xffffffff);\r
- }\r
- byte[] workstation = new byte[0];\r
- if (suppliedWorkstation != null &&\r
- suppliedWorkstation.length() != 0) {\r
- hostInfo = true;\r
- flags |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED;\r
- workstation =\r
- suppliedWorkstation.toUpperCase().getBytes(\r
- getOEMEncoding());\r
- } else {\r
- flags &= (NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED ^\r
- 0xffffffff);\r
- }\r
- byte[] type1 = new byte[hostInfo ?\r
- (32 + domain.length + workstation.length) : 16];\r
- System.arraycopy(NTLMSSP_SIGNATURE, 0, type1, 0, 8);\r
- writeULong(type1, 8, 1);\r
- writeULong(type1, 12, flags);\r
- if (hostInfo) {\r
- writeSecurityBuffer(type1, 16, 32, domain);\r
- writeSecurityBuffer(type1, 24, 32 + domain.length, workstation);\r
- }\r
- return type1;\r
- } catch (IOException ex) {\r
- throw new IllegalStateException(ex.getMessage());\r
- }\r
- }\r
-\r
- public String toString() {\r
- String suppliedDomain = getSuppliedDomain();\r
- String suppliedWorkstation = getSuppliedWorkstation();\r
- int flags = getFlags();\r
- StringBuffer buffer = new StringBuffer();\r
- if (suppliedDomain != null) {\r
- buffer.append("suppliedDomain: ").append(suppliedDomain);\r
- }\r
- if (suppliedWorkstation != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("suppliedWorkstation: ").append(suppliedWorkstation);\r
- }\r
- if (flags != 0) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("flags: ");\r
- buffer.append("0x");\r
- buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(flags & 0x0f));\r
- }\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Returns the default flags for a generic Type-1 message in the\r
- * current environment.\r
- * \r
- * @return An <code>int</code> containing the default flags.\r
- */\r
- public static int getDefaultFlags() {\r
- return DEFAULT_FLAGS;\r
- }\r
-\r
- /**\r
- * Returns the default domain from the current environment.\r
- *\r
- * @return A <code>String</code> containing the default domain.\r
- */\r
- public static String getDefaultDomain() {\r
- return DEFAULT_DOMAIN;\r
- }\r
-\r
- /**\r
- * Returns the default workstation from the current environment.\r
- *\r
- * @return A <code>String</code> containing the default workstation.\r
- */\r
- public static String getDefaultWorkstation() {\r
- return DEFAULT_WORKSTATION;\r
- }\r
-\r
- private void parse(byte[] material) throws IOException {\r
- for (int i = 0; i < 8; i++) {\r
- if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
- throw new IOException("Not an NTLMSSP message.");\r
- }\r
- }\r
- if (readULong(material, 8) != 1) {\r
- throw new IOException("Not a Type 1 message.");\r
- }\r
- int flags = readULong(material, 12);\r
- String suppliedDomain = null;\r
- if ((flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) != 0) {\r
- byte[] domain = readSecurityBuffer(material, 16);\r
- suppliedDomain = new String(domain, getOEMEncoding());\r
- }\r
- String suppliedWorkstation = null;\r
- if ((flags & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) != 0) {\r
- byte[] workstation = readSecurityBuffer(material, 24);\r
- suppliedWorkstation = new String(workstation, getOEMEncoding());\r
- }\r
- setFlags(flags);\r
- setSuppliedDomain(suppliedDomain);\r
- setSuppliedWorkstation(suppliedWorkstation);\r
- }\r
-\r
-}\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.ntlmssp;\r
-\r
-import java.io.IOException;\r
-\r
-import java.net.UnknownHostException;\r
-\r
-import jcifs.Config;\r
-\r
-import jcifs.netbios.NbtAddress;\r
-\r
-/**\r
- * Represents an NTLMSSP Type-2 message.\r
- */\r
-public class Type2Message extends NtlmMessage {\r
-\r
- private static final int DEFAULT_FLAGS;\r
-\r
- private static final String DEFAULT_DOMAIN;\r
-\r
- private static final byte[] DEFAULT_TARGET_INFORMATION;\r
-\r
- private byte[] challenge;\r
-\r
- private String target;\r
-\r
- private byte[] context;\r
-\r
- private byte[] targetInformation;\r
-\r
- static {\r
- DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
- (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
- NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
- DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
- byte[] domain = new byte[0];\r
- if (DEFAULT_DOMAIN != null) {\r
- try {\r
- domain = DEFAULT_DOMAIN.getBytes("UnicodeLittleUnmarked");\r
- } catch (IOException ex) { }\r
- }\r
- int domainLength = domain.length;\r
- byte[] server = new byte[0];\r
- try {\r
- String host = NbtAddress.getLocalHost().getHostName();\r
- if (host != null) {\r
- try {\r
- server = host.getBytes("UnicodeLittleUnmarked");\r
- } catch (IOException ex) { }\r
- }\r
- } catch (UnknownHostException ex) { }\r
- int serverLength = server.length;\r
- byte[] targetInfo = new byte[(domainLength > 0 ? domainLength + 4 : 0) +\r
- (serverLength > 0 ? serverLength + 4 : 0) + 4];\r
- int offset = 0;\r
- if (domainLength > 0) {\r
- writeUShort(targetInfo, offset, 2);\r
- offset += 2;\r
- writeUShort(targetInfo, offset, domainLength);\r
- offset += 2;\r
- System.arraycopy(domain, 0, targetInfo, offset, domainLength);\r
- offset += domainLength;\r
- }\r
- if (serverLength > 0) {\r
- writeUShort(targetInfo, offset, 1);\r
- offset += 2;\r
- writeUShort(targetInfo, offset, serverLength);\r
- offset += 2;\r
- System.arraycopy(server, 0, targetInfo, offset, serverLength);\r
- }\r
- DEFAULT_TARGET_INFORMATION = targetInfo;\r
- }\r
-\r
- /**\r
- * Creates a Type-2 message using default values from the current\r
- * environment.\r
- */\r
- public Type2Message() {\r
- this(getDefaultFlags(), null, null);\r
- }\r
-\r
- /**\r
- * Creates a Type-2 message in response to the given Type-1 message\r
- * using default values from the current environment.\r
- *\r
- * @param type1 The Type-1 message which this represents a response to.\r
- */\r
- public Type2Message(Type1Message type1) {\r
- this(type1, null, null);\r
- }\r
-\r
- /**\r
- * Creates a Type-2 message in response to the given Type-1 message.\r
- *\r
- * @param type1 The Type-1 message which this represents a response to.\r
- * @param challenge The challenge from the domain controller/server.\r
- * @param target The authentication target.\r
- */\r
- public Type2Message(Type1Message type1, byte[] challenge, String target) {\r
- this(getDefaultFlags(type1), challenge, (type1 != null &&\r
- target == null && type1.getFlag(NTLMSSP_REQUEST_TARGET)) ?\r
- getDefaultDomain() : target);\r
- }\r
-\r
- /**\r
- * Creates a Type-2 message with the specified parameters.\r
- *\r
- * @param flags The flags to apply to this message.\r
- * @param challenge The challenge from the domain controller/server.\r
- * @param target The authentication target.\r
- */\r
- public Type2Message(int flags, byte[] challenge, String target) {\r
- setFlags(flags);\r
- setChallenge(challenge);\r
- setTarget(target);\r
- if (target != null) setTargetInformation(getDefaultTargetInformation());\r
- }\r
-\r
- /**\r
- * Creates a Type-2 message using the given raw Type-2 material.\r
- *\r
- * @param material The raw Type-2 material used to construct this message.\r
- * @throws IOException If an error occurs while parsing the material.\r
- */\r
- public Type2Message(byte[] material) throws IOException {\r
- parse(material);\r
- }\r
-\r
- /**\r
- * Returns the challenge for this message.\r
- *\r
- * @return A <code>byte[]</code> containing the challenge.\r
- */\r
- public byte[] getChallenge() {\r
- return challenge;\r
- }\r
-\r
- /**\r
- * Sets the challenge for this message.\r
- *\r
- * @param challenge The challenge from the domain controller/server.\r
- */\r
- public void setChallenge(byte[] challenge) {\r
- this.challenge = challenge;\r
- }\r
-\r
- /**\r
- * Returns the authentication target.\r
- *\r
- * @return A <code>String</code> containing the authentication target.\r
- */\r
- public String getTarget() {\r
- return target;\r
- }\r
-\r
- /**\r
- * Sets the authentication target.\r
- *\r
- * @param target The authentication target.\r
- */\r
- public void setTarget(String target) {\r
- this.target = target;\r
- }\r
-\r
- /**\r
- * Returns the target information block.\r
- *\r
- * @return A <code>byte[]</code> containing the target information block.\r
- * The target information block is used by the client to create an\r
- * NTLMv2 response.\r
- */ \r
- public byte[] getTargetInformation() {\r
- return targetInformation;\r
- }\r
-\r
- /**\r
- * Sets the target information block.\r
- * The target information block is used by the client to create\r
- * an NTLMv2 response.\r
- * \r
- * @param targetInformation The target information block.\r
- */\r
- public void setTargetInformation(byte[] targetInformation) {\r
- this.targetInformation = targetInformation;\r
- }\r
-\r
- /**\r
- * Returns the local security context.\r
- *\r
- * @return A <code>byte[]</code> containing the local security\r
- * context. This is used by the client to negotiate local\r
- * authentication.\r
- */\r
- public byte[] getContext() {\r
- return context;\r
- }\r
-\r
- /**\r
- * Sets the local security context. This is used by the client\r
- * to negotiate local authentication.\r
- *\r
- * @param context The local security context.\r
- */\r
- public void setContext(byte[] context) {\r
- this.context = context;\r
- }\r
-\r
- public byte[] toByteArray() {\r
- try {\r
- String targetName = getTarget();\r
- byte[] challenge = getChallenge();\r
- byte[] context = getContext();\r
- byte[] targetInformation = getTargetInformation();\r
- int flags = getFlags();\r
- byte[] target = new byte[0];\r
- if ((flags & (NTLMSSP_TARGET_TYPE_DOMAIN |\r
- NTLMSSP_TARGET_TYPE_SERVER |\r
- NTLMSSP_TARGET_TYPE_SHARE)) != 0) {\r
- if (targetName != null && targetName.length() != 0) {\r
- target = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0 ?\r
- targetName.getBytes("UnicodeLittleUnmarked") :\r
- targetName.toUpperCase().getBytes(getOEMEncoding());\r
- } else {\r
- flags &= (0xffffffff ^ (NTLMSSP_TARGET_TYPE_DOMAIN |\r
- NTLMSSP_TARGET_TYPE_SERVER |\r
- NTLMSSP_TARGET_TYPE_SHARE));\r
- }\r
- }\r
- if (targetInformation != null) {\r
- flags ^= NTLMSSP_NEGOTIATE_TARGET_INFO;\r
- // empty context is needed for padding when t.i. is supplied.\r
- if (context == null) context = new byte[8];\r
- }\r
- int data = 32;\r
- if (context != null) data += 8;\r
- if (targetInformation != null) data += 8;\r
- byte[] type2 = new byte[data + target.length +\r
- (targetInformation != null ? targetInformation.length : 0)];\r
- System.arraycopy(NTLMSSP_SIGNATURE, 0, type2, 0, 8);\r
- writeULong(type2, 8, 2);\r
- writeSecurityBuffer(type2, 12, data, target);\r
- writeULong(type2, 20, flags);\r
- System.arraycopy(challenge != null ? challenge : new byte[8], 0,\r
- type2, 24, 8);\r
- if (context != null) System.arraycopy(context, 0, type2, 32, 8);\r
- if (targetInformation != null) {\r
- writeSecurityBuffer(type2, 40, data + target.length,\r
- targetInformation);\r
- }\r
- return type2;\r
- } catch (IOException ex) {\r
- throw new IllegalStateException(ex.getMessage());\r
- }\r
- }\r
-\r
- public String toString() {\r
- String target = getTarget();\r
- byte[] challenge = getChallenge();\r
- byte[] context = getContext();\r
- byte[] targetInformation = getTargetInformation();\r
- int flags = getFlags();\r
- StringBuffer buffer = new StringBuffer();\r
- if (target != null) {\r
- buffer.append("target: ").append(target);\r
- }\r
- if (challenge != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("challenge: ");\r
- buffer.append("0x");\r
- for (int i = 0; i < challenge.length; i++) {\r
- buffer.append(Integer.toHexString((challenge[i] >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(challenge[i] & 0x0f));\r
- }\r
- }\r
- if (context != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("context: ");\r
- buffer.append("0x");\r
- for (int i = 0; i < context.length; i++) {\r
- buffer.append(Integer.toHexString((context[i] >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(context[i] & 0x0f));\r
- }\r
- }\r
- if (targetInformation != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("targetInformation: ");\r
- buffer.append("0x");\r
- for (int i = 0; i < targetInformation.length; i++) {\r
- buffer.append(Integer.toHexString((targetInformation[i] >> 4) &\r
- 0x0f));\r
- buffer.append(Integer.toHexString(targetInformation[i] & 0x0f));\r
- }\r
- }\r
- if (flags != 0) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("flags: ");\r
- buffer.append("0x");\r
- buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(flags & 0x0f));\r
- }\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Returns the default flags for a generic Type-2 message in the\r
- * current environment.\r
- *\r
- * @return An <code>int</code> containing the default flags.\r
- */\r
- public static int getDefaultFlags() {\r
- return DEFAULT_FLAGS;\r
- }\r
-\r
- /**\r
- * Returns the default flags for a Type-2 message created in response\r
- * to the given Type-1 message in the current environment.\r
- *\r
- * @return An <code>int</code> containing the default flags.\r
- */\r
- public static int getDefaultFlags(Type1Message type1) {\r
- if (type1 == null) return DEFAULT_FLAGS;\r
- int flags = NTLMSSP_NEGOTIATE_NTLM;\r
- int type1Flags = type1.getFlags();\r
- flags |= ((type1Flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
- NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM;\r
- if ((type1Flags & NTLMSSP_REQUEST_TARGET) != 0) {\r
- String domain = getDefaultDomain();\r
- if (domain != null) {\r
- flags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_TARGET_TYPE_DOMAIN;\r
- }\r
- }\r
- return flags;\r
- }\r
-\r
- /**\r
- * Returns the default domain from the current environment.\r
- *\r
- * @return A <code>String</code> containing the domain.\r
- */\r
- public static String getDefaultDomain() {\r
- return DEFAULT_DOMAIN;\r
- }\r
-\r
- public static byte[] getDefaultTargetInformation() {\r
- return DEFAULT_TARGET_INFORMATION;\r
- }\r
-\r
- private void parse(byte[] material) throws IOException {\r
- for (int i = 0; i < 8; i++) {\r
- if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
- throw new IOException("Not an NTLMSSP message.");\r
- }\r
- }\r
- if (readULong(material, 8) != 2) {\r
- throw new IOException("Not a Type 2 message.");\r
- }\r
- int flags = readULong(material, 20);\r
- setFlags(flags);\r
- String target = null;\r
- byte[] bytes = readSecurityBuffer(material, 12);\r
- if (bytes.length != 0) {\r
- target = new String(bytes,\r
- ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
- "UnicodeLittleUnmarked" : getOEMEncoding());\r
- }\r
- setTarget(target);\r
- for (int i = 24; i < 32; i++) {\r
- if (material[i] != 0) {\r
- byte[] challenge = new byte[8];\r
- System.arraycopy(material, 24, challenge, 0, 8);\r
- setChallenge(challenge);\r
- break;\r
- }\r
- }\r
- int offset = readULong(material, 16); // offset of targetname start\r
- if (offset == 32 || material.length == 32) return;\r
- for (int i = 32; i < 40; i++) {\r
- if (material[i] != 0) {\r
- byte[] context = new byte[8];\r
- System.arraycopy(material, 32, context, 0, 8);\r
- setContext(context);\r
- break;\r
- }\r
- }\r
- if (offset == 40 || material.length == 40) return;\r
- bytes = readSecurityBuffer(material, 40);\r
- if (bytes.length != 0) setTargetInformation(bytes);\r
- }\r
-\r
-}\r
+++ /dev/null
-/* jcifs smb client library in Java\r
- * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
- * "Eric Glass" <jcifs at samba dot org>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-package jcifs.ntlmssp;\r
-\r
-import java.io.IOException;\r
-\r
-import java.net.UnknownHostException;\r
-\r
-import java.security.SecureRandom;\r
-\r
-import jcifs.Config;\r
-\r
-import jcifs.netbios.NbtAddress;\r
-\r
-import jcifs.smb.NtlmPasswordAuthentication;\r
-\r
-/**\r
- * Represents an NTLMSSP Type-3 message.\r
- */\r
-public class Type3Message extends NtlmMessage {\r
-\r
- private static final int DEFAULT_FLAGS;\r
-\r
- private static final String DEFAULT_DOMAIN;\r
-\r
- private static final String DEFAULT_USER;\r
-\r
- private static final String DEFAULT_PASSWORD;\r
-\r
- private static final String DEFAULT_WORKSTATION;\r
-\r
- private static final int LM_COMPATIBILITY;\r
-\r
- private static final SecureRandom RANDOM = new SecureRandom();\r
-\r
- private byte[] lmResponse;\r
-\r
- private byte[] ntResponse;\r
-\r
- private String domain;\r
-\r
- private String user;\r
-\r
- private String workstation;\r
-\r
- private byte[] sessionKey;\r
-\r
- static {\r
- DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
- (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
- NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
- DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
- DEFAULT_USER = Config.getProperty("jcifs.smb.client.username", null);\r
- DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password",\r
- null);\r
- String defaultWorkstation = null;\r
- try {\r
- defaultWorkstation = NbtAddress.getLocalHost().getHostName();\r
- } catch (UnknownHostException ex) { }\r
- DEFAULT_WORKSTATION = defaultWorkstation;\r
- LM_COMPATIBILITY = Config.getInt("jcifs.smb.lmCompatibility", 0);\r
- }\r
-\r
- /**\r
- * Creates a Type-3 message using default values from the current\r
- * environment.\r
- */\r
- public Type3Message() {\r
- setFlags(getDefaultFlags());\r
- setDomain(getDefaultDomain());\r
- setUser(getDefaultUser());\r
- setWorkstation(getDefaultWorkstation());\r
- }\r
-\r
- /**\r
- * Creates a Type-3 message in response to the given Type-2 message\r
- * using default values from the current environment.\r
- *\r
- * @param type2 The Type-2 message which this represents a response to.\r
- */\r
- public Type3Message(Type2Message type2) {\r
- setFlags(getDefaultFlags(type2));\r
- setWorkstation(getDefaultWorkstation());\r
- String domain = getDefaultDomain();\r
- setDomain(domain);\r
- String user = getDefaultUser();\r
- setUser(user);\r
- String password = getDefaultPassword();\r
- switch (LM_COMPATIBILITY) {\r
- case 0:\r
- case 1:\r
- setLMResponse(getLMResponse(type2, password));\r
- setNTResponse(getNTResponse(type2, password));\r
- break;\r
- case 2:\r
- byte[] nt = getNTResponse(type2, password);\r
- setLMResponse(nt);\r
- setNTResponse(nt);\r
- break;\r
- case 3:\r
- case 4:\r
- case 5:\r
- byte[] clientChallenge = new byte[8];\r
- RANDOM.nextBytes(clientChallenge);\r
- setLMResponse(getLMv2Response(type2, domain, user, password,\r
- clientChallenge));\r
- /*\r
- setNTResponse(getNTLMv2Response(type2, domain, user, password,\r
- clientChallenge));\r
- */\r
- break;\r
- default:\r
- setLMResponse(getLMResponse(type2, password));\r
- setNTResponse(getNTResponse(type2, password));\r
- }\r
- }\r
-\r
- /**\r
- * Creates a Type-3 message in response to the given Type-2 message.\r
- *\r
- * @param type2 The Type-2 message which this represents a response to.\r
- * @param password The password to use when constructing the response.\r
- * @param domain The domain in which the user has an account.\r
- * @param user The username for the authenticating user.\r
- * @param workstation The workstation from which authentication is\r
- * taking place.\r
- */\r
- public Type3Message(Type2Message type2, String password, String domain,\r
- String user, String workstation) {\r
- setFlags(getDefaultFlags(type2));\r
- setDomain(domain);\r
- setUser(user);\r
- setWorkstation(workstation);\r
- switch (LM_COMPATIBILITY) {\r
- case 0:\r
- case 1:\r
- setLMResponse(getLMResponse(type2, password));\r
- setNTResponse(getNTResponse(type2, password));\r
- break;\r
- case 2:\r
- byte[] nt = getNTResponse(type2, password);\r
- setLMResponse(nt);\r
- setNTResponse(nt);\r
- break;\r
- case 3:\r
- case 4:\r
- case 5:\r
- byte[] clientChallenge = new byte[8];\r
- RANDOM.nextBytes(clientChallenge);\r
- setLMResponse(getLMv2Response(type2, domain, user, password,\r
- clientChallenge));\r
- /*\r
- setNTResponse(getNTLMv2Response(type2, domain, user, password,\r
- clientChallenge));\r
- */\r
- break;\r
- default:\r
- setLMResponse(getLMResponse(type2, password));\r
- setNTResponse(getNTResponse(type2, password));\r
- }\r
- }\r
-\r
- /**\r
- * Creates a Type-3 message with the specified parameters.\r
- *\r
- * @param flags The flags to apply to this message.\r
- * @param lmResponse The LanManager/LMv2 response.\r
- * @param domain The NT/NTLMv2 response.\r
- * @param domain The domain in which the user has an account.\r
- * @param user The username for the authenticating user.\r
- * @param workstation The workstation from which authentication is\r
- * taking place.\r
- */\r
- public Type3Message(int flags, byte[] lmResponse, byte[] ntResponse,\r
- String domain, String user, String workstation) {\r
- setFlags(flags);\r
- setLMResponse(lmResponse);\r
- setNTResponse(ntResponse);\r
- setDomain(domain);\r
- setUser(user);\r
- setWorkstation(workstation);\r
- }\r
-\r
- /**\r
- * Creates a Type-3 message using the given raw Type-3 material.\r
- *\r
- * @param material The raw Type-3 material used to construct this message.\r
- * @throws IOException If an error occurs while parsing the material.\r
- */\r
- public Type3Message(byte[] material) throws IOException {\r
- parse(material);\r
- }\r
-\r
- /**\r
- * Returns the LanManager/LMv2 response.\r
- *\r
- * @return A <code>byte[]</code> containing the LanManager response.\r
- */\r
- public byte[] getLMResponse() {\r
- return lmResponse;\r
- }\r
-\r
- /**\r
- * Sets the LanManager/LMv2 response for this message.\r
- *\r
- * @param lmResponse The LanManager response.\r
- */\r
- public void setLMResponse(byte[] lmResponse) {\r
- this.lmResponse = lmResponse;\r
- }\r
-\r
- /**\r
- * Returns the NT/NTLMv2 response.\r
- *\r
- * @return A <code>byte[]</code> containing the NT/NTLMv2 response.\r
- */\r
- public byte[] getNTResponse() {\r
- return ntResponse;\r
- }\r
-\r
- /**\r
- * Sets the NT/NTLMv2 response for this message.\r
- *\r
- * @param lmResponse The NT/NTLMv2 response.\r
- */\r
- public void setNTResponse(byte[] ntResponse) {\r
- this.ntResponse = ntResponse;\r
- }\r
-\r
- /**\r
- * Returns the domain in which the user has an account.\r
- *\r
- * @return A <code>String</code> containing the domain for the user.\r
- */\r
- public String getDomain() {\r
- return domain;\r
- }\r
-\r
- /**\r
- * Sets the domain for this message.\r
- *\r
- * @param domain The domain.\r
- */\r
- public void setDomain(String domain) {\r
- this.domain = domain;\r
- }\r
-\r
- /**\r
- * Returns the username for the authenticating user.\r
- *\r
- * @return A <code>String</code> containing the user for this message.\r
- */\r
- public String getUser() {\r
- return user;\r
- }\r
-\r
- /**\r
- * Sets the user for this message.\r
- *\r
- * @param user The user.\r
- */\r
- public void setUser(String user) {\r
- this.user = user;\r
- }\r
-\r
- /**\r
- * Returns the workstation from which authentication is being performed.\r
- *\r
- * @return A <code>String</code> containing the workstation.\r
- */\r
- public String getWorkstation() {\r
- return workstation;\r
- }\r
-\r
- /**\r
- * Sets the workstation for this message.\r
- *\r
- * @param workstation The workstation.\r
- */\r
- public void setWorkstation(String workstation) {\r
- this.workstation = workstation;\r
- }\r
-\r
- /**\r
- * Returns the session key.\r
- *\r
- * @return A <code>byte[]</code> containing the session key.\r
- */\r
- public byte[] getSessionKey() {\r
- return sessionKey;\r
- }\r
-\r
- /**\r
- * Sets the session key.\r
- *\r
- * @param sessionKey The session key.\r
- */\r
- public void setSessionKey(byte[] sessionKey) {\r
- this.sessionKey = sessionKey;\r
- }\r
-\r
- public byte[] toByteArray() {\r
- try {\r
- int flags = getFlags();\r
- boolean unicode = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0;\r
- String oem = unicode ? null : getOEMEncoding();\r
- String domainName = getDomain();\r
- byte[] domain = null;\r
- if (domainName != null && domainName.length() != 0) {\r
- domain = unicode ?\r
- domainName.getBytes("UnicodeLittleUnmarked") :\r
- domainName.toUpperCase().getBytes(oem);\r
- }\r
- int domainLength = (domain != null) ? domain.length : 0;\r
- String userName = getUser();\r
- byte[] user = null;\r
- if (userName != null && userName.length() != 0) {\r
- user = unicode ? userName.getBytes("UnicodeLittleUnmarked") :\r
- userName.toUpperCase().getBytes(oem);\r
- }\r
- int userLength = (user != null) ? user.length : 0;\r
- String workstationName = getWorkstation();\r
- byte[] workstation = null;\r
- if (workstationName != null && workstationName.length() != 0) {\r
- workstation = unicode ?\r
- workstationName.getBytes("UnicodeLittleUnmarked") :\r
- workstationName.toUpperCase().getBytes(oem);\r
- }\r
- int workstationLength = (workstation != null) ?\r
- workstation.length : 0;\r
- byte[] lmResponse = getLMResponse();\r
- int lmLength = (lmResponse != null) ? lmResponse.length : 0;\r
- byte[] ntResponse = getNTResponse();\r
- int ntLength = (ntResponse != null) ? ntResponse.length : 0;\r
- byte[] sessionKey = getSessionKey();\r
- int keyLength = (sessionKey != null) ? sessionKey.length : 0;\r
- byte[] type3 = new byte[64 + domainLength + userLength +\r
- workstationLength + lmLength + ntLength + keyLength];\r
- System.arraycopy(NTLMSSP_SIGNATURE, 0, type3, 0, 8);\r
- writeULong(type3, 8, 3);\r
- int offset = 64;\r
- writeSecurityBuffer(type3, 12, offset, lmResponse);\r
- offset += lmLength;\r
- writeSecurityBuffer(type3, 20, offset, ntResponse);\r
- offset += ntLength;\r
- writeSecurityBuffer(type3, 28, offset, domain);\r
- offset += domainLength;\r
- writeSecurityBuffer(type3, 36, offset, user);\r
- offset += userLength;\r
- writeSecurityBuffer(type3, 44, offset, workstation);\r
- offset += workstationLength;\r
- writeSecurityBuffer(type3, 52, offset, sessionKey);\r
- writeULong(type3, 60, flags);\r
- return type3;\r
- } catch (IOException ex) {\r
- throw new IllegalStateException(ex.getMessage());\r
- }\r
- }\r
-\r
- public String toString() {\r
- String user = getUser();\r
- String domain = getDomain();\r
- String workstation = getWorkstation();\r
- byte[] lmResponse = getLMResponse();\r
- byte[] ntResponse = getNTResponse();\r
- byte[] sessionKey = getSessionKey();\r
- int flags = getFlags();\r
- StringBuffer buffer = new StringBuffer();\r
- if (domain != null) {\r
- buffer.append("domain: ").append(domain);\r
- }\r
- if (user != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("user: ").append(user);\r
- }\r
- if (workstation != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("workstation: ").append(workstation);\r
- }\r
- if (lmResponse != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("lmResponse: ");\r
- buffer.append("0x");\r
- for (int i = 0; i < lmResponse.length; i++) {\r
- buffer.append(Integer.toHexString((lmResponse[i] >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(lmResponse[i] & 0x0f));\r
- }\r
- }\r
- if (ntResponse != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("ntResponse: ");\r
- buffer.append("0x");\r
- for (int i = 0; i < ntResponse.length; i++) {\r
- buffer.append(Integer.toHexString((ntResponse[i] >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(ntResponse[i] & 0x0f));\r
- }\r
- }\r
- if (sessionKey != null) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("sessionKey: ");\r
- buffer.append("0x");\r
- for (int i = 0; i < sessionKey.length; i++) {\r
- buffer.append(Integer.toHexString((sessionKey[i] >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(sessionKey[i] & 0x0f));\r
- }\r
- }\r
- if (flags != 0) {\r
- if (buffer.length() > 0) buffer.append("; ");\r
- buffer.append("flags: ");\r
- buffer.append("0x");\r
- buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
- buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
- buffer.append(Integer.toHexString(flags & 0x0f));\r
- }\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Returns the default flags for a generic Type-3 message in the\r
- * current environment.\r
- *\r
- * @return An <code>int</code> containing the default flags.\r
- */\r
- public static int getDefaultFlags() {\r
- return DEFAULT_FLAGS;\r
- }\r
-\r
- /**\r
- * Returns the default flags for a Type-3 message created in response\r
- * to the given Type-2 message in the current environment.\r
- *\r
- * @return An <code>int</code> containing the default flags.\r
- */\r
- public static int getDefaultFlags(Type2Message type2) {\r
- if (type2 == null) return DEFAULT_FLAGS;\r
- int flags = NTLMSSP_NEGOTIATE_NTLM;\r
- flags |= ((type2.getFlags() & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
- NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM;\r
- return flags;\r
- }\r
-\r
- /**\r
- * Constructs the LanManager response to the given Type-2 message using\r
- * the supplied password.\r
- *\r
- * @param type2 The Type-2 message.\r
- * @param password The password.\r
- * @return A <code>byte[]</code> containing the LanManager response.\r
- */\r
- public static byte[] getLMResponse(Type2Message type2, String password) {\r
- if (type2 == null || password == null) return null;\r
- return NtlmPasswordAuthentication.getPreNTLMResponse(password,\r
- type2.getChallenge());\r
- }\r
-\r
- public static byte[] getLMv2Response(Type2Message type2,\r
- String domain, String user, String password,\r
- byte[] clientChallenge) {\r
- if (type2 == null || domain == null || user == null ||\r
- password == null || clientChallenge == null) {\r
- return null;\r
- }\r
- return NtlmPasswordAuthentication.getLMv2Response(domain, user,\r
- password, type2.getChallenge(), clientChallenge);\r
- }\r
-\r
- /**\r
- * Constructs the NT response to the given Type-2 message using\r
- * the supplied password.\r
- *\r
- * @param type2 The Type-2 message.\r
- * @param password The password.\r
- * @return A <code>byte[]</code> containing the NT response.\r
- */\r
- public static byte[] getNTResponse(Type2Message type2, String password) {\r
- if (type2 == null || password == null) return null;\r
- return NtlmPasswordAuthentication.getNTLMResponse(password,\r
- type2.getChallenge());\r
- }\r
-\r
- /**\r
- * Returns the default domain from the current environment.\r
- *\r
- * @return The default domain.\r
- */\r
- public static String getDefaultDomain() {\r
- return DEFAULT_DOMAIN;\r
- }\r
-\r
- /**\r
- * Returns the default user from the current environment.\r
- *\r
- * @return The default user.\r
- */\r
- public static String getDefaultUser() {\r
- return DEFAULT_USER;\r
- }\r
-\r
- /**\r
- * Returns the default password from the current environment.\r
- *\r
- * @return The default password.\r
- */\r
- public static String getDefaultPassword() {\r
- return DEFAULT_PASSWORD;\r
- }\r
-\r
- /**\r
- * Returns the default workstation from the current environment.\r
- *\r
- * @return The default workstation.\r
- */\r
- public static String getDefaultWorkstation() {\r
- return DEFAULT_WORKSTATION;\r
- }\r
-\r
- private void parse(byte[] material) throws IOException {\r
- for (int i = 0; i < 8; i++) {\r
- if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
- throw new IOException("Not an NTLMSSP message.");\r
- }\r
- }\r
- if (readULong(material, 8) != 3) {\r
- throw new IOException("Not a Type 3 message.");\r
- }\r
- byte[] lmResponse = readSecurityBuffer(material, 12);\r
- int lmResponseOffset = readULong(material, 16);\r
- byte[] ntResponse = readSecurityBuffer(material, 20);\r
- int ntResponseOffset = readULong(material, 24);\r
- byte[] domain = readSecurityBuffer(material, 28);\r
- int domainOffset = readULong(material, 32);\r
- byte[] user = readSecurityBuffer(material, 36);\r
- int userOffset = readULong(material, 40);\r
- byte[] workstation = readSecurityBuffer(material, 44);\r
- int workstationOffset = readULong(material, 48);\r
- int flags;\r
- String charset;\r
- if (lmResponseOffset == 52 || ntResponseOffset == 52 ||\r
- domainOffset == 52 || userOffset == 52 ||\r
- workstationOffset == 52) {\r
- flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_OEM;\r
- charset = getOEMEncoding();\r
- } else {\r
- setSessionKey(readSecurityBuffer(material, 52));\r
- flags = readULong(material, 60);\r
- charset = ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
- "UnicodeLittleUnmarked" : getOEMEncoding();\r
- }\r
- setFlags(flags);\r
- setLMResponse(lmResponse);\r
- // NTLMv2 issues w/cross-domain authentication; leave NT empty if >= 3\r
- if (LM_COMPATIBILITY < 3) setNTResponse(ntResponse);\r
- setDomain(new String(domain, charset));\r
- setUser(new String(user, charset));\r
- setWorkstation(new String(workstation, charset));\r
- }\r
-\r
-}\r