From: Felix Schumacher Date: Wed, 6 Aug 2008 14:08:02 +0000 (+0200) Subject: jcifs-0.7.13 from tgz X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=7c53b8a0e77002116eadfbbb3e2749ea0c3d5a9c;p=jcifs_without_docs.git jcifs-0.7.13 from tgz 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). --- diff --git a/CHANGES.txt b/CHANGES.txt index 2b77d33..def84bd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,24 @@ +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 diff --git a/README.txt b/README.txt index b16e455..05acc07 100644 --- a/README.txt +++ b/README.txt @@ -1,3 +1,13 @@ +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. diff --git a/build.xml b/build.xml index b7a2669..f7caea3 100644 --- a/build.xml +++ b/build.xml @@ -66,24 +66,24 @@ - + - + - - - + + + - + - + diff --git a/examples/Equals.java b/examples/Equals.java new file mode 100644 index 0000000..6fbe616 --- /dev/null +++ b/examples/Equals.java @@ -0,0 +1,12 @@ +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 )); + } +} + diff --git a/src/jcifs/smb/NtlmPasswordAuthentication.java b/src/jcifs/smb/NtlmPasswordAuthentication.java index c076ab8..d5650de 100644 --- a/src/jcifs/smb/NtlmPasswordAuthentication.java +++ b/src/jcifs/smb/NtlmPasswordAuthentication.java @@ -24,7 +24,7 @@ import jcifs.util.MD4; 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; @@ -52,7 +52,7 @@ public final class NtlmPasswordAuthentication implements Principal { 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 = { @@ -160,6 +160,7 @@ public final class NtlmPasswordAuthentication implements Principal { byte[] ansiHash; byte[] unicodeHash; boolean hashesExternal = false; + byte[] clientChallenge = null; /** * Create an NtlmPasswordAuthentication object from the userinfo @@ -256,6 +257,7 @@ public final class NtlmPasswordAuthentication implements Principal { 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. */ @@ -272,8 +274,10 @@ public final class NtlmPasswordAuthentication implements Principal { 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: @@ -296,8 +300,10 @@ public final class NtlmPasswordAuthentication implements Principal { 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); */ @@ -306,6 +312,74 @@ public final class NtlmPasswordAuthentication implements Principal { return getNTLMResponse( password, challenge ); } } + + /** + * Returns the effective user session key. + * + * @param challenge The server challenge. + * @return A byte[] 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 NtlmPasswordAuthentication objects for * equality. Two NtlmPasswordAuthentication objects are equal if diff --git a/src/jcifs/smb/ServerMessageBlock.java b/src/jcifs/smb/ServerMessageBlock.java index 7d5f6e4..f216d5d 100644 --- a/src/jcifs/smb/ServerMessageBlock.java +++ b/src/jcifs/smb/ServerMessageBlock.java @@ -94,6 +94,7 @@ abstract class ServerMessageBlock { 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; @@ -261,6 +262,7 @@ abstract class ServerMessageBlock { wordCount, byteCount; boolean useUnicode, received; long responseTimeout = 1; + int verifySequence; ServerMessageBlock() { flags = (byte)( FLAGS_PATH_NAMES_CASELESS | FLAGS_PATH_NAMES_CANONICALIZED ); diff --git a/src/jcifs/smb/SmbComSessionSetupAndX.java0 b/src/jcifs/smb/SmbComSessionSetupAndX.java0 deleted file mode 100644 index da61a71..0000000 --- a/src/jcifs/smb/SmbComSessionSetupAndX.java0 +++ /dev/null @@ -1,172 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2000 "Michael B. Allen" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package jcifs.smb; - -import jcifs.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; - } -} diff --git a/src/jcifs/smb/SmbException.java b/src/jcifs/smb/SmbException.java index 39c9ebb..3b4a443 100644 --- a/src/jcifs/smb/SmbException.java +++ b/src/jcifs/smb/SmbException.java @@ -79,6 +79,10 @@ public class SmbException extends IOException { */ 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; @@ -154,6 +158,10 @@ public class SmbException extends IOException { * 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; @@ -278,6 +286,9 @@ public class SmbException extends IOException { 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 + "]"; } @@ -290,6 +301,9 @@ public class SmbException extends IOException { case ERRnotready: result += "The device is not ready"; break; + case ERRgeneral: + result += "General failure"; + break; default: result += "No description available [ERRHRD/" + errorCode + "]"; } diff --git a/src/jcifs/smb/SmbFile.java b/src/jcifs/smb/SmbFile.java index e332cb2..add39d9 100644 --- a/src/jcifs/smb/SmbFile.java +++ b/src/jcifs/smb/SmbFile.java @@ -1925,7 +1925,14 @@ The shareAccess parameter controls what permissions other clients have */ 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(); } /** @@ -1950,7 +1957,6 @@ The shareAccess parameter controls what permissions other clients have */ public boolean equals( Object obj ) { -//HERE return obj instanceof SmbFile && obj.hashCode() == hashCode(); } diff --git a/src/jcifs/smb/SmbSession.java b/src/jcifs/smb/SmbSession.java index a9fa6d5..e3dbda3 100644 --- a/src/jcifs/smb/SmbSession.java +++ b/src/jcifs/smb/SmbSession.java @@ -115,6 +115,14 @@ synchronized( transport ) { 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 ); diff --git a/src/jcifs/smb/SmbTransport.java b/src/jcifs/smb/SmbTransport.java index af7f2db..2fa76af 100644 --- a/src/jcifs/smb/SmbTransport.java +++ b/src/jcifs/smb/SmbTransport.java @@ -31,7 +31,12 @@ import java.io.InterruptedIOException; 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; @@ -43,6 +48,10 @@ class SmbTransport implements Runnable { 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; @@ -59,16 +68,22 @@ class SmbTransport implements Runnable { 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; @@ -76,6 +91,8 @@ private static byte[] rcv_buf = new byte[0xFFFF]; int negotiatedMaxBufferSize; int negotiatedCapabilities; + int ssnLimit = Config.getInt( "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT ); + ClientProperties client = new ClientProperties(); ServerProperties server = new ServerProperties(); @@ -90,8 +107,7 @@ private static byte[] rcv_buf = new byte[0xFFFF]; * 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", @@ -161,12 +177,16 @@ private static byte[] rcv_buf = new byte[0xFFFF]; 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; @@ -178,16 +198,27 @@ private static byte[] rcv_buf = new byte[0xFFFF]; 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 ) { @@ -273,8 +304,10 @@ private static byte[] rcv_buf = new byte[0xFFFF]; 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 ) { @@ -366,6 +399,9 @@ synchronized( rcv_buf ) { if( response.errorCode != 0 || e.hasMoreElements() == false ) { ((SmbComTransactionResponse)response).hasMore = false; + if( useSigning ) { + verify(rcv_buf, 0, response.length, response.verifySequence); + } response.notify(); } else { ensureOpen(); @@ -381,6 +417,9 @@ synchronized( rcv_buf ) { 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(); } @@ -403,6 +442,108 @@ synchronized( rcv_buf ) { } } } + + 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; @@ -418,7 +559,11 @@ synchronized( rcv_buf ) { 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 ); @@ -452,7 +597,12 @@ synchronized( snd_buf ) { 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 ); @@ -541,7 +691,12 @@ synchronized( snd_buf ) { 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 ); @@ -586,7 +741,12 @@ synchronized(snd_buf) { 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 ); @@ -661,6 +821,9 @@ synchronized( snd_buf ) { * Negotiate Protocol Request / Response */ + // reset MAC signing + macSigningKey = null; + SmbComNegotiateResponse response = new SmbComNegotiateResponse(); send( new SmbComNegotiate(), response ); @@ -674,6 +837,22 @@ synchronized( snd_buf ) { 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 ? diff --git a/src/jcifs/smb/SmbTransport.java.bak b/src/jcifs/smb/SmbTransport.java.bak new file mode 100644 index 0000000..d9501e4 --- /dev/null +++ b/src/jcifs/smb/SmbTransport.java.bak @@ -0,0 +1,832 @@ +/* jcifs smb client library in Java + * Copyright (C) 2000 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +import jcifs.netbios.NbtSocket; +import jcifs.netbios.NbtException; +import jcifs.netbios.NbtAddress; +import jcifs.UniAddress; +import jcifs.Config; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Vector; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.Enumeration; +import java.util.Hashtable; + +class SmbTransport implements Runnable { + + private static final int DEFAULT_MAX_MPX_COUNT = 10; + private static final int DEFAULT_RESPONSE_TIMEOUT = 10000; + private static final int DEFAULT_SO_TIMEOUT = 15000; + private static final int PUSHBACK_BUF_SIZE = 64; + private static final int DEFAULT_RCV_BUF_SIZE = 60416; + private static final int DEFAULT_SND_BUF_SIZE = 5000; + private static final int DEFAULT_SSN_LIMIT = 100; + + static final int MID_OFFSET = 30; + static final int HEADER_LENGTH = 32; + static final int FLAGS_OFFSET = 9; + static final int FLAGS_RESPONSE = 0x80; + + private NbtSocket socket; // should become UniSocket? + private InputStream in; + private OutputStream out; + private int localPort, soTimeout, responseTimeout; + private long closeTime; + private InetAddress localAddr; + private Hashtable responseTable; + private Thread thread; + private Object outLock; + + private static Vector connections = new Vector(); + private static MpxControl mpxCtrl = new MpxControl(); + +private static byte[] snd_buf = new byte[0xFFFF]; +private static byte[] rcv_buf = new byte[0xFFFF]; + + LinkedList sessions; + boolean negotiated, useUnicode; + + UniAddress address; + int port, rcv_buf_size, snd_buf_size; + + int negotiatedDialectIndex; + int negotiatedMaxMpxCount; + int negotiatedMaxBufferSize; + int negotiatedCapabilities; + + int ssnLimit = Config.getInt( "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT ); + + ClientProperties client = new ClientProperties(); + ServerProperties server = new ServerProperties(); + + class ClientProperties { + int maxMpxCount = Config.getInt( "jcifs.smb.client.maxMpxCount", + DEFAULT_MAX_MPX_COUNT ); + int maxBufferSize = Config.getInt( "jcifs.smb.client.snd_buf_size", + DEFAULT_SND_BUF_SIZE ); + int sessionKey = 0x00000000; + /* NT 4 Workstation client capabilities 0x00D4 + * we don't do NT Status Codes or Level II oplocks + * but we could do raw and mpx + */ + int flags2 = Config.getInt( "jcifs.smb.client.flags2", + ServerMessageBlock.FLAGS2_LONG_FILENAMES | + ServerMessageBlock.FLAGS2_UNICODE ); + int capabilities = Config.getInt( "jcifs.smb.client.capabilities", + ServerMessageBlock.CAP_UNICODE | ServerMessageBlock.CAP_NT_SMBS ); + String nativeOs = Config.getProperty( "jcifs.smb.client.nativeOs", + System.getProperty( "os.name" )); + String nativeLanMan = Config.getProperty( "jcifs.smb.client.nativeLanMan", "jCIFS" ); + int vcNumber = 1; + } + class ServerProperties { + String tconHostName; + + byte flags; + int flags2; + int maxMpxCount; + int maxBufferSize; + int sessionKey; + // NT 4 Workstation is 0x43FD + int capabilities; + String oemDomainName; + String primaryDomain; + String nativeOs; + String nativeLanMan; + + int securityMode; + int security; + boolean encryptedPasswords; + boolean signaturesEnabled; + boolean signaturesRequired; + int maxNumberVcs; + int maxRawSize; + long serverTime; + int serverTimeZone; + int encryptionKeyLength; + byte[] encryptionKey; + } + + static synchronized SmbTransport getSmbTransport( UniAddress address, int port ) { + return getSmbTransport( address, port, + Config.getInetAddress( "jcifs.smb.client.laddr", null ), + Config.getInt( "jcifs.smb.client.lport", 0 )); + } + static synchronized SmbTransport getSmbTransport( UniAddress address, int port, + InetAddress localAddr, int localPort ) { + SmbTransport conn; + for( Enumeration e = connections.elements(); e.hasMoreElements(); ) { + conn = (SmbTransport)e.nextElement(); + if( conn.matches( address, port, localAddr, localPort )) { + return conn; + } + } + + conn = new SmbTransport( address, port, localAddr, localPort ); + connections.addElement( conn ); + return conn; + } + + SmbTransport( UniAddress address, int port, InetAddress localAddr, int localPort ) { + this.address = address; + this.port = port; + this.localAddr = localAddr; + this.localPort = localPort; + + useUnicode = Config.getBoolean( "jcifs.smb.client.useUnicode", true ); + if( useUnicode == false ) { + client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE; + client.capabilities &= 0xFFFF ^ ServerMessageBlock.CAP_UNICODE; + } + if( Config.getBoolean( "jcifs.smb.client.useNTSmbs", false ) == true ) { + client.capabilities |= ServerMessageBlock.CAP_NT_SMBS; + } + + soTimeout = Config.getInt( "jcifs.smb.client.soTimeout", DEFAULT_SO_TIMEOUT ); + responseTimeout = Config.getInt( "jcifs.smb.client.responseTimeout", DEFAULT_RESPONSE_TIMEOUT ); + rcv_buf_size = Config.getInt( "jcifs.smb.client.rcv_buf_size", DEFAULT_RCV_BUF_SIZE ); + snd_buf_size = client.maxBufferSize; + sessions = new LinkedList(); + responseTable = new Hashtable(); + outLock = new Object(); + negotiated = false; + } + + synchronized SmbSession getSmbSession() { + return getSmbSession( new NtlmPasswordAuthentication( null, null, null )); + } + synchronized SmbSession getSmbSession( NtlmPasswordAuthentication auth ) { + SmbSession ssn; + + ListIterator iter = sessions.listIterator(); + while( iter.hasNext() ) { + ssn = (SmbSession)iter.next(); + if( ssn.matches( auth )) { + ssn.auth = auth; + return ssn; + } + } + ssn = new SmbSession( this, auth ); + sessions.add( ssn ); + + if( sessions.size() > ssnLimit ) { + int nclose = sessions.size() / 10; + SmbSession s; + + while( nclose-- > 0 ) { + s = (SmbSession)sessions.removeFirst(); + s.logoff( false ); + } + } + + return ssn; + } + boolean matches( UniAddress address, int port, InetAddress localAddr, int localPort ) { + InetAddress defaultLocal = null; + try { + defaultLocal = InetAddress.getLocalHost(); + } catch( UnknownHostException uhe ) { + } + int p1 = ( port == 0 || port == 139 ) ? 0 : port; + int p2 = ( this.port == 0 || this.port == 139 ) ? 0 : this.port; + InetAddress la1 = localAddr == null ? defaultLocal : localAddr; + InetAddress la2 = this.localAddr == null ? defaultLocal : this.localAddr; + return address.equals( this.address ) && + p1 == p2 && + la1.equals( la2 ) && + localPort == this.localPort; + } + boolean hasCapability( int cap ) throws SmbException { + negotiate(); + return (negotiatedCapabilities & cap) == cap; + } + void ensureOpen() throws IOException { + if( socket == null ) { + Object obj; + NbtAddress naddr; + String calledName; + + /* Hack to convert InetAddress to NbtAddress until we properly + * abstract NetBIOSless transport with some kind of "UniSocket". + */ + obj = address.getAddress(); + if( obj instanceof NbtAddress ) { + naddr = (NbtAddress)obj; + } else { + try { + naddr = NbtAddress.getByName( ((InetAddress)obj).getHostAddress() ); + } catch( UnknownHostException uhe ) { + naddr = null; // never happen + } + } + + calledName = address.firstCalledName(); + do { + try { + socket = new NbtSocket( naddr, calledName, port, localAddr, localPort ); + break; + } catch( NbtException ne ) { + if( ne.errorClass == NbtException.ERR_SSN_SRVC && + ( ne.errorCode == NbtException.CALLED_NOT_PRESENT || + ne.errorCode == NbtException.NOT_LISTENING_CALLED )) { + Log.println( Log.WARNINGS, "smb warning", ne.getMessage() ); + } else { + throw ne; + } + } + } while(( calledName = address.nextCalledName()) != null ); + + if( calledName == null ) { + throw new IOException( "Failed to establish session with " + address ); + } + + /* Save the calledName for using on SMB_COM_TREE_CONNECT + */ + if( calledName == NbtAddress.SMBSERVER_NAME ) { + server.tconHostName = address.getHostAddress(); + } else { + server.tconHostName = calledName; + } + + if( Config.getBoolean( "jcifs.smb.client.tcpNoDelay", false )) { + socket.setTcpNoDelay( true ); + } + in = new PushbackInputStream( socket.getInputStream(), PUSHBACK_BUF_SIZE ); + out = socket.getOutputStream(); + thread = new Thread( this, "JCIFS-SmbTransport" ); + thread.setDaemon( true ); + thread.start(); + } + } + void tryClose( boolean inError ) { + SmbSession ssn; + + if( socket == null ) { + inError = true; + } + + ListIterator iter = sessions.listIterator(); + while( iter.hasNext() ) { + ssn = (SmbSession)iter.next(); + ssn.logoff( inError ); + } + if( socket != null ) { + try { + socket.close(); + } catch( IOException ioe ) { + } + } + in = null; + out = null; + socket = null; + negotiated = false; + thread = null; + responseTable.clear(); + } + public void run() { + int mid, l, i, n, m; + ServerMessageBlock response; + int magic[] = { 0xFF, 'S', 'M', 'B' }; + + while( thread == Thread.currentThread() ) { + try { + socket.setSoTimeout( soTimeout ); + + m = 0; + while( m < 4 ) { + if(( i = in.read() ) < 0 ) { + return; + } + if(( i & 0xFF ) == magic[m] ) { + m++; + } else if(( i & 0xFF ) == 0xFF ) { + m = 1; + } else { + m = 0; + } + } + +synchronized( rcv_buf ) { + rcv_buf[0] = (byte)0xFF; + rcv_buf[1] = (byte)'S'; + rcv_buf[2] = (byte)'M'; + rcv_buf[3] = (byte)'B'; + + if( in.read( rcv_buf, 4, HEADER_LENGTH - 4 ) != ( HEADER_LENGTH - 4 )) { + /* Read on a netbios SocketInputStream does not + * return until len bytes have been read or the stream is + * closed. This must be the latter case. + */ + break; + } + ((PushbackInputStream)in).unread( rcv_buf, 0, HEADER_LENGTH ); + if( rcv_buf[0] != (byte)0xFF || + rcv_buf[1] != (byte)'S' || + rcv_buf[2] != (byte)'M' || + rcv_buf[3] != (byte)'B' ) { + Log.println( Log.WARNINGS, "smb warning", + "bad smb header, purging session message" ); + in.skip( in.available() ); + continue; + } + if(( rcv_buf[FLAGS_OFFSET] & FLAGS_RESPONSE ) == FLAGS_RESPONSE ) { + mid = ServerMessageBlock.readInt2( rcv_buf, MID_OFFSET ); + + response = (ServerMessageBlock)responseTable.get( new Integer( mid )); + if( response == null ) { + Log.println( Log.WARNINGS, "smb warning", + " no handler for mid=" + mid + ", purging session message" ); + in.skip( in.available() ); + continue; + } + synchronized( response ) { + response.useUnicode = useUnicode; + Log.println( Log.DEBUGGING, "smb transport warning", + " new data read from socket" ); + + if( response instanceof SmbComTransactionResponse ) { + Enumeration e = (Enumeration)response; + if( e.hasMoreElements() ) { + e.nextElement(); + } else { + Log.println( Log.WARNINGS, "smb warning", + "more responses to transaction than expected" ); + continue; + } + response.readWireFormat( in, rcv_buf, 0 ); + response.received = true; + Log.printMessageData( "smb received", response ); + + if( response.errorCode != 0 || e.hasMoreElements() == false ) { + ((SmbComTransactionResponse)response).hasMore = false; + response.notify(); + } else { + ensureOpen(); + } + } else { + response.readWireFormat( in, rcv_buf, 0 ); + response.received = true; + + Log.printMessageData( "smb received", response ); + ServerMessageBlock smb = response; + while( smb instanceof AndXServerMessageBlock && + ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) { + Log.printMessageData( "smb andx data", smb ); + } + Log.printHexDump( "smb received", rcv_buf, 0, response.length ); + + response.notify(); + } + } + } else { + // it's a request(break oplock) + } +} + } catch( InterruptedIOException iioe ) { + if( responseTable.size() == 0 ) { + tryClose( false ); + } else { + Log.println( Log.WARNINGS, "smb warning", + " soTimeout has occured but there are " + + responseTable.size() + " pending requests" ); + } + } catch( IOException ioe ) { + tryClose( true ); + Log.printStackTrace( "exception reading from socket input: " + address, ioe ); + } + } + } + void send( ServerMessageBlock request, + ServerMessageBlock response ) throws SmbException { + Integer mid = null; + + negotiate(); + + request.flags2 = client.flags2; + request.mid = aquireMid(); + request.useUnicode = useUnicode; + + if( response == null ) { + try { + synchronized( outLock ) { + ensureOpen(); +synchronized( snd_buf ) { + out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); + out.flush(); + + Log.printMessageData( "smb sent", request ); + ServerMessageBlock smb = request; + while( smb instanceof AndXServerMessageBlock && + ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) { + Log.printMessageData( "smb andx data", smb ); + } + Log.printHexDump( "smb sent", snd_buf, 0, request.length ); +} + } + } catch( IOException ioe ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); + } finally { + releaseMid( request.mid ); + } + + return; + } + + // now for the normal case where response is not null + + try { + synchronized( response ) { + + response.received = false; + mid = new Integer( request.mid ); + responseTable.put( mid, response ); + + synchronized( outLock ) { + ensureOpen(); +synchronized( snd_buf ) { + out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); + out.flush(); + + Log.printMessageData( "smb sent", request ); + ServerMessageBlock smb = request; + while( smb instanceof AndXServerMessageBlock && + ( smb = ((AndXServerMessageBlock)smb).andx ) != null ) { + Log.printMessageData( "smb andx data", smb ); + } + Log.printHexDump( "smb sent", snd_buf, 0, request.length ); +} + } + + // default it 1 so that 0 can be used as forever + response.wait( response.responseTimeout == 1 ? + responseTimeout : response.responseTimeout ); + } + } catch( InterruptedException ie ) { + tryClose( true ); + } catch( IOException ioe ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); + } finally { + responseTable.remove( mid ); + releaseMid( request.mid ); + } + + if( response.received == false ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRserverTimeout, + address ); + } + switch( response.errorCode & 0xFF ) { + case SmbException.SUCCESS: + break; + case SmbException.ERRDOS: + switch(( response.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRnoaccess: + throw new SmbAuthException( response.errorCode ); + } + throw new SmbException( response.errorCode ); + case SmbException.ERRSRV: + switch(( response.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRbadpw: + case SmbException.ERRaccess: + case SmbException.ERRaccountExpired: + case SmbException.ERRbadClient: + case SmbException.ERRbadLogonTime: + case SmbException.ERRpasswordExpired: + throw new SmbAuthException( response.errorCode ); + } + default: + throw new SmbException( response.errorCode ); + } + + } + void sendTransaction( SmbComTransaction request, + SmbComTransactionResponse response ) throws SmbException { + Integer mid = null; + + negotiate(); + + request.flags2 = client.flags2; + request.mid = aquireMid(); + mid = new Integer( request.mid ); + request.useUnicode = useUnicode; + request.maxBufferSize = negotiatedMaxBufferSize; + response.received = false; + response.hasMore = true; + response.isPrimary = true; + + try { + request.txn_buf = BufferCache.getBuffer(); + response.txn_buf = BufferCache.getBuffer(); + + request.nextElement(); + if( request.hasMoreElements() ) { + // multi-part request + + SmbComBlankResponse interimResponse = new SmbComBlankResponse(); + + synchronized( interimResponse ) { + + responseTable.put( mid, interimResponse ); + + synchronized( outLock ) { + ensureOpen(); +synchronized(snd_buf) { + out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); + out.flush(); + + Log.printMessageData( "smb sent", request ); + Log.printHexDump( "smb sent", snd_buf, 0, request.length ); +} + } + + interimResponse.wait( responseTimeout ); + + if( interimResponse.received == false ) { + throw new SmbException( SmbException.ERRserverTimeout ); + } + switch( interimResponse.errorCode & 0xFF ) { + case SmbException.SUCCESS: + break; + case SmbException.ERRDOS: + switch(( response.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRnoaccess: + throw new SmbAuthException( response.errorCode ); + } + throw new SmbException( response.errorCode ); + case SmbException.ERRSRV: + switch(( interimResponse.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRbadpw: + case SmbException.ERRaccess: + case SmbException.ERRaccountExpired: + case SmbException.ERRbadClient: + case SmbException.ERRbadLogonTime: + case SmbException.ERRpasswordExpired: + throw new SmbAuthException( interimResponse.errorCode ); + } + default: + throw new SmbException( interimResponse.errorCode ); + } + } + request.nextElement(); + } + + synchronized( response ) { + responseTable.put( mid, response ); + do { + synchronized( outLock ) { + ensureOpen(); +synchronized( snd_buf ) { + out.write( snd_buf, 0, request.writeWireFormat( snd_buf, 0 )); + out.flush(); + + Log.printMessageData( "smb sent", request ); + Log.printHexDump( "smb sent", snd_buf, 0, request.length ); +} + } + } while( request.hasMoreElements() && request.nextElement() != null ); + + do { + // default it 1 so that 0 can be used as forever + response.received = false; + response.wait( response.responseTimeout == 1 ? responseTimeout : response.responseTimeout ); + } while( response.received && response.hasMoreElements() ); + } + } catch( InterruptedException ie ) { + tryClose( true ); + } catch( IOException ioe ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); + } finally { + responseTable.remove( mid ); + releaseMid( request.mid ); + BufferCache.releaseBuffer( request.txn_buf ); + BufferCache.releaseBuffer( response.txn_buf ); + } + + if( response.received == false ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRserverTimeout, + address ); + } + switch( response.errorCode & 0xFF ) { + case SmbException.SUCCESS: + break; + case SmbException.ERRDOS: + switch(( response.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRnoaccess: + throw new SmbAuthException( response.errorCode ); + } + throw new SmbException( response.errorCode ); + case SmbException.ERRSRV: + switch(( response.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRbadpw: + case SmbException.ERRaccess: + case SmbException.ERRaccountExpired: + case SmbException.ERRbadClient: + case SmbException.ERRbadLogonTime: + case SmbException.ERRpasswordExpired: + throw new SmbAuthException( response.errorCode ); + } + default: + throw new SmbException( response.errorCode ); + } + } + synchronized void negotiate() throws SmbException { + + if( negotiated ) { + return; + } + /* we must set this here rather than later because this + * calls send() which calls negotiate so we don't want to + * end up in a loop. The alternative would be to inline + * the send routine here but this seems okay for now + */ + negotiated = true; + + Log.println( Log.WARNINGS, "smb negotiation warning", + " requesting negotiation with " + address ); + + /* + * Negotiate Protocol Request / Response + */ + + SmbComNegotiateResponse response = new SmbComNegotiateResponse(); + send( new SmbComNegotiate(), response ); + + if( response.dialectIndex > 10 ) { + tryClose( true ); + throw new SmbException( SmbException.ERRCLI, SmbException.ERRbadDialect ); + } + + server.securityMode = response.securityMode; + server.security = response.security; + server.encryptedPasswords = response.encryptedPasswords; + server.signaturesEnabled = response.signaturesEnabled; + server.signaturesRequired = response.signaturesRequired; + negotiatedDialectIndex = response.dialectIndex;; + server.maxMpxCount = response.maxMpxCount; + negotiatedMaxMpxCount = client.maxMpxCount < server.maxMpxCount ? + client.maxMpxCount : server.maxMpxCount; + mpxCtrl.maxMpxCount = negotiatedMaxMpxCount < 1 ? + 1 : negotiatedMaxMpxCount; + server.maxNumberVcs = response.maxNumberVcs; + server.maxBufferSize = response.maxBufferSize; + negotiatedMaxBufferSize = client.maxBufferSize < server.maxBufferSize ? + client.maxBufferSize : server.maxBufferSize; + server.maxRawSize = response.maxRawSize; + server.sessionKey = response.sessionKey; + server.capabilities = response.capabilities; + negotiatedCapabilities = client.capabilities & server.capabilities; + if(( negotiatedCapabilities & ServerMessageBlock.CAP_UNICODE ) == 0 ) { + // server doesn't want unicode + if( Config.getBoolean( "jcifs.smb.client.useUnicode", false )) { + // force unicode + negotiatedCapabilities |= ServerMessageBlock.CAP_UNICODE; + } else { + // not explicitly set to true so flip unicode off as server requests + useUnicode = false; + client.flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE; + } + } + server.serverTime = response.serverTime; + server.serverTimeZone = response.serverTimeZone; + server.encryptionKeyLength = response.encryptionKeyLength; + server.encryptionKey = response.encryptionKey; + server.oemDomainName = response.oemDomainName; + } + public String toString() { + String ret = "SmbTransport[address=" + address; + if( socket == null ) { + ret += ",port=,localAddr=,localPort=]"; + } else { + ret += ",port=" + socket.getPort() + + ",localAddr=" + socket.getLocalAddress() + + ",localPort=" + socket.getLocalPort() + "]"; + } + return ret; + } + + static int aquireMid() throws SmbException { + try { + return mpxCtrl.aquireMid(); + } catch( InterruptedException ie ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ie.getMessage() ); + } + } + static void releaseMid( int mid ) { + mpxCtrl.releaseMid( mid ); + } + + static class MpxControl { + MpxListNode first, last; + class MpxListNode { + int i; + MpxListNode next; + MpxListNode( int id ) { + i = id; + next = null; + } + } + int nextMid, mpxCount, maxMpxCount; + + MpxControl() { + nextMid = 0; + mpxCount = 0; + maxMpxCount = 1; + first = last = null; + } + + synchronized void clear() { + nextMid = 0; + mpxCount = 0; + maxMpxCount = 1; + first = last = null; + } + synchronized int aquireMid() throws InterruptedException { + while( mpxCount >= maxMpxCount ) { + wait(); + } + if(( ++nextMid % 0xFFFF ) == 0 ) { + nextMid = 1; + } + add( nextMid ); + mpxCount++; + return nextMid; + } + synchronized void releaseMid( int i ) { + remove( i ); + mpxCount--; + notify(); + } + synchronized boolean contains( int i ) { + for( MpxListNode tmp = first; tmp != null; tmp = tmp.next ) { + if( tmp.i == i ) { + return true; + } + } + return false; + } + synchronized void add( int i ) { + if( contains( i )) { + return; + } + if( first == null ) { + first = last = new MpxListNode( i ); + } else { + last = last.next = new MpxListNode( i ); + } + } + synchronized void remove( int i ) { + if( first == null ) { + return; + } else if( first == last && first.i == i ) { + first = last = null; + return; + } + MpxListNode tmp, prev; + for( tmp = prev = first; tmp != null; tmp = tmp.next ) { + if( tmp.i == i ) { + if( tmp == first ) { + first = first.next; + } else if( tmp == last ) { + last = prev; + last.next = null; + } else { + prev.next = tmp.next; + } + return; + } + prev = tmp; + } + } + } +} diff --git a/update/Base64.java b/update/Base64.java deleted file mode 100644 index 9008667..0000000 --- a/update/Base64.java +++ /dev/null @@ -1,94 +0,0 @@ -/* Encodes and decodes to and from Base64 notation. - * Copyright (C) 2003 "Eric Glass" - * - * 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.util; - -public class Base64 { - - private static final String ALPHABET = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - /** - * Base-64 encodes the supplied block of data. Line wrapping is not - * applied on output. - * - * @param bytes The block of data that is to be Base-64 encoded. - * @return A String containing the encoded data. - */ - public static String encode(byte[] bytes) { - int length = bytes.length; - if (length == 0) return ""; - StringBuffer buffer = - new StringBuffer((int) Math.ceil((double) length / 3d) * 4); - int remainder = length % 3; - length -= remainder; - int block; - int i = 0; - while (i < length) { - block = ((bytes[i++] & 0xff) << 16) | ((bytes[i++] & 0xff) << 8) | - (bytes[i++] & 0xff); - buffer.append(ALPHABET.charAt(block >>> 18)); - buffer.append(ALPHABET.charAt((block >>> 12) & 0x3f)); - buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f)); - buffer.append(ALPHABET.charAt(block & 0x3f)); - } - if (remainder == 0) return buffer.toString(); - if (remainder == 1) { - block = (bytes[i] & 0xff) << 4; - buffer.append(ALPHABET.charAt(block >>> 6)); - buffer.append(ALPHABET.charAt(block & 0x3f)); - buffer.append("=="); - return buffer.toString(); - } - block = (((bytes[i++] & 0xff) << 8) | ((bytes[i]) & 0xff)) << 2; - buffer.append(ALPHABET.charAt(block >>> 12)); - buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f)); - buffer.append(ALPHABET.charAt(block & 0x3f)); - buffer.append("="); - return buffer.toString(); - } - - /** - * Decodes the supplied Base-64 encoded string. - * - * @param string The Base-64 encoded string that is to be decoded. - * @return A byte[] containing the decoded data block. - */ - public static byte[] decode(String string) { - int length = string.length(); - if (length == 0) return new byte[0]; - int pad = (string.charAt(length - 2) == '=') ? 2 : - (string.charAt(length - 1) == '=') ? 1 : 0; - int size = length * 3 / 4 - pad; - byte[] buffer = new byte[size]; - int block; - int i = 0; - int index = 0; - while (i < length) { - block = (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 18 | - (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 12 | - (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 6 | - (ALPHABET.indexOf(string.charAt(i++)) & 0xff); - buffer[index++] = (byte) (block >>> 16); - if (index < size) buffer[index++] = (byte) ((block >>> 8) & 0xff); - if (index < size) buffer[index++] = (byte) (block & 0xff); - } - return buffer; - } - -} diff --git a/update/HMACT64.java b/update/HMACT64.java deleted file mode 100644 index 8420fb9..0000000 --- a/update/HMACT64.java +++ /dev/null @@ -1,116 +0,0 @@ -/* HMACT64 keyed hashing algorithm - * Copyright (C) 2003 "Eric Glass" - * - * 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.util; - -import java.security.MessageDigest; - -/** - * This is an implementation of the HMACT64 keyed hashing algorithm. - * HMACT64 is defined by Luke Leighton as a modified HMAC-MD5 (RFC 2104) - * in which the key is truncated at 64 bytes (rather than being hashed - * via MD5). - */ -public class HMACT64 extends MessageDigest implements Cloneable { - - private static final int BLOCK_LENGTH = 64; - - private static final byte IPAD = (byte) 0x36; - - private static final byte OPAD = (byte) 0x5c; - - private MessageDigest md5; - - private byte[] ipad = new byte[BLOCK_LENGTH]; - - private byte[] opad = new byte[BLOCK_LENGTH]; - - /** - * Creates an HMACT64 instance which uses the given secret key material. - * - * @param key The key material to use in hashing. - */ - public HMACT64(byte[] key) { - super("HMACT64"); - int length = Math.min(key.length, BLOCK_LENGTH); - for (int i = 0; i < length; i++) { - ipad[i] = (byte) (key[i] ^ IPAD); - opad[i] = (byte) (key[i] ^ OPAD); - } - for (int i = length; i < BLOCK_LENGTH; i++) { - ipad[i] = IPAD; - opad[i] = OPAD; - } - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (Exception ex) { - throw new IllegalStateException(ex.getMessage()); - } - engineReset(); - } - - private HMACT64(HMACT64 hmac) throws CloneNotSupportedException { - super("HMACT64"); - this.ipad = hmac.ipad; - this.opad = hmac.opad; - this.md5 = (MessageDigest) hmac.md5.clone(); - } - - public Object clone() { - try { - return new HMACT64(this); - } catch (CloneNotSupportedException ex) { - throw new IllegalStateException(ex.getMessage()); - } - } - - protected byte[] engineDigest() { - byte[] digest = md5.digest(); - md5.update(opad); - return md5.digest(digest); - } - - protected int engineDigest(byte[] buf, int offset, int len) { - byte[] digest = md5.digest(); - md5.update(opad); - md5.update(digest); - try { - return md5.digest(buf, offset, len); - } catch (Exception ex) { - throw new IllegalStateException(); - } - } - - protected int engineGetDigestLength() { - return md5.getDigestLength(); - } - - protected void engineReset() { - md5.reset(); - md5.update(ipad); - } - - protected void engineUpdate(byte b) { - md5.update(b); - } - - protected void engineUpdate(byte[] input, int offset, int len) { - md5.update(input, offset, len); - } - -} diff --git a/update/NtlmHttpFilter.java b/update/NtlmHttpFilter.java deleted file mode 100644 index d160e65..0000000 --- a/update/NtlmHttpFilter.java +++ /dev/null @@ -1,164 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Jason Pugsley" - * "skeetz" - * "Eric Glass" - * - * 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.http; - -import java.io.*; -import java.util.Enumeration; -import java.net.UnknownHostException; -import javax.servlet.*; -import javax.servlet.http.*; -import jcifs.*; -import jcifs.smb.SmbSession; -import jcifs.smb.NtlmPasswordAuthentication; -import jcifs.smb.SmbAuthException; -import jcifs.util.Base64; - -/** - * This servlet Filter can be used to negotiate password hashes with - * MSIE clients using NTLM SSP. This is similar to Authentication: - * BASIC but weakly encrypted and without requiring the user to re-supply - * authentication credentials. - *

- * Read jCIFS NTLM HTTP Authentication and the Network Explorer Servlet for complete details. - */ - -public class NtlmHttpFilter implements Filter { - - private String defaultDomain; - - private String domainController; - - private boolean enableBasic; - - private boolean insecureBasic; - - private String realm; - - public void init( FilterConfig filterConfig ) throws ServletException { - String name; - - /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min. - */ - Config.setProperty( "jcifs.smb.client.soTimeout", "300000" ); - Config.setProperty( "jcifs.netbios.cachePolicy", "600" ); - - Enumeration e = filterConfig.getInitParameterNames(); - while( e.hasMoreElements() ) { - name = (String)e.nextElement(); - if( name.startsWith( "jcifs." )) { - Config.setProperty( name, filterConfig.getInitParameter( name )); - } - } - defaultDomain = Config.getProperty("jcifs.smb.client.domain"); - domainController = Config.getProperty( "jcifs.http.domainController" ); - if( domainController == null ) domainController = defaultDomain; - enableBasic = Boolean.valueOf( - Config.getProperty("jcifs.http.enableBasic")).booleanValue(); - insecureBasic = Boolean.valueOf( - Config.getProperty("jcifs.http.insecureBasic")).booleanValue(); - realm = Config.getProperty("jcifs.http.basicRealm"); - if (realm == null) realm = "jCIFS"; - } - public void destroy() { - } - public void doFilter( ServletRequest request, - ServletResponse response, - FilterChain chain ) throws IOException, ServletException { - HttpServletRequest req; - HttpServletResponse resp; - UniAddress dc; - String msg; - - NtlmPasswordAuthentication ntlm = null; - req = (HttpServletRequest)request; - resp = (HttpServletResponse)response; - msg = req.getHeader( "Authorization" ); - boolean offerBasic = enableBasic && (insecureBasic || req.isSecure()); - - if( msg != null && (msg.startsWith( "NTLM " ) || - (offerBasic && msg.startsWith("Basic ")))) { - dc = UniAddress.getByName( domainController, true ); - if (msg.startsWith("NTLM ")) { - byte[] challenge = SmbSession.getChallenge( dc ); - if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) { - return; - } - } else { - String auth = new String(Base64.decode(msg.substring(6)), - "US-ASCII"); - int index = auth.indexOf(':'); - String user = (index != -1) ? auth.substring(0, index) : auth; - String password = (index != -1) ? auth.substring(index + 1) : - ""; - index = user.indexOf('\\'); - if (index == -1) index = user.indexOf('/'); - String domain = (index != -1) ? user.substring(0, index) : - defaultDomain; - user = (index != -1) ? user.substring(index + 1) : user; - ntlm = new NtlmPasswordAuthentication(domain, user, password); - } - try { - SmbSession.logon( dc, ntlm ); - } catch( SmbAuthException sae ) { - resp.setHeader( "WWW-Authenticate", "NTLM" ); - if (offerBasic) { - resp.addHeader( "WWW-Authenticate", "Basic realm=\"" + - realm + "\""); - } - resp.setHeader( "Connection", "close" ); - resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED ); - resp.flushBuffer(); - return; - } - req.getSession().setAttribute( "NtlmHttpAuth", ntlm ); - } else { - HttpSession ssn = req.getSession(false); - if (ssn == null || (ntlm = (NtlmPasswordAuthentication) - ssn.getAttribute("NtlmHttpAuth")) == null) { - resp.setHeader( "WWW-Authenticate", "NTLM" ); - if (offerBasic) { - resp.addHeader( "WWW-Authenticate", "Basic realm=\"" + - realm + "\""); - } - resp.setHeader( "Connection", "close" ); - resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED ); - resp.flushBuffer(); - return; - } - } - - chain.doFilter( new NtlmHttpServletRequest( req, ntlm ), response ); - } - - // Added by cgross to work with weblogic 6.1. - public void setFilterConfig( FilterConfig f ) { - try { - init( f ); - } catch( Exception e ) { - e.printStackTrace(); - } - } - public FilterConfig getFilterConfig() { - return null; - } -} - diff --git a/update/NtlmHttpURLConnection.java b/update/NtlmHttpURLConnection.java deleted file mode 100644 index 2af8f50..0000000 --- a/update/NtlmHttpURLConnection.java +++ /dev/null @@ -1,563 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Eric Glass" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * 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.http; - -import java.io.InputStream; -import java.io.IOException; -import java.io.OutputStream; - -import java.net.Authenticator; -import java.net.HttpURLConnection; -import java.net.PasswordAuthentication; -import java.net.ProtocolException; -import java.net.URL; -import java.net.URLDecoder; - -import java.security.Permission; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import jcifs.Config; - -import jcifs.ntlmssp.NtlmFlags; -import jcifs.ntlmssp.NtlmMessage; -import jcifs.ntlmssp.Type1Message; -import jcifs.ntlmssp.Type2Message; -import jcifs.ntlmssp.Type3Message; - -import jcifs.util.Base64; - -/** - * Wraps an HttpURLConnection to provide NTLM authentication - * services. - * - * Please read Using jCIFS NTLM Authentication for HTTP Connections. - */ -public class NtlmHttpURLConnection extends HttpURLConnection { - - private static final int MAX_REDIRECTS = - Integer.parseInt(System.getProperty("http.maxRedirects", "20")); - - private static final int LM_COMPATIBILITY = - Config.getInt("jcifs.smb.lmCompatibility", 0); - - private static final String DEFAULT_DOMAIN; - - private HttpURLConnection connection; - - private Map requestProperties; - - private Map headerFields; - - private String authProperty; - - private String method; - - static { - String domain = System.getProperty("http.auth.ntlm.domain"); - if (domain == null) domain = Type3Message.getDefaultDomain(); - DEFAULT_DOMAIN = domain; - } - - public NtlmHttpURLConnection(HttpURLConnection connection) { - super(connection.getURL()); - this.connection = connection; - requestProperties = new HashMap(); - } - - public void connect() throws IOException { - if (connected) return; - doConnect(); - connected = true; - } - - public URL getURL() { - return connection.getURL(); - } - - public int getContentLength() { - try { - connect(); - } catch (IOException ex) { } - return connection.getContentLength(); - } - - public String getContentType() { - try { - connect(); - } catch (IOException ex) { } - return connection.getContentType(); - } - - public String getContentEncoding() { - try { - connect(); - } catch (IOException ex) { } - return connection.getContentEncoding(); - } - - public long getExpiration() { - try { - connect(); - } catch (IOException ex) { } - return connection.getExpiration(); - } - - public long getDate() { - try { - connect(); - } catch (IOException ex) { } - return connection.getDate(); - } - - public long getLastModified() { - try { - connect(); - } catch (IOException ex) { } - return connection.getLastModified(); - } - - public String getHeaderField(String header) { - try { - connect(); - } catch (IOException ex) { } - return connection.getHeaderField(header); - } - - private synchronized Map getHeaderFields0() { - if (headerFields != null) return headerFields; - Map map = new HashMap(); - String key = connection.getHeaderFieldKey(0); - String value = connection.getHeaderField(0); - for (int i = 1; key != null || value != null; i++) { - List values = (List) map.get(key); - if (values == null) { - values = new ArrayList(); - map.put(key, values); - } - values.add(value); - key = connection.getHeaderFieldKey(i); - value = connection.getHeaderField(i); - } - Iterator entries = map.entrySet().iterator(); - while (entries.hasNext()) { - Map.Entry entry = (Map.Entry) entries.next(); - entry.setValue(Collections.unmodifiableList((List) - entry.getValue())); - } - return (headerFields = Collections.unmodifiableMap(map)); - } - - public Map getHeaderFields() { - synchronized (this) { - if (headerFields != null) return headerFields; - } - try { - connect(); - } catch (IOException ex) { } - return getHeaderFields0(); - } - - public int getHeaderFieldInt(String header, int def) { - try { - connect(); - } catch (IOException ex) { } - return connection.getHeaderFieldInt(header, def); - } - - public long getHeaderFieldDate(String header, long def) { - try { - connect(); - } catch (IOException ex) { } - return connection.getHeaderFieldDate(header, def); - } - - public String getHeaderFieldKey(int index) { - try { - connect(); - } catch (IOException ex) { } - return connection.getHeaderFieldKey(index); - } - - public String getHeaderField(int index) { - try { - connect(); - } catch (IOException ex) { } - return connection.getHeaderField(index); - } - - public Object getContent() throws IOException { - try { - connect(); - } catch (IOException ex) { } - return connection.getContent(); - } - - public Object getContent(Class[] classes) throws IOException { - try { - connect(); - } catch (IOException ex) { } - return connection.getContent(classes); - } - - public Permission getPermission() throws IOException { - return connection.getPermission(); - } - - public InputStream getInputStream() throws IOException { - try { - connect(); - } catch (IOException ex) { } - return connection.getInputStream(); - } - - public OutputStream getOutputStream() throws IOException { - try { - connect(); - } catch (IOException ex) { } - return connection.getOutputStream(); - } - - public String toString() { - return connection.toString(); - } - - public void setDoInput(boolean doInput) { - connection.setDoInput(doInput); - this.doInput = doInput; - } - - public boolean getDoInput() { - return connection.getDoInput(); - } - - public void setDoOutput(boolean doOutput) { - connection.setDoOutput(doOutput); - this.doOutput = doOutput; - } - - public boolean getDoOutput() { - return connection.getDoOutput(); - } - - public void setAllowUserInteraction(boolean allowUserInteraction) { - connection.setAllowUserInteraction(allowUserInteraction); - this.allowUserInteraction = allowUserInteraction; - } - - public boolean getAllowUserInteraction() { - return connection.getAllowUserInteraction(); - } - - public void setUseCaches(boolean useCaches) { - connection.setUseCaches(useCaches); - this.useCaches = useCaches; - } - - public boolean getUseCaches() { - return connection.getUseCaches(); - } - - public void setIfModifiedSince(long ifModifiedSince) { - connection.setIfModifiedSince(ifModifiedSince); - this.ifModifiedSince = ifModifiedSince; - } - - public long getIfModifiedSince() { - return connection.getIfModifiedSince(); - } - - public boolean getDefaultUseCaches() { - return connection.getDefaultUseCaches(); - } - - public void setDefaultUseCaches(boolean defaultUseCaches) { - connection.setDefaultUseCaches(defaultUseCaches); - } - - public void setRequestProperty(String key, String value) { - if (key == null) throw new NullPointerException(); - List values = new ArrayList(); - values.add(value); - boolean found = false; - synchronized (requestProperties) { - Iterator entries = requestProperties.entrySet().iterator(); - while (entries.hasNext()) { - Map.Entry entry = (Map.Entry) entries.next(); - if (key.equalsIgnoreCase((String) entry.getKey())) { - entry.setValue(value); - found = true; - break; - } - } - if (!found) requestProperties.put(key, values); - } - connection.setRequestProperty(key, value); - } - - public void addRequestProperty(String key, String value) { - if (key == null) throw new NullPointerException(); - List values = null; - synchronized (requestProperties) { - Iterator entries = requestProperties.entrySet().iterator(); - while (entries.hasNext()) { - Map.Entry entry = (Map.Entry) entries.next(); - if (key.equalsIgnoreCase((String) entry.getKey())) { - values = (List) entry.getValue(); - values.add(value); - break; - } - } - if (values == null) { - values = new ArrayList(); - values.add(value); - requestProperties.put(key, values); - } - } - // 1.3-compatible. - StringBuffer buffer = new StringBuffer(); - Iterator propertyValues = values.iterator(); - while (propertyValues.hasNext()) { - buffer.append(propertyValues.next()); - if (propertyValues.hasNext()) { - buffer.append(", "); - } - } - connection.setRequestProperty(key, buffer.toString()); - } - - public String getRequestProperty(String key) { - return connection.getRequestProperty(key); - } - - public Map getRequestProperties() { - Map map = new HashMap(); - synchronized (requestProperties) { - Iterator entries = requestProperties.entrySet().iterator(); - while (entries.hasNext()) { - Map.Entry entry = (Map.Entry) entries.next(); - map.put(entry.getKey(), - Collections.unmodifiableList((List) entry.getValue())); - } - } - return Collections.unmodifiableMap(map); - } - - public void setInstanceFollowRedirects(boolean instanceFollowRedirects) { - connection.setInstanceFollowRedirects(instanceFollowRedirects); - } - - public boolean getInstanceFollowRedirects() { - return connection.getInstanceFollowRedirects(); - } - - public void setRequestMethod(String requestMethod) - throws ProtocolException { - connection.setRequestMethod(requestMethod); - } - - public String getRequestMethod() { - return connection.getRequestMethod(); - } - - public int getResponseCode() throws IOException { - return connection.getResponseCode(); - } - - public String getResponseMessage() throws IOException { - return connection.getResponseMessage(); - } - - public void disconnect() { - connection.disconnect(); - connected = false; - } - - public boolean usingProxy() { - return connection.usingProxy(); - } - - public InputStream getErrorStream() { - return connection.getErrorStream(); - } - - private int parseResponseCode() throws IOException { - try { - String response = connection.getHeaderField(0); - int index = response.indexOf(' '); - while (response.charAt(index) == ' ') index++; - return Integer.parseInt(response.substring(index, index + 3)); - } catch (Exception ex) { - throw new IOException(ex.getMessage()); - } - } - - private synchronized void doConnect() throws IOException { - connection.connect(); - int response = parseResponseCode(); - if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) { - return; - } - Type1Message type1 = (Type1Message) attemptNegotiation(response); - if (type1 == null) return; // no NTLM - int attempt = 0; - while (attempt < MAX_REDIRECTS) { - connection.setRequestProperty(authProperty, method + ' ' + - Base64.encode(type1.toByteArray())); - connection.connect(); // send type 1 - response = parseResponseCode(); - if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) { - return; - } - Type3Message type3 = (Type3Message) attemptNegotiation(response); - if (type3 == null) return; - connection.setRequestProperty(authProperty, method + ' ' + - Base64.encode(type3.toByteArray())); - connection.connect(); // send type 3 - response = parseResponseCode(); - if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) { - return; - } - attempt++; - if (attempt < MAX_REDIRECTS) reconnect(); - } - throw new IOException("Unable to negotiate NTLM authentication."); - } - - private NtlmMessage attemptNegotiation(int response) throws IOException { - authProperty = null; - method = null; - InputStream errorStream = connection.getErrorStream(); - if (errorStream != null && errorStream.available() != 0) { - int count; - byte[] buf = new byte[1024]; - while ((count = errorStream.read(buf, 0, 1024)) != -1); - } - String authHeader; - if (response == HTTP_UNAUTHORIZED) { - authHeader = "WWW-Authenticate"; - authProperty = "Authorization"; - } else { - authHeader = "Proxy-Authenticate"; - authProperty = "Proxy-Authorization"; - } - String authorization = null; - List methods = (List) getHeaderFields0().get(authHeader); - if (methods == null) return null; - Iterator iterator = methods.iterator(); - while (iterator.hasNext()) { - String authMethod = (String) iterator.next(); - if (authMethod.startsWith("NTLM")) { - if (authMethod.length() == 4) { - method = "NTLM"; - break; - } - if (authMethod.indexOf(' ') != 4) continue; - method = "NTLM"; - authorization = authMethod.substring(5).trim(); - break; - } else if (authMethod.startsWith("Negotiate")) { - if (authMethod.length() == 9) { - method = "Negotiate"; - break; - } - if (authMethod.indexOf(' ') != 9) continue; - method = "Negotiate"; - authorization = authMethod.substring(10).trim(); - break; - } - } - if (method == null) return null; - NtlmMessage message = (authorization != null) ? - new Type2Message(Base64.decode(authorization)) : null; - reconnect(); - if (message == null) { - message = new Type1Message(); - if (LM_COMPATIBILITY > 2) { - message.setFlag(NtlmFlags.NTLMSSP_REQUEST_TARGET, true); - } - } else { - String domain = DEFAULT_DOMAIN; - String user = Type3Message.getDefaultUser(); - String password = Type3Message.getDefaultPassword(); - String userInfo = url.getUserInfo(); - if (userInfo != null) { - userInfo = URLDecoder.decode(userInfo); - int index = userInfo.indexOf(':'); - user = (index != -1) ? userInfo.substring(0, index) : userInfo; - if (index != -1) password = userInfo.substring(index + 1); - index = user.indexOf('\\'); - if (index == -1) index = user.indexOf('/'); - domain = (index != -1) ? user.substring(0, index) : domain; - user = (index != -1) ? user.substring(index + 1) : user; - } - if (user == null) { - try { - URL url = getURL(); - String protocol = url.getProtocol(); - int port = url.getPort(); - if (port == -1) { - port = "https".equalsIgnoreCase(protocol) ? 443 : 80; - } - PasswordAuthentication auth = - Authenticator.requestPasswordAuthentication(null, - port, protocol, "", method); - if (auth != null) { - user = auth.getUserName(); - password = new String(auth.getPassword()); - } - } catch (Exception ex) { } - } - Type2Message type2 = (Type2Message) message; - message = new Type3Message(type2, password, domain, user, - Type3Message.getDefaultWorkstation()); - } - return message; - } - - private void reconnect() throws IOException { - connection = (HttpURLConnection) connection.getURL().openConnection(); - headerFields = null; - synchronized (requestProperties) { - Iterator properties = requestProperties.entrySet().iterator(); - while (properties.hasNext()) { - Map.Entry property = (Map.Entry) properties.next(); - String key = (String) property.getKey(); - StringBuffer value = new StringBuffer(); - Iterator values = ((List) property.getValue()).iterator(); - while (values.hasNext()) { - value.append(values.next()); - if (values.hasNext()) value.append(", "); - } - connection.setRequestProperty(key, value.toString()); - } - } - connection.setAllowUserInteraction(allowUserInteraction); - connection.setDoInput(doInput); - connection.setDoOutput(doOutput); - connection.setIfModifiedSince(ifModifiedSince); - connection.setUseCaches(useCaches); - } -} diff --git a/update/NtlmMessage.java b/update/NtlmMessage.java deleted file mode 100644 index 3b8c692..0000000 --- a/update/NtlmMessage.java +++ /dev/null @@ -1,138 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Eric Glass" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * 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.ntlmssp; - -import jcifs.Config; - -/** - * Abstract superclass for all NTLMSSP messages. - */ -public abstract class NtlmMessage implements NtlmFlags { - - /** - * The NTLMSSP "preamble". - */ - protected static final byte[] NTLMSSP_SIGNATURE = new byte[] { - (byte) 'N', (byte) 'T', (byte) 'L', (byte) 'M', - (byte) 'S', (byte) 'S', (byte) 'P', (byte) 0 - }; - - private static final String OEM_ENCODING = - Config.getProperty("jcifs.smb.client.codepage", - Config.getProperty("jcifs.encoding", - System.getProperty("file.encoding"))); - - private int flags; - - /** - * Returns the flags currently in use for this message. - * - * @return An int containing the flags in use for this - * message. - */ - public int getFlags() { - return flags; - } - - /** - * Sets the flags for this message. - * - * @param flags The flags for this message. - */ - public void setFlags(int flags) { - this.flags = flags; - } - - /** - * Returns the status of the specified flag. - * - * @param flag The flag to test (i.e., NTLMSSP_NEGOTIATE_OEM). - * @return A boolean indicating whether the flag is set. - */ - public boolean getFlag(int flag) { - return (getFlags() & flag) != 0; - } - - /** - * Sets or clears the specified flag. - * - * @param flag The flag to set/clear (i.e., - * NTLMSSP_NEGOTIATE_OEM). - * @param value Indicates whether to set (true) or - * clear (false) the specified flag. - */ - public void setFlag(int flag, boolean value) { - setFlags(value ? (getFlags() | flag) : - (getFlags() & (0xffffffff ^ flag))); - } - - static int readULong(byte[] src, int index) { - return (src[index] & 0xff) | - ((src[index + 1] & 0xff) << 8) | - ((src[index + 2] & 0xff) << 16) | - ((src[index + 3] & 0xff) << 24); - } - - static int readUShort(byte[] src, int index) { - return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8); - } - - static byte[] readSecurityBuffer(byte[] src, int index) { - int length = readUShort(src, index); - int offset = readULong(src, index + 4); - byte[] buffer = new byte[length]; - System.arraycopy(src, offset, buffer, 0, length); - return buffer; - } - - static void writeULong(byte[] dest, int offset, int ulong) { - dest[offset] = (byte) (ulong & 0xff); - dest[offset + 1] = (byte) (ulong >> 8 & 0xff); - dest[offset + 2] = (byte) (ulong >> 16 & 0xff); - dest[offset + 3] = (byte) (ulong >> 24 & 0xff); - } - - static void writeUShort(byte[] dest, int offset, int ushort) { - dest[offset] = (byte) (ushort & 0xff); - dest[offset + 1] = (byte) (ushort >> 8 & 0xff); - } - - static void writeSecurityBuffer(byte[] dest, int offset, int bodyOffset, - byte[] src) { - int length = (src != null) ? src.length : 0; - if (length == 0) return; - writeUShort(dest, offset, length); - writeUShort(dest, offset + 2, length); - writeULong(dest, offset + 4, bodyOffset); - System.arraycopy(src, 0, dest, bodyOffset, length); - } - - static String getOEMEncoding() { - return OEM_ENCODING; - } - - /** - * Returns the raw byte representation of this message. - * - * @return A byte[] containing the raw message material. - */ - public abstract byte[] toByteArray(); - -} diff --git a/update/NtlmPasswordAuthentication.java b/update/NtlmPasswordAuthentication.java deleted file mode 100644 index a180c0c..0000000 --- a/update/NtlmPasswordAuthentication.java +++ /dev/null @@ -1,348 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package jcifs.smb; - -import jcifs.util.DES; -import jcifs.util.MD4; -import jcifs.util.HMACT64; -import java.io.UnsupportedEncodingException; -import java.security.Principal; -import java.security.SecureRandom; -import java.util.Arrays; -import jcifs.Config; - -/** - * This class stores and encrypts NTLM user credentials. The default - * credentials are retrieved from the jcifs.smb.client.domain, - * jcifs.smb.client.username, and jcifs.smb.client.password - * properties. - *

- * Read jCIFS Exceptions and - * NtlmAuthenticator for related information. - */ - -public final class NtlmPasswordAuthentication implements Principal { - - private static final int LM_COMPATIBILITY = - Config.getInt("jcifs.smb.lmCompatibility", 0); - - private static final String DEFAULT_DOMAIN = - Config.getProperty("jcifs.smb.client.domain", "?"); - - private static final String DEFAULT_USERNAME = - Config.getProperty("jcifs.smb.client.username", "GUEST"); - - private static final String DEFAULT_PASSWORD = - Config.getProperty("jcifs.smb.client.password", ""); - - private static final SecureRandom random = new SecureRandom(); - - // KGS!@#$% - static final byte[] S8 = { - (byte)0x4b, (byte)0x47, (byte)0x53, (byte)0x21, - (byte)0x40, (byte)0x23, (byte)0x24, (byte)0x25 - }; - static void E( byte[] key, byte[] data, byte[] e ) { - byte[] key7 = new byte[7]; - byte[] e8 = new byte[8]; - - for( int i = 0; i < key.length / 7; i++ ) { - System.arraycopy( key, i * 7, key7, 0, 7 ); - DES des = new DES( key7 ); - des.encrypt( data, e8 ); - System.arraycopy( e8, 0, e, i * 8, 8 ); - } - } -/** - * Generate the ANSI DES hash for the password associated with these credentials. - */ - static public byte[] getPreNTLMResponse( String password, byte[] challenge ) { - byte[] p14 = new byte[14]; - byte[] p21 = new byte[21]; - byte[] p24 = new byte[24]; - byte[] passwordBytes; - try { - passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.encoding ); - } catch( UnsupportedEncodingException uee ) { - return null; - } - int passwordLength = passwordBytes.length; - - // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM - if( passwordLength > 14) { - passwordLength = 14; - } - System.arraycopy( passwordBytes, 0, p14, 0, passwordLength ); - E( p14, S8, p21); - E( p21, challenge, p24); - return p24; - } -/** - * Generate the Unicode MD4 hash for the password associated with these credentials. - */ - static public byte[] getNTLMResponse( String password, byte[] challenge ) { - byte[] uni = null; - byte[] p21 = new byte[21]; - byte[] p24 = new byte[24]; - - try { - uni = password.getBytes( "UnicodeLittleUnmarked" ); - } catch( UnsupportedEncodingException uee ) { - Log.printStackTrace( "password encryption exception", uee ); - } - MD4 md4 = new MD4(); - md4.update( uni ); - try { - md4.digest(p21, 0, 16); - } catch (Exception ex) { - Log.printStackTrace( "digest exception", ex ); - } - E( p21, challenge, p24 ); - return p24; - } - - /** - * Creates the LMv2 response for the supplied information. - * - * @param domain The domain in which the username exists. - * @param user The username. - * @param password The user's password. - * @param challenge The server challenge. - * @param clientChallenge The client challenge (nonce). - */ - public static byte[] getLMv2Response(String domain, String user, - String password, byte[] challenge, byte[] clientChallenge) { - try { - byte[] hash = new byte[16]; - byte[] response = new byte[24]; - MD4 md4 = new MD4(); - md4.update(password.getBytes("UnicodeLittleUnmarked")); - HMACT64 hmac = new HMACT64(md4.digest()); - hmac.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked")); - hmac.update(domain.toUpperCase().getBytes("UnicodeLittleUnmarked")); - hmac = new HMACT64(hmac.digest()); - hmac.update(challenge); - hmac.update(clientChallenge); - hmac.digest(response, 0, 16); - System.arraycopy(clientChallenge, 0, response, 16, 8); - return response; - } catch (Exception ex) { - Log.printStackTrace("Error creating LMv2 response", ex); - return null; - } - } - - static final NtlmPasswordAuthentication NULL = - new NtlmPasswordAuthentication( "", "", "" ); - static final NtlmPasswordAuthentication GUEST = - new NtlmPasswordAuthentication( "?", "GUEST", "" ); - - String domain; - String username; - String password; - byte[] ansiHash; - byte[] unicodeHash; - boolean hashesExternal = false; - -/** - * Create an NtlmPasswordAuthentication object from the userinfo - * component of an SMB URL like "domain;user:pass". This constructor - * is used internally be jCIFS when parsing SMB URLs. - */ - - public NtlmPasswordAuthentication( String userInfo ) { - domain = username = password = null; - - if( userInfo != null ) { - int i, u, end; - char c; - - end = userInfo.length(); - for( i = 0, u = 0; i < end; i++ ) { - c = userInfo.charAt( i ); - if( c == ';' ) { - domain = userInfo.substring( 0, i ); - u = i + 1; - } else if( c == ':' ) { - password = userInfo.substring( i + 1 ); - break; - } - } - username = userInfo.substring( u, i ); - } - - if( domain == null ) this.domain = DEFAULT_DOMAIN; - if( username == null ) this.username = DEFAULT_USERNAME; - if( password == null ) this.password = DEFAULT_PASSWORD; - } -/** - * Create an NtlmPasswordAuthentication object from a - * domain, username, and password. Parameters that are null - * will be substituted with jcifs.smb.client.domain, - * jcifs.smb.client.username, jcifs.smb.client.password - * property values. - */ - public NtlmPasswordAuthentication( String domain, String username, String password ) { - this.domain = domain; - this.username = username; - this.password = password; - if( domain == null ) this.domain = DEFAULT_DOMAIN; - if( username == null ) this.username = DEFAULT_USERNAME; - if( password == null ) this.password = DEFAULT_PASSWORD; - } -/** - * Create an NtlmPasswordAuthentication object with raw password - * hashes. This is used exclusively by the jcifs.http.NtlmSsp - * class which is in turn used by NTLM HTTP authentication functionality. - */ - public NtlmPasswordAuthentication( String domain, String username, - byte[] ansiHash, byte[] unicodeHash ) { - if( domain == null || username == null || - ansiHash == null || unicodeHash == null ) { - throw new IllegalArgumentException( "External credentials cannot null" ); - } - this.domain = domain; - this.username = username; - this.password = null; - this.ansiHash = ansiHash; - this.unicodeHash = unicodeHash; - hashesExternal = true; - } - -/** - * Returns the domain. - */ - public String getDomain() { - return domain; - } -/** - * Returns the username. - */ - public String getUsername() { - return username; - } -/** - * Returns the password in plain text or null if the raw password - * hashes were used to construct this NtlmPasswordAuthentication - * object which will be the case when NTLM HTTP Authentication is - * used. There is no way to retrieve a users password in plain text unless - * it is supplied by the user at runtime. - */ - public String getPassword() { - return password; - } -/** - * Return the domain and username in the format: - * domain\\username. This is equivalent to toString(). - */ - public String getName() { - boolean d = domain.length() > 0 && domain.equals( "?" ) == false; - return d ? domain + "\\" + username : username; - } -/** - * Computes the 24 byte ANSI password hash given the 8 byte server challenge. - */ - public byte[] getAnsiHash( byte[] challenge ) { - if( hashesExternal ) { - return ansiHash; - } - switch (LM_COMPATIBILITY) { - case 0: - case 1: - return getPreNTLMResponse( password, challenge ); - case 2: - return getNTLMResponse( password, challenge ); - case 3: - case 4: - case 5: - byte[] clientChallenge = new byte[8]; - random.nextBytes(clientChallenge); - return getLMv2Response(domain, username, password, challenge, - clientChallenge); - default: - return getPreNTLMResponse( password, challenge ); - } - } -/** - * Computes the 24 byte Unicode password hash given the 8 byte server challenge. - */ - public byte[] getUnicodeHash( byte[] challenge ) { - if( hashesExternal ) { - return unicodeHash; - } - switch (LM_COMPATIBILITY) { - case 0: - case 1: - case 2: - return getNTLMResponse( password, challenge ); - case 3: - case 4: - case 5: - /* - byte[] clientChallenge = new byte[8]; - random.nextBytes(clientChallenge); - return getNTLMv2Response(domain, username, password, null, - challenge, clientChallenge); - */ - return new byte[0]; - default: - return getNTLMResponse( password, challenge ); - } - } -/** - * Compares two NtlmPasswordAuthentication objects for - * equality. Two NtlmPasswordAuthentication objects are equal if - * their caseless domain and username fields are equal and either both hashes are external and they are equal or both internally supplied passwords are equal. If one NtlmPasswordAuthentication object has external hashes (meaning negotiated via NTLM HTTP Authentication) and the other does not they will not be equal. This is technically not correct however the server 8 byte challage would be required to compute and compare the password hashes but that it not available with this method. - */ - public boolean equals( Object obj ) { - if( obj instanceof NtlmPasswordAuthentication ) { - NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj; - if( ntlm.domain.toUpperCase().equals( domain.toUpperCase() ) && - ntlm.username.toUpperCase().equals( username.toUpperCase() )) { - if( hashesExternal && ntlm.hashesExternal ) { - return Arrays.equals( ansiHash, ntlm.ansiHash ) && - Arrays.equals( unicodeHash, ntlm.unicodeHash ); - /* This still isn't quite right. If one npa object does not have external - * hashes and the other does then they will not be considered equal even - * though they may be. - */ - } else if( !hashesExternal && password.equals( ntlm.password )) { - return true; - } - } - } - return false; - } - - -/** - * Return the upcased username hash code. - */ - public int hashCode() { - return getName().toUpperCase().hashCode(); - } -/** - * Return the domain and username in the format: - * domain\\username. This is equivalent to getName(). - */ - public String toString() { - return getName(); - } -} - diff --git a/update/NtlmServlet.java b/update/NtlmServlet.java deleted file mode 100644 index 1304051..0000000 --- a/update/NtlmServlet.java +++ /dev/null @@ -1,158 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Eric Glass" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * 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.http; - -import java.io.IOException; - -import java.net.UnknownHostException; - -import java.util.Enumeration; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.UnavailableException; - -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import jcifs.Config; -import jcifs.UniAddress; - -import jcifs.smb.NtlmPasswordAuthentication; -import jcifs.smb.SmbAuthException; -import jcifs.smb.SmbSession; - -import jcifs.util.Base64; - -/** - * This servlet may be used with pre-2.3 servlet containers - * to protect content with NTLM HTTP Authentication. Servlets that - * extend this abstract base class may be authenticatied against an SMB - * server or domain controller depending on how the - * jcifs.smb.client.domain or jcifs.http.domainController - * properties are be specified. With later containers the - * NtlmHttpFilter should be used/b>. For custom NTLM HTTP Authentication schemes the NtlmSsp may be used. - *

- * Read jCIFS NTLM HTTP Authentication and the Network Explorer Servlet related information. - */ - -public abstract class NtlmServlet extends HttpServlet { - - private String defaultDomain; - - private String domainController; - - private boolean enableBasic; - - private boolean insecureBasic; - - private String realm; - - public void init(ServletConfig config) throws ServletException { - super.init(config); - - /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min. - */ - Config.setProperty( "jcifs.smb.client.soTimeout", "300000" ); - Config.setProperty( "jcifs.netbios.cachePolicy", "600" ); - - Enumeration e = config.getInitParameterNames(); - String name; - while (e.hasMoreElements()) { - name = (String) e.nextElement(); - if (name.startsWith("jcifs.")) { - Config.setProperty(name, config.getInitParameter(name)); - } - } - defaultDomain = Config.getProperty("jcifs.smb.client.domain"); - domainController = Config.getProperty("jcifs.http.domainController"); - if (domainController == null) domainController = defaultDomain; - enableBasic = Boolean.valueOf( - Config.getProperty("jcifs.http.enableBasic")).booleanValue(); - insecureBasic = Boolean.valueOf( - Config.getProperty("jcifs.http.insecureBasic")).booleanValue(); - realm = Config.getProperty("jcifs.http.basicRealm"); - if (realm == null) realm = "jCIFS"; - } - - protected void service(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - boolean offerBasic = enableBasic && - (insecureBasic || request.isSecure()); - String msg = request.getHeader("Authorization"); - if (msg != null && (msg.startsWith("NTLM ") || - (offerBasic && msg.startsWith("Basic ")))) { - UniAddress dc = UniAddress.getByName(domainController, true); - NtlmPasswordAuthentication ntlm; - if (msg.startsWith("NTLM ")) { - byte[] challenge = SmbSession.getChallenge(dc); - ntlm = NtlmSsp.authenticate(request, response, challenge); - if (ntlm == null) return; - } else { - String auth = new String(Base64.decode(msg.substring(6)), - "US-ASCII"); - int index = auth.indexOf(':'); - String user = (index != -1) ? auth.substring(0, index) : auth; - String password = (index != -1) ? auth.substring(index + 1) : - ""; - index = user.indexOf('\\'); - if (index == -1) index = user.indexOf('/'); - String domain = (index != -1) ? user.substring(0, index) : - defaultDomain; - user = (index != -1) ? user.substring(index + 1) : user; - ntlm = new NtlmPasswordAuthentication(domain, user, password); - } - try { - SmbSession.logon(dc, ntlm); - } catch (SmbAuthException sae) { - response.setHeader("WWW-Authenticate", "NTLM"); - if (offerBasic) { - response.addHeader("WWW-Authenticate", "Basic realm=\"" + - realm + "\""); - } - response.setHeader("Connection", "close"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.flushBuffer(); - return; - } - HttpSession ssn = request.getSession(); - ssn.setAttribute("NtlmHttpAuth", ntlm); - ssn.setAttribute( "ntlmdomain", ntlm.getDomain() ); - ssn.setAttribute( "ntlmuser", ntlm.getUsername() ); - } else { - HttpSession ssn = request.getSession(false); - if (ssn == null || ssn.getAttribute("NtlmHttpAuth") == null) { - response.setHeader("WWW-Authenticate", "NTLM"); - if (offerBasic) { - response.addHeader("WWW-Authenticate", "Basic realm=\"" + - realm + "\""); - } - response.setHeader("Connection", "close"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.flushBuffer(); - return; - } - } - super.service(request, response); - } -} - diff --git a/update/NtlmSsp.java b/update/NtlmSsp.java deleted file mode 100644 index f781f1c..0000000 --- a/update/NtlmSsp.java +++ /dev/null @@ -1,112 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Eric Glass" - * "Jason Pugsley" - * "skeetz" - * - * 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.http; - -import java.io.IOException; - -import javax.servlet.ServletException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import jcifs.smb.NtlmPasswordAuthentication; - -import jcifs.util.Base64; - -import jcifs.ntlmssp.NtlmFlags; -import jcifs.ntlmssp.Type1Message; -import jcifs.ntlmssp.Type2Message; -import jcifs.ntlmssp.Type3Message; - -/** - * This class is used internally by NtlmHttpFilter, - * NtlmServlet, and NetworkExplorer to negiotiate password - * hashes via NTLM SSP with MSIE. It might also be used directly by servlet - * containers to incorporate similar functionality. - *

- * How NTLMSSP is used in conjunction with HTTP and MSIE clients is - * described in an NTLM - * Authentication Scheme for HTTP.

Also, read jCIFS NTLM HTTP Authentication and - * the Network Explorer Servlet related information. - */ - -public class NtlmSsp implements NtlmFlags { - - /** - * Calls the static {@link #authenticate(HttpServletRequest, - * HttpServletResponse, byte[])} method to perform NTLM authentication - * for the specified servlet request. - * - * @param req The request being serviced. - * @param resp The response. - * @param challenge The domain controller challenge. - * @throws IOException If an IO error occurs. - * @throws ServletException If an error occurs. - */ - public NtlmPasswordAuthentication doAuthentication( - HttpServletRequest req, HttpServletResponse resp, byte[] challenge) - throws IOException, ServletException { - return authenticate(req, resp, challenge); - } - - /** - * Performs NTLM authentication for the servlet request. - * - * @param req The request being serviced. - * @param resp The response. - * @param challenge The domain controller challenge. - * @throws IOException If an IO error occurs. - * @throws ServletException If an error occurs. - */ - public static NtlmPasswordAuthentication authenticate( - HttpServletRequest req, HttpServletResponse resp, byte[] challenge) - throws IOException, ServletException { - String msg = req.getHeader("Authorization"); - if (msg != null && msg.startsWith("NTLM ")) { - byte[] src = Base64.decode(msg.substring(5)); - if (src[8] == 1) { - Type1Message type1 = new Type1Message(src); - Type2Message type2 = new Type2Message(type1, challenge, null); - msg = Base64.encode(type2.toByteArray()); - resp.setHeader( "WWW-Authenticate", "NTLM " + msg ); - } else if (src[8] == 3) { - Type3Message type3 = new Type3Message(src); - byte[] lmResponse = type3.getLMResponse(); - if (lmResponse == null) lmResponse = new byte[0]; - byte[] ntResponse = type3.getNTResponse(); - if (ntResponse == null) ntResponse = new byte[0]; - return new NtlmPasswordAuthentication(type3.getDomain(), - type3.getUser(), lmResponse, ntResponse); - } - } else { - resp.setHeader("WWW-Authenticate", "NTLM"); - resp.setHeader("Connection", "close"); - } - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - resp.setContentLength( 0 ); - resp.flushBuffer(); - return null; - } - -} - diff --git a/update/Type1Message.java b/update/Type1Message.java deleted file mode 100644 index ce31ec2..0000000 --- a/update/Type1Message.java +++ /dev/null @@ -1,248 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Eric Glass" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * 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.ntlmssp; - -import java.io.IOException; - -import java.net.UnknownHostException; - -import jcifs.netbios.NbtAddress; - -import jcifs.Config; - -/** - * Represents an NTLMSSP Type-1 message. - */ -public class Type1Message extends NtlmMessage { - - private static final int DEFAULT_FLAGS; - - private static final String DEFAULT_DOMAIN; - - private static final String DEFAULT_WORKSTATION; - - private String suppliedDomain; - - private String suppliedWorkstation; - - static { - DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM | - (Config.getBoolean("jcifs.smb.client.useUnicode", true) ? - NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM); - DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null); - String defaultWorkstation = null; - try { - defaultWorkstation = NbtAddress.getLocalHost().getHostName(); - } catch (UnknownHostException ex) { } - DEFAULT_WORKSTATION = defaultWorkstation; - } - - /** - * Creates a Type-1 message using default values from the current - * environment. - */ - public Type1Message() { - this(getDefaultFlags(), getDefaultDomain(), getDefaultWorkstation()); - } - - /** - * Creates a Type-1 message with the specified parameters. - * - * @param flags The flags to apply to this message. - * @param suppliedDomain The supplied authentication domain. - * @param suppliedWorkstation The supplied workstation name. - */ - public Type1Message(int flags, String suppliedDomain, - String suppliedWorkstation) { - setFlags(flags); - setSuppliedDomain(suppliedDomain); - setSuppliedWorkstation(suppliedWorkstation); - } - - /** - * Creates a Type-1 message using the given raw Type-1 material. - * - * @param material The raw Type-1 material used to construct this message. - * @throws IOException If an error occurs while parsing the material. - */ - public Type1Message(byte[] material) throws IOException { - parse(material); - } - - /** - * Returns the supplied authentication domain. - * - * @return A String containing the supplied domain. - */ - public String getSuppliedDomain() { - return suppliedDomain; - } - - /** - * Sets the supplied authentication domain for this message. - * - * @param suppliedDomain The supplied domain for this message. - */ - public void setSuppliedDomain(String suppliedDomain) { - this.suppliedDomain = suppliedDomain; - } - - /** - * Returns the supplied workstation name. - * - * @return A String containing the supplied workstation name. - */ - public String getSuppliedWorkstation() { - return suppliedWorkstation; - } - - /** - * Sets the supplied workstation name for this message. - * - * @param suppliedWorkstation The supplied workstation for this message. - */ - public void setSuppliedWorkstation(String suppliedWorkstation) { - this.suppliedWorkstation = suppliedWorkstation; - } - - public byte[] toByteArray() { - try { - String suppliedDomain = getSuppliedDomain(); - String suppliedWorkstation = getSuppliedWorkstation(); - int flags = getFlags(); - boolean hostInfo = false; - byte[] domain = new byte[0]; - if (suppliedDomain != null && suppliedDomain.length() != 0) { - hostInfo = true; - flags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED; - domain = suppliedDomain.toUpperCase().getBytes( - getOEMEncoding()); - } else { - flags &= (NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED ^ 0xffffffff); - } - byte[] workstation = new byte[0]; - if (suppliedWorkstation != null && - suppliedWorkstation.length() != 0) { - hostInfo = true; - flags |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED; - workstation = - suppliedWorkstation.toUpperCase().getBytes( - getOEMEncoding()); - } else { - flags &= (NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED ^ - 0xffffffff); - } - byte[] type1 = new byte[hostInfo ? - (32 + domain.length + workstation.length) : 16]; - System.arraycopy(NTLMSSP_SIGNATURE, 0, type1, 0, 8); - writeULong(type1, 8, 1); - writeULong(type1, 12, flags); - if (hostInfo) { - writeSecurityBuffer(type1, 16, 32, domain); - writeSecurityBuffer(type1, 24, 32 + domain.length, workstation); - } - return type1; - } catch (IOException ex) { - throw new IllegalStateException(ex.getMessage()); - } - } - - public String toString() { - String suppliedDomain = getSuppliedDomain(); - String suppliedWorkstation = getSuppliedWorkstation(); - int flags = getFlags(); - StringBuffer buffer = new StringBuffer(); - if (suppliedDomain != null) { - buffer.append("suppliedDomain: ").append(suppliedDomain); - } - if (suppliedWorkstation != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("suppliedWorkstation: ").append(suppliedWorkstation); - } - if (flags != 0) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("flags: "); - buffer.append("0x"); - buffer.append(Integer.toHexString((flags >> 28) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 24) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 20) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 16) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 12) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 8) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 4) & 0x0f)); - buffer.append(Integer.toHexString(flags & 0x0f)); - } - return buffer.toString(); - } - - /** - * Returns the default flags for a generic Type-1 message in the - * current environment. - * - * @return An int containing the default flags. - */ - public static int getDefaultFlags() { - return DEFAULT_FLAGS; - } - - /** - * Returns the default domain from the current environment. - * - * @return A String containing the default domain. - */ - public static String getDefaultDomain() { - return DEFAULT_DOMAIN; - } - - /** - * Returns the default workstation from the current environment. - * - * @return A String containing the default workstation. - */ - public static String getDefaultWorkstation() { - return DEFAULT_WORKSTATION; - } - - private void parse(byte[] material) throws IOException { - for (int i = 0; i < 8; i++) { - if (material[i] != NTLMSSP_SIGNATURE[i]) { - throw new IOException("Not an NTLMSSP message."); - } - } - if (readULong(material, 8) != 1) { - throw new IOException("Not a Type 1 message."); - } - int flags = readULong(material, 12); - String suppliedDomain = null; - if ((flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) != 0) { - byte[] domain = readSecurityBuffer(material, 16); - suppliedDomain = new String(domain, getOEMEncoding()); - } - String suppliedWorkstation = null; - if ((flags & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) != 0) { - byte[] workstation = readSecurityBuffer(material, 24); - suppliedWorkstation = new String(workstation, getOEMEncoding()); - } - setFlags(flags); - setSuppliedDomain(suppliedDomain); - setSuppliedWorkstation(suppliedWorkstation); - } - -} diff --git a/update/Type2Message.java b/update/Type2Message.java deleted file mode 100644 index a27db1f..0000000 --- a/update/Type2Message.java +++ /dev/null @@ -1,414 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Eric Glass" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * 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.ntlmssp; - -import java.io.IOException; - -import java.net.UnknownHostException; - -import jcifs.Config; - -import jcifs.netbios.NbtAddress; - -/** - * Represents an NTLMSSP Type-2 message. - */ -public class Type2Message extends NtlmMessage { - - private static final int DEFAULT_FLAGS; - - private static final String DEFAULT_DOMAIN; - - private static final byte[] DEFAULT_TARGET_INFORMATION; - - private byte[] challenge; - - private String target; - - private byte[] context; - - private byte[] targetInformation; - - static { - DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM | - (Config.getBoolean("jcifs.smb.client.useUnicode", true) ? - NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM); - DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null); - byte[] domain = new byte[0]; - if (DEFAULT_DOMAIN != null) { - try { - domain = DEFAULT_DOMAIN.getBytes("UnicodeLittleUnmarked"); - } catch (IOException ex) { } - } - int domainLength = domain.length; - byte[] server = new byte[0]; - try { - String host = NbtAddress.getLocalHost().getHostName(); - if (host != null) { - try { - server = host.getBytes("UnicodeLittleUnmarked"); - } catch (IOException ex) { } - } - } catch (UnknownHostException ex) { } - int serverLength = server.length; - byte[] targetInfo = new byte[(domainLength > 0 ? domainLength + 4 : 0) + - (serverLength > 0 ? serverLength + 4 : 0) + 4]; - int offset = 0; - if (domainLength > 0) { - writeUShort(targetInfo, offset, 2); - offset += 2; - writeUShort(targetInfo, offset, domainLength); - offset += 2; - System.arraycopy(domain, 0, targetInfo, offset, domainLength); - offset += domainLength; - } - if (serverLength > 0) { - writeUShort(targetInfo, offset, 1); - offset += 2; - writeUShort(targetInfo, offset, serverLength); - offset += 2; - System.arraycopy(server, 0, targetInfo, offset, serverLength); - } - DEFAULT_TARGET_INFORMATION = targetInfo; - } - - /** - * Creates a Type-2 message using default values from the current - * environment. - */ - public Type2Message() { - this(getDefaultFlags(), null, null); - } - - /** - * Creates a Type-2 message in response to the given Type-1 message - * using default values from the current environment. - * - * @param type1 The Type-1 message which this represents a response to. - */ - public Type2Message(Type1Message type1) { - this(type1, null, null); - } - - /** - * Creates a Type-2 message in response to the given Type-1 message. - * - * @param type1 The Type-1 message which this represents a response to. - * @param challenge The challenge from the domain controller/server. - * @param target The authentication target. - */ - public Type2Message(Type1Message type1, byte[] challenge, String target) { - this(getDefaultFlags(type1), challenge, (type1 != null && - target == null && type1.getFlag(NTLMSSP_REQUEST_TARGET)) ? - getDefaultDomain() : target); - } - - /** - * Creates a Type-2 message with the specified parameters. - * - * @param flags The flags to apply to this message. - * @param challenge The challenge from the domain controller/server. - * @param target The authentication target. - */ - public Type2Message(int flags, byte[] challenge, String target) { - setFlags(flags); - setChallenge(challenge); - setTarget(target); - if (target != null) setTargetInformation(getDefaultTargetInformation()); - } - - /** - * Creates a Type-2 message using the given raw Type-2 material. - * - * @param material The raw Type-2 material used to construct this message. - * @throws IOException If an error occurs while parsing the material. - */ - public Type2Message(byte[] material) throws IOException { - parse(material); - } - - /** - * Returns the challenge for this message. - * - * @return A byte[] containing the challenge. - */ - public byte[] getChallenge() { - return challenge; - } - - /** - * Sets the challenge for this message. - * - * @param challenge The challenge from the domain controller/server. - */ - public void setChallenge(byte[] challenge) { - this.challenge = challenge; - } - - /** - * Returns the authentication target. - * - * @return A String containing the authentication target. - */ - public String getTarget() { - return target; - } - - /** - * Sets the authentication target. - * - * @param target The authentication target. - */ - public void setTarget(String target) { - this.target = target; - } - - /** - * Returns the target information block. - * - * @return A byte[] containing the target information block. - * The target information block is used by the client to create an - * NTLMv2 response. - */ - public byte[] getTargetInformation() { - return targetInformation; - } - - /** - * Sets the target information block. - * The target information block is used by the client to create - * an NTLMv2 response. - * - * @param targetInformation The target information block. - */ - public void setTargetInformation(byte[] targetInformation) { - this.targetInformation = targetInformation; - } - - /** - * Returns the local security context. - * - * @return A byte[] containing the local security - * context. This is used by the client to negotiate local - * authentication. - */ - public byte[] getContext() { - return context; - } - - /** - * Sets the local security context. This is used by the client - * to negotiate local authentication. - * - * @param context The local security context. - */ - public void setContext(byte[] context) { - this.context = context; - } - - public byte[] toByteArray() { - try { - String targetName = getTarget(); - byte[] challenge = getChallenge(); - byte[] context = getContext(); - byte[] targetInformation = getTargetInformation(); - int flags = getFlags(); - byte[] target = new byte[0]; - if ((flags & (NTLMSSP_TARGET_TYPE_DOMAIN | - NTLMSSP_TARGET_TYPE_SERVER | - NTLMSSP_TARGET_TYPE_SHARE)) != 0) { - if (targetName != null && targetName.length() != 0) { - target = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0 ? - targetName.getBytes("UnicodeLittleUnmarked") : - targetName.toUpperCase().getBytes(getOEMEncoding()); - } else { - flags &= (0xffffffff ^ (NTLMSSP_TARGET_TYPE_DOMAIN | - NTLMSSP_TARGET_TYPE_SERVER | - NTLMSSP_TARGET_TYPE_SHARE)); - } - } - if (targetInformation != null) { - flags ^= NTLMSSP_NEGOTIATE_TARGET_INFO; - // empty context is needed for padding when t.i. is supplied. - if (context == null) context = new byte[8]; - } - int data = 32; - if (context != null) data += 8; - if (targetInformation != null) data += 8; - byte[] type2 = new byte[data + target.length + - (targetInformation != null ? targetInformation.length : 0)]; - System.arraycopy(NTLMSSP_SIGNATURE, 0, type2, 0, 8); - writeULong(type2, 8, 2); - writeSecurityBuffer(type2, 12, data, target); - writeULong(type2, 20, flags); - System.arraycopy(challenge != null ? challenge : new byte[8], 0, - type2, 24, 8); - if (context != null) System.arraycopy(context, 0, type2, 32, 8); - if (targetInformation != null) { - writeSecurityBuffer(type2, 40, data + target.length, - targetInformation); - } - return type2; - } catch (IOException ex) { - throw new IllegalStateException(ex.getMessage()); - } - } - - public String toString() { - String target = getTarget(); - byte[] challenge = getChallenge(); - byte[] context = getContext(); - byte[] targetInformation = getTargetInformation(); - int flags = getFlags(); - StringBuffer buffer = new StringBuffer(); - if (target != null) { - buffer.append("target: ").append(target); - } - if (challenge != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("challenge: "); - buffer.append("0x"); - for (int i = 0; i < challenge.length; i++) { - buffer.append(Integer.toHexString((challenge[i] >> 4) & 0x0f)); - buffer.append(Integer.toHexString(challenge[i] & 0x0f)); - } - } - if (context != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("context: "); - buffer.append("0x"); - for (int i = 0; i < context.length; i++) { - buffer.append(Integer.toHexString((context[i] >> 4) & 0x0f)); - buffer.append(Integer.toHexString(context[i] & 0x0f)); - } - } - if (targetInformation != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("targetInformation: "); - buffer.append("0x"); - for (int i = 0; i < targetInformation.length; i++) { - buffer.append(Integer.toHexString((targetInformation[i] >> 4) & - 0x0f)); - buffer.append(Integer.toHexString(targetInformation[i] & 0x0f)); - } - } - if (flags != 0) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("flags: "); - buffer.append("0x"); - buffer.append(Integer.toHexString((flags >> 28) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 24) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 20) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 16) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 12) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 8) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 4) & 0x0f)); - buffer.append(Integer.toHexString(flags & 0x0f)); - } - return buffer.toString(); - } - - /** - * Returns the default flags for a generic Type-2 message in the - * current environment. - * - * @return An int containing the default flags. - */ - public static int getDefaultFlags() { - return DEFAULT_FLAGS; - } - - /** - * Returns the default flags for a Type-2 message created in response - * to the given Type-1 message in the current environment. - * - * @return An int containing the default flags. - */ - public static int getDefaultFlags(Type1Message type1) { - if (type1 == null) return DEFAULT_FLAGS; - int flags = NTLMSSP_NEGOTIATE_NTLM; - int type1Flags = type1.getFlags(); - flags |= ((type1Flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? - NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM; - if ((type1Flags & NTLMSSP_REQUEST_TARGET) != 0) { - String domain = getDefaultDomain(); - if (domain != null) { - flags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_TARGET_TYPE_DOMAIN; - } - } - return flags; - } - - /** - * Returns the default domain from the current environment. - * - * @return A String containing the domain. - */ - public static String getDefaultDomain() { - return DEFAULT_DOMAIN; - } - - public static byte[] getDefaultTargetInformation() { - return DEFAULT_TARGET_INFORMATION; - } - - private void parse(byte[] material) throws IOException { - for (int i = 0; i < 8; i++) { - if (material[i] != NTLMSSP_SIGNATURE[i]) { - throw new IOException("Not an NTLMSSP message."); - } - } - if (readULong(material, 8) != 2) { - throw new IOException("Not a Type 2 message."); - } - int flags = readULong(material, 20); - setFlags(flags); - String target = null; - byte[] bytes = readSecurityBuffer(material, 12); - if (bytes.length != 0) { - target = new String(bytes, - ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? - "UnicodeLittleUnmarked" : getOEMEncoding()); - } - setTarget(target); - for (int i = 24; i < 32; i++) { - if (material[i] != 0) { - byte[] challenge = new byte[8]; - System.arraycopy(material, 24, challenge, 0, 8); - setChallenge(challenge); - break; - } - } - int offset = readULong(material, 16); // offset of targetname start - if (offset == 32 || material.length == 32) return; - for (int i = 32; i < 40; i++) { - if (material[i] != 0) { - byte[] context = new byte[8]; - System.arraycopy(material, 32, context, 0, 8); - setContext(context); - break; - } - } - if (offset == 40 || material.length == 40) return; - bytes = readSecurityBuffer(material, 40); - if (bytes.length != 0) setTargetInformation(bytes); - } - -} diff --git a/update/Type3Message.java b/update/Type3Message.java deleted file mode 100644 index 123b703..0000000 --- a/update/Type3Message.java +++ /dev/null @@ -1,580 +0,0 @@ -/* jcifs smb client library in Java - * Copyright (C) 2002 "Michael B. Allen" - * "Eric Glass" - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * 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.ntlmssp; - -import java.io.IOException; - -import java.net.UnknownHostException; - -import java.security.SecureRandom; - -import jcifs.Config; - -import jcifs.netbios.NbtAddress; - -import jcifs.smb.NtlmPasswordAuthentication; - -/** - * Represents an NTLMSSP Type-3 message. - */ -public class Type3Message extends NtlmMessage { - - private static final int DEFAULT_FLAGS; - - private static final String DEFAULT_DOMAIN; - - private static final String DEFAULT_USER; - - private static final String DEFAULT_PASSWORD; - - private static final String DEFAULT_WORKSTATION; - - private static final int LM_COMPATIBILITY; - - private static final SecureRandom RANDOM = new SecureRandom(); - - private byte[] lmResponse; - - private byte[] ntResponse; - - private String domain; - - private String user; - - private String workstation; - - private byte[] sessionKey; - - static { - DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM | - (Config.getBoolean("jcifs.smb.client.useUnicode", true) ? - NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM); - DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null); - DEFAULT_USER = Config.getProperty("jcifs.smb.client.username", null); - DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password", - null); - String defaultWorkstation = null; - try { - defaultWorkstation = NbtAddress.getLocalHost().getHostName(); - } catch (UnknownHostException ex) { } - DEFAULT_WORKSTATION = defaultWorkstation; - LM_COMPATIBILITY = Config.getInt("jcifs.smb.lmCompatibility", 0); - } - - /** - * Creates a Type-3 message using default values from the current - * environment. - */ - public Type3Message() { - setFlags(getDefaultFlags()); - setDomain(getDefaultDomain()); - setUser(getDefaultUser()); - setWorkstation(getDefaultWorkstation()); - } - - /** - * Creates a Type-3 message in response to the given Type-2 message - * using default values from the current environment. - * - * @param type2 The Type-2 message which this represents a response to. - */ - public Type3Message(Type2Message type2) { - setFlags(getDefaultFlags(type2)); - setWorkstation(getDefaultWorkstation()); - String domain = getDefaultDomain(); - setDomain(domain); - String user = getDefaultUser(); - setUser(user); - String password = getDefaultPassword(); - switch (LM_COMPATIBILITY) { - case 0: - case 1: - setLMResponse(getLMResponse(type2, password)); - setNTResponse(getNTResponse(type2, password)); - break; - case 2: - byte[] nt = getNTResponse(type2, password); - setLMResponse(nt); - setNTResponse(nt); - break; - case 3: - case 4: - case 5: - byte[] clientChallenge = new byte[8]; - RANDOM.nextBytes(clientChallenge); - setLMResponse(getLMv2Response(type2, domain, user, password, - clientChallenge)); - /* - setNTResponse(getNTLMv2Response(type2, domain, user, password, - clientChallenge)); - */ - break; - default: - setLMResponse(getLMResponse(type2, password)); - setNTResponse(getNTResponse(type2, password)); - } - } - - /** - * Creates a Type-3 message in response to the given Type-2 message. - * - * @param type2 The Type-2 message which this represents a response to. - * @param password The password to use when constructing the response. - * @param domain The domain in which the user has an account. - * @param user The username for the authenticating user. - * @param workstation The workstation from which authentication is - * taking place. - */ - public Type3Message(Type2Message type2, String password, String domain, - String user, String workstation) { - setFlags(getDefaultFlags(type2)); - setDomain(domain); - setUser(user); - setWorkstation(workstation); - switch (LM_COMPATIBILITY) { - case 0: - case 1: - setLMResponse(getLMResponse(type2, password)); - setNTResponse(getNTResponse(type2, password)); - break; - case 2: - byte[] nt = getNTResponse(type2, password); - setLMResponse(nt); - setNTResponse(nt); - break; - case 3: - case 4: - case 5: - byte[] clientChallenge = new byte[8]; - RANDOM.nextBytes(clientChallenge); - setLMResponse(getLMv2Response(type2, domain, user, password, - clientChallenge)); - /* - setNTResponse(getNTLMv2Response(type2, domain, user, password, - clientChallenge)); - */ - break; - default: - setLMResponse(getLMResponse(type2, password)); - setNTResponse(getNTResponse(type2, password)); - } - } - - /** - * Creates a Type-3 message with the specified parameters. - * - * @param flags The flags to apply to this message. - * @param lmResponse The LanManager/LMv2 response. - * @param domain The NT/NTLMv2 response. - * @param domain The domain in which the user has an account. - * @param user The username for the authenticating user. - * @param workstation The workstation from which authentication is - * taking place. - */ - public Type3Message(int flags, byte[] lmResponse, byte[] ntResponse, - String domain, String user, String workstation) { - setFlags(flags); - setLMResponse(lmResponse); - setNTResponse(ntResponse); - setDomain(domain); - setUser(user); - setWorkstation(workstation); - } - - /** - * Creates a Type-3 message using the given raw Type-3 material. - * - * @param material The raw Type-3 material used to construct this message. - * @throws IOException If an error occurs while parsing the material. - */ - public Type3Message(byte[] material) throws IOException { - parse(material); - } - - /** - * Returns the LanManager/LMv2 response. - * - * @return A byte[] containing the LanManager response. - */ - public byte[] getLMResponse() { - return lmResponse; - } - - /** - * Sets the LanManager/LMv2 response for this message. - * - * @param lmResponse The LanManager response. - */ - public void setLMResponse(byte[] lmResponse) { - this.lmResponse = lmResponse; - } - - /** - * Returns the NT/NTLMv2 response. - * - * @return A byte[] containing the NT/NTLMv2 response. - */ - public byte[] getNTResponse() { - return ntResponse; - } - - /** - * Sets the NT/NTLMv2 response for this message. - * - * @param lmResponse The NT/NTLMv2 response. - */ - public void setNTResponse(byte[] ntResponse) { - this.ntResponse = ntResponse; - } - - /** - * Returns the domain in which the user has an account. - * - * @return A String containing the domain for the user. - */ - public String getDomain() { - return domain; - } - - /** - * Sets the domain for this message. - * - * @param domain The domain. - */ - public void setDomain(String domain) { - this.domain = domain; - } - - /** - * Returns the username for the authenticating user. - * - * @return A String containing the user for this message. - */ - public String getUser() { - return user; - } - - /** - * Sets the user for this message. - * - * @param user The user. - */ - public void setUser(String user) { - this.user = user; - } - - /** - * Returns the workstation from which authentication is being performed. - * - * @return A String containing the workstation. - */ - public String getWorkstation() { - return workstation; - } - - /** - * Sets the workstation for this message. - * - * @param workstation The workstation. - */ - public void setWorkstation(String workstation) { - this.workstation = workstation; - } - - /** - * Returns the session key. - * - * @return A byte[] containing the session key. - */ - public byte[] getSessionKey() { - return sessionKey; - } - - /** - * Sets the session key. - * - * @param sessionKey The session key. - */ - public void setSessionKey(byte[] sessionKey) { - this.sessionKey = sessionKey; - } - - public byte[] toByteArray() { - try { - int flags = getFlags(); - boolean unicode = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0; - String oem = unicode ? null : getOEMEncoding(); - String domainName = getDomain(); - byte[] domain = null; - if (domainName != null && domainName.length() != 0) { - domain = unicode ? - domainName.getBytes("UnicodeLittleUnmarked") : - domainName.toUpperCase().getBytes(oem); - } - int domainLength = (domain != null) ? domain.length : 0; - String userName = getUser(); - byte[] user = null; - if (userName != null && userName.length() != 0) { - user = unicode ? userName.getBytes("UnicodeLittleUnmarked") : - userName.toUpperCase().getBytes(oem); - } - int userLength = (user != null) ? user.length : 0; - String workstationName = getWorkstation(); - byte[] workstation = null; - if (workstationName != null && workstationName.length() != 0) { - workstation = unicode ? - workstationName.getBytes("UnicodeLittleUnmarked") : - workstationName.toUpperCase().getBytes(oem); - } - int workstationLength = (workstation != null) ? - workstation.length : 0; - byte[] lmResponse = getLMResponse(); - int lmLength = (lmResponse != null) ? lmResponse.length : 0; - byte[] ntResponse = getNTResponse(); - int ntLength = (ntResponse != null) ? ntResponse.length : 0; - byte[] sessionKey = getSessionKey(); - int keyLength = (sessionKey != null) ? sessionKey.length : 0; - byte[] type3 = new byte[64 + domainLength + userLength + - workstationLength + lmLength + ntLength + keyLength]; - System.arraycopy(NTLMSSP_SIGNATURE, 0, type3, 0, 8); - writeULong(type3, 8, 3); - int offset = 64; - writeSecurityBuffer(type3, 12, offset, lmResponse); - offset += lmLength; - writeSecurityBuffer(type3, 20, offset, ntResponse); - offset += ntLength; - writeSecurityBuffer(type3, 28, offset, domain); - offset += domainLength; - writeSecurityBuffer(type3, 36, offset, user); - offset += userLength; - writeSecurityBuffer(type3, 44, offset, workstation); - offset += workstationLength; - writeSecurityBuffer(type3, 52, offset, sessionKey); - writeULong(type3, 60, flags); - return type3; - } catch (IOException ex) { - throw new IllegalStateException(ex.getMessage()); - } - } - - public String toString() { - String user = getUser(); - String domain = getDomain(); - String workstation = getWorkstation(); - byte[] lmResponse = getLMResponse(); - byte[] ntResponse = getNTResponse(); - byte[] sessionKey = getSessionKey(); - int flags = getFlags(); - StringBuffer buffer = new StringBuffer(); - if (domain != null) { - buffer.append("domain: ").append(domain); - } - if (user != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("user: ").append(user); - } - if (workstation != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("workstation: ").append(workstation); - } - if (lmResponse != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("lmResponse: "); - buffer.append("0x"); - for (int i = 0; i < lmResponse.length; i++) { - buffer.append(Integer.toHexString((lmResponse[i] >> 4) & 0x0f)); - buffer.append(Integer.toHexString(lmResponse[i] & 0x0f)); - } - } - if (ntResponse != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("ntResponse: "); - buffer.append("0x"); - for (int i = 0; i < ntResponse.length; i++) { - buffer.append(Integer.toHexString((ntResponse[i] >> 4) & 0x0f)); - buffer.append(Integer.toHexString(ntResponse[i] & 0x0f)); - } - } - if (sessionKey != null) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("sessionKey: "); - buffer.append("0x"); - for (int i = 0; i < sessionKey.length; i++) { - buffer.append(Integer.toHexString((sessionKey[i] >> 4) & 0x0f)); - buffer.append(Integer.toHexString(sessionKey[i] & 0x0f)); - } - } - if (flags != 0) { - if (buffer.length() > 0) buffer.append("; "); - buffer.append("flags: "); - buffer.append("0x"); - buffer.append(Integer.toHexString((flags >> 28) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 24) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 20) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 16) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 12) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 8) & 0x0f)); - buffer.append(Integer.toHexString((flags >> 4) & 0x0f)); - buffer.append(Integer.toHexString(flags & 0x0f)); - } - return buffer.toString(); - } - - /** - * Returns the default flags for a generic Type-3 message in the - * current environment. - * - * @return An int containing the default flags. - */ - public static int getDefaultFlags() { - return DEFAULT_FLAGS; - } - - /** - * Returns the default flags for a Type-3 message created in response - * to the given Type-2 message in the current environment. - * - * @return An int containing the default flags. - */ - public static int getDefaultFlags(Type2Message type2) { - if (type2 == null) return DEFAULT_FLAGS; - int flags = NTLMSSP_NEGOTIATE_NTLM; - flags |= ((type2.getFlags() & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? - NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM; - return flags; - } - - /** - * Constructs the LanManager response to the given Type-2 message using - * the supplied password. - * - * @param type2 The Type-2 message. - * @param password The password. - * @return A byte[] containing the LanManager response. - */ - public static byte[] getLMResponse(Type2Message type2, String password) { - if (type2 == null || password == null) return null; - return NtlmPasswordAuthentication.getPreNTLMResponse(password, - type2.getChallenge()); - } - - public static byte[] getLMv2Response(Type2Message type2, - String domain, String user, String password, - byte[] clientChallenge) { - if (type2 == null || domain == null || user == null || - password == null || clientChallenge == null) { - return null; - } - return NtlmPasswordAuthentication.getLMv2Response(domain, user, - password, type2.getChallenge(), clientChallenge); - } - - /** - * Constructs the NT response to the given Type-2 message using - * the supplied password. - * - * @param type2 The Type-2 message. - * @param password The password. - * @return A byte[] containing the NT response. - */ - public static byte[] getNTResponse(Type2Message type2, String password) { - if (type2 == null || password == null) return null; - return NtlmPasswordAuthentication.getNTLMResponse(password, - type2.getChallenge()); - } - - /** - * Returns the default domain from the current environment. - * - * @return The default domain. - */ - public static String getDefaultDomain() { - return DEFAULT_DOMAIN; - } - - /** - * Returns the default user from the current environment. - * - * @return The default user. - */ - public static String getDefaultUser() { - return DEFAULT_USER; - } - - /** - * Returns the default password from the current environment. - * - * @return The default password. - */ - public static String getDefaultPassword() { - return DEFAULT_PASSWORD; - } - - /** - * Returns the default workstation from the current environment. - * - * @return The default workstation. - */ - public static String getDefaultWorkstation() { - return DEFAULT_WORKSTATION; - } - - private void parse(byte[] material) throws IOException { - for (int i = 0; i < 8; i++) { - if (material[i] != NTLMSSP_SIGNATURE[i]) { - throw new IOException("Not an NTLMSSP message."); - } - } - if (readULong(material, 8) != 3) { - throw new IOException("Not a Type 3 message."); - } - byte[] lmResponse = readSecurityBuffer(material, 12); - int lmResponseOffset = readULong(material, 16); - byte[] ntResponse = readSecurityBuffer(material, 20); - int ntResponseOffset = readULong(material, 24); - byte[] domain = readSecurityBuffer(material, 28); - int domainOffset = readULong(material, 32); - byte[] user = readSecurityBuffer(material, 36); - int userOffset = readULong(material, 40); - byte[] workstation = readSecurityBuffer(material, 44); - int workstationOffset = readULong(material, 48); - int flags; - String charset; - if (lmResponseOffset == 52 || ntResponseOffset == 52 || - domainOffset == 52 || userOffset == 52 || - workstationOffset == 52) { - flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_OEM; - charset = getOEMEncoding(); - } else { - setSessionKey(readSecurityBuffer(material, 52)); - flags = readULong(material, 60); - charset = ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? - "UnicodeLittleUnmarked" : getOEMEncoding(); - } - setFlags(flags); - setLMResponse(lmResponse); - // NTLMv2 issues w/cross-domain authentication; leave NT empty if >= 3 - if (LM_COMPATIBILITY < 3) setNTResponse(ntResponse); - setDomain(new String(domain, charset)); - setUser(new String(user, charset)); - setWorkstation(new String(workstation, charset)); - } - -}