+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
+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
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>
* 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
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() ) {
+++ /dev/null
-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" );
- }
-}
-
--- /dev/null
+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
synchronized( dirList ) {
while( dirList.isEmpty() ) {
-System.err.println( "workingThreads=" + workingThreads );
+//System.err.println( "workingThreads=" + workingThreads );
if( workingThreads == 0 ) {
return; // done
}
workingThreads++;
}
- String[] l = e.dir.list();
+ SmbFile[] l = e.dir.listFiles();
int n = maxDepth - e.depth;
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 ) {
}
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" );
}
}
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 );
}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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;
+ }
+ }
+
+}
/* 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
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
* 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;
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() {
}
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 );
/* 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
public Principal getUserPrincipal() {
return principal;
}
+ public String getAuthType() {
+ return "NTLM";
+ }
}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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);
+ }
+}
/* 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
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
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);
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);
}
/* 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
* 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;
}
+
}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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;
+ }
+
+}
i++;
}
}
+ } else if( hostName.hexCode == 0x1D ) {
+ calledName = SMBSERVER_NAME;
}
return calledName;
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
*/
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
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();
} else {
this.calledName = new Name( calledName, 0x20, null );
}
+ soTimeout = Config.getInt( "jcifs.netbios.soTimeout", DEFAULT_SO_TIMEOUT );
connect();
}
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 );
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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;
+
+}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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();
+
+}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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);
+ }
+
+}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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);
+ }
+
+}
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
+ * "Eric Glass" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.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));
+ }
+
+}
/* 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
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;
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
};
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 );
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 =
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;
}
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
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
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.
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
/* 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
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 ) {
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.smb;
+
+import jcifs.Config;
+import java.io.IOException;
+import java.io.InputStream;
+
+class SmbComSessionSetupAndX extends AndXServerMessageBlock {
+
+ static final int BATCH_LIMIT =
+ Config.getInt( "jcifs.smb.client.SessionSetupAndX.TreeConnectAndX", 1 );
+
+ byte[] accountPassword, unicodePassword;
+ int passwordLength, unicodePasswordLength;
+ int negotiatedMaxBufferSize,
+ negotiatedMaxMpxCount,
+ vcNumber,
+ sessionKey;
+ String accountName,
+ primaryDomain,
+ nativeOs,
+ nativeLanMan;
+
+ SmbSession session;
+
+ SmbComSessionSetupAndX( SmbSession session, ServerMessageBlock andx ) {
+ super( andx );
+ command = SMB_COM_SESSION_SETUP_ANDX;
+ this.session = session;
+ }
+
+ int getBatchLimit( byte command ) {
+ return command == SMB_COM_TREE_CONNECT_ANDX ? BATCH_LIMIT : 0;
+ }
+ int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) {
+ int start = dstIndex;
+
+ if( session.transport.server.security == SECURITY_USER &&
+ ( session.auth.hashesExternal ||
+ session.auth.password.length() > 0 )) {
+ if( session.transport.server.encryptedPasswords ) {
+ // encrypted
+ accountPassword = session.auth.getAnsiHash( session.transport.server.encryptionKey );
+ unicodePassword = session.auth.getUnicodeHash( session.transport.server.encryptionKey );
+ // fix for NTLMv2 or empty NTLM/LM
+ passwordLength = accountPassword.length;
+ unicodePasswordLength = unicodePassword.length;
+ } else if( Config.getBoolean( "jcifs.smb.client.disablePlainTextPasswords", true )) {
+ throw new RuntimeException( "Plain text passwords are disabled" );
+ } else if( useUnicode ) {
+ // plain text
+ String password = session.auth.getPassword();
+ accountPassword = new byte[0];
+ passwordLength = 0;
+ unicodePassword = new byte[(password.length() + 1) * 2];
+ unicodePasswordLength = writeString( password, unicodePassword, 0 );
+ } else {
+ // plain text
+ String password = session.auth.getPassword();
+ accountPassword = new byte[(password.length() + 1) * 2];
+ passwordLength = writeString( password, accountPassword, 0 );
+ unicodePassword = new byte[0];
+ unicodePasswordLength = 0;
+ }
+ } else {
+ // no password in session setup
+ passwordLength = unicodePasswordLength = 0;
+ }
+
+ negotiatedMaxBufferSize = session.transport.negotiatedMaxBufferSize;
+ negotiatedMaxMpxCount = session.transport.negotiatedMaxMpxCount;
+ vcNumber = session.transport.client.vcNumber;
+ sessionKey = session.transport.client.sessionKey;
+
+ writeInt2( negotiatedMaxBufferSize, dst, dstIndex );
+ dstIndex += 2;
+ writeInt2( negotiatedMaxMpxCount, dst, dstIndex );
+ dstIndex += 2;
+ writeInt2( vcNumber, dst, dstIndex );
+ dstIndex += 2;
+ writeInt4( sessionKey, dst, dstIndex );
+ dstIndex += 4;
+ writeInt2( passwordLength, dst, dstIndex );
+ dstIndex += 2;
+ writeInt2( unicodePasswordLength, dst, dstIndex );
+ dstIndex += 2;
+ dst[dstIndex++] = (byte)0x00;
+ dst[dstIndex++] = (byte)0x00;
+ dst[dstIndex++] = (byte)0x00;
+ dst[dstIndex++] = (byte)0x00;
+ writeInt4( session.transport.client.capabilities, dst, dstIndex );
+ dstIndex += 4;
+
+ return dstIndex - start;
+ }
+ int writeBytesWireFormat( byte[] dst, int dstIndex ) {
+ int start = dstIndex;
+
+ accountName = session.auth.username.toUpperCase();
+ primaryDomain = session.auth.domain.toUpperCase();
+ nativeOs = session.transport.client.nativeOs;
+ nativeLanMan = session.transport.client.nativeLanMan;
+
+ if( session.transport.server.security == SECURITY_USER &&
+ ( session.auth.hashesExternal ||
+ session.auth.password.length() > 0 )) {
+ System.arraycopy( accountPassword, 0, dst, dstIndex, passwordLength );
+ dstIndex += passwordLength;
+ System.arraycopy( unicodePassword, 0, dst, dstIndex, unicodePasswordLength );
+ dstIndex += unicodePasswordLength;
+ }
+ if( useUnicode ) {
+ // at least NT 4 observed needing this only with unicode
+ dst[dstIndex++] = (byte)'\0';
+ }
+
+ dstIndex += writeString( accountName, dst, dstIndex );
+ dstIndex += writeString( primaryDomain, dst, dstIndex );
+ dstIndex += writeString( nativeOs, dst, dstIndex );
+/*
+ // at least NT 4 observed with 2 zero bytes here
+ dst[dstIndex++] = (byte)0x00;
+ dst[dstIndex++] = (byte)0x00;
+This still isn't quite right.
+*/
+ dstIndex += writeString( nativeLanMan, dst, dstIndex );
+
+ return dstIndex - start;
+ }
+ int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) {
+ return 0;
+ }
+ int readBytesWireFormat( byte[] buffer, int bufferIndex ) {
+ return 0;
+ }
+ int readBytesDirectWireFormat( InputStream in, int byteCount ) throws IOException {
+ return 0;
+ }
+ public String toString() {
+ String result = new String( "SmbComSessionSetupAndX[" +
+ super.toString() +
+ ",maxBufferSize=" + negotiatedMaxBufferSize +
+ ",maxMpxCount=" + negotiatedMaxMpxCount +
+ ",vcNumber=" + vcNumber +
+ ",sessionKey=" + sessionKey +
+ ",passwordLength=" + passwordLength +
+ ",unicodePasswordLength=" + unicodePasswordLength +
+ ",capabilities=" + session.transport.client.capabilities +
+ ",accountName=" + accountName +
+ ",primaryDomain=" + primaryDomain +
+ ",nativeOs=" + nativeOs +
+ ",nativeLanMan=" + nativeLanMan + "]" );
+ return result;
+ }
+}
txn_buf = null;
}
+ public void reset() {
+ bufDataStart = 0;
+ isPrimary = hasMore = true;
+ parametersDone = dataDone = false;
+ }
public boolean hasMoreElements() {
return hasMore;
}
/* 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
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 {
-/* 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
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
* 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
*/
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 );
}
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;
if( getUncPath0().length() == 1 ) {
return true;
}
- exists();
+ if (!exists()) return false;
return ( attributes & ATTR_DIRECTORY ) == ATTR_DIRECTORY;
}
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;
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;
",newFileName=" + dest.unc );
attrExpiration = sizeExpiration = 0;
+ dest.attrExpiration = 0;
/*
* Rename Request / Response
private SmbFile file;
private long fp;
- private int off, readSize, openFlags;
+ private int readSize, openFlags;
private byte[] tmp = new byte[1];
/**
// 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
}
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;
+ }
}
-/* 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
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 ) {
uid = response.uid;
sessionSetup = true;
+}
}
- synchronized void logoff( boolean inError ) {
+ void logoff( boolean inError ) {
+synchronized( transport ) {
if( sessionSetup == false ) {
return;
}
}
}
sessionSetup = false;
+}
}
public String toString() {
return "SmbSession[accountName=" + auth.username +
}
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 ) {
default:
throw new SmbException( response.errorCode );
}
+
}
void sendTransaction( SmbComTransaction request,
SmbComTransactionResponse response ) throws SmbException {
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;
tid = response.tid;
service = response.service;
treeConnected = true;
- }
}
}
- synchronized void treeDisconnect( boolean inError ) {
+ void treeDisconnect( boolean inError ) {
+synchronized( session.transport ) {
if( treeConnected == false ) {
return;
}
}
}
treeConnected = false;
+}
}
public String toString() {
-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;
+ }
+
+}
--- /dev/null
+/* 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);
+ }
+
+}
+++ /dev/null
-/*
- * @(#)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> </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 "x-www-form-urlencoded"
- * 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;
- }
-}
-
-
--- /dev/null
+/* Encodes and decodes to and from Base64 notation.\r
+ * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.util;\r
+\r
+public class Base64 {\r
+\r
+ private static final String ALPHABET =\r
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+\r
+ /**\r
+ * Base-64 encodes the supplied block of data. Line wrapping is not\r
+ * applied on output.\r
+ *\r
+ * @param bytes The block of data that is to be Base-64 encoded.\r
+ * @return A <code>String</code> containing the encoded data.\r
+ */\r
+ public static String encode(byte[] bytes) {\r
+ int length = bytes.length;\r
+ if (length == 0) return "";\r
+ StringBuffer buffer =\r
+ new StringBuffer((int) Math.ceil((double) length / 3d) * 4);\r
+ int remainder = length % 3;\r
+ length -= remainder;\r
+ int block;\r
+ int i = 0;\r
+ while (i < length) {\r
+ block = ((bytes[i++] & 0xff) << 16) | ((bytes[i++] & 0xff) << 8) |\r
+ (bytes[i++] & 0xff);\r
+ buffer.append(ALPHABET.charAt(block >>> 18));\r
+ buffer.append(ALPHABET.charAt((block >>> 12) & 0x3f));\r
+ buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));\r
+ buffer.append(ALPHABET.charAt(block & 0x3f));\r
+ }\r
+ if (remainder == 0) return buffer.toString();\r
+ if (remainder == 1) {\r
+ block = (bytes[i] & 0xff) << 4;\r
+ buffer.append(ALPHABET.charAt(block >>> 6));\r
+ buffer.append(ALPHABET.charAt(block & 0x3f));\r
+ buffer.append("==");\r
+ return buffer.toString();\r
+ }\r
+ block = (((bytes[i++] & 0xff) << 8) | ((bytes[i]) & 0xff)) << 2;\r
+ buffer.append(ALPHABET.charAt(block >>> 12));\r
+ buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));\r
+ buffer.append(ALPHABET.charAt(block & 0x3f));\r
+ buffer.append("=");\r
+ return buffer.toString();\r
+ }\r
+\r
+ /**\r
+ * Decodes the supplied Base-64 encoded string.\r
+ *\r
+ * @param string The Base-64 encoded string that is to be decoded.\r
+ * @return A <code>byte[]</code> containing the decoded data block.\r
+ */\r
+ public static byte[] decode(String string) {\r
+ int length = string.length();\r
+ if (length == 0) return new byte[0];\r
+ int pad = (string.charAt(length - 2) == '=') ? 2 :\r
+ (string.charAt(length - 1) == '=') ? 1 : 0;\r
+ int size = length * 3 / 4 - pad;\r
+ byte[] buffer = new byte[size];\r
+ int block;\r
+ int i = 0;\r
+ int index = 0;\r
+ while (i < length) {\r
+ block = (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 18 |\r
+ (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 12 |\r
+ (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 6 |\r
+ (ALPHABET.indexOf(string.charAt(i++)) & 0xff);\r
+ buffer[index++] = (byte) (block >>> 16);\r
+ if (index < size) buffer[index++] = (byte) ((block >>> 8) & 0xff);\r
+ if (index < size) buffer[index++] = (byte) (block & 0xff);\r
+ }\r
+ return buffer;\r
+ }\r
+\r
+}\r
--- /dev/null
+/* HMACT64 keyed hashing algorithm\r
+ * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.util;\r
+\r
+import java.security.MessageDigest;\r
+\r
+/**\r
+ * This is an implementation of the HMACT64 keyed hashing algorithm.\r
+ * HMACT64 is defined by Luke Leighton as a modified HMAC-MD5 (RFC 2104)\r
+ * in which the key is truncated at 64 bytes (rather than being hashed\r
+ * via MD5).\r
+ */ \r
+public class HMACT64 extends MessageDigest implements Cloneable {\r
+\r
+ private static final int BLOCK_LENGTH = 64;\r
+\r
+ private static final byte IPAD = (byte) 0x36;\r
+\r
+ private static final byte OPAD = (byte) 0x5c;\r
+\r
+ private MessageDigest md5;\r
+\r
+ private byte[] ipad = new byte[BLOCK_LENGTH];\r
+\r
+ private byte[] opad = new byte[BLOCK_LENGTH];\r
+\r
+ /**\r
+ * Creates an HMACT64 instance which uses the given secret key material.\r
+ *\r
+ * @param key The key material to use in hashing.\r
+ */\r
+ public HMACT64(byte[] key) {\r
+ super("HMACT64");\r
+ int length = Math.min(key.length, BLOCK_LENGTH);\r
+ for (int i = 0; i < length; i++) {\r
+ ipad[i] = (byte) (key[i] ^ IPAD);\r
+ opad[i] = (byte) (key[i] ^ OPAD);\r
+ }\r
+ for (int i = length; i < BLOCK_LENGTH; i++) {\r
+ ipad[i] = IPAD;\r
+ opad[i] = OPAD;\r
+ }\r
+ try {\r
+ md5 = MessageDigest.getInstance("MD5");\r
+ } catch (Exception ex) {\r
+ throw new IllegalStateException(ex.getMessage());\r
+ }\r
+ engineReset();\r
+ }\r
+\r
+ private HMACT64(HMACT64 hmac) throws CloneNotSupportedException {\r
+ super("HMACT64");\r
+ this.ipad = hmac.ipad;\r
+ this.opad = hmac.opad;\r
+ this.md5 = (MessageDigest) hmac.md5.clone();\r
+ }\r
+\r
+ public Object clone() {\r
+ try {\r
+ return new HMACT64(this);\r
+ } catch (CloneNotSupportedException ex) {\r
+ throw new IllegalStateException(ex.getMessage());\r
+ }\r
+ }\r
+\r
+ protected byte[] engineDigest() {\r
+ byte[] digest = md5.digest();\r
+ md5.update(opad);\r
+ return md5.digest(digest);\r
+ }\r
+\r
+ protected int engineDigest(byte[] buf, int offset, int len) {\r
+ byte[] digest = md5.digest();\r
+ md5.update(opad);\r
+ md5.update(digest);\r
+ try {\r
+ return md5.digest(buf, offset, len);\r
+ } catch (Exception ex) {\r
+ throw new IllegalStateException();\r
+ }\r
+ }\r
+\r
+ protected int engineGetDigestLength() {\r
+ return md5.getDigestLength();\r
+ }\r
+\r
+ protected void engineReset() {\r
+ md5.reset();\r
+ md5.update(ipad);\r
+ }\r
+\r
+ protected void engineUpdate(byte b) {\r
+ md5.update(b);\r
+ }\r
+\r
+ protected void engineUpdate(byte[] input, int offset, int len) {\r
+ md5.update(input, offset, len);\r
+ }\r
+\r
+}\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Jason Pugsley" <jcifs at samba dot org>\r
+ * "skeetz" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.*;\r
+import java.util.Enumeration;\r
+import java.net.UnknownHostException;\r
+import javax.servlet.*;\r
+import javax.servlet.http.*;\r
+import jcifs.*;\r
+import jcifs.smb.SmbSession;\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+import jcifs.smb.SmbAuthException;\r
+import jcifs.util.Base64;\r
+\r
+/**\r
+ * This servlet Filter can be used to negotiate password hashes with\r
+ * MSIE clients using NTLM SSP. This is similar to <tt>Authentication:\r
+ * BASIC</tt> but weakly encrypted and without requiring the user to re-supply\r
+ * authentication credentials.\r
+ * <p>\r
+ * Read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a> for complete details.\r
+ */\r
+\r
+public class NtlmHttpFilter implements Filter {\r
+\r
+ private String defaultDomain;\r
+\r
+ private String domainController;\r
+\r
+ private boolean enableBasic;\r
+\r
+ private boolean insecureBasic;\r
+\r
+ private String realm;\r
+\r
+ public void init( FilterConfig filterConfig ) throws ServletException {\r
+ String name;\r
+\r
+ /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min.\r
+ */\r
+ Config.setProperty( "jcifs.smb.client.soTimeout", "300000" );\r
+ Config.setProperty( "jcifs.netbios.cachePolicy", "600" );\r
+\r
+ Enumeration e = filterConfig.getInitParameterNames();\r
+ while( e.hasMoreElements() ) {\r
+ name = (String)e.nextElement();\r
+ if( name.startsWith( "jcifs." )) {\r
+ Config.setProperty( name, filterConfig.getInitParameter( name ));\r
+ }\r
+ }\r
+ defaultDomain = Config.getProperty("jcifs.smb.client.domain");\r
+ domainController = Config.getProperty( "jcifs.http.domainController" );\r
+ if( domainController == null ) domainController = defaultDomain;\r
+ enableBasic = Boolean.valueOf(\r
+ Config.getProperty("jcifs.http.enableBasic")).booleanValue();\r
+ insecureBasic = Boolean.valueOf(\r
+ Config.getProperty("jcifs.http.insecureBasic")).booleanValue();\r
+ realm = Config.getProperty("jcifs.http.basicRealm");\r
+ if (realm == null) realm = "jCIFS";\r
+ }\r
+ public void destroy() {\r
+ }\r
+ public void doFilter( ServletRequest request,\r
+ ServletResponse response,\r
+ FilterChain chain ) throws IOException, ServletException {\r
+ HttpServletRequest req;\r
+ HttpServletResponse resp;\r
+ UniAddress dc;\r
+ String msg;\r
+\r
+ NtlmPasswordAuthentication ntlm = null;\r
+ req = (HttpServletRequest)request;\r
+ resp = (HttpServletResponse)response;\r
+ msg = req.getHeader( "Authorization" );\r
+ boolean offerBasic = enableBasic && (insecureBasic || req.isSecure());\r
+\r
+ if( msg != null && (msg.startsWith( "NTLM " ) ||\r
+ (offerBasic && msg.startsWith("Basic ")))) {\r
+ dc = UniAddress.getByName( domainController, true );\r
+ if (msg.startsWith("NTLM ")) {\r
+ byte[] challenge = SmbSession.getChallenge( dc );\r
+ if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) {\r
+ return;\r
+ }\r
+ } else {\r
+ String auth = new String(Base64.decode(msg.substring(6)),\r
+ "US-ASCII");\r
+ int index = auth.indexOf(':');\r
+ String user = (index != -1) ? auth.substring(0, index) : auth;\r
+ String password = (index != -1) ? auth.substring(index + 1) :\r
+ "";\r
+ index = user.indexOf('\\');\r
+ if (index == -1) index = user.indexOf('/');\r
+ String domain = (index != -1) ? user.substring(0, index) :\r
+ defaultDomain;\r
+ user = (index != -1) ? user.substring(index + 1) : user;\r
+ ntlm = new NtlmPasswordAuthentication(domain, user, password);\r
+ }\r
+ try {\r
+ SmbSession.logon( dc, ntlm );\r
+ } catch( SmbAuthException sae ) {\r
+ resp.setHeader( "WWW-Authenticate", "NTLM" );\r
+ if (offerBasic) {\r
+ resp.addHeader( "WWW-Authenticate", "Basic realm=\"" +\r
+ realm + "\"");\r
+ }\r
+ resp.setHeader( "Connection", "close" );\r
+ resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );\r
+ resp.flushBuffer();\r
+ return;\r
+ }\r
+ req.getSession().setAttribute( "NtlmHttpAuth", ntlm );\r
+ } else {\r
+ HttpSession ssn = req.getSession(false);\r
+ if (ssn == null || (ntlm = (NtlmPasswordAuthentication)\r
+ ssn.getAttribute("NtlmHttpAuth")) == null) {\r
+ resp.setHeader( "WWW-Authenticate", "NTLM" );\r
+ if (offerBasic) {\r
+ resp.addHeader( "WWW-Authenticate", "Basic realm=\"" +\r
+ realm + "\"");\r
+ }\r
+ resp.setHeader( "Connection", "close" );\r
+ resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );\r
+ resp.flushBuffer();\r
+ return;\r
+ }\r
+ }\r
+\r
+ chain.doFilter( new NtlmHttpServletRequest( req, ntlm ), response );\r
+ }\r
+\r
+ // Added by cgross to work with weblogic 6.1.\r
+ public void setFilterConfig( FilterConfig f ) {\r
+ try {\r
+ init( f );\r
+ } catch( Exception e ) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ public FilterConfig getFilterConfig() {\r
+ return null;\r
+ }\r
+}\r
+\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.InputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import java.net.Authenticator;\r
+import java.net.HttpURLConnection;\r
+import java.net.PasswordAuthentication;\r
+import java.net.ProtocolException;\r
+import java.net.URL;\r
+import java.net.URLDecoder;\r
+\r
+import java.security.Permission;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import jcifs.Config;\r
+\r
+import jcifs.ntlmssp.NtlmFlags;\r
+import jcifs.ntlmssp.NtlmMessage;\r
+import jcifs.ntlmssp.Type1Message;\r
+import jcifs.ntlmssp.Type2Message;\r
+import jcifs.ntlmssp.Type3Message;\r
+\r
+import jcifs.util.Base64;\r
+\r
+/**\r
+ * Wraps an <code>HttpURLConnection</code> to provide NTLM authentication\r
+ * services.\r
+ *\r
+ * Please read <a href="../../../httpclient.html">Using jCIFS NTLM Authentication for HTTP Connections</a>.\r
+ */\r
+public class NtlmHttpURLConnection extends HttpURLConnection {\r
+\r
+ private static final int MAX_REDIRECTS =\r
+ Integer.parseInt(System.getProperty("http.maxRedirects", "20"));\r
+\r
+ private static final int LM_COMPATIBILITY =\r
+ Config.getInt("jcifs.smb.lmCompatibility", 0);\r
+\r
+ private static final String DEFAULT_DOMAIN;\r
+\r
+ private HttpURLConnection connection;\r
+\r
+ private Map requestProperties;\r
+\r
+ private Map headerFields;\r
+\r
+ private String authProperty;\r
+\r
+ private String method;\r
+\r
+ static {\r
+ String domain = System.getProperty("http.auth.ntlm.domain");\r
+ if (domain == null) domain = Type3Message.getDefaultDomain();\r
+ DEFAULT_DOMAIN = domain;\r
+ }\r
+\r
+ public NtlmHttpURLConnection(HttpURLConnection connection) {\r
+ super(connection.getURL());\r
+ this.connection = connection;\r
+ requestProperties = new HashMap();\r
+ }\r
+\r
+ public void connect() throws IOException {\r
+ if (connected) return;\r
+ doConnect();\r
+ connected = true;\r
+ }\r
+\r
+ public URL getURL() {\r
+ return connection.getURL();\r
+ }\r
+\r
+ public int getContentLength() {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getContentLength();\r
+ }\r
+\r
+ public String getContentType() {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getContentType();\r
+ }\r
+\r
+ public String getContentEncoding() {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getContentEncoding();\r
+ }\r
+\r
+ public long getExpiration() {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getExpiration();\r
+ }\r
+\r
+ public long getDate() {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getDate();\r
+ }\r
+\r
+ public long getLastModified() {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getLastModified();\r
+ }\r
+\r
+ public String getHeaderField(String header) {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getHeaderField(header);\r
+ }\r
+\r
+ private synchronized Map getHeaderFields0() {\r
+ if (headerFields != null) return headerFields;\r
+ Map map = new HashMap();\r
+ String key = connection.getHeaderFieldKey(0);\r
+ String value = connection.getHeaderField(0);\r
+ for (int i = 1; key != null || value != null; i++) {\r
+ List values = (List) map.get(key);\r
+ if (values == null) {\r
+ values = new ArrayList();\r
+ map.put(key, values);\r
+ }\r
+ values.add(value);\r
+ key = connection.getHeaderFieldKey(i);\r
+ value = connection.getHeaderField(i);\r
+ }\r
+ Iterator entries = map.entrySet().iterator();\r
+ while (entries.hasNext()) {\r
+ Map.Entry entry = (Map.Entry) entries.next();\r
+ entry.setValue(Collections.unmodifiableList((List)\r
+ entry.getValue()));\r
+ }\r
+ return (headerFields = Collections.unmodifiableMap(map));\r
+ }\r
+\r
+ public Map getHeaderFields() {\r
+ synchronized (this) {\r
+ if (headerFields != null) return headerFields;\r
+ }\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return getHeaderFields0();\r
+ }\r
+\r
+ public int getHeaderFieldInt(String header, int def) {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getHeaderFieldInt(header, def);\r
+ }\r
+\r
+ public long getHeaderFieldDate(String header, long def) {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getHeaderFieldDate(header, def);\r
+ }\r
+\r
+ public String getHeaderFieldKey(int index) {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getHeaderFieldKey(index);\r
+ }\r
+\r
+ public String getHeaderField(int index) {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getHeaderField(index);\r
+ }\r
+\r
+ public Object getContent() throws IOException {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getContent();\r
+ }\r
+\r
+ public Object getContent(Class[] classes) throws IOException {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getContent(classes);\r
+ }\r
+\r
+ public Permission getPermission() throws IOException {\r
+ return connection.getPermission();\r
+ }\r
+\r
+ public InputStream getInputStream() throws IOException {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getInputStream();\r
+ }\r
+\r
+ public OutputStream getOutputStream() throws IOException {\r
+ try {\r
+ connect();\r
+ } catch (IOException ex) { }\r
+ return connection.getOutputStream();\r
+ }\r
+\r
+ public String toString() {\r
+ return connection.toString();\r
+ }\r
+\r
+ public void setDoInput(boolean doInput) {\r
+ connection.setDoInput(doInput);\r
+ this.doInput = doInput;\r
+ }\r
+\r
+ public boolean getDoInput() {\r
+ return connection.getDoInput();\r
+ }\r
+\r
+ public void setDoOutput(boolean doOutput) {\r
+ connection.setDoOutput(doOutput);\r
+ this.doOutput = doOutput;\r
+ }\r
+\r
+ public boolean getDoOutput() {\r
+ return connection.getDoOutput();\r
+ }\r
+\r
+ public void setAllowUserInteraction(boolean allowUserInteraction) {\r
+ connection.setAllowUserInteraction(allowUserInteraction);\r
+ this.allowUserInteraction = allowUserInteraction;\r
+ }\r
+\r
+ public boolean getAllowUserInteraction() {\r
+ return connection.getAllowUserInteraction();\r
+ }\r
+\r
+ public void setUseCaches(boolean useCaches) {\r
+ connection.setUseCaches(useCaches);\r
+ this.useCaches = useCaches;\r
+ }\r
+\r
+ public boolean getUseCaches() {\r
+ return connection.getUseCaches();\r
+ }\r
+\r
+ public void setIfModifiedSince(long ifModifiedSince) {\r
+ connection.setIfModifiedSince(ifModifiedSince);\r
+ this.ifModifiedSince = ifModifiedSince;\r
+ }\r
+\r
+ public long getIfModifiedSince() {\r
+ return connection.getIfModifiedSince();\r
+ }\r
+\r
+ public boolean getDefaultUseCaches() {\r
+ return connection.getDefaultUseCaches();\r
+ }\r
+\r
+ public void setDefaultUseCaches(boolean defaultUseCaches) {\r
+ connection.setDefaultUseCaches(defaultUseCaches);\r
+ }\r
+\r
+ public void setRequestProperty(String key, String value) {\r
+ if (key == null) throw new NullPointerException();\r
+ List values = new ArrayList();\r
+ values.add(value);\r
+ boolean found = false;\r
+ synchronized (requestProperties) {\r
+ Iterator entries = requestProperties.entrySet().iterator();\r
+ while (entries.hasNext()) {\r
+ Map.Entry entry = (Map.Entry) entries.next();\r
+ if (key.equalsIgnoreCase((String) entry.getKey())) {\r
+ entry.setValue(value);\r
+ found = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!found) requestProperties.put(key, values);\r
+ }\r
+ connection.setRequestProperty(key, value);\r
+ }\r
+\r
+ public void addRequestProperty(String key, String value) {\r
+ if (key == null) throw new NullPointerException();\r
+ List values = null;\r
+ synchronized (requestProperties) {\r
+ Iterator entries = requestProperties.entrySet().iterator();\r
+ while (entries.hasNext()) {\r
+ Map.Entry entry = (Map.Entry) entries.next();\r
+ if (key.equalsIgnoreCase((String) entry.getKey())) {\r
+ values = (List) entry.getValue();\r
+ values.add(value);\r
+ break;\r
+ }\r
+ }\r
+ if (values == null) {\r
+ values = new ArrayList();\r
+ values.add(value);\r
+ requestProperties.put(key, values);\r
+ }\r
+ }\r
+ // 1.3-compatible.\r
+ StringBuffer buffer = new StringBuffer();\r
+ Iterator propertyValues = values.iterator();\r
+ while (propertyValues.hasNext()) {\r
+ buffer.append(propertyValues.next());\r
+ if (propertyValues.hasNext()) {\r
+ buffer.append(", ");\r
+ }\r
+ }\r
+ connection.setRequestProperty(key, buffer.toString());\r
+ }\r
+\r
+ public String getRequestProperty(String key) {\r
+ return connection.getRequestProperty(key);\r
+ }\r
+\r
+ public Map getRequestProperties() {\r
+ Map map = new HashMap();\r
+ synchronized (requestProperties) {\r
+ Iterator entries = requestProperties.entrySet().iterator();\r
+ while (entries.hasNext()) {\r
+ Map.Entry entry = (Map.Entry) entries.next();\r
+ map.put(entry.getKey(),\r
+ Collections.unmodifiableList((List) entry.getValue()));\r
+ }\r
+ }\r
+ return Collections.unmodifiableMap(map);\r
+ }\r
+\r
+ public void setInstanceFollowRedirects(boolean instanceFollowRedirects) {\r
+ connection.setInstanceFollowRedirects(instanceFollowRedirects);\r
+ }\r
+\r
+ public boolean getInstanceFollowRedirects() {\r
+ return connection.getInstanceFollowRedirects();\r
+ }\r
+\r
+ public void setRequestMethod(String requestMethod)\r
+ throws ProtocolException {\r
+ connection.setRequestMethod(requestMethod);\r
+ }\r
+\r
+ public String getRequestMethod() {\r
+ return connection.getRequestMethod();\r
+ }\r
+\r
+ public int getResponseCode() throws IOException {\r
+ return connection.getResponseCode();\r
+ }\r
+\r
+ public String getResponseMessage() throws IOException {\r
+ return connection.getResponseMessage();\r
+ }\r
+\r
+ public void disconnect() {\r
+ connection.disconnect();\r
+ connected = false;\r
+ }\r
+\r
+ public boolean usingProxy() {\r
+ return connection.usingProxy();\r
+ }\r
+\r
+ public InputStream getErrorStream() {\r
+ return connection.getErrorStream();\r
+ }\r
+\r
+ private int parseResponseCode() throws IOException {\r
+ try {\r
+ String response = connection.getHeaderField(0);\r
+ int index = response.indexOf(' ');\r
+ while (response.charAt(index) == ' ') index++;\r
+ return Integer.parseInt(response.substring(index, index + 3));\r
+ } catch (Exception ex) {\r
+ throw new IOException(ex.getMessage());\r
+ }\r
+ }\r
+\r
+ private synchronized void doConnect() throws IOException {\r
+ connection.connect();\r
+ int response = parseResponseCode();\r
+ if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
+ return;\r
+ }\r
+ Type1Message type1 = (Type1Message) attemptNegotiation(response);\r
+ if (type1 == null) return; // no NTLM\r
+ int attempt = 0;\r
+ while (attempt < MAX_REDIRECTS) {\r
+ connection.setRequestProperty(authProperty, method + ' ' +\r
+ Base64.encode(type1.toByteArray()));\r
+ connection.connect(); // send type 1\r
+ response = parseResponseCode();\r
+ if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
+ return;\r
+ }\r
+ Type3Message type3 = (Type3Message) attemptNegotiation(response);\r
+ if (type3 == null) return;\r
+ connection.setRequestProperty(authProperty, method + ' ' +\r
+ Base64.encode(type3.toByteArray()));\r
+ connection.connect(); // send type 3\r
+ response = parseResponseCode();\r
+ if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {\r
+ return;\r
+ }\r
+ attempt++;\r
+ if (attempt < MAX_REDIRECTS) reconnect();\r
+ }\r
+ throw new IOException("Unable to negotiate NTLM authentication.");\r
+ }\r
+\r
+ private NtlmMessage attemptNegotiation(int response) throws IOException {\r
+ authProperty = null;\r
+ method = null;\r
+ InputStream errorStream = connection.getErrorStream();\r
+ if (errorStream != null && errorStream.available() != 0) {\r
+ int count;\r
+ byte[] buf = new byte[1024];\r
+ while ((count = errorStream.read(buf, 0, 1024)) != -1);\r
+ }\r
+ String authHeader;\r
+ if (response == HTTP_UNAUTHORIZED) {\r
+ authHeader = "WWW-Authenticate";\r
+ authProperty = "Authorization";\r
+ } else {\r
+ authHeader = "Proxy-Authenticate";\r
+ authProperty = "Proxy-Authorization";\r
+ }\r
+ String authorization = null;\r
+ List methods = (List) getHeaderFields0().get(authHeader);\r
+ if (methods == null) return null;\r
+ Iterator iterator = methods.iterator();\r
+ while (iterator.hasNext()) {\r
+ String authMethod = (String) iterator.next();\r
+ if (authMethod.startsWith("NTLM")) {\r
+ if (authMethod.length() == 4) {\r
+ method = "NTLM";\r
+ break;\r
+ }\r
+ if (authMethod.indexOf(' ') != 4) continue;\r
+ method = "NTLM";\r
+ authorization = authMethod.substring(5).trim();\r
+ break;\r
+ } else if (authMethod.startsWith("Negotiate")) {\r
+ if (authMethod.length() == 9) {\r
+ method = "Negotiate";\r
+ break;\r
+ }\r
+ if (authMethod.indexOf(' ') != 9) continue;\r
+ method = "Negotiate";\r
+ authorization = authMethod.substring(10).trim();\r
+ break;\r
+ }\r
+ }\r
+ if (method == null) return null;\r
+ NtlmMessage message = (authorization != null) ?\r
+ new Type2Message(Base64.decode(authorization)) : null;\r
+ reconnect();\r
+ if (message == null) {\r
+ message = new Type1Message();\r
+ if (LM_COMPATIBILITY > 2) {\r
+ message.setFlag(NtlmFlags.NTLMSSP_REQUEST_TARGET, true);\r
+ }\r
+ } else {\r
+ String domain = DEFAULT_DOMAIN;\r
+ String user = Type3Message.getDefaultUser();\r
+ String password = Type3Message.getDefaultPassword();\r
+ String userInfo = url.getUserInfo();\r
+ if (userInfo != null) {\r
+ userInfo = URLDecoder.decode(userInfo);\r
+ int index = userInfo.indexOf(':');\r
+ user = (index != -1) ? userInfo.substring(0, index) : userInfo;\r
+ if (index != -1) password = userInfo.substring(index + 1);\r
+ index = user.indexOf('\\');\r
+ if (index == -1) index = user.indexOf('/');\r
+ domain = (index != -1) ? user.substring(0, index) : domain;\r
+ user = (index != -1) ? user.substring(index + 1) : user;\r
+ }\r
+ if (user == null) {\r
+ try {\r
+ URL url = getURL();\r
+ String protocol = url.getProtocol();\r
+ int port = url.getPort();\r
+ if (port == -1) {\r
+ port = "https".equalsIgnoreCase(protocol) ? 443 : 80;\r
+ }\r
+ PasswordAuthentication auth =\r
+ Authenticator.requestPasswordAuthentication(null,\r
+ port, protocol, "", method);\r
+ if (auth != null) {\r
+ user = auth.getUserName();\r
+ password = new String(auth.getPassword());\r
+ }\r
+ } catch (Exception ex) { }\r
+ }\r
+ Type2Message type2 = (Type2Message) message;\r
+ message = new Type3Message(type2, password, domain, user,\r
+ Type3Message.getDefaultWorkstation());\r
+ }\r
+ return message;\r
+ }\r
+\r
+ private void reconnect() throws IOException {\r
+ connection = (HttpURLConnection) connection.getURL().openConnection();\r
+ headerFields = null;\r
+ synchronized (requestProperties) {\r
+ Iterator properties = requestProperties.entrySet().iterator();\r
+ while (properties.hasNext()) {\r
+ Map.Entry property = (Map.Entry) properties.next();\r
+ String key = (String) property.getKey();\r
+ StringBuffer value = new StringBuffer();\r
+ Iterator values = ((List) property.getValue()).iterator();\r
+ while (values.hasNext()) {\r
+ value.append(values.next());\r
+ if (values.hasNext()) value.append(", ");\r
+ }\r
+ connection.setRequestProperty(key, value.toString());\r
+ }\r
+ }\r
+ connection.setAllowUserInteraction(allowUserInteraction);\r
+ connection.setDoInput(doInput);\r
+ connection.setDoOutput(doOutput);\r
+ connection.setIfModifiedSince(ifModifiedSince);\r
+ connection.setUseCaches(useCaches);\r
+ }\r
+}\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import jcifs.Config;\r
+\r
+/**\r
+ * Abstract superclass for all NTLMSSP messages.\r
+ */\r
+public abstract class NtlmMessage implements NtlmFlags {\r
+\r
+ /**\r
+ * The NTLMSSP "preamble".\r
+ */\r
+ protected static final byte[] NTLMSSP_SIGNATURE = new byte[] {\r
+ (byte) 'N', (byte) 'T', (byte) 'L', (byte) 'M',\r
+ (byte) 'S', (byte) 'S', (byte) 'P', (byte) 0\r
+ };\r
+\r
+ private static final String OEM_ENCODING =\r
+ Config.getProperty("jcifs.smb.client.codepage",\r
+ Config.getProperty("jcifs.encoding",\r
+ System.getProperty("file.encoding")));\r
+\r
+ private int flags;\r
+\r
+ /**\r
+ * Returns the flags currently in use for this message.\r
+ *\r
+ * @return An <code>int</code> containing the flags in use for this\r
+ * message.\r
+ */\r
+ public int getFlags() {\r
+ return flags;\r
+ }\r
+\r
+ /**\r
+ * Sets the flags for this message.\r
+ *\r
+ * @param flags The flags for this message.\r
+ */\r
+ public void setFlags(int flags) {\r
+ this.flags = flags;\r
+ }\r
+\r
+ /**\r
+ * Returns the status of the specified flag.\r
+ *\r
+ * @param flag The flag to test (i.e., <code>NTLMSSP_NEGOTIATE_OEM</code>).\r
+ * @return A <code>boolean</code> indicating whether the flag is set.\r
+ */\r
+ public boolean getFlag(int flag) {\r
+ return (getFlags() & flag) != 0;\r
+ }\r
+\r
+ /**\r
+ * Sets or clears the specified flag.\r
+ * \r
+ * @param flag The flag to set/clear (i.e.,\r
+ * <code>NTLMSSP_NEGOTIATE_OEM</code>).\r
+ * @param value Indicates whether to set (<code>true</code>) or\r
+ * clear (<code>false</code>) the specified flag.\r
+ */\r
+ public void setFlag(int flag, boolean value) {\r
+ setFlags(value ? (getFlags() | flag) :\r
+ (getFlags() & (0xffffffff ^ flag)));\r
+ }\r
+\r
+ static int readULong(byte[] src, int index) {\r
+ return (src[index] & 0xff) |\r
+ ((src[index + 1] & 0xff) << 8) |\r
+ ((src[index + 2] & 0xff) << 16) |\r
+ ((src[index + 3] & 0xff) << 24);\r
+ }\r
+\r
+ static int readUShort(byte[] src, int index) {\r
+ return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);\r
+ }\r
+\r
+ static byte[] readSecurityBuffer(byte[] src, int index) {\r
+ int length = readUShort(src, index);\r
+ int offset = readULong(src, index + 4);\r
+ byte[] buffer = new byte[length];\r
+ System.arraycopy(src, offset, buffer, 0, length);\r
+ return buffer;\r
+ }\r
+\r
+ static void writeULong(byte[] dest, int offset, int ulong) {\r
+ dest[offset] = (byte) (ulong & 0xff);\r
+ dest[offset + 1] = (byte) (ulong >> 8 & 0xff);\r
+ dest[offset + 2] = (byte) (ulong >> 16 & 0xff);\r
+ dest[offset + 3] = (byte) (ulong >> 24 & 0xff);\r
+ }\r
+\r
+ static void writeUShort(byte[] dest, int offset, int ushort) {\r
+ dest[offset] = (byte) (ushort & 0xff);\r
+ dest[offset + 1] = (byte) (ushort >> 8 & 0xff);\r
+ }\r
+\r
+ static void writeSecurityBuffer(byte[] dest, int offset, int bodyOffset,\r
+ byte[] src) {\r
+ int length = (src != null) ? src.length : 0;\r
+ if (length == 0) return;\r
+ writeUShort(dest, offset, length);\r
+ writeUShort(dest, offset + 2, length);\r
+ writeULong(dest, offset + 4, bodyOffset);\r
+ System.arraycopy(src, 0, dest, bodyOffset, length);\r
+ }\r
+\r
+ static String getOEMEncoding() {\r
+ return OEM_ENCODING;\r
+ }\r
+\r
+ /**\r
+ * Returns the raw byte representation of this message.\r
+ *\r
+ * @return A <code>byte[]</code> containing the raw message material.\r
+ */\r
+ public abstract byte[] toByteArray();\r
+\r
+}\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.smb;\r
+\r
+import jcifs.util.DES;\r
+import jcifs.util.MD4;\r
+import jcifs.util.HMACT64;\r
+import java.io.UnsupportedEncodingException;\r
+import java.security.Principal;\r
+import java.security.SecureRandom;\r
+import java.util.Arrays;\r
+import jcifs.Config;\r
+\r
+/**\r
+ * This class stores and encrypts NTLM user credentials. The default\r
+ * credentials are retrieved from the <tt>jcifs.smb.client.domain</tt>,\r
+ * <tt>jcifs.smb.client.username</tt>, and <tt>jcifs.smb.client.password</tt>\r
+ * properties.\r
+ * <p>\r
+ * Read <a href="../../../authhandler.html">jCIFS Exceptions and\r
+ * NtlmAuthenticator</a> for related information.\r
+ */\r
+\r
+public final class NtlmPasswordAuthentication implements Principal {\r
+\r
+ private static final int LM_COMPATIBILITY =\r
+ Config.getInt("jcifs.smb.lmCompatibility", 0);\r
+\r
+ private static final String DEFAULT_DOMAIN =\r
+ Config.getProperty("jcifs.smb.client.domain", "?");\r
+\r
+ private static final String DEFAULT_USERNAME =\r
+ Config.getProperty("jcifs.smb.client.username", "GUEST");\r
+\r
+ private static final String DEFAULT_PASSWORD =\r
+ Config.getProperty("jcifs.smb.client.password", "");\r
+\r
+ private static final SecureRandom random = new SecureRandom();\r
+\r
+ // KGS!@#$%\r
+ static final byte[] S8 = {\r
+ (byte)0x4b, (byte)0x47, (byte)0x53, (byte)0x21,\r
+ (byte)0x40, (byte)0x23, (byte)0x24, (byte)0x25\r
+ };\r
+ static void E( byte[] key, byte[] data, byte[] e ) {\r
+ byte[] key7 = new byte[7];\r
+ byte[] e8 = new byte[8];\r
+\r
+ for( int i = 0; i < key.length / 7; i++ ) {\r
+ System.arraycopy( key, i * 7, key7, 0, 7 );\r
+ DES des = new DES( key7 );\r
+ des.encrypt( data, e8 );\r
+ System.arraycopy( e8, 0, e, i * 8, 8 );\r
+ }\r
+ }\r
+/**\r
+ * Generate the ANSI DES hash for the password associated with these credentials.\r
+ */\r
+ static public byte[] getPreNTLMResponse( String password, byte[] challenge ) {\r
+ byte[] p14 = new byte[14];\r
+ byte[] p21 = new byte[21];\r
+ byte[] p24 = new byte[24];\r
+ byte[] passwordBytes;\r
+ try {\r
+ passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.encoding );\r
+ } catch( UnsupportedEncodingException uee ) {\r
+ return null;\r
+ }\r
+ int passwordLength = passwordBytes.length;\r
+\r
+ // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM\r
+ if( passwordLength > 14) {\r
+ passwordLength = 14;\r
+ }\r
+ System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );\r
+ E( p14, S8, p21);\r
+ E( p21, challenge, p24);\r
+ return p24;\r
+ }\r
+/**\r
+ * Generate the Unicode MD4 hash for the password associated with these credentials.\r
+ */\r
+ static public byte[] getNTLMResponse( String password, byte[] challenge ) {\r
+ byte[] uni = null;\r
+ byte[] p21 = new byte[21];\r
+ byte[] p24 = new byte[24];\r
+\r
+ try {\r
+ uni = password.getBytes( "UnicodeLittleUnmarked" );\r
+ } catch( UnsupportedEncodingException uee ) {\r
+ Log.printStackTrace( "password encryption exception", uee );\r
+ }\r
+ MD4 md4 = new MD4();\r
+ md4.update( uni );\r
+ try {\r
+ md4.digest(p21, 0, 16);\r
+ } catch (Exception ex) {\r
+ Log.printStackTrace( "digest exception", ex );\r
+ }\r
+ E( p21, challenge, p24 );\r
+ return p24;\r
+ }\r
+\r
+ /**\r
+ * Creates the LMv2 response for the supplied information.\r
+ *\r
+ * @param domain The domain in which the username exists.\r
+ * @param user The username.\r
+ * @param password The user's password.\r
+ * @param challenge The server challenge.\r
+ * @param clientChallenge The client challenge (nonce). \r
+ */ \r
+ public static byte[] getLMv2Response(String domain, String user,\r
+ String password, byte[] challenge, byte[] clientChallenge) {\r
+ try {\r
+ byte[] hash = new byte[16];\r
+ byte[] response = new byte[24];\r
+ MD4 md4 = new MD4();\r
+ md4.update(password.getBytes("UnicodeLittleUnmarked"));\r
+ HMACT64 hmac = new HMACT64(md4.digest());\r
+ hmac.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked"));\r
+ hmac.update(domain.toUpperCase().getBytes("UnicodeLittleUnmarked"));\r
+ hmac = new HMACT64(hmac.digest());\r
+ hmac.update(challenge);\r
+ hmac.update(clientChallenge);\r
+ hmac.digest(response, 0, 16);\r
+ System.arraycopy(clientChallenge, 0, response, 16, 8);\r
+ return response;\r
+ } catch (Exception ex) {\r
+ Log.printStackTrace("Error creating LMv2 response", ex);\r
+ return null;\r
+ }\r
+ }\r
+\r
+ static final NtlmPasswordAuthentication NULL =\r
+ new NtlmPasswordAuthentication( "", "", "" );\r
+ static final NtlmPasswordAuthentication GUEST =\r
+ new NtlmPasswordAuthentication( "?", "GUEST", "" );\r
+\r
+ String domain;\r
+ String username;\r
+ String password;\r
+ byte[] ansiHash;\r
+ byte[] unicodeHash;\r
+ boolean hashesExternal = false;\r
+\r
+/**\r
+ * Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo\r
+ * component of an SMB URL like "<tt>domain;user:pass</tt>". This constructor\r
+ * is used internally be jCIFS when parsing SMB URLs.\r
+ */\r
+\r
+ public NtlmPasswordAuthentication( String userInfo ) {\r
+ domain = username = password = null;\r
+\r
+ if( userInfo != null ) {\r
+ int i, u, end;\r
+ char c;\r
+\r
+ end = userInfo.length();\r
+ for( i = 0, u = 0; i < end; i++ ) {\r
+ c = userInfo.charAt( i );\r
+ if( c == ';' ) {\r
+ domain = userInfo.substring( 0, i );\r
+ u = i + 1;\r
+ } else if( c == ':' ) {\r
+ password = userInfo.substring( i + 1 );\r
+ break;\r
+ }\r
+ }\r
+ username = userInfo.substring( u, i );\r
+ }\r
+\r
+ if( domain == null ) this.domain = DEFAULT_DOMAIN;\r
+ if( username == null ) this.username = DEFAULT_USERNAME;\r
+ if( password == null ) this.password = DEFAULT_PASSWORD;\r
+ }\r
+/**\r
+ * Create an <tt>NtlmPasswordAuthentication</tt> object from a\r
+ * domain, username, and password. Parameters that are <tt>null</tt>\r
+ * will be substituted with <tt>jcifs.smb.client.domain</tt>,\r
+ * <tt>jcifs.smb.client.username</tt>, <tt>jcifs.smb.client.password</tt>\r
+ * property values.\r
+ */\r
+ public NtlmPasswordAuthentication( String domain, String username, String password ) {\r
+ this.domain = domain;\r
+ this.username = username;\r
+ this.password = password;\r
+ if( domain == null ) this.domain = DEFAULT_DOMAIN;\r
+ if( username == null ) this.username = DEFAULT_USERNAME;\r
+ if( password == null ) this.password = DEFAULT_PASSWORD;\r
+ }\r
+/**\r
+ * Create an <tt>NtlmPasswordAuthentication</tt> object with raw password\r
+ * hashes. This is used exclusively by the <tt>jcifs.http.NtlmSsp</tt>\r
+ * class which is in turn used by NTLM HTTP authentication functionality.\r
+ */\r
+ public NtlmPasswordAuthentication( String domain, String username,\r
+ byte[] ansiHash, byte[] unicodeHash ) {\r
+ if( domain == null || username == null ||\r
+ ansiHash == null || unicodeHash == null ) {\r
+ throw new IllegalArgumentException( "External credentials cannot null" );\r
+ }\r
+ this.domain = domain;\r
+ this.username = username;\r
+ this.password = null;\r
+ this.ansiHash = ansiHash;\r
+ this.unicodeHash = unicodeHash;\r
+ hashesExternal = true;\r
+ }\r
+\r
+/**\r
+ * Returns the domain.\r
+ */\r
+ public String getDomain() {\r
+ return domain;\r
+ }\r
+/**\r
+ * Returns the username.\r
+ */\r
+ public String getUsername() {\r
+ return username;\r
+ }\r
+/**\r
+ * Returns the password in plain text or <tt>null</tt> if the raw password\r
+ * hashes were used to construct this <tt>NtlmPasswordAuthentication</tt>\r
+ * object which will be the case when NTLM HTTP Authentication is\r
+ * used. There is no way to retrieve a users password in plain text unless\r
+ * it is supplied by the user at runtime.\r
+ */\r
+ public String getPassword() {\r
+ return password;\r
+ }\r
+/**\r
+ * Return the domain and username in the format:\r
+ * <tt>domain\\username</tt>. This is equivalent to <tt>toString()</tt>.\r
+ */\r
+ public String getName() {\r
+ boolean d = domain.length() > 0 && domain.equals( "?" ) == false;\r
+ return d ? domain + "\\" + username : username;\r
+ }\r
+/**\r
+ * Computes the 24 byte ANSI password hash given the 8 byte server challenge.\r
+ */\r
+ public byte[] getAnsiHash( byte[] challenge ) {\r
+ if( hashesExternal ) {\r
+ return ansiHash;\r
+ }\r
+ switch (LM_COMPATIBILITY) {\r
+ case 0:\r
+ case 1:\r
+ return getPreNTLMResponse( password, challenge );\r
+ case 2:\r
+ return getNTLMResponse( password, challenge );\r
+ case 3:\r
+ case 4:\r
+ case 5:\r
+ byte[] clientChallenge = new byte[8];\r
+ random.nextBytes(clientChallenge);\r
+ return getLMv2Response(domain, username, password, challenge,\r
+ clientChallenge);\r
+ default:\r
+ return getPreNTLMResponse( password, challenge );\r
+ }\r
+ }\r
+/**\r
+ * Computes the 24 byte Unicode password hash given the 8 byte server challenge.\r
+ */\r
+ public byte[] getUnicodeHash( byte[] challenge ) {\r
+ if( hashesExternal ) {\r
+ return unicodeHash;\r
+ }\r
+ switch (LM_COMPATIBILITY) {\r
+ case 0:\r
+ case 1:\r
+ case 2:\r
+ return getNTLMResponse( password, challenge );\r
+ case 3:\r
+ case 4:\r
+ case 5:\r
+ /*\r
+ byte[] clientChallenge = new byte[8];\r
+ random.nextBytes(clientChallenge);\r
+ return getNTLMv2Response(domain, username, password, null,\r
+ challenge, clientChallenge);\r
+ */\r
+ return new byte[0];\r
+ default:\r
+ return getNTLMResponse( password, challenge );\r
+ }\r
+ }\r
+/**\r
+ * Compares two <tt>NtlmPasswordAuthentication</tt> objects for\r
+ * equality. Two <tt>NtlmPasswordAuthentication</tt> objects are equal if\r
+ * their caseless domain and username fields are equal and either both hashes are external and they are equal or both internally supplied passwords are equal. If one <tt>NtlmPasswordAuthentication</tt> object has external hashes (meaning negotiated via NTLM HTTP Authentication) and the other does not they will not be equal. This is technically not correct however the server 8 byte challage would be required to compute and compare the password hashes but that it not available with this method.\r
+ */\r
+ public boolean equals( Object obj ) {\r
+ if( obj instanceof NtlmPasswordAuthentication ) {\r
+ NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj;\r
+ if( ntlm.domain.toUpperCase().equals( domain.toUpperCase() ) &&\r
+ ntlm.username.toUpperCase().equals( username.toUpperCase() )) {\r
+ if( hashesExternal && ntlm.hashesExternal ) {\r
+ return Arrays.equals( ansiHash, ntlm.ansiHash ) &&\r
+ Arrays.equals( unicodeHash, ntlm.unicodeHash );\r
+ /* This still isn't quite right. If one npa object does not have external\r
+ * hashes and the other does then they will not be considered equal even\r
+ * though they may be.\r
+ */\r
+ } else if( !hashesExternal && password.equals( ntlm.password )) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+\r
+/**\r
+ * Return the upcased username hash code.\r
+ */\r
+ public int hashCode() {\r
+ return getName().toUpperCase().hashCode();\r
+ }\r
+/**\r
+ * Return the domain and username in the format:\r
+ * <tt>domain\\username</tt>. This is equivalent to <tt>getName()</tt>.\r
+ */\r
+ public String toString() {\r
+ return getName();\r
+ }\r
+}\r
+\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import java.util.Enumeration;\r
+\r
+import javax.servlet.ServletConfig;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.UnavailableException;\r
+\r
+import javax.servlet.http.HttpSession;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import jcifs.Config;\r
+import jcifs.UniAddress;\r
+\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+import jcifs.smb.SmbAuthException;\r
+import jcifs.smb.SmbSession;\r
+\r
+import jcifs.util.Base64;\r
+\r
+/**\r
+ * This servlet may be used with pre-2.3 servlet containers\r
+ * to protect content with NTLM HTTP Authentication. Servlets that\r
+ * extend this abstract base class may be authenticatied against an SMB\r
+ * server or domain controller depending on how the\r
+ * <tt>jcifs.smb.client.domain</tt> or <tt>jcifs.http.domainController</tt>\r
+ * properties are be specified. <b>With later containers the\r
+ * <tt>NtlmHttpFilter</tt> should be used/b>. For custom NTLM HTTP Authentication schemes the <tt>NtlmSsp</tt> may be used.\r
+ * <p>\r
+ * Read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a> related information.\r
+ */\r
+\r
+public abstract class NtlmServlet extends HttpServlet {\r
+\r
+ private String defaultDomain;\r
+\r
+ private String domainController;\r
+\r
+ private boolean enableBasic;\r
+\r
+ private boolean insecureBasic;\r
+\r
+ private String realm;\r
+\r
+ public void init(ServletConfig config) throws ServletException {\r
+ super.init(config);\r
+\r
+ /* Set jcifs properties we know we want; soTimeout and cachePolicy to 10min.\r
+ */\r
+ Config.setProperty( "jcifs.smb.client.soTimeout", "300000" );\r
+ Config.setProperty( "jcifs.netbios.cachePolicy", "600" );\r
+\r
+ Enumeration e = config.getInitParameterNames();\r
+ String name;\r
+ while (e.hasMoreElements()) {\r
+ name = (String) e.nextElement();\r
+ if (name.startsWith("jcifs.")) {\r
+ Config.setProperty(name, config.getInitParameter(name));\r
+ }\r
+ }\r
+ defaultDomain = Config.getProperty("jcifs.smb.client.domain");\r
+ domainController = Config.getProperty("jcifs.http.domainController");\r
+ if (domainController == null) domainController = defaultDomain;\r
+ enableBasic = Boolean.valueOf(\r
+ Config.getProperty("jcifs.http.enableBasic")).booleanValue();\r
+ insecureBasic = Boolean.valueOf(\r
+ Config.getProperty("jcifs.http.insecureBasic")).booleanValue();\r
+ realm = Config.getProperty("jcifs.http.basicRealm");\r
+ if (realm == null) realm = "jCIFS";\r
+ }\r
+\r
+ protected void service(HttpServletRequest request,\r
+ HttpServletResponse response) throws ServletException, IOException {\r
+ boolean offerBasic = enableBasic &&\r
+ (insecureBasic || request.isSecure());\r
+ String msg = request.getHeader("Authorization");\r
+ if (msg != null && (msg.startsWith("NTLM ") ||\r
+ (offerBasic && msg.startsWith("Basic ")))) {\r
+ UniAddress dc = UniAddress.getByName(domainController, true);\r
+ NtlmPasswordAuthentication ntlm;\r
+ if (msg.startsWith("NTLM ")) {\r
+ byte[] challenge = SmbSession.getChallenge(dc);\r
+ ntlm = NtlmSsp.authenticate(request, response, challenge);\r
+ if (ntlm == null) return;\r
+ } else {\r
+ String auth = new String(Base64.decode(msg.substring(6)),\r
+ "US-ASCII");\r
+ int index = auth.indexOf(':');\r
+ String user = (index != -1) ? auth.substring(0, index) : auth;\r
+ String password = (index != -1) ? auth.substring(index + 1) :\r
+ "";\r
+ index = user.indexOf('\\');\r
+ if (index == -1) index = user.indexOf('/');\r
+ String domain = (index != -1) ? user.substring(0, index) :\r
+ defaultDomain;\r
+ user = (index != -1) ? user.substring(index + 1) : user;\r
+ ntlm = new NtlmPasswordAuthentication(domain, user, password);\r
+ }\r
+ try {\r
+ SmbSession.logon(dc, ntlm);\r
+ } catch (SmbAuthException sae) {\r
+ response.setHeader("WWW-Authenticate", "NTLM");\r
+ if (offerBasic) {\r
+ response.addHeader("WWW-Authenticate", "Basic realm=\"" +\r
+ realm + "\"");\r
+ }\r
+ response.setHeader("Connection", "close");\r
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+ response.flushBuffer();\r
+ return;\r
+ }\r
+ HttpSession ssn = request.getSession();\r
+ ssn.setAttribute("NtlmHttpAuth", ntlm);\r
+ ssn.setAttribute( "ntlmdomain", ntlm.getDomain() );\r
+ ssn.setAttribute( "ntlmuser", ntlm.getUsername() );\r
+ } else {\r
+ HttpSession ssn = request.getSession(false);\r
+ if (ssn == null || ssn.getAttribute("NtlmHttpAuth") == null) {\r
+ response.setHeader("WWW-Authenticate", "NTLM");\r
+ if (offerBasic) {\r
+ response.addHeader("WWW-Authenticate", "Basic realm=\"" +\r
+ realm + "\"");\r
+ }\r
+ response.setHeader("Connection", "close");\r
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+ response.flushBuffer();\r
+ return;\r
+ }\r
+ }\r
+ super.service(request, response);\r
+ }\r
+}\r
+\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ * "Jason Pugsley" <jcifs at samba dot org>\r
+ * "skeetz" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.http;\r
+\r
+import java.io.IOException;\r
+\r
+import javax.servlet.ServletException;\r
+\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+\r
+import jcifs.util.Base64;\r
+\r
+import jcifs.ntlmssp.NtlmFlags;\r
+import jcifs.ntlmssp.Type1Message;\r
+import jcifs.ntlmssp.Type2Message;\r
+import jcifs.ntlmssp.Type3Message;\r
+\r
+/**\r
+ * This class is used internally by <tt>NtlmHttpFilter</tt>,\r
+ * <tt>NtlmServlet</tt>, and <tt>NetworkExplorer</tt> to negiotiate password\r
+ * hashes via NTLM SSP with MSIE. It might also be used directly by servlet\r
+ * containers to incorporate similar functionality.\r
+ * <p>\r
+ * How NTLMSSP is used in conjunction with HTTP and MSIE clients is\r
+ * described in an <A HREF="http://www.innovation.ch/java/ntlm.html">NTLM\r
+ * Authentication Scheme for HTTP</A>. <p> Also, read <a\r
+ * href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and\r
+ * the Network Explorer Servlet</a> related information.\r
+ */\r
+\r
+public class NtlmSsp implements NtlmFlags {\r
+\r
+ /**\r
+ * Calls the static {@link #authenticate(HttpServletRequest,\r
+ * HttpServletResponse, byte[])} method to perform NTLM authentication\r
+ * for the specified servlet request.\r
+ *\r
+ * @param req The request being serviced.\r
+ * @param resp The response.\r
+ * @param challenge The domain controller challenge.\r
+ * @throws IOException If an IO error occurs.\r
+ * @throws ServletException If an error occurs.\r
+ */\r
+ public NtlmPasswordAuthentication doAuthentication(\r
+ HttpServletRequest req, HttpServletResponse resp, byte[] challenge)\r
+ throws IOException, ServletException {\r
+ return authenticate(req, resp, challenge);\r
+ }\r
+\r
+ /**\r
+ * Performs NTLM authentication for the servlet request.\r
+ *\r
+ * @param req The request being serviced.\r
+ * @param resp The response.\r
+ * @param challenge The domain controller challenge.\r
+ * @throws IOException If an IO error occurs.\r
+ * @throws ServletException If an error occurs.\r
+ */\r
+ public static NtlmPasswordAuthentication authenticate(\r
+ HttpServletRequest req, HttpServletResponse resp, byte[] challenge)\r
+ throws IOException, ServletException {\r
+ String msg = req.getHeader("Authorization");\r
+ if (msg != null && msg.startsWith("NTLM ")) {\r
+ byte[] src = Base64.decode(msg.substring(5));\r
+ if (src[8] == 1) {\r
+ Type1Message type1 = new Type1Message(src);\r
+ Type2Message type2 = new Type2Message(type1, challenge, null);\r
+ msg = Base64.encode(type2.toByteArray());\r
+ resp.setHeader( "WWW-Authenticate", "NTLM " + msg );\r
+ } else if (src[8] == 3) {\r
+ Type3Message type3 = new Type3Message(src);\r
+ byte[] lmResponse = type3.getLMResponse();\r
+ if (lmResponse == null) lmResponse = new byte[0];\r
+ byte[] ntResponse = type3.getNTResponse();\r
+ if (ntResponse == null) ntResponse = new byte[0];\r
+ return new NtlmPasswordAuthentication(type3.getDomain(),\r
+ type3.getUser(), lmResponse, ntResponse);\r
+ }\r
+ } else {\r
+ resp.setHeader("WWW-Authenticate", "NTLM");\r
+ resp.setHeader("Connection", "close");\r
+ }\r
+ resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+ resp.setContentLength( 0 );\r
+ resp.flushBuffer();\r
+ return null;\r
+ }\r
+\r
+}\r
+\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import jcifs.netbios.NbtAddress;\r
+\r
+import jcifs.Config;\r
+\r
+/**\r
+ * Represents an NTLMSSP Type-1 message.\r
+ */\r
+public class Type1Message extends NtlmMessage {\r
+\r
+ private static final int DEFAULT_FLAGS;\r
+\r
+ private static final String DEFAULT_DOMAIN;\r
+\r
+ private static final String DEFAULT_WORKSTATION;\r
+\r
+ private String suppliedDomain;\r
+\r
+ private String suppliedWorkstation;\r
+\r
+ static {\r
+ DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
+ (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
+ NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
+ DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
+ String defaultWorkstation = null;\r
+ try {\r
+ defaultWorkstation = NbtAddress.getLocalHost().getHostName();\r
+ } catch (UnknownHostException ex) { }\r
+ DEFAULT_WORKSTATION = defaultWorkstation;\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-1 message using default values from the current\r
+ * environment.\r
+ */\r
+ public Type1Message() {\r
+ this(getDefaultFlags(), getDefaultDomain(), getDefaultWorkstation());\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-1 message with the specified parameters.\r
+ *\r
+ * @param flags The flags to apply to this message.\r
+ * @param suppliedDomain The supplied authentication domain.\r
+ * @param suppliedWorkstation The supplied workstation name.\r
+ */\r
+ public Type1Message(int flags, String suppliedDomain,\r
+ String suppliedWorkstation) {\r
+ setFlags(flags);\r
+ setSuppliedDomain(suppliedDomain);\r
+ setSuppliedWorkstation(suppliedWorkstation);\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-1 message using the given raw Type-1 material.\r
+ *\r
+ * @param material The raw Type-1 material used to construct this message.\r
+ * @throws IOException If an error occurs while parsing the material.\r
+ */\r
+ public Type1Message(byte[] material) throws IOException {\r
+ parse(material);\r
+ }\r
+\r
+ /**\r
+ * Returns the supplied authentication domain.\r
+ *\r
+ * @return A <code>String</code> containing the supplied domain.\r
+ */\r
+ public String getSuppliedDomain() {\r
+ return suppliedDomain;\r
+ }\r
+\r
+ /**\r
+ * Sets the supplied authentication domain for this message.\r
+ *\r
+ * @param suppliedDomain The supplied domain for this message.\r
+ */\r
+ public void setSuppliedDomain(String suppliedDomain) {\r
+ this.suppliedDomain = suppliedDomain;\r
+ }\r
+\r
+ /**\r
+ * Returns the supplied workstation name.\r
+ * \r
+ * @return A <code>String</code> containing the supplied workstation name.\r
+ */\r
+ public String getSuppliedWorkstation() {\r
+ return suppliedWorkstation;\r
+ }\r
+\r
+ /**\r
+ * Sets the supplied workstation name for this message.\r
+ * \r
+ * @param suppliedWorkstation The supplied workstation for this message.\r
+ */\r
+ public void setSuppliedWorkstation(String suppliedWorkstation) {\r
+ this.suppliedWorkstation = suppliedWorkstation;\r
+ }\r
+\r
+ public byte[] toByteArray() {\r
+ try {\r
+ String suppliedDomain = getSuppliedDomain();\r
+ String suppliedWorkstation = getSuppliedWorkstation();\r
+ int flags = getFlags();\r
+ boolean hostInfo = false;\r
+ byte[] domain = new byte[0];\r
+ if (suppliedDomain != null && suppliedDomain.length() != 0) {\r
+ hostInfo = true;\r
+ flags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;\r
+ domain = suppliedDomain.toUpperCase().getBytes(\r
+ getOEMEncoding());\r
+ } else {\r
+ flags &= (NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED ^ 0xffffffff);\r
+ }\r
+ byte[] workstation = new byte[0];\r
+ if (suppliedWorkstation != null &&\r
+ suppliedWorkstation.length() != 0) {\r
+ hostInfo = true;\r
+ flags |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED;\r
+ workstation =\r
+ suppliedWorkstation.toUpperCase().getBytes(\r
+ getOEMEncoding());\r
+ } else {\r
+ flags &= (NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED ^\r
+ 0xffffffff);\r
+ }\r
+ byte[] type1 = new byte[hostInfo ?\r
+ (32 + domain.length + workstation.length) : 16];\r
+ System.arraycopy(NTLMSSP_SIGNATURE, 0, type1, 0, 8);\r
+ writeULong(type1, 8, 1);\r
+ writeULong(type1, 12, flags);\r
+ if (hostInfo) {\r
+ writeSecurityBuffer(type1, 16, 32, domain);\r
+ writeSecurityBuffer(type1, 24, 32 + domain.length, workstation);\r
+ }\r
+ return type1;\r
+ } catch (IOException ex) {\r
+ throw new IllegalStateException(ex.getMessage());\r
+ }\r
+ }\r
+\r
+ public String toString() {\r
+ String suppliedDomain = getSuppliedDomain();\r
+ String suppliedWorkstation = getSuppliedWorkstation();\r
+ int flags = getFlags();\r
+ StringBuffer buffer = new StringBuffer();\r
+ if (suppliedDomain != null) {\r
+ buffer.append("suppliedDomain: ").append(suppliedDomain);\r
+ }\r
+ if (suppliedWorkstation != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("suppliedWorkstation: ").append(suppliedWorkstation);\r
+ }\r
+ if (flags != 0) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("flags: ");\r
+ buffer.append("0x");\r
+ buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(flags & 0x0f));\r
+ }\r
+ return buffer.toString();\r
+ }\r
+\r
+ /**\r
+ * Returns the default flags for a generic Type-1 message in the\r
+ * current environment.\r
+ * \r
+ * @return An <code>int</code> containing the default flags.\r
+ */\r
+ public static int getDefaultFlags() {\r
+ return DEFAULT_FLAGS;\r
+ }\r
+\r
+ /**\r
+ * Returns the default domain from the current environment.\r
+ *\r
+ * @return A <code>String</code> containing the default domain.\r
+ */\r
+ public static String getDefaultDomain() {\r
+ return DEFAULT_DOMAIN;\r
+ }\r
+\r
+ /**\r
+ * Returns the default workstation from the current environment.\r
+ *\r
+ * @return A <code>String</code> containing the default workstation.\r
+ */\r
+ public static String getDefaultWorkstation() {\r
+ return DEFAULT_WORKSTATION;\r
+ }\r
+\r
+ private void parse(byte[] material) throws IOException {\r
+ for (int i = 0; i < 8; i++) {\r
+ if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
+ throw new IOException("Not an NTLMSSP message.");\r
+ }\r
+ }\r
+ if (readULong(material, 8) != 1) {\r
+ throw new IOException("Not a Type 1 message.");\r
+ }\r
+ int flags = readULong(material, 12);\r
+ String suppliedDomain = null;\r
+ if ((flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) != 0) {\r
+ byte[] domain = readSecurityBuffer(material, 16);\r
+ suppliedDomain = new String(domain, getOEMEncoding());\r
+ }\r
+ String suppliedWorkstation = null;\r
+ if ((flags & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) != 0) {\r
+ byte[] workstation = readSecurityBuffer(material, 24);\r
+ suppliedWorkstation = new String(workstation, getOEMEncoding());\r
+ }\r
+ setFlags(flags);\r
+ setSuppliedDomain(suppliedDomain);\r
+ setSuppliedWorkstation(suppliedWorkstation);\r
+ }\r
+\r
+}\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import jcifs.Config;\r
+\r
+import jcifs.netbios.NbtAddress;\r
+\r
+/**\r
+ * Represents an NTLMSSP Type-2 message.\r
+ */\r
+public class Type2Message extends NtlmMessage {\r
+\r
+ private static final int DEFAULT_FLAGS;\r
+\r
+ private static final String DEFAULT_DOMAIN;\r
+\r
+ private static final byte[] DEFAULT_TARGET_INFORMATION;\r
+\r
+ private byte[] challenge;\r
+\r
+ private String target;\r
+\r
+ private byte[] context;\r
+\r
+ private byte[] targetInformation;\r
+\r
+ static {\r
+ DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
+ (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
+ NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
+ DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
+ byte[] domain = new byte[0];\r
+ if (DEFAULT_DOMAIN != null) {\r
+ try {\r
+ domain = DEFAULT_DOMAIN.getBytes("UnicodeLittleUnmarked");\r
+ } catch (IOException ex) { }\r
+ }\r
+ int domainLength = domain.length;\r
+ byte[] server = new byte[0];\r
+ try {\r
+ String host = NbtAddress.getLocalHost().getHostName();\r
+ if (host != null) {\r
+ try {\r
+ server = host.getBytes("UnicodeLittleUnmarked");\r
+ } catch (IOException ex) { }\r
+ }\r
+ } catch (UnknownHostException ex) { }\r
+ int serverLength = server.length;\r
+ byte[] targetInfo = new byte[(domainLength > 0 ? domainLength + 4 : 0) +\r
+ (serverLength > 0 ? serverLength + 4 : 0) + 4];\r
+ int offset = 0;\r
+ if (domainLength > 0) {\r
+ writeUShort(targetInfo, offset, 2);\r
+ offset += 2;\r
+ writeUShort(targetInfo, offset, domainLength);\r
+ offset += 2;\r
+ System.arraycopy(domain, 0, targetInfo, offset, domainLength);\r
+ offset += domainLength;\r
+ }\r
+ if (serverLength > 0) {\r
+ writeUShort(targetInfo, offset, 1);\r
+ offset += 2;\r
+ writeUShort(targetInfo, offset, serverLength);\r
+ offset += 2;\r
+ System.arraycopy(server, 0, targetInfo, offset, serverLength);\r
+ }\r
+ DEFAULT_TARGET_INFORMATION = targetInfo;\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-2 message using default values from the current\r
+ * environment.\r
+ */\r
+ public Type2Message() {\r
+ this(getDefaultFlags(), null, null);\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-2 message in response to the given Type-1 message\r
+ * using default values from the current environment.\r
+ *\r
+ * @param type1 The Type-1 message which this represents a response to.\r
+ */\r
+ public Type2Message(Type1Message type1) {\r
+ this(type1, null, null);\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-2 message in response to the given Type-1 message.\r
+ *\r
+ * @param type1 The Type-1 message which this represents a response to.\r
+ * @param challenge The challenge from the domain controller/server.\r
+ * @param target The authentication target.\r
+ */\r
+ public Type2Message(Type1Message type1, byte[] challenge, String target) {\r
+ this(getDefaultFlags(type1), challenge, (type1 != null &&\r
+ target == null && type1.getFlag(NTLMSSP_REQUEST_TARGET)) ?\r
+ getDefaultDomain() : target);\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-2 message with the specified parameters.\r
+ *\r
+ * @param flags The flags to apply to this message.\r
+ * @param challenge The challenge from the domain controller/server.\r
+ * @param target The authentication target.\r
+ */\r
+ public Type2Message(int flags, byte[] challenge, String target) {\r
+ setFlags(flags);\r
+ setChallenge(challenge);\r
+ setTarget(target);\r
+ if (target != null) setTargetInformation(getDefaultTargetInformation());\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-2 message using the given raw Type-2 material.\r
+ *\r
+ * @param material The raw Type-2 material used to construct this message.\r
+ * @throws IOException If an error occurs while parsing the material.\r
+ */\r
+ public Type2Message(byte[] material) throws IOException {\r
+ parse(material);\r
+ }\r
+\r
+ /**\r
+ * Returns the challenge for this message.\r
+ *\r
+ * @return A <code>byte[]</code> containing the challenge.\r
+ */\r
+ public byte[] getChallenge() {\r
+ return challenge;\r
+ }\r
+\r
+ /**\r
+ * Sets the challenge for this message.\r
+ *\r
+ * @param challenge The challenge from the domain controller/server.\r
+ */\r
+ public void setChallenge(byte[] challenge) {\r
+ this.challenge = challenge;\r
+ }\r
+\r
+ /**\r
+ * Returns the authentication target.\r
+ *\r
+ * @return A <code>String</code> containing the authentication target.\r
+ */\r
+ public String getTarget() {\r
+ return target;\r
+ }\r
+\r
+ /**\r
+ * Sets the authentication target.\r
+ *\r
+ * @param target The authentication target.\r
+ */\r
+ public void setTarget(String target) {\r
+ this.target = target;\r
+ }\r
+\r
+ /**\r
+ * Returns the target information block.\r
+ *\r
+ * @return A <code>byte[]</code> containing the target information block.\r
+ * The target information block is used by the client to create an\r
+ * NTLMv2 response.\r
+ */ \r
+ public byte[] getTargetInformation() {\r
+ return targetInformation;\r
+ }\r
+\r
+ /**\r
+ * Sets the target information block.\r
+ * The target information block is used by the client to create\r
+ * an NTLMv2 response.\r
+ * \r
+ * @param targetInformation The target information block.\r
+ */\r
+ public void setTargetInformation(byte[] targetInformation) {\r
+ this.targetInformation = targetInformation;\r
+ }\r
+\r
+ /**\r
+ * Returns the local security context.\r
+ *\r
+ * @return A <code>byte[]</code> containing the local security\r
+ * context. This is used by the client to negotiate local\r
+ * authentication.\r
+ */\r
+ public byte[] getContext() {\r
+ return context;\r
+ }\r
+\r
+ /**\r
+ * Sets the local security context. This is used by the client\r
+ * to negotiate local authentication.\r
+ *\r
+ * @param context The local security context.\r
+ */\r
+ public void setContext(byte[] context) {\r
+ this.context = context;\r
+ }\r
+\r
+ public byte[] toByteArray() {\r
+ try {\r
+ String targetName = getTarget();\r
+ byte[] challenge = getChallenge();\r
+ byte[] context = getContext();\r
+ byte[] targetInformation = getTargetInformation();\r
+ int flags = getFlags();\r
+ byte[] target = new byte[0];\r
+ if ((flags & (NTLMSSP_TARGET_TYPE_DOMAIN |\r
+ NTLMSSP_TARGET_TYPE_SERVER |\r
+ NTLMSSP_TARGET_TYPE_SHARE)) != 0) {\r
+ if (targetName != null && targetName.length() != 0) {\r
+ target = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0 ?\r
+ targetName.getBytes("UnicodeLittleUnmarked") :\r
+ targetName.toUpperCase().getBytes(getOEMEncoding());\r
+ } else {\r
+ flags &= (0xffffffff ^ (NTLMSSP_TARGET_TYPE_DOMAIN |\r
+ NTLMSSP_TARGET_TYPE_SERVER |\r
+ NTLMSSP_TARGET_TYPE_SHARE));\r
+ }\r
+ }\r
+ if (targetInformation != null) {\r
+ flags ^= NTLMSSP_NEGOTIATE_TARGET_INFO;\r
+ // empty context is needed for padding when t.i. is supplied.\r
+ if (context == null) context = new byte[8];\r
+ }\r
+ int data = 32;\r
+ if (context != null) data += 8;\r
+ if (targetInformation != null) data += 8;\r
+ byte[] type2 = new byte[data + target.length +\r
+ (targetInformation != null ? targetInformation.length : 0)];\r
+ System.arraycopy(NTLMSSP_SIGNATURE, 0, type2, 0, 8);\r
+ writeULong(type2, 8, 2);\r
+ writeSecurityBuffer(type2, 12, data, target);\r
+ writeULong(type2, 20, flags);\r
+ System.arraycopy(challenge != null ? challenge : new byte[8], 0,\r
+ type2, 24, 8);\r
+ if (context != null) System.arraycopy(context, 0, type2, 32, 8);\r
+ if (targetInformation != null) {\r
+ writeSecurityBuffer(type2, 40, data + target.length,\r
+ targetInformation);\r
+ }\r
+ return type2;\r
+ } catch (IOException ex) {\r
+ throw new IllegalStateException(ex.getMessage());\r
+ }\r
+ }\r
+\r
+ public String toString() {\r
+ String target = getTarget();\r
+ byte[] challenge = getChallenge();\r
+ byte[] context = getContext();\r
+ byte[] targetInformation = getTargetInformation();\r
+ int flags = getFlags();\r
+ StringBuffer buffer = new StringBuffer();\r
+ if (target != null) {\r
+ buffer.append("target: ").append(target);\r
+ }\r
+ if (challenge != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("challenge: ");\r
+ buffer.append("0x");\r
+ for (int i = 0; i < challenge.length; i++) {\r
+ buffer.append(Integer.toHexString((challenge[i] >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(challenge[i] & 0x0f));\r
+ }\r
+ }\r
+ if (context != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("context: ");\r
+ buffer.append("0x");\r
+ for (int i = 0; i < context.length; i++) {\r
+ buffer.append(Integer.toHexString((context[i] >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(context[i] & 0x0f));\r
+ }\r
+ }\r
+ if (targetInformation != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("targetInformation: ");\r
+ buffer.append("0x");\r
+ for (int i = 0; i < targetInformation.length; i++) {\r
+ buffer.append(Integer.toHexString((targetInformation[i] >> 4) &\r
+ 0x0f));\r
+ buffer.append(Integer.toHexString(targetInformation[i] & 0x0f));\r
+ }\r
+ }\r
+ if (flags != 0) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("flags: ");\r
+ buffer.append("0x");\r
+ buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(flags & 0x0f));\r
+ }\r
+ return buffer.toString();\r
+ }\r
+\r
+ /**\r
+ * Returns the default flags for a generic Type-2 message in the\r
+ * current environment.\r
+ *\r
+ * @return An <code>int</code> containing the default flags.\r
+ */\r
+ public static int getDefaultFlags() {\r
+ return DEFAULT_FLAGS;\r
+ }\r
+\r
+ /**\r
+ * Returns the default flags for a Type-2 message created in response\r
+ * to the given Type-1 message in the current environment.\r
+ *\r
+ * @return An <code>int</code> containing the default flags.\r
+ */\r
+ public static int getDefaultFlags(Type1Message type1) {\r
+ if (type1 == null) return DEFAULT_FLAGS;\r
+ int flags = NTLMSSP_NEGOTIATE_NTLM;\r
+ int type1Flags = type1.getFlags();\r
+ flags |= ((type1Flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+ NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM;\r
+ if ((type1Flags & NTLMSSP_REQUEST_TARGET) != 0) {\r
+ String domain = getDefaultDomain();\r
+ if (domain != null) {\r
+ flags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_TARGET_TYPE_DOMAIN;\r
+ }\r
+ }\r
+ return flags;\r
+ }\r
+\r
+ /**\r
+ * Returns the default domain from the current environment.\r
+ *\r
+ * @return A <code>String</code> containing the domain.\r
+ */\r
+ public static String getDefaultDomain() {\r
+ return DEFAULT_DOMAIN;\r
+ }\r
+\r
+ public static byte[] getDefaultTargetInformation() {\r
+ return DEFAULT_TARGET_INFORMATION;\r
+ }\r
+\r
+ private void parse(byte[] material) throws IOException {\r
+ for (int i = 0; i < 8; i++) {\r
+ if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
+ throw new IOException("Not an NTLMSSP message.");\r
+ }\r
+ }\r
+ if (readULong(material, 8) != 2) {\r
+ throw new IOException("Not a Type 2 message.");\r
+ }\r
+ int flags = readULong(material, 20);\r
+ setFlags(flags);\r
+ String target = null;\r
+ byte[] bytes = readSecurityBuffer(material, 12);\r
+ if (bytes.length != 0) {\r
+ target = new String(bytes,\r
+ ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+ "UnicodeLittleUnmarked" : getOEMEncoding());\r
+ }\r
+ setTarget(target);\r
+ for (int i = 24; i < 32; i++) {\r
+ if (material[i] != 0) {\r
+ byte[] challenge = new byte[8];\r
+ System.arraycopy(material, 24, challenge, 0, 8);\r
+ setChallenge(challenge);\r
+ break;\r
+ }\r
+ }\r
+ int offset = readULong(material, 16); // offset of targetname start\r
+ if (offset == 32 || material.length == 32) return;\r
+ for (int i = 32; i < 40; i++) {\r
+ if (material[i] != 0) {\r
+ byte[] context = new byte[8];\r
+ System.arraycopy(material, 32, context, 0, 8);\r
+ setContext(context);\r
+ break;\r
+ }\r
+ }\r
+ if (offset == 40 || material.length == 40) return;\r
+ bytes = readSecurityBuffer(material, 40);\r
+ if (bytes.length != 0) setTargetInformation(bytes);\r
+ }\r
+\r
+}\r
--- /dev/null
+/* jcifs smb client library in Java\r
+ * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>\r
+ * "Eric Glass" <jcifs at samba dot org>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ */\r
+\r
+package jcifs.ntlmssp;\r
+\r
+import java.io.IOException;\r
+\r
+import java.net.UnknownHostException;\r
+\r
+import java.security.SecureRandom;\r
+\r
+import jcifs.Config;\r
+\r
+import jcifs.netbios.NbtAddress;\r
+\r
+import jcifs.smb.NtlmPasswordAuthentication;\r
+\r
+/**\r
+ * Represents an NTLMSSP Type-3 message.\r
+ */\r
+public class Type3Message extends NtlmMessage {\r
+\r
+ private static final int DEFAULT_FLAGS;\r
+\r
+ private static final String DEFAULT_DOMAIN;\r
+\r
+ private static final String DEFAULT_USER;\r
+\r
+ private static final String DEFAULT_PASSWORD;\r
+\r
+ private static final String DEFAULT_WORKSTATION;\r
+\r
+ private static final int LM_COMPATIBILITY;\r
+\r
+ private static final SecureRandom RANDOM = new SecureRandom();\r
+\r
+ private byte[] lmResponse;\r
+\r
+ private byte[] ntResponse;\r
+\r
+ private String domain;\r
+\r
+ private String user;\r
+\r
+ private String workstation;\r
+\r
+ private byte[] sessionKey;\r
+\r
+ static {\r
+ DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM |\r
+ (Config.getBoolean("jcifs.smb.client.useUnicode", true) ?\r
+ NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM);\r
+ DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", null);\r
+ DEFAULT_USER = Config.getProperty("jcifs.smb.client.username", null);\r
+ DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password",\r
+ null);\r
+ String defaultWorkstation = null;\r
+ try {\r
+ defaultWorkstation = NbtAddress.getLocalHost().getHostName();\r
+ } catch (UnknownHostException ex) { }\r
+ DEFAULT_WORKSTATION = defaultWorkstation;\r
+ LM_COMPATIBILITY = Config.getInt("jcifs.smb.lmCompatibility", 0);\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-3 message using default values from the current\r
+ * environment.\r
+ */\r
+ public Type3Message() {\r
+ setFlags(getDefaultFlags());\r
+ setDomain(getDefaultDomain());\r
+ setUser(getDefaultUser());\r
+ setWorkstation(getDefaultWorkstation());\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-3 message in response to the given Type-2 message\r
+ * using default values from the current environment.\r
+ *\r
+ * @param type2 The Type-2 message which this represents a response to.\r
+ */\r
+ public Type3Message(Type2Message type2) {\r
+ setFlags(getDefaultFlags(type2));\r
+ setWorkstation(getDefaultWorkstation());\r
+ String domain = getDefaultDomain();\r
+ setDomain(domain);\r
+ String user = getDefaultUser();\r
+ setUser(user);\r
+ String password = getDefaultPassword();\r
+ switch (LM_COMPATIBILITY) {\r
+ case 0:\r
+ case 1:\r
+ setLMResponse(getLMResponse(type2, password));\r
+ setNTResponse(getNTResponse(type2, password));\r
+ break;\r
+ case 2:\r
+ byte[] nt = getNTResponse(type2, password);\r
+ setLMResponse(nt);\r
+ setNTResponse(nt);\r
+ break;\r
+ case 3:\r
+ case 4:\r
+ case 5:\r
+ byte[] clientChallenge = new byte[8];\r
+ RANDOM.nextBytes(clientChallenge);\r
+ setLMResponse(getLMv2Response(type2, domain, user, password,\r
+ clientChallenge));\r
+ /*\r
+ setNTResponse(getNTLMv2Response(type2, domain, user, password,\r
+ clientChallenge));\r
+ */\r
+ break;\r
+ default:\r
+ setLMResponse(getLMResponse(type2, password));\r
+ setNTResponse(getNTResponse(type2, password));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-3 message in response to the given Type-2 message.\r
+ *\r
+ * @param type2 The Type-2 message which this represents a response to.\r
+ * @param password The password to use when constructing the response.\r
+ * @param domain The domain in which the user has an account.\r
+ * @param user The username for the authenticating user.\r
+ * @param workstation The workstation from which authentication is\r
+ * taking place.\r
+ */\r
+ public Type3Message(Type2Message type2, String password, String domain,\r
+ String user, String workstation) {\r
+ setFlags(getDefaultFlags(type2));\r
+ setDomain(domain);\r
+ setUser(user);\r
+ setWorkstation(workstation);\r
+ switch (LM_COMPATIBILITY) {\r
+ case 0:\r
+ case 1:\r
+ setLMResponse(getLMResponse(type2, password));\r
+ setNTResponse(getNTResponse(type2, password));\r
+ break;\r
+ case 2:\r
+ byte[] nt = getNTResponse(type2, password);\r
+ setLMResponse(nt);\r
+ setNTResponse(nt);\r
+ break;\r
+ case 3:\r
+ case 4:\r
+ case 5:\r
+ byte[] clientChallenge = new byte[8];\r
+ RANDOM.nextBytes(clientChallenge);\r
+ setLMResponse(getLMv2Response(type2, domain, user, password,\r
+ clientChallenge));\r
+ /*\r
+ setNTResponse(getNTLMv2Response(type2, domain, user, password,\r
+ clientChallenge));\r
+ */\r
+ break;\r
+ default:\r
+ setLMResponse(getLMResponse(type2, password));\r
+ setNTResponse(getNTResponse(type2, password));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-3 message with the specified parameters.\r
+ *\r
+ * @param flags The flags to apply to this message.\r
+ * @param lmResponse The LanManager/LMv2 response.\r
+ * @param domain The NT/NTLMv2 response.\r
+ * @param domain The domain in which the user has an account.\r
+ * @param user The username for the authenticating user.\r
+ * @param workstation The workstation from which authentication is\r
+ * taking place.\r
+ */\r
+ public Type3Message(int flags, byte[] lmResponse, byte[] ntResponse,\r
+ String domain, String user, String workstation) {\r
+ setFlags(flags);\r
+ setLMResponse(lmResponse);\r
+ setNTResponse(ntResponse);\r
+ setDomain(domain);\r
+ setUser(user);\r
+ setWorkstation(workstation);\r
+ }\r
+\r
+ /**\r
+ * Creates a Type-3 message using the given raw Type-3 material.\r
+ *\r
+ * @param material The raw Type-3 material used to construct this message.\r
+ * @throws IOException If an error occurs while parsing the material.\r
+ */\r
+ public Type3Message(byte[] material) throws IOException {\r
+ parse(material);\r
+ }\r
+\r
+ /**\r
+ * Returns the LanManager/LMv2 response.\r
+ *\r
+ * @return A <code>byte[]</code> containing the LanManager response.\r
+ */\r
+ public byte[] getLMResponse() {\r
+ return lmResponse;\r
+ }\r
+\r
+ /**\r
+ * Sets the LanManager/LMv2 response for this message.\r
+ *\r
+ * @param lmResponse The LanManager response.\r
+ */\r
+ public void setLMResponse(byte[] lmResponse) {\r
+ this.lmResponse = lmResponse;\r
+ }\r
+\r
+ /**\r
+ * Returns the NT/NTLMv2 response.\r
+ *\r
+ * @return A <code>byte[]</code> containing the NT/NTLMv2 response.\r
+ */\r
+ public byte[] getNTResponse() {\r
+ return ntResponse;\r
+ }\r
+\r
+ /**\r
+ * Sets the NT/NTLMv2 response for this message.\r
+ *\r
+ * @param lmResponse The NT/NTLMv2 response.\r
+ */\r
+ public void setNTResponse(byte[] ntResponse) {\r
+ this.ntResponse = ntResponse;\r
+ }\r
+\r
+ /**\r
+ * Returns the domain in which the user has an account.\r
+ *\r
+ * @return A <code>String</code> containing the domain for the user.\r
+ */\r
+ public String getDomain() {\r
+ return domain;\r
+ }\r
+\r
+ /**\r
+ * Sets the domain for this message.\r
+ *\r
+ * @param domain The domain.\r
+ */\r
+ public void setDomain(String domain) {\r
+ this.domain = domain;\r
+ }\r
+\r
+ /**\r
+ * Returns the username for the authenticating user.\r
+ *\r
+ * @return A <code>String</code> containing the user for this message.\r
+ */\r
+ public String getUser() {\r
+ return user;\r
+ }\r
+\r
+ /**\r
+ * Sets the user for this message.\r
+ *\r
+ * @param user The user.\r
+ */\r
+ public void setUser(String user) {\r
+ this.user = user;\r
+ }\r
+\r
+ /**\r
+ * Returns the workstation from which authentication is being performed.\r
+ *\r
+ * @return A <code>String</code> containing the workstation.\r
+ */\r
+ public String getWorkstation() {\r
+ return workstation;\r
+ }\r
+\r
+ /**\r
+ * Sets the workstation for this message.\r
+ *\r
+ * @param workstation The workstation.\r
+ */\r
+ public void setWorkstation(String workstation) {\r
+ this.workstation = workstation;\r
+ }\r
+\r
+ /**\r
+ * Returns the session key.\r
+ *\r
+ * @return A <code>byte[]</code> containing the session key.\r
+ */\r
+ public byte[] getSessionKey() {\r
+ return sessionKey;\r
+ }\r
+\r
+ /**\r
+ * Sets the session key.\r
+ *\r
+ * @param sessionKey The session key.\r
+ */\r
+ public void setSessionKey(byte[] sessionKey) {\r
+ this.sessionKey = sessionKey;\r
+ }\r
+\r
+ public byte[] toByteArray() {\r
+ try {\r
+ int flags = getFlags();\r
+ boolean unicode = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0;\r
+ String oem = unicode ? null : getOEMEncoding();\r
+ String domainName = getDomain();\r
+ byte[] domain = null;\r
+ if (domainName != null && domainName.length() != 0) {\r
+ domain = unicode ?\r
+ domainName.getBytes("UnicodeLittleUnmarked") :\r
+ domainName.toUpperCase().getBytes(oem);\r
+ }\r
+ int domainLength = (domain != null) ? domain.length : 0;\r
+ String userName = getUser();\r
+ byte[] user = null;\r
+ if (userName != null && userName.length() != 0) {\r
+ user = unicode ? userName.getBytes("UnicodeLittleUnmarked") :\r
+ userName.toUpperCase().getBytes(oem);\r
+ }\r
+ int userLength = (user != null) ? user.length : 0;\r
+ String workstationName = getWorkstation();\r
+ byte[] workstation = null;\r
+ if (workstationName != null && workstationName.length() != 0) {\r
+ workstation = unicode ?\r
+ workstationName.getBytes("UnicodeLittleUnmarked") :\r
+ workstationName.toUpperCase().getBytes(oem);\r
+ }\r
+ int workstationLength = (workstation != null) ?\r
+ workstation.length : 0;\r
+ byte[] lmResponse = getLMResponse();\r
+ int lmLength = (lmResponse != null) ? lmResponse.length : 0;\r
+ byte[] ntResponse = getNTResponse();\r
+ int ntLength = (ntResponse != null) ? ntResponse.length : 0;\r
+ byte[] sessionKey = getSessionKey();\r
+ int keyLength = (sessionKey != null) ? sessionKey.length : 0;\r
+ byte[] type3 = new byte[64 + domainLength + userLength +\r
+ workstationLength + lmLength + ntLength + keyLength];\r
+ System.arraycopy(NTLMSSP_SIGNATURE, 0, type3, 0, 8);\r
+ writeULong(type3, 8, 3);\r
+ int offset = 64;\r
+ writeSecurityBuffer(type3, 12, offset, lmResponse);\r
+ offset += lmLength;\r
+ writeSecurityBuffer(type3, 20, offset, ntResponse);\r
+ offset += ntLength;\r
+ writeSecurityBuffer(type3, 28, offset, domain);\r
+ offset += domainLength;\r
+ writeSecurityBuffer(type3, 36, offset, user);\r
+ offset += userLength;\r
+ writeSecurityBuffer(type3, 44, offset, workstation);\r
+ offset += workstationLength;\r
+ writeSecurityBuffer(type3, 52, offset, sessionKey);\r
+ writeULong(type3, 60, flags);\r
+ return type3;\r
+ } catch (IOException ex) {\r
+ throw new IllegalStateException(ex.getMessage());\r
+ }\r
+ }\r
+\r
+ public String toString() {\r
+ String user = getUser();\r
+ String domain = getDomain();\r
+ String workstation = getWorkstation();\r
+ byte[] lmResponse = getLMResponse();\r
+ byte[] ntResponse = getNTResponse();\r
+ byte[] sessionKey = getSessionKey();\r
+ int flags = getFlags();\r
+ StringBuffer buffer = new StringBuffer();\r
+ if (domain != null) {\r
+ buffer.append("domain: ").append(domain);\r
+ }\r
+ if (user != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("user: ").append(user);\r
+ }\r
+ if (workstation != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("workstation: ").append(workstation);\r
+ }\r
+ if (lmResponse != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("lmResponse: ");\r
+ buffer.append("0x");\r
+ for (int i = 0; i < lmResponse.length; i++) {\r
+ buffer.append(Integer.toHexString((lmResponse[i] >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(lmResponse[i] & 0x0f));\r
+ }\r
+ }\r
+ if (ntResponse != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("ntResponse: ");\r
+ buffer.append("0x");\r
+ for (int i = 0; i < ntResponse.length; i++) {\r
+ buffer.append(Integer.toHexString((ntResponse[i] >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(ntResponse[i] & 0x0f));\r
+ }\r
+ }\r
+ if (sessionKey != null) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("sessionKey: ");\r
+ buffer.append("0x");\r
+ for (int i = 0; i < sessionKey.length; i++) {\r
+ buffer.append(Integer.toHexString((sessionKey[i] >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(sessionKey[i] & 0x0f));\r
+ }\r
+ }\r
+ if (flags != 0) {\r
+ if (buffer.length() > 0) buffer.append("; ");\r
+ buffer.append("flags: ");\r
+ buffer.append("0x");\r
+ buffer.append(Integer.toHexString((flags >> 28) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 24) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 20) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 16) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 12) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 8) & 0x0f));\r
+ buffer.append(Integer.toHexString((flags >> 4) & 0x0f));\r
+ buffer.append(Integer.toHexString(flags & 0x0f));\r
+ }\r
+ return buffer.toString();\r
+ }\r
+\r
+ /**\r
+ * Returns the default flags for a generic Type-3 message in the\r
+ * current environment.\r
+ *\r
+ * @return An <code>int</code> containing the default flags.\r
+ */\r
+ public static int getDefaultFlags() {\r
+ return DEFAULT_FLAGS;\r
+ }\r
+\r
+ /**\r
+ * Returns the default flags for a Type-3 message created in response\r
+ * to the given Type-2 message in the current environment.\r
+ *\r
+ * @return An <code>int</code> containing the default flags.\r
+ */\r
+ public static int getDefaultFlags(Type2Message type2) {\r
+ if (type2 == null) return DEFAULT_FLAGS;\r
+ int flags = NTLMSSP_NEGOTIATE_NTLM;\r
+ flags |= ((type2.getFlags() & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+ NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM;\r
+ return flags;\r
+ }\r
+\r
+ /**\r
+ * Constructs the LanManager response to the given Type-2 message using\r
+ * the supplied password.\r
+ *\r
+ * @param type2 The Type-2 message.\r
+ * @param password The password.\r
+ * @return A <code>byte[]</code> containing the LanManager response.\r
+ */\r
+ public static byte[] getLMResponse(Type2Message type2, String password) {\r
+ if (type2 == null || password == null) return null;\r
+ return NtlmPasswordAuthentication.getPreNTLMResponse(password,\r
+ type2.getChallenge());\r
+ }\r
+\r
+ public static byte[] getLMv2Response(Type2Message type2,\r
+ String domain, String user, String password,\r
+ byte[] clientChallenge) {\r
+ if (type2 == null || domain == null || user == null ||\r
+ password == null || clientChallenge == null) {\r
+ return null;\r
+ }\r
+ return NtlmPasswordAuthentication.getLMv2Response(domain, user,\r
+ password, type2.getChallenge(), clientChallenge);\r
+ }\r
+\r
+ /**\r
+ * Constructs the NT response to the given Type-2 message using\r
+ * the supplied password.\r
+ *\r
+ * @param type2 The Type-2 message.\r
+ * @param password The password.\r
+ * @return A <code>byte[]</code> containing the NT response.\r
+ */\r
+ public static byte[] getNTResponse(Type2Message type2, String password) {\r
+ if (type2 == null || password == null) return null;\r
+ return NtlmPasswordAuthentication.getNTLMResponse(password,\r
+ type2.getChallenge());\r
+ }\r
+\r
+ /**\r
+ * Returns the default domain from the current environment.\r
+ *\r
+ * @return The default domain.\r
+ */\r
+ public static String getDefaultDomain() {\r
+ return DEFAULT_DOMAIN;\r
+ }\r
+\r
+ /**\r
+ * Returns the default user from the current environment.\r
+ *\r
+ * @return The default user.\r
+ */\r
+ public static String getDefaultUser() {\r
+ return DEFAULT_USER;\r
+ }\r
+\r
+ /**\r
+ * Returns the default password from the current environment.\r
+ *\r
+ * @return The default password.\r
+ */\r
+ public static String getDefaultPassword() {\r
+ return DEFAULT_PASSWORD;\r
+ }\r
+\r
+ /**\r
+ * Returns the default workstation from the current environment.\r
+ *\r
+ * @return The default workstation.\r
+ */\r
+ public static String getDefaultWorkstation() {\r
+ return DEFAULT_WORKSTATION;\r
+ }\r
+\r
+ private void parse(byte[] material) throws IOException {\r
+ for (int i = 0; i < 8; i++) {\r
+ if (material[i] != NTLMSSP_SIGNATURE[i]) {\r
+ throw new IOException("Not an NTLMSSP message.");\r
+ }\r
+ }\r
+ if (readULong(material, 8) != 3) {\r
+ throw new IOException("Not a Type 3 message.");\r
+ }\r
+ byte[] lmResponse = readSecurityBuffer(material, 12);\r
+ int lmResponseOffset = readULong(material, 16);\r
+ byte[] ntResponse = readSecurityBuffer(material, 20);\r
+ int ntResponseOffset = readULong(material, 24);\r
+ byte[] domain = readSecurityBuffer(material, 28);\r
+ int domainOffset = readULong(material, 32);\r
+ byte[] user = readSecurityBuffer(material, 36);\r
+ int userOffset = readULong(material, 40);\r
+ byte[] workstation = readSecurityBuffer(material, 44);\r
+ int workstationOffset = readULong(material, 48);\r
+ int flags;\r
+ String charset;\r
+ if (lmResponseOffset == 52 || ntResponseOffset == 52 ||\r
+ domainOffset == 52 || userOffset == 52 ||\r
+ workstationOffset == 52) {\r
+ flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_OEM;\r
+ charset = getOEMEncoding();\r
+ } else {\r
+ setSessionKey(readSecurityBuffer(material, 52));\r
+ flags = readULong(material, 60);\r
+ charset = ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ?\r
+ "UnicodeLittleUnmarked" : getOEMEncoding();\r
+ }\r
+ setFlags(flags);\r
+ setLMResponse(lmResponse);\r
+ // NTLMv2 issues w/cross-domain authentication; leave NT empty if >= 3\r
+ if (LM_COMPATIBILITY < 3) setNTResponse(ntResponse);\r
+ setDomain(new String(domain, charset));\r
+ setUser(new String(user, charset));\r
+ setWorkstation(new String(workstation, charset));\r
+ }\r
+\r
+}\r