Merge
authorvaleriep
Tue, 08 Jan 2013 13:06:22 -0800
changeset 15011 ca5fca41f778
parent 15010 ec6b49ce42b1 (current diff)
parent 15007 31d8a6072b16 (diff)
child 15012 8fa5871c30c6
Merge
--- a/jdk/src/share/classes/sun/security/jgss/krb5/InitSecContextToken.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/InitSecContextToken.java	Tue Jan 08 13:06:22 2013 -0800
@@ -86,7 +86,7 @@
      * For the context acceptor to call. It reads the bytes out of an
      * InputStream and constructs an InitSecContextToken with them.
      */
-    InitSecContextToken(Krb5Context context, EncryptionKey[] keys,
+    InitSecContextToken(Krb5Context context, Krb5AcceptCredential cred,
                                InputStream is)
         throws IOException, GSSException, KrbException  {
 
@@ -105,7 +105,7 @@
         if (context.getChannelBinding() != null) {
             addr = context.getChannelBinding().getInitiatorAddress();
         }
-        apReq = new KrbApReq(apReqBytes, keys, addr);
+        apReq = new KrbApReq(apReqBytes, cred, addr);
         //debug("\nReceived AP-REQ and authenticated it.\n");
 
         EncryptionKey sessionKey = apReq.getCreds().getSessionKey();
--- a/jdk/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java	Tue Jan 08 13:06:22 2013 -0800
@@ -45,13 +45,10 @@
 public class Krb5AcceptCredential
     implements Krb5CredElement {
 
-    private static final long serialVersionUID = 7714332137352567952L;
-
-    private Krb5NameElement name;
+    private final Krb5NameElement name;
+    private final ServiceCreds screds;
 
-    private Krb5Util.ServiceCreds screds;
-
-    private Krb5AcceptCredential(Krb5NameElement name, Krb5Util.ServiceCreds creds) {
+    private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) {
         /*
          * Initialize this instance with the data from the acquired
          * KerberosKey. This class needs to be a KerberosKey too
@@ -69,11 +66,11 @@
             name.getKrb5PrincipalName().getName());
         final AccessControlContext acc = AccessController.getContext();
 
-        Krb5Util.ServiceCreds creds = null;
+        ServiceCreds creds = null;
         try {
             creds = AccessController.doPrivileged(
-                        new PrivilegedExceptionAction<Krb5Util.ServiceCreds>() {
-                public Krb5Util.ServiceCreds run() throws Exception {
+                        new PrivilegedExceptionAction<ServiceCreds>() {
+                public ServiceCreds run() throws Exception {
                     return Krb5Util.getServiceCreds(
                         caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
                         serverPrinc, acc);
@@ -92,8 +89,10 @@
 
         if (name == null) {
             String fullName = creds.getName();
-            name = Krb5NameElement.getInstance(fullName,
+            if (fullName != null) {
+                name = Krb5NameElement.getInstance(fullName,
                                        Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
+            }
         }
 
         return new Krb5AcceptCredential(name, creds);
@@ -153,8 +152,8 @@
         return Krb5MechFactory.PROVIDER;
     }
 
-    EncryptionKey[] getKrb5EncryptionKeys() {
-        return screds.getEKeys();
+    public EncryptionKey[] getKrb5EncryptionKeys(PrincipalName princ) {
+        return screds.getEKeys(princ);
     }
 
     /**
--- a/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Context.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Context.java	Tue Jan 08 13:06:22 2013 -0800
@@ -818,16 +818,23 @@
                 }
                 myName = (Krb5NameElement) myCred.getName();
 
-                checkPermission(myName.getKrb5PrincipalName().getName(),
-                                "accept");
-
-                EncryptionKey[] secretKeys =
-                 ((Krb5AcceptCredential) myCred).getKrb5EncryptionKeys();
+                // If there is already a bound name, check now
+                if (myName != null) {
+                    Krb5MechFactory.checkAcceptCredPermission(myName, myName);
+                }
 
                 InitSecContextToken token = new InitSecContextToken(this,
-                                                    secretKeys, is);
+                                                    (Krb5AcceptCredential) myCred, is);
                 PrincipalName clientName = token.getKrbApReq().getClient();
                 peerName = Krb5NameElement.getInstance(clientName);
+
+                // If unbound, check after the bound name is found
+                if (myName == null) {
+                    myName = Krb5NameElement.getInstance(
+                        token.getKrbApReq().getCreds().getServer());
+                    Krb5MechFactory.checkAcceptCredPermission(myName, myName);
+                }
+
                 if (getMutualAuthState()) {
                         retVal = new AcceptSecContextToken(this,
                                           token.getKrbApReq()).encode();
--- a/jdk/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java	Tue Jan 08 13:06:22 2013 -0800
@@ -158,7 +158,7 @@
     public static void checkAcceptCredPermission(Krb5NameElement name,
                                            GSSNameSpi originalName) {
         SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
+        if (sm != null && name != null) {
             ServicePermission perm = new ServicePermission
                 (name.getKrb5PrincipalName().getName(), "accept");
             try {
--- a/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Util.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Util.java	Tue Jan 08 13:06:22 2013 -0800
@@ -187,114 +187,6 @@
     }
 
     /**
-     * Credentials of a service, the private secret to authenticate its
-     * identity, which can be:
-     *   1. Some KerberosKeys (generated from password)
-     *   2. A KeyTab (for a typical service)
-     *   3. A TGT (for S4U2proxy extension)
-     *
-     * Note that some creds can coexist. For example, a user2user service
-     * can use its keytab (or keys) if the client can successfully obtain a
-     * normal service ticket, otherwise, it can uses the TGT (actually, the
-     * session key of the TGT) if the client can only acquire a service ticket
-     * of ENC-TKT-IN-SKEY style.
-     */
-    public static class ServiceCreds {
-        private KerberosPrincipal kp;
-        private List<KeyTab> ktabs;
-        private List<KerberosKey> kk;
-        private Subject subj;
-        private KerberosTicket tgt;
-
-        private static ServiceCreds getInstance(
-                Subject subj, String serverPrincipal) {
-
-            ServiceCreds sc = new ServiceCreds();
-            sc.subj = subj;
-
-            for (KerberosPrincipal p: subj.getPrincipals(KerberosPrincipal.class)) {
-                if (serverPrincipal == null ||
-                        p.getName().equals(serverPrincipal)) {
-                    sc.kp = p;
-                    serverPrincipal = p.getName();
-                    break;
-                }
-            }
-            if (sc.kp == null) {
-                // Compatibility with old behavior: even when there is no
-                // KerberosPrincipal, we can find one from KerberosKeys
-                List<KerberosKey> keys = SubjectComber.findMany(
-                        subj, serverPrincipal, null, KerberosKey.class);
-                if (!keys.isEmpty()) {
-                    sc.kp = keys.get(0).getPrincipal();
-                    serverPrincipal = sc.kp.getName();
-                    if (DEBUG) {
-                        System.out.println(">>> ServiceCreds: no kp?"
-                                + " find one from kk: " + serverPrincipal);
-                    }
-                } else {
-                    return null;
-                }
-            }
-            sc.ktabs = SubjectComber.findMany(
-                        subj, null, null, KeyTab.class);
-            sc.kk = SubjectComber.findMany(
-                        subj, serverPrincipal, null, KerberosKey.class);
-            sc.tgt = SubjectComber.find(
-                    subj, null, serverPrincipal, KerberosTicket.class);
-            if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) {
-                return null;
-            }
-            return sc;
-        }
-
-        public String getName() {
-            return kp.getName();
-        }
-
-        public KerberosKey[] getKKeys() {
-            List<KerberosKey> keys = new ArrayList<>();
-            for (KerberosKey k: kk) {
-                keys.add(k);
-            }
-            for (KeyTab ktab: ktabs) {
-                for (KerberosKey k: ktab.getKeys(kp)) {
-                    keys.add(k);
-                }
-            }
-            return keys.toArray(new KerberosKey[keys.size()]);
-        }
-
-        public EncryptionKey[] getEKeys() {
-            KerberosKey[] kkeys = getKKeys();
-            EncryptionKey[] ekeys = new EncryptionKey[kkeys.length];
-            for (int i=0; i<ekeys.length; i++) {
-                ekeys[i] =  new EncryptionKey(
-                            kkeys[i].getEncoded(), kkeys[i].getKeyType(),
-                            new Integer(kkeys[i].getVersionNumber()));
-            }
-            return ekeys;
-        }
-
-        public Credentials getInitCred() {
-            if (tgt == null) {
-                return null;
-            }
-            try {
-                return ticketToCreds(tgt);
-            } catch (KrbException | IOException e) {
-                return null;
-            }
-        }
-
-        public void destroy() {
-            kp = null;
-            ktabs = null;
-            kk = null;
-            tgt = null;
-        }
-    }
-    /**
      * Retrieves the ServiceCreds for the specified server principal from
      * the Subject in the specified AccessControlContext. If not found, and if
      * useSubjectCredsOnly is false, then obtain from a LoginContext.
@@ -361,5 +253,4 @@
         return KerberosSecrets.getJavaxSecurityAuthKerberosAccess().
                 keyTabGetEncryptionKeys(ktab, cname);
     }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/ServiceCreds.java	Tue Jan 08 13:06:22 2013 -0800
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.jgss.krb5;
+
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KeyTab;
+import javax.security.auth.Subject;
+
+import sun.security.krb5.Credentials;
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.KrbException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import sun.security.krb5.*;
+import sun.security.krb5.internal.Krb5;
+
+/**
+ * Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is
+ * the principal. It can be specified as the serverPrincipal argument
+ * in the getInstance() method, or uses only KerberosPrincipal in the subject.
+ * Otherwise, the creds object is unbound and kp is null.
+ *
+ * The class also encapsulates various secrets, which can be:
+ *
+ *   1. Some KerberosKeys (generated from password)
+ *   2. Some KeyTabs (for a typical service based on keytabs)
+ *   3. A TGT (for S4U2proxy extension or user2user)
+ *
+ * Note that some secrets can coexist. For example, a user2user service
+ * can use its keytab (or keys) if the client can successfully obtain a
+ * normal service ticket, or it can use the TGT (actually, the session key
+ * of the TGT) if the client can only acquire a service ticket
+ * of ENC-TKT-IN-SKEY style.
+ *
+ * @since 1.8
+ */
+public final class ServiceCreds {
+    // The principal, or null if unbound
+    private KerberosPrincipal kp;
+
+    // All principals in the subject's princ set
+    private Set<KerberosPrincipal> allPrincs;
+
+    // All private credentials that can be used
+    private List<KeyTab> ktabs;
+    private List<KerberosKey> kk;
+    private KerberosTicket tgt;
+
+    private boolean destroyed;
+
+    private ServiceCreds() {
+        // Make sure this class cannot be instantiated externally.
+    }
+
+    /**
+     * Creates a ServiceCreds object based on info in a Subject for
+     * a given principal name (if specified).
+     * @return the object, or null if there is no private creds for it
+     */
+    public static ServiceCreds getInstance(
+            Subject subj, String serverPrincipal) {
+
+        ServiceCreds sc = new ServiceCreds();
+
+        sc.allPrincs =
+                subj.getPrincipals(KerberosPrincipal.class);
+
+        // Compatibility. A key implies its own principal
+        for (KerberosKey key: SubjectComber.findMany(
+                subj, serverPrincipal, null, KerberosKey.class)) {
+            sc.allPrincs.add(key.getPrincipal());
+        }
+
+        if (serverPrincipal != null) {      // A named principal
+            sc.kp = new KerberosPrincipal(serverPrincipal);
+        } else {
+            if (sc.allPrincs.size() == 1) { // choose the only one
+                sc.kp = sc.allPrincs.iterator().next();
+                serverPrincipal = sc.kp.getName();
+            }
+        }
+
+        sc.ktabs = SubjectComber.findMany(
+                    subj, serverPrincipal, null, KeyTab.class);
+        sc.kk = SubjectComber.findMany(
+                    subj, serverPrincipal, null, KerberosKey.class);
+        sc.tgt = SubjectComber.find(
+                subj, null, serverPrincipal, KerberosTicket.class);
+        if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) {
+            return null;
+        }
+
+        sc.destroyed = false;
+
+        return sc;
+    }
+
+    // can be null
+    public String getName() {
+        if (destroyed) {
+            throw new IllegalStateException("This object is destroyed");
+        }
+        return kp == null ? null : kp.getName();
+    }
+
+    /**
+     * Gets keys for someone unknown.
+     * Used by TLS or as a fallback in getEKeys(). Can still return an
+     * empty array.
+     */
+    public KerberosKey[] getKKeys() {
+        if (destroyed) {
+            throw new IllegalStateException("This object is destroyed");
+        }
+        if (kp != null) {
+            return getKKeys(kp);
+        } else if (!allPrincs.isEmpty()) {
+            return getKKeys(allPrincs.iterator().next());
+        }
+        return new KerberosKey[0];
+    }
+
+    /**
+     * Get kkeys for a principal,
+     * @param princ the target name initiator requests. Not null.
+     * @return keys for the princ, never null, might be empty
+     */
+    private KerberosKey[] getKKeys(KerberosPrincipal princ) {
+        ArrayList<KerberosKey> keys = new ArrayList<>();
+        if (kp != null && !princ.equals(kp)) {
+            return new KerberosKey[0];      // Not me
+        }
+        if (!allPrincs.contains(princ)) {
+            return new KerberosKey[0];      // Not someone I know, This check
+                                            // is necessary but a KeyTab has
+                                            // no principal name recorded.
+        }
+        for (KerberosKey k: kk) {
+            if (k.getPrincipal().equals(princ)) {
+                keys.add(k);
+            }
+        }
+        for (KeyTab ktab: ktabs) {
+            for (KerberosKey k: ktab.getKeys(princ)) {
+                keys.add(k);
+            }
+        }
+        return keys.toArray(new KerberosKey[keys.size()]);
+    }
+
+    /**
+     * Gets EKeys for a principal.
+     * @param princ the target name initiator requests. Not null.
+     * @return keys for the princ, never null, might be empty
+     */
+    public EncryptionKey[] getEKeys(PrincipalName princ) {
+        if (destroyed) {
+            throw new IllegalStateException("This object is destroyed");
+        }
+        KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName()));
+        if (kkeys.length == 0) {
+            // Note: old JDK does not perform real name checking. If the
+            // acceptor starts by name A but initiator requests for B,
+            // as long as their keys match (i.e. A's keys can decrypt B's
+            // service ticket), the authentication is OK. There are real
+            // customers depending on this to use different names for a
+            // single service.
+            kkeys = getKKeys();
+        }
+        EncryptionKey[] ekeys = new EncryptionKey[kkeys.length];
+        for (int i=0; i<ekeys.length; i++) {
+            ekeys[i] =  new EncryptionKey(
+                        kkeys[i].getEncoded(), kkeys[i].getKeyType(),
+                        new Integer(kkeys[i].getVersionNumber()));
+        }
+        return ekeys;
+    }
+
+    public Credentials getInitCred() {
+        if (destroyed) {
+            throw new IllegalStateException("This object is destroyed");
+        }
+        if (tgt == null) {
+            return null;
+        }
+        try {
+            return Krb5Util.ticketToCreds(tgt);
+        } catch (KrbException | IOException e) {
+            return null;
+        }
+    }
+
+    public void destroy() {
+        // Do not wipe out real keys because they are references to the
+        // priv creds in subject. Just make it useless.
+        destroyed = true;
+        kp = null;
+        ktabs.clear();
+        kk.clear();
+        tgt = null;
+    }
+}
--- a/jdk/src/share/classes/sun/security/jgss/krb5/SubjectComber.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/SubjectComber.java	Tue Jan 08 13:06:22 2013 -0800
@@ -33,6 +33,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import javax.security.auth.kerberos.KerberosPrincipal;
 import javax.security.auth.kerberos.KeyTab;
 
 /**
@@ -84,19 +85,37 @@
         } else {
             List<T> answer = (oneOnly ? null : new ArrayList<T>());
 
-            if (credClass == KeyTab.class) {    // Principal un-related
-                // We are looking for credentials unrelated to serverPrincipal
-                Iterator<T> iterator =
-                    subject.getPrivateCredentials(credClass).iterator();
-                while (iterator.hasNext()) {
-                    T t = iterator.next();
-                    if (DEBUG) {
-                        System.out.println("Found " + credClass.getSimpleName());
+            if (credClass == KeyTab.class) {
+                // TODO: There is currently no good way to filter out keytabs
+                // not for serverPrincipal. We can only check the principal
+                // set. If the server is not there, we can be sure none of the
+                // keytabs should be used, otherwise, use all for safety.
+                boolean useAll = false;
+                if (serverPrincipal != null) {
+                    for (KerberosPrincipal princ:
+                            subject.getPrincipals(KerberosPrincipal.class)) {
+                        if (princ.getName().equals(serverPrincipal)) {
+                            useAll = true;
+                            break;
+                        }
                     }
-                    if (oneOnly) {
-                        return t;
-                    } else {
-                        answer.add(t);
+                } else {
+                    useAll = true;
+                }
+                if (useAll) {
+                    Iterator<KeyTab> iterator =
+                        subject.getPrivateCredentials(KeyTab.class).iterator();
+                    while (iterator.hasNext()) {
+                        KeyTab t = iterator.next();
+                        if (DEBUG) {
+                            System.out.println("Found " + credClass.getSimpleName()
+                                    + " " + t);
+                        }
+                        if (oneOnly) {
+                            return t;
+                        } else {
+                            answer.add(credClass.cast(t));
+                        }
                     }
                 }
             } else if (credClass == KerberosKey.class) {
@@ -114,11 +133,6 @@
                          if (oneOnly) {
                              return t;
                          } else {
-                             if (serverPrincipal == null) {
-                                 // Record name so that keys returned will all
-                                 // belong to the same principal
-                                 serverPrincipal = name;
-                             }
                              answer.add(credClass.cast(t));
                          }
                     }
--- a/jdk/src/share/classes/sun/security/krb5/KrbApReq.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/krb5/KrbApReq.java	Tue Jan 08 13:06:22 2013 -0800
@@ -34,6 +34,7 @@
 import sun.security.krb5.internal.*;
 import sun.security.krb5.internal.crypto.*;
 import sun.security.krb5.internal.rcache.*;
+import sun.security.jgss.krb5.Krb5AcceptCredential;
 import java.net.InetAddress;
 import sun.security.util.*;
 import java.io.IOException;
@@ -135,13 +136,13 @@
      */
      // Used in InitSecContextToken (for AP_REQ and not TGS REQ)
     public KrbApReq(byte[] message,
-                    EncryptionKey[] keys,
+                    Krb5AcceptCredential cred,
                     InetAddress initiator)
         throws KrbException, IOException {
         obuf = message;
         if (apReqMessg == null)
             decode();
-        authenticate(keys, initiator);
+        authenticate(cred, initiator);
     }
 
     /**
@@ -260,10 +261,11 @@
         }
     }
 
-    private void authenticate(EncryptionKey[] keys, InetAddress initiator)
+    private void authenticate(Krb5AcceptCredential cred, InetAddress initiator)
         throws KrbException, IOException {
         int encPartKeyType = apReqMessg.ticket.encPart.getEType();
         Integer kvno = apReqMessg.ticket.encPart.getKeyVersionNumber();
+        EncryptionKey[] keys = cred.getKrb5EncryptionKeys(apReqMessg.ticket.sname);
         EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, kvno, keys);
 
         if (dkey == null) {
--- a/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java	Tue Jan 08 13:06:22 2013 -0800
@@ -382,9 +382,15 @@
      */
     public void addEntry(PrincipalName service, char[] psswd,
             int kvno, boolean append) throws KrbException {
+        addEntry(service, service.getSalt(), psswd, kvno, append);
+    }
+
+    // Called by KDC test
+    public void addEntry(PrincipalName service, String salt, char[] psswd,
+            int kvno, boolean append) throws KrbException {
 
         EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys(
-            psswd, service.getSalt());
+            psswd, salt);
 
         // There should be only one maximum KVNO value for all etypes, so that
         // all added keys can have the same KVNO.
