6578647: Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication()
Reviewed-by: chegar, valeriep
diff --git a/src/share/classes/sun/net/www/protocol/http/AuthenticationHeader.java b/src/share/classes/sun/net/www/protocol/http/AuthenticationHeader.java
index 8eb05bf..6826244 100644
--- a/src/share/classes/sun/net/www/protocol/http/AuthenticationHeader.java
+++ b/src/share/classes/sun/net/www/protocol/http/AuthenticationHeader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2002-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -81,8 +81,7 @@
     MessageHeader rsp; // the response to be parsed
     HeaderParser preferred;
     String preferred_r; // raw Strings
-    String host = null; // the hostname for server,
-                        // used in checking the availability of Negotiate
+    private final HttpCallerInfo hci;   // un-schemed, need check
 
     // When set true, do not use Negotiate even if the response
     // headers suggest so.
@@ -115,22 +114,11 @@
 
     /**
      * parse a set of authentication headers and choose the preferred scheme
-     * that we support
-     */
-    public AuthenticationHeader (String hdrname, MessageHeader response) {
-        rsp = response;
-        this.hdrname = hdrname;
-        schemes = new HashMap();
-        parse();
-    }
-
-    /**
-     * parse a set of authentication headers and choose the preferred scheme
      * that we support for a given host
      */
     public AuthenticationHeader (String hdrname, MessageHeader response,
-            String host, boolean dontUseNegotiate) {
-        this.host = host;
+            HttpCallerInfo hci, boolean dontUseNegotiate) {
+        this.hci = hci;
         this.dontUseNegotiate = dontUseNegotiate;
         rsp = response;
         this.hdrname = hdrname;
@@ -138,6 +126,9 @@
         parse();
     }
 
+    public HttpCallerInfo getHttpCallerInfo() {
+        return hci;
+    }
     /* we build up a map of scheme names mapped to SchemeMapValue objects */
     static class SchemeMapValue {
         SchemeMapValue (HeaderParser h, String r) {raw=r; parser=h;}
@@ -186,7 +177,7 @@
             if(v == null && !dontUseNegotiate) {
                 SchemeMapValue tmp = (SchemeMapValue)schemes.get("negotiate");
                 if(tmp != null) {
-                    if(host == null || !NegotiateAuthentication.isSupported(host, "Negotiate")) {
+                    if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Negotiate"))) {
                         tmp = null;
                     }
                     v = tmp;
@@ -206,7 +197,7 @@
                     //
                     // The only chance this line get executed is that the server
                     // only suggest the Kerberos scheme.
-                    if(host == null || !NegotiateAuthentication.isSupported(host, "Kerberos")) {
+                    if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Kerberos"))) {
                         tmp = null;
                     }
                     v = tmp;
diff --git a/src/share/classes/sun/net/www/protocol/http/HttpCallerInfo.java b/src/share/classes/sun/net/www/protocol/http/HttpCallerInfo.java
new file mode 100644
index 0000000..3aeba81
--- /dev/null
+++ b/src/share/classes/sun/net/www/protocol/http/HttpCallerInfo.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.net.www.protocol.http;
+
+import java.net.Authenticator.RequestorType;
+import java.net.InetAddress;
+import java.net.URL;
+
+/**
+ * Used in HTTP/Negotiate, to feed HTTP request info into JGSS as a HttpCaller,
+ * so that special actions can be taken, including special callback handler,
+ * special useSubjectCredsOnly value.
+ *
+ * This is an immutable class. It can be instantiated in two styles;
+ *
+ * 1. Un-schemed: Create at the beginning before the preferred scheme is
+ * determined. This object can be fed into AuthenticationHeader to check
+ * for the preference.
+ *
+ * 2. Schemed: With the scheme field filled, can be used in JGSS-API calls.
+ */
+final public class HttpCallerInfo {
+    // All info that an Authenticator needs.
+    final public URL url;
+    final public String host, protocol, prompt, scheme;
+    final public int port;
+    final public InetAddress addr;
+    final public RequestorType authType;
+
+    /**
+     * Create a schemed object based on an un-schemed one.
+     */
+    public HttpCallerInfo(HttpCallerInfo old, String scheme) {
+        this.url = old.url;
+        this.host = old.host;
+        this.protocol = old.protocol;
+        this.prompt = old.prompt;
+        this.port = old.port;
+        this.addr = old.addr;
+        this.authType = old.authType;
+        this.scheme = scheme;
+    }
+
+    /**
+     * Constructor an un-schemed object for site access.
+     */
+    public HttpCallerInfo(URL url) {
+        this.url= url;
+        prompt = "";
+        host = url.getHost();
+
+        int p = url.getPort();
+        if (p == -1) {
+            port = url.getDefaultPort();
+        } else {
+            port = p;
+        }
+
+        InetAddress ia;
+        try {
+            ia = InetAddress.getByName(url.getHost());
+        } catch (Exception e) {
+            ia = null;
+        }
+        addr = ia;
+
+        protocol = url.getProtocol();
+        authType = RequestorType.SERVER;
+        scheme = "";
+    }
+
+    /**
+     * Constructor an un-schemed object for proxy access.
+     */
+    public HttpCallerInfo(URL url, String host, int port) {
+        this.url= url;
+        this.host = host;
+        this.port = port;
+        prompt = "";
+        addr = null;
+        protocol = url.getProtocol();
+        authType = RequestorType.PROXY;
+        scheme = "";
+    }
+}
diff --git a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
index 770ffba..aacab5f 100644
--- a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
+++ b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1995-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1165,7 +1165,9 @@
 
                     AuthenticationHeader authhdr = new AuthenticationHeader (
                             "Proxy-Authenticate", responses,
-                            http.getProxyHostUsed(), dontUseNegotiate
+                            new HttpCallerInfo(url, http.getProxyHostUsed(),
+                                http.getProxyPortUsed()),
+                            dontUseNegotiate
                     );
 
                     if (!doingNTLMp2ndStage) {
@@ -1230,7 +1232,8 @@
 
                     srvHdr = new AuthenticationHeader (
                             "WWW-Authenticate", responses,
-                            url.getHost().toLowerCase(), dontUseNegotiate
+                            new HttpCallerInfo(url),
+                            dontUseNegotiate
                     );
 
                     String raw = srvHdr.raw();
@@ -1595,7 +1598,9 @@
 
                     AuthenticationHeader authhdr = new AuthenticationHeader (
                             "Proxy-Authenticate", responses,
-                            http.getProxyHostUsed(), dontUseNegotiate
+                            new HttpCallerInfo(url, http.getProxyHostUsed(),
+                                http.getProxyPortUsed()),
+                            dontUseNegotiate
                     );
                     if (!doingNTLMp2ndStage) {
                         proxyAuthentication =
@@ -1811,9 +1816,9 @@
 
                     tryTransparentNTLMProxy = false;
                 } else if (schemeID == NegotiateAuthentication.NEGOTIATE_AUTH) {
-                    ret = new NegotiateAuthentication(true, host, port, null, "Negotiate");
+                    ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
                 } else if (schemeID == NegotiateAuthentication.KERBEROS_AUTH) {
-                    ret = new NegotiateAuthentication(true, host, port, null, "Kerberos");
+                    ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
                 }
             }
             // For backwards compatibility, we also try defaultAuth
@@ -1897,7 +1902,7 @@
                     } catch (Exception e) {
                         url1 = url;
                     }
-                    ret = new NegotiateAuthentication(false, url1, null, "Kerberos");
+                    ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
                 }
                 if (schemeID == NegotiateAuthentication.NEGOTIATE_AUTH) {
                     URL url1;
@@ -1906,7 +1911,7 @@
                     } catch (Exception e) {
                         url1 = url;
                     }
-                    ret = new NegotiateAuthentication(false, url1, null, "Negotiate");
+                    ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
                 }
                 if (schemeID == BasicAuthentication.BASIC_AUTH) {
                     PasswordAuthentication a =
diff --git a/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java b/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
index 81353fc..db5d509 100644
--- a/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
+++ b/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,17 +25,15 @@
 
 package sun.net.www.protocol.http;
 
-import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Map;
 
 import sun.net.www.HeaderParser;
 import sun.misc.BASE64Decoder;
 import sun.misc.BASE64Encoder;
 
 import java.net.URL;
-import java.net.PasswordAuthentication;
 import java.io.IOException;
+import java.net.Authenticator.RequestorType;
 
 
 /**
@@ -49,7 +47,7 @@
 
     private static final long serialVersionUID = 100L;
 
-    private String scheme = null;
+    final private HttpCallerInfo hci;
 
     static final char NEGOTIATE_AUTH = 'S';
     static final char KERBEROS_AUTH = 'K';
@@ -66,25 +64,16 @@
     private Negotiator negotiator = null;
 
    /**
-    * Constructor used for WWW entries. <code>pw</code> is not used because
-    * for GSS there is only one single PasswordAuthentication which is
-    * independant of host/port/... info.
+    * Constructor used for both WWW and proxy entries.
+    * @param hci a schemed object.
     */
-    public NegotiateAuthentication(boolean isProxy, URL url,
-            PasswordAuthentication pw, String scheme) {
-        super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
-                NEGOTIATE_AUTH, url, "");
-        this.scheme = scheme;
-    }
-
-   /**
-    * Constructor used for proxy entries
-    */
-    public NegotiateAuthentication(boolean isProxy, String host, int port,
-                                PasswordAuthentication pw, String scheme) {
-        super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
-                NEGOTIATE_AUTH,host, port, "");
-        this.scheme = scheme;
+    public NegotiateAuthentication(HttpCallerInfo hci) {
+        super(RequestorType.PROXY==hci.authType?
+                    PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
+                hci.scheme.equalsIgnoreCase("Negotiate")?
+                    NEGOTIATE_AUTH:KERBEROS_AUTH,
+                hci.url, "");
+        this.hci = hci;
     }
 
     /**
@@ -95,32 +84,29 @@
     }
 
     /**
-     * Find out if a hostname supports Negotiate protocol. In order to find
-     * out yes or no, an initialization of a Negotiator object against
-     * hostname and scheme is tried. The generated object will be cached
-     * under the name of hostname at a success try.<br>
+     * Find out if the HttpCallerInfo supports Negotiate protocol. In order to
+     * find out yes or no, an initialization of a Negotiator object against it
+     * is tried. The generated object will be cached under the name of ths
+     * hostname at a success try.<br>
      *
-     * If this method is called for the second time on a hostname, the answer is
-     * already saved in <code>supported</code>, so no need to try again.
+     * If this method is called for the second time on an HttpCallerInfo with
+     * the same hostname, the answer is retrieved from cache.
      *
-     * @param hostname hostname to test
-     * @param scheme scheme to test
      * @return true if supported
      */
