--- a/jdk/src/share/classes/sun/security/jgss/krb5/InitSecContextToken.java Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/InitSecContextToken.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Context.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Util.java Tue Jan 08 14:54:56 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 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/SubjectComber.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/krb5/KrbApReq.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java Tue Jan 08 14:54:56 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];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/ServiceCredsCombination.java Tue Jan 08 14:54:56 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 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/CleanState.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/Context.java Tue Jan 08 14:54:56 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 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/DynamicKeytab.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/KDC.java Tue Jan 08 14:54:56 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 Mon Jan 07 18:09:07 2013 -0800
+++ b/jdk/test/sun/security/krb5/auto/KeyTabCompat.java Tue Jan 08 14:54:56 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 14:54:56 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();
+ }
+}