--- a/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java	Tue Jan 08 13:06:22 2013 -0800
@@ -36,6 +36,7 @@
 
 import sun.security.jgss.GSSCaller;
 import sun.security.jgss.krb5.Krb5Util;
+import sun.security.jgss.krb5.ServiceCreds;
 import sun.security.krb5.PrincipalName;
 import sun.security.ssl.Krb5Proxy;
 
@@ -62,7 +63,7 @@
     @Override
     public SecretKey[] getServerKeys(AccessControlContext acc)
             throws LoginException {
-        Krb5Util.ServiceCreds serviceCreds =
+        ServiceCreds serviceCreds =
             Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc);
         return serviceCreds != null ? serviceCreds.getKKeys() :
                                         new KerberosKey[0];
--- a/jdk/src/windows/classes/sun/nio/ch/PipeImpl.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/src/windows/classes/sun/nio/ch/PipeImpl.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2012, Oracle and/or its affiliates. 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,67 +72,97 @@
 
         private final SelectorProvider sp;
 
+        private IOException ioe = null;
+
         private Initializer(SelectorProvider sp) {
             this.sp = sp;
         }
 
+        @Override
         public Void run() throws IOException {
-            ServerSocketChannel ssc = null;
-            SocketChannel sc1 = null;
-            SocketChannel sc2 = null;
-
-            try {
-                // loopback address
-                InetAddress lb = InetAddress.getByName("127.0.0.1");
-                assert(lb.isLoopbackAddress());
-
-                // bind ServerSocketChannel to a port on the loopback address
-                ssc = ServerSocketChannel.open();
-                ssc.socket().bind(new InetSocketAddress(lb, 0));
-
-                // Establish connection (assumes connections are eagerly
-                // accepted)
-                InetSocketAddress sa
-                    = new InetSocketAddress(lb, ssc.socket().getLocalPort());
-                sc1 = SocketChannel.open(sa);
-
-                ByteBuffer bb = ByteBuffer.allocate(8);
-                long secret = rnd.nextLong();
-                bb.putLong(secret).flip();
-                sc1.write(bb);
-
-                // Get a connection and verify it is legitimate
+            LoopbackConnector connector = new LoopbackConnector();
+            connector.run();
+            if (ioe instanceof ClosedByInterruptException) {
+                ioe = null;
+                Thread connThread = new Thread(connector) {
+                    @Override
+                    public void interrupt() {}
+                };
+                connThread.start();
                 for (;;) {
-                    sc2 = ssc.accept();
-                    bb.clear();
-                    sc2.read(bb);
-                    bb.rewind();
-                    if (bb.getLong() == secret)
+                    try {
+                        connThread.join();
                         break;
-                    sc2.close();
+                    } catch (InterruptedException ex) {}
                 }
+                Thread.currentThread().interrupt();
+            }
+
+            if (ioe != null)
+                throw new IOException("Unable to establish loopback connection", ioe);
 
-                // Create source and sink channels
-                source = new SourceChannelImpl(sp, sc1);
-                sink = new SinkChannelImpl(sp, sc2);
-            } catch (IOException e) {
+            return null;
+        }
+
+        private class LoopbackConnector implements Runnable {
+
+            @Override
+            public void run() {
+                ServerSocketChannel ssc = null;
+                SocketChannel sc1 = null;
+                SocketChannel sc2 = null;
+
                 try {
-                    if (sc1 != null)
-                        sc1.close();
-                    if (sc2 != null)
+                    // Loopback address
+                    InetAddress lb = InetAddress.getByName("127.0.0.1");
+                    assert(lb.isLoopbackAddress());
+                    InetSocketAddress sa = null;
+                    for(;;) {
+                        // Bind ServerSocketChannel to a port on the loopback
+                        // address
+                        if (ssc == null || !ssc.isOpen()) {
+                            ssc = ServerSocketChannel.open();
+                            ssc.socket().bind(new InetSocketAddress(lb, 0));
+                            sa = new InetSocketAddress(lb, ssc.socket().getLocalPort());
+                        }
+
+                        // Establish connection (assume connections are eagerly
+                        // accepted)
+                        sc1 = SocketChannel.open(sa);
+                        ByteBuffer bb = ByteBuffer.allocate(8);
+                        long secret = rnd.nextLong();
+                        bb.putLong(secret).flip();
+                        sc1.write(bb);
+
+                        // Get a connection and verify it is legitimate
+                        sc2 = ssc.accept();
+                        bb.clear();
+                        sc2.read(bb);
+                        bb.rewind();
+                        if (bb.getLong() == secret)
+                            break;
                         sc2.close();
-                } catch (IOException e2) { }
-                IOException x = new IOException("Unable to establish"
-                                                + " loopback connection");
-                x.initCause(e);
-                throw x;
-            } finally {
-                try {
-                    if (ssc != null)
-                        ssc.close();
-                } catch (IOException e2) { }
+                        sc1.close();
+                    }
+
+                    // Create source and sink channels
+                    source = new SourceChannelImpl(sp, sc1);
+                    sink = new SinkChannelImpl(sp, sc2);
+                } catch (IOException e) {
+                    try {
+                        if (sc1 != null)
+                            sc1.close();
+                        if (sc2 != null)
+                            sc2.close();
+                    } catch (IOException e2) {}
+                    ioe = e;
+                } finally {
+                    try {
+                        if (ssc != null)
+                            ssc.close();
+                    } catch (IOException e2) {}
+                }
             }
-            return null;
         }
     }
 
