| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.net.www.protocol.http;  | 
 | 
 | 
 | 
import java.net.URL;  | 
 | 
import java.io.IOException;  | 
 | 
import java.net.Authenticator.RequestorType;  | 
 | 
import java.util.Base64;  | 
 | 
import java.util.HashMap;  | 
 | 
import sun.net.www.HeaderParser;  | 
 | 
import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;  | 
 | 
import static sun.net.www.protocol.http.AuthScheme.KERBEROS;  | 
 | 
import sun.security.action.GetPropertyAction;  | 
 | 
 | 
 | 
/**  | 
 | 
 * NegotiateAuthentication:  | 
 | 
 *  | 
 | 
 * @author weijun.wang@sun.com  | 
 | 
 * @since 1.6  | 
 | 
 */  | 
 | 
 | 
 | 
class NegotiateAuthentication extends AuthenticationInfo { | 
 | 
 | 
 | 
    private static final long serialVersionUID = 100L;  | 
 | 
 | 
 | 
    final private HttpCallerInfo hci;  | 
 | 
 | 
 | 
    // These maps are used to manage the GSS availability for diffrent  | 
 | 
    // hosts. The key for both maps is the host name.  | 
 | 
    // <code>supported</code> is set when isSupported is checked,  | 
 | 
 | 
 | 
    // if it's true, a cached Negotiator is put into <code>cache</code>.  | 
 | 
      | 
 | 
    static HashMap <String, Boolean> supported = null;  | 
 | 
    static ThreadLocal <HashMap <String, Negotiator>> cache = null;  | 
 | 
      | 
 | 
    private static final boolean cacheSPNEGO;  | 
 | 
    static { | 
 | 
        String spnegoCacheProp = java.security.AccessController.doPrivileged(  | 
 | 
            new sun.security.action.GetPropertyAction("jdk.spnego.cache", "true")); | 
 | 
        cacheSPNEGO = Boolean.parseBoolean(spnegoCacheProp);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private Negotiator negotiator = null;  | 
 | 
 | 
 | 
     | 
 | 
 | 
 | 
 | 
 | 
    */  | 
 | 
    public NegotiateAuthentication(HttpCallerInfo hci) { | 
 | 
        super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,  | 
 | 
              hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS, | 
 | 
              hci.url,  | 
 | 
              "");  | 
 | 
        this.hci = hci;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public boolean supportsPreemptiveAuthorization() { | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    synchronized public static boolean isSupported(HttpCallerInfo hci) { | 
 | 
        if (supported == null) { | 
 | 
            supported = new HashMap<>();  | 
 | 
        }  | 
 | 
        String hostname = hci.host;  | 
 | 
        hostname = hostname.toLowerCase();  | 
 | 
        if (supported.containsKey(hostname)) { | 
 | 
            return supported.get(hostname);  | 
 | 
        }  | 
 | 
 | 
 | 
        Negotiator neg = Negotiator.getNegotiator(hci);  | 
 | 
        if (neg != null) { | 
 | 
            supported.put(hostname, true);  | 
 | 
            // the only place cache.put is called. here we can make sure  | 
 | 
              | 
 | 
            if (cache == null) { | 
 | 
                cache = new ThreadLocal<HashMap<String, Negotiator>>() { | 
 | 
                    @Override  | 
 | 
                    protected HashMap<String, Negotiator> initialValue() { | 
 | 
                        return new HashMap<>();  | 
 | 
                    }  | 
 | 
                };  | 
 | 
            }  | 
 | 
            cache.get().put(hostname, neg);  | 
 | 
            return true;  | 
 | 
        } else { | 
 | 
            supported.put(hostname, false);  | 
 | 
            return false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static synchronized HashMap<String, Negotiator> getCache() { | 
 | 
        if (cache == null) return null;  | 
 | 
        return cache.get();  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    protected boolean useAuthCache() { | 
 | 
        return super.useAuthCache() && cacheSPNEGO;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public String getHeaderValue(URL url, String method) { | 
 | 
        throw new RuntimeException ("getHeaderValue not supported"); | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public boolean isAuthorizationStale (String header) { | 
 | 
        return false; /* should not be called for Negotiate */  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { | 
 | 
 | 
 | 
        try { | 
 | 
            String response;  | 
 | 
            byte[] incoming = null;  | 
 | 
            String[] parts = raw.split("\\s+"); | 
 | 
            if (parts.length > 1) { | 
 | 
                incoming = Base64.getDecoder().decode(parts[1]);  | 
 | 
            }  | 
 | 
            response = hci.scheme + " " + Base64.getEncoder().encodeToString(  | 
 | 
                        incoming==null?firstToken():nextToken(incoming));  | 
 | 
 | 
 | 
            conn.setAuthenticationProperty(getHeaderName(), response);  | 
 | 
            return true;  | 
 | 
        } catch (IOException e) { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private byte[] firstToken() throws IOException { | 
 | 
        negotiator = null;  | 
 | 
        HashMap <String, Negotiator> cachedMap = getCache();  | 
 | 
        if (cachedMap != null) { | 
 | 
            negotiator = cachedMap.get(getHost());  | 
 | 
            if (negotiator != null) { | 
 | 
                cachedMap.remove(getHost());   | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (negotiator == null) { | 
 | 
            negotiator = Negotiator.getNegotiator(hci);  | 
 | 
            if (negotiator == null) { | 
 | 
                IOException ioe = new IOException("Cannot initialize Negotiator"); | 
 | 
                throw ioe;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        return negotiator.firstToken();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private byte[] nextToken(byte[] token) throws IOException { | 
 | 
        return negotiator.nextToken(token);  | 
 | 
    }  | 
 | 
 | 
 | 
    // MS will send a final WWW-Authenticate even if the status is already  | 
 | 
    // 200 OK. The token can be fed into initSecContext() again to determine  | 
 | 
    // if the server can be trusted. This is not the same concept as Digest's  | 
 | 
    // Authentication-Info header.  | 
 | 
    //  | 
 | 
    // Currently we ignore this header.  | 
 | 
 | 
 | 
}  |