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
26package sun.net.www.protocol.http.ntlm;
27
28import java.io.IOException;
29import java.net.InetAddress;
30import java.net.PasswordAuthentication;
31import java.net.UnknownHostException;
32import java.net.URL;
33import java.util.Objects;
34import java.util.Properties;
35import sun.net.www.HeaderParser;
36import sun.net.www.protocol.http.AuthenticationInfo;
37import sun.net.www.protocol.http.AuthScheme;
38import sun.net.www.protocol.http.HttpURLConnection;
39import sun.security.action.GetPropertyAction;
40
41/**
42 * NTLMAuthentication:
43 *
44 * @author Michael McMahon
45 */
46
47public class NTLMAuthentication extends AuthenticationInfo {
48
49    private static final long serialVersionUID = 100L;
50
51    private static final NTLMAuthenticationCallback NTLMAuthCallback =
52        NTLMAuthenticationCallback.getNTLMAuthenticationCallback();
53
54    private String hostname;
55    /* Domain to use if not specified by user */
56    private static final String defaultDomain;
57    /* Whether cache is enabled for NTLM */
58    private static final boolean ntlmCache;
59    static {
60        Properties props = GetPropertyAction.privilegedGetProperties();
61        defaultDomain = props.getProperty("http.auth.ntlm.domain", "domain");
62        String ntlmCacheProp = props.getProperty("jdk.ntlm.cache", "true");
63        ntlmCache = Boolean.parseBoolean(ntlmCacheProp);
64    }
65
66    private void init0() {
67
68        hostname = java.security.AccessController.doPrivileged(
69            new java.security.PrivilegedAction<String>() {
70            public String run() {
71                String localhost;
72                try {
73                    localhost = InetAddress.getLocalHost().getHostName().toUpperCase();
74                } catch (UnknownHostException e) {
75                     localhost = "localhost";
76                }
77                return localhost;
78            }
79        });
80        int x = hostname.indexOf ('.');
81        if (x != -1) {
82            hostname = hostname.substring (0, x);
83        }
84    }
85
86    String username;
87    String ntdomain;
88    String password;
89
90    /**
91     * Create a NTLMAuthentication:
92     * Username may be specified as {@literal domain<BACKSLASH>username}
93     * in the application Authenticator.
94     * If this notation is not used, then the domain will be taken
95     * from a system property: "http.auth.ntlm.domain".
96     */
97    public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw,
98                              String authenticatorKey) {
99        super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
100              AuthScheme.NTLM,
101              url,
102              "",
103              Objects.requireNonNull(authenticatorKey));
104        init (pw);
105    }
106
107    private void init (PasswordAuthentication pw) {
108        this.pw = pw;
109        if (pw != null) {
110            String s = pw.getUserName();
111            int i = s.indexOf ('\\');
112            if (i == -1) {
113                username = s;
114                ntdomain = defaultDomain;
115            } else {
116                ntdomain = s.substring (0, i).toUpperCase();
117                username = s.substring (i+1);
118            }
119            password = new String (pw.getPassword());
120        } else {
121            /* credentials will be acquired from OS */
122            username = null;
123            ntdomain = null;
124            password = null;
125        }
126        init0();
127    }
128
129   /**
130    * Constructor used for proxy entries
131    */
132    public NTLMAuthentication(boolean isProxy, String host, int port,
133                              PasswordAuthentication pw,
134                              String authenticatorKey) {
135        super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
136              AuthScheme.NTLM,
137              host,
138              port,
139              "",
140              Objects.requireNonNull(authenticatorKey));
141        init (pw);
142    }
143
144    @Override
145    protected boolean useAuthCache() {
146        return ntlmCache && super.useAuthCache();
147    }
148
149    /**
150     * @return true if this authentication supports preemptive authorization
151     */
152    @Override
153    public boolean supportsPreemptiveAuthorization() {
154        return false;
155    }
156
157    /**
158     * @return true if NTLM supported transparently (no password needed, SSO)
159     */
160    public static boolean supportsTransparentAuth() {
161        return true;
162    }
163
164    /**
165     * Returns true if the given site is trusted, i.e. we can try
166     * transparent Authentication.
167     */
168    public static boolean isTrustedSite(URL url) {
169        return NTLMAuthCallback.isTrustedSite(url);
170    }
171
172    /**
173     * Not supported. Must use the setHeaders() method
174     */
175    @Override
176    public String getHeaderValue(URL url, String method) {
177        throw new RuntimeException ("getHeaderValue not supported");
178    }
179
180    /**
181     * Check if the header indicates that the current auth. parameters are stale.
182     * If so, then replace the relevant field with the new value
183     * and return true. Otherwise return false.
184     * returning true means the request can be retried with the same userid/password
185     * returning false means we have to go back to the user to ask for a new
186     * username password.
187     */
188    @Override
189    public boolean isAuthorizationStale (String header) {
190        return false; /* should not be called for ntlm */
191    }
192
193    /**
194     * Set header(s) on the given connection.
195     * @param conn The connection to apply the header(s) to
196     * @param p A source of header values for this connection, not used because
197     *          HeaderParser converts the fields to lower case, use raw instead
198     * @param raw The raw header field.
199     * @return true if all goes well, false if no headers were set.
200     */
201    @Override
202    public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
203
204        try {
205            NTLMAuthSequence seq = (NTLMAuthSequence)conn.authObj();
206            if (seq == null) {
207                seq = new NTLMAuthSequence (username, password, ntdomain);
208                conn.authObj(seq);
209            }
210            String response = "NTLM " + seq.getAuthHeader (raw.length()>6?raw.substring(5):null);
211            conn.setAuthenticationProperty(getHeaderName(), response);
212            if (seq.isComplete()) {
213                conn.authObj(null);
214            }
215            return true;
216        } catch (IOException e) {
217            conn.authObj(null);
218            return false;
219        }
220    }
221
222}
223