@@ -144,7 +174,6 @@
         }
     }
 
-
     public SourceChannel source() {
         return source;
     }
--- a/jdk/test/ProblemList.txt	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/ProblemList.txt	Tue Jan 08 13:06:22 2013 -0800
@@ -1,6 +1,6 @@
 ###########################################################################
 #
-# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2013, Oracle and/or its affiliates. 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
@@ -238,9 +238,6 @@
 # 7146541
 java/rmi/transport/rapidExportUnexport/RapidExportUnexport.java	linux-all
 
-# 7187882
-java/rmi/activation/checkusage/CheckUsage.java                  generic-all
-
 # 7190106
 java/rmi/reliability/benchmark/runRmiBench.sh                   generic-all
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/Pipe/PipeInterrupt.java	Tue Jan 08 13:06:22 2013 -0800
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8002306
+ * @summary Ensure that a Pipe can open even if its thread has already
+ *          been interrupted.
+ * @author Dan Xu
+ */
+
+import java.io.IOException;
+import java.nio.channels.Pipe;
+
+
+public class PipeInterrupt {
+
+    private Exception exc = null;
+
+    public static void main(String[] args) throws Exception {
+        PipeInterrupt instance = new PipeInterrupt();
+        instance.test();
+    }
+
+    public void test() throws Exception {
+
+        Thread tester = new Thread("PipeTester") {
+            private Pipe testPipe = null;
+
+            @Override
+            public void run() {
+                for (;;) {
+                    boolean interrupted = this.isInterrupted();
+                    try {
+                        testPipe = Pipe.open();
+                        close();
+                        if (interrupted) {
+                            if (!this.isInterrupted())
+                               exc = new RuntimeException("interrupt status reset");
+                            break;
+                        }
+                    } catch (IOException ioe) {
+                        exc = ioe;
+                    }
+                }
+            }
+
+            private void close() throws IOException {
+                if (testPipe != null) {
+                    testPipe.sink().close();
+                    testPipe.source().close();
+                }
+            }
+        };
+
+        tester.start();
+        Thread.sleep(200);
+        tester.interrupt();
+        tester.join();
+
+        if (exc != null)
+            throw exc;
+    }
+}
--- a/jdk/test/java/rmi/activation/Activatable/shutdownGracefully/ShutdownGracefully.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/activation/Activatable/shutdownGracefully/ShutdownGracefully.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. 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
@@ -168,7 +168,7 @@
             registering = null;
 
             // Need to make sure that rmid goes away by itself
