KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jcifs > smb > NtlmPasswordAuthentication


1 /* jcifs smb client library in Java
2  * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
3  * "Eric Glass" <jcifs at samba dot org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package jcifs.smb;
21
22 import java.io.UnsupportedEncodingException JavaDoc;
23 import java.io.Serializable JavaDoc;
24 import java.security.Principal JavaDoc;
25 import java.util.Random JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import jcifs.Config;
28 import jcifs.util.*;
29
30 /**
31  * This class stores and encrypts NTLM user credentials. The default
32  * credentials are retrieved from the <tt>jcifs.smb.client.domain</tt>,
33  * <tt>jcifs.smb.client.username</tt>, and <tt>jcifs.smb.client.password</tt>
34  * properties.
35  * <p>
36  * Read <a HREF="../../../authhandler.html">jCIFS Exceptions and
37  * NtlmAuthenticator</a> for related information.
38  */

39
40 public final class NtlmPasswordAuthentication implements Principal JavaDoc, Serializable JavaDoc {
41
42     private static final int LM_COMPATIBILITY =
43             Config.getInt("jcifs.smb.lmCompatibility", 0);
44
45     private static final Random JavaDoc RANDOM = new Random JavaDoc();
46
47     private static LogStream log = LogStream.getInstance();
48
49     // KGS!@#$%
50
private static final byte[] S8 = {
51         (byte)0x4b, (byte)0x47, (byte)0x53, (byte)0x21,
52         (byte)0x40, (byte)0x23, (byte)0x24, (byte)0x25
53     };
54     private static void E( byte[] key, byte[] data, byte[] e ) {
55         byte[] key7 = new byte[7];
56         byte[] e8 = new byte[8];
57
58         for( int i = 0; i < key.length / 7; i++ ) {
59             System.arraycopy( key, i * 7, key7, 0, 7 );
60             DES des = new DES( key7 );
61             des.encrypt( data, e8 );
62             System.arraycopy( e8, 0, e, i * 8, 8 );
63         }
64     }
65
66     static String JavaDoc DEFAULT_DOMAIN;
67     static String JavaDoc DEFAULT_USERNAME;
68     static String JavaDoc DEFAULT_PASSWORD;
69     static final String JavaDoc BLANK = "";
70
71     static void initDefaults() {
72         if (DEFAULT_DOMAIN != null) return;
73         DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", "?");
74         DEFAULT_USERNAME = Config.getProperty("jcifs.smb.client.username", "GUEST");
75         DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password", BLANK);
76     }
77
78 /**
79  * Generate the ANSI DES hash for the password associated with these credentials.
80  */

81     static public byte[] getPreNTLMResponse( String JavaDoc password, byte[] challenge ) {
82         byte[] p14 = new byte[14];
83         byte[] p21 = new byte[21];
84         byte[] p24 = new byte[24];
85         byte[] passwordBytes;
86         try {
87             passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.OEM_ENCODING );
88         } catch( UnsupportedEncodingException JavaDoc uee ) {
89             throw new RuntimeException JavaDoc("Try setting jcifs.encoding=US-ASCII", uee);
90         }
91         int passwordLength = passwordBytes.length;
92
93         // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM
94
if( passwordLength > 14) {
95             passwordLength = 14;
96         }
97         System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );
98         E( p14, S8, p21);
99         E( p21, challenge, p24);
100         return p24;
101     }
102 /**
103  * Generate the Unicode MD4 hash for the password associated with these credentials.
104  */

105     static public byte[] getNTLMResponse( String JavaDoc password, byte[] challenge ) {
106         byte[] uni = null;
107         byte[] p21 = new byte[21];
108         byte[] p24 = new byte[24];
109
110         try {
111             uni = password.getBytes( "UnicodeLittleUnmarked" );
112         } catch( UnsupportedEncodingException JavaDoc uee ) {
113             if( log.level > 0 )
114                 uee.printStackTrace( log );
115         }
116         MD4 md4 = new MD4();
117         md4.update( uni );
118         try {
119             md4.digest(p21, 0, 16);
120         } catch (Exception JavaDoc ex) {
121             if( log.level > 0 )
122                 ex.printStackTrace( log );
123         }
124         E( p21, challenge, p24 );
125         return p24;
126     }
127
128     /**
129      * Creates the LMv2 response for the supplied information.
130      *
131      * @param domain The domain in which the username exists.
132      * @param user The username.
133      * @param password The user's password.
134      * @param challenge The server challenge.
135      * @param clientChallenge The client challenge (nonce).
136      */

