jdk/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
changeset 25859 3317bb8137f4
parent 24135 f273b57ed7e1
child 32227 34721a47bc92
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 2005, 2014, 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.Base64;
       
    32 import java.util.HashMap;
       
    33 import sun.net.www.HeaderParser;
       
    34 import sun.util.logging.PlatformLogger;
       
    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     private static final PlatformLogger logger = HttpURLConnection.getHttpLogger();
       
    49 
       
    50     final private HttpCallerInfo hci;
       
    51 
       
    52     // These maps are used to manage the GSS availability for diffrent
       
    53     // hosts. The key for both maps is the host name.
       
    54     // <code>supported</code> is set when isSupported is checked,
       
    55     // if it's true, a cached Negotiator is put into <code>cache</code>.
       
    56     // the cache can be used only once, so after the first use, it's cleaned.
       
    57     static HashMap <String, Boolean> supported = null;
       
    58     static HashMap <String, Negotiator> cache = null;
       
    59 
       
    60     // The HTTP Negotiate Helper
       
    61     private Negotiator negotiator = null;
       
    62 
       
    63    /**
       
    64     * Constructor used for both WWW and proxy entries.
       
    65     * @param hci a schemed object.
       
    66     */
       
    67     public NegotiateAuthentication(HttpCallerInfo hci) {
       
    68         super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
       
    69               hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS,
       
    70               hci.url,
       
    71               "");
       
    72         this.hci = hci;
       
    73     }
       
    74 
       
    75     /**
       
    76      * @return true if this authentication supports preemptive authorization
       
    77      */
       
    78     @Override
       
    79     public boolean supportsPreemptiveAuthorization() {
       
    80         return false;
       
    81     }
       
    82 
       
    83     /**
       
    84      * Find out if the HttpCallerInfo supports Negotiate protocol.
       
    85      * @return true if supported
       
    86      */
       
    87     public static boolean isSupported(HttpCallerInfo hci) {
       
    88         ClassLoader loader = null;
       
    89         try {
       
    90             loader = Thread.currentThread().getContextClassLoader();
       
    91         } catch (SecurityException se) {
       
    92             if (logger.isLoggable(PlatformLogger.Level.FINER)) {
       
    93                 logger.finer("NegotiateAuthentication: " +
       
    94                     "Attempt to get the context class loader failed - " + se);
       
    95             }
       
    96         }
       
    97 
       
    98         if (loader != null) {
       
    99             // Lock on the class loader instance to avoid the deadlock engaging
       
   100             // the lock in "ClassLoader.loadClass(String, boolean)" method.
       
   101             synchronized (loader) {
       
   102                 return isSupportedImpl(hci);
       
   103             }
       
   104         }
       
   105         return isSupportedImpl(hci);
       
   106     }
       
   107 
       
   108     /**
       
   109      * Find out if the HttpCallerInfo supports Negotiate protocol. In order to
       
   110      * find out yes or no, an initialization of a Negotiator object against it
       
   111      * is tried. The generated object will be cached under the name of ths
       
   112      * hostname at a success try.<br>
       
   113      *
       
   114      * If this method is called for the second time on an HttpCallerInfo with
       
   115      * the same hostname, the answer is retrieved from cache.
       
   116      *
       
   117      * @return true if supported
       
   118      */
       
   119     private static synchronized boolean isSupportedImpl(HttpCallerInfo hci) {
       
   120         if (supported == null) {
       
   121             supported = new HashMap <String, Boolean>();
       
   122             cache = new HashMap <String, Negotiator>();
       
   123         }
       
   124         String hostname = hci.host;
       
   125         hostname = hostname.toLowerCase();
       
   126         if (supported.containsKey(hostname)) {
       
   127             return supported.get(hostname);
       
   128         }
       
   129 
       
   130         Negotiator neg = Negotiator.getNegotiator(hci);
       
   131         if (neg != null) {
       
   132             supported.put(hostname, true);
       
   133             // the only place cache.put is called. here we can make sure
       
   134             // the object is valid and the oneToken inside is not null
       
   135             cache.put(hostname, neg);
       
   136             return true;
       
   137         } else {
       
   138             supported.put(hostname, false);
       
   139             return false;
       
   140         }
       
   141     }
       
   142 
       
   143     /**
       
   144      * Not supported. Must use the setHeaders() method
       
   145      */
       
   146     @Override
       
   147     public String getHeaderValue(URL url, String method) {
       
   148         throw new RuntimeException ("getHeaderValue not supported");
       
   149     }
       
   150 
       
   151     /**
       
   152      * Check if the header indicates that the current auth. parameters are stale.
       
   153      * If so, then replace the relevant field with the new value
       
   154      * and return true. Otherwise return false.
       
   155      * returning true means the request can be retried with the same userid/password
       
   156      * returning false means we have to go back to the user to ask for a new
       
   157      * username password.
       
   158      */
       
   159     @Override
       
   160     public boolean isAuthorizationStale (String header) {
       
   161         return false; /* should not be called for Negotiate */
       
   162     }
       
   163 
       
   164     /**
       
   165      * Set header(s) on the given connection.
       
   166      * @param conn The connection to apply the header(s) to
       
   167      * @param p A source of header values for this connection, not used because
       
   168      *          HeaderParser converts the fields to lower case, use raw instead
       
   169      * @param raw The raw header field.
       
   170      * @return true if all goes well, false if no headers were set.
       
   171      */
       
   172     @Override
       
   173     public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
       
   174 
       
   175         try {
       
   176             String response;
       
   177             byte[] incoming = null;
       
   178             String[] parts = raw.split("\\s+");
       
   179             if (parts.length > 1) {
       
   180                 incoming = Base64.getDecoder().decode(parts[1]);
       
   181             }
       
   182             response = hci.scheme + " " + Base64.getEncoder().encodeToString(
       
   183                         incoming==null?firstToken():nextToken(incoming));
       
   184 
       
   185             conn.setAuthenticationProperty(getHeaderName(), response);
       
   186             return true;
       
   187         } catch (IOException e) {
       
   188             return false;
       
   189         }
       
   190     }
       
   191 
       
   192     /**
       
   193      * return the first token.
       
   194      * @returns the token
       
   195      * @throws IOException if <code>Negotiator.getNegotiator()</code> or
       
   196      *                     <code>Negotiator.firstToken()</code> failed.
       
   197      */
       
   198     private byte[] firstToken() throws IOException {
       
   199         negotiator = null;
       
   200         if (cache != null) {
       
   201             synchronized(cache) {
       
   202                 negotiator = cache.get(getHost());
       
   203                 if (negotiator != null) {
       
   204                     cache.remove(getHost()); // so that it is only used once
       
   205                 }
       
   206             }
       
   207         }
       
   208         if (negotiator == null) {
       
   209             negotiator = Negotiator.getNegotiator(hci);
       
   210             if (negotiator == null) {
       
   211                 IOException ioe = new IOException("Cannot initialize Negotiator");
       
   212                 throw ioe;
       
   213             }
       
   214         }
       
   215 
       
   216         return negotiator.firstToken();
       
   217     }
       
   218 
       
   219     /**
       
   220      * return more tokens
       
   221      * @param token the token to be fed into <code>negotiator.nextToken()</code>
       
   222      * @returns the token
       
   223      * @throws IOException if <code>negotiator.nextToken()</code> throws Exception.
       
   224      *  May happen if the input token is invalid.
       
   225      */
       
   226     private byte[] nextToken(byte[] token) throws IOException {
       
   227         return negotiator.nextToken(token);
       
   228     }
       
   229 
       
   230     // MS will send a final WWW-Authenticate even if the status is already
       
   231     // 200 OK. The token can be fed into initSecContext() again to determine
       
   232     // if the server can be trusted. This is not the same concept as Digest's
       
   233     // Authentication-Info header.
       
   234     //
       
   235     // Currently we ignore this header.
       
   236 
       
   237 }