-            Process rmidProcess = rmid.getVM();
+            JavaVM rmidProcess = rmid;
             if (rmidProcess != null) {
                 try {
                     Runnable waitThread =
@@ -205,9 +205,9 @@
      * class that waits for rmid to exit
      */
     private static class ShutdownDetectThread implements Runnable {
-        private Process rmidProcess = null;
+        private JavaVM rmidProcess = null;
 
-        ShutdownDetectThread(Process rmidProcess) {
+        ShutdownDetectThread(JavaVM rmidProcess) {
             this.rmidProcess = rmidProcess;
         }
         public void run() {
--- a/jdk/test/java/rmi/activation/checkusage/CheckUsage.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/activation/checkusage/CheckUsage.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. 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
@@ -23,6 +23,7 @@
 
 /* @test
  * @bug 4259564
+ * @summary RMID's usage message is incomplete and inconsistent with other tools
  *
  * @library ../../testlibrary
  * @build TestLibrary JavaVM
@@ -37,23 +38,16 @@
  */
 public class CheckUsage {
     public static void main(String[] args) {
-
-        System.err.println("\nregression test for 4259564\n");
-
-        JavaVM rmidVM = null;
-
         try {
-            // make sure the registry exits with a proper usage statement
             ByteArrayOutputStream berr = new ByteArrayOutputStream();
 
-            // run a VM to start the registry
-            rmidVM = new JavaVM("sun.rmi.server.Activation", "", "foo",
-                                    System.out, berr);
+            // create rmid with incorrect command line args
+            JavaVM rmidVM = new JavaVM("sun.rmi.server.Activation", "", "foo",
+                                       System.out, berr);
             System.err.println("starting rmid");
-            rmidVM.start();
 
-            // wait for registry exit
-            int rmidVMExitStatus = rmidVM.getVM().waitFor();
+            // run the subprocess and wait for it to exit
+            int rmidVMExitStatus = rmidVM.execute();
             System.err.println("rmid exited with status: " +
                                rmidVMExitStatus);
 
@@ -66,12 +60,8 @@
             } else {
                 System.err.println("test passed");
             }
-
         } catch (Exception e) {
             TestLibrary.bomb(e);
-        } finally {
-            rmidVM.destroy();
-            rmidVM = null;
         }
     }
 }
--- a/jdk/test/java/rmi/registry/altSecurityManager/AltSecurityManager.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/registry/altSecurityManager/AltSecurityManager.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. 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
@@ -76,8 +76,7 @@
             }
 
             System.err.println("starting " + utilityToStart);
-            vm.start();
-            vm.getVM().waitFor();
+            vm.execute();
 
         } catch (Exception e) {
             TestLibrary.bomb(e);
--- a/jdk/test/java/rmi/registry/checkusage/CheckUsage.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/registry/checkusage/CheckUsage.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. 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
@@ -35,34 +35,21 @@
 
 /**
  * Make sure that the rmiregistry prints out a correct usage statement
- * when run with an incorrect command line; test written to conform to
- * new tighter bug fix/regression test guidelines.
+ * when run with an incorrect command line.
  */
 public class CheckUsage {
     public static void main(String[] args) {
 
-        System.err.println("\nregression test for 4151966\n");
-
-        JavaVM registryVM = null;
-
         try {
-            // make sure the registry exits with a proper usage statement
             ByteArrayOutputStream berr = new ByteArrayOutputStream();
 
             // run a VM to start the registry
-            registryVM = new JavaVM("sun.rmi.registry.RegistryImpl",
-                                    "", "foo",
-                                    System.out, berr);
+            JavaVM registryVM = new JavaVM("sun.rmi.registry.RegistryImpl",
+                                           "", "foo",
+                                           System.out, berr);
             System.err.println("starting registry");
-            registryVM.start();
-
-            // wait for registry exit
             System.err.println(" registry exited with status: " +
-                               registryVM.getVM().waitFor());
-            try {
-                Thread.sleep(7000);
-            } catch (InterruptedException ie) {
-            }
+                               registryVM.execute());
 
             String usage = new String(berr.toByteArray());
 
@@ -75,9 +62,6 @@
             }
         } catch (Exception e) {
             TestLibrary.bomb(e);
-        } finally {
-            registryVM.destroy();
-            registryVM = null;
         }
     }
 }
--- a/jdk/test/java/rmi/registry/reexport/Reexport.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/registry/reexport/Reexport.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. 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
@@ -122,8 +122,7 @@
         try {
             JavaVM jvm = new JavaVM("RegistryRunner", "", Integer.toString(p));
             jvm.start();
-            Reexport.subreg = jvm.getVM();
-
+            Reexport.subreg = jvm;
         } catch (IOException e) {
             // one of these is summarily dropped, can't remember which one
             System.out.println ("Test setup failed - cannot run rmiregistry");
@@ -135,7 +134,8 @@
         } catch (Exception whatever) {
         }
     }
