1 /*
2  * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.net.www.protocol.http.ntlm;
27 
28 import java.io.IOException;
29 import java.net.InetAddress;
30 import java.net.PasswordAuthentication;
31 import java.net.UnknownHostException;
32 import java.net.URL;
33 import java.util.Objects;
34 import java.util.Properties;
35 import sun.net.NetProperties;
36 import sun.net.www.HeaderParser;
37 import sun.net.www.protocol.http.AuthenticationInfo;
38 import sun.net.www.protocol.http.AuthScheme;
39 import sun.net.www.protocol.http.HttpURLConnection;
40 import sun.security.action.GetPropertyAction;
41 
42 /**
43  * NTLMAuthentication:
44  *
45  * @author Michael McMahon
46  */
47 
48 public class NTLMAuthentication extends AuthenticationInfo {
49 
50     private static final long serialVersionUID = 100L;
51 
52     private static final NTLMAuthenticationCallback NTLMAuthCallback =
53         NTLMAuthenticationCallback.getNTLMAuthenticationCallback();
54 
55     private String hostname;
56     /* Domain to use if not specified by user */
57     private static final String defaultDomain;
58     /* Whether cache is enabled for NTLM */
59     private static final boolean ntlmCache;
60 
61     enum TransparentAuth {
62         DISABLED,      // disable for all hosts (default)
63         TRUSTED_HOSTS, // use Windows trusted hosts settings
64         ALL_HOSTS      // attempt for all hosts
65     }
66 
67     private static final TransparentAuth authMode;
68 
69     static {
70         Properties props = GetPropertyAction.privilegedGetProperties();
71         defaultDomain = props.getProperty("http.auth.ntlm.domain", "domain");
72         String ntlmCacheProp = props.getProperty("jdk.ntlm.cache", "true");
73         ntlmCache = Boolean.parseBoolean(ntlmCacheProp);
74         String modeProp = java.security.AccessController.doPrivileged(
75             new java.security.PrivilegedAction<String>() {
76                 public String run() {
77                     return NetProperties.get("jdk.http.ntlm.transparentAuth");
78                 }
79             });
80 
81         if ("trustedHosts".equalsIgnoreCase(modeProp))
82             authMode = TransparentAuth.TRUSTED_HOSTS;
83         else if ("allHosts".equalsIgnoreCase(modeProp))
84             authMode = TransparentAuth.ALL_HOSTS;
85         else
86             authMode = TransparentAuth.DISABLED;
87     }
88 
init0()89     private void init0() {
90 
91         hostname = java.security.AccessController.doPrivileged(
92             new java.security.PrivilegedAction<String>() {
93             public String run() {
94                 String localhost;
95                 try {
96                     localhost = InetAddress.getLocalHost().getHostName().toUpperCase();
97                 } catch (UnknownHostException e) {
98                      localhost = "localhost";
99                 }
100                 return localhost;
101             }
102         });
103         int x = hostname.indexOf ('.');
104         if (x != -1) {
105             hostname = hostname.substring (0, x);
106         }
107     }
108 
109     String username;
110     String ntdomain;
111     String password;
112 
113     /**
114      * Create a NTLMAuthentication:
115      * Username may be specified as {@literal domain<BACKSLASH>username}
116      * in the application Authenticator.
117      * If this notation is not used, then the domain will be taken
118      * from a system property: "http.auth.ntlm.domain".
119      */
NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw, String authenticatorKey)120     public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw,
121                               String authenticatorKey) {
122         super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
123               AuthScheme.NTLM,
124               url,
125               "",
126               Objects.requireNonNull(authenticatorKey));
127         init (pw);
128     }
129 
init(PasswordAuthentication pw)130     private void init (PasswordAuthentication pw) {
131         this.pw = pw;
132         if (pw != null) {
133             String s = pw.getUserName();
134             int i = s.indexOf ('\\');
135             if (i == -1) {
136                 username = s;
137                 ntdomain = defaultDomain;
138             } else {
139                 ntdomain = s.substring (0, i).toUpperCase();
140                 username = s.substring (i+1);
141             }
142             password = new String (pw.getPassword());
143         } else {
144             /* credentials will be acquired from OS */
145             username = null;
146             ntdomain = null;
147             password = null;
148         }
149         init0();
150     }
151 
152    /**
153     * Constructor used for proxy entries
154     */
NTLMAuthentication(boolean isProxy, String host, int port, PasswordAuthentication pw, String authenticatorKey)155     public NTLMAuthentication(boolean isProxy, String host, int port,
156                               PasswordAuthentication pw,
157                               String authenticatorKey) {
158         super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
159               AuthScheme.NTLM,
160               host,
161               port,
162               "",
163               Objects.requireNonNull(authenticatorKey));
164         init (pw);
165     }
166 
167     @Override
useAuthCache()168     protected boolean useAuthCache() {
169         return ntlmCache && super.useAuthCache();
170     }
171 
172     /**
173      * @return true if this authentication supports preemptive authorization
174      */
175     @Override
supportsPreemptiveAuthorization()176     public boolean supportsPreemptiveAuthorization() {
177         return false;
178     }
179 
180     /**
181      * @return true if NTLM supported transparently (no password needed, SSO)
182      */
supportsTransparentAuth()183     public static boolean supportsTransparentAuth() {
184         return true;
185     }
186 
187     /**
188      * Returns true if the given site is trusted, i.e. we can try
189      * transparent Authentication.
190      */
isTrustedSite(URL url)191     public static boolean isTrustedSite(URL url) {
192         if (NTLMAuthCallback != null)
193             return NTLMAuthCallback.isTrustedSite(url);
194 
195         switch (authMode) {
196             case TRUSTED_HOSTS:
197                 return isTrustedSite(url.toString());
198             case ALL_HOSTS:
199                 return true;
200             default:
201                 return false;
202         }
203     }
204 
isTrustedSite(String url)205     static native boolean isTrustedSite(String url);
206 
207     /**
208      * Not supported. Must use the setHeaders() method
209      */
210     @Override
getHeaderValue(URL url, String method)211     public String getHeaderValue(URL url, String method) {
212         throw new RuntimeException ("getHeaderValue not supported");
213     }
214 
215     /**
216      * Check if the header indicates that the current auth. parameters are stale.
217      * If so, then replace the relevant field with the new value
218      * and return true. Otherwise return false.
219      * returning true means the request can be retried with the same userid/password
220      * returning false means we have to go back to the user to ask for a new
221      * username password.
222      */
223     @Override
isAuthorizationStale(String header)224     public boolean isAuthorizationStale (String header) {
225         return false; /* should not be called for ntlm */
226     }
227 
228     /**
229      * Set header(s) on the given connection.
230      * @param conn The connection to apply the header(s) to
231      * @param p A source of header values for this connection, not used because
232      *          HeaderParser converts the fields to lower case, use raw instead
233      * @param raw The raw header field.
234      * @return true if all goes well, false if no headers were set.
235      */
236     @Override
setHeaders(HttpURLConnection conn, HeaderParser p, String raw)237     public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
238 
239         try {
240             NTLMAuthSequence seq = (NTLMAuthSequence)conn.authObj();
241             if (seq == null) {
242                 seq = new NTLMAuthSequence (username, password, ntdomain);
243                 conn.authObj(seq);
244             }
245             String response = "NTLM " + seq.getAuthHeader (raw.length()>6?raw.substring(5):null);
246             conn.setAuthenticationProperty(getHeaderName(), response);
247             if (seq.isComplete()) {
248                 conn.authObj(null);
249             }
250             return true;
251         } catch (IOException e) {
252             conn.authObj(null);
253             return false;
254         }
255     }
256 }
257