• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2005, 2009, 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;
27 
28 import java.net.URL;
29 import java.io.IOException;
30 import java.net.Authenticator.RequestorType;
31 import java.util.HashMap;
32 import sun.net.www.HeaderParser;
33 import sun.misc.BASE64Decoder;
34 import sun.misc.BASE64Encoder;
35 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
36 import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
37 
38 /**
39  * NegotiateAuthentication:
40  *
41  * @author weijun.wang@sun.com
42  * @since 1.6
43  */
44 
45 class NegotiateAuthentication extends AuthenticationInfo {
46 
47     private static final long serialVersionUID = 100L;
48 
49     final private HttpCallerInfo hci;
50 
51     // These maps are used to manage the GSS availability for diffrent
52     // hosts. The key for both maps is the host name.
53     // <code>supported</code> is set when isSupported is checked,
54     // if it's true, a cached Negotiator is put into <code>cache</code>.
55     // the cache can be used only once, so after the first use, it's cleaned.
56     static HashMap <String, Boolean> supported = null;
57     static HashMap <String, Negotiator> cache = null;
58 
59     // The HTTP Negotiate Helper
60     private Negotiator negotiator = null;
61 
62    /**
63     * Constructor used for both WWW and proxy entries.
64     * @param hci a schemed object.
65     */
NegotiateAuthentication(HttpCallerInfo hci)66     public NegotiateAuthentication(HttpCallerInfo hci) {
67         super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
68               hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS,
69               hci.url,
70               "");
71         this.hci = hci;
72     }
73 
74     /**
75      * @return true if this authentication supports preemptive authorization
76      */
77     @Override
supportsPreemptiveAuthorization()78     public boolean supportsPreemptiveAuthorization() {
79         return false;
80     }
81 
82     /**
83      * Find out if the HttpCallerInfo supports Negotiate protocol. In order to
84      * find out yes or no, an initialization of a Negotiator object against it
85      * is tried. The generated object will be cached under the name of ths
86      * hostname at a success try.<br>
87      *
88      * If this method is called for the second time on an HttpCallerInfo with
89      * the same hostname, the answer is retrieved from cache.
90      *
91      * @return true if supported
92      */
isSupported(HttpCallerInfo hci)93     synchronized public static boolean isSupported(HttpCallerInfo hci) {
94         if (supported == null) {
95             supported = new HashMap <String, Boolean>();
96             cache = new HashMap <String, Negotiator>();
97         }
98         String hostname = hci.host;
99         hostname = hostname.toLowerCase();
100         if (supported.containsKey(hostname)) {
101             return supported.get(hostname);
102         }
103 
104         Negotiator neg = Negotiator.getNegotiator(hci);
105         if (neg != null) {
106             supported.put(hostname, true);
107             // the only place cache.put is called. here we can make sure
108             // the object is valid and the oneToken inside is not null
109             cache.put(hostname, neg);
110             return true;
111         } else {
112             supported.put(hostname, false);
113             return false;
114         }
115     }
116 
117     /**
118      * Not supported. Must use the setHeaders() method
119      */
120     @Override
getHeaderValue(URL url, String method)121     public String getHeaderValue(URL url, String method) {
122         throw new RuntimeException ("getHeaderValue not supported");
123     }
124 
125     /**
126      * Check if the header indicates that the current auth. parameters are stale.
127      * If so, then replace the relevant field with the new value
128      * and return true. Otherwise return false.
129      * returning true means the request can be retried with the same userid/password
130      * returning false means we have to go back to the user to ask for a new
131      * username password.
132      */
133     @Override
isAuthorizationStale(String header)134     public boolean isAuthorizationStale (String header) {
135         return false; /* should not be called for Negotiate */
136     }
137 
138     /**
139      * Set header(s) on the given connection.
140      * @param conn The connection to apply the header(s) to
141      * @param p A source of header values for this connection, not used because
142      *          HeaderParser converts the fields to lower case, use raw instead
143      * @param raw The raw header field.
144      * @return true if all goes well, false if no headers were set.
145      */
146     @Override
setHeaders(HttpURLConnection conn, HeaderParser p, String raw)147     public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
148 
149         try {
150             String response;
151             byte[] incoming = null;
152             String[] parts = raw.split("\\s+");
153             if (parts.length > 1) {
154                 incoming = new BASE64Decoder().decodeBuffer(parts[1]);
155             }
156             response = hci.scheme + " " + new B64Encoder().encode(
157                         incoming==null?firstToken():nextToken(incoming));
158 
159             conn.setAuthenticationProperty(getHeaderName(), response);
160             return true;
161         } catch (IOException e) {
162             return false;
163         }
164     }
165 
166     /**
167      * return the first token.
168      * @returns the token
169      * @throws IOException if <code>Negotiator.getNegotiator()</code> or
170      *                     <code>Negotiator.firstToken()</code> failed.
171      */
firstToken()172     private byte[] firstToken() throws IOException {
173         negotiator = null;
174         if (cache != null) {
175             synchronized(cache) {
176                 negotiator = cache.get(getHost());
177                 if (negotiator != null) {
178                     cache.remove(getHost()); // so that it is only used once
179                 }
180             }
181         }
182         if (negotiator == null) {
183             negotiator = Negotiator.getNegotiator(hci);
184             if (negotiator == null) {
185                 IOException ioe = new IOException("Cannot initialize Negotiator");
186                 throw ioe;
187             }
188         }
189 
190         return negotiator.firstToken();
191     }
192 
193     /**
194      * return more tokens
195      * @param token the token to be fed into <code>negotiator.nextToken()</code>
196      * @returns the token
197      * @throws IOException if <code>negotiator.nextToken()</code> throws Exception.
198      *  May happen if the input token is invalid.
199      */
nextToken(byte[] token)200     private byte[] nextToken(byte[] token) throws IOException {
201         return negotiator.nextToken(token);
202     }
203 
204     class B64Encoder extends BASE64Encoder {
bytesPerLine()205         protected int bytesPerLine () {
206             return 100000;  // as big as it can be, maybe INT_MAX
207         }
208     }
209 
210     // MS will send a final WWW-Authenticate even if the status is already
211     // 200 OK. The token can be fed into initSecContext() again to determine
212     // if the server can be trusted. This is not the same concept as Digest's
213     // Authentication-Info header.
214     //
215     // Currently we ignore this header.
216 
217 }
218