001: /*
002: * This program is free software; you can redistribute it and/or modify
003: * it under the terms of the GNU General Public License as published by
004: * the Free Software Foundation; either version 2 of the License, or
005: * (at your option) any later version.
006: *
007: * This program is distributed in the hope that it will be useful,
008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: * GNU General Public License for more details.
011: *
012: * You should have received a copy of the GNU General Public License
013: * along with this program; if not, write to the Free Software
014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
015: */
016:
017: /*
018: * NTLMAuthorizationHandler.java
019: * Copyright (C) 2002 Luigi Dragone
020: *
021: */
022:
023: package net.matuschek.http;
024:
025: import java.io.*;
026: import java.security.Provider;
027: import java.security.Security;
028:
029: import com.luigidragone.net.ntlm.NTLM;
030: import net.matuschek.util.Base64;
031:
032: /**
033: * <p>
034: * This is an NTLM protocol authorization module for
035: * <a href="http://www.matuschek.net/software/jobo/index.html">JOBO</a>.
036: * </p>
037: * <p>
038: * NTLM is a Microsoft proprietary network authentication protocol used in many
039: * situations and HTTPClient is a versatile and extendible component for
040: * implementing HTTP client applications.
041: * </p>
042: * <p>
043: * This class relies on {@link NTLM NTLM} class. It also requires a JCE
044: * compliant library (e.g., <a href="http://www.cryptix.org/"> Cryptix JCE</a>)
045: * that implements MD4 and DES algorithms.
046: * </p>
047: * To perform an authentication the following information are needed:
048: * <ul>
049: * <li>the host name (with its own domain);</li>
050: * <li>the user name (with its own domain);</li>
051: * <li>the user password.</li>
052: * </ul>
053: * Alternatively, the user password can be replaced with its Lan Manager and
054: * NT hashed versions. On a Windows system these can be collected in the registry
055: * (with a bit of JNI, so), otherwise can be extracted from a SAMBA password
056: * file.<br>
057: * Notice that the host and user domain could not be the same.
058: * </p>
059: *
060: * @author Luigi Dragone (<a href="mailto:luigi@luigidragone. com">luigi@luigidragone.com</a>)
061: * @author oliver_schmidt
062: *
063: * @see <a href="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
064: * @see <a href="http://www.innovation.ch/">HTTPClient</a>
065: * @see <a href="http://rfc.net/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
066: * @see <a href="http://rfc.net/rfc2617.html">HTTP Authentication: Basic and Digest Access Authentication</a>
067: *
068: * @version $Id: NTLMAuthorization.java,v 1.2 2003/05/23 17:00:00 oliver_schmidt
069: * Exp $
070: * </p>
071: */
072:
073: public class NTLMAuthorization implements Serializable, Cloneable {
074:
075: // if nothing in data structure has changed, keep this UID:
076: // otherwise calculate new
077: static final long serialVersionUID = -1347968095010834437L;
078:
079: public class MissingJceException extends Exception {
080: private static final long serialVersionUID = -6580761514651447918L;
081:
082: MissingJceException(String msg) {
083: super (msg);
084: }
085: };
086:
087: public static final String NTLM_TAG = "NTLM";
088: public static final String PROXY_AUTHENTICATE_HEADER = "Proxy-Authenticate";
089: public static final String WWW_AUTHENTICATE_HEADER = "WWW-Authenticate";
090:
091: transient byte[] nonce = null;
092: private String host = null;
093: private String user = null;
094: private String hostDomain = null;
095: private String userDomain = null;
096: private String securityProvider = null;
097: private byte[] lmPassword = null;
098: private byte[] ntPassword = null;
099:
100: private static boolean securityProviderAdded = false;
101:
102: /**
103: * Default constructor requires setting of attributes.
104: */
105: public NTLMAuthorization() {
106: }
107:
108: /**
109: * <p>
110: * Build an NTLM authorization handler for the specified authentication credentials.
111: * </p>
112: * <p>
113: * All the arguments are mandatory (null value are not allowed).
114: * </p>
115: *
116: * @param host the name of the host that is authenticating
117: * @param hostDomain the name of the domain to which the host belongs
118: * @param user the name of the user
119: * @param userDomain the name of the domain to which the user belongs
120: * @param lmPassword a 16-bytes array containing the Lan Manager hashed password
121: * @param ntPassword a 16-bytes array containing the NT hashed password
122: *
123: * @exception IllegalArgumentException if a supplied argument is invalid
124: */
125: public NTLMAuthorization(String host, String hostDomain,
126: String user, String userDomain, byte[] lmPassword,
127: byte[] ntPassword) {
128: setHost(host);
129: setHostDomain(hostDomain);
130: setUser(user);
131: setUserDomain(userDomain);
132: setLmPasswordHash(lmPassword);
133: setNtPasswordHash(ntPassword);
134: }
135:
136: /**
137: * <p>
138: * Build an NTLM authorization handler for the specified authentication credentials.
139: * </p>
140: * <p>
141: * All the arguments are mandatory (null value are not allowed).
142: * </p>
143: *
144: * @param host the name of the host that is authenticating
145: * @param hostDomain the name of the domain to which the host belongs
146: * @param user the name of the user
147: * @param userDomain the name of the domain to which the user belongs
148: * @param password the user's password
149: *
150: * @exception IllegalArgumentException if a supplied argument is invalid
151: * @exception javax.crypto.NoSuchPaddingException if there isn't any suitable padding method
152: * @exception NoSuchAlgorithmException if there isn't any suitable cipher algorithm
153: */
154: public NTLMAuthorization(String host, String hostDomain,
155: String user, String userDomain, String password)
156: throws MissingJceException {
157: setHost(host);
158: setHostDomain(hostDomain);
159: setUser(user);
160: setUserDomain(userDomain);
161: setPassword(password);
162: }
163:
164: public String getResponse() throws MissingJceException {
165: try {
166: return new String(Base64.encode(NTLM.formatResponse(host,
167: user, userDomain, lmPassword, ntPassword, nonce)));
168: } catch (Exception e) {
169: throw new MissingJceException(e.getMessage());
170: }
171: }
172:
173: public String getRequest() throws IOException {
174: return new String(Base64.encode(NTLM.formatRequest(host,
175: hostDomain)));
176: }
177:
178: public void extractNonce(String challenge)
179: throws java.io.IOException {
180: nonce = null;
181: try {
182: // String challenge = parm1.getHeader(PROXY_AUTHENTICATE_HEADER);
183: if ((challenge != null) && challenge.startsWith(NTLM_TAG)
184: && challenge.length() > 4)
185: nonce = NTLM.getNonce(Base64.decode(challenge
186: .substring(challenge.indexOf(' ') + 1).trim()));
187: /* else {
188: challenge = parm1.getHeader(WWW_AUTHENTICATE_HEADER);
189: if((challenge != null) && challenge.startsWith(NTLM_TAG) && challenge.length() > 4)
190: nonce = NTLM.getNonce(Codecs.base64Decode(challenge.substring(challenge.indexOf(' ') + 1).trim()).getBytes());
191: } */
192: } catch (Exception ex) {
193: ex.printStackTrace();
194: }
195: }
196:
197: public void setHost(String host) {
198: if (host == null)
199: throw new IllegalArgumentException(
200: "host : null value not allowed");
201: this .host = host;
202: }
203:
204: public String getHost() {
205: return host;
206: }
207:
208: public void setHostDomain(String hostDomain) {
209: if (hostDomain == null)
210: throw new IllegalArgumentException(
211: "hostDomain : null value not allowed");
212: this .hostDomain = hostDomain;
213: }
214:
215: public String getHostDomain() {
216: return hostDomain;
217: }
218:
219: public void setUser(String user) {
220: if (user == null)
221: throw new IllegalArgumentException(
222: "user : null value not allowed");
223: this .user = user;
224: }
225:
226: public String getUser() {
227: return user;
228: }
229:
230: public void setUserDomain(String userDomain) {
231: if (userDomain == null)
232: throw new IllegalArgumentException(
233: "userDomain : null value not allowed");
234: this .userDomain = userDomain;
235: }
236:
237: public void setPassword(String password) throws MissingJceException {
238: setLmPassword(password);
239: setNtPassword(password);
240: }
241:
242: public String getUserDomain() {
243: return userDomain;
244: }
245:
246: public String getDomain() {
247: return userDomain;
248: }
249:
250: public void setDomain(String domain) {
251: setUserDomain(domain);
252: setHostDomain(domain);
253: }
254:
255: public void setLmPassword(String password)
256: throws MissingJceException {
257: addStandardSecurityProvider();
258: if (password == null)
259: throw new IllegalArgumentException(
260: "lmPassword : null value not allowed");
261: try {
262: this .lmPassword = NTLM.computeLMPassword(password);
263: } catch (Exception e) {
264: throw new MissingJceException(e.getMessage());
265: }
266: }
267:
268: public String getLmPassword() {
269: return lmPassword.toString();
270: };
271:
272: public String getNtPassword() {
273: return ntPassword.toString();
274: };
275:
276: public String getPassword() {
277: return ntPassword.toString();
278: };
279:
280: public byte[] getLmPasswordHash() {
281: return lmPassword;
282: };
283:
284: public byte[] getNtPasswordHash() {
285: return ntPassword;
286: };
287:
288: public void setLmPasswordHash(byte[] password) {
289: if (password == null)
290: throw new IllegalArgumentException(
291: "lmPassword : null value not allowed");
292: if (password.length != 16)
293: throw new IllegalArgumentException(
294: "lmPassword : illegal size");
295: this .lmPassword = password;
296: }
297:
298: public void setNtPassword(String password)
299: throws MissingJceException {
300: addStandardSecurityProvider();
301: if (password == null)
302: throw new IllegalArgumentException(
303: "ntPassword : null value not allowed");
304: try {
305: this .ntPassword = NTLM.computeNTPassword(password);
306: } catch (Exception e) {
307: throw new MissingJceException(e.getMessage());
308: }
309: }
310:
311: public void setNtPasswordHash(byte[] password) {
312: if (password == null)
313: throw new IllegalArgumentException(
314: "ntPassword : null value not allowed");
315: if (password.length != 16)
316: throw new IllegalArgumentException(
317: "ntPassword : illegal size");
318: this .ntPassword = password;
319: }
320:
321: public String toString() {
322: return "Host=" + host + ", HostDomain=" + hostDomain
323: + ", User=" + user + ", UserDomain=" + userDomain
324: + ", lmPwd=" + lmPassword + ", ntPwd=" + ntPassword
325: + ", Nonce=" + nonce;
326: }
327:
328: /**
329: * Adds a standard security provider "cryptix.jce. provider.CryptixCrypto".
330: */
331: public final void addStandardSecurityProvider() {
332: if (!securityProviderAdded) {
333: try {
334: setSecurityProvider("cryptix.jce.provider.CryptixCrypto");
335: } catch (Exception ex) {
336: // Ignore errors. Hopefully a provider is available.
337: }
338: securityProviderAdded = true;
339: }
340: }
341:
342: public void setSecurityProvider(String securityProviderClassName)
343: throws ClassNotFoundException, InstantiationException,
344: IllegalAccessException {
345: this .securityProvider = securityProviderClassName;
346: if (securityProviderClassName != null) {
347: Class securityProvider = Class
348: .forName(securityProviderClassName);
349: Security.addProvider((Provider) securityProvider
350: .newInstance());
351: securityProviderAdded = true;
352: }
353: }
354:
355: public String getSecurityProvider() {
356: return securityProvider;
357: }
358:
359: public boolean isComplete() {
360: return lmPassword != null && ntPassword != null
361: && hostDomain != null && userDomain != null
362: && user != null;
363: }
364:
365: public void writeToFile(String filename)
366: throws FileNotFoundException, IOException {
367: ObjectOutputStream os = new ObjectOutputStream(
368: new FileOutputStream(filename));
369: os.writeObject(this );
370: os.close();
371: }
372:
373: public static NTLMAuthorization readFromFile(String filename)
374: throws OptionalDataException, ClassNotFoundException,
375: IOException {
376: ObjectInputStream is = new ObjectInputStream(
377: new FileInputStream(filename));
378: NTLMAuthorization auth = (NTLMAuthorization) is.readObject();
379: return auth;
380: }
381:
382: public void setFilename(String filename)
383: throws OptionalDataException, IOException,
384: ClassNotFoundException, InstantiationException,
385: IllegalAccessException {
386: NTLMAuthorization auth = readFromFile(filename);
387: host = auth.host;
388: hostDomain = auth.hostDomain;
389: lmPassword = auth.lmPassword;
390: ntPassword = auth.ntPassword;
391: setSecurityProvider(auth.securityProvider);
392: user = auth.user;
393: userDomain = auth.userDomain;
394: }
395:
396: public String getFilename() {
397: return null;
398: }
399:
400: public static void main(String[] args) {
401: NTLMAuthorization auth = new NTLMAuthorization();
402: try {
403: String filename = "";
404: for (int i = 0; i < args.length; i++) {
405: String s = args[i];
406: int sep = s.indexOf("=");
407: String left = sep >= 0 ? s.substring(0, sep)
408: .toLowerCase() : null;
409: String right = sep >= 0 ? s.substring(sep + 1) : s;
410:
411: if (left == null) {
412: filename = s;
413: } else if (left.equals("-domain")) {
414: auth.setDomain(right);
415: } else if (left.equals("-host")) {
416: auth.setHost(right);
417: } else if (left.equals("-hostdomain")) {
418: auth.setHostDomain(right);
419: } else if (left.equals("-lmpassword")) {
420: auth.setLmPassword(right);
421: } else if (left.equals("-ntpassword")) {
422: auth.setNtPassword(right);
423: } else if (left.equals("-password")) {
424: auth.setPassword(right);
425: } else if (left.equals("-securityprovider")) {
426: auth.setSecurityProvider(right);
427: } else if (left.equals("-user")) {
428: auth.setUser(right);
429: } else if (left.equals("-userDomain")) {
430: auth.setUserDomain(right);
431: } else {
432: System.err.println("Unrecognized parameter: "
433: + left);
434: }
435: }
436: if (!auth.isComplete() || filename == null) {
437: System.err
438: .println("Syntax: <filename> <-parameter=value>*");
439: System.err
440: .println("Required parameters: domain | (hostDomain, userDomain),");
441: System.err.println(" user,");
442: System.err
443: .println(" password | (lmPassword, ntPassword)");
444: System.err
445: .println("Optional parameters: securityprovider\n");
446: System.err
447: .println("Example : ntAuth.dat -user=NTLM -domain=TEAMSPORT -password=NTLMNTLM");
448: System.exit(0);
449: }
450:
451: auth.writeToFile(filename);
452: System.out.println(filename + " successfully written.");
453:
454: auth = readFromFile(filename);
455: System.out.println(filename + " successfully read:");
456: System.out.println(auth);
457:
458: } catch (Exception e) {
459: e.printStackTrace();
460: }
461: }
462:
463: public Object clone() {
464: try {
465: return super .clone();
466: } catch (CloneNotSupportedException e) {
467: return null; // Canīt happen
468: }
469: }
470:
471: }
|