jcifs-0.7.11 from tgz
authorFelix Schumacher <p0354740@isib001.(none)>
Wed, 6 Aug 2008 14:06:32 +0000 (16:06 +0200)
committerFelix Schumacher <p0354740@isib001.(none)>
Wed, 6 Aug 2008 14:06:32 +0000 (16:06 +0200)
Thu Jul 10 22:07:09 EDT 2003

Support  for  LMv2  authentication has been added. See the Overview page in
the  API  documentation  regarding  the jcifs.smb.lmCompatibility property.
Some  additonal  "cleanup"  has  also  been performed. One side-effect that
might be noticed is that the default domain/username/password can no longer
be  set  at runtime. Once the client is initialized the default credentials
are  fixed.  Setting  default  credentials are runtime was always an unsafe
operation. Create an NtlmPasswordAuthentication object instead.

Thu Jul  3 20:59:25 EDT 2003

There  have  been  two small bug fixes in the SMB layer; the count field in
the  SMB_COM_WRITE_ANDX  response  was  being ignored. Apparently the count
specified  in the request is always honored or we would have seen plenty of
file corruption by now. Second, the list() and listFiles() did not properly
handle  Transaction  buffering  with  certain  values  for the listSize and
listCount  properties  (if  multipart transaction responses are send). Also
removed  a  few  obnoxious warnings getting logged during file transfers if
Log.WARNINGS is set.

Regarding  NTLM  HTTP Authentication; there have been additions and updates
to  the  documentation  on the NTLM flags, a fix to NtlmSsp to only provide
the  target when requested by the client via the NTLM "request target" flag
(this   is   the   correct  behavior),  and  a  bugfix  to  NtlmSsp  to  do
Base64.encodeBytes(bytes,  false)  instead  of Base64.encodeBytes(bytes) (a
linewrap  in  the  header  can  cause  an  error).  Eric has also excellent
documentation detailing his analysis of the NTLM protocol:

  http://davenport.sourceforge.net/ntlm.html

Thu Jun 12 00:18:05 EDT 2003

It  was  discovered  that if a server responds to the NBT session setup but
not  to  the  SMB_COM_NEGOTIATE  request the client will hang indefinately.
This  is not a likely thing to occur because servers usually respond or not
at  all.  Regardless  it  has  been  fixed.  The  client will timeout after
jcifs.netbios.soTimeout in this situation.

Some tweeking of the NTLM http code has been performed.

Wed May 28 19:09:17 EDT 2003

jcifs-0.7.8 released

A bug in the new ntlm http client code was identified and fixed.

Tue May 27 21:40:58 EDT 2003

jcifs-0.7.7 released

A  deadlock  was  identified  in SmbTransport very similar to the one found
last  year.  A  significant  change  has  been made that greatly simplifies
synchronization in the transport layer. It eliminates this issue as well as
the  extra synchronization introduced to fix the previous problem. It is an
elemental change however so proceed with caution.

Two  new  packages  have  been  introduced.  The  jcifs.ntlmssp package now
contains  all NTLMSSP base code. This code is used by the NtlmHttpFilter as
well  has  the  new  NtlmHttpURLConnection class for transparently enabling
your  HTTP and HTTPS client to negotiate NTLM authentication. The other new
package  is  jcifs.https  which  just  contains  the HTTPS protocol handler
necesary for proper protocol handler registration. Please read the document
entitled  "Using  jCIFS  NTLM  Authentication  for  HTTP  Connections"  for
important details.

To permit SmbFileOutputStream to be used with unusual named pipe modes (see
5/1/03  message)  that  will  be  both  read  and written the open flags in
SmbFileOutputStream have been changed from O_WRONLY to O_RDWR.

The    attrExpiration    period    of    SmbFile    is    now    reset   in
SmbFileOutputStream.write()  to  prevent  jCIFS  from  reporting  incorrect
attributes  values  after  writing  the file. Previously it was possible to
write  to  the file and immediated check the timestamp or file size and get
an old value. This should no longer happen.

The  SmbFileInputStream.skip()  method  has  been implemented in a way that
will  not  result  in  any  IO to the server. Thus it is possible to resume
large downloads from the point of failure for instance.

Wed Apr 16 22:46:07 EDT 2003

jcifs-0.7.6 released

The  isDirectory method has been changed to return false if the target does
not  exist.  Previously  this  would return true which is inconsistent with
java.io.File.

Wed Apr  2 23:56:26 EST 2003

jcifs-0.7.5 released

More  refinement  to  the  NTLM  HTTP authentication code has been applied.
There  is  also  a fix for listing hosts from Windows ME/98/95 local master
browsers.

Wed Mar 26 19:17:24 EST 2003

jcifs-0.7.4 released

Some NtlmHttpFilter issues were reported by several users of Win98 clients.
Eric  has  provided  a  new  NtlmSsp.java  that  works correctly with these
clients.

Wed Feb 12 01:23:02 EST 2003

From    the    beginning   jCIFS   identified   SmbSessions   uniquely   by
domain\username. This meant that once a session was established it could be
shared  by  another  requestor even if they supplied an incorrect password.
This  was  done  for  reasons  that  strangely  enough  had  to do with SMB
chaining.  However  with  the  introduction  of  the http package it is now
common  to  use  jCIFS  to  authenticate  web  clients from a central jCIFS
instance.  This  introduces  a security flaw because there would be a brief
window  of  opportunity after a user logs in during which an impostor could
log  in  as that user with a simple change to their MSIE security settings.
This  problem has been fixed. Another related issue has also been fixed. If
the   NtlmHttpFilter  or  NetworkExplorer  servlet  is  supplied  with  bad
credentials  the  behavior  was to throw an SmbAuthException up through the
container.   Instead   these   classes   will  respond  with  the  standard
Authentication:   NTLM  response  triggering  the  password  dialog  to  be
displayed until correct credentials are displayed.

48 files changed:
CHANGES.txt
README.txt
build.xml
examples/FileOps.java
examples/LogonTest.java [deleted file]
examples/NtlmHttpClient.java [new file with mode: 0644]
examples/ThreadedSmbCrawler.java
examples/VerifyReads.java
src/jcifs/http/Handler.java [new file with mode: 0644]
src/jcifs/http/NtlmHttpFilter.java
src/jcifs/http/NtlmHttpServletRequest.java
src/jcifs/http/NtlmHttpURLConnection.java [new file with mode: 0644]
src/jcifs/http/NtlmServlet.java
src/jcifs/http/NtlmSsp.java
src/jcifs/https/Handler.java [new file with mode: 0644]
src/jcifs/netbios/NbtAddress.java
src/jcifs/netbios/NbtSocket.java
src/jcifs/ntlmssp/NtlmFlags.java [new file with mode: 0644]
src/jcifs/ntlmssp/NtlmMessage.java [new file with mode: 0644]
src/jcifs/ntlmssp/Type1Message.java [new file with mode: 0644]
src/jcifs/ntlmssp/Type2Message.java [new file with mode: 0644]
src/jcifs/ntlmssp/Type3Message.java [new file with mode: 0644]
src/jcifs/smb/NtlmPasswordAuthentication.java
src/jcifs/smb/SmbComSessionSetupAndX.java
src/jcifs/smb/SmbComSessionSetupAndX.java0 [new file with mode: 0644]
src/jcifs/smb/SmbComTransactionResponse.java
src/jcifs/smb/SmbComTreeConnectAndX.java
src/jcifs/smb/SmbComWriteAndXResponse.java
src/jcifs/smb/SmbFile.java
src/jcifs/smb/SmbFileInputStream.java
src/jcifs/smb/SmbFileOutputStream.java
src/jcifs/smb/SmbSession.java
src/jcifs/smb/SmbTransport.java
src/jcifs/smb/SmbTree.java
src/jcifs/util/Base64.java
src/jcifs/util/HMACT64.java [new file with mode: 0644]
src/jcifs/util/URLDecoder.jav [deleted file]
update/Base64.java [new file with mode: 0644]
update/HMACT64.java [new file with mode: 0644]
update/NtlmHttpFilter.java [new file with mode: 0644]
update/NtlmHttpURLConnection.java [new file with mode: 0644]
update/NtlmMessage.java [new file with mode: 0644]
update/NtlmPasswordAuthentication.java [new file with mode: 0644]
update/NtlmServlet.java [new file with mode: 0644]
update/NtlmSsp.java [new file with mode: 0644]
update/Type1Message.java [new file with mode: 0644]
update/Type2Message.java [new file with mode: 0644]
update/Type3Message.java [new file with mode: 0644]

index 8d50af1..2b77d33 100644 (file)
@@ -1,3 +1,107 @@
+Thu Jul 10 22:07:09 EDT 2003
+
+Support  for  LMv2  authentication has been added. See the Overview page in
+the  API  documentation  regarding  the jcifs.smb.lmCompatibility property.
+Some  additonal  "cleanup"  has  also  been performed. One side-effect that
+might be noticed is that the default domain/username/password can no longer
+be  set  at runtime. Once the client is initialized the default credentials
+are  fixed.  Setting  default  credentials are runtime was always an unsafe
+operation. Create an NtlmPasswordAuthentication object instead. 
+
+Thu Jul  3 20:59:25 EDT 2003
+
+There  have  been  two small bug fixes in the SMB layer; the count field in
+the  SMB_COM_WRITE_ANDX  response  was  being ignored. Apparently the count
+specified  in the request is always honored or we would have seen plenty of
+file corruption by now. Second, the list() and listFiles() did not properly
+handle  Transaction  buffering  with  certain  values  for the listSize and
+listCount  properties  (if  multipart transaction responses are send). Also
+removed  a  few  obnoxious warnings getting logged during file transfers if
+Log.WARNINGS is set. 
+
+Regarding  NTLM  HTTP Authentication; there have been additions and updates
+to  the  documentation  on the NTLM flags, a fix to NtlmSsp to only provide
+the  target when requested by the client via the NTLM "request target" flag
+(this   is   the   correct  behavior),  and  a  bugfix  to  NtlmSsp  to  do
+Base64.encodeBytes(bytes,  false)  instead  of Base64.encodeBytes(bytes) (a
+linewrap  in  the  header  can  cause  an  error).  Eric has also excellent
+documentation detailing his analysis of the NTLM protocol: 
+
+  http://davenport.sourceforge.net/ntlm.html
+
+Thu Jun 12 00:18:05 EDT 2003
+
+It  was  discovered  that if a server responds to the NBT session setup but
+not  to  the  SMB_COM_NEGOTIATE  request the client will hang indefinately.
+This  is not a likely thing to occur because servers usually respond or not
+at  all.  Regardless  it  has  been  fixed.  The  client will timeout after
+jcifs.netbios.soTimeout in this situation. 
+
+Some tweeking of the NTLM http code has been performed.
+
+Wed May 28 19:09:17 EDT 2003
+
+jcifs-0.7.8 released
+
+A bug in the new ntlm http client code was identified and fixed.
+
+Tue May 27 21:40:58 EDT 2003
+
+jcifs-0.7.7 released
+
+A  deadlock  was  identified  in SmbTransport very similar to the one found
+last  year.  A  significant  change  has  been made that greatly simplifies
+synchronization in the transport layer. It eliminates this issue as well as
+the  extra synchronization introduced to fix the previous problem. It is an
+elemental change however so proceed with caution.
+
+Two  new  packages  have  been  introduced.  The  jcifs.ntlmssp package now
+contains  all NTLMSSP base code. This code is used by the NtlmHttpFilter as
+well  has  the  new  NtlmHttpURLConnection class for transparently enabling
+your  HTTP and HTTPS client to negotiate NTLM authentication. The other new
+package  is  jcifs.https  which  just  contains  the HTTPS protocol handler
+necesary for proper protocol handler registration. Please read the document
+entitled  "Using  jCIFS  NTLM  Authentication  for  HTTP  Connections"  for
+important details.
+
+To permit SmbFileOutputStream to be used with unusual named pipe modes (see
+5/1/03  message)  that  will  be  both  read  and written the open flags in
+SmbFileOutputStream have been changed from O_WRONLY to O_RDWR.
+
+The    attrExpiration    period    of    SmbFile    is    now    reset   in
+SmbFileOutputStream.write()  to  prevent  jCIFS  from  reporting  incorrect
+attributes  values  after  writing  the file. Previously it was possible to
+write  to  the file and immediated check the timestamp or file size and get
+an old value. This should no longer happen.
+
+The  SmbFileInputStream.skip()  method  has  been implemented in a way that
+will  not  result  in  any  IO to the server. Thus it is possible to resume
+large downloads from the point of failure for instance. 
+
+Wed Apr 16 22:46:07 EDT 2003
+
+jcifs-0.7.6 released
+
+The  isDirectory method has been changed to return false if the target does
+not  exist.  Previously  this  would return true which is inconsistent with
+java.io.File. 
+
+Wed Apr  2 23:56:26 EST 2003
+
+jcifs-0.7.5 released
+
+More  refinement  to  the  NTLM  HTTP authentication code has been applied.
+There  is  also  a fix for listing hosts from Windows ME/98/95 local master
+browsers. 
+
+Wed Mar 26 19:17:24 EST 2003
+
+jcifs-0.7.4 released
+
+Some NtlmHttpFilter issues were reported by several users of Win98 clients.
+Eric  has  provided  a  new  NtlmSsp.java  that  works correctly with these
+clients. 
+
 Wed Feb 12 01:23:02 EST 2003
 
 From    the    beginning   jCIFS   identified   SmbSessions   uniquely   by
index 17beab8..b16e455 100644 (file)
@@ -1,3 +1,59 @@
+Thu Jul 10 22:07:09 EDT 2003
+
+Support  for  LMv2  authentication has been added.
+
+Thu Jul  3 20:59:25 EDT 2003
+
+There  have  been minor bug fixes in the NTLM code and SMB layer as well as
+documentation updates. 
+
+Thu Jun 12 00:22:05 EDT 2003
+
+jcifs-0.7.9 released
+
+A bug that could cause a connection to hang indefinately has been fixed and
+some NTLM HTTP authentication tweeking has been applied. 
+
+Wed May 28 19:09:17 EDT 2003
+
+jcifs-0.7.8 released
+
+A  bug in the new ntlm http client code that would cause it to hang for ~60
+seconds before retreiving the target document has been fixed. 
+
+Tue May 27 21:40:58 EDT 2003
+
+jcifs-0.7.7 released
+
+A  deadlock has been identified and fixed, NTLM authentication for HTTP and
+HTTPS  clients  has  been  added  (the  inverse of the NtlmHttpFilter), the
+SmbFileInputStream.skip()  method will skip without reading, and there have
+two other small fixes. 
+
+Wed Apr 16 22:46:07 EDT 2003
+
+jcifs-0.7.6 released
+
+The  isDirectory method has been changed to return false if the target does
+not  exist.  Previously  this  would return true which is inconsistent with
+java.io.File. 
+
+Wed Apr  2 23:56:26 EST 2003
+
+jcifs-0.7.5 released
+
+More  refinement  to  the  NTLM  HTTP authentication code has been applied.
+There  is  also  a fix for listing hosts from Windows ME/98/95 local master
+browsers. 
+
+Wed Mar 26 19:17:24 EST 2003
+
+jcifs-0.7.4 released
+
+Some NtlmHttpFilter issues were reported by several users of Win98 clients.
+Eric  has  provided  a  new  NtlmSsp.java  that  works correctly with these
+clients. 
+
 Wed Feb 12 01:23:02 EST 2003
 
 A  security issue regarding the SmbSession.logon() method used by NTLM HTTP
index c16e817..b7a2669 100644 (file)
--- a/build.xml
+++ b/build.xml
                                includes="jcifs/http/*.java"
                                debug="on"/>
        </target>
-       <target name="smb" depends="comm,netbios,util,http">
+       <target name="https">
+               <mkdir dir="build"/>
+               <javac srcdir="src"
+                               destdir="build"
+                               includes="jcifs/https/*.java"
+                               debug="on"/>
+       </target>
+       <target name="smb" depends="comm,netbios,util,http,https">
                <mkdir dir="build"/>
                <javac srcdir="src"
                                destdir="build"
        <target name="jar" depends="smb">
                <copy file="src/jcifs/util/mime.map" tofile="build/jcifs/util/mime.map" overwrite="yes"/>
                <copy file="src/jcifs/http/ne.css" tofile="build/jcifs/http/ne.css" overwrite="yes"/>
-               <jar jarfile="jcifs-0.7.3.jar" basedir="build"/>
+               <jar jarfile="jcifs-0.7.11.jar" basedir="build"/>
        </target>
 
        <target name="tgz">
-               <copy todir="dist_tmp/jcifs_0.7.3">
+               <copy todir="dist_tmp/jcifs_0.7.11">
                        <fileset dir="." excludes="ant,**/.*,build,jcifs.prp,**/*.tgz,**/*.zip"/>
                </copy>
-               <tar tarfile="jcifs-0.7.3.tar" basedir="dist_tmp"/>
-               <gzip src="jcifs-0.7.3.tar" zipfile="jcifs-0.7.3.tgz"/>
-               <delete file="jcifs-0.7.3.tar"/>
+               <tar tarfile="jcifs-0.7.11.tar" basedir="dist_tmp"/>
+               <gzip src="jcifs-0.7.11.tar" zipfile="jcifs-0.7.11.tgz"/>
+               <delete file="jcifs-0.7.11.tar"/>
                <delete dir="dist_tmp"/>
        </target>
        <target name="zip">
-               <copy todir="dist_tmp/jcifs_0.7.3">
+               <copy todir="dist_tmp/jcifs_0.7.11">
                        <fileset dir="." excludes="ant,**/.*,build,jcifs.prp,**/*.tgz,**/*.zip"/>
                </copy>
                <fixcrlf srcdir="dist_tmp" cr="add" tab="remove" tablength="4" excludes="**/*.jar,**/*.exe"/>
-               <zip zipfile="jcifs-0.7.3.zip" basedir="dist_tmp"/>
+               <zip zipfile="jcifs-0.7.11.zip" basedir="dist_tmp"/>
                <delete dir="dist_tmp"/>
        </target>
 
index b582892..931accf 100644 (file)
@@ -18,7 +18,7 @@
  *     false - the opposite of the above
  * isDirectory
  *     true  - the target is a workgroup, server, share, or directory
- *     false - the target is not one of the above
+ *     false - the target is not one of the above or does not exist
  * isFile
  *     direct opposite of isDirectory
  * isHidden
@@ -205,6 +205,16 @@ public class FileOps {
                        System.out.println( "fail - isDirectory " + d + " returned false but it really is a directory" );
                }
 