137     public static byte[] getLMv2Response(String JavaDoc domain, String JavaDoc user,
138             String JavaDoc password, byte[] challenge, byte[] clientChallenge) {
139         try {
140             byte[] hash = new byte[16];
141             byte[] response = new byte[24];
142             MD4 md4 = new MD4();
143             md4.update(password.getBytes("UnicodeLittleUnmarked"));
144             HMACT64 hmac = new HMACT64(md4.digest());
145             hmac.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked"));
146             hmac.update(domain.toUpperCase().getBytes("UnicodeLittleUnmarked"));
147             hmac = new HMACT64(hmac.digest());
148             hmac.update(challenge);
149             hmac.update(clientChallenge);
150             hmac.digest(response, 0, 16);
151             System.arraycopy(clientChallenge, 0, response, 16, 8);
152             return response;
153         } catch (Exception JavaDoc ex) {
154             if( log.level > 0 )
155                 ex.printStackTrace( log );
156             return null;
157         }
158     }
159
160     static final NtlmPasswordAuthentication NULL =
161                 new NtlmPasswordAuthentication( "", "", "" );
162     static final NtlmPasswordAuthentication GUEST =
163                 new NtlmPasswordAuthentication( "?", "GUEST", "" );
164     static final NtlmPasswordAuthentication DEFAULT =
165                 new NtlmPasswordAuthentication( null );
166
167     String JavaDoc domain;
168     String JavaDoc username;
169     String JavaDoc password;
170     byte[] ansiHash;
171     byte[] unicodeHash;
172     boolean hashesExternal = false;
173     byte[] clientChallenge = null;
174     byte[] challenge = null;
175
176 /**
177  * Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo
178  * component of an SMB URL like "<tt>domain;user:pass</tt>". This constructor
179  * is used internally be jCIFS when parsing SMB URLs.
180  */

181
182     public NtlmPasswordAuthentication( String JavaDoc userInfo ) {
183         domain = username = password = null;
184
185         if( userInfo != null ) {
186             int i, u, end;
187             char c;
188
189             end = userInfo.length();
190             for( i = 0, u = 0; i < end; i++ ) {
191                 c = userInfo.charAt( i );
192                 if( c == ';' ) {
193                     domain = userInfo.substring( 0, i );
194                     u = i + 1;
195                 } else if( c == ':' ) {
196                     password = userInfo.substring( i + 1 );
197                     break;
198                 }
199             }
200             username = userInfo.substring( u, i );
201         }
202
203         initDefaults();
204
205         if( domain == null ) this.domain = DEFAULT_DOMAIN;
206         if( username == null ) this.username = DEFAULT_USERNAME;
207         if( password == null ) this.password = DEFAULT_PASSWORD;
208     }
209 /**
210  * Create an <tt>NtlmPasswordAuthentication</tt> object from a
211  * domain, username, and password. Parameters that are <tt>null</tt>
212  * will be substituted with <tt>jcifs.smb.client.domain</tt>,
213  * <tt>jcifs.smb.client.username</tt>, <tt>jcifs.smb.client.password</tt>
214  * property values.
215  */

216     public NtlmPasswordAuthentication( String JavaDoc domain, String JavaDoc username, String JavaDoc password ) {
217         this.domain = domain;
218         this.username = username;
219         this.password = password;
220
221         initDefaults();
222
223         if( domain == null ) this.domain = DEFAULT_DOMAIN;
224         if( username == null ) this.username = DEFAULT_USERNAME;
225         if( password == null ) this.password = DEFAULT_PASSWORD;
226     }
227 /**
228  * Create an <tt>NtlmPasswordAuthentication</tt> object with raw password
229  * hashes. This is used exclusively by the <tt>jcifs.http.NtlmSsp</tt>
230  * class which is in turn used by NTLM HTTP authentication functionality.
231  */

232     public NtlmPasswordAuthentication( String JavaDoc domain, String JavaDoc username,
233                     byte[] challenge, byte[] ansiHash, byte[] unicodeHash ) {
234         if( domain == null || username == null ||
235                                     ansiHash == null || unicodeHash == null ) {
236             throw new IllegalArgumentException JavaDoc( "External credentials cannot be null" );
237         }
238         this.domain = domain;
239         this.username = username;
240         this.password = null;
241         this.challenge = challenge;
242         this.ansiHash = ansiHash;
243         this.unicodeHash = unicodeHash;
244         hashesExternal = true;
245     }
246
247 /**
248  * Returns the domain.
249  */

250     public String JavaDoc getDomain() {
251         return domain;
252     }
253 /**
254  * Returns the username.
255  */

256     public String JavaDoc getUsername() {
257         return username;
258     }
259 /**
260  * Returns the password in plain text or <tt>null</tt> if the raw password
261  * hashes were used to construct this <tt>NtlmPasswordAuthentication</tt>
262  * object which will be the case when NTLM HTTP Authentication is
263  * used. There is no way to retrieve a users password in plain text unless
264  * it is supplied by the user at runtime.
265  */

266     public String JavaDoc getPassword() {
267         return password;
268     }
269 /**
270  * Return the domain and username in the format:
271  * <tt>domain\\username</tt>. This is equivalent to <tt>toString()</tt>.
272  */

273     public String JavaDoc getName() {
274         boolean d = domain.length() > 0 && domain.equals( "?" ) == false;
275         return d ? domain + "\\" + username : username;
276     }
277
278 /**
279  * Computes the 24 byte ANSI password hash given the 8 byte server challenge.
280  */