-    synchronized public static boolean isSupported(String hostname,
-            String scheme) {
+    synchronized public static boolean isSupported(HttpCallerInfo hci) {
         if (supported == null) {
             supported = new HashMap <String, Boolean>();
             cache = new HashMap <String, Negotiator>();
         }
-
+        String hostname = hci.host;
         hostname = hostname.toLowerCase();
         if (supported.containsKey(hostname)) {
             return supported.get(hostname);
         }
 
         try {
-            Negotiator neg = Negotiator.getSupported(hostname, scheme);
+            Negotiator neg = Negotiator.getSupported(hci);
             supported.put(hostname, true);
             // the only place cache.put is called. here we can make sure
             // the object is valid and the oneToken inside is not null
@@ -179,7 +165,7 @@
             if (parts.length > 1) {
                 incoming = new BASE64Decoder().decodeBuffer(parts[1]);
             }
-            response = scheme + " " + new B64Encoder().encode(
+            response = hci.scheme + " " + new B64Encoder().encode(
                         incoming==null?firstToken():nextToken(incoming));
 
             conn.setAuthenticationProperty(getHeaderName(), response);
@@ -207,7 +193,7 @@
         }
         if (negotiator == null) {
             try {
-                negotiator = Negotiator.getSupported(getHost(), scheme);
+                negotiator = Negotiator.getSupported(hci);
             } catch(Exception e) {
                 IOException ioe = new IOException("Cannot initialize Negotiator");
                 ioe.initCause(e);
@@ -255,18 +241,18 @@
  * NegotiatorImpl, so that JAAS and JGSS calls can be made
  */
 abstract class Negotiator {
-    static Negotiator getSupported(String hostname, String scheme)
+    static Negotiator getSupported(HttpCallerInfo hci)
                 throws Exception {
 
         // These lines are equivalent to
-        //     return new NegotiatorImpl(hostname, scheme);
+        //     return new NegotiatorImpl(hci);
         // The current implementation will make sure NegotiatorImpl is not
         // directly referenced when compiling, thus smooth the way of building
         // the J2SE platform where HttpURLConnection is a bootstrap class.
 
         Class clazz = Class.forName("sun.net.www.protocol.http.NegotiatorImpl");
-        java.lang.reflect.Constructor c = clazz.getConstructor(String.class, String.class);
-        return (Negotiator) (c.newInstance(hostname, scheme));
+        java.lang.reflect.Constructor c = clazz.getConstructor(HttpCallerInfo.class);
+        return (Negotiator) (c.newInstance(hci));
     }
 
     abstract byte[] firstToken() throws IOException;
diff --git a/src/share/classes/sun/net/www/protocol/http/NegotiateCallbackHandler.java b/src/share/classes/sun/net/www/protocol/http/NegotiateCallbackHandler.java
index 8ab6eb6..7ac2593 100644
--- a/src/share/classes/sun/net/www/protocol/http/NegotiateCallbackHandler.java
+++ b/src/share/classes/sun/net/www/protocol/http/NegotiateCallbackHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -36,12 +36,19 @@
 
 /**
  * @since 1.6
+ * Special callback handler used in JGSS for the HttpCaller.
  */
 public class NegotiateCallbackHandler implements CallbackHandler {
 
     private String username;
     private char[] password;
 
+    private final HttpCallerInfo hci;
+
+    public NegotiateCallbackHandler(HttpCallerInfo hci) {
+        this.hci = hci;
+    }
+
     public void handle(Callback[] callbacks) throws
             UnsupportedCallbackException, IOException {
         for (int i=0; i<callbacks.length; i++) {
@@ -51,8 +58,8 @@
                 if (username == null) {
                     PasswordAuthentication passAuth =
                             Authenticator.requestPasswordAuthentication(
-                            null, null, 0, null,
-                            null, "Negotiate");
+                            hci.host, hci.addr, hci.port, hci.protocol,
+                            hci.prompt, hci.scheme, hci.url, hci.authType);
                     username = passAuth.getUserName();
                     password = passAuth.getPassword();
                 }
@@ -66,8 +73,8 @@
                 if (password == null) {
                     PasswordAuthentication passAuth =
                             Authenticator.requestPasswordAuthentication(
-                            null, null, 0, null,
-                            null, "Negotiate");
+                            hci.host, hci.addr, hci.port, hci.protocol,
+                            hci.prompt, hci.scheme, hci.url, hci.authType);
                     username = passAuth.getUserName();
                     password = passAuth.getPassword();
                 }
@@ -76,7 +83,7 @@
             } else {
                 throw new UnsupportedCallbackException(callBack,
                         "Call back not supported");
-            }//else
-        }//for
+            }
+        }
     }
 }
diff --git a/src/share/classes/sun/net/www/protocol/http/NegotiatorImpl.java b/src/share/classes/sun/net/www/protocol/http/NegotiatorImpl.java
index 6252517..2c195e4 100644
--- a/src/share/classes/sun/net/www/protocol/http/NegotiatorImpl.java
+++ b/src/share/classes/sun/net/www/protocol/http/NegotiatorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,9 +34,10 @@
 
 import sun.security.jgss.GSSManagerImpl;
 import sun.security.jgss.GSSUtil;
+import sun.security.jgss.HttpCaller;
 
 /**
- * This class encapsulates all JAAS and JGSS API calls in a seperate class
+ * This class encapsulates all JAAS and JGSS API calls in a separate class
  * outside NegotiateAuthentication.java so that J2SE build can go smoothly
  * without the presence of it.
  *
@@ -54,21 +55,16 @@
 
     /**
      * Initialize the object, which includes:<ul>
-     * <li>Find out what GSS mechanism to use from <code>http.negotiate.mechanism.oid</code>,
-     *     defaults SPNEGO
+     * <li>Find out what GSS mechanism to use from the system property
+     * <code>http.negotiate.mechanism.oid</code>, defaults SPNEGO
      * <li>Creating the GSSName for the target host, "HTTP/"+hostname
      * <li>Creating GSSContext
      * <li>A first call to initSecContext</ul>
-     * @param hostname name of peer server
-     * @param scheme auth scheme requested, Negotiate ot Kerberos
-     * @throws GSSException if any JGSS-API call fails
      */
-    private void init(final String hostname, String scheme) throws GSSException {
-        // "1.2.840.113554.1.2.2" Kerberos
-        // "1.3.6.1.5.5.2" SPNEGO
+    private void init(HttpCallerInfo hci) throws GSSException {
         final Oid oid;
 
-        if (scheme.equalsIgnoreCase("Kerberos")) {
+        if (hci.scheme.equalsIgnoreCase("Kerberos")) {
             // we can only use Kerberos mech when the scheme is kerberos
             oid = GSSUtil.GSS_KRB5_MECH_OID;
         } else {
@@ -89,9 +85,11 @@
         }
 
         GSSManagerImpl manager = new GSSManagerImpl(
-                GSSUtil.CALLER_HTTP_NEGOTIATE);
+                new HttpCaller(hci));
 
-        String peerName = "HTTP@" + hostname;
+        // RFC 4559 4.1 uses uppercase service name "HTTP".
+        // RFC 4120 6.2.1 demands the host be lowercase
+        String peerName = "HTTP@" + hci.host.toLowerCase();
 
         GSSName serverName = manager.createName(peerName,
                 GSSName.NT_HOSTBASED_SERVICE);
@@ -114,16 +112,15 @@
 
     /**
      * Constructor
-     * @param hostname name of peer server
-     * @param scheme auth scheme requested, Negotiate ot Kerberos
      * @throws java.io.IOException If negotiator cannot be constructed
      */
-    public NegotiatorImpl(String hostname, String scheme) throws IOException {
+    public NegotiatorImpl(HttpCallerInfo hci) throws IOException {
         try {
-            init(hostname, scheme);
+            init(hci);
         } catch (GSSException e) {
             if (DEBUG) {
-                System.out.println("Negotiate support not initiated, will fallback to other scheme if allowed. Reason:");
+                System.out.println("Negotiate support not initiated, will " +
+                        "fallback to other scheme if allowed. Reason:");
                 e.printStackTrace();
             }
             IOException ioe = new IOException("Negotiate support not initiated");
diff --git a/src/share/classes/sun/security/jgss/GSSCaller.java b/src/share/classes/sun/security/jgss/GSSCaller.java
new file mode 100644
index 0000000..e38c348
--- /dev/null
+++ b/src/share/classes/sun/security/jgss/GSSCaller.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.security.jgss;
+
+/**
+ * Denotes what client is calling the JGSS-API. The object can be sent deep
+ * into the mechanism level so that special actions can be performed for
+ * different callers.
+ */
+public class GSSCaller {
+    public static final GSSCaller CALLER_UNKNOWN = new GSSCaller();
+    public static final GSSCaller CALLER_INITIATE = new GSSCaller();
+    public static final GSSCaller CALLER_ACCEPT = new GSSCaller();
+    public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller();
+    public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller();
+}
+
diff --git a/src/share/classes/sun/security/jgss/GSSManagerImpl.java b/src/share/classes/sun/security/jgss/GSSManagerImpl.java
index a886b87..eeb23fc 100644
--- a/src/share/classes/sun/security/jgss/GSSManagerImpl.java
+++ b/src/share/classes/sun/security/jgss/GSSManagerImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,8 +27,6 @@
 
 import org.ietf.jgss.*;
 import sun.security.jgss.spi.*;
-import java.io.*;
-import java.security.NoSuchProviderException;
 import java.security.Provider;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -63,17 +61,17 @@
     private ProviderList list;
 
     // Used by java SPNEGO impl to make sure native is disabled
-    public GSSManagerImpl(int caller, boolean useNative) {
+    public GSSManagerImpl(GSSCaller caller, boolean useNative) {
         list = new ProviderList(caller, useNative);
     }
 
     // Used by HTTP/SPNEGO NegotiatorImpl
-    public GSSManagerImpl(int caller) {
+    public GSSManagerImpl(GSSCaller caller) {
         list = new ProviderList(caller, USE_NATIVE);
     }
 
     public GSSManagerImpl() {
-        list = new ProviderList(GSSUtil.CALLER_UNKNOWN, USE_NATIVE);
+        list = new ProviderList(GSSCaller.CALLER_UNKNOWN, USE_NATIVE);
     }
 
     public Oid[] getMechs(){
diff --git a/src/share/classes/sun/security/jgss/GSSUtil.java b/src/share/classes/sun/security/jgss/GSSUtil.java
index 4c161b2..12a791d 100644
--- a/src/share/classes/sun/security/jgss/GSSUtil.java
+++ b/src/share/classes/sun/security/jgss/GSSUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -72,13 +72,6 @@
     private static final String DEFAULT_HANDLER =
             "auth.login.defaultCallbackHandler";
 
-    public static final int CALLER_UNKNOWN         = -1;
-    public static final int CALLER_INITIATE        = 1;
-    public static final int CALLER_ACCEPT          = 2;
-    public static final int CALLER_SSL_CLIENT      = 3;
-    public static final int CALLER_SSL_SERVER      = 4;
-    public static final int CALLER_HTTP_NEGOTIATE  = 5;
-
     static final boolean DEBUG;
     static {
         DEBUG = (AccessController.doPrivileged
@@ -240,11 +233,12 @@
      * @param mech the mech to be used
      * @return the authenticated subject
      */
-    public static Subject login(int caller, Oid mech) throws LoginException {
+    public static Subject login(GSSCaller caller, Oid mech) throws LoginException {
 
         CallbackHandler cb = null;
-        if (caller == GSSUtil.CALLER_HTTP_NEGOTIATE) {
-            cb = new sun.net.www.protocol.http.NegotiateCallbackHandler();
+        if (caller instanceof HttpCaller) {
+            cb = new sun.net.www.protocol.http.NegotiateCallbackHandler(
+                    ((HttpCaller)caller).info());
         } else {
             String defaultHandler =
                     java.security.Security.getProperty(DEFAULT_HANDLER);
@@ -274,12 +268,12 @@
      * The application indicates this by explicitly setting the system
      * property javax.security.auth.useSubjectCredsOnly to false.
      */
-    public static boolean useSubjectCredsOnly(int caller) {
+    public static boolean useSubjectCredsOnly(GSSCaller caller) {
 
         // HTTP/SPNEGO doesn't use the standard JAAS framework. Instead, it
         // uses the java.net.Authenticator style, therefore always return
         // false here.
-        if (caller == CALLER_HTTP_NEGOTIATE) {
+        if (caller instanceof HttpCaller) {
             return false;
         }
         /*
diff --git a/src/share/classes/sun/security/jgss/HttpCaller.java b/src/share/classes/sun/security/jgss/HttpCaller.java
new file mode 100644
index 0000000..4ec4398
--- /dev/null
+++ b/src/share/classes/sun/security/jgss/HttpCaller.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.security.jgss;
+
+import sun.net.www.protocol.http.HttpCallerInfo;
+
+/**
+ * A special kind of GSSCaller, which origins from HTTP/Negotiate and contains
+ * info about what triggers the JGSS calls.
+ */
+public class HttpCaller extends GSSCaller {
+    final private HttpCallerInfo hci;
+
+    public HttpCaller(HttpCallerInfo hci) {
+        this.hci = hci;
+    }
+
+    public HttpCallerInfo info() {
+        return hci;
+    }
+}
+
diff --git a/src/share/classes/sun/security/jgss/LoginConfigImpl.java b/src/share/classes/sun/security/jgss/LoginConfigImpl.java
index a421e51..7262965 100644
--- a/src/share/classes/sun/security/jgss/LoginConfigImpl.java
+++ b/src/share/classes/sun/security/jgss/LoginConfigImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -39,7 +39,7 @@
 public class LoginConfigImpl extends Configuration {
 
     private final Configuration config;
-    private final int caller;
+    private final GSSCaller caller;
     private final String mechName;
     private static final sun.security.util.Debug debug =
         sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]");
@@ -50,7 +50,7 @@
      * @param caller defined in GSSUtil as CALLER_XXX final fields
      * @param oid defined in GSSUtil as XXX_MECH_OID final fields
      */
-    public LoginConfigImpl(int caller, Oid mech) {
+    public LoginConfigImpl(GSSCaller caller, Oid mech) {
 
         this.caller = caller;
 
@@ -88,40 +88,31 @@
         // entry name is not provided.
 
         if ("krb5".equals(mechName)) {
-            switch (caller) {
-            case GSSUtil.CALLER_INITIATE:
+            if (caller == GSSCaller.CALLER_INITIATE) {
                 alts = new String[] {
                     "com.sun.security.jgss.krb5.initiate",
                     "com.sun.security.jgss.initiate",
                 };
-                break;
-            case GSSUtil.CALLER_ACCEPT:
+            } else if (caller == GSSCaller.CALLER_ACCEPT) {
                 alts = new String[] {
                     "com.sun.security.jgss.krb5.accept",
                     "com.sun.security.jgss.accept",
                 };
-                break;
-            case GSSUtil.CALLER_SSL_CLIENT:
+            } else if (caller == GSSCaller.CALLER_SSL_CLIENT) {
                 alts = new String[] {
                     "com.sun.security.jgss.krb5.initiate",
                     "com.sun.net.ssl.client",
                 };
-                break;
-            case GSSUtil.CALLER_SSL_SERVER:
+            } else if (caller == GSSCaller.CALLER_SSL_SERVER) {
                 alts = new String[] {
                     "com.sun.security.jgss.krb5.accept",
                     "com.sun.net.ssl.server",
                 };
-                break;
-            case GSSUtil.CALLER_HTTP_NEGOTIATE:
+            } else if (caller instanceof HttpCaller) {
                 alts = new String[] {
                     "com.sun.security.jgss.krb5.initiate",
                 };
-                break;
-            case GSSUtil.CALLER_UNKNOWN:
-                // should never use
-                throw new AssertionError("caller cannot be unknown");
-            default:
+            } else if (caller == GSSCaller.CALLER_UNKNOWN) {
                 throw new AssertionError("caller not defined");
             }
         } else {
@@ -199,8 +190,8 @@
         return null;
     }
 
-    private static boolean isServerSide (int caller) {
-        return GSSUtil.CALLER_ACCEPT == caller ||
-               GSSUtil.CALLER_SSL_SERVER == caller;
+    private static boolean isServerSide (GSSCaller caller) {
+        return GSSCaller.CALLER_ACCEPT == caller ||
+               GSSCaller.CALLER_SSL_SERVER == caller;
     }
 }
diff --git a/src/share/classes/sun/security/jgss/ProviderList.java b/src/share/classes/sun/security/jgss/ProviderList.java
index 8df718c..45e69b2 100644
--- a/src/share/classes/sun/security/jgss/ProviderList.java
+++ b/src/share/classes/sun/security/jgss/ProviderList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,8 +28,6 @@
 import java.lang.reflect.InvocationTargetException;
 import org.ietf.jgss.*;
 import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.PrivilegedAction;
 import java.security.Provider;
 import java.security.Security;
 import java.util.ArrayList;
@@ -37,7 +35,6 @@
 import java.util.HashMap;
 import java.util.Enumeration;
 import java.util.Iterator;
-import javax.security.auth.Subject;
 import sun.security.jgss.spi.*;
 import sun.security.jgss.wrapper.NativeGSSFactory;
 import sun.security.jgss.wrapper.SunNativeProvider;
@@ -124,9 +121,9 @@
                         new HashMap<PreferencesEntry, MechanismFactory>(5);
     private HashSet<Oid> mechs = new HashSet<Oid>(5);
 
-    final private int caller;
+    final private GSSCaller caller;
 
-    public ProviderList(int caller, boolean useNative) {
+    public ProviderList(GSSCaller caller, boolean useNative) {
         this.caller = caller;
         Provider[] provList;
         if (useNative) {
@@ -274,7 +271,7 @@
     private static MechanismFactory getMechFactoryImpl(Provider p,
                                                        String className,
                                                        Oid mechOid,
-                                                       int caller)
+                                                       GSSCaller caller)
         throws GSSException {
 
         try {
@@ -301,7 +298,7 @@
             if (baseClass.isAssignableFrom(implClass)) {
 
                 java.lang.reflect.Constructor<?> c =
-                                implClass.getConstructor(Integer.TYPE);
+                                implClass.getConstructor(GSSCaller.class);
                 MechanismFactory mf = (MechanismFactory) (c.newInstance(caller));
 
                 if (mf instanceof NativeGSSFactory) {
diff --git a/src/share/classes/sun/security/jgss/krb5/InitialToken.java b/src/share/classes/sun/security/jgss/krb5/InitialToken.java
index 4533813..7ce0b44 100644
--- a/src/share/classes/sun/security/jgss/krb5/InitialToken.java
+++ b/src/share/classes/sun/security/jgss/krb5/InitialToken.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,7 +34,7 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import sun.security.krb5.*;
-import sun.security.jgss.GSSUtil;
+import sun.security.jgss.HttpCaller;
 import sun.security.krb5.internal.Krb5;
 
 abstract class InitialToken extends Krb5Token {
@@ -85,7 +85,7 @@
                 CHECKSUM_FLAGS_SIZE;
 
             if (context.getCredDelegState()) {
-                if (context.getCaller() == GSSUtil.CALLER_HTTP_NEGOTIATE &&
+                if (context.getCaller() instanceof HttpCaller &&
                         !serviceTicket.getFlags()[Krb5.TKT_OPTS_DELEGATE]) {
                     // When the caller is HTTP/SPNEGO and OK-AS-DELEGATE
                     // is not present in the service ticket, delegation
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java b/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
index 3fb4835..ddbd48b 100644
--- a/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,10 @@
 package sun.security.jgss.krb5;
 
 import org.ietf.jgss.*;
-import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 import sun.security.jgss.spi.*;
 import sun.security.krb5.*;
 import javax.security.auth.kerberos.*;
-import java.io.IOException;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.security.AccessController;
@@ -80,7 +79,7 @@
         }
     }
 
-    static Krb5AcceptCredential getInstance(final int caller, Krb5NameElement name)
+    static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
         throws GSSException {
 
         final String serverPrinc = (name == null? null:
@@ -93,7 +92,7 @@
                         new PrivilegedExceptionAction<KerberosKey[]>() {
                 public KerberosKey[] run() throws Exception {
                     return Krb5Util.getKeys(
-                        caller == GSSUtil.CALLER_UNKNOWN ? GSSUtil.CALLER_ACCEPT: caller,
+                        caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
                         serverPrinc, acc);
                 }});
         } catch (PrivilegedActionException e) {
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java
index 9e92a6f..f2ef7d5 100644
--- a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
 import org.ietf.jgss.*;
 import sun.misc.HexDumpEncoder;
 import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 import sun.security.jgss.spi.*;
 import sun.security.jgss.TokenTracker;
 import sun.security.krb5.*;
@@ -37,8 +38,6 @@
 import java.security.Provider;
 import java.security.AccessController;
 import java.security.AccessControlContext;
-import java.security.GeneralSecurityException;
-import java.security.PrivilegedAction;
 import java.security.PrivilegedExceptionAction;
 import java.security.PrivilegedActionException;
 import javax.crypto.Cipher;
@@ -113,14 +112,14 @@
     // stored elsewhere
     private Credentials serviceCreds;
     private KrbApReq apReq;
-    final private int caller;
+    final private GSSCaller caller;
     private static final boolean DEBUG = Krb5Util.DEBUG;
 
     /**
      * Constructor for Krb5Context to be called on the context initiator's
      * side.
      */
-    Krb5Context(int caller, Krb5NameElement peerName, Krb5CredElement myCred,
+    Krb5Context(GSSCaller caller, Krb5NameElement peerName, Krb5CredElement myCred,
                 int lifetime)
         throws GSSException {
 
@@ -138,7 +137,7 @@
      * Constructor for Krb5Context to be called on the context acceptor's
      * side.
      */
-    Krb5Context(int caller, Krb5CredElement myCred)
+    Krb5Context(GSSCaller caller, Krb5CredElement myCred)
         throws GSSException {
         this.caller = caller;
         this.myCred = myCred;
@@ -148,7 +147,7 @@
     /**
      * Constructor for Krb5Context to import a previously exported context.
      */
-    public Krb5Context(int caller, byte [] interProcessToken)
+    public Krb5Context(GSSCaller caller, byte [] interProcessToken)
         throws GSSException {
         throw new GSSException(GSSException.UNAVAILABLE,
                                -1, "GSS Import Context not available");
@@ -573,7 +572,7 @@
                                     // SubjectComber.find
                                     // instead of Krb5Util.getTicket
                                     return Krb5Util.getTicket(
-                                        GSSUtil.CALLER_UNKNOWN,
+                                        GSSCaller.CALLER_UNKNOWN,
                                         // since it's useSubjectCredsOnly here,
                                         // don't worry about the null
                                         myName.getKrb5PrincipalName().getName(),
@@ -1280,7 +1279,7 @@
         }
     }
 
-    int getCaller() {
+    GSSCaller getCaller() {
         // Currently used by InitialToken only
         return caller;
     }
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java
index 8e9cd1b..1aac872 100644
--- a/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
 package sun.security.jgss.krb5;
 
 import org.ietf.jgss.*;
-import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 import sun.security.jgss.spi.*;
 import sun.security.krb5.*;
 import sun.security.krb5.Config;
@@ -138,7 +138,7 @@
         this.krb5Credentials = delegatedCred;
     }
 
-    static Krb5InitCredential getInstance(int caller, Krb5NameElement name,
+    static Krb5InitCredential getInstance(GSSCaller caller, Krb5NameElement name,
                                    int initLifetime)
         throws GSSException {
 
@@ -305,7 +305,7 @@
     // XXX call to this.destroy() should destroy the locally cached copy
     // of krb5Credentials and then call super.destroy().
 
-    private static KerberosTicket getTgt(int caller, Krb5NameElement name,
+    private static KerberosTicket getTgt(GSSCaller caller, Krb5NameElement name,
                                                  int initLifetime)
         throws GSSException {
 
@@ -337,8 +337,8 @@
         final AccessControlContext acc = AccessController.getContext();
 
         try {
-            final int realCaller = (caller == GSSUtil.CALLER_UNKNOWN)
-                                   ? GSSUtil.CALLER_INITIATE
+            final GSSCaller realCaller = (caller == GSSCaller.CALLER_UNKNOWN)
+                                   ? GSSCaller.CALLER_INITIATE
                                    : caller;
             return AccessController.doPrivileged(
                 new PrivilegedExceptionAction<KerberosTicket>() {
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java b/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java
index 3977777..5e32977 100644
--- a/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,12 +27,10 @@
 
 import org.ietf.jgss.*;
 import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 import sun.security.jgss.spi.*;
 import javax.security.auth.kerberos.ServicePermission;
 import java.security.Provider;
-import sun.security.util.DerOutputStream;
-import sun.security.util.ObjectIdentifier;
-import java.io.IOException;
 import java.util.Vector;
 
 /**
@@ -62,7 +60,7 @@
                         GSSName.NT_EXPORT_NAME,
                         NT_GSS_KRB5_PRINCIPAL};
 
-    final private int caller;
+    final private GSSCaller caller;
 
     private static Krb5CredElement getCredFromSubject(GSSNameSpi name,
                                                       boolean initiate)
@@ -88,7 +86,7 @@
         return result;
     }
 
-    public Krb5MechFactory(int caller) {
+    public Krb5MechFactory(GSSCaller caller) {
         this.caller = caller;
     }
 
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java
index 62b5795..849dbc4 100644
--- a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
 import javax.security.auth.login.LoginException;
 import java.security.AccessControlContext;
 import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 
 import sun.security.krb5.Credentials;
 import sun.security.krb5.EncryptionKey;
@@ -67,7 +68,7 @@
      *
      * NOTE: This method is used by JSSE Kerberos Cipher Suites
      */
-    public static KerberosTicket getTicketFromSubjectAndTgs(int caller,
+    public static KerberosTicket getTicketFromSubjectAndTgs(GSSCaller caller,
         String clientPrincipal, String serverPrincipal, String tgsPrincipal,
         AccessControlContext acc)
         throws LoginException, KrbException, IOException {
@@ -138,7 +139,7 @@
      * useSubjectCredsOnly is false, then obtain ticket from
      * a LoginContext.
      */
-    static KerberosTicket getTicket(int caller,
+    static KerberosTicket getTicket(GSSCaller caller,
         String clientPrincipal, String serverPrincipal,
         AccessControlContext acc) throws LoginException {
 
@@ -168,7 +169,7 @@
      *
      * NOTE: This method is used by JSSE Kerberos Cipher Suites
      */
-    public static Subject getSubject(int caller,
+    public static Subject getSubject(GSSCaller caller,
         AccessControlContext acc) throws LoginException {
 
         // Try to get the Subject from acc
@@ -190,7 +191,7 @@
      *
      * NOTE: This method is used by JSSE Kerberos Cipher Suites
      */
-    public static KerberosKey[] getKeys(int caller,
+    public static KerberosKey[] getKeys(GSSCaller caller,
         String serverPrincipal, AccessControlContext acc)
                 throws LoginException {
 
diff --git a/src/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java b/src/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java
index d782967..ae12d6b 100644
--- a/src/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java
+++ b/src/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -90,7 +90,7 @@
         return result;
     }
 
-    public SpNegoMechFactory(int caller) {
+    public SpNegoMechFactory(GSSCaller caller) {
         manager = new GSSManagerImpl(caller, false);
         Oid[] mechs = manager.getMechs();
         availableMechs = new Oid[mechs.length-1];
diff --git a/src/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java b/src/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java
index 315e560..7e2146c 100644
--- a/src/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java
+++ b/src/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,17 +26,11 @@
 package sun.security.jgss.wrapper;
 
 import java.io.UnsupportedEncodingException;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.PrivilegedAction;
 import java.security.Provider;
-import java.util.Set;
 import java.util.Vector;
-import java.util.Iterator;
-import javax.security.auth.Subject;
-import javax.security.auth.kerberos.*;
 import org.ietf.jgss.*;
 import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 import sun.security.jgss.GSSExceptionImpl;
 import sun.security.jgss.spi.*;
 
@@ -49,7 +43,7 @@
 public final class NativeGSSFactory implements MechanismFactory {
 
     GSSLibStub cStub = null;
-    private final int caller;
+    private final GSSCaller caller;
 
     private GSSCredElement getCredFromSubject(GSSNameElement name,
                                               boolean initiate)
@@ -74,7 +68,7 @@
         return result;
     }
 
-    public NativeGSSFactory(int caller) {
+    public NativeGSSFactory(GSSCaller caller) {
         this.caller = caller;
         // Have to call setMech(Oid) explicitly before calling other
         // methods. Otherwise, NPE may be thrown unexpectantly
diff --git a/src/share/classes/sun/security/ssl/ClientHandshaker.java b/src/share/classes/sun/security/ssl/ClientHandshaker.java
index f29397d..08877ba 100644
--- a/src/share/classes/sun/security/ssl/ClientHandshaker.java
+++ b/src/share/classes/sun/security/ssl/ClientHandshaker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -46,13 +46,12 @@
 import javax.security.auth.Subject;
 import javax.security.auth.kerberos.KerberosPrincipal;
 import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 
 import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
 
 import sun.security.ssl.HandshakeMessage.*;
 import sun.security.ssl.CipherSuite.*;
-import static sun.security.ssl.CipherSuite.*;
 import static sun.security.ssl.CipherSuite.KeyExchange.*;
 
 /**
@@ -364,7 +363,7 @@
                             new PrivilegedExceptionAction<Subject>() {
                             public Subject run() throws Exception {
                                 return Krb5Util.getSubject(
-                                    GSSUtil.CALLER_SSL_CLIENT,
+                                    GSSCaller.CALLER_SSL_CLIENT,
                                     getAccSE());
                             }});
                     } catch (PrivilegedActionException e) {
diff --git a/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java b/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java
index cb7543b..e79026e 100644
--- a/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java
+++ b/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,19 +34,16 @@
 import java.security.SecureRandom;
 import java.net.InetAddress;
 
-import javax.net.ssl.SSLException;
 import javax.security.auth.kerberos.KerberosTicket;
 import javax.security.auth.kerberos.KerberosKey;
 import javax.security.auth.kerberos.KerberosPrincipal;
 import javax.security.auth.kerberos.ServicePermission;
-import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 
-import sun.security.krb5.Config;
 import sun.security.krb5.EncryptionKey;
 import sun.security.krb5.EncryptedData;
 import sun.security.krb5.PrincipalName;
 import sun.security.krb5.Realm;
-import sun.security.krb5.KrbException;
 import sun.security.krb5.internal.Ticket;
 import sun.security.krb5.internal.EncTicketPart;
 import sun.security.krb5.internal.crypto.KeyUsage;
@@ -310,7 +307,7 @@
                 new PrivilegedExceptionAction<KerberosTicket>() {
                 public KerberosTicket run() throws Exception {
                     return Krb5Util.getTicketFromSubjectAndTgs(
-                        GSSUtil.CALLER_SSL_CLIENT,
+                        GSSCaller.CALLER_SSL_CLIENT,
                         clientPrincipal, serverPrincipal,
                         tgsPrincipal, acc);
                         }});
diff --git a/src/share/classes/sun/security/ssl/ServerHandshaker.java b/src/share/classes/sun/security/ssl/ServerHandshaker.java
index ab2ebd3..772cdb6 100644
--- a/src/share/classes/sun/security/ssl/ServerHandshaker.java
+++ b/src/share/classes/sun/security/ssl/ServerHandshaker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 1996-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,7 +43,7 @@
 import javax.security.auth.kerberos.KerberosPrincipal;
 import javax.security.auth.kerberos.ServicePermission;
 import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 
 import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
 
@@ -367,7 +367,7 @@
                                 new PrivilegedExceptionAction<Subject>() {
                                 public Subject run() throws Exception {
                                     return Krb5Util.getSubject(
-                                        GSSUtil.CALLER_SSL_SERVER,
+                                        GSSCaller.CALLER_SSL_SERVER,
                                         getAccSE());
                             }});
                         } catch (PrivilegedActionException e) {
@@ -918,7 +918,7 @@
                 public KerberosKey[] run() throws Exception {
                     // get kerberos key for the default principal
                     return Krb5Util.getKeys(
-                        GSSUtil.CALLER_SSL_SERVER, null, acc);
+                        GSSCaller.CALLER_SSL_SERVER, null, acc);
                         }});
 
             // check permission to access and use the secret key of the
diff --git a/test/sun/security/jgss/DefaultGssConfig.java b/test/sun/security/jgss/DefaultGssConfig.java
index 5959f8e..672be74 100644
--- a/test/sun/security/jgss/DefaultGssConfig.java
+++ b/test/sun/security/jgss/DefaultGssConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2006-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,7 @@
 import java.security.URIParameter;
 import javax.security.auth.login.Configuration;
 import sun.security.jgss.GSSUtil;
+import sun.security.jgss.GSSCaller;
 import sun.security.jgss.LoginConfigImpl;
 
 public class DefaultGssConfig {
@@ -56,11 +57,11 @@
         Configuration.getConfiguration();
 
         // 3. Make sure there're default entries for GSS krb5 client/server
-        LoginConfigImpl lc = new LoginConfigImpl(GSSUtil.CALLER_INITIATE, GSSUtil.GSS_KRB5_MECH_OID);
+        LoginConfigImpl lc = new LoginConfigImpl(GSSCaller.CALLER_INITIATE, GSSUtil.GSS_KRB5_MECH_OID);
         if (lc.getAppConfigurationEntry("").length == 0) {
             throw new Exception("No default config for GSS krb5 client");
         }
-        lc = new LoginConfigImpl(GSSUtil.CALLER_ACCEPT, GSSUtil.GSS_KRB5_MECH_OID);
+        lc = new LoginConfigImpl(GSSCaller.CALLER_ACCEPT, GSSUtil.GSS_KRB5_MECH_OID);
         if (lc.getAppConfigurationEntry("").length == 0) {
             throw new Exception("No default config for GSS krb5 server");
         }
diff --git a/test/sun/security/jgss/GssNPE.java b/test/sun/security/jgss/GssNPE.java
index 9d9b144..815e248 100644
--- a/test/sun/security/jgss/GssNPE.java
+++ b/test/sun/security/jgss/GssNPE.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,6 @@
  * @summary GSS throws NPE when the JAAS config file does not exist
  */
 
-import org.ietf.jgss.*;
 import sun.security.jgss.*;
 
 public class GssNPE {
@@ -40,7 +39,7 @@
         // not exist. New caller-enabled JGSS changed this. this bug fix will
         // revert to the old behavior.
         try {
-            GSSUtil.login(GSSUtil.CALLER_INITIATE, GSSUtil.GSS_KRB5_MECH_OID);
+            GSSUtil.login(GSSCaller.CALLER_INITIATE, GSSUtil.GSS_KRB5_MECH_OID);
         } catch (SecurityException se) {
             if (se.getCause() instanceof java.io.IOException) {
                 // what had been and should be...
diff --git a/test/sun/security/krb5/auto/HttpNegotiateServer.java b/test/sun/security/krb5/auto/HttpNegotiateServer.java
new file mode 100644
index 0000000..9505455
--- /dev/null
+++ b/test/sun/security/krb5/auto/HttpNegotiateServer.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6578647
+ * @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication()
+ * @run main/othervm -Dsun.net.spi.nameservice.provider.1=ns,mock HttpNegotiateServer
+ */
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpPrincipal;
+import com.sun.security.auth.module.Krb5LoginModule;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.InetAddress;
+import java.net.PasswordAuthentication;
+import java.net.Proxy;
+import java.net.URL;
+import java.security.PrivilegedExceptionAction;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.security.auth.Subject;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSManager;
+import sun.security.jgss.GSSUtil;
+import sun.net.spi.nameservice.NameService;
+import sun.net.spi.nameservice.NameServiceDescriptor;
+import sun.security.krb5.Config;
+
+/**
+ * Basic JGSS/krb5 test with 3 parties: client, server, backend server. Each
+ * party uses JAAS login to get subjects and executes JGSS calls using
+ * Subject.doAs.
+ */
+public class HttpNegotiateServer implements NameServiceDescriptor {
+
+    // Two realm, web server in one, proxy server in another
+    final static String REALM_WEB = "WEB.DOMAIN";
+    final static String REALM_PROXY = "PROXY.DOMAIN";
+    final static String KRB5_CONF = "web.conf";
+    final static String KRB5_TAB = "web.ktab";
+
+    // user principals
+    final static String WEB_USER = "web";
+    final static char[] WEB_PASS = "webby".toCharArray();
+    final static String PROXY_USER = "pro";
+    final static char[] PROXY_PASS = "proxy".toCharArray();
+    final static int WEB_PORT = 17840;
+
+    final static String WEB_HOST = "host.web.domain";
+    final static String PROXY_HOST = "host.proxy.domain";
+    final static int PROXY_PORT = 17841;
+
+    // web page content
+    final static String CONTENT = "Hello, World!";
+
+    // URLs for web test, proxy test. The proxy server is not a real proxy
+    // since it fakes the same content for any URL. :)
+    final static URL webUrl, proxyUrl;
+    static {
+        URL u1 = null, u2 = null;
+        try {
+            u1 = new URL("http://" + WEB_HOST +":" + WEB_PORT + "/a/b/c");
+            u2 = new URL("http://nosuchplace/a/b/c");
+        } catch (Exception e) {
+        }
+        webUrl = u1; proxyUrl = u2;
+    }
+
+    /**
+     * This Authenticator checks everything:
+     * scheme, protocol, requestor type, host, port, and url
+     */
+    static class KnowAllAuthenticator extends java.net.Authenticator {
+        public PasswordAuthentication getPasswordAuthentication () {
+            if (!getRequestingScheme().equalsIgnoreCase("Negotiate")) {
+                throw new RuntimeException("Bad scheme");
+            }
+            if (!getRequestingProtocol().equalsIgnoreCase("HTTP")) {
+                throw new RuntimeException("Bad protocol");
+            }
+            if (getRequestorType() == RequestorType.SERVER) {
+                if (!this.getRequestingHost().equalsIgnoreCase(webUrl.getHost())) {
+                    throw new RuntimeException("Bad host");
+                }
+                if (this.getRequestingPort() != webUrl.getPort()) {
+                    throw new RuntimeException("Bad port");
+                }
+                if (!this.getRequestingURL().equals(webUrl)) {
+                    throw new RuntimeException("Bad url");
+                }
+                return new PasswordAuthentication(
+                        WEB_USER+"@"+REALM_WEB, WEB_PASS);
+            } else if (getRequestorType() == RequestorType.PROXY) {
+                if (!this.getRequestingHost().equalsIgnoreCase(PROXY_HOST)) {
+                    throw new RuntimeException("Bad host");
+                }
+                if (this.getRequestingPort() != PROXY_PORT) {
+                    throw new RuntimeException("Bad port");
+                }
+                if (!this.getRequestingURL().equals(proxyUrl)) {
+                    throw new RuntimeException("Bad url");
+                }
+                return new PasswordAuthentication(
+                        PROXY_USER+"@"+REALM_PROXY, PROXY_PASS);
+            } else  {
+                throw new RuntimeException("Bad requster type");
+            }
+        }
+    }
+
+    public static void main(String[] args)
+            throws Exception {
+
+        KDC kdcw = new KDC(REALM_WEB, 0, true);
+        kdcw.addPrincipal(WEB_USER, WEB_PASS);
+        kdcw.addPrincipalRandKey("krbtgt/" + REALM_WEB);
+        kdcw.addPrincipalRandKey("HTTP/" + WEB_HOST);
+
+        KDC kdcp = new KDC(REALM_PROXY, 0, true);
+        kdcp.addPrincipal(PROXY_USER, PROXY_PASS);
+        kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);
+        kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);
+
+        KDC.writeMultiKtab(KRB5_TAB, kdcw, kdcp);
+        KDC.saveConfig(KRB5_CONF, kdcw, kdcp,
+                "default_keytab_name = " + KRB5_TAB,
+                "[domain_realm]",
+                "",
+                ".web.domain="+REALM_WEB,
+                ".proxy.domain="+REALM_PROXY);
+
+        System.setProperty("java.security.krb5.conf", KRB5_CONF);
+        Config.refresh();
+
+        HttpServer h1 = httpd(WEB_PORT, "Negotiate", false,
+                "HTTP/" + WEB_HOST + "@" + REALM_WEB, KRB5_TAB);
+        HttpServer h2 = httpd(PROXY_PORT, "Negotiate", true,
+                "HTTP/" + PROXY_HOST + "@" + REALM_PROXY, KRB5_TAB);
+
+        try {
+
+            BufferedReader reader;
+            java.net.Authenticator.setDefault(new KnowAllAuthenticator());
+
+            reader = new BufferedReader(new InputStreamReader(
+                    webUrl.openConnection().getInputStream()));
+            if (!reader.readLine().equals(CONTENT)) {
+                throw new RuntimeException("Bad content");
+            }
+
+            reader = new BufferedReader(new InputStreamReader(
+                    proxyUrl.openConnection(
+                    new Proxy(Proxy.Type.HTTP,
+                        new InetSocketAddress(PROXY_HOST, PROXY_PORT)))
+                    .getInputStream()));
+            if (!reader.readLine().equals(CONTENT)) {
+                throw new RuntimeException("Bad content");
+            }
+        } finally {
+            // Must stop. Seems there's no HttpServer.startAsDaemon()
+            if (h1 != null) h1.stop(0);
+            if (h2 != null) h2.stop(0);
+        }
+    }
+
+    /**
+     * Creates and starts an HTTP or proxy server that requires
+     * Negotiate authentication.
+     * @param scheme "Negotiate" or "Kerberos"
+     * @param principal the krb5 service principal the server runs with
+     * @return the server
+     */
+    public static HttpServer httpd(int port, String scheme, boolean proxy,
+            String principal, String ktab) throws Exception {
+        MyHttpHandler h = new MyHttpHandler();
+        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
+        HttpContext hc = server.createContext("/", h);
+        hc.setAuthenticator(new MyServerAuthenticator(
+                proxy, scheme, principal, ktab));
+        server.start();
+        return server;
+    }
+
+    static class MyHttpHandler implements HttpHandler {
+        public void handle(HttpExchange t) throws IOException {
+            t.sendResponseHeaders(200, 0);
+            t.getResponseBody().write(CONTENT.getBytes());
+            t.close();
+        }
+    }
+
+    static class MyServerAuthenticator
+            extends com.sun.net.httpserver.Authenticator {
+        Subject s = new Subject();
+        GSSManager m = null;
+        GSSCredential cred = null;
+        String scheme = null;
+        String reqHdr = "WWW-Authenticate";
+        String respHdr = "Authorization";
+        int err = HttpURLConnection.HTTP_UNAUTHORIZED;
+
+        public MyServerAuthenticator(boolean proxy, String scheme,
+                String principal, String ktab) throws Exception {
+
+            this.scheme = scheme;
+            if (proxy) {
+                reqHdr = "Proxy-Authenticate";
+                respHdr = "Proxy-Authorization";
+                err = HttpURLConnection.HTTP_PROXY_AUTH;
+            }
+
+            Krb5LoginModule krb5 = new Krb5LoginModule();
+            Map<String, String> map = new HashMap<String, String>();
+            Map<String, Object> shared = new HashMap<String, Object>();
+
+            map.put("storeKey", "true");
+            map.put("isInitiator", "false");
+            map.put("useKeyTab", "true");
+            map.put("keyTab", ktab);
+            map.put("principal", principal);
+            krb5.initialize(s, null, shared, map);
+            krb5.login();
+            krb5.commit();
+            m = GSSManager.getInstance();
+            cred = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {
+                @Override
+                public GSSCredential run() throws Exception {
+                    System.err.println("Creating GSSCredential");
+                    return m.createCredential(
+                            null,
+                            GSSCredential.INDEFINITE_LIFETIME,
+                            MyServerAuthenticator.this.scheme.equalsIgnoreCase("Negotiate")?
+                                    GSSUtil.GSS_SPNEGO_MECH_OID:
+                                    GSSUtil.GSS_KRB5_MECH_OID,
+                            GSSCredential.ACCEPT_ONLY);
+                }
+            });
+        }
+
+        @Override
+        public Result authenticate(HttpExchange exch) {
+            // The GSContext is stored in an HttpContext attribute named
+            // "GSSContext" and is created at the first request.
+            GSSContext c = null;
+            String auth = exch.getRequestHeaders().getFirst(respHdr);
+            try {
+                c = (GSSContext)exch.getHttpContext().getAttributes().get("GSSContext");
+                if (auth == null) {                 // First request
+                    Headers map = exch.getResponseHeaders();
+                    map.set (reqHdr, scheme);        // Challenge!
+                    c = Subject.doAs(s, new PrivilegedExceptionAction<GSSContext>() {
+                        @Override
+                        public GSSContext run() throws Exception {
+                            return m.createContext(cred);
+                        }
+                    });
+                    exch.getHttpContext().getAttributes().put("GSSContext", c);
+                    return new com.sun.net.httpserver.Authenticator.Retry(err);
+                } else {                            // Later requests
+                    byte[] token = new sun.misc.BASE64Decoder()
+                            .decodeBuffer(auth.split(" ")[1]);
+                    token = c.acceptSecContext(token, 0, token.length);
+                    Headers map = exch.getResponseHeaders();
+                    map.set (reqHdr, scheme + " " + new sun.misc.BASE64Encoder()
+                            .encode(token).replaceAll("\\s", ""));
+                    if (c.isEstablished()) {
+                        return new com.sun.net.httpserver.Authenticator.Success(
+                                new HttpPrincipal(c.getSrcName().toString(), ""));
+                    } else {
+                        return new com.sun.net.httpserver.Authenticator.Retry(err);
+                    }
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
+    public NameService createNameService() throws Exception {
+        NameService ns = new NameService() {
+            @Override
+            public InetAddress[] lookupAllHostAddr(String host)
+                    throws UnknownHostException {
+                // Everything is localhost
+                return new InetAddress[]{
+                    InetAddress.getByAddress(host, new byte[]{127,0,0,1})
+                };
+            }
+            @Override
+            public String getHostByAddr(byte[] addr)
+                    throws UnknownHostException {
+                // No reverse lookup
+                throw new UnknownHostException();
+            }
+        };
+        return ns;
+    }
+
+    @Override
+    public String getProviderName() {
+        return "mock";
+    }
+
+    @Override
+    public String getType() {
+        return "ns";
+    }
+}
+
diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java
index 3e6a779..14767c4 100644
--- a/test/sun/security/krb5/auto/KDC.java
+++ b/test/sun/security/krb5/auto/KDC.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -201,14 +201,14 @@
     }
 
     /**
-     * Write all principals' keys into a keytab file. Note that the keys for
-     * the krbtgt principal for this realm will not be written.
+     * Write all principals' keys from multiple KDCsinto one keytab file.
+     * Note that the keys for the krbtgt principals will not be written.
      * <p>
      * Attention: This method references krb5.conf settings. If you need to
      * setup krb5.conf later, please call <code>Config.refresh()</code> after
      * the new setting. For example:
      * <pre>
-     * kdc.writeKtab("/etc/kdc/ktab");  // Config is initialized,
+     * KDC.writeKtab("/etc/kdc/ktab", kdc);  // Config is initialized,
      * System.setProperty("java.security.krb5.conf", "/home/mykrb5.conf");
      * Config.refresh();
      * </pre>
@@ -223,21 +223,32 @@
      * @throws sun.security.krb5.KrbException for any realm and/or principal
      *         name error.
      */
-    public void writeKtab(String tab) throws IOException, KrbException {
+    public static void writeMultiKtab(String tab, KDC... kdcs)
+            throws IOException, KrbException {
         KeyTab ktab = KeyTab.create(tab);
-        for (String name : passwords.keySet()) {
-            if (name.equals("krbtgt/" + realm)) {
-                continue;
+        for (KDC kdc: kdcs) {
+            for (String name : kdc.passwords.keySet()) {
+                if (name.equals("krbtgt/" + kdc.realm)) {
+                    continue;
+                }
+                ktab.addEntry(new PrincipalName(name + "@" + kdc.realm,
+                        name.indexOf('/') < 0 ?
+                            PrincipalName.KRB_NT_UNKNOWN :
+                            PrincipalName.KRB_NT_SRV_HST),
+                            kdc.passwords.get(name));
             }
-            ktab.addEntry(new PrincipalName(name + "@" + realm,
-                    name.indexOf('/') < 0 ?
-                        PrincipalName.KRB_NT_UNKNOWN :
-                        PrincipalName.KRB_NT_SRV_HST), passwords.get(name));
         }
         ktab.save();
     }
 
     /**
+     * Write a ktab for this KDC.
+     */
+    public void writeKtab(String tab) throws IOException, KrbException {
+        KDC.writeMultiKtab(tab, this);
+    }
+
+    /**
      * Adds a new principal to this realm with a given password.
      * @param user the principal's name. For a service principal, use the
      *        form of host/f.q.d.n
diff --git a/test/sun/security/krb5/auto/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/test/sun/security/krb5/auto/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
new file mode 100644
index 0000000..32f3a09
--- /dev/null
+++ b/test/sun/security/krb5/auto/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
@@ -0,0 +1 @@
+HttpNegotiateServer