+       // isDirectory - Test directory that does not exist
+
+               b = new SmbFile( d, "bogus" );
+
+               if( b.isDirectory() ) {
+                       System.out.println( "fail - isDirectory " + b + " returned true but it does not exist" );
+               } else {
+                       System.out.println( "okay - isDirectory " + b + " does not exist" );
+               }
+
        // isFile - Test file as a file
 
                if( f.isFile() ) {
diff --git a/examples/LogonTest.java b/examples/LogonTest.java
deleted file mode 100644 (file)
index 57b9bd7..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-import jcifs.UniAddress;
-import jcifs.smb.SmbSession;
-import jcifs.smb.NtlmPasswordAuthentication;
-
-public class LogonTest {
-
-       public static void main( String argv[] ) throws Exception {
-               UniAddress dc = UniAddress.getByName( "miallen2" );
-               NtlmPasswordAuthentication good = new NtlmPasswordAuthentication( "mlamrs", "miallen", "ru_RUhrd" );
-               NtlmPasswordAuthentication bad = new NtlmPasswordAuthentication( "mlamrs", "miallen", "bogus" );
-               SmbSession.logon( dc, good );
-               System.out.println( "good" );
-               SmbSession.logon( dc, bad );
-               System.out.println( "bad" );
-       }
-}
-
diff --git a/examples/NtlmHttpClient.java b/examples/NtlmHttpClient.java
new file mode 100644 (file)
index 0000000..98d8914
--- /dev/null
@@ -0,0 +1,49 @@
+import java.io.*;\r
+\r
+import java.net.*;\r
+\r
+import jcifs.*;\r
+\r
+public class NtlmHttpClient {\r
+\r
+    public static void main(String[] args) throws Exception {\r
+        // Normally set this outside application.\r
+        // Note that as a side effect due to the way handlers are located,\r
+        // you can also achieve this by simply doing:\r
+             Config.registerSmbURLHandler();\r
+        // which we already do to register the smb handler.\r
+        //String pkgs = System.getProperty("java.protocol.handler.pkgs");\r
+        //pkgs = (pkgs != null) ? "jcifs|" + pkgs : "jcifs";\r
+        //System.setProperty("java.protocol.handler.pkgs", pkgs);\r
+        //\r
+\r
+        if (args == null || args.length < 4) {\r
+            System.out.println("NtlmHttpClient <url> <domain> <user> <password>");\r
+            System.exit(1);\r
+        }\r
+        String location = args[0];\r
+        String domain = args[1];\r
+        String user = args[2];\r
+        String password = args[3];\r
+        // can also specify these in the URL, i.e.\r
+        //     http://DOMAIN%5cuser:password@host/dir/file.html\r
+        // which will override these properties\r
+        Config.setProperty("jcifs.smb.client.domain", domain);\r
+        Config.setProperty("jcifs.smb.client.username", user);\r
+        Config.setProperty("jcifs.smb.client.password", password);\r
+\r
+        try {\r
+            Config.setProperty("jcifs.netbios.hostname",\r
+                    Config.getProperty("jcifs.netbios.hostname",\r
+                            InetAddress.getLocalHost().getHostName()));\r
+        } catch (Exception ex) { }\r
+        URL url = new URL(location);\r
+        BufferedReader reader = new BufferedReader(\r
+                new InputStreamReader(url.openStream()));\r
+        String line;\r
+        while ((line = reader.readLine()) != null) {\r
+            System.out.println(line);\r
+        }\r
+    }\r
+\r
+}\r
index 97d271e..0b515ad 100644 (file)
@@ -48,7 +48,7 @@ public class ThreadedSmbCrawler {
 
                                        synchronized( dirList ) {
                                                while( dirList.isEmpty() ) {
-System.err.println( "workingThreads=" + workingThreads );
+//System.err.println( "workingThreads=" + workingThreads );
                                                        if( workingThreads == 0 ) {
                                                                return; // done
                                                        }
@@ -61,7 +61,7 @@ System.err.println( "workingThreads=" + workingThreads );
                                                workingThreads++;
                                        }
 
-                                       String[] l = e.dir.list();
+                                       SmbFile[] l = e.dir.listFiles();
 
                                        int n = maxDepth - e.depth;
 
@@ -71,7 +71,7 @@ System.err.println( "workingThreads=" + workingThreads );
                                                        for( int k = 0; k < n; k++ ) {
                                                                sb.append( "    " );
                                                        }
-                                                       SmbFile d = new SmbFile( e.dir, l[i] );
+                                                       SmbFile d = l[i];
                                                        System.err.println( sb.append( d ));
                                                        if( d.isDirectory() ) {
                                                                synchronized( dirList ) {
@@ -132,6 +132,6 @@ System.err.println( "workingThreads=" + workingThreads );
                }
 
                tsc = new ThreadedSmbCrawler( argv[0], Integer.parseInt( argv[1] ), Integer.parseInt( argv[2] ));
-               System.err.println( "Crawling Complete: " + (tsc.go() / 1000 / 60) + "min" );
+               System.err.println( "Crawling Complete: " + (tsc.go() / 1000) + "sec" );
        }
 }
index cfa42fa..7d8b6da 100644 (file)
@@ -59,7 +59,7 @@ public class VerifyReads {
                int depth;
 
                if( argv.length < 2 ) {
-                       System.err.println( "Must specify ini directory location (e.g. smb://mlamrs\\;rschprod:grispass@rsch-nyc-19b9/vendorapps) followd by the maximum traversal depth");
+                       System.err.println( "Must specify ini directory location (e.g. smb://mydom\\;user:pass@nyc-19b9/apps) followd by the maximum traversal depth");
                        System.exit( 1 );
                }
 
diff --git a/src/jcifs/http/Handler.java b/src/jcifs/http/Handler.java
new file mode 100644 (file)
index 0000000..d915faa
--- /dev/null
@@ -0,0 +1,159 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.http;
+
+import java.io.IOException;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * A <code>URLStreamHandler</code> used to provide NTLM authentication
+ * capabilities to the default HTTP handler.  This acts as a wrapper,
+ * handling authentication and passing control to the underlying
+ * stream handler.
+ */
+public class Handler extends URLStreamHandler {
+
+    /**
+     * The default HTTP port (<code>80</code>).
+     */
+    public static final int DEFAULT_HTTP_PORT = 80;
+
+    private static final Map PROTOCOL_HANDLERS = new HashMap();
+
+    private static final String HANDLER_PKGS_PROPERTY =
+            "java.protocol.handler.pkgs";
+
+    /**
+     * Vendor-specific default packages.  If no packages are specified in
+     * "java.protocol.handler.pkgs", the VM uses one or more default
+     * packages, which are vendor specific.  Sun's is included below
+     * for convenience; others could be as well.  If a particular vendor's
+     * package isn't listed, it can be specified in
+     * "java.protocol.handler.pkgs".
+     */
+    private static final String[] JVM_VENDOR_DEFAULT_PKGS = new String[] {
+        "sun.net.www.protocol"
+    };
+
+    private static URLStreamHandlerFactory factory;
+
+    /**
+     * Sets the URL stream handler factory for the environment.  This
+     * allows specification of the factory used in creating underlying
+     * stream handlers.  This can be called once per JVM instance.
+     *
+     * @param factory The URL stream handler factory.
+     */
+    public static void setURLStreamHandlerFactory(
+            URLStreamHandlerFactory factory) {
+        synchronized (PROTOCOL_HANDLERS) {
+            if (Handler.factory != null) {
+                throw new IllegalStateException(
+                        "URLStreamHandlerFactory already set.");
+            }
+            PROTOCOL_HANDLERS.clear();
+            Handler.factory = factory;
+        }
+    }
+
+    /**
+     * Returns the default HTTP port.
+     *
+     * @return An <code>int</code> containing the default HTTP port.
+     */
+    protected int getDefaultPort() {
+        return DEFAULT_HTTP_PORT;
+    }
+
+    protected URLConnection openConnection(URL url) throws IOException {
+        url = new URL(url, url.toExternalForm(),
+                getDefaultStreamHandler(url.getProtocol()));
+        return new NtlmHttpURLConnection((HttpURLConnection)
+                url.openConnection());
+    }
+
+    private static URLStreamHandler getDefaultStreamHandler(String protocol)
+            throws IOException {
+        synchronized (PROTOCOL_HANDLERS) {
+            URLStreamHandler handler = (URLStreamHandler)
+                    PROTOCOL_HANDLERS.get(protocol);
+            if (handler != null) return handler;
+            if (factory != null) {
+                handler = factory.createURLStreamHandler(protocol);
+            }
+            if (handler == null) {
+                String path = System.getProperty(HANDLER_PKGS_PROPERTY);
+                StringTokenizer tokenizer = new StringTokenizer(path, "|");
+                while (tokenizer.hasMoreTokens()) {
+                    String provider = tokenizer.nextToken().trim();
+                    if (provider.equals("jcifs")) continue;
+                    String className = provider + "." + protocol + ".Handler";
+                    try {
+                        Class handlerClass = null;
+                        try {
+                            handlerClass = Class.forName(className);
+                        } catch (Exception ex) { }
+                        if (handlerClass == null) {
+                            handlerClass = ClassLoader.getSystemClassLoader(
+                                    ).loadClass(className);
+                        }
+                        handler = (URLStreamHandler) handlerClass.newInstance();
+                        break;
+                    } catch (Exception ex) { }
+                }
+            }
+            if (handler == null) {
+                for (int i = 0; i < JVM_VENDOR_DEFAULT_PKGS.length; i++) {
+                    String className = JVM_VENDOR_DEFAULT_PKGS[i] + "." +
+                            protocol + ".Handler";
+                    try {
+                        Class handlerClass = null;
+                        try {
+                            handlerClass = Class.forName(className);
+                        } catch (Exception ex) { }
+                        if (handlerClass == null) {
+                            handlerClass = ClassLoader.getSystemClassLoader(
+                                    ).loadClass(className);
+                        }
+                        handler = (URLStreamHandler) handlerClass.newInstance();
+                    } catch (Exception ex) { }
+                    if (handler != null) break;
+                }
+            }
+            if (handler == null) {
+                throw new IOException(
+                        "Unable to find default handler for protocol: " +
+                                protocol);
+            }
+            PROTOCOL_HANDLERS.put(protocol, handler);
+            return handler;
+        }
+    }
+
+}
index 8b70e7d..350fec3 100644 (file)
@@ -1,19 +1,19 @@
 /* jcifs smb client library in Java
  * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
- *                     "Jason Pugsley" <jcifs at samba dot org>
- *                     "skeetz" <jcifs at samba dot org>
- *                     "Eric Glass" <jcifs at samba dot org>
- * 
+ *                                      "Jason Pugsley" <jcifs at samba dot org>
+ *                                      "skeetz" <jcifs at samba dot org>
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
@@ -30,6 +30,7 @@ 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
@@ -40,10 +41,18 @@ import jcifs.smb.SmbAuthException;
  * Read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a> for complete details.
  */
 
-public class NtlmHttpFilter extends NtlmSsp implements Filter {
+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;
 
@@ -59,11 +68,15 @@ public class NtlmHttpFilter extends NtlmSsp implements Filter {
                                Config.setProperty( name, filterConfig.getInitParameter( name ));
                        }
                }
-
+               defaultDomain = Config.getProperty("jcifs.smb.client.domain");
                domainController = Config.getProperty( "jcifs.http.domainController" );
-               if( domainController == null ) {
-                       domainController = Config.getProperty( "jcifs.smb.client.domain" );
-               }
+               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() {
        }
@@ -72,39 +85,65 @@ public class NtlmHttpFilter extends NtlmSsp implements Filter {
                                FilterChain chain ) throws IOException, ServletException {
                HttpServletRequest req;
                HttpServletResponse resp;
-               NtlmPasswordAuthentication ntlm;
                UniAddress dc;
-               byte[] challenge;
                String msg;
-               HttpSession ssn;
 
+               NtlmPasswordAuthentication ntlm = null;
                req = (HttpServletRequest)request;
                resp = (HttpServletResponse)response;
-               ssn = req.getSession();
                msg = req.getHeader( "Authorization" );
+               boolean offerBasic = enableBasic && (insecureBasic || req.isSecure());
 
-               if( msg != null && msg.startsWith( "NTLM " )) {
+               if( msg != null && (msg.startsWith( "NTLM " ) ||
+                                       (offerBasic && msg.startsWith("Basic ")))) {
                        dc = UniAddress.getByName( domainController, true );
-                       challenge = SmbSession.getChallenge( dc );
-                       if(( ntlm = doAuthentication( req, resp, challenge )) == null ) {
-                               return;
+                       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;
                        }
-                       ssn.setAttribute( "NtlmHttpAuth", ntlm );
-               } else if(( ntlm = (NtlmPasswordAuthentication)ssn.getAttribute( "NtlmHttpAuth" )) == null ) {
-                       resp.setHeader( "WWW-Authenticate", "NTLM" );
-                       resp.setHeader( "Connection", "close" );
-                       resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
-                       resp.flushBuffer();
-                       return;
                }
 
                chain.doFilter( new NtlmHttpServletRequest( req, ntlm ), response );
index f8526ad..47629a9 100644 (file)
@@ -1,6 +1,6 @@
 /* jcifs smb client library in Java
  * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
- *                     "Eric Glass" <jcifs at samba dot org>
+ *                                      "Eric Glass" <jcifs at samba dot org>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -37,5 +37,8 @@ class NtlmHttpServletRequest extends HttpServletRequestWrapper {
        public Principal getUserPrincipal() {
                return principal;
        }
+       public String getAuthType() {
+               return "NTLM";
+       }
 }
 
diff --git a/src/jcifs/http/NtlmHttpURLConnection.java b/src/jcifs/http/NtlmHttpURLConnection.java
new file mode 100644 (file)
index 0000000..5dd30f4
--- /dev/null
@@ -0,0 +1,563 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.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 <code>HttpURLConnection</code> to provide NTLM authentication
+ * services.
+ *
+ * Please read <a href="../../../httpclient.html">Using jCIFS NTLM Authentication for HTTP Connections</a>.
+ */
+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);
+       }
+}
index 14e47db..96fe897 100644 (file)
@@ -1,17 +1,17 @@
 /* jcifs smb client library in Java
  * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
- *                     "Eric Glass" <jcifs at samba dot org>
- * 
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
@@ -38,8 +38,11 @@ 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
@@ -54,9 +57,15 @@ import jcifs.smb.SmbSession;
 
 public abstract class NtlmServlet extends HttpServlet {
 
-       private static final NtlmSsp AUTH = new NtlmSsp();
+       private String defaultDomain;
+
+       private String domainController;
+
+       private boolean enableBasic;
 
-       private UniAddress domainController;
+       private boolean insecureBasic;
+
+       private String realm;
 
        public void init(ServletConfig config) throws ServletException {
                super.init(config);
@@ -74,37 +83,74 @@ public abstract class NtlmServlet extends HttpServlet {
                                Config.setProperty(name, config.getInitParameter(name));
                        }
                }
-               String dc = Config.getProperty("jcifs.http.domainController");
-               if (dc == null && (dc = Config.getProperty( "jcifs.smb.client.domain" )) == null) {
-                       throw new UnavailableException("No domain controller specified.");
-               }
-               try {
-                       domainController = UniAddress.getByName(dc);
-               } catch (UnknownHostException ex) {
-                       throw new UnavailableException("Specified DC unreachable.");
-               }
+               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")) {
-                       HttpSession ssn;
-                       byte[] challenge = SmbSession.getChallenge(domainController);
-                       NtlmPasswordAuthentication ntlm = AUTH.doAuthentication(request,
-                                       response, challenge);
-                       if (ntlm == null) return;
-                       SmbSession.logon(domainController, ntlm);
-                       ssn = request.getSession();
+               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 if (request.getSession().getAttribute("NtlmHttpAuth") == null) {
-                       response.setHeader("WWW-Authenticate", "NTLM");
-                       response.setHeader("Connection", "close");
-                       response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-                       response.flushBuffer();
-                       return;
+               } 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);
        }
index d6a02d1..5cab7f2 100644 (file)
@@ -1,18 +1,19 @@
 /* jcifs smb client library in Java
  * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
- *                     "Jason Pugsley" <jcifs at samba dot org>
- *                     "skeetz" <jcifs at samba dot org>
- * 
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *                                      "Jason Pugsley" <jcifs at samba dot org>
+ *                                      "skeetz" <jcifs at samba dot org>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 package jcifs.http;
 
-import java.io.*;
-import javax.servlet.*;
-import javax.servlet.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 <tt>NtlmHttpFilter</tt>,
  * <tt>NtlmServlet</tt>, and <tt>NetworkExplorer</tt> to negiotiate password
@@ -39,93 +50,63 @@ import jcifs.util.Base64;
  * the Network Explorer Servlet</a> related information.
  */
 
-public class NtlmSsp {
-
-       static final byte[] msg2 = {
-               (byte)0x4E, (byte)0x54, (byte)0x4C, (byte)0x4D,
-               (byte)0x53, (byte)0x53, (byte)0x50, (byte)0x00,
-               (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00,
-               (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-               (byte)0x28, (byte)0x00, (byte)0x00, (byte)0x00,
-               (byte)0x01, (byte)0x82, (byte)0x00, (byte)0x00,
-               (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
-               (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
-               (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-               (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
-       };
-
-       int readInt2( byte[] src, int srcIndex ) {
-               return ( src[srcIndex] & 0xFF ) +
-                               (( src[srcIndex + 1] & 0xFF ) << 8 );
-       }
-
-       /* Decode the type-1-message and encode the type-2-message. Note the "domain"
-     * in the type-1-message isn't necessarily
-        * the  authentication  domain.  There  is  a  good  possibilty it is only the
-        * workgroup  used  for browsing. The 3rd message with have the authenticating
-        * domain.
-        */
-       int encodeType2Message( byte[] src, byte[] dst, byte[] challenge ) throws IOException {
-
-jcifs.util.Log.printHexDump( "type-1-message", src );
-
-               System.arraycopy( msg2, 0, dst, 0, 40 );
-               System.arraycopy( challenge, 0, dst, 24, 8 );
-
-jcifs.util.Log.printHexDump( "type-2-message", dst, 0, 40 );
-               return 40;
-       }
-       /* Decode type-3-message.
+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.
         */
-       NtlmPasswordAuthentication decodeType3Message( byte[] src ) throws IOException {
-               int off, len;
-               byte[] lm_hash = new byte[24];
-               byte[] nt_hash = new byte[24];
-               String domain, user;
-
-jcifs.util.Log.printHexDump( "type-3-message", src );
-
-               off = readInt2( src, 16 );
-               System.arraycopy( src, off, lm_hash, 0, 24 );
-               off = readInt2( src, 24 );
-               System.arraycopy( src, off, nt_hash, 0, 24 );
-               len = readInt2( src, 28 );
-               off = readInt2( src, 32 );
-               domain = new String( src, off, len, "UnicodeLittleUnmarked" );
-               len = readInt2( src, 36 );
-               off = readInt2( src, 40 );
-               user = new String( src, off, len, "UnicodeLittleUnmarked" );
-
-               return new NtlmPasswordAuthentication( domain, user, lm_hash, nt_hash );
+       public NtlmPasswordAuthentication doAuthentication(
+                       HttpServletRequest req, HttpServletResponse resp, byte[] challenge)
+                                       throws IOException, ServletException {
+               return authenticate(req, resp, challenge);
        }
-       public NtlmPasswordAuthentication doAuthentication( HttpServletRequest req,
-                               HttpServletResponse resp, byte[] challenge )
-                               throws IOException, ServletException {
-               String msg;
-
-               msg = req.getHeader( "Authorization" );
-
-               if( msg != null && msg.startsWith( "NTLM " )) {
-                       byte[] src = Base64.decode( msg.substring( 5 ));
-
-                       if( src[8] == 1 ) {
-                               byte[] dst = new byte[40];
-
-                               msg = Base64.encodeBytes( dst, 0, encodeType2Message( src, dst, 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 );
-                               resp.setContentLength( 0 );
-                       } else if( src[8] == 3 ) {
-                               return decodeType3Message( src );
+                       } 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.setHeader("WWW-Authenticate", "NTLM");
+                       resp.setHeader("Connection", "close");
                }
-               resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
+               resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+               resp.setContentLength( 0 );
                resp.flushBuffer();
-
                return null;
        }
+
 }
 
diff --git a/src/jcifs/https/Handler.java b/src/jcifs/https/Handler.java
new file mode 100644 (file)
index 0000000..041b008
--- /dev/null
@@ -0,0 +1,44 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.https;
+
+/**
+ * A <code>URLStreamHandler</code> used to provide NTLM authentication
+ * capabilities to the default HTTPS handler.  This acts as a wrapper,
+ * handling authentication and passing control to the underlying
+ * stream handler.
+ */
+public class Handler extends jcifs.http.Handler {
+
+    /**
+     * The default HTTPS port (<code>443</code>).
+     */
+    public static final int DEFAULT_HTTPS_PORT = 443;
+
+    /**
+     * Returns the default HTTPS port.
+     *
+     * @return An <code>int</code> containing the default HTTPS port.
+     */
+    protected int getDefaultPort() {
+        return DEFAULT_HTTPS_PORT;
+    }
+
+}
index b93409e..fdbc005 100644 (file)
@@ -584,6 +584,8 @@ public final class NbtAddress {
                                        i++;
                                }
                        }
+               } else if( hostName.hexCode == 0x1D ) {
+                       calledName = SMBSERVER_NAME;
                }
 
                return calledName;
@@ -597,7 +599,14 @@ public final class NbtAddress {
 
                        try {
                                addrs = client.getNodeStatus( this );
-                               if( isDataFromNodeStatus ) {
+                               if( hostName.hexCode == 0x1D ) {
+                                       for( int i = 0; i < addrs.length; i++ ) {
+                                               if( addrs[i].hostName.hexCode == 0x20 ) {
+                                                       return addrs[i].hostName.name;
+                                               }
+                                       }
+                                       return null;
+                               } else if( isDataFromNodeStatus ) {
                                        /* 'this' has been updated and should now
                                         * have a real NetBIOS name
                                         */
index 2a2b5bd..f9c4714 100644 (file)
@@ -23,6 +23,7 @@ import java.net.InetAddress;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.IOException;
+import jcifs.Config;
 
 /**
  * This class represents a netbios TCP/IP socket. Under no
@@ -35,9 +36,11 @@ public class NbtSocket extends Socket {
 
        private static final int SSN_SRVC_PORT = 139;
        private static final int BUFFER_SIZE = 512;
+    private static final int DEFAULT_SO_TIMEOUT = 5000;
 
        NbtAddress address;
        Name calledName;
+       int soTimeout;
 
        public NbtSocket() {
                super();
@@ -59,6 +62,7 @@ public class NbtSocket extends Socket {
                } else {
                        this.calledName = new Name( calledName, 0x20, null );
                }
+        soTimeout = Config.getInt( "jcifs.netbios.soTimeout", DEFAULT_SO_TIMEOUT );
                connect();
        }
 
@@ -100,6 +104,7 @@ public class NbtSocket extends Socket {
                SessionServicePacket ssp0 = new SessionRequestPacket( calledName, NbtAddress.localhost.hostName );
                out.write( buffer, 0, ssp0.writeWireFormat( buffer, 0 ));
 
+               setSoTimeout( soTimeout );
                switch( ssp0.readPacketType( in, buffer, 0 )) {
                        case SessionServicePacket.POSITIVE_SESSION_RESPONSE:
                                Log.println( Log.WARNINGS, "session service warning", " session established ok with " + address );
diff --git a/src/jcifs/ntlmssp/NtlmFlags.java b/src/jcifs/ntlmssp/NtlmFlags.java
new file mode 100644 (file)
index 0000000..9cd8d50
--- /dev/null
@@ -0,0 +1,155 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                    "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.ntlmssp;
+
+/**
+ * Flags used during negotiation of NTLMSSP authentication.
+ */
+public interface NtlmFlags {
+
+    /**
+    * Indicates whether Unicode strings are supported or used.
+    */
+    public static final int NTLMSSP_NEGOTIATE_UNICODE = 0x00000001;
+
+    /**
+    * Indicates whether OEM strings are supported or used.
+    */
+    public static final int NTLMSSP_NEGOTIATE_OEM = 0x00000002;
+
+    /**
+    * Indicates whether the authentication target is requested from
+    * the server.
+    */
+    public static final int NTLMSSP_REQUEST_TARGET = 0x00000004;
+
+    /**
+    * Specifies that communication across the authenticated channel
+    * should carry a digital signature (message integrity).
+    */
+    public static final int NTLMSSP_NEGOTIATE_SIGN = 0x00000010;
+
+    /**
+    * Specifies that communication across the authenticated channel
+    * should be encrypted (message confidentiality).
+    */
+    public static final int NTLMSSP_NEGOTIATE_SEAL = 0x00000020;
+
+    /**
+    * Indicates datagram authentication.
+    */
+    public static final int NTLMSSP_NEGOTIATE_DATAGRAM_STYLE = 0x00000040;
+
+    /**
+    * Indicates that the LAN Manager session key should be used for
+    * signing and sealing authenticated communication.
+    */
+    public static final int NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080;
+
+    public static final int NTLMSSP_NEGOTIATE_NETWARE = 0x00000100;
+
+    /**
+    * Indicates support for NTLM authentication.
+    */
+    public static final int NTLMSSP_NEGOTIATE_NTLM = 0x00000200;
+
+    /**
+    * Indicates whether the OEM-formatted domain name in which the
+    * client workstation has membership is supplied in the Type-1 message.
+    * This is used in the negotation of local authentication. 
+    */
+    public static final int NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED =
+            0x00001000;
+
+    /**
+    * Indicates whether the OEM-formatted workstation name is supplied
+    * in the Type-1 message.  This is used in the negotiation of local
+    * authentication.
+    */
+    public static final int NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED =
+            0x00002000;
+
+    /**
+    * Sent by the server to indicate that the server and client are
+    * on the same machine.  This implies that the server will include
+    * a local security context handle in the Type 2 message, for
+    * use in local authentication.
+    */
+    public static final int NTLMSSP_NEGOTIATE_LOCAL_CALL = 0x00004000;
+
+    /**
+    * Indicates that authenticated communication between the client
+    * and server should carry a "dummy" digital signature.
+    */
+    public static final int NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000;
+
+    /**
+    * Sent by the server in the Type 2 message to indicate that the 
+    * target authentication realm is a domain.
+    */
+    public static final int NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000;
+
+    /**
+    * Sent by the server in the Type 2 message to indicate that the 
+    * target authentication realm is a server.
+    */
+    public static final int NTLMSSP_TARGET_TYPE_SERVER = 0x00020000;
+
+    /**
+    * Sent by the server in the Type 2 message to indicate that the 
+    * target authentication realm is a share (presumably for share-level
+    * authentication).
+    */
+    public static final int NTLMSSP_TARGET_TYPE_SHARE = 0x00040000;
+
+    /**
+    * Indicates that the NTLM2 signing and sealing scheme should be used
+    * for protecting authenticated communications.  This refers to a
+    * particular session security scheme, and is not related to the use
+    * of NTLMv2 authentication.
+    */ 
+    public static final int NTLMSSP_NEGOTIATE_NTLM2 = 0x00080000;
+
+    public static final int NTLMSSP_REQUEST_INIT_RESPONSE = 0x00100000;
+
+    public static final int NTLMSSP_REQUEST_ACCEPT_RESPONSE = 0x00200000;
+
+    public static final int NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000;
+
+    /**
+    * Sent by the server in the Type 2 message to indicate that it is
+    * including a Target Information block in the message.  The Target
+    * Information block is used in the calculation of the NTLMv2 response.
+    */
+    public static final int NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000;
+
+    /**
+    * Indicates that 128-bit encryption is supported.
+    */
+    public static final int NTLMSSP_NEGOTIATE_128 = 0x20000000;
+
+    public static final int NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000;
+
+    /**
+    * Indicates that 56-bit encryption is supported.
+    */
+    public static final int NTLMSSP_NEGOTIATE_56 = 0x80000000;
+
+}
diff --git a/src/jcifs/ntlmssp/NtlmMessage.java b/src/jcifs/ntlmssp/NtlmMessage.java
new file mode 100644 (file)
index 0000000..5689a71
--- /dev/null
@@ -0,0 +1,138 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.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 <code>int</code> 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., <code>NTLMSSP_NEGOTIATE_OEM</code>).
+        * @return A <code>boolean</code> 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.,
+        * <code>NTLMSSP_NEGOTIATE_OEM</code>).
+        * @param value Indicates whether to set (<code>true</code>) or
+        * clear (<code>false</code>) 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 <code>byte[]</code> containing the raw message material.
+        */
+       public abstract byte[] toByteArray();
+
+}
diff --git a/src/jcifs/ntlmssp/Type1Message.java b/src/jcifs/ntlmssp/Type1Message.java
new file mode 100644 (file)
index 0000000..42d3610
--- /dev/null
@@ -0,0 +1,248 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                                      "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.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 <code>String</code> 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 <code>String</code> 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 <code>int</code> containing the default flags.
+        */
+       public static int getDefaultFlags() {
+               return DEFAULT_FLAGS;
+       }
+
+       /**
+        * Returns the default domain from the current environment.
+        *
+        * @return A <code>String</code> containing the default domain.
+        */
+       public static String getDefaultDomain() {
+               return DEFAULT_DOMAIN;
+       }
+
+       /**
+        * Returns the default workstation from the current environment.
+        *
+        * @return A <code>String</code> 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/src/jcifs/ntlmssp/Type2Message.java b/src/jcifs/ntlmssp/Type2Message.java
new file mode 100644 (file)
index 0000000..bb1de85
--- /dev/null
@@ -0,0 +1,414 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                                "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.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 <code>byte[]</code> 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 <code>String</code> 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 <code>byte[]</code> 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 <code>byte[]</code> 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 <code>int</code> 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 <code>int</code> 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 <code>String</code> 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/src/jcifs/ntlmssp/Type3Message.java b/src/jcifs/ntlmssp/Type3Message.java
new file mode 100644 (file)
index 0000000..c11f1fc
--- /dev/null
@@ -0,0 +1,580 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
+ *                                "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.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 <code>byte[]</code> 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 <code>byte[]</code> 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 <code>String</code> 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 <code>String</code> 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 <code>String</code> 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 <code>byte[]</code> 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 <code>int</code> 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 <code>int</code> 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 <code>byte[]</code> 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 <code>byte[]</code> 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));
+       }
+
+}
index 529fc89..c076ab8 100644 (file)
@@ -1,16 +1,17 @@
 /* jcifs smb client library in Java
  * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>
- * 
+ *                                     "Eric Glass" <jcifs at samba dot org>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
@@ -20,8 +21,10 @@ 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;
 
@@ -37,8 +40,22 @@ import jcifs.Config;
 
 public final class NtlmPasswordAuthentication implements Principal {
 
-    // KGS!@#$%
-    static final byte[] S8 = {
+       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
        };
@@ -53,10 +70,13 @@ public final class NtlmPasswordAuthentication implements Principal {
                        System.arraycopy( e8, 0, e, i * 8, 8 );
                }
        }
-       static byte[] getPreNTLMResponse( String password, byte[] challenge ) {
+/**
+ * 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[] p21 = new byte[21];
+               byte[] p24 = new byte[24];
                byte[] passwordBytes;
                try {
                        passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.encoding );
@@ -69,28 +89,66 @@ public final class NtlmPasswordAuthentication implements Principal {
                if( passwordLength > 14) {
                        passwordLength = 14;
                }
-        System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );
-        E( p14, S8, p21);
+               System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );
+               E( p14, S8, p21);
                E( p21, challenge, p24);
-        return p24;
+               return p24;
        }
-       static byte[] getNTLMResponse( String password, byte[] challenge ) {
+/**
+ * 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" );
+                       uni = password.getBytes( "UnicodeLittleUnmarked" );
                } catch( UnsupportedEncodingException uee ) {
                        Log.printStackTrace( "password encryption exception", uee );
                }
                MD4 md4 = new MD4();
                md4.update( uni );
-               System.arraycopy( md4.digest(), 0, p21, 0, 16 );
+               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 =
@@ -117,12 +175,12 @@ public final class NtlmPasswordAuthentication implements Principal {
                        char c;
 
                        end = userInfo.length();
-                       for( i = 0, u = 0; i < end; i++ ) { 
+                       for( i = 0, u = 0; i < end; i++ ) {
                                c = userInfo.charAt( i );
-                               if( c == ';' ) { 
-                                       domain = userInfo.substring( 0, i ); 
+                               if( c == ';' ) {
+                                       domain = userInfo.substring( 0, i );
                                        u = i + 1;
-                               } else if( c == ':' ) { 
+                               } else if( c == ':' ) {
                                        password = userInfo.substring( i + 1 );
                                        break;
                                }
@@ -130,15 +188,9 @@ public final class NtlmPasswordAuthentication implements Principal {
                        username = userInfo.substring( u, i );
                }
 
-               if( domain == null ) {
-                       this.domain = Config.getProperty( "jcifs.smb.client.domain", "?" );
-               }
-               if( username == null ) {
-                       this.username = Config.getProperty( "jcifs.smb.client.username", "GUEST" );
-               }
-               if( password == null ) {
-                       this.password = Config.getProperty( "jcifs.smb.client.password", "" );
-               }
+               if( domain == null ) this.domain = DEFAULT_DOMAIN;
+               if( username == null ) this.username = DEFAULT_USERNAME;
+               if( password == null ) this.password = DEFAULT_PASSWORD;
        }
 /**
  * Create an <tt>NtlmPasswordAuthentication</tt> object from a
@@ -151,15 +203,9 @@ public final class NtlmPasswordAuthentication implements Principal {
                this.domain = domain;
                this.username = username;
                this.password = password;
-               if( domain == null ) {
-                       this.domain = Config.getProperty( "jcifs.smb.client.domain", "?" );
-               }
-               if( username == null ) {
-                       this.username = Config.getProperty( "jcifs.smb.client.username", "GUEST" );
-               }
-               if( password == null ) {
-                       this.password = Config.getProperty( "jcifs.smb.client.password", "" );
-               }
+               if( domain == null ) this.domain = DEFAULT_DOMAIN;
+               if( username == null ) this.username = DEFAULT_USERNAME;
+               if( password == null ) this.password = DEFAULT_PASSWORD;
        }
 /**
  * Create an <tt>NtlmPasswordAuthentication</tt> object with raw password
@@ -217,7 +263,22 @@ public final class NtlmPasswordAuthentication implements Principal {
                if( hashesExternal ) {
                        return ansiHash;
                }
-               return getPreNTLMResponse( password, challenge );
+               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.
@@ -226,7 +287,24 @@ public final class NtlmPasswordAuthentication implements Principal {
                if( hashesExternal ) {
                        return unicodeHash;
                }
-               return getNTLMResponse( password, challenge );
+               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 <tt>NtlmPasswordAuthentication</tt> objects for
index 8beed59..90918e0 100644 (file)
@@ -1,16 +1,16 @@
 /* jcifs smb client library in Java
  * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
- * 
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
@@ -60,6 +60,8 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock {
                                accountPassword = session.auth.getAnsiHash( session.transport.server.encryptionKey );
                                unicodePassword = session.auth.getUnicodeHash( session.transport.server.encryptionKey );
                                passwordLength = unicodePasswordLength = 24;
+                               // fix for win9x clients
+                               if (unicodePassword.length == 0) unicodePasswordLength = 0;
                        } else if( Config.getBoolean( "jcifs.smb.client.disablePlainTextPasswords", true )) {
                                throw new RuntimeException( "Plain text passwords are disabled" );
                        } else if( useUnicode ) {
diff --git a/src/jcifs/smb/SmbComSessionSetupAndX.java0 b/src/jcifs/smb/SmbComSessionSetupAndX.java0
new file mode 100644 (file)
index 0000000..da61a71
--- /dev/null
@@ -0,0 +1,172 @@
+/* jcifs smb client library in Java
+ * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.smb;
+
+import jcifs.Config;
+import java.io.IOException;
+import java.io.InputStream;
+
+class SmbComSessionSetupAndX extends AndXServerMessageBlock {
+
+       static final int BATCH_LIMIT =
+                       Config.getInt( "jcifs.smb.client.SessionSetupAndX.TreeConnectAndX", 1 );
+
+       byte[] accountPassword, unicodePassword;
+       int passwordLength, unicodePasswordLength;
+       int negotiatedMaxBufferSize,
+               negotiatedMaxMpxCount,
+               vcNumber,
+               sessionKey;
+       String accountName,
+               primaryDomain,
+               nativeOs,
+               nativeLanMan;
+
+       SmbSession session;
+
+       SmbComSessionSetupAndX( SmbSession session, ServerMessageBlock andx ) {
+               super( andx );
+               command = SMB_COM_SESSION_SETUP_ANDX;
+               this.session = session;
+       }
+
+       int getBatchLimit( byte command ) {
+               return command == SMB_COM_TREE_CONNECT_ANDX ? BATCH_LIMIT : 0;
+       }
+       int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) {
+               int start = dstIndex;
+
+               if( session.transport.server.security == SECURITY_USER &&
+                                               ( session.auth.hashesExternal ||
+                                               session.auth.password.length() > 0 )) {
+                       if( session.transport.server.encryptedPasswords ) {
+                               // encrypted
+                               accountPassword = session.auth.getAnsiHash( session.transport.server.encryptionKey );
+                               unicodePassword = session.auth.getUnicodeHash( session.transport.server.encryptionKey );
+                               // fix for NTLMv2 or empty NTLM/LM
+                               passwordLength = accountPassword.length;
+                               unicodePasswordLength = unicodePassword.length;
+                       } else if( Config.getBoolean( "jcifs.smb.client.disablePlainTextPasswords", true )) {
+                               throw new RuntimeException( "Plain text passwords are disabled" );
+                       } else if( useUnicode ) {
+                               // plain text
+                               String password = session.auth.getPassword();
+                               accountPassword = new byte[0];
+                               passwordLength = 0;
+                               unicodePassword = new byte[(password.length() + 1) * 2];
+                               unicodePasswordLength = writeString( password, unicodePassword, 0 );
+                       } else {
+                               // plain text
+                               String password = session.auth.getPassword();
+                               accountPassword = new byte[(password.length() + 1) * 2];
+                               passwordLength = writeString( password, accountPassword, 0 );
+                               unicodePassword = new byte[0];
+                               unicodePasswordLength = 0;
+                       }
+               } else {
+                       // no password in session setup
+                       passwordLength = unicodePasswordLength = 0;
+               }
+
+               negotiatedMaxBufferSize = session.transport.negotiatedMaxBufferSize;
+               negotiatedMaxMpxCount = session.transport.negotiatedMaxMpxCount;
+               vcNumber = session.transport.client.vcNumber;
+               sessionKey = session.transport.client.sessionKey;
+
+               writeInt2( negotiatedMaxBufferSize, dst, dstIndex );
+               dstIndex += 2;
+               writeInt2( negotiatedMaxMpxCount, dst, dstIndex );
+               dstIndex += 2;
+               writeInt2( vcNumber, dst, dstIndex );
+               dstIndex += 2;
+               writeInt4( sessionKey, dst, dstIndex );
+               dstIndex += 4;
+               writeInt2( passwordLength, dst, dstIndex );
+               dstIndex += 2;
+               writeInt2( unicodePasswordLength, dst, dstIndex );
+               dstIndex += 2;
+               dst[dstIndex++] = (byte)0x00;
+               dst[dstIndex++] = (byte)0x00;
+               dst[dstIndex++] = (byte)0x00;
+               dst[dstIndex++] = (byte)0x00;
+               writeInt4( session.transport.client.capabilities, dst, dstIndex );
+               dstIndex += 4;
+
+               return dstIndex - start;
+       }
+       int writeBytesWireFormat( byte[] dst, int dstIndex ) {
+               int start = dstIndex;
+
+               accountName = session.auth.username.toUpperCase();
+               primaryDomain = session.auth.domain.toUpperCase();
+               nativeOs = session.transport.client.nativeOs;
+               nativeLanMan = session.transport.client.nativeLanMan;
+
+               if( session.transport.server.security == SECURITY_USER &&
+                                               ( session.auth.hashesExternal ||
+                                               session.auth.password.length() > 0 )) {
+                       System.arraycopy( accountPassword, 0, dst, dstIndex, passwordLength );
+                       dstIndex += passwordLength;
+                       System.arraycopy( unicodePassword, 0, dst, dstIndex, unicodePasswordLength );
+                       dstIndex += unicodePasswordLength;
+               }
+               if( useUnicode ) {
+                       // at least NT 4 observed needing this only with unicode
+                       dst[dstIndex++] = (byte)'\0';
+               }
+
+               dstIndex += writeString( accountName, dst, dstIndex );
+               dstIndex += writeString( primaryDomain, dst, dstIndex );
+               dstIndex += writeString( nativeOs, dst, dstIndex );
+/*
+               // at least NT 4 observed with 2 zero bytes here
+               dst[dstIndex++] = (byte)0x00;
+               dst[dstIndex++] = (byte)0x00;
+This still isn't quite right.
+*/
+               dstIndex += writeString( nativeLanMan, dst, dstIndex );
+
+               return dstIndex - start;
+       }
+       int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) {
+               return 0;
+       }
+       int readBytesWireFormat( byte[] buffer, int bufferIndex ) {
+               return 0;
+       }
+       int readBytesDirectWireFormat( InputStream in, int byteCount ) throws IOException {
+               return 0;
+       }
+       public String toString() {
+               String result = new String( "SmbComSessionSetupAndX[" +
+                       super.toString() +
+                       ",maxBufferSize=" + negotiatedMaxBufferSize +
+                       ",maxMpxCount=" + negotiatedMaxMpxCount +
+                       ",vcNumber=" + vcNumber +
+                       ",sessionKey=" + sessionKey +
+                       ",passwordLength=" + passwordLength +
+                       ",unicodePasswordLength=" + unicodePasswordLength +
+                       ",capabilities=" + session.transport.client.capabilities +
+                       ",accountName=" + accountName +
+                       ",primaryDomain=" + primaryDomain +
+                       ",nativeOs=" + nativeOs +
+                       ",nativeLanMan=" + nativeLanMan + "]" );
+               return result;
+       }
+}
index a0384ba..24a16d2 100644 (file)
@@ -53,6 +53,11 @@ abstract class SmbComTransactionResponse extends ServerMessageBlock implements E
                txn_buf = null;
        }
 
+       public void reset() {
+               bufDataStart = 0;
+               isPrimary = hasMore = true; 
+               parametersDone = dataDone = false;
+       }
        public boolean hasMoreElements() {
                return hasMore;
        }
index 0437116..8a3bd6e 100644 (file)
@@ -1,16 +1,16 @@
 /* jcifs smb client library in Java
  * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
- * 
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
@@ -121,11 +121,11 @@ class SmbComTreeConnectAndX extends AndXServerMessageBlock {
                if( session.transport.server.security == SECURITY_SHARE &&
                                                ( session.auth.hashesExternal ||
                                                session.auth.password.length() > 0 )) {
-                                               
+
                        if( session.transport.server.encryptedPasswords ) {
                                // encrypted
                                password = session.auth.getAnsiHash( session.transport.server.encryptionKey );
-                               passwordLength = 24;
+                               passwordLength = password.length;
                        } else if( Config.getBoolean( "jcifs.smb.client.disablePlainTextPasswords", true )) {
                                throw new RuntimeException( "Plain text passwords are disabled" );
                        } else {
index d728bb6..c1219aa 100644 (file)
@@ -1,52 +1,52 @@
-/* jcifs smb client library in Java
- * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package jcifs.smb;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-class SmbComWriteAndXResponse extends AndXServerMessageBlock {
-
-       int count;
-
-       SmbComWriteAndXResponse() {
-       }
-
-       int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) {
-               return 0;
-       }
-       int writeBytesWireFormat( byte[] dst, int dstIndex ) {
-               return 0;
-       }
-       int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) {
-               count = readInt2( buffer, bufferIndex );
-               return 8;
-       }
-       int readBytesWireFormat( byte[] buffer, int bufferIndex ) {
-               return 0;
-       }
-       int readBytesDirectWireFormat( InputStream in, int byteCount ) throws IOException {
-               return 0;
-       }
-       public String toString() {
-               return new String( "SmbComWriteAndXResponse[" +
-                       super.toString() +
-                       ",count=" + count + "]" );
-       }
-}
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.smb;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+class SmbComWriteAndXResponse extends AndXServerMessageBlock {\r
+\r
+       long count;\r
+\r
+       SmbComWriteAndXResponse() {\r
+       }\r
+\r
+       int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) {\r
+               return 0;\r
+       }\r
+       int writeBytesWireFormat( byte[] dst, int dstIndex ) {\r
+               return 0;\r
+       }\r
+       int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) {\r
+               count = readInt2( buffer, bufferIndex ) & 0xFFFFL;\r
+               return 8;\r
+       }\r
+       int readBytesWireFormat( byte[] buffer, int bufferIndex ) {\r
+               return 0;\r
+       }\r
+       int readBytesDirectWireFormat( InputStream in, int byteCount ) throws IOException {\r
+               return 0;\r
+       }\r
+       public String toString() {\r
+               return new String( "SmbComWriteAndXResponse[" +\r
+                       super.toString() +\r
+                       ",count=" + count + "]" );\r
+       }\r
+}\r
index c53f127..e332cb2 100644 (file)
@@ -261,9 +261,21 @@ public class SmbFile extends URLConnection {
        static final int O_APPEND = 0x040000;
 
        // share access
+/**
+ * When specified as the <tt>shareAccess</tt> constructor parameter, other SMB clients (including other threads macking calls into jCIFS) will not be permitted to access the target file and will receive "The file is being accessed by another process" message.
+ */
        public static final int FILE_NO_SHARE     = 0x00;
+/**
+ * When specified as the <tt>shareAccess</tt> constructor parameter, other SMB clients will be permitted to read from the target file while this file is open. This constant may be logically OR'd with other share access flags.
+ */
        public static final int FILE_SHARE_READ   = 0x01;
+/**
+ * When specified as the <tt>shareAccess</tt> constructor parameter, other SMB clients will be permitted to write to the target file while this file is open. This constant may be logically OR'd with other share access flags.
+ */
        public static final int FILE_SHARE_WRITE  = 0x02;
+/**
+ * When specified as the <tt>shareAccess</tt> constructor parameter, other SMB clients will be permitted to delete the target file while this file is open. This constant may be logically OR'd with other share access flags.
+ */
        public static final int FILE_SHARE_DELETE = 0x04;
 
        // Open Function Encoding
@@ -391,10 +403,10 @@ public class SmbFile extends URLConnection {
  * the <code>parent</code>. See the description above for examples of
  * using the second <code>chile</code> parameter.
  *
- * @param   parent A URL string
- * @param   child A path string relative to the <code>parent</code> paremeter
+ * @param   context A URL string
+ * @param   name A path string relative to the <code>context</code> paremeter
  * @throws  MalformedURLException
- *          If the <code>parent</code> and <code>child</code> parameters
+ *          If the <code>context</code> and <code>name</code> parameters
  *          do not follow the prescribed syntax
  */
 
@@ -402,14 +414,54 @@ public class SmbFile extends URLConnection {
                this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ));
        }
 
+/**
+ * Constructs an SmbFile representing a resource on an SMB network such
+ * as a file or directory.
+The second parameter may be constructed explicitly or retreived with <tt>HttpServletRequest.getUserPrincipal()</tt> if NTLM HTTP authentication has been successfully negotiated.
+ *
+ * @param   url A URL string
+ * @param   auth The credentials the client should use for authentication
+ * @throws  MalformedURLException
+ *          If the <code>url</code> parameter does not follow the prescribed syntax
+ */
        public SmbFile( String url, NtlmPasswordAuthentication auth )
                                        throws MalformedURLException {
                this( new URL( null, url, Handler.SMB_HANDLER ), auth );
        }
+/**
+ * Constructs an SmbFile representing a resource on an SMB network such
+ * as a file or directory. The second parameter is a relative path from
+ * the <code>context</code>. See the description above for examples of
+ * using the second <code>name</code> parameter.
+The third parameter may be constructed explicitly or retreived with <tt>HttpServletRequest.getUserPrincipal()</tt> if NTLM HTTP authentication has been successfully negotiated.
+ *
+ * @param   context A URL string
+ * @param   name A path string relative to the <code>context</code> paremeter
+ * @param   auth The credentials the client should use for authentication
+ * @throws  MalformedURLException
+ *          If the <code>context</code> and <code>name</code> parameters
+ *          do not follow the prescribed syntax
+ */
        public SmbFile( String context, String name, NtlmPasswordAuthentication auth )
                                        throws MalformedURLException {
                this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
        }
+/**
+ * Constructs an SmbFile representing a resource on an SMB network such
+ * as a file or directory. The second parameter is a relative path from
+ * the <code>context</code>. See the description above for examples of
+ * using the second <code>name</code> parameter.
+The third parameter may be constructed explicitly or retreived with <tt>HttpServletRequest.getUserPrincipal()</tt> if NTLM HTTP authentication has been successfully negotiated.
+The <tt>shareAccess</tt> parameter controls what permissions other clients have when trying to access the same file while this instance is still open. This value is either <tt>FILE_NO_SHARE</tt> or any combination of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
+ *
+ * @param   context A URL string
+ * @param   name A path string relative to the <code>context</code> paremeter
+ * @param   auth The credentials the client should use for authentication
+ * @param   shareAccess Specifies what access other clients have while this file is open.
+ * @throws  MalformedURLException
+ *          If the <code>context</code> and <code>name</code> parameters
+ *          do not follow the prescribed syntax
+ */
        public SmbFile( String context, String name, NtlmPasswordAuthentication auth, int shareAccess )
                                        throws MalformedURLException {
                this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
@@ -418,9 +470,22 @@ public class SmbFile extends URLConnection {
                }
                this.shareAccess = shareAccess;
        }
+/**
+ * Constructs an SmbFile representing a resource on an SMB network such
+ * as a file or directory from a <tt>URL</tt> object.
+ *
+ * @param   url The URL of the target resource
+ */
        public SmbFile( URL url ) {
                this( url, new NtlmPasswordAuthentication( url.getUserInfo() ));
        }
+/**
+ * Constructs an SmbFile representing a resource on an SMB network such
+ * as a file or directory from a <tt>URL</tt> object and an <tt>NtlmPasswordAuthentication object which may be constructed explicitly or retreived with <tt>HttpServletRequest.getUserPrincipal()</tt> if NTLM HTTP authentication has been successfully negotiated.
+ *
+ * @param   url The URL of the target resource
+ * @param   auth The credentials the client should use for authentication
+ */
        public SmbFile( URL url, NtlmPasswordAuthentication auth ) {
                super( url );
                this.auth = auth == null ? new NtlmPasswordAuthentication( url.getUserInfo() ) : auth;
@@ -999,7 +1064,7 @@ public class SmbFile extends URLConnection {
                if( getUncPath0().length() == 1 ) {
                        return true;
                }
-               exists();
+               if (!exists()) return false;
                return ( attributes & ATTR_DIRECTORY ) == ATTR_DIRECTORY;
        }
 
@@ -1173,6 +1238,7 @@ public class SmbFile extends URLConnection {
                response.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2;
 
                while( response.isEndOfSearch == false && response.searchCount > 0 ) {
+                       response.reset();
                        sendTransaction( new Trans2FindNext2( sid, response.resumeKey,
                                                                                                        response.lastName ), response );
                        count += response.searchCount;
@@ -1414,6 +1480,7 @@ public class SmbFile extends URLConnection {
                        response.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2;
        
                        while( response.isEndOfSearch == false && response.searchCount > 0 ) {
+                               response.reset();
                                sendTransaction( new Trans2FindNext2( sid, response.resumeKey,
                                                                                                        response.lastName ), response );
                                count += response.searchCount;
@@ -1492,6 +1559,7 @@ public class SmbFile extends URLConnection {
                                ",newFileName=" + dest.unc );
 
                attrExpiration = sizeExpiration = 0;
+               dest.attrExpiration = 0;
 
                /*
                 * Rename Request / Response
index e0cf1a5..b0a04a4 100644 (file)
@@ -32,7 +32,7 @@ public class SmbFileInputStream extends InputStream {
 
        private SmbFile file;
        private long fp;
-       private int off, readSize, openFlags;
+       private int readSize, openFlags;
        private byte[] tmp = new byte[1];
 
 /**
@@ -120,8 +120,7 @@ public class SmbFileInputStream extends InputStream {
                // ensure file is open
                file.open( openFlags );
 
-               Log.println( Log.WARNINGS, "smb read warning",
-                               " fid=" + file.fid + ",off=" + off + ",len=" + len );
+//Log.println( Log.WARNINGS, "smb read warning", " fid=" + file.fid + ",off=" + off + ",len=" + len );
 
                /*
                 * Read AndX Request / Response
@@ -180,5 +179,18 @@ public class SmbFileInputStream extends InputStream {
                }
                return resp.available;
        }
+/**
+ * Skip n bytes of data on this stream. This operation will not result
+ * in any IO with the server. Unlink <tt>InputStream</tt> value less than
+ * the one provided will not be returned if it exceeds the end of the file
+ * (if this is a problem let us know).
+ */
+       public long skip( long n ) throws IOException {
+               if (n > 0) {
+                       fp += n;
+                       return n;
+               }
+               return 0;
+       }
 }
 
index 5f2d285..72a8dc4 100644 (file)
-/* jcifs smb client library in Java
- * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package jcifs.smb;
-
-import java.net.URL;
-import java.io.OutputStream;
-import java.io.IOException;
-import java.net.UnknownHostException;
-import java.net.MalformedURLException;
-
-/**
- * This <code>OutputStream</code> can write bytes to a file on an SMB file server.
- */
-
-public class SmbFileOutputStream extends OutputStream {
-
-       private SmbFile file;
-       private boolean append;
-       private int openFlags, writeSize;
-       private long fp;
-       private byte[] tmp = new byte[1];
-
-/**
- * Creates an {@link java.io.OutputStream} for writing to a file
- * on an SMB server addressed by the URL parameter. See {@link
- * jcifs.smb.SmbFile} for a detailed description and examples of
- * the smb URL syntax.
- *
- * @param url An smb URL string representing the file to write to
- * @return A new <code>OutputStream</code> for the specified file
- */
-
-       public SmbFileOutputStream( String url ) throws SmbException, MalformedURLException, UnknownHostException {
-               this( url, false );
-       }
-
-/**
- * Creates an {@link java.io.OutputStream} for writing bytes to a file on
- * an SMB server represented by the {@link jcifs.smb.SmbFile} parameter. See
- * {@link jcifs.smb.SmbFile} for a detailed description and examples of
- * the smb URL syntax.
- *
- * @param url An <code>SmbFile</code> specifying the file to write to
- * @return A new <code>OutputStream</code> for the specified <code>SmbFile</code>
- */
-
-       public SmbFileOutputStream( SmbFile file ) throws SmbException, MalformedURLException, UnknownHostException {
-               this( file, false );
-       }
-
-/**
- * Creates an {@link java.io.OutputStream} for writing bytes to a file on an
- * SMB server addressed by the URL parameter. See {@link jcifs.smb.SmbFile}
- * for a detailed description and examples of the smb URL syntax. If the
- * second argument is <code>true</code>, then bytes will be written to the
- * end of the file rather than the beginning.
- *
- * @param url An smb URL string representing the file to write to
- * @return A new <code>OutputStream</code> for the specified <code>url</code>
- */
-
-       public SmbFileOutputStream( String url, boolean append ) throws SmbException, MalformedURLException, UnknownHostException {
-               this( new SmbFile( url ), append );
-       }
-
-/**
- * Creates an {@link java.io.OutputStream} for writing bytes to a file
- * on an SMB server addressed by the <code>SmbFile</code> parameter. See
- * {@link jcifs.smb.SmbFile} for a detailed description and examples of
- * the smb URL syntax. If the second argument is <code>true</code>, then
- * bytes will be written to the end of the file rather than the beginning.
- * 
- * @param url An <code>SmbFile</code> representing the file to write to
- * @return A new <code>OutputStream</code> for the specified <code>SmbFile</code>
- */
-
-       public SmbFileOutputStream( SmbFile file, boolean append ) throws SmbException, MalformedURLException, UnknownHostException {
-               this( file, append, append ? SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_APPEND :
-                                                                       SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC );
-       }
-/**
- * Creates an {@link java.io.OutputStream} for writing bytes to a file
- * on an SMB server addressed by the <code>SmbFile</code> parameter. See
- * {@link jcifs.smb.SmbFile} for a detailed description and examples of
- * the smb URL syntax.
-<p>
-The second parameter specifies how the file should be shared. If
-<code>SmbFile.FILE_NO_SHARE</code> is specified the client will
-have exclusive access to the file. An additional open command
-from jCIFS or another application will fail with the "file is being
-accessed by another process" error. The <code>FILE_SHARE_READ</code>,
-<code>FILE_SHARE_WRITE</code>, and <code>FILE_SHARE_DELETE</code> may be
-combined with the bitwise OR '|' to specify that other peocesses may read,
-write, and/or delete the file while the jCIFS user has the file open.
- * 
- * @param url An <code>SmbFile</code> representing the file to write to
- * @return A new <code>OutputStream</code> for the specified <code>SmbFile</code>
- */
-
-       public SmbFileOutputStream( String url, int shareAccess ) throws SmbException, MalformedURLException, UnknownHostException {
-               this( new SmbFile( url, "", null, shareAccess ), false );
-       }
-
-       SmbFileOutputStream( SmbFile file, boolean append, int openFlags ) throws SmbException, MalformedURLException, UnknownHostException {
-               this.file = file;
-               this.append = append;
-               this.openFlags = openFlags;
-               if( append ) {
-                       try {
-                               fp = file.length();
-                       } catch( SmbException se ) {
-                               fp = 0L;
-                       }
-               }
-               file.open( openFlags );
-               writeSize = Math.min( file.tree.session.transport.snd_buf_size - 70,
-                                                       file.tree.session.transport.server.maxBufferSize - 70 );
-       }
-
-/**
- * Closes this output stream and releases any system resources associated
- * with it.
- *
- * @throws IOException if a network error occurs
- */
-
-       public void close() throws IOException {
-               file.close();
-       }
-
-/**
- * Writes the specified byte to this file output stream.
- *
- * @throws IOException if a network error occurs
- */
-
-       public void write( int b ) throws IOException {
-               tmp[0] = (byte)b;
-               write( tmp, 0, 1 );
-       }
-
-/**
- * Writes b.length bytes from the specified byte array to this
- * file output stream.
- *
- * @throws IOException if a network error occurs
- */
-
-       public void write( byte[] b ) throws IOException {
-               write( b, 0, b.length );
-       }
-
-/**
- * Writes len bytes from the specified byte array starting at
- * offset off to this file output stream.
- *
- * @param b The array 
- * @throws IOException if a network error occurs
- */
-
-       public void write( byte[] b, int off, int len ) throws IOException {
-               if( len <= 0 ) {
-                       return;
-               }
-
-               // ensure file is open
-               if( file.isOpen() == false ) {
-                       file.open( openFlags );
-                       if( append ) {
-                               fp = file.length();
-                       }
-               }
-
-/*
-               Log.println( Log.WARNINGS, "smb write warning",
-                                               " fid=" + file.fid + ",off=" + off + ",len=" + len );
-*/
-               int w;
-               do {
-                       w = len > writeSize ? writeSize : len;
-                       file.send( new SmbComWriteAndX( file.fid, fp, len - w, b, off, w, null ),
-                                                                                                               new SmbComWriteAndXResponse() );
-                       fp += w;
-                       len -= w;
-                       off += w;
-               } while( len > 0 );
-       }
-}
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.smb;\r
+\r
+import java.net.URL;\r
+import java.io.OutputStream;\r
+import java.io.IOException;\r
+import java.net.UnknownHostException;\r
+import java.net.MalformedURLException;\r
+\r
+/**\r
+ * This <code>OutputStream</code> can write bytes to a file on an SMB file server.\r
+ */\r
+\r
+public class SmbFileOutputStream extends OutputStream {\r
+\r
+       private SmbFile file;\r
+       private boolean append;\r
+       private int openFlags, writeSize;\r
+       private long fp;\r
+       private byte[] tmp = new byte[1];\r
+\r
+/**\r
+ * Creates an {@link java.io.OutputStream} for writing to a file\r
+ * on an SMB server addressed by the URL parameter. See {@link\r
+ * jcifs.smb.SmbFile} for a detailed description and examples of\r
+ * the smb URL syntax.\r
+ *\r
+ * @param url An smb URL string representing the file to write to\r
+ * @return A new <code>OutputStream</code> for the specified file\r
+ */\r
+\r
+       public SmbFileOutputStream( String url ) throws SmbException, MalformedURLException, UnknownHostException {\r
+               this( url, false );\r
+       }\r
+\r
+/**\r
+ * Creates an {@link java.io.OutputStream} for writing bytes to a file on\r
+ * an SMB server represented by the {@link jcifs.smb.SmbFile} parameter. See\r
+ * {@link jcifs.smb.SmbFile} for a detailed description and examples of\r
+ * the smb URL syntax.\r
+ *\r
+ * @param url An <code>SmbFile</code> specifying the file to write to\r
+ * @return A new <code>OutputStream</code> for the specified <code>SmbFile</code>\r
+ */\r
+\r
+       public SmbFileOutputStream( SmbFile file ) throws SmbException, MalformedURLException, UnknownHostException {\r
+               this( file, false );\r
+       }\r
+\r
+/**\r
+ * Creates an {@link java.io.OutputStream} for writing bytes to a file on an\r
+ * SMB server addressed by the URL parameter. See {@link jcifs.smb.SmbFile}\r
+ * for a detailed description and examples of the smb URL syntax. If the\r
+ * second argument is <code>true</code>, then bytes will be written to the\r
+ * end of the file rather than the beginning.\r
+ *\r
+ * @param url An smb URL string representing the file to write to\r
+ * @return A new <code>OutputStream</code> for the specified <code>url</code>\r
+ */\r
+\r
+       public SmbFileOutputStream( String url, boolean append ) throws SmbException, MalformedURLException, UnknownHostException {\r
+               this( new SmbFile( url ), append );\r
+       }\r
+\r
+/**\r
+ * Creates an {@link java.io.OutputStream} for writing bytes to a file\r
+ * on an SMB server addressed by the <code>SmbFile</code> parameter. See\r
+ * {@link jcifs.smb.SmbFile} for a detailed description and examples of\r
+ * the smb URL syntax. If the second argument is <code>true</code>, then\r
+ * bytes will be written to the end of the file rather than the beginning.\r
+ * \r
+ * @param url An <code>SmbFile</code> representing the file to write to\r
+ * @return A new <code>OutputStream</code> for the specified <code>SmbFile</code>\r
+ */\r
+\r
+       public SmbFileOutputStream( SmbFile file, boolean append ) throws SmbException, MalformedURLException, UnknownHostException {\r
+               this( file, append, append ? SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_APPEND :\r
+                                                                       SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC );\r
+       }\r
+/**\r
+ * Creates an {@link java.io.OutputStream} for writing bytes to a file\r
+ * on an SMB server addressed by the <code>SmbFile</code> parameter. See\r
+ * {@link jcifs.smb.SmbFile} for a detailed description and examples of\r
+ * the smb URL syntax.\r
+<p>\r
+The second parameter specifies how the file should be shared. If\r
+<code>SmbFile.FILE_NO_SHARE</code> is specified the client will\r
+have exclusive access to the file. An additional open command\r
+from jCIFS or another application will fail with the "file is being\r
+accessed by another process" error. The <code>FILE_SHARE_READ</code>,\r
+<code>FILE_SHARE_WRITE</code>, and <code>FILE_SHARE_DELETE</code> may be\r
+combined with the bitwise OR '|' to specify that other peocesses may read,\r
+write, and/or delete the file while the jCIFS user has the file open.\r
+ * \r
+ * @param url An <code>SmbFile</code> representing the file to write to\r
+ * @return A new <code>OutputStream</code> for the specified <code>SmbFile</code>\r
+ */\r
+\r
+       public SmbFileOutputStream( String url, int shareAccess ) throws SmbException, MalformedURLException, UnknownHostException {\r
+               this( new SmbFile( url, "", null, shareAccess ), false );\r
+       }\r
+\r
+       SmbFileOutputStream( SmbFile file, boolean append, int openFlags ) throws SmbException, MalformedURLException, UnknownHostException {\r
+               this.file = file;\r
+               this.append = append;\r
+               this.openFlags = openFlags;\r
+               if( append ) {\r
+                       try {\r
+                               fp = file.length();\r
+                       } catch( SmbException se ) {\r
+                               fp = 0L;\r
+                       }\r
+               }\r
+               file.open( openFlags );\r
+               writeSize = Math.min( file.tree.session.transport.snd_buf_size - 70,\r
+                                                       file.tree.session.transport.server.maxBufferSize - 70 );\r
+       }\r
+\r
+/**\r
+ * Closes this output stream and releases any system resources associated\r
+ * with it.\r
+ *\r
+ * @throws IOException if a network error occurs\r
+ */\r
+\r
+       public void close() throws IOException {\r
+               file.close();\r
+       }\r
+\r
+/**\r
+ * Writes the specified byte to this file output stream.\r
+ *\r
+ * @throws IOException if a network error occurs\r
+ */\r
+\r
+       public void write( int b ) throws IOException {\r
+               tmp[0] = (byte)b;\r
+               write( tmp, 0, 1 );\r
+       }\r
+\r
+/**\r
+ * Writes b.length bytes from the specified byte array to this\r
+ * file output stream.\r
+ *\r
+ * @throws IOException if a network error occurs\r
+ */\r
+\r
+       public void write( byte[] b ) throws IOException {\r
+               write( b, 0, b.length );\r
+       }\r
+\r
+/**\r
+ * Writes len bytes from the specified byte array starting at\r
+ * offset off to this file output stream.\r
+ *\r
+ * @param b The array \r
+ * @throws IOException if a network error occurs\r
+ */\r
+\r
+       public void write( byte[] b, int off, int len ) throws IOException {\r
+               if( len <= 0 ) {\r
+                       return;\r
+               }\r
+\r
+               // ensure file is open\r
+               if( file.isOpen() == false ) {\r
+                       file.open( openFlags );\r
+                       if( append ) {\r
+                               fp = file.length();\r
+                       }\r
+               }\r
+\r
+/*\r
+               Log.println( Log.WARNINGS, "smb write warning",\r
+                                               " fid=" + file.fid + ",off=" + off + ",len=" + len );\r
+*/\r
+               int w;\r
+               SmbComWriteAndXResponse rsp = new SmbComWriteAndXResponse();\r
+               do {\r
+                       w = len > writeSize ? writeSize : len;\r
+                       file.send( new SmbComWriteAndX( file.fid, fp, len - w, b, off, w, null ), rsp );\r
+                       fp += rsp.count;\r
+                       len -= rsp.count;\r
+                       off += rsp.count;\r
+               } while( len > 0 );\r
+       }\r
+}\r
index 9981daf..a9fa6d5 100644 (file)
@@ -106,8 +106,9 @@ public final class SmbSession {
                request.uid = uid;
                transport.send( request, response );
        }
-       synchronized void sessionSetup( ServerMessageBlock andx,
+       void sessionSetup( ServerMessageBlock andx,
                                                        ServerMessageBlock andxResponse ) throws SmbException {
+synchronized( transport ) {
                SmbComSessionSetupAndXResponse response = null;
 
                if( sessionSetup ) {
@@ -127,8 +128,10 @@ public final class SmbSession {
 
                uid = response.uid;
                sessionSetup = true;
+}
        }
-       synchronized void logoff( boolean inError ) {
+       void logoff( boolean inError ) {
+synchronized( transport ) {
                if( sessionSetup == false ) {
                        return;
                }
@@ -156,6 +159,7 @@ public final class SmbSession {
                        }
                }
                sessionSetup = false;
+}
        }
        public String toString() {
                return "SmbSession[accountName=" + auth.username +
index cf47e66..af7f2db 100644 (file)
@@ -348,7 +348,7 @@ synchronized( rcv_buf ) {
                                        }
                                        synchronized( response ) {
                                                response.useUnicode = useUnicode;
-                                               Log.println( Log.WARNINGS, "smb transport warning",
+                                               Log.println( Log.DEBUGGING, "smb transport warning",
                                                                                                                " new data read from socket" );
 
                                                if( response instanceof SmbComTransactionResponse ) {
@@ -507,6 +507,7 @@ synchronized( snd_buf ) {
                        default:
                                throw new SmbException( response.errorCode );
                }
+
        }
        void sendTransaction( SmbComTransaction request,
                                                        SmbComTransactionResponse response ) throws SmbException {
index 63f1179..de27bab 100644 (file)
@@ -94,15 +94,7 @@ class SmbTree {
        void treeConnect( ServerMessageBlock andx,
                                                        ServerMessageBlock andxResponse ) throws SmbException {
                String unc;
-/* 
- * We must lock the session and *then* this tree because there is a small
- * possibilty that the session could be trying to disconnect the tree at
- * the same time. In this case it will have the session locked and wait
- * indefinately trying to acquire the tree which would be locked trying to
- * aquire the session resulting in deadlock.
- */ 
-synchronized( session ) {
-       synchronized( this ) {
+synchronized( session.transport ) {
 
                if( treeConnected ) {
                        return;
@@ -134,10 +126,10 @@ synchronized( session ) {
                tid = response.tid;
                service = response.service;
                treeConnected = true;
-       }       
 }
        }
-       synchronized void treeDisconnect( boolean inError ) {
+       void treeDisconnect( boolean inError ) {
+synchronized( session.transport ) {
                if( treeConnected == false ) {
                        return;
                }
@@ -148,6 +140,7 @@ synchronized( session ) {
                        }
                }
                treeConnected = false;
+}
        }
 
        public String toString() {
index 297604d..a539146 100644 (file)
-package jcifs.util;\r
-\r
-import java.io.UnsupportedEncodingException;\r
-\r
-/**\r
- * Encodes and decodes to and from Base64 notation.\r
- *\r
- * <p>\r
- * Change Log:\r
- * </p>\r
- * <ul>\r
- *  <li>Trimmed object and stream releated methods for inclusion into jcifs.util package.\r
- *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream\r
- *      where last buffer being read, if not completely full, was not returned.</li>\r
- *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>\r
- *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>\r
- * </ul>\r
- *\r
- * <p>\r
- * I am placing this code in the Public Domain. Do with it as you will.\r
- * This software comes with no guarantees or warranties but with\r
- * plenty of well-wishing instead!\r
- * Please visit <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>\r
- * periodically to check for updates or to contribute improvements.\r
- * </p>\r
- *\r
- * @author Robert Harder\r
- * @author rob@iharder.net\r
- * @version 1.3.4\r
- */\r
-public class Base64\r
-{\r
-    \r
-    /** Specify encoding (value is <tt>true</tt>). */\r
-    public final static boolean ENCODE = true;\r
-    \r
-    \r
-    /** Specify decoding (value is <tt>false</tt>). */\r
-    public final static boolean DECODE = false;\r
-    \r
-    \r
-    /** Maximum line length (76) of Base64 output. */\r
-    private final static int MAX_LINE_LENGTH = 76;\r
-    \r
-    \r
-    /** The equals sign (=) as a byte. */\r
-    private final static byte EQUALS_SIGN = (byte)'=';\r
-    \r
-    \r
-    /** The new line character (\n) as a byte. */\r
-    private final static byte NEW_LINE = (byte)'\n';\r
-    \r
-    \r
-    /** The 64 valid Base64 values. */\r
-    private final static byte[] ALPHABET =\r
-    {\r
-        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',\r
-        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',\r
-        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', \r
-        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',\r
-        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',\r
-        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',\r
-        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', \r
-        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',\r
-        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', \r
-        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'\r
-    };\r
-    \r
-    /** \r
-     * Translates a Base64 value to either its 6-bit reconstruction value\r
-     * or a negative number indicating some other meaning.\r
-     **/\r
-    private final static byte[] DECODABET =\r
-    {   \r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8\r
-        -5,-5,                                      // Whitespace: Tab and Linefeed\r
-        -9,-9,                                      // Decimal 11 - 12\r
-        -5,                                         // Whitespace: Carriage Return\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26\r
-        -9,-9,-9,-9,-9,                             // Decimal 27 - 31\r
-        -5,                                         // Whitespace: Space\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42\r
-        62,                                         // Plus sign at decimal 43\r
-        -9,-9,-9,                                   // Decimal 44 - 46\r
-        63,                                         // Slash at decimal 47\r
-        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine\r
-        -9,-9,-9,                                   // Decimal 58 - 60\r
-        -1,                                         // Equals sign at decimal 61\r
-        -9,-9,-9,                                      // Decimal 62 - 64\r
-        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'\r
-        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'\r
-        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96\r
-        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'\r
-        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'\r
-        -9,-9,-9,-9                                 // Decimal 123 - 126\r
-        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243\r
-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */\r
-    };\r
-    \r
-    private final static byte BAD_ENCODING    = -9; // Indicates error in encoding\r
-    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding\r
-    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding\r
-\r
-    \r
-    /** Defeats instantiation. */\r
-    private Base64(){}\r
-    \r
-/* ********  E N C O D I N G   M E T H O D S  ******** */    \r
-    \r
-    \r
-    /**\r
-     * Encodes the first three bytes of array <var>threeBytes</var>\r
-     * and returns a four-byte array in Base64 notation.\r
-     *\r
-     * @param threeBytes the array to convert\r
-     * @return four byte array in Base64 notation.\r
-     * @since 1.3\r
-     */\r
-    private static byte[] encode3to4( byte[] threeBytes )\r
-    {   return encode3to4( threeBytes, 3 );\r
-    }   // end encodeToBytes\r
-    \r
-    \r
-    \r
-    /**\r
-     * Encodes up to the first three bytes of array <var>threeBytes</var>\r
-     * and returns a four-byte array in Base64 notation.\r
-     * The actual number of significant bytes in your array is\r
-     * given by <var>numSigBytes</var>.\r
-     * The array <var>threeBytes</var> needs only be as big as\r
-     * <var>numSigBytes</var>.\r
-     *\r
-     * @param threeBytes the array to convert\r
-     * @param numSigBytes the number of significant bytes in your array\r
-     * @return four byte array in Base64 notation.\r
-     * @since 1.3\r
-     */\r
-    private static byte[] encode3to4( byte[] threeBytes, int numSigBytes )\r
-    {   byte[] dest = new byte[4];\r
-        encode3to4( threeBytes, 0, numSigBytes, dest, 0 );\r
-        return dest;\r
-    }\r
-    \r
-    \r
-    \r
-    /**\r
-     * Encodes up to three bytes of the array <var>source</var>\r
-     * and writes the resulting four Base64 bytes to <var>destination</var>.\r
-     * The source and destination arrays can be manipulated\r
-     * anywhere along their length by specifying \r
-     * <var>srcOffset</var> and <var>destOffset</var>.\r
-     * This method does not check to make sure your arrays\r
-     * are large enough to accomodate <var>srcOffset</var> + 3 for\r
-     * the <var>source</var> array or <var>destOffset</var> + 4 for\r
-     * the <var>destination</var> array.\r
-     * The actual number of significant bytes in your array is\r
-     * given by <var>numSigBytes</var>.\r
-     *\r
-     * @param source the array to convert\r
-     * @param srcOffset the index where conversion begins\r
-     * @param numSigBytes the number of significant bytes in your array\r
-     * @param destination the array to hold the conversion\r
-     * @param destOffset the index where output will be put\r
-     * @return the <var>destination</var> array\r
-     * @since 1.3\r
-     */\r
-    private static byte[] encode3to4( \r
-     byte[] source, int srcOffset, int numSigBytes,\r
-     byte[] destination, int destOffset )\r
-    {\r
-        //           1         2         3  \r
-        // 01234567890123456789012345678901 Bit position\r
-        // --------000000001111111122222222 Array position from threeBytes\r
-        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET\r
-        //          >>18  >>12  >> 6  >> 0  Right shift necessary\r
-        //                0x3f  0x3f  0x3f  Additional AND\r
-        \r
-        // Create buffer with zero-padding if there are only one or two\r
-        // significant bytes passed in the array.\r
-        // We have to shift left 24 in order to flush out the 1's that appear\r
-        // when Java treats a value as negative that is cast from a byte to an int.\r
-        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )\r
-                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )\r
-                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );\r
-\r
-        switch( numSigBytes )\r
-        {\r
-            case 3:\r
-                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];\r
-                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];\r
-                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];\r
-                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];\r
-                return destination;\r
-                \r
-            case 2:\r
-                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];\r
-                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];\r
-                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];\r
-                destination[ destOffset + 3 ] = EQUALS_SIGN;\r
-                return destination;\r
-                \r
-            case 1:\r
-                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];\r
-                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];\r
-                destination[ destOffset + 2 ] = EQUALS_SIGN;\r
-                destination[ destOffset + 3 ] = EQUALS_SIGN;\r
-                return destination;\r
-                \r
-            default:\r
-                return destination;\r
-        }   // end switch\r
-    }   // end encode3to4\r
-    \r
-    /**\r
-     * Encodes a byte array into Base64 notation.\r
-     * Equivalen to calling\r
-     * <code>encodeBytes( source, 0, source.length )</code>\r
-     *\r
-     * @param source The data to convert\r
-     * @since 1.4\r
-     */\r
-    public static String encodeBytes( byte[] source )\r
-    {\r
-        return encodeBytes( source, true );\r
-    }   // end encodeBytes\r
-    \r
-    /**\r
-     * Encodes a byte array into Base64 notation.\r
-     * Equivalen to calling\r
-     * <code>encodeBytes( source, 0, source.length )</code>\r
-     *\r
-     * @param source The data to convert\r
-     * @param breakLines Break lines at 80 characters or less.\r
-     * @since 1.4\r
-     */\r
-    public static String encodeBytes( byte[] source, boolean breakLines )\r
-    {   \r
-        return encodeBytes( source, 0, source.length, breakLines );\r
-    }   // end encodeBytes\r
-    \r
-    \r
-    /**\r
-     * Encodes a byte array into Base64 notation.\r
-     *\r
-     * @param source The data to convert\r
-     * @param off Offset in array where conversion should begin\r
-     * @param len Length of data to convert\r
-     * @since 1.4\r
-     */\r
-    public static String encodeBytes( byte[] source, int off, int len )\r
-    {\r
-        return encodeBytes( source, off, len, true );\r
-    }   // end encodeBytes\r
-    \r
-    \r
-    /**\r
-     * Encodes a byte array into Base64 notation.\r
-     *\r
-     * @param source The data to convert\r
-     * @param off Offset in array where conversion should begin\r
-     * @param len Length of data to convert\r
-     * @param breakLines Break lines at 80 characters or less.\r
-     * @since 1.4\r
-     */\r
-    public static String encodeBytes( byte[] source, int off, int len, boolean breakLines )\r
-    {\r
-        int    len43   = len * 4 / 3;\r
-        byte[] outBuff = new byte[   ( len43 )                      // Main 4:3\r
-                                   + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding\r
-                                   + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      \r
-        int d = 0;\r
-        int e = 0;\r
-        int len2 = len - 2;\r
-        int lineLength = 0;\r
-        for( ; d < len2; d+=3, e+=4 )\r
-        {\r
-            encode3to4( source, d+off, 3, outBuff, e );\r
-            \r
-            lineLength += 4;\r
-            if( breakLines && lineLength == MAX_LINE_LENGTH )\r
-            {   \r
-                outBuff[e+4] = NEW_LINE;\r
-                e++;\r
-                lineLength = 0;\r
-            }   // end if: end of line\r
-        }   // en dfor: each piece of array\r
-        \r
-        if( d < len )\r
-        {\r
-            encode3to4( source, d+off, len - d, outBuff, e );\r
-            e += 4;\r
-        }   // end if: some padding needed\r
-        \r
-               try {\r
-               return new String( outBuff, 0, e, "ASCII" );\r
-               } catch( UnsupportedEncodingException uee ) {\r
-               }\r
-               return null;\r
-    }   // end encodeBytes\r
-    \r
-    \r
-    /**\r
-     * Encodes a string in Base64 notation with line breaks\r
-     * after every 75 Base64 characters.\r
-     *\r
-     * @param s the string to encode\r
-     * @return the encoded string\r
-     * @since 1.3\r
-     */\r
-    public static String encodeString( String s )\r
-    {\r
-        return encodeString( s, true );\r
-    }   // end encodeString\r
-    \r
-    /**\r
-     * Encodes a string in Base64 notation with line breaks\r
-     * after every 75 Base64 characters.\r
-     *\r
-     * @param s the string to encode\r
-     * @param breakLines Break lines at 80 characters or less.\r
-     * @return the encoded string\r
-     * @since 1.3\r
-     */\r
-    public static String encodeString( String s, boolean breakLines )\r
-    {   \r
-               try {\r
-               return encodeBytes( s.getBytes( "ASCII" ), breakLines );\r
-               } catch( UnsupportedEncodingException uee ) {\r
-               }\r
-               return null;\r
-    }   // end encodeString\r
-    \r
-    \r
-    \r
-    \r
-/* ********  D E C O D I N G   M E T H O D S  ******** */\r
-    \r
-    \r
-    /**\r
-     * Decodes the first four bytes of array <var>fourBytes</var>\r
-     * and returns an array up to three bytes long with the\r
-     * decoded values.\r
-     *\r
-     * @param fourBytes the array with Base64 content\r
-     * @return array with decoded values\r
-     * @since 1.3\r
-     */\r
-    private static byte[] decode4to3( byte[] fourBytes )\r
-    {\r
-        byte[] outBuff1 = new byte[3];\r
-        int    count    = decode4to3( fourBytes, 0, outBuff1, 0 );\r
-        byte[] outBuff2 = new byte[ count ];\r
-        \r
-        for( int i = 0; i < count; i++ )\r
-            outBuff2[i] = outBuff1[i];\r
-        \r
-        return outBuff2;\r
-    }\r
-    \r
-    \r
-    \r
-    \r
-    /**\r
-     * Decodes four bytes from array <var>source</var>\r
-     * and writes the resulting bytes (up to three of them)\r
-     * to <var>destination</var>.\r
-     * The source and destination arrays can be manipulated\r
-     * anywhere along their length by specifying \r
-     * <var>srcOffset</var> and <var>destOffset</var>.\r
-     * This method does not check to make sure your arrays\r
-     * are large enough to accomodate <var>srcOffset</var> + 4 for\r
-     * the <var>source</var> array or <var>destOffset</var> + 3 for\r
-     * the <var>destination</var> array.\r
-     * This method returns the actual number of bytes that \r
-     * were converted from the Base64 encoding.\r
-     * \r
-     *\r
-     * @param source the array to convert\r
-     * @param srcOffset the index where conversion begins\r
-     * @param destination the array to hold the conversion\r
-     * @param destOffset the index where output will be put\r
-     * @return the number of decoded bytes converted\r
-     * @since 1.3\r
-     */\r
-    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )\r
-    {\r
-        // Example: Dk==\r
-        if( source[ srcOffset + 2] == EQUALS_SIGN )\r
-        {\r
-            // Two ways to do the same thing. Don't know which way I like best.\r
-            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )\r
-            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );\r
-            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )\r
-                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );\r
-            \r
-            destination[ destOffset ] = (byte)( outBuff >>> 16 );\r
-            return 1;\r
-        }\r
-        \r
-        // Example: DkL=\r
-        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )\r
-        {\r
-            // Two ways to do the same thing. Don't know which way I like best.\r
-            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )\r
-            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )\r
-            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );\r
-            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )\r
-                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )\r
-                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );\r
-            \r
-            destination[ destOffset     ] = (byte)( outBuff >>> 16 );\r
-            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );\r
-            return 2;\r
-        }\r
-        \r
-        // Example: DkLE\r
-        else\r
-        {\r
-            try{\r
-            // Two ways to do the same thing. Don't know which way I like best.\r
-            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )\r
-            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )\r
-            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )\r
-            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );\r
-            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )\r
-                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )\r
-                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)\r
-                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );\r
-\r
-            \r
-            destination[ destOffset     ] = (byte)( outBuff >> 16 );\r
-            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );\r
-            destination[ destOffset + 2 ] = (byte)( outBuff       );\r
-\r
-            return 3;\r
-            }catch( Exception e){\r
-                jcifs.util.Log.println( jcifs.util.Log.WARNINGS, "Base64", ""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );\r
-                jcifs.util.Log.println( jcifs.util.Log.WARNINGS, "Base64", ""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );\r
-                jcifs.util.Log.println( jcifs.util.Log.WARNINGS, "Base64", ""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );\r
-                jcifs.util.Log.println( jcifs.util.Log.WARNINGS, "Base64", ""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );\r
-                return -1;\r
-            }   //e nd catch\r
-        }\r
-    }   // end decodeToBytes\r
-    \r
-    \r
-    \r
-    /**\r
-     * Decodes data from Base64 notation.\r
-     *\r
-     * @param s the string to decode\r
-     * @return the decoded data\r
-     * @since 1.4\r
-     */\r
-    public static byte[] decode( String s )\r
-    {   \r
-               try {\r
-               byte[] bytes = s.getBytes( "ASCII" );\r
-               return decode( bytes, 0, bytes.length );\r
-               } catch( UnsupportedEncodingException uee ) {\r
-               }\r
-               return null;\r
-    }   // end decode\r
-    \r
-    \r
-    /**\r
-     * Decodes data from Base64 notation and\r
-     * returns it as a string.\r
-     * Equivlaent to calling\r
-     * <code>new String( decode( s ) )</code>\r
-     *\r
-     * @param s the strind to decode\r
-     * @return The data as a string\r
-     * @since 1.4\r
-     */\r
-    public static String decodeToString( String s )\r
-    {\r
-               try {\r
-                       return new String( decode( s ), "ISO8859_1" );\r
-               } catch( UnsupportedEncodingException uee ) {\r
-               }\r
-               return null;\r
-    }   // end decodeToString\r
-\r
-\r
-    /**\r
-     * Decodes Base64 content in byte array format and returns\r
-     * the decoded byte array.\r
-     *\r
-     * @param source The Base64 encoded data\r
-     * @param off    The offset of where to begin decoding\r
-     * @param len    The length of characters to decode\r
-     * @return decoded data\r
-     * @since 1.3\r
-     */\r
-    public static byte[] decode( byte[] source, int off, int len )\r
-    {\r
-        int    len34   = len * 3 / 4;\r
-        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output\r
-        int    outBuffPosn = 0;\r
-        \r
-        byte[] b4        = new byte[4];\r
-        int    b4Posn    = 0;\r
-        int    i         = 0;\r
-        byte   sbiCrop   = 0;\r
-        byte   sbiDecode = 0;\r
-        for( i = 0; i < len; i++ )\r
-        {\r
-            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits\r
-            sbiDecode = DECODABET[ sbiCrop ];\r
-            \r
-            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better\r
-            {\r
-                if( sbiDecode >= EQUALS_SIGN_ENC )\r
-                {\r
-                    b4[ b4Posn++ ] = sbiCrop;\r
-                    if( b4Posn > 3 )\r
-                    {\r
-                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );\r
-                        b4Posn = 0;\r
-                        \r
-                        // If that was the equals sign, break out of 'for' loop\r
-                        if( sbiCrop == EQUALS_SIGN )\r
-                            break;\r
-                    }   // end if: quartet built\r
-                    \r
-                }   // end if: equals sign or better\r
-                \r
-            }   // end if: white space, equals sign or better\r
-            else\r
-            {\r
-                System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );\r
-                return null;\r
-            }   // end else: \r
-        }   // each input character\r
-                                   \r
-        byte[] out = new byte[ outBuffPosn ];\r
-        System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); \r
-        return out;\r
-    }   // end decode\r
-}   // end class Base64\r
+/* Encodes and decodes to and from Base64 notation.
+ * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.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 <code>String</code> 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 <code>byte[]</code> 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/src/jcifs/util/HMACT64.java b/src/jcifs/util/HMACT64.java
new file mode 100644 (file)
index 0000000..11c6d38
--- /dev/null
@@ -0,0 +1,116 @@
+/* HMACT64 keyed hashing algorithm
+ * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package jcifs.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/src/jcifs/util/URLDecoder.jav b/src/jcifs/util/URLDecoder.jav
deleted file mode 100644 (file)
index 3c4e719..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * @(#)URLDecoder.java 1.9 00/02/02
- *
- * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved.
- * 
- * This software is the proprietary information of Sun Microsystems, Inc.  
- * Use is subject to license terms.
- * 
- */
-
-package jcifs.util;
-
-import java.io.*;
-
-/**
- * The class contains a utility method for converting from
- * a MIME format called "<code>x-www-form-urlencoded</code>"
- * to a <code>String</code>
- * <p>
- * To convert to a <code>String</code>, each character is examined in turn:
- * <ul>
- * <li>The ASCII characters '<code>a</code>' through '<code>z</code>',
- * '<code>A</code>' through '<code>Z</code>', and '<code>0</code>'
- * through '<code>9</code>' remain the same.
- * <li>The plus sign '<code>+</code>'is converted into a
- * space character '<code>&nbsp;</code>'.
- * <li>The remaining characters are represented by 3-character
- * strings which begin with the percent sign,
- * "<code>%<i>xy</i></code>", where <i>xy</i> is the two-digit
- * hexadecimal representation of the lower 8-bits of the character.
- * </ul>
- *
- * @author  Mark Chamness
- * @author  Michael McCloskey
- * @version 1.9, 02/02/00
- * @since   1.2
- */
-
-public class URLDecoder {
-
-/**
- * Decodes a &quot;x-www-form-urlencoded&quot; 
- * to a <tt>String</tt>.
- * @param s the <code>String</code> to decode
- * @return the newly decoded <code>String</code>
- */
-    public static String decode(String s) {
-        StringBuffer sb = new StringBuffer();
-        for(int i=0; i<s.length(); i++) {
-            char c = s.charAt(i);
-            switch (c) {
-                case '+':
-                    sb.append(' ');
-                    break;
-                case '%':
-                    try {
-                        sb.append((char)Integer.parseInt(
-                                        s.substring(i+1,i+3),16));
-                    } catch (NumberFormatException e) {
-                        throw new IllegalArgumentException();
-                    }
-                    i += 2;
-                    break;
-                default:
-                    sb.append(c);
-                    break;
-            }
-        }
-        // Undo conversion to external encoding
-        String result = sb.toString();
-        try {
-            byte[] inputBytes = result.getBytes("8859_1");
-            result = new String(inputBytes);
-        } catch (UnsupportedEncodingException e) {
-            // The system should always have 8859_1
-        }
-        return result;
-    }
-}
-
-
diff --git a/update/Base64.java b/update/Base64.java
new file mode 100644 (file)
index 0000000..9008667
--- /dev/null
@@ -0,0 +1,94 @@
+/* Encodes and decodes to and from Base64 notation.\r
+ * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.util;\r
+\r
+public class Base64 {\r
+\r
+    private static final String ALPHABET =\r
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+\r
+    /**\r
+     * Base-64 encodes the supplied block of data.  Line wrapping is not\r
+     * applied on output.\r
+     *\r
+     * @param bytes The block of data that is to be Base-64 encoded.\r
+     * @return A <code>String</code> containing the encoded data.\r
+     */\r
+    public static String encode(byte[] bytes) {\r
+        int length = bytes.length;\r
+        if (length == 0) return "";\r
+        StringBuffer buffer =\r
+                new StringBuffer((int) Math.ceil((double) length / 3d) * 4);\r
+        int remainder = length % 3;\r
+        length -= remainder;\r
+        int block;\r
+        int i = 0;\r
+        while (i < length) {\r
+            block = ((bytes[i++] & 0xff) << 16) | ((bytes[i++] & 0xff) << 8) |\r
+                    (bytes[i++] & 0xff);\r
+            buffer.append(ALPHABET.charAt(block >>> 18));\r
+            buffer.append(ALPHABET.charAt((block >>> 12) & 0x3f));\r
+            buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));\r
+            buffer.append(ALPHABET.charAt(block & 0x3f));\r
+        }\r
+        if (remainder == 0) return buffer.toString();\r
+        if (remainder == 1) {\r
+            block = (bytes[i] & 0xff) << 4;\r
+            buffer.append(ALPHABET.charAt(block >>> 6));\r
+            buffer.append(ALPHABET.charAt(block & 0x3f));\r
+            buffer.append("==");\r
+            return buffer.toString();\r
+        }\r
+        block = (((bytes[i++] & 0xff) << 8) | ((bytes[i]) & 0xff)) << 2;\r
+        buffer.append(ALPHABET.charAt(block >>> 12));\r
+        buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));\r
+        buffer.append(ALPHABET.charAt(block & 0x3f));\r
+        buffer.append("=");\r
+        return buffer.toString();\r
+    }\r
+\r
+    /**\r
+     * Decodes the supplied Base-64 encoded string.\r
+     *\r
+     * @param string The Base-64 encoded string that is to be decoded.\r
+     * @return A <code>byte[]</code> containing the decoded data block.\r
+     */\r
+    public static byte[] decode(String string) {\r
+        int length = string.length();\r
+        if (length == 0) return new byte[0];\r
+        int pad = (string.charAt(length - 2) == '=') ? 2 :\r
+                (string.charAt(length - 1) == '=') ? 1 : 0;\r
+        int size = length * 3 / 4 - pad;\r
+        byte[] buffer = new byte[size];\r
+        int block;\r
+        int i = 0;\r
+        int index = 0;\r
+        while (i < length) {\r
+            block = (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 18 |\r
+                    (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 12 |\r
+                    (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 6 |\r
+                    (ALPHABET.indexOf(string.charAt(i++)) & 0xff);\r
+            buffer[index++] = (byte) (block >>> 16);\r
+            if (index < size) buffer[index++] = (byte) ((block >>> 8) & 0xff);\r
+            if (index < size) buffer[index++] = (byte) (block & 0xff);\r
+        }\r
+        return buffer;\r
+    }\r
+\r
+}\r
diff --git a/update/HMACT64.java b/update/HMACT64.java
new file mode 100644 (file)
index 0000000..8420fb9
--- /dev/null
@@ -0,0 +1,116 @@
+/* HMACT64 keyed hashing algorithm\r
+ * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.util;\r
+\r
+import java.security.MessageDigest;\r
+\r
+/**\r
+ * This is an implementation of the HMACT64 keyed hashing algorithm.\r
+ * HMACT64 is defined by Luke Leighton as a modified HMAC-MD5 (RFC 2104)\r
+ * in which the key is truncated at 64 bytes (rather than being hashed\r
+ * via MD5).\r
+ */ \r
+public class HMACT64 extends MessageDigest implements Cloneable {\r
+\r
+    private static final int BLOCK_LENGTH = 64;\r
+\r
+    private static final byte IPAD = (byte) 0x36;\r
+\r
+    private static final byte OPAD = (byte) 0x5c;\r
+\r
+    private MessageDigest md5;\r
+\r
+    private byte[] ipad = new byte[BLOCK_LENGTH];\r
+\r
+    private byte[] opad = new byte[BLOCK_LENGTH];\r
+\r
+    /**\r
+     * Creates an HMACT64 instance which uses the given secret key material.\r
+     *\r
+     * @param key The key material to use in hashing.\r
+     */\r
+    public HMACT64(byte[] key) {\r
+        super("HMACT64");\r
+        int length = Math.min(key.length, BLOCK_LENGTH);\r
+        for (int i = 0; i < length; i++) {\r
+            ipad[i] = (byte) (key[i] ^ IPAD);\r
+            opad[i] = (byte) (key[i] ^ OPAD);\r
+        }\r
+        for (int i = length; i < BLOCK_LENGTH; i++) {\r
+            ipad[i] = IPAD;\r
+            opad[i] = OPAD;\r
+        }\r
+        try {\r
+            md5 = MessageDigest.getInstance("MD5");\r
+        } catch (Exception ex) {\r
+            throw new IllegalStateException(ex.getMessage());\r
+        }\r
+        engineReset();\r
+    }\r
+\r
+    private HMACT64(HMACT64 hmac) throws CloneNotSupportedException {\r
+        super("HMACT64");\r
+        this.ipad = hmac.ipad;\r
+        this.opad = hmac.opad;\r
+        this.md5 = (MessageDigest) hmac.md5.clone();\r
+    }\r
+\r
+    public Object clone() {\r
+        try {\r
+            return new HMACT64(this);\r
+        } catch (CloneNotSupportedException ex) {\r
+            throw new IllegalStateException(ex.getMessage());\r
+        }\r
+    }\r
+\r
+    protected byte[] engineDigest() {\r
+        byte[] digest = md5.digest();\r
+        md5.update(opad);\r
+        return md5.digest(digest);\r
+    }\r
+\r
+    protected int engineDigest(byte[] buf, int offset, int len) {\r
+        byte[] digest = md5.digest();\r
+        md5.update(opad);\r
+        md5.update(digest);\r
+        try {\r
+            return md5.digest(buf, offset, len);\r
+        } catch (Exception ex) {\r
+            throw new IllegalStateException();\r
+        }\r
+    }\r
+\r
+    protected int engineGetDigestLength() {\r
+        return md5.getDigestLength();\r
+    }\r
+\r
+    protected void engineReset() {\r
+        md5.reset();\r
+        md5.update(ipad);\r
+    }\r
+\r
+    protected void engineUpdate(byte b) {\r
+        md5.update(b);\r
+    }\r
+\r
+    protected void engineUpdate(byte[] input, int offset, int len) {\r
+        md5.update(input, offset, len);\r
+    }\r
+\r
+}\r
diff --git a/update/NtlmHttpFilter.java b/update/NtlmHttpFilter.java
new file mode 100644 (file)
index 0000000..d160e65
--- /dev/null
@@ -0,0 +1,164 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                                      "Jason Pugsley" <jcifs at samba dot org>\r
+ *                                      "skeetz" <jcifs at samba dot org>\r
+ *                                      "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.*;\r
+import java.util.Enumeration;\r
+import java.net.UnknownHostException;\r
+import javax.servlet.*;\r
+import javax.servlet.http.*;\r
+import jcifs.*;\r
+import jcifs.smb.SmbSession;\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+import jcifs.smb.SmbAuthException;\r
+import jcifs.util.Base64;\r
+\r
+/**\r
+ * This servlet Filter can be used to negotiate password hashes with\r
+ * MSIE clients using NTLM SSP. This is similar to <tt>Authentication:\r
+ * BASIC</tt> but weakly encrypted and without requiring the user to re-supply\r
+ * authentication credentials.\r
+ * <p>\r
+ * Read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a> for complete details.\r
+ */\r
+\r
+public class NtlmHttpFilter implements Filter {\r
+\r
+    private String defaultDomain;\r
+\r
+       private String domainController;\r
+\r
+       private boolean enableBasic;\r
+\r
+       private boolean insecureBasic;\r
+\r
+       private String realm;\r
+\r
+       public void init( FilterConfig filterConfig ) throws ServletException {\r
+               String name;\r
+\r
+               /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min.\r
+                */\r
+               Config.setProperty( "jcifs.smb.client.soTimeout", "300000" );\r
+               Config.setProperty( "jcifs.netbios.cachePolicy", "600" );\r
+\r
+               Enumeration e = filterConfig.getInitParameterNames();\r
+               while( e.hasMoreElements() ) {\r
+                       name = (String)e.nextElement();\r
+                       if( name.startsWith( "jcifs." )) {\r
+                               Config.setProperty( name, filterConfig.getInitParameter( name ));\r
+                       }\r
+               }\r
+        defaultDomain = Config.getProperty("jcifs.smb.client.domain");\r
+               domainController = Config.getProperty( "jcifs.http.domainController" );\r
+               if( domainController == null ) domainController = defaultDomain;\r
+               enableBasic = Boolean.valueOf(\r
+                               Config.getProperty("jcifs.http.enableBasic")).booleanValue();\r
+               insecureBasic = Boolean.valueOf(\r
+                               Config.getProperty("jcifs.http.insecureBasic")).booleanValue();\r
+               realm = Config.getProperty("jcifs.http.basicRealm");\r
+               if (realm == null) realm = "jCIFS";\r
+       }\r
+       public void destroy() {\r
+       }\r
+       public void doFilter( ServletRequest request,\r
+                               ServletResponse response,\r
+                               FilterChain chain ) throws IOException, ServletException {\r
+               HttpServletRequest req;\r
+               HttpServletResponse resp;\r
+               UniAddress dc;\r
+               String msg;\r
+\r
+               NtlmPasswordAuthentication ntlm = null;\r
+               req = (HttpServletRequest)request;\r
+               resp = (HttpServletResponse)response;\r
+               msg = req.getHeader( "Authorization" );\r
+               boolean offerBasic = enableBasic && (insecureBasic || req.isSecure());\r
+\r
+               if( msg != null && (msg.startsWith( "NTLM " ) ||\r
+                                       (offerBasic && msg.startsWith("Basic ")))) {\r
+                       dc = UniAddress.getByName( domainController, true );\r
+                       if (msg.startsWith("NTLM ")) {\r
+                               byte[] challenge = SmbSession.getChallenge( dc );\r
+                               if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) {\r
+                                       return;\r
+                               }\r
+                       } else {\r
+                               String auth = new String(Base64.decode(msg.substring(6)),\r
+                                               "US-ASCII");\r
+                               int index = auth.indexOf(':');\r
+                               String user = (index != -1) ? auth.substring(0, index) : auth;\r
+                               String password = (index != -1) ? auth.substring(index + 1) :\r
+                                               "";\r
+                               index = user.indexOf('\\');\r
+                               if (index == -1) index = user.indexOf('/');\r
+                               String domain = (index != -1) ? user.substring(0, index) :\r
+                        defaultDomain;\r
+                               user = (index != -1) ? user.substring(index + 1) : user;\r
+                               ntlm = new NtlmPasswordAuthentication(domain, user, password);\r
+                       }\r
+                       try {\r
+                               SmbSession.logon( dc, ntlm );\r
+                       } catch( SmbAuthException sae ) {\r
+                               resp.setHeader( "WWW-Authenticate", "NTLM" );\r
+                               if (offerBasic) {\r
+                                       resp.addHeader( "WWW-Authenticate", "Basic realm=\"" +\r
+                                                       realm + "\"");\r
+                               }\r
+                               resp.setHeader( "Connection", "close" );\r
+                               resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );\r
+                               resp.flushBuffer();\r
+                               return;\r
+                       }\r
+                       req.getSession().setAttribute( "NtlmHttpAuth", ntlm );\r
+        } else {\r
+            HttpSession ssn = req.getSession(false);\r
+            if (ssn == null || (ntlm = (NtlmPasswordAuthentication)\r
+                    ssn.getAttribute("NtlmHttpAuth")) == null) {\r
+                resp.setHeader( "WWW-Authenticate", "NTLM" );\r
+                if (offerBasic) {\r
+                    resp.addHeader( "WWW-Authenticate", "Basic realm=\"" +\r
+                            realm + "\"");\r
+                }\r
+                resp.setHeader( "Connection", "close" );\r
+                resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );\r
+                resp.flushBuffer();\r
+                return;\r
+            }\r
+               }\r
+\r
+               chain.doFilter( new NtlmHttpServletRequest( req, ntlm ), response );\r
+       }\r
+\r
+       // Added by cgross to work with weblogic 6.1.\r
+       public void setFilterConfig( FilterConfig f ) {\r
+               try {\r
+                       init( f );\r
+               } catch( Exception e ) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       public FilterConfig getFilterConfig() {\r
+               return null;\r
+       }\r
+}\r
+\r
diff --git a/update/NtlmHttpURLConnection.java b/update/NtlmHttpURLConnection.java
new file mode 100644 (file)
index 0000000..2af8f50
--- /dev/null
@@ -0,0 +1,563 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                                      "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.InputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import java.net.Authenticator;\r
+import java.net.HttpURLConnection;\r
+import java.net.PasswordAuthentication;\r
+import java.net.ProtocolException;\r
+import java.net.URL;\r
+import java.net.URLDecoder;\r
+\r
+import java.security.Permission;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import jcifs.Config;\r
+\r
+import jcifs.ntlmssp.NtlmFlags;\r
+import jcifs.ntlmssp.NtlmMessage;\r
+import jcifs.ntlmssp.Type1Message;\r
+import jcifs.ntlmssp.Type2Message;\r
+import jcifs.ntlmssp.Type3Message;\r
+\r
+import jcifs.util.Base64;\r
+\r
+/**\r
+ * Wraps an <code>HttpURLConnection</code> to provide NTLM authentication\r
+ * services.\r
+ *\r
+ * Please read <a href="../../../httpclient.html">Using jCIFS NTLM Authentication for HTTP Connections</a>.\r
+ */\r
+public class NtlmHttpURLConnection extends HttpURLConnection {\r
+\r
+    private static final int MAX_REDIRECTS =\r
+            Integer.parseInt(System.getProperty("http.maxRedirects", "20"));\r
+\r
+    private static final int LM_COMPATIBILITY =\r
+            Config.getInt("jcifs.smb.lmCompatibility", 0);\r
+\r
+    private static final String DEFAULT_DOMAIN;\r
+\r
+    private HttpURLConnection connection;\r
+\r
+    private Map requestProperties;\r
+\r
+    private Map headerFields;\r
+\r
+    private String authProperty;\r
+\r
+    private String method;\r
+\r
+    static {\r
+        String domain = System.getProperty("http.auth.ntlm.domain");\r
+        if (domain == null) domain = Type3Message.getDefaultDomain();\r
+        DEFAULT_DOMAIN = domain;\r
+    }\r
+\r
+    public NtlmHttpURLConnection(HttpURLConnection connection) {\r
+        super(connection.getURL());\r
+        this.connection = connection;\r
+        requestProperties = new HashMap();\r
+    }\r
+\r
+    public void connect() throws IOException {\r
+        if (connected) return;\r
+        doConnect();\r
+        connected = true;\r
+    }\r
+\r
+    public URL getURL() {\r
+        return connection.getURL();\r
+    }\r
+\r
+    public int getContentLength() {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getContentLength();\r
+    }\r
+\r
+    public String getContentType() {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getContentType();\r
+    }\r
+\r
+    public String getContentEncoding() {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getContentEncoding();\r
+    }\r
+\r
+    public long getExpiration() {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getExpiration();\r
+    }\r
+\r
+    public long getDate() {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getDate();\r
+    }\r
+\r
+    public long getLastModified() {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getLastModified();\r
+    }\r
+\r
+    public String getHeaderField(String header) {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getHeaderField(header);\r
+    }\r
+\r
+    private synchronized Map getHeaderFields0() {\r
+        if (headerFields != null) return headerFields;\r
+        Map map = new HashMap();\r
+        String key = connection.getHeaderFieldKey(0);\r
+        String value = connection.getHeaderField(0);\r
+        for (int i = 1; key != null || value != null; i++) {\r
+            List values = (List) map.get(key);\r
+            if (values == null) {\r
+                values = new ArrayList();\r
+                map.put(key, values);\r
+            }\r
+            values.add(value);\r
+            key = connection.getHeaderFieldKey(i);\r
+            value = connection.getHeaderField(i);\r
+        }\r
+        Iterator entries = map.entrySet().iterator();\r
+        while (entries.hasNext()) {\r
+            Map.Entry entry = (Map.Entry) entries.next();\r
+            entry.setValue(Collections.unmodifiableList((List)\r
+                    entry.getValue()));\r
+        }\r
+        return (headerFields = Collections.unmodifiableMap(map));\r
+    }\r
+\r
+    public Map getHeaderFields() {\r
+        synchronized (this) {\r
+            if (headerFields != null) return headerFields;\r
+        }\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return getHeaderFields0();\r
+    }\r
+\r
+    public int getHeaderFieldInt(String header, int def) {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getHeaderFieldInt(header, def);\r
+    }\r
+\r
+    public long getHeaderFieldDate(String header, long def) {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getHeaderFieldDate(header, def);\r
+    }\r
+\r
+    public String getHeaderFieldKey(int index) {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getHeaderFieldKey(index);\r
+    }\r
+\r
+    public String getHeaderField(int index) {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getHeaderField(index);\r
+    }\r
+\r
+    public Object getContent() throws IOException {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getContent();\r
+    }\r
+\r
+    public Object getContent(Class[] classes) throws IOException {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getContent(classes);\r
+    }\r
+\r
+    public Permission getPermission() throws IOException {\r
+        return connection.getPermission();\r
+    }\r
+\r
+    public InputStream getInputStream() throws IOException {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getInputStream();\r
+    }\r
+\r
+    public OutputStream getOutputStream() throws IOException {\r
+        try {\r
+            connect();\r
+        } catch (IOException ex) { }\r
+        return connection.getOutputStream();\r
+    }\r
+\r
+    public String toString() {\r
+        return connection.toString();\r
+    }\r
+\r
+    public void setDoInput(boolean doInput) {\r
+        connection.setDoInput(doInput);\r
+        this.doInput = doInput;\r
+    }\r
+\r
+    public boolean getDoInput() {\r
+        return connection.getDoInput();\r
+    }\r
+\r
+    public void setDoOutput(boolean doOutput) {\r
+        connection.setDoOutput(doOutput);\r
+        this.doOutput = doOutput;\r
+    }\r
+\r
+    public boolean getDoOutput() {\r
+        return connection.getDoOutput();\r
+    }\r
+\r
+    public void setAllowUserInteraction(boolean allowUserInteraction) {\r
+        connection.setAllowUserInteraction(allowUserInteraction);\r
+        this.allowUserInteraction = allowUserInteraction;\r
+    }\r
+\r
+    public boolean getAllowUserInteraction() {\r
+        return connection.getAllowUserInteraction();\r
+    }\r
+\r
+    public void setUseCaches(boolean useCaches) {\r
+        connection.setUseCaches(useCaches);\r
+        this.useCaches = useCaches;\r
+    }\r
+\r
+    public boolean getUseCaches() {\r
+        return connection.getUseCaches();\r
+    }\r
+\r
+    public void setIfModifiedSince(long ifModifiedSince) {\r
+        connection.setIfModifiedSince(ifModifiedSince);\r
+        this.ifModifiedSince = ifModifiedSince;\r
+    }\r
+\r
+    public long getIfModifiedSince() {\r
+        return connection.getIfModifiedSince();\r
+    }\r
+\r
+    public boolean getDefaultUseCaches() {\r
+        return connection.getDefaultUseCaches();\r
+    }\r
+\r
+    public void setDefaultUseCaches(boolean defaultUseCaches) {\r
+        connection.setDefaultUseCaches(defaultUseCaches);\r
+    }\r
+\r
+    public void setRequestProperty(String key, String value) {\r
+        if (key == null) throw new NullPointerException();\r
+        List values = new ArrayList();\r
+        values.add(value);\r
+        boolean found = false;\r
+        synchronized (requestProperties) {\r
+            Iterator entries = requestProperties.entrySet().iterator();\r
+            while (entries.hasNext()) {\r
+                Map.Entry entry = (Map.Entry) entries.next();\r
+                if (key.equalsIgnoreCase((String) entry.getKey())) {\r
+                    entry.setValue(value);\r
+                    found = true;\r
+                    break;\r
+                }\r
+            }\r
+            if (!found) requestProperties.put(key, values);\r
+        }\r
+        connection.setRequestProperty(key, value);\r
+    }\r
+\r
+    public void addRequestProperty(String key, String value) {\r
+        if (key == null) throw new NullPointerException();\r
+        List values = null;\r
+        synchronized (requestProperties) {\r
+            Iterator entries = requestProperties.entrySet().iterator();\r
+            while (entries.hasNext()) {\r
+                Map.Entry entry = (Map.Entry) entries.next();\r
+                if (key.equalsIgnoreCase((String) entry.getKey())) {\r
+                    values = (List) entry.getValue();\r
+                    values.add(value);\r
+                    break;\r
+                }\r
+            }\r
+            if (values == null) {\r
+                values = new ArrayList();\r
+                values.add(value);\r
+                requestProperties.put(key, values);\r
+            }\r
+        }\r
+        // 1.3-compatible.\r
+        StringBuffer buffer = new StringBuffer();\r
+        Iterator propertyValues = values.iterator();\r
+        while (propertyValues.hasNext()) {\r
+            buffer.append(propertyValues.next());\r
+            if (propertyValues.hasNext()) {\r
+                buffer.append(", ");\r
+            }\r
+        }\r
+        connection.setRequestProperty(key, buffer.toString());\r
+    }\r
+\r
+    public String getRequestProperty(String key) {\r
+        return connection.getRequestProperty(key);\r
+    }\r
+\r
+    public Map getRequestProperties() {\r
+        Map map = new HashMap();\r
+        synchronized (requestProperties) {\r
+            Iterator entries = requestProperties.entrySet().iterator();\r
+            while (entries.hasNext()) {\r
+                Map.Entry entry = (Map.Entry) entries.next();\r
+                map.put(entry.getKey(),\r
+                        Collections.unmodifiableList((List) entry.getValue()));\r
+            }\r
+        }\r
+        return Collections.unmodifiableMap(map);\r
+    }\r
+\r
+    public void setInstanceFollowRedirects(boolean instanceFollowRedirects) {\r
+        connection.setInstanceFollowRedirects(instanceFollowRedirects);\r
+    }\r
+\r
+    public boolean getInstanceFollowRedirects() {\r
+        return connection.getInstanceFollowRedirects();\r
+    }\r
+\r
+    public void setRequestMethod(String requestMethod)\r
+            throws ProtocolException {\r
+        connection.setRequestMethod(requestMethod);\r
+    }\r
+\r
+    public String getRequestMethod() {\r
+        return connection.getRequestMethod();\r
+    }\r
+\r
+    public int getResponseCode() throws IOException {\r
+        return connection.getResponseCode();\r
+    }\r
+\r
+    public String getResponseMessage() throws IOException {\r
+        return connection.getResponseMessage();\r
+    }\r
+\r
+    public void disconnect() {\r
+        connection.disconnect();\r
+        connected = false;\r
+    }\r
+\r
+    public boolean usingProxy() {\r
+        return connection.usingProxy();\r
+    }\r
+\r
+    public InputStream getErrorStream() {\r
+        return connection.getErrorStream();\r
+    }\r
+\r
+    private int parseResponseCode() throws IOException {\r
+        try {\r
+            String response = connection.getHeaderField(0);\r
+            int index = response.indexOf(' ');\r
+            while (response.charAt(index) == ' ') index++;\r
+            return Integer.parseInt(response.substring(index, index + 3));\r
+        } catch (Exception ex) {\r
+            throw new IOException(ex.getMessage());\r
+        }\r
+    }\r
+\r
+    private synchronized void doConnect() throws IOException {\r
+        connection.connect();\r
+        int response = parseResponseCode();\r
+        if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
+            return;\r
+        }\r
+        Type1Message type1 = (Type1Message) attemptNegotiation(response);\r
+        if (type1 == null) return; // no NTLM\r
+        int attempt = 0;\r
+        while (attempt < MAX_REDIRECTS) {\r
+            connection.setRequestProperty(authProperty, method + ' ' +\r
+                    Base64.encode(type1.toByteArray()));\r
+            connection.connect(); // send type 1\r
+            response = parseResponseCode();\r
+            if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
+                return;\r
+            }\r
+            Type3Message type3 = (Type3Message) attemptNegotiation(response);\r
+            if (type3 == null) return;\r
+            connection.setRequestProperty(authProperty, method + ' ' +\r
+                    Base64.encode(type3.toByteArray()));\r
+            connection.connect(); // send type 3\r
+            response = parseResponseCode();\r
+            if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
+                return;\r
+            }\r
+            attempt++;\r
+            if (attempt < MAX_REDIRECTS) reconnect();\r
+        }\r
+        throw new IOException("Unable to negotiate NTLM authentication.");\r
+    }\r
+\r
+    private NtlmMessage attemptNegotiation(int response) throws IOException {\r
+        authProperty = null;\r
+        method = null;\r
+        InputStream errorStream = connection.getErrorStream();\r
+        if (errorStream != null && errorStream.available() != 0) {\r
+            int count;\r
+            byte[] buf = new byte[1024];\r
+            while ((count = errorStream.read(buf, 0, 1024)) != -1);\r
+        }\r
+        String authHeader;\r
+        if (response == HTTP_UNAUTHORIZED) {\r
+            authHeader = "WWW-Authenticate";\r
+            authProperty = "Authorization";\r
+        } else {\r
+            authHeader = "Proxy-Authenticate";\r
+            authProperty = "Proxy-Authorization";\r
+        }\r
+        String authorization = null;\r
+        List methods = (List) getHeaderFields0().get(authHeader);\r
+        if (methods == null) return null;\r
+        Iterator iterator = methods.iterator();\r
+        while (iterator.hasNext()) {\r
+            String authMethod = (String) iterator.next();\r
+            if (authMethod.startsWith("NTLM")) {\r
+                if (authMethod.length() == 4) {\r
+                    method = "NTLM";\r
+                    break;\r
+                }\r
+                if (authMethod.indexOf(' ') != 4) continue;\r
+                method = "NTLM";\r
+                authorization = authMethod.substring(5).trim();\r
+                break;\r
+            } else if (authMethod.startsWith("Negotiate")) {\r
+                if (authMethod.length() == 9) {\r
+                    method = "Negotiate";\r
+                    break;\r
+                }\r
+                if (authMethod.indexOf(' ') != 9) continue;\r
+                method = "Negotiate";\r
+                authorization = authMethod.substring(10).trim();\r
+                break;\r
+            }\r
+        }\r
+        if (method == null) return null;\r
+        NtlmMessage message = (authorization != null) ?\r
+                new Type2Message(Base64.decode(authorization)) : null;\r
+        reconnect();\r
+        if (message == null) {\r
+            message = new Type1Message();\r
+            if (LM_COMPATIBILITY > 2) {\r
+                message.setFlag(NtlmFlags.NTLMSSP_REQUEST_TARGET, true);\r
+            }\r
+        } else {\r
+            String domain = DEFAULT_DOMAIN;\r
+            String user = Type3Message.getDefaultUser();\r
+            String password = Type3Message.getDefaultPassword();\r
+            String userInfo = url.getUserInfo();\r
+            if (userInfo != null) {\r
+                userInfo = URLDecoder.decode(userInfo);\r
+                int index = userInfo.indexOf(':');\r
+                user = (index != -1) ? userInfo.substring(0, index) : userInfo;\r
+                if (index != -1) password = userInfo.substring(index + 1);\r
+                index = user.indexOf('\\');\r
+                if (index == -1) index = user.indexOf('/');\r
+                domain = (index != -1) ? user.substring(0, index) : domain;\r
+                user = (index != -1) ? user.substring(index + 1) : user;\r
+            }\r
+            if (user == null) {\r
+                try {\r
+                    URL url = getURL();\r
+                    String protocol = url.getProtocol();\r
+                    int port = url.getPort();\r
+                    if (port == -1) {\r
+                        port = "https".equalsIgnoreCase(protocol) ? 443 : 80;\r
+                    }\r
+                    PasswordAuthentication auth =\r
+                            Authenticator.requestPasswordAuthentication(null,\r
+                                    port, protocol, "", method);\r
+                    if (auth != null) {\r
+                        user = auth.getUserName();\r
+                        password = new String(auth.getPassword());\r
+                    }\r
+                } catch (Exception ex) { }\r
+            }\r
+            Type2Message type2 = (Type2Message) message;\r
+            message = new Type3Message(type2, password, domain, user,\r
+                    Type3Message.getDefaultWorkstation());\r
+        }\r
+        return message;\r
+    }\r
+\r
+    private void reconnect() throws IOException {\r
+        connection = (HttpURLConnection) connection.getURL().openConnection();\r
+        headerFields = null;\r
+        synchronized (requestProperties) {\r
+            Iterator properties = requestProperties.entrySet().iterator();\r
+            while (properties.hasNext()) {\r
+                Map.Entry property = (Map.Entry) properties.next();\r
+                String key = (String) property.getKey();\r
+                StringBuffer value = new StringBuffer();\r
+                Iterator values = ((List) property.getValue()).iterator();\r
+                while (values.hasNext()) {\r
+                    value.append(values.next());\r
+                    if (values.hasNext()) value.append(", ");\r
+                }\r
+                connection.setRequestProperty(key, value.toString());\r
+            }\r
+        }\r
+        connection.setAllowUserInteraction(allowUserInteraction);\r
+        connection.setDoInput(doInput);\r
+        connection.setDoOutput(doOutput);\r
+        connection.setIfModifiedSince(ifModifiedSince);\r
+        connection.setUseCaches(useCaches);\r
+    }\r
+}\r
diff --git a/update/NtlmMessage.java b/update/NtlmMessage.java
new file mode 100644 (file)
index 0000000..3b8c692
--- /dev/null
@@ -0,0 +1,138 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                                      "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import jcifs.Config;\r
+\r
+/**\r
+ * Abstract superclass for all NTLMSSP messages.\r
+ */\r
+public abstract class NtlmMessage implements NtlmFlags {\r
+\r
+    /**\r
+     * The NTLMSSP "preamble".\r
+     */\r
+    protected static final byte[] NTLMSSP_SIGNATURE = new byte[] {\r
+        (byte) 'N', (byte) 'T', (byte) 'L', (byte) 'M',\r
+        (byte) 'S', (byte) 'S', (byte) 'P', (byte) 0\r
+    };\r
+\r
+    private static final String OEM_ENCODING =\r
+            Config.getProperty("jcifs.smb.client.codepage",\r
+                    Config.getProperty("jcifs.encoding",\r
+                            System.getProperty("file.encoding")));\r
+\r
+    private int flags;\r
+\r
+    /**\r
+     * Returns the flags currently in use for this message.\r
+     *\r
+     * @return An <code>int</code> containing the flags in use for this\r
+     * message.\r
+     */\r
+    public int getFlags() {\r
+        return flags;\r
+    }\r
+\r
+    /**\r
+     * Sets the flags for this message.\r
+     *\r
+     * @param flags The flags for this message.\r
+     */\r
+    public void setFlags(int flags) {\r
+        this.flags = flags;\r
+    }\r
+\r
+    /**\r
+     * Returns the status of the specified flag.\r
+     *\r
+     * @param flag The flag to test (i.e., <code>NTLMSSP_NEGOTIATE_OEM</code>).\r
+     * @return A <code>boolean</code> indicating whether the flag is set.\r
+     */\r
+    public boolean getFlag(int flag) {\r
+        return (getFlags() & flag) != 0;\r
+    }\r
+\r
+    /**\r
+     * Sets or clears the specified flag.\r
+     * \r
+     * @param flag The flag to set/clear (i.e.,\r
+     * <code>NTLMSSP_NEGOTIATE_OEM</code>).\r
+     * @param value Indicates whether to set (<code>true</code>) or\r
+     * clear (<code>false</code>) the specified flag.\r
+     */\r
+    public void setFlag(int flag, boolean value) {\r
+        setFlags(value ? (getFlags() | flag) :\r
+                (getFlags() & (0xffffffff ^ flag)));\r
+    }\r
+\r
+    static int readULong(byte[] src, int index) {\r
+        return (src[index] & 0xff) |\r
+                ((src[index + 1] & 0xff) << 8) |\r
+                ((src[index + 2] & 0xff) << 16) |\r
+                ((src[index + 3] & 0xff) << 24);\r
+    }\r
+\r
+    static int readUShort(byte[] src, int index) {\r
+        return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);\r
+    }\r
+\r
+    static byte[] readSecurityBuffer(byte[] src, int index) {\r
+        int length = readUShort(src, index);\r
+        int offset = readULong(src, index + 4);\r
+        byte[] buffer = new byte[length];\r
+        System.arraycopy(src, offset, buffer, 0, length);\r
+        return buffer;\r
+    }\r
+\r
+    static void writeULong(byte[] dest, int offset, int ulong) {\r
+        dest[offset] = (byte) (ulong & 0xff);\r
+        dest[offset + 1] = (byte) (ulong >> 8 & 0xff);\r
+        dest[offset + 2] = (byte) (ulong >> 16 & 0xff);\r
+        dest[offset + 3] = (byte) (ulong >> 24 & 0xff);\r
+    }\r
+\r
+    static void writeUShort(byte[] dest, int offset, int ushort) {\r
+        dest[offset] = (byte) (ushort & 0xff);\r
+        dest[offset + 1] = (byte) (ushort >> 8 & 0xff);\r
+    }\r
+\r
+    static void writeSecurityBuffer(byte[] dest, int offset, int bodyOffset,\r
+            byte[] src) {\r
+        int length = (src != null) ? src.length : 0;\r
+        if (length == 0) return;\r
+        writeUShort(dest, offset, length);\r
+        writeUShort(dest, offset + 2, length);\r
+        writeULong(dest, offset + 4, bodyOffset);\r
+        System.arraycopy(src, 0, dest, bodyOffset, length);\r
+    }\r
+\r
+    static String getOEMEncoding() {\r
+        return OEM_ENCODING;\r
+    }\r
+\r
+    /**\r
+     * Returns the raw byte representation of this message.\r
+     *\r
+     * @return A <code>byte[]</code> containing the raw message material.\r
+     */\r
+    public abstract byte[] toByteArray();\r
+\r
+}\r
diff --git a/update/NtlmPasswordAuthentication.java b/update/NtlmPasswordAuthentication.java
new file mode 100644 (file)
index 0000000..a180c0c
--- /dev/null
@@ -0,0 +1,348 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.smb;\r
+\r
+import jcifs.util.DES;\r
+import jcifs.util.MD4;\r
+import jcifs.util.HMACT64;\r
+import java.io.UnsupportedEncodingException;\r
+import java.security.Principal;\r
+import java.security.SecureRandom;\r
+import java.util.Arrays;\r
+import jcifs.Config;\r
+\r
+/**\r
+ * This class stores and encrypts NTLM user credentials. The default\r
+ * credentials are retrieved from the <tt>jcifs.smb.client.domain</tt>,\r
+ * <tt>jcifs.smb.client.username</tt>, and <tt>jcifs.smb.client.password</tt>\r
+ * properties.\r
+ * <p>\r
+ * Read <a href="../../../authhandler.html">jCIFS Exceptions and\r
+ * NtlmAuthenticator</a> for related information.\r
+ */\r
+\r
+public final class NtlmPasswordAuthentication implements Principal {\r
+\r
+    private static final int LM_COMPATIBILITY =\r
+            Config.getInt("jcifs.smb.lmCompatibility", 0);\r
+\r
+    private static final String DEFAULT_DOMAIN =\r
+            Config.getProperty("jcifs.smb.client.domain", "?");\r
+\r
+    private static final String DEFAULT_USERNAME =\r
+            Config.getProperty("jcifs.smb.client.username", "GUEST");\r
+\r
+    private static final String DEFAULT_PASSWORD =\r
+            Config.getProperty("jcifs.smb.client.password", "");\r
+\r
+    private static final SecureRandom random = new SecureRandom();\r
+\r
+    // KGS!@#$%\r
+    static final byte[] S8 = {\r
+        (byte)0x4b, (byte)0x47, (byte)0x53, (byte)0x21,\r
+        (byte)0x40, (byte)0x23, (byte)0x24, (byte)0x25\r
+    };\r
+    static void E( byte[] key, byte[] data, byte[] e ) {\r
+        byte[] key7 = new byte[7];\r
+        byte[] e8 = new byte[8];\r
+\r
+        for( int i = 0; i < key.length / 7; i++ ) {\r
+            System.arraycopy( key, i * 7, key7, 0, 7 );\r
+            DES des = new DES( key7 );\r
+            des.encrypt( data, e8 );\r
+            System.arraycopy( e8, 0, e, i * 8, 8 );\r
+        }\r
+    }\r
+/**\r
+ * Generate the ANSI DES hash for the password associated with these credentials.\r
+ */\r
+    static public byte[] getPreNTLMResponse( String password, byte[] challenge ) {\r
+        byte[] p14 = new byte[14];\r
+        byte[] p21 = new byte[21];\r
+        byte[] p24 = new byte[24];\r
+        byte[] passwordBytes;\r
+        try {\r
+            passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.encoding );\r
+        } catch( UnsupportedEncodingException uee ) {\r
+            return null;\r
+        }\r
+        int passwordLength = passwordBytes.length;\r
+\r
+        // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM\r
+        if( passwordLength > 14) {\r
+            passwordLength = 14;\r
+        }\r
+        System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );\r
+        E( p14, S8, p21);\r
+        E( p21, challenge, p24);\r
+        return p24;\r
+    }\r
+/**\r
+ * Generate the Unicode MD4 hash for the password associated with these credentials.\r
+ */\r
+    static public byte[] getNTLMResponse( String password, byte[] challenge ) {\r
+        byte[] uni = null;\r
+        byte[] p21 = new byte[21];\r
+        byte[] p24 = new byte[24];\r
+\r
+        try {\r
+            uni = password.getBytes( "UnicodeLittleUnmarked" );\r
+        } catch( UnsupportedEncodingException uee ) {\r
+            Log.printStackTrace( "password encryption exception", uee );\r
+        }\r
+        MD4 md4 = new MD4();\r
+        md4.update( uni );\r
+        try {\r
+            md4.digest(p21, 0, 16);\r
+        } catch (Exception ex) {\r
+            Log.printStackTrace( "digest exception", ex );\r
+        }\r
+        E( p21, challenge, p24 );\r
+        return p24;\r
+    }\r
+\r
+    /**\r
+     * Creates the LMv2 response for the supplied information.\r
+     *\r
+     * @param domain The domain in which the username exists.\r
+     * @param user The username.\r
+     * @param password The user's password.\r
+     * @param challenge The server challenge.\r
+     * @param clientChallenge The client challenge (nonce). \r
+     */ \r
+    public static byte[] getLMv2Response(String domain, String user,\r
+            String password, byte[] challenge, byte[] clientChallenge) {\r
+        try {\r
+            byte[] hash = new byte[16];\r
+            byte[] response = new byte[24];\r
+            MD4 md4 = new MD4();\r
+            md4.update(password.getBytes("UnicodeLittleUnmarked"));\r
+            HMACT64 hmac = new HMACT64(md4.digest());\r
+            hmac.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked"));\r
+            hmac.update(domain.toUpperCase().getBytes("UnicodeLittleUnmarked"));\r
+            hmac = new HMACT64(hmac.digest());\r
+            hmac.update(challenge);\r
+            hmac.update(clientChallenge);\r
+            hmac.digest(response, 0, 16);\r
+            System.arraycopy(clientChallenge, 0, response, 16, 8);\r
+            return response;\r
+        } catch (Exception ex) {\r
+            Log.printStackTrace("Error creating LMv2 response", ex);\r
+            return null;\r
+        }\r
+    }\r
+\r
+    static final NtlmPasswordAuthentication NULL =\r
+                new NtlmPasswordAuthentication( "", "", "" );\r
+    static final NtlmPasswordAuthentication GUEST =\r
+                new NtlmPasswordAuthentication( "?", "GUEST", "" );\r
+\r
+    String domain;\r
+    String username;\r
+    String password;\r
+    byte[] ansiHash;\r
+    byte[] unicodeHash;\r
+    boolean hashesExternal = false;\r
+\r
+/**\r
+ * Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo\r
+ * component of an SMB URL like "<tt>domain;user:pass</tt>". This constructor\r
+ * is used internally be jCIFS when parsing SMB URLs.\r
+ */\r
+\r
+    public NtlmPasswordAuthentication( String userInfo ) {\r
+        domain = username = password = null;\r
+\r
+        if( userInfo != null ) {\r
+            int i, u, end;\r
+            char c;\r
+\r
+            end = userInfo.length();\r
+            for( i = 0, u = 0; i < end; i++ ) {\r
+                c = userInfo.charAt( i );\r
+                if( c == ';' ) {\r
+                    domain = userInfo.substring( 0, i );\r
+                    u = i + 1;\r
+                } else if( c == ':' ) {\r
+                    password = userInfo.substring( i + 1 );\r
+                    break;\r
+                }\r
+            }\r
+            username = userInfo.substring( u, i );\r
+        }\r
+\r
+        if( domain == null ) this.domain = DEFAULT_DOMAIN;\r
+        if( username == null ) this.username = DEFAULT_USERNAME;\r
+        if( password == null ) this.password = DEFAULT_PASSWORD;\r
+    }\r
+/**\r
+ * Create an <tt>NtlmPasswordAuthentication</tt> object from a\r
+ * domain, username, and password. Parameters that are <tt>null</tt>\r
+ * will be substituted with <tt>jcifs.smb.client.domain</tt>,\r
+ * <tt>jcifs.smb.client.username</tt>, <tt>jcifs.smb.client.password</tt>\r
+ * property values.\r
+ */\r
+    public NtlmPasswordAuthentication( String domain, String username, String password ) {\r
+        this.domain = domain;\r
+        this.username = username;\r
+        this.password = password;\r
+        if( domain == null ) this.domain = DEFAULT_DOMAIN;\r
+        if( username == null ) this.username = DEFAULT_USERNAME;\r
+        if( password == null ) this.password = DEFAULT_PASSWORD;\r
+    }\r
+/**\r
+ * Create an <tt>NtlmPasswordAuthentication</tt> object with raw password\r
+ * hashes. This is used exclusively by the <tt>jcifs.http.NtlmSsp</tt>\r
+ * class which is in turn used by NTLM HTTP authentication functionality.\r
+ */\r
+    public NtlmPasswordAuthentication( String domain, String username,\r
+                                    byte[] ansiHash, byte[] unicodeHash ) {\r
+        if( domain == null || username == null ||\r
+                                    ansiHash == null || unicodeHash == null ) {\r
+            throw new IllegalArgumentException( "External credentials cannot null" );\r
+        }\r
+        this.domain = domain;\r
+        this.username = username;\r
+        this.password = null;\r
+        this.ansiHash = ansiHash;\r
+        this.unicodeHash = unicodeHash;\r
+        hashesExternal = true;\r
+    }\r
+\r
+/**\r
+ * Returns the domain.\r
+ */\r
+    public String getDomain() {\r
+        return domain;\r
+    }\r
+/**\r
+ * Returns the username.\r
+ */\r
+    public String getUsername() {\r
+        return username;\r
+    }\r
+/**\r
+ * Returns the password in plain text or <tt>null</tt> if the raw password\r
+ * hashes were used to construct this <tt>NtlmPasswordAuthentication</tt>\r
+ * object which will be the case when NTLM HTTP Authentication is\r
+ * used. There is no way to retrieve a users password in plain text unless\r
+ * it is supplied by the user at runtime.\r
+ */\r
+    public String getPassword() {\r
+        return password;\r
+    }\r
+/**\r
+ * Return the domain and username in the format:\r
+ * <tt>domain\\username</tt>. This is equivalent to <tt>toString()</tt>.\r
+ */\r
+    public String getName() {\r
+        boolean d = domain.length() > 0 && domain.equals( "?" ) == false;\r
+        return d ? domain + "\\" + username : username;\r
+    }\r
+/**\r
+ * Computes the 24 byte ANSI password hash given the 8 byte server challenge.\r
+ */\r
+    public byte[] getAnsiHash( byte[] challenge ) {\r
+        if( hashesExternal ) {\r
+            return ansiHash;\r
+        }\r
+        switch (LM_COMPATIBILITY) {\r
+        case 0:\r
+        case 1:\r
+            return getPreNTLMResponse( password, challenge );\r
+        case 2:\r
+            return getNTLMResponse( password, challenge );\r
+        case 3:\r
+        case 4:\r
+        case 5:\r
+            byte[] clientChallenge = new byte[8];\r
+            random.nextBytes(clientChallenge);\r
+            return getLMv2Response(domain, username, password, challenge,\r
+                    clientChallenge);\r
+        default:\r
+            return getPreNTLMResponse( password, challenge );\r
+        }\r
+    }\r
+/**\r
+ * Computes the 24 byte Unicode password hash given the 8 byte server challenge.\r
+ */\r
+    public byte[] getUnicodeHash( byte[] challenge ) {\r
+        if( hashesExternal ) {\r
+            return unicodeHash;\r
+        }\r
+        switch (LM_COMPATIBILITY) {\r
+        case 0:\r
+        case 1:\r
+        case 2:\r
+            return getNTLMResponse( password, challenge );\r
+        case 3:\r
+        case 4:\r
+        case 5:\r
+            /*\r
+            byte[] clientChallenge = new byte[8];\r
+            random.nextBytes(clientChallenge);\r
+            return getNTLMv2Response(domain, username, password, null,\r
+                    challenge, clientChallenge);\r
+            */\r
+            return new byte[0];\r
+        default:\r
+            return getNTLMResponse( password, challenge );\r
+        }\r
+    }\r
+/**\r
+ * Compares two <tt>NtlmPasswordAuthentication</tt> objects for\r
+ * equality. Two <tt>NtlmPasswordAuthentication</tt> objects are equal if\r
+ * their caseless domain and username fields are equal and either both hashes are external and they are equal or both internally supplied passwords are equal. If one <tt>NtlmPasswordAuthentication</tt> object has external hashes (meaning negotiated via NTLM HTTP Authentication) and the other does not they will not be equal. This is technically not correct however the server 8 byte challage would be required to compute and compare the password hashes but that it not available with this method.\r
+ */\r
+    public boolean equals( Object obj ) {\r
+        if( obj instanceof NtlmPasswordAuthentication ) {\r
+            NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj;\r
+            if( ntlm.domain.toUpperCase().equals( domain.toUpperCase() ) &&\r
+                        ntlm.username.toUpperCase().equals( username.toUpperCase() )) {\r
+                if( hashesExternal && ntlm.hashesExternal ) {\r
+                    return Arrays.equals( ansiHash, ntlm.ansiHash ) &&\r
+                                Arrays.equals( unicodeHash, ntlm.unicodeHash );\r
+                    /* This still isn't quite right. If one npa object does not have external\r
+                     * hashes and the other does then they will not be considered equal even\r
+                     * though they may be.\r
+                     */\r
+                } else if( !hashesExternal && password.equals( ntlm.password )) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+\r
+/**\r
+ * Return the upcased username hash code.\r
+ */\r
+    public int hashCode() {\r
+        return getName().toUpperCase().hashCode();\r
+    }\r
+/**\r
+ * Return the domain and username in the format:\r
+ * <tt>domain\\username</tt>. This is equivalent to <tt>getName()</tt>.\r
+ */\r
+    public String toString() {\r
+        return getName();\r
+    }\r
+}\r
+\r
diff --git a/update/NtlmServlet.java b/update/NtlmServlet.java
new file mode 100644 (file)
index 0000000..1304051
--- /dev/null
@@ -0,0 +1,158 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                                      "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import java.util.Enumeration;\r
+\r
+import javax.servlet.ServletConfig;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.UnavailableException;\r
+\r
+import javax.servlet.http.HttpSession;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import jcifs.Config;\r
+import jcifs.UniAddress;\r
+\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+import jcifs.smb.SmbAuthException;\r
+import jcifs.smb.SmbSession;\r
+\r
+import jcifs.util.Base64;\r
+\r
+/**\r
+ * This servlet may be used with pre-2.3 servlet containers\r
+ * to protect content with NTLM HTTP Authentication. Servlets that\r
+ * extend this abstract base class may be authenticatied against an SMB\r
+ * server or domain controller depending on how the\r
+ * <tt>jcifs.smb.client.domain</tt> or <tt>jcifs.http.domainController</tt>\r
+ * properties are be specified. <b>With later containers the\r
+ * <tt>NtlmHttpFilter</tt> should be used/b>. For custom NTLM HTTP Authentication schemes the <tt>NtlmSsp</tt> may be used.\r
+ * <p>\r
+ * Read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a> related information.\r
+ */\r
+\r
+public abstract class NtlmServlet extends HttpServlet {\r
+\r
+    private String defaultDomain;\r
+\r
+       private String domainController;\r
+\r
+       private boolean enableBasic;\r
+\r
+       private boolean insecureBasic;\r
+\r
+       private String realm;\r
+\r
+       public void init(ServletConfig config) throws ServletException {\r
+               super.init(config);\r
+\r
+               /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min.\r
+                */\r
+               Config.setProperty( "jcifs.smb.client.soTimeout", "300000" );\r
+               Config.setProperty( "jcifs.netbios.cachePolicy", "600" );\r
+\r
+               Enumeration e = config.getInitParameterNames();\r
+               String name;\r
+               while (e.hasMoreElements()) {\r
+                       name = (String) e.nextElement();\r
+                       if (name.startsWith("jcifs.")) {\r
+                               Config.setProperty(name, config.getInitParameter(name));\r
+                       }\r
+               }\r
+        defaultDomain = Config.getProperty("jcifs.smb.client.domain");\r
+               domainController = Config.getProperty("jcifs.http.domainController");\r
+               if (domainController == null) domainController = defaultDomain;\r
+               enableBasic = Boolean.valueOf(\r
+                               Config.getProperty("jcifs.http.enableBasic")).booleanValue();\r
+               insecureBasic = Boolean.valueOf(\r
+                               Config.getProperty("jcifs.http.insecureBasic")).booleanValue();\r
+               realm = Config.getProperty("jcifs.http.basicRealm");\r
+               if (realm == null) realm = "jCIFS";\r
+       }\r
+\r
+       protected void service(HttpServletRequest request,\r
+                       HttpServletResponse response) throws ServletException, IOException {\r
+               boolean offerBasic = enableBasic &&\r
+                               (insecureBasic || request.isSecure());\r
+               String msg = request.getHeader("Authorization");\r
+               if (msg != null && (msg.startsWith("NTLM ") ||\r
+                                       (offerBasic && msg.startsWith("Basic ")))) {\r
+            UniAddress dc = UniAddress.getByName(domainController, true);\r
+                       NtlmPasswordAuthentication ntlm;\r
+                       if (msg.startsWith("NTLM ")) {\r
+                               byte[] challenge = SmbSession.getChallenge(dc);\r
+                               ntlm = NtlmSsp.authenticate(request, response, challenge);\r
+                               if (ntlm == null) return;\r
+                       } else {\r
+                               String auth = new String(Base64.decode(msg.substring(6)),\r
+                                               "US-ASCII");\r
+                               int index = auth.indexOf(':');\r
+                               String user = (index != -1) ? auth.substring(0, index) : auth;\r
+                               String password = (index != -1) ? auth.substring(index + 1) :\r
+                                               "";\r
+                               index = user.indexOf('\\');\r
+                               if (index == -1) index = user.indexOf('/');\r
+                               String domain = (index != -1) ? user.substring(0, index) :\r
+                                               defaultDomain;\r
+                               user = (index != -1) ? user.substring(index + 1) : user;\r
+                               ntlm = new NtlmPasswordAuthentication(domain, user, password);\r
+                       }\r
+                       try {\r
+                               SmbSession.logon(dc, ntlm);\r
+                       } catch (SmbAuthException sae) {\r
+                               response.setHeader("WWW-Authenticate", "NTLM");\r
+                               if (offerBasic) {\r
+                                       response.addHeader("WWW-Authenticate", "Basic realm=\"" +\r
+                                                       realm + "\"");\r
+                               }\r
+                               response.setHeader("Connection", "close");\r
+                               response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+                               response.flushBuffer();\r
+                               return;\r
+                       }\r
+                       HttpSession ssn = request.getSession();\r
+                       ssn.setAttribute("NtlmHttpAuth", ntlm);\r
+                       ssn.setAttribute( "ntlmdomain", ntlm.getDomain() );\r
+                       ssn.setAttribute( "ntlmuser", ntlm.getUsername() );\r
+        } else {\r
+            HttpSession ssn = request.getSession(false);\r
+            if (ssn == null || ssn.getAttribute("NtlmHttpAuth") == null) {\r
+                response.setHeader("WWW-Authenticate", "NTLM");\r
+                if (offerBasic) {\r
+                    response.addHeader("WWW-Authenticate", "Basic realm=\"" +\r
+                            realm + "\"");\r
+                }\r
+                response.setHeader("Connection", "close");\r
+                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+                response.flushBuffer();\r
+                return;\r
+            }\r
+               }\r
+               super.service(request, response);\r
+       }\r
+}\r
+\r
diff --git a/update/NtlmSsp.java b/update/NtlmSsp.java
new file mode 100644 (file)
index 0000000..f781f1c
--- /dev/null
@@ -0,0 +1,112 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                                      "Eric Glass" <jcifs at samba dot org>\r
+ *                                      "Jason Pugsley" <jcifs at samba dot org>\r
+ *                                      "skeetz" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.IOException;\r
+\r
+import javax.servlet.ServletException;\r
+\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+\r
+import jcifs.util.Base64;\r
+\r
+import jcifs.ntlmssp.NtlmFlags;\r
+import jcifs.ntlmssp.Type1Message;\r
+import jcifs.ntlmssp.Type2Message;\r
+import jcifs.ntlmssp.Type3Message;\r
+\r
+/**\r
+ * This class is used internally by <tt>NtlmHttpFilter</tt>,\r
+ * <tt>NtlmServlet</tt>, and <tt>NetworkExplorer</tt> to negiotiate password\r
+ * hashes via NTLM SSP with MSIE. It might also be used directly by servlet\r
+ * containers to incorporate similar functionality.\r
+ * <p>\r
+ * How NTLMSSP is used in conjunction with HTTP and MSIE clients is\r
+ * described in an <A HREF="http://www.innovation.ch/java/ntlm.html">NTLM\r
+ * Authentication Scheme for HTTP</A>.  <p> Also, read <a\r
+ * href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and\r
+ * the Network Explorer Servlet</a> related information.\r
+ */\r
+\r
+public class NtlmSsp implements NtlmFlags {\r
+\r
+       /**\r
+        * Calls the static {@link #authenticate(HttpServletRequest,\r
+     * HttpServletResponse, byte[])} method to perform NTLM authentication\r
+     * for the specified servlet request.\r
+        *\r
+        * @param req The request being serviced.\r
+        * @param resp The response.\r
+        * @param challenge The domain controller challenge.\r
+        * @throws IOException If an IO error occurs.\r
+        * @throws ServletException If an error occurs.\r
+        */\r
+       public NtlmPasswordAuthentication doAuthentication(\r
+            HttpServletRequest req, HttpServletResponse resp, byte[] challenge)\r
+                                       throws IOException, ServletException {\r
+        return authenticate(req, resp, challenge);\r
+    }\r
+\r
+       /**\r
+        * Performs NTLM authentication for the servlet request.\r
+        *\r
+        * @param req The request being serviced.\r
+        * @param resp The response.\r
+        * @param challenge The domain controller challenge.\r
+        * @throws IOException If an IO error occurs.\r
+        * @throws ServletException If an error occurs.\r
+        */\r
+       public static NtlmPasswordAuthentication authenticate(\r
+            HttpServletRequest req, HttpServletResponse resp, byte[] challenge)\r
+                                       throws IOException, ServletException {\r
+               String msg = req.getHeader("Authorization");\r
+               if (msg != null && msg.startsWith("NTLM ")) {\r
+                       byte[] src = Base64.decode(msg.substring(5));\r
+                       if (src[8] == 1) {\r
+                               Type1Message type1 = new Type1Message(src);\r
+                               Type2Message type2 = new Type2Message(type1, challenge, null);\r
+                               msg = Base64.encode(type2.toByteArray());\r
+                               resp.setHeader( "WWW-Authenticate", "NTLM " + msg );\r
+                       } else if (src[8] == 3) {\r
+                               Type3Message type3 = new Type3Message(src);\r
+                               byte[] lmResponse = type3.getLMResponse();\r
+                               if (lmResponse == null) lmResponse = new byte[0];\r
+                               byte[] ntResponse = type3.getNTResponse();\r
+                               if (ntResponse == null) ntResponse = new byte[0];\r
+                               return new NtlmPasswordAuthentication(type3.getDomain(),\r
+                                               type3.getUser(), lmResponse, ntResponse);\r
+                       }\r
+               } else {\r
+                       resp.setHeader("WWW-Authenticate", "NTLM");\r
+                       resp.setHeader("Connection", "close");\r
+               }\r
+               resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+               resp.setContentLength( 0 );\r
+               resp.flushBuffer();\r
+               return null;\r
+       }\r
+\r
+}\r
+\r
diff --git a/update/Type1Message.java b/update/Type1Message.java
new file mode 100644 (file)
index 0000000..ce31ec2
--- /dev/null
@@ -0,0 +1,248 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                                      "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import jcifs.netbios.NbtAddress;\r
+\r
+import jcifs.Config;\r
+\r
+/**\r
+ * Represents an NTLMSSP Type-1 message.\r
+ */\r
+public class Type1Message extends NtlmMessage {\r
+\r
+    private static final int DEFAULT_FLAGS;\r
+\r
+    private static final String DEFAULT_DOMAIN;\r
+\r
+    private static final String DEFAULT_WORKSTATION;\r
+\r
+    private String suppliedDomain;\r
+\r
+    private String suppliedWorkstation;\r
+\r
+    static {\r
+        DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
+                (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
+                        NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
+        DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
+        String defaultWorkstation = null;\r
+        try {\r
+            defaultWorkstation = NbtAddress.getLocalHost().getHostName();\r
+        } catch (UnknownHostException ex) { }\r
+        DEFAULT_WORKSTATION = defaultWorkstation;\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-1 message using default values from the current\r
+     * environment.\r
+     */\r
+    public Type1Message() {\r
+        this(getDefaultFlags(), getDefaultDomain(), getDefaultWorkstation());\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-1 message with the specified parameters.\r
+     *\r
+     * @param flags The flags to apply to this message.\r
+     * @param suppliedDomain The supplied authentication domain.\r
+     * @param suppliedWorkstation The supplied workstation name.\r
+     */\r
+    public Type1Message(int flags, String suppliedDomain,\r
+            String suppliedWorkstation) {\r
+        setFlags(flags);\r
+        setSuppliedDomain(suppliedDomain);\r
+        setSuppliedWorkstation(suppliedWorkstation);\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-1 message using the given raw Type-1 material.\r
+     *\r
+     * @param material The raw Type-1 material used to construct this message.\r
+     * @throws IOException If an error occurs while parsing the material.\r
+     */\r
+    public Type1Message(byte[] material) throws IOException {\r
+        parse(material);\r
+    }\r
+\r
+    /**\r
+     * Returns the supplied authentication domain.\r
+     *\r
+     * @return A <code>String</code> containing the supplied domain.\r
+     */\r
+    public String getSuppliedDomain() {\r
+        return suppliedDomain;\r
+    }\r
+\r
+    /**\r
+     * Sets the supplied authentication domain for this message.\r
+     *\r
+     * @param suppliedDomain The supplied domain for this message.\r
+     */\r
+    public void setSuppliedDomain(String suppliedDomain) {\r
+        this.suppliedDomain = suppliedDomain;\r
+    }\r
+\r
+    /**\r
+     * Returns the supplied workstation name.\r
+     * \r
+     * @return A <code>String</code> containing the supplied workstation name.\r
+     */\r
+    public String getSuppliedWorkstation() {\r
+        return suppliedWorkstation;\r
+    }\r
+\r
+    /**\r
+     * Sets the supplied workstation name for this message.\r
+     * \r
+     * @param suppliedWorkstation The supplied workstation for this message.\r
+     */\r
+    public void setSuppliedWorkstation(String suppliedWorkstation) {\r
+        this.suppliedWorkstation = suppliedWorkstation;\r
+    }\r
+\r
+    public byte[] toByteArray() {\r
+        try {\r
+            String suppliedDomain = getSuppliedDomain();\r
+            String suppliedWorkstation = getSuppliedWorkstation();\r
+            int flags = getFlags();\r
+            boolean hostInfo = false;\r
+            byte[] domain = new byte[0];\r
+            if (suppliedDomain != null && suppliedDomain.length() != 0) {\r
+                hostInfo = true;\r
+                flags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;\r
+                domain = suppliedDomain.toUpperCase().getBytes(\r
+                        getOEMEncoding());\r
+            } else {\r
+                flags &= (NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED ^ 0xffffffff);\r
+            }\r
+            byte[] workstation = new byte[0];\r
+            if (suppliedWorkstation != null &&\r
+                    suppliedWorkstation.length() != 0) {\r
+                hostInfo = true;\r
+                flags |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED;\r
+                workstation =\r
+                        suppliedWorkstation.toUpperCase().getBytes(\r
+                                getOEMEncoding());\r
+            } else {\r
+                flags &= (NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED ^\r
+                        0xffffffff);\r
+            }\r
+            byte[] type1 = new byte[hostInfo ?\r
+                    (32 + domain.length + workstation.length) : 16];\r
+            System.arraycopy(NTLMSSP_SIGNATURE, 0, type1, 0, 8);\r
+            writeULong(type1, 8, 1);\r
+            writeULong(type1, 12, flags);\r
+            if (hostInfo) {\r
+                writeSecurityBuffer(type1, 16, 32, domain);\r
+                writeSecurityBuffer(type1, 24, 32 + domain.length, workstation);\r
+            }\r
+            return type1;\r
+        } catch (IOException ex) {\r
+            throw new IllegalStateException(ex.getMessage());\r
+        }\r
+    }\r
+\r
+    public String toString() {\r
+        String suppliedDomain = getSuppliedDomain();\r
+        String suppliedWorkstation = getSuppliedWorkstation();\r
+        int flags = getFlags();\r
+        StringBuffer buffer = new StringBuffer();\r
+        if (suppliedDomain != null) {\r
+            buffer.append("suppliedDomain: ").append(suppliedDomain);\r
+        }\r
+        if (suppliedWorkstation != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("suppliedWorkstation: ").append(suppliedWorkstation);\r
+        }\r
+        if (flags != 0) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("flags: ");\r
+            buffer.append("0x");\r
+            buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
+            buffer.append(Integer.toHexString(flags & 0x0f));\r
+        }\r
+        return buffer.toString();\r
+    }\r
+\r
+    /**\r
+     * Returns the default flags for a generic Type-1 message in the\r
+     * current environment.\r
+     * \r
+     * @return An <code>int</code> containing the default flags.\r
+     */\r
+    public static int getDefaultFlags() {\r
+        return DEFAULT_FLAGS;\r
+    }\r
+\r
+    /**\r
+     * Returns the default domain from the current environment.\r
+     *\r
+     * @return A <code>String</code> containing the default domain.\r
+     */\r
+    public static String getDefaultDomain() {\r
+        return DEFAULT_DOMAIN;\r
+    }\r
+\r
+    /**\r
+     * Returns the default workstation from the current environment.\r
+     *\r
+     * @return A <code>String</code> containing the default workstation.\r
+     */\r
+    public static String getDefaultWorkstation() {\r
+        return DEFAULT_WORKSTATION;\r
+    }\r
+\r
+    private void parse(byte[] material) throws IOException {\r
+        for (int i = 0; i < 8; i++) {\r
+            if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
+                throw new IOException("Not an NTLMSSP message.");\r
+            }\r
+        }\r
+        if (readULong(material, 8) != 1) {\r
+            throw new IOException("Not a Type 1 message.");\r
+        }\r
+        int flags = readULong(material, 12);\r
+        String suppliedDomain = null;\r
+        if ((flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) != 0) {\r
+            byte[] domain = readSecurityBuffer(material, 16);\r
+            suppliedDomain = new String(domain, getOEMEncoding());\r
+        }\r
+        String suppliedWorkstation = null;\r
+        if ((flags & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) != 0) {\r
+            byte[] workstation = readSecurityBuffer(material, 24);\r
+            suppliedWorkstation = new String(workstation, getOEMEncoding());\r
+        }\r
+        setFlags(flags);\r
+        setSuppliedDomain(suppliedDomain);\r
+        setSuppliedWorkstation(suppliedWorkstation);\r
+    }\r
+\r
+}\r
diff --git a/update/Type2Message.java b/update/Type2Message.java
new file mode 100644 (file)
index 0000000..a27db1f
--- /dev/null
@@ -0,0 +1,414 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                   "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import jcifs.Config;\r
+\r
+import jcifs.netbios.NbtAddress;\r
+\r
+/**\r
+ * Represents an NTLMSSP Type-2 message.\r
+ */\r
+public class Type2Message extends NtlmMessage {\r
+\r
+    private static final int DEFAULT_FLAGS;\r
+\r
+    private static final String DEFAULT_DOMAIN;\r
+\r
+    private static final byte[] DEFAULT_TARGET_INFORMATION;\r
+\r
+    private byte[] challenge;\r
+\r
+    private String target;\r
+\r
+    private byte[] context;\r
+\r
+    private byte[] targetInformation;\r
+\r
+    static {\r
+        DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
+                (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
+                        NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
+        DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
+        byte[] domain = new byte[0];\r
+        if (DEFAULT_DOMAIN != null) {\r
+            try {\r
+                domain = DEFAULT_DOMAIN.getBytes("UnicodeLittleUnmarked");\r
+            } catch (IOException ex) { }\r
+        }\r
+        int domainLength = domain.length;\r
+        byte[] server = new byte[0];\r
+        try {\r
+            String host = NbtAddress.getLocalHost().getHostName();\r
+            if (host != null) {\r
+                try {\r
+                    server = host.getBytes("UnicodeLittleUnmarked");\r
+                } catch (IOException ex) { }\r
+            }\r
+        } catch (UnknownHostException ex) { }\r
+        int serverLength = server.length;\r
+        byte[] targetInfo = new byte[(domainLength > 0 ? domainLength + 4 : 0) +\r
+                (serverLength > 0 ? serverLength + 4 : 0) + 4];\r
+        int offset = 0;\r
+        if (domainLength > 0) {\r
+            writeUShort(targetInfo, offset, 2);\r
+            offset += 2;\r
+            writeUShort(targetInfo, offset, domainLength);\r
+            offset += 2;\r
+            System.arraycopy(domain, 0, targetInfo, offset, domainLength);\r
+            offset += domainLength;\r
+        }\r
+        if (serverLength > 0) {\r
+            writeUShort(targetInfo, offset, 1);\r
+            offset += 2;\r
+            writeUShort(targetInfo, offset, serverLength);\r
+            offset += 2;\r
+            System.arraycopy(server, 0, targetInfo, offset, serverLength);\r
+        }\r
+        DEFAULT_TARGET_INFORMATION = targetInfo;\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-2 message using default values from the current\r
+     * environment.\r
+     */\r
+    public Type2Message() {\r
+        this(getDefaultFlags(), null, null);\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-2 message in response to the given Type-1 message\r
+     * using default values from the current environment.\r
+     *\r
+     * @param type1 The Type-1 message which this represents a response to.\r
+     */\r
+    public Type2Message(Type1Message type1) {\r
+        this(type1, null, null);\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-2 message in response to the given Type-1 message.\r
+     *\r
+     * @param type1 The Type-1 message which this represents a response to.\r
+     * @param challenge The challenge from the domain controller/server.\r
+     * @param target The authentication target.\r
+     */\r
+    public Type2Message(Type1Message type1, byte[] challenge, String target) {\r
+        this(getDefaultFlags(type1), challenge, (type1 != null &&\r
+                target == null && type1.getFlag(NTLMSSP_REQUEST_TARGET)) ?\r
+                        getDefaultDomain() : target);\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-2 message with the specified parameters.\r
+     *\r
+     * @param flags The flags to apply to this message.\r
+     * @param challenge The challenge from the domain controller/server.\r
+     * @param target The authentication target.\r
+     */\r
+    public Type2Message(int flags, byte[] challenge, String target) {\r
+        setFlags(flags);\r
+        setChallenge(challenge);\r
+        setTarget(target);\r
+        if (target != null) setTargetInformation(getDefaultTargetInformation());\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-2 message using the given raw Type-2 material.\r
+     *\r
+     * @param material The raw Type-2 material used to construct this message.\r
+     * @throws IOException If an error occurs while parsing the material.\r
+     */\r
+    public Type2Message(byte[] material) throws IOException {\r
+        parse(material);\r
+    }\r
+\r
+    /**\r
+     * Returns the challenge for this message.\r
+     *\r
+     * @return A <code>byte[]</code> containing the challenge.\r
+     */\r
+    public byte[] getChallenge() {\r
+        return challenge;\r
+    }\r
+\r
+    /**\r
+     * Sets the challenge for this message.\r
+     *\r
+     * @param challenge The challenge from the domain controller/server.\r
+     */\r
+    public void setChallenge(byte[] challenge) {\r
+        this.challenge = challenge;\r
+    }\r
+\r
+    /**\r
+     * Returns the authentication target.\r
+     *\r
+     * @return A <code>String</code> containing the authentication target.\r
+     */\r
+    public String getTarget() {\r
+        return target;\r
+    }\r
+\r
+    /**\r
+     * Sets the authentication target.\r
+     *\r
+     * @param target The authentication target.\r
+     */\r
+    public void setTarget(String target) {\r
+        this.target = target;\r
+    }\r
+\r
+    /**\r
+     * Returns the target information block.\r
+     *\r
+     * @return A <code>byte[]</code> containing the target information block.\r
+     * The target information block is used by the client to create an\r
+     * NTLMv2 response.\r
+     */ \r
+    public byte[] getTargetInformation() {\r
+        return targetInformation;\r
+    }\r
+\r
+    /**\r
+     * Sets the target information block.\r
+     * The target information block is used by the client to create\r
+     * an NTLMv2 response.\r
+     * \r
+     * @param targetInformation The target information block.\r
+     */\r
+    public void setTargetInformation(byte[] targetInformation) {\r
+        this.targetInformation = targetInformation;\r
+    }\r
+\r
+    /**\r
+     * Returns the local security context.\r
+     *\r
+     * @return A <code>byte[]</code> containing the local security\r
+     * context.  This is used by the client to negotiate local\r
+     * authentication.\r
+     */\r
+    public byte[] getContext() {\r
+        return context;\r
+    }\r
+\r
+    /**\r
+     * Sets the local security context.  This is used by the client\r
+     * to negotiate local authentication.\r
+     *\r
+     * @param context The local security context.\r
+     */\r
+    public void setContext(byte[] context) {\r
+        this.context = context;\r
+    }\r
+\r
+    public byte[] toByteArray() {\r
+        try {\r
+            String targetName = getTarget();\r
+            byte[] challenge = getChallenge();\r
+            byte[] context = getContext();\r
+            byte[] targetInformation = getTargetInformation();\r
+            int flags = getFlags();\r
+            byte[] target = new byte[0];\r
+            if ((flags & (NTLMSSP_TARGET_TYPE_DOMAIN |\r
+                    NTLMSSP_TARGET_TYPE_SERVER |\r
+                            NTLMSSP_TARGET_TYPE_SHARE)) != 0) {\r
+                if (targetName != null && targetName.length() != 0) {\r
+                    target = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0 ?\r
+                            targetName.getBytes("UnicodeLittleUnmarked") :\r
+                            targetName.toUpperCase().getBytes(getOEMEncoding());\r
+                } else {\r
+                    flags &= (0xffffffff ^ (NTLMSSP_TARGET_TYPE_DOMAIN |\r
+                            NTLMSSP_TARGET_TYPE_SERVER |\r
+                                    NTLMSSP_TARGET_TYPE_SHARE));\r
+                }\r
+            }\r
+            if (targetInformation != null) {\r
+                flags ^= NTLMSSP_NEGOTIATE_TARGET_INFO;\r
+                // empty context is needed for padding when t.i. is supplied.\r
+                if (context == null) context = new byte[8];\r
+            }\r
+            int data = 32;\r
+            if (context != null) data += 8;\r
+            if (targetInformation != null) data += 8;\r
+            byte[] type2 = new byte[data + target.length +\r
+                    (targetInformation != null ? targetInformation.length : 0)];\r
+            System.arraycopy(NTLMSSP_SIGNATURE, 0, type2, 0, 8);\r
+            writeULong(type2, 8, 2);\r
+            writeSecurityBuffer(type2, 12, data, target);\r
+            writeULong(type2, 20, flags);\r
+            System.arraycopy(challenge != null ? challenge : new byte[8], 0,\r
+                    type2, 24, 8);\r
+            if (context != null) System.arraycopy(context, 0, type2, 32, 8);\r
+            if (targetInformation != null) {\r
+                writeSecurityBuffer(type2, 40, data + target.length,\r
+                        targetInformation);\r
+            }\r
+            return type2;\r
+        } catch (IOException ex) {\r
+            throw new IllegalStateException(ex.getMessage());\r
+        }\r
+    }\r
+\r
+    public String toString() {\r
+        String target = getTarget();\r
+        byte[] challenge = getChallenge();\r
+        byte[] context = getContext();\r
+        byte[] targetInformation = getTargetInformation();\r
+        int flags = getFlags();\r
+        StringBuffer buffer = new StringBuffer();\r
+        if (target != null) {\r
+            buffer.append("target: ").append(target);\r
+        }\r
+        if (challenge != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("challenge: ");\r
+            buffer.append("0x");\r
+            for (int i = 0; i < challenge.length; i++) {\r
+                buffer.append(Integer.toHexString((challenge[i] >> 4) & 0x0f));\r
+                buffer.append(Integer.toHexString(challenge[i] & 0x0f));\r
+            }\r
+        }\r
+        if (context != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("context: ");\r
+            buffer.append("0x");\r
+            for (int i = 0; i < context.length; i++) {\r
+                buffer.append(Integer.toHexString((context[i] >> 4) & 0x0f));\r
+                buffer.append(Integer.toHexString(context[i] & 0x0f));\r
+            }\r
+        }\r
+        if (targetInformation != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("targetInformation: ");\r
+            buffer.append("0x");\r
+            for (int i = 0; i < targetInformation.length; i++) {\r
+                buffer.append(Integer.toHexString((targetInformation[i] >> 4) &\r
+                        0x0f));\r
+                buffer.append(Integer.toHexString(targetInformation[i] & 0x0f));\r
+            }\r
+        }\r
+        if (flags != 0) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("flags: ");\r
+            buffer.append("0x");\r
+            buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
+            buffer.append(Integer.toHexString(flags & 0x0f));\r
+        }\r
+        return buffer.toString();\r
+    }\r
+\r
+    /**\r
+     * Returns the default flags for a generic Type-2 message in the\r
+     * current environment.\r
+     *\r
+     * @return An <code>int</code> containing the default flags.\r
+     */\r
+    public static int getDefaultFlags() {\r
+        return DEFAULT_FLAGS;\r
+    }\r
+\r
+    /**\r
+     * Returns the default flags for a Type-2 message created in response\r
+     * to the given Type-1 message in the current environment.\r
+     *\r
+     * @return An <code>int</code> containing the default flags.\r
+     */\r
+    public static int getDefaultFlags(Type1Message type1) {\r
+        if (type1 == null) return DEFAULT_FLAGS;\r
+        int flags = NTLMSSP_NEGOTIATE_NTLM;\r
+        int type1Flags = type1.getFlags();\r
+        flags |= ((type1Flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+                NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM;\r
+        if ((type1Flags & NTLMSSP_REQUEST_TARGET) != 0) {\r
+            String domain = getDefaultDomain();\r
+            if (domain != null) {\r
+                flags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_TARGET_TYPE_DOMAIN;\r
+            }\r
+        }\r
+        return flags;\r
+    }\r
+\r
+    /**\r
+     * Returns the default domain from the current environment.\r
+     *\r
+     * @return A <code>String</code> containing the domain.\r
+     */\r
+    public static String getDefaultDomain() {\r
+        return DEFAULT_DOMAIN;\r
+    }\r
+\r
+    public static byte[] getDefaultTargetInformation() {\r
+        return DEFAULT_TARGET_INFORMATION;\r
+    }\r
+\r
+    private void parse(byte[] material) throws IOException {\r
+        for (int i = 0; i < 8; i++) {\r
+            if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
+                throw new IOException("Not an NTLMSSP message.");\r
+            }\r
+        }\r
+        if (readULong(material, 8) != 2) {\r
+            throw new IOException("Not a Type 2 message.");\r
+        }\r
+        int flags = readULong(material, 20);\r
+        setFlags(flags);\r
+        String target = null;\r
+        byte[] bytes = readSecurityBuffer(material, 12);\r
+        if (bytes.length != 0) {\r
+            target = new String(bytes,\r
+                    ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+                            "UnicodeLittleUnmarked" : getOEMEncoding());\r
+        }\r
+        setTarget(target);\r
+        for (int i = 24; i < 32; i++) {\r
+            if (material[i] != 0) {\r
+                byte[] challenge = new byte[8];\r
+                System.arraycopy(material, 24, challenge, 0, 8);\r
+                setChallenge(challenge);\r
+                break;\r
+            }\r
+        }\r
+        int offset = readULong(material, 16); // offset of targetname start\r
+        if (offset == 32 || material.length == 32) return;\r
+        for (int i = 32; i < 40; i++) {\r
+            if (material[i] != 0) {\r
+                byte[] context = new byte[8];\r
+                System.arraycopy(material, 32, context, 0, 8);\r
+                setContext(context);\r
+                break;\r
+            }\r
+        }\r
+        if (offset == 40 || material.length == 40) return;\r
+        bytes = readSecurityBuffer(material, 40);\r
+        if (bytes.length != 0) setTargetInformation(bytes);\r
+    }\r
+\r
+}\r
diff --git a/update/Type3Message.java b/update/Type3Message.java
new file mode 100644 (file)
index 0000000..123b703
--- /dev/null
@@ -0,0 +1,580 @@
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002  "Michael B. Allen" <jcifs at samba dot org>\r
+ *                   "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import java.security.SecureRandom;\r
+\r
+import jcifs.Config;\r
+\r
+import jcifs.netbios.NbtAddress;\r
+\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+\r
+/**\r
+ * Represents an NTLMSSP Type-3 message.\r
+ */\r
+public class Type3Message extends NtlmMessage {\r
+\r
+    private static final int DEFAULT_FLAGS;\r
+\r
+    private static final String DEFAULT_DOMAIN;\r
+\r
+    private static final String DEFAULT_USER;\r
+\r
+    private static final String DEFAULT_PASSWORD;\r
+\r
+    private static final String DEFAULT_WORKSTATION;\r
+\r
+    private static final int LM_COMPATIBILITY;\r
+\r
+    private static final SecureRandom RANDOM = new SecureRandom();\r
+\r
+    private byte[] lmResponse;\r
+\r
+    private byte[] ntResponse;\r
+\r
+    private String domain;\r
+\r
+    private String user;\r
+\r
+    private String workstation;\r
+\r
+    private byte[] sessionKey;\r
+\r
+    static {\r
+        DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
+                (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
+                        NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
+        DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
+        DEFAULT_USER = Config.getProperty("jcifs.smb.client.username", null);\r
+        DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password",\r
+                null);\r
+        String defaultWorkstation = null;\r
+        try {\r
+            defaultWorkstation = NbtAddress.getLocalHost().getHostName();\r
+        } catch (UnknownHostException ex) { }\r
+        DEFAULT_WORKSTATION = defaultWorkstation;\r
+        LM_COMPATIBILITY = Config.getInt("jcifs.smb.lmCompatibility", 0);\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-3 message using default values from the current\r
+     * environment.\r
+     */\r
+    public Type3Message() {\r
+        setFlags(getDefaultFlags());\r
+        setDomain(getDefaultDomain());\r
+        setUser(getDefaultUser());\r
+        setWorkstation(getDefaultWorkstation());\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-3 message in response to the given Type-2 message\r
+     * using default values from the current environment.\r
+     *\r
+     * @param type2 The Type-2 message which this represents a response to.\r
+     */\r
+    public Type3Message(Type2Message type2) {\r
+        setFlags(getDefaultFlags(type2));\r
+        setWorkstation(getDefaultWorkstation());\r
+        String domain = getDefaultDomain();\r
+        setDomain(domain);\r
+        String user = getDefaultUser();\r
+        setUser(user);\r
+        String password = getDefaultPassword();\r
+        switch (LM_COMPATIBILITY) {\r
+        case 0:\r
+        case 1:\r
+            setLMResponse(getLMResponse(type2, password));\r
+            setNTResponse(getNTResponse(type2, password));\r
+            break;\r
+        case 2:\r
+            byte[] nt = getNTResponse(type2, password);\r
+            setLMResponse(nt);\r
+            setNTResponse(nt);\r
+            break;\r
+        case 3:\r
+        case 4:\r
+        case 5:\r
+            byte[] clientChallenge = new byte[8];\r
+            RANDOM.nextBytes(clientChallenge);\r
+            setLMResponse(getLMv2Response(type2, domain, user, password,\r
+                    clientChallenge));\r
+            /*\r
+            setNTResponse(getNTLMv2Response(type2, domain, user, password,\r
+                    clientChallenge));\r
+            */\r
+            break;\r
+        default:\r
+            setLMResponse(getLMResponse(type2, password));\r
+            setNTResponse(getNTResponse(type2, password));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-3 message in response to the given Type-2 message.\r
+     *\r
+     * @param type2 The Type-2 message which this represents a response to.\r
+     * @param password The password to use when constructing the response.\r
+     * @param domain The domain in which the user has an account.\r
+     * @param user The username for the authenticating user.\r
+     * @param workstation The workstation from which authentication is\r
+     * taking place.\r
+     */\r
+    public Type3Message(Type2Message type2, String password, String domain,\r
+            String user, String workstation) {\r
+        setFlags(getDefaultFlags(type2));\r
+        setDomain(domain);\r
+        setUser(user);\r
+        setWorkstation(workstation);\r
+        switch (LM_COMPATIBILITY) {\r
+        case 0:\r
+        case 1:\r
+            setLMResponse(getLMResponse(type2, password));\r
+            setNTResponse(getNTResponse(type2, password));\r
+            break;\r
+        case 2:\r
+            byte[] nt = getNTResponse(type2, password);\r
+            setLMResponse(nt);\r
+            setNTResponse(nt);\r
+            break;\r
+        case 3:\r
+        case 4:\r
+        case 5:\r
+            byte[] clientChallenge = new byte[8];\r
+            RANDOM.nextBytes(clientChallenge);\r
+            setLMResponse(getLMv2Response(type2, domain, user, password,\r
+                    clientChallenge));\r
+            /*\r
+            setNTResponse(getNTLMv2Response(type2, domain, user, password,\r
+                    clientChallenge));\r
+            */\r
+            break;\r
+        default:\r
+            setLMResponse(getLMResponse(type2, password));\r
+            setNTResponse(getNTResponse(type2, password));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-3 message with the specified parameters.\r
+     *\r
+     * @param flags The flags to apply to this message.\r
+     * @param lmResponse The LanManager/LMv2 response.\r
+     * @param domain The NT/NTLMv2 response.\r
+     * @param domain The domain in which the user has an account.\r
+     * @param user The username for the authenticating user.\r
+     * @param workstation The workstation from which authentication is\r
+     * taking place.\r
+     */\r
+    public Type3Message(int flags, byte[] lmResponse, byte[] ntResponse,\r
+            String domain, String user, String workstation) {\r
+        setFlags(flags);\r
+        setLMResponse(lmResponse);\r
+        setNTResponse(ntResponse);\r
+        setDomain(domain);\r
+        setUser(user);\r
+        setWorkstation(workstation);\r
+    }\r
+\r
+    /**\r
+     * Creates a Type-3 message using the given raw Type-3 material.\r
+     *\r
+     * @param material The raw Type-3 material used to construct this message.\r
+     * @throws IOException If an error occurs while parsing the material.\r
+     */\r
+    public Type3Message(byte[] material) throws IOException {\r
+        parse(material);\r
+    }\r
+\r
+    /**\r
+     * Returns the LanManager/LMv2 response.\r
+     *\r
+     * @return A <code>byte[]</code> containing the LanManager response.\r
+     */\r
+    public byte[] getLMResponse() {\r
+        return lmResponse;\r
+    }\r
+\r
+    /**\r
+     * Sets the LanManager/LMv2 response for this message.\r
+     *\r
+     * @param lmResponse The LanManager response.\r
+     */\r
+    public void setLMResponse(byte[] lmResponse) {\r
+        this.lmResponse = lmResponse;\r
+    }\r
+\r
+    /**\r
+     * Returns the NT/NTLMv2 response.\r
+     *\r
+     * @return A <code>byte[]</code> containing the NT/NTLMv2 response.\r
+     */\r
+    public byte[] getNTResponse() {\r
+        return ntResponse;\r
+    }\r
+\r
+    /**\r
+     * Sets the NT/NTLMv2 response for this message.\r
+     *\r
+     * @param lmResponse The NT/NTLMv2 response.\r
+     */\r
+    public void setNTResponse(byte[] ntResponse) {\r
+        this.ntResponse = ntResponse;\r
+    }\r
+\r
+    /**\r
+     * Returns the domain in which the user has an account.\r
+     *\r
+     * @return A <code>String</code> containing the domain for the user.\r
+     */\r
+    public String getDomain() {\r
+        return domain;\r
+    }\r
+\r
+    /**\r
+     * Sets the domain for this message.\r
+     *\r
+     * @param domain The domain.\r
+     */\r
+    public void setDomain(String domain) {\r
+        this.domain = domain;\r
+    }\r
+\r
+    /**\r
+     * Returns the username for the authenticating user.\r
+     *\r
+     * @return A <code>String</code> containing the user for this message.\r
+     */\r
+    public String getUser() {\r
+        return user;\r
+    }\r
+\r
+    /**\r
+     * Sets the user for this message.\r
+     *\r
+     * @param user The user.\r
+     */\r
+    public void setUser(String user) {\r
+        this.user = user;\r
+    }\r
+\r
+    /**\r
+     * Returns the workstation from which authentication is being performed.\r
+     *\r
+     * @return A <code>String</code> containing the workstation.\r
+     */\r
+    public String getWorkstation() {\r
+        return workstation;\r
+    }\r
+\r
+    /**\r
+     * Sets the workstation for this message.\r
+     *\r
+     * @param workstation The workstation.\r
+     */\r
+    public void setWorkstation(String workstation) {\r
+        this.workstation = workstation;\r
+    }\r
+\r
+    /**\r
+     * Returns the session key.\r
+     *\r
+     * @return A <code>byte[]</code> containing the session key.\r
+     */\r
+    public byte[] getSessionKey() {\r
+        return sessionKey;\r
+    }\r
+\r
+    /**\r
+     * Sets the session key.\r
+     *\r
+     * @param sessionKey The session key.\r
+     */\r
+    public void setSessionKey(byte[] sessionKey) {\r
+        this.sessionKey = sessionKey;\r
+    }\r
+\r
+    public byte[] toByteArray() {\r
+        try {\r
+            int flags = getFlags();\r
+            boolean unicode = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0;\r
+            String oem = unicode ? null : getOEMEncoding();\r
+            String domainName = getDomain();\r
+            byte[] domain = null;\r
+            if (domainName != null && domainName.length() != 0) {\r
+                domain = unicode ?\r
+                        domainName.getBytes("UnicodeLittleUnmarked") :\r
+                                domainName.toUpperCase().getBytes(oem);\r
+            }\r
+            int domainLength = (domain != null) ? domain.length : 0;\r
+            String userName = getUser();\r
+            byte[] user = null;\r
+            if (userName != null && userName.length() != 0) {\r
+                user = unicode ? userName.getBytes("UnicodeLittleUnmarked") :\r
+                        userName.toUpperCase().getBytes(oem);\r
+            }\r
+            int userLength = (user != null) ? user.length : 0;\r
+            String workstationName = getWorkstation();\r
+            byte[] workstation = null;\r
+            if (workstationName != null && workstationName.length() != 0) {\r
+                workstation = unicode ?\r
+                        workstationName.getBytes("UnicodeLittleUnmarked") :\r
+                                workstationName.toUpperCase().getBytes(oem);\r
+            }\r
+            int workstationLength = (workstation != null) ?\r
+                    workstation.length : 0;\r
+            byte[] lmResponse = getLMResponse();\r
+            int lmLength = (lmResponse != null) ? lmResponse.length : 0;\r
+            byte[] ntResponse = getNTResponse();\r
+            int ntLength = (ntResponse != null) ? ntResponse.length : 0;\r
+            byte[] sessionKey = getSessionKey();\r
+            int keyLength = (sessionKey != null) ? sessionKey.length : 0;\r
+            byte[] type3 = new byte[64 + domainLength + userLength +\r
+                    workstationLength + lmLength + ntLength + keyLength];\r
+            System.arraycopy(NTLMSSP_SIGNATURE, 0, type3, 0, 8);\r
+            writeULong(type3, 8, 3);\r
+            int offset = 64;\r
+            writeSecurityBuffer(type3, 12, offset, lmResponse);\r
+            offset += lmLength;\r
+            writeSecurityBuffer(type3, 20, offset, ntResponse);\r
+            offset += ntLength;\r
+            writeSecurityBuffer(type3, 28, offset, domain);\r
+            offset += domainLength;\r
+            writeSecurityBuffer(type3, 36, offset, user);\r
+            offset += userLength;\r
+            writeSecurityBuffer(type3, 44, offset, workstation);\r
+            offset += workstationLength;\r
+            writeSecurityBuffer(type3, 52, offset, sessionKey);\r
+            writeULong(type3, 60, flags);\r
+            return type3;\r
+        } catch (IOException ex) {\r
+            throw new IllegalStateException(ex.getMessage());\r
+        }\r
+    }\r
+\r
+    public String toString() {\r
+        String user = getUser();\r
+        String domain = getDomain();\r
+        String workstation = getWorkstation();\r
+        byte[] lmResponse = getLMResponse();\r
+        byte[] ntResponse = getNTResponse();\r
+        byte[] sessionKey = getSessionKey();\r
+        int flags = getFlags();\r
+        StringBuffer buffer = new StringBuffer();\r
+        if (domain != null) {\r
+            buffer.append("domain: ").append(domain);\r
+        }\r
+        if (user != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("user: ").append(user);\r
+        }\r
+        if (workstation != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("workstation: ").append(workstation);\r
+        }\r
+        if (lmResponse != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("lmResponse: ");\r
+            buffer.append("0x");\r
+            for (int i = 0; i < lmResponse.length; i++) {\r
+                buffer.append(Integer.toHexString((lmResponse[i] >> 4) & 0x0f));\r
+                buffer.append(Integer.toHexString(lmResponse[i] & 0x0f));\r
+            }\r
+        }\r
+        if (ntResponse != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("ntResponse: ");\r
+            buffer.append("0x");\r
+            for (int i = 0; i < ntResponse.length; i++) {\r
+                buffer.append(Integer.toHexString((ntResponse[i] >> 4) & 0x0f));\r
+                buffer.append(Integer.toHexString(ntResponse[i] & 0x0f));\r
+            }\r
+        }\r
+        if (sessionKey != null) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("sessionKey: ");\r
+            buffer.append("0x");\r
+            for (int i = 0; i < sessionKey.length; i++) {\r
+                buffer.append(Integer.toHexString((sessionKey[i] >> 4) & 0x0f));\r
+                buffer.append(Integer.toHexString(sessionKey[i] & 0x0f));\r
+            }\r
+        }\r
+        if (flags != 0) {\r
+            if (buffer.length() > 0) buffer.append("; ");\r
+            buffer.append("flags: ");\r
+            buffer.append("0x");\r
+            buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
+            buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
+            buffer.append(Integer.toHexString(flags & 0x0f));\r
+        }\r
+        return buffer.toString();\r
+    }\r
+\r
+    /**\r
+     * Returns the default flags for a generic Type-3 message in the\r
+     * current environment.\r
+     *\r
+     * @return An <code>int</code> containing the default flags.\r
+     */\r
+    public static int getDefaultFlags() {\r
+        return DEFAULT_FLAGS;\r
+    }\r
+\r
+    /**\r
+     * Returns the default flags for a Type-3 message created in response\r
+     * to the given Type-2 message in the current environment.\r
+     *\r
+     * @return An <code>int</code> containing the default flags.\r
+     */\r
+    public static int getDefaultFlags(Type2Message type2) {\r
+        if (type2 == null) return DEFAULT_FLAGS;\r
+        int flags = NTLMSSP_NEGOTIATE_NTLM;\r
+        flags |= ((type2.getFlags() & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+                NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM;\r
+        return flags;\r
+    }\r
+\r
+    /**\r
+     * Constructs the LanManager response to the given Type-2 message using\r
+     * the supplied password.\r
+     *\r
+     * @param type2 The Type-2 message.\r
+     * @param password The password.\r
+     * @return A <code>byte[]</code> containing the LanManager response.\r
+     */\r
+    public static byte[] getLMResponse(Type2Message type2, String password) {\r
+        if (type2 == null || password == null) return null;\r
+        return NtlmPasswordAuthentication.getPreNTLMResponse(password,\r
+                type2.getChallenge());\r
+    }\r
+\r
+    public static byte[] getLMv2Response(Type2Message type2,\r
+            String domain, String user, String password,\r
+                    byte[] clientChallenge) {\r
+        if (type2 == null || domain == null || user == null ||\r
+                password == null || clientChallenge == null) {\r
+            return null;\r
+        }\r
+        return NtlmPasswordAuthentication.getLMv2Response(domain, user,\r
+                password, type2.getChallenge(), clientChallenge);\r
+    }\r
+\r
+    /**\r
+     * Constructs the NT response to the given Type-2 message using\r
+     * the supplied password.\r
+     *\r
+     * @param type2 The Type-2 message.\r
+     * @param password The password.\r
+     * @return A <code>byte[]</code> containing the NT response.\r
+     */\r
+    public static byte[] getNTResponse(Type2Message type2, String password) {\r
+        if (type2 == null || password == null) return null;\r
+        return NtlmPasswordAuthentication.getNTLMResponse(password,\r
+                type2.getChallenge());\r
+    }\r
+\r
+    /**\r
+     * Returns the default domain from the current environment.\r
+     *\r
+     * @return The default domain.\r
+     */\r
+    public static String getDefaultDomain() {\r
+        return DEFAULT_DOMAIN;\r
+    }\r
+\r
+    /**\r
+     * Returns the default user from the current environment.\r
+     *\r
+     * @return The default user.\r
+     */\r
+    public static String getDefaultUser() {\r
+        return DEFAULT_USER;\r
+    }\r
+\r
+    /**\r
+     * Returns the default password from the current environment.\r
+     *\r
+     * @return The default password.\r
+     */\r
+    public static String getDefaultPassword() {\r
+        return DEFAULT_PASSWORD;\r
+    }\r
+\r
+    /**\r
+     * Returns the default workstation from the current environment.\r
+     *\r
+     * @return The default workstation.\r
+     */\r
+    public static String getDefaultWorkstation() {\r
+        return DEFAULT_WORKSTATION;\r
+    }\r
+\r
+    private void parse(byte[] material) throws IOException {\r
+        for (int i = 0; i < 8; i++) {\r
+            if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
+                throw new IOException("Not an NTLMSSP message.");\r
+            }\r
+        }\r
+        if (readULong(material, 8) != 3) {\r
+            throw new IOException("Not a Type 3 message.");\r
+        }\r
+        byte[] lmResponse = readSecurityBuffer(material, 12);\r
+        int lmResponseOffset = readULong(material, 16);\r
+        byte[] ntResponse = readSecurityBuffer(material, 20);\r
+        int ntResponseOffset = readULong(material, 24);\r
+        byte[] domain = readSecurityBuffer(material, 28);\r
+        int domainOffset = readULong(material, 32);\r
+        byte[] user = readSecurityBuffer(material, 36);\r
+        int userOffset = readULong(material, 40);\r
+        byte[] workstation = readSecurityBuffer(material, 44);\r
+        int workstationOffset = readULong(material, 48);\r
+        int flags;\r
+        String charset;\r
+        if (lmResponseOffset == 52 || ntResponseOffset == 52 ||\r
+                domainOffset == 52 || userOffset == 52 ||\r
+                        workstationOffset == 52) {\r
+            flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_OEM;\r
+            charset = getOEMEncoding();\r
+        } else {\r
+            setSessionKey(readSecurityBuffer(material, 52));\r
+            flags = readULong(material, 60);\r
+            charset = ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+                "UnicodeLittleUnmarked" : getOEMEncoding();\r
+        }\r
+        setFlags(flags);\r
+        setLMResponse(lmResponse);\r
+        // NTLMv2 issues w/cross-domain authentication; leave NT empty if >= 3\r
+        if (LM_COMPATIBILITY < 3) setNTResponse(ntResponse);\r
+        setDomain(new String(domain, charset));\r
+        setUser(new String(user, charset));\r
+        setWorkstation(new String(workstation, charset));\r
+    }\r
+\r
+}\r