-    private static Process subreg = null;
+
+    private static JavaVM subreg = null;
 
     public static void killRegistry(int port) {
         if (Reexport.subreg != null) {
--- a/jdk/test/java/rmi/testlibrary/JavaVM.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/testlibrary/JavaVM.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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
@@ -41,6 +41,8 @@
     private OutputStream outputStream = System.out;
     private OutputStream errorStream = System.err;
     private String policyFileName = null;
+    private StreamPipe outPipe;
+    private StreamPipe errPipe;
 
     private static void mesg(Object mesg) {
         System.err.println("JAVAVM: " + mesg.toString());
@@ -145,13 +147,12 @@
         }
 
         mesg("command = " + Arrays.asList(javaCommand).toString());
-        System.err.println("");
 
         vm = Runtime.getRuntime().exec(javaCommand);
 
         /* output from the execed process may optionally be captured. */
-        StreamPipe.plugTogether(vm.getInputStream(), this.outputStream);
-        StreamPipe.plugTogether(vm.getErrorStream(), this.errorStream);
+        outPipe = StreamPipe.plugTogether(vm.getInputStream(), this.outputStream);
+        errPipe = StreamPipe.plugTogether(vm.getErrorStream(), this.errorStream);
     }
 
     public void destroy() {
@@ -161,7 +162,25 @@
         vm = null;
     }
 
-    protected Process getVM() {
-        return vm;
+    /**
+     * Waits for the subprocess to exit, joins the pipe threads to ensure that
+     * all output is collected, and returns its exit status.
+     */
+    public int waitFor() throws InterruptedException {
+        if (vm == null)
+            throw new IllegalStateException("can't wait for JavaVM that hasn't started");
+
+        int status = vm.waitFor();
+        outPipe.join();
+        errPipe.join();
+        return status;
+    }
+
+    /**
+     * Starts the subprocess, waits for it to exit, and returns its exit status.
+     */
+    public int execute() throws IOException, InterruptedException {
+        start();
+        return waitFor();
     }
 }
--- a/jdk/test/java/rmi/testlibrary/RMID.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/testlibrary/RMID.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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
@@ -202,8 +202,6 @@
 
     public void start(long waitTime) throws IOException {
 
-        if (getVM() != null) return;
-
         // if rmid is already running, then the test will fail with
         // a well recognized exception (port already in use...).
 
--- a/jdk/test/java/rmi/transport/checkFQDN/CheckFQDN.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/transport/checkFQDN/CheckFQDN.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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
@@ -114,6 +114,7 @@
                 equal = "=";
             }
 
+            // create a client to tell checkFQDN what its rmi name is.
             JavaVM jvm = new JavaVM("CheckFQDNClient",
                                     propOption + property +
                                     equal +
@@ -125,10 +126,7 @@
             propertyBeingTested=property;
             propertyBeingTestedValue=propertyValue;
 
-            // create a client to tell checkFQDN what its rmi name is. */
-            jvm.start();
-
-            if (jvm.getVM().waitFor() != 0 ) {
+            if (jvm.execute() != 0) {
                 TestLibrary.bomb("Test failed, error in client.");
             }
 
--- a/jdk/test/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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
@@ -102,9 +102,8 @@
                                         " -Drmi.registry.port=" +
                                         registryPort,
                                         "");
-                jvm.start();
 
-                if (jvm.getVM().waitFor() == 1 ) {
+                if (jvm.execute() != 0) {
                     TestLibrary.bomb("Client process failed");
                 }
             }
--- a/jdk/test/sun/rmi/runtime/Log/4504153/Test4504153.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/rmi/runtime/Log/4504153/Test4504153.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. 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
@@ -48,8 +48,7 @@
         ByteArrayOutputStream err = new ByteArrayOutputStream();
         JavaVM vm = new JavaVM(StartRegistry.class.getName(),
                                "-Dsun.rmi.transport.logLevel=v", "", out, err);
-        vm.start();
-        vm.getVM().waitFor();
+        vm.execute();
 
         String errString = err.toString();
 
--- a/jdk/test/sun/rmi/runtime/Log/6409194/NoConsoleOutput.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/rmi/runtime/Log/6409194/NoConsoleOutput.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2013, Oracle and/or its affiliates. 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
@@ -65,8 +65,7 @@
         JavaVM vm = new JavaVM(DoRMIStuff.class.getName(),
             "-Djava.util.logging.config.file=" + loggingPropertiesFile,
                                "", out, err);
-        vm.start();
-        vm.getVM().waitFor();
+        vm.execute();
 
         /*
          * Verify that the subprocess had no System.out or System.err
--- a/jdk/test/sun/rmi/transport/tcp/DeadCachedConnection.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/rmi/transport/tcp/DeadCachedConnection.java	Tue Jan 08 13:06:22 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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
@@ -104,7 +104,7 @@
             JavaVM jvm =
                 new JavaVM("sun.rmi.registry.RegistryImpl", "", Integer.toString(p));
             jvm.start();
-            DeadCachedConnection.subreg = jvm.getVM();
+            DeadCachedConnection.subreg = jvm;
 
         } catch (IOException e) {
             // one of these is summarily dropped, can't remember which one
@@ -117,7 +117,7 @@
         } catch (Exception whatever) {
         }
     }
-    private static Process subreg = null;
+    private static JavaVM subreg = null;
 
     public static void killRegistry() {
         if (DeadCachedConnection.subreg != null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/ServiceCredsCombination.java	Tue Jan 08 13:06:22 2013 -0800
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8005447
+ * @compile -XDignore.symbol.file ServiceCredsCombination.java
+ * @run main ServiceCredsCombination
+ * @summary default principal can act as anyone
+ */
+
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Objects;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KeyTab;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import sun.security.jgss.GSSUtil;
+
+public class ServiceCredsCombination {
+
+    public static void main(String[] args) throws Exception {
+        // pass
+        check("a", "a", princ("a"), key("a"));
+        check(null, "a", princ("a"), key("a"));
+        check("x", "NOCRED", princ("a"), key("a"));
+        // two pass
+        check("a", "a", princ("a"), key("a"), princ("b"), key("b"));
+        check("b", "b", princ("a"), key("a"), princ("b"), key("b"));
+        check(null, null, princ("a"), key("a"), princ("b"), key("b"));
+        check("x", "NOCRED", princ("a"), key("a"), princ("b"), key("b"));
+        // old ktab
+        check("b", "b", princ("b"), oldktab());
+        check("x", "NOCRED", princ("b"), oldktab());
+        check(null, "b", princ("b"), oldktab());
+        // Two old ktab
+        check("a", "a", princ("a"), princ("b"), oldktab(), oldktab());
+        check("b", "b", princ("a"), princ("b"), oldktab(), oldktab());
+        check(null, null, princ("a"), princ("b"), oldktab(), oldktab());
+        check("x", "NOCRED", princ("a"), princ("b"), oldktab(), oldktab());
+        // pass + old ktab
+        check("a", "a", princ("a"), princ("b"), key("a"), oldktab());
+        check("b", "b", princ("a"), princ("b"), key("a"), oldktab());
+        check(null, null, princ("a"), princ("b"), key("a"), oldktab());
+        check("x", "NOCRED", princ("a"), princ("b"), key("a"), oldktab());
+        // Compatibility, automatically add princ for keys
+        check(null, "a", key("a"));
+        check("x", "NOCRED", key("a"));
+        check(null, "a", key("a"), oldktab());
+        check("x", "NOCRED", key("a"), oldktab());
+        // Limitation, "a" has no key, but we don't know oldktab() is for "b"
+        check("a", "a", princ("a"), princ("b"), oldktab());
+    }
+
+    /**
+     * Checks the correct bound
+     * @param a get a creds for this principal, null for default one
+     * @param b expected name, null for still unbound, "NOCRED" for no creds
+     * @param objs princs, keys and keytabs in the subject
+     */
+    private static void check(final String a, String b, Object... objs)
+            throws Exception {
+        Subject subj = new Subject();
+        for (Object obj: objs) {
+            if (obj instanceof KerberosPrincipal) {
+                subj.getPrincipals().add((KerberosPrincipal)obj);
+            } else if (obj instanceof KerberosKey || obj instanceof KeyTab) {
+                subj.getPrivateCredentials().add(obj);
+            }
+        }
+        final GSSManager man = GSSManager.getInstance();
+        try {
+            String result = Subject.doAs(
+                    subj, new PrivilegedExceptionAction<String>() {
+                @Override
+                public String run() throws GSSException {
+                    GSSCredential cred = man.createCredential(
+                            a == null ? null : man.createName(r(a), null),
+                            GSSCredential.INDEFINITE_LIFETIME,
+                            GSSUtil.GSS_KRB5_MECH_OID,
+                            GSSCredential.ACCEPT_ONLY);
+                    GSSName name = cred.getName();
+                    return name == null ? null : name.toString();
+                }
+            });
+            if (!Objects.equals(result, r(b))) {
+                throw new Exception("Check failed: getInstance(" + a
+                        + ") has name " + result + ", not " + b);
+            }
+        } catch (PrivilegedActionException e) {
+            if (!"NOCRED".equals(b)) {
+                throw new Exception("Check failed: getInstance(" + a
+                        + ") is null " + ", but not one with name " + b);
+            }
+        }
+    }
+    private static String r(String s) {
+        return s == null ? null : (s+"@REALM");
+    }
+    private static KerberosPrincipal princ(String s) {
+        return new KerberosPrincipal(r(s));
+    }
+    private static KerberosKey key(String s) {
+        return new KerberosKey(princ(s), new byte[0], 0, 0);
+    }
+    private static KeyTab oldktab() {
+        return KeyTab.getInstance();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/AcceptPermissions.java	Tue Jan 08 13:06:22 2013 -0800
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 9999999
+ * @summary default principal can act as anyone
+ * @compile -XDignore.symbol.file AcceptPermissions.java
+ * @run main/othervm AcceptPermissions
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.security.Permission;
+import javax.security.auth.kerberos.ServicePermission;
+import sun.security.jgss.GSSUtil;
+import java.util.*;
+
+public class AcceptPermissions extends SecurityManager {
+
+    private static Map<Permission,String> perms = new HashMap<>();
+    @Override
+    public void checkPermission(Permission perm) {
+        if (!(perm instanceof ServicePermission)) {
+            return;
+        }
+        ServicePermission sp = (ServicePermission)perm;
+        if (!sp.getActions().equals("accept")) {
+            return;
+        }
+        // We only care about accept ServicePermission in this test
+        try {
+            super.checkPermission(sp);
+        } catch (SecurityException se) {
+            if (perms.containsKey(sp)) {
+                perms.put(sp, "checked");
+            } else {
+                throw se;   // We didn't expect this is needed
+            }
+        }
+    }
+
+    // Fills in permissions we are expecting
+    private static void initPerms(String... names) {
+        perms.clear();
+        for (String name: names) {
+            perms.put(new ServicePermission(
+                    name + "@" + OneKDC.REALM, "accept"), "expected");
+        }
+    }
+
+    // Checks if they are all checked
+    private static void checkPerms() {
+        for (Map.Entry<Permission,String> entry: perms.entrySet()) {
+            if (entry.getValue().equals("expected")) {
+                throw new RuntimeException(
+                        "Expected but not used: " + entry.getKey());
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.setSecurityManager(new AcceptPermissions());
+        new OneKDC(null).writeJAASConf();
+        String two = "two {\n"
+                + " com.sun.security.auth.module.Krb5LoginModule required"
+                + "     principal=\"" + OneKDC.SERVER + "\" useKeyTab=true"
+                + "     isInitiator=false storeKey=true;\n"
+                + " com.sun.security.auth.module.Krb5LoginModule required"
+                + "     principal=\"" + OneKDC.BACKEND + "\" useKeyTab=true"
+                + "     isInitiator=false storeKey=true;\n"
+                + "};\n";
+        Files.write(Paths.get(OneKDC.JAAS_CONF), two.getBytes(),
+                StandardOpenOption.APPEND);
+
+        Context c, s;
+
+        // In all cases, a ServicePermission on the acceptor name is needed
+        // for a handshake. For default principal with no predictable name,
+        // permission not needed (yet) for credentials creation.
+
+        // Named principal
+        initPerms(OneKDC.SERVER);
+        c = Context.fromJAAS("client");
+        s = Context.fromJAAS("server");
+        c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+        s.startAsServer(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+        checkPerms();
+        initPerms(OneKDC.SERVER);
+        Context.handshake(c, s);
+        checkPerms();
+
+        // Named principal (even if there are 2 JAAS modules)
+        initPerms(OneKDC.SERVER);
+        c = Context.fromJAAS("client");
+        s = Context.fromJAAS("two");
+        c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+        s.startAsServer(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+        checkPerms();
+        initPerms(OneKDC.SERVER);
+        Context.handshake(c, s);
+        checkPerms();
+
+        // Default principal with a predictable name
+        initPerms(OneKDC.SERVER);
+        c = Context.fromJAAS("client");
+        s = Context.fromJAAS("server");
+        c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+        s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+        checkPerms();
+        initPerms(OneKDC.SERVER);
+        Context.handshake(c, s);
+        checkPerms();
+
+        // Default principal with no predictable name
+        initPerms();    // permission not needed for cred !!!
+        c = Context.fromJAAS("client");
+        s = Context.fromJAAS("two");
+        c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+        s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+        checkPerms();
+        initPerms(OneKDC.SERVER);   // still needed for handshake !!!
+        Context.handshake(c, s);
+        checkPerms();
+    }
+}
--- a/jdk/test/sun/security/krb5/auto/CleanState.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/CleanState.java	Tue Jan 08 13:06:22 2013 -0800
@@ -24,6 +24,7 @@
 /*
  * @test
  * @bug 6716534
+ * @compile -XDignore.symbol.file CleanState.java
  * @run main/othervm CleanState
  * @summary Krb5LoginModule has not cleaned temp info between authentication attempts
  */
--- a/jdk/test/sun/security/krb5/auto/Context.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/Context.java	Tue Jan 08 13:06:22 2013 -0800
@@ -131,21 +131,24 @@
         return out;
     }
 
+    /**
+     * Logins with username/password as a new Subject
+     */
     public static Context fromUserPass(
             String user, char[] pass, boolean storeKey) throws Exception {
-        return fromUserPass(null, user, pass, storeKey);
+        return fromUserPass(new Subject(), user, pass, storeKey);
     }
 
     /**
-     * Logins with a username and a password, using Krb5LoginModule directly
-     * @param s existing subject, test multiple princ & creds for single subj
-     * @param storeKey true if key should be saved, used on acceptor side
+     * Logins with username/password as an existing Subject. The
+     * same subject can be used multiple times to simulate multiple logins.
+     * @param s existing subject
      */
     public static Context fromUserPass(Subject s,
             String user, char[] pass, boolean storeKey) throws Exception {
         Context out = new Context();
         out.name = user;
-        out.s = s == null ? new Subject() : s;
+        out.s = s;
         Krb5LoginModule krb5 = new Krb5LoginModule();
         Map<String, String> map = new HashMap<>();
         Map<String, Object> shared = new HashMap<>();
@@ -172,14 +175,23 @@
     }
 
     /**
-     * Logins with a username and a keytab, using Krb5LoginModule directly
-     * @param storeKey true if key should be saved, used on acceptor side
+     * Logins with username/keytab as an existing Subject. The
+     * same subject can be used multiple times to simulate multiple logins.
+     * @param s existing subject
      */
-    public static Context fromUserKtab(String user, String ktab, boolean storeKey)
-            throws Exception {
+    public static Context fromUserKtab(
+            String user, String ktab, boolean storeKey) throws Exception {
+        return fromUserKtab(new Subject(), user, ktab, storeKey);
+    }
+
+    /**
+     * Logins with username/keytab as a new subject,
+     */
+    public static Context fromUserKtab(Subject s,
+            String user, String ktab, boolean storeKey) throws Exception {
         Context out = new Context();
         out.name = user;
-        out.s = new Subject();
+        out.s = s;
         Krb5LoginModule krb5 = new Krb5LoginModule();
         Map<String, String> map = new HashMap<>();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/DiffNameSameKey.java	Tue Jan 08 13:06:22 2013 -0800
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8005447
+ * @summary default principal can act as anyone
+ * @compile -XDignore.symbol.file DiffNameSameKey.java
+ * @run main/othervm/fail DiffNameSameKey a
+ * @run main/othervm DiffNameSameKey b
+ */
+
+import sun.security.jgss.GSSUtil;
+import sun.security.krb5.PrincipalName;
+
+/**
+ * This test confirms the compatibility codes described in
+ * ServiceCreds.getEKeys(). If the acceptor starts as x.us.oracle.com
+ * but client requests for x.us, as long as the KDC supports both names
+ * and the keys are the same, the auth should succeed.
+ */
+public class DiffNameSameKey {
+
+    static final String SERVER2 = "x" + OneKDC.SERVER;
+
+    public static void main(String[] args) throws Exception {
+
+        OneKDC kdc = new KDC2();
+        kdc.addPrincipal(SERVER2, "samepass".toCharArray());
+        kdc.addPrincipal(OneKDC.SERVER, "samepass".toCharArray());
+        kdc.writeJAASConf();
+        kdc.writeKtab(OneKDC.KTAB);
+
+        Context c, s;
+        c = Context.fromJAAS("client");
+        s = Context.fromJAAS("server");
+
+        switch (args[0]) {
+            case "a":   // If server starts as another service, should fail
+                c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_SPNEGO_MECH_OID);
+                s.startAsServer(SERVER2.replace('/', '@'),
+                        GSSUtil.GSS_SPNEGO_MECH_OID);
+                break;
+            case "b":   // If client requests another server with the same keys,
+                        // succeed to be compatible
+                c.startAsClient(SERVER2, GSSUtil.GSS_SPNEGO_MECH_OID);
+                s.startAsServer(OneKDC.SERVER.replace('/', '@'),
+                        GSSUtil.GSS_SPNEGO_MECH_OID);
+                break;
+        }
+
+        Context.handshake(c, s);
+
+        s.dispose();
+        c.dispose();
+    }
+
+    /**
+     * This KDC returns the same salt for all principals. This means same
+     * passwords generate same keys.
+     */
+    static class KDC2 extends OneKDC {
+        KDC2() throws Exception {
+            super(null);
+        }
+        @Override
+        public String getSalt(PrincipalName pn) {
+            return "SAME";
+        }
+    }
+}
--- a/jdk/test/sun/security/krb5/auto/DynamicKeytab.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/DynamicKeytab.java	Tue Jan 08 13:06:22 2013 -0800
@@ -24,6 +24,7 @@
 /*
  * @test
  * @bug 6894072
+ * @compile -XDignore.symbol.file DynamicKeytab.java
  * @run main/othervm DynamicKeytab
  * @summary always refresh keytab
  */
--- a/jdk/test/sun/security/krb5/auto/KDC.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/KDC.java	Tue Jan 08 13:06:22 2013 -0800
@@ -285,10 +285,12 @@
             if (Character.isDigit(pass[pass.length-1])) {
                 kvno = pass[pass.length-1] - '0';
             }
-            ktab.addEntry(new PrincipalName(name,
-                    name.indexOf('/') < 0 ?
-                        PrincipalName.KRB_NT_UNKNOWN :
-                        PrincipalName.KRB_NT_SRV_HST),
+            PrincipalName pn = new PrincipalName(name,
+                        name.indexOf('/') < 0 ?
+                            PrincipalName.KRB_NT_UNKNOWN :
+                            PrincipalName.KRB_NT_SRV_HST);
+            ktab.addEntry(pn,
+                        getSalt(pn),
                         pass,
                         kvno,
                         true);
@@ -534,7 +536,7 @@
         if (pass == null) {
             throw new KrbException(server?
                 Krb5.KDC_ERR_S_PRINCIPAL_UNKNOWN:
-                Krb5.KDC_ERR_C_PRINCIPAL_UNKNOWN);
+                Krb5.KDC_ERR_C_PRINCIPAL_UNKNOWN, pn.toString());
         }
         return pass;
     }
@@ -544,7 +546,7 @@
      * @param p principal
      * @return the salt
      */
-    private String getSalt(PrincipalName p) {
+    protected String getSalt(PrincipalName p) {
         String pn = p.toString();
         if (p.getRealmString() == null) {
             pn = pn + "@" + getRealm();
--- a/jdk/test/sun/security/krb5/auto/KeyTabCompat.java	Tue Jan 08 11:55:21 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/KeyTabCompat.java	Tue Jan 08 13:06:22 2013 -0800
@@ -38,7 +38,7 @@
  *
  * 1. If there is only KerberosKeys in private credential set and no
  *    KerberosPrincipal. JAAS login should go on.
- * 2. Even if KeyTab is used, user can still get KerberosKeys from
+ * 2. If KeyTab is used, user won't get KerberosKeys from
  *    private credentials set.
  */
 public class KeyTabCompat {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/TwoOrThree.java	Tue Jan 08 13:06:22 2013 -0800
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8005447
+ * @summary default principal can act as anyone
+ * @compile -XDignore.symbol.file TwoOrThree.java
+ * @run main/othervm TwoOrThree first first
+ * @run main/othervm/fail TwoOrThree first second
+ * @run main/othervm TwoOrThree - first
+ * @run main/othervm TwoOrThree - second
+ * @run main/othervm/fail TwoOrThree - third
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import javax.security.auth.Subject;
+import sun.security.jgss.GSSUtil;
+
+/*
+ * The JAAS login has two krb5 modules
+ *   1. principal is A
+ *   2. principal is B
+ * A named principal can only accept itself. The default principal can accept
+ * either, but not any other service even if the keytab also include its keys.
+ */
+public class TwoOrThree {
+
+    public static void main(String[] args) throws Exception {
+
+        String server = args[0].equals("-") ? null : args[0];
+        String target = args[1];
+        OneKDC kdc = new OneKDC(null);
+        kdc.addPrincipal("first", "first".toCharArray());
+        kdc.addPrincipal("second", "second".toCharArray());
+        kdc.addPrincipal("third", "third".toCharArray());
+        kdc.writeKtab(OneKDC.KTAB);
+
+        Context c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
+
+        // Using keytabs
+        Subject sub4s = new Subject();
+        Context.fromUserKtab(sub4s, "first", OneKDC.KTAB, true);
+        Context s = Context.fromUserKtab(sub4s, "second", OneKDC.KTAB, true);
+        c.startAsClient(target, GSSUtil.GSS_KRB5_MECH_OID);
+        s.startAsServer(server, GSSUtil.GSS_KRB5_MECH_OID);
+        Context.handshake(c, s);
+
+        // Using keys
+        sub4s = new Subject();
+        Context.fromUserPass(sub4s, "first", "first".toCharArray(), true);
+        s = Context.fromUserPass(sub4s, "second", "second".toCharArray(), true);
+        c.startAsClient(target, GSSUtil.GSS_KRB5_MECH_OID);
+        s.startAsServer(server, GSSUtil.GSS_KRB5_MECH_OID);
+        Context.handshake(c, s);
+
+        s.dispose();
+        c.dispose();
+    }
+}