281     public byte[] getAnsiHash( byte[] challenge ) {
282         if( hashesExternal ) {
283             return ansiHash;
284         }
285         switch (LM_COMPATIBILITY) {
286         case 0:
287         case 1:
288             return getPreNTLMResponse( password, challenge );
289         case 2:
290             return getNTLMResponse( password, challenge );
291         case 3:
292         case 4:
293         case 5:
294             if( clientChallenge == null ) {
295                 clientChallenge = new byte[8];
296                 RANDOM.nextBytes( clientChallenge );
297             }
298             return getLMv2Response(domain, username, password, challenge,
299                     clientChallenge);
300         default:
301             return getPreNTLMResponse( password, challenge );
302         }
303     }
304 /**
305  * Computes the 24 byte Unicode password hash given the 8 byte server challenge.
306  */

307     public byte[] getUnicodeHash( byte[] challenge ) {
308         if( hashesExternal ) {
309             return unicodeHash;
310         }
311         switch (LM_COMPATIBILITY) {
312         case 0:
313         case 1:
314         case 2:
315             return getNTLMResponse( password, challenge );
316         case 3:
317         case 4:
318         case 5:
319             /*
320             if( clientChallenge == null ) {
321                 clientChallenge = new byte[8];
322                 RANDOM.nextBytes( clientChallenge );
323             }
324             return getNTLMv2Response(domain, username, password, null,
325                     challenge, clientChallenge);
326             */

327             return new byte[0];
328         default:
329             return getNTLMResponse( password, challenge );
330         }
331     }
332
333     /**
334      * Returns the effective user session key.
335      *
336      * @param challenge The server challenge.
337      * @return A <code>byte[]</code> containing the effective user session key,
338      * used in SMB MAC signing and NTLMSSP signing and sealing.
339      */

340     public byte[] getUserSessionKey(byte[] challenge) {
341         if (hashesExternal) return null;
342         byte[] key = new byte[16];
343         try {
344             getUserSessionKey(challenge, key, 0);
345         } catch (Exception JavaDoc ex) {
346             if( log.level > 0 )
347                 ex.printStackTrace( log );
348         }
349         return key;
350     }
351
352     /**
353      * Calculates the effective user session key.
354      *
355      * @param challenge The server challenge.
356      * @param dest The destination array in which the user session key will be
357      * placed.
358      * @param offset The offset in the destination array at which the
359      * session key will start.
360      */

361     void getUserSessionKey(byte[] challenge, byte[] dest, int offset)
362             throws Exception JavaDoc {
363         if (hashesExternal) return;
364         MD4 md4 = new MD4();
365         md4.update(password.getBytes("UnicodeLittleUnmarked"));
366         switch (LM_COMPATIBILITY) {
367         case 0:
368         case 1:
369         case 2:
370             md4.update(md4.digest());
371             md4.digest(dest, offset, 16);
372             break;
373         case 3:
374         case 4:
375         case 5:
376             if( clientChallenge == null ) {
377                 clientChallenge = new byte[8];
378                 RANDOM.nextBytes( clientChallenge );
379             }
380
381             HMACT64 hmac = new HMACT64(md4.digest());
382             hmac.update(username.toUpperCase().getBytes(
383                     "UnicodeLittleUnmarked"));
384             hmac.update(domain.toUpperCase().getBytes(
385                     "UnicodeLittleUnmarked"));
386             byte[] ntlmv2Hash = hmac.digest();
387             hmac = new HMACT64(ntlmv2Hash);
388             hmac.update(challenge);
389             hmac.update(clientChallenge);
390             HMACT64 userKey = new HMACT64(ntlmv2Hash);
391             userKey.update(hmac.digest());
392             userKey.digest(dest, offset, 16);
393             break;
394         default:
395             md4.update(md4.digest());
396             md4.digest(dest, offset, 16);
397             break;
398         }
399     }
400
401 /**
402  * Compares two <tt>NtlmPasswordAuthentication</tt> objects for
403  * equality. Two <tt>NtlmPasswordAuthentication</tt> objects are equal if
404  * 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.
405  */

406     public boolean equals( Object JavaDoc obj ) {
407         if( obj instanceof NtlmPasswordAuthentication ) {
408             NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj;
409             if( ntlm.domain.toUpperCase().equals( domain.toUpperCase() ) &&
410                         ntlm.username.toUpperCase().equals( username.toUpperCase() )) {
411                 if( hashesExternal && ntlm.hashesExternal ) {
412                     return Arrays.equals( ansiHash, ntlm.ansiHash ) &&
413                                 Arrays.equals( unicodeHash, ntlm.unicodeHash );
414                     /* This still isn't quite right. If one npa object does not have external
415                      * hashes and the other does then they will not be considered equal even
416                      * though they may be.
417                      */

418                 } else if( !hashesExternal && password.equals( ntlm.password )) {
419                     return true;
420                 }
421             }
422         }
423         return false;
424     }
425
426
427 /**
428  * Return the upcased username hash code.
429  */

430     public int hashCode() {
431         return getName().toUpperCase().hashCode();
432     }
433 /**
434  * Return the domain and username in the format:
435  * <tt>domain\\username</tt>. This is equivalent to <tt>getName()</tt>.
436  */

437     public String JavaDoc toString() {
438         return getName();
439     }
440 }
441
442
Popular Tags