diff --git a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
index c9499f1d1a0f528ed61cebc8ba95c1e2f60ce427..5da153d72ea1c4783746e30ae8ac972f771f63e8 100644
--- a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
+++ b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, 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
@@ -395,7 +395,13 @@ public class Krb5LoginModule implements LoginModule {
private boolean succeeded = false;
private boolean commitSucceeded = false;
private String username;
+
+ // Encryption keys calculated from password. Assigned when storekey == true
+ // and useKeyTab == false (or true but not found)
private EncryptionKey[] encKeys = null;
+
+ KeyTab ktab = null;
+
private Credentials cred = null;
private PrincipalName principal = null;
@@ -663,28 +669,49 @@ public class Krb5LoginModule implements LoginModule {
(krb5PrincName.toString(),
PrincipalName.KRB_NT_PRINCIPAL);
}
+
+ /*
+ * Before dynamic KeyTab support (6894072), here we check if
+ * the keytab contains keys for the principal. If no, keytab
+ * will not be used and password is prompted for.
+ *
+ * After 6894072, we normally don't check it, and expect the
+ * keys can be populated until a real connection is made. The
+ * check is still done when isInitiator == true, where the keys
+ * will be used right now.
+ *
+ * Probably tricky relations:
+ *
+ * useKeyTab is config flag, but when it's true but the ktab
+ * does not contains keys for principal, we would use password
+ * and keep the flag unchanged (for reuse?). In this method,
+ * we use (ktab != null) to check whether keytab is used.
+ * After this method (and when storeKey == true), we use
+ * (encKeys == null) to check.
+ */
if (useKeyTab) {
- encKeys =
- EncryptionKey.acquireSecretKeys(principal, keyTabName);
-
- if (debug) {
- if (encKeys != null)
- System.out.println
- ("principal's key obtained from the keytab");
- else
- System.out.println
- ("Key for the principal " +
- principal +
- " not available in " +
- ((keyTabName == null) ?
- "default key tab" : keyTabName));
+ ktab = (keyTabName == null)
+ ? KeyTab.getInstance()
+ : KeyTab.getInstance(new File(keyTabName));
+ if (isInitiator) {
+ if (Krb5Util.keysFromJavaxKeyTab(ktab, principal).length
+ == 0) {
+ ktab = null;
+ if (debug) {
+ System.out.println
+ ("Key for the principal " +
+ principal +
+ " not available in " +
+ ((keyTabName == null) ?
+ "default key tab" : keyTabName));
+ }
+ }
}
-
}
KrbAsReqBuilder builder;
- // We can't get the key from the keytab so prompt
- if (encKeys == null) {
+
+ if (ktab == null) {
promptForPass(getPasswdFromSharedState);
builder = new KrbAsReqBuilder(principal, password);
if (isInitiator) {
@@ -693,9 +720,13 @@ public class Krb5LoginModule implements LoginModule {
// updated with PA info
cred = builder.action().getCreds();
}
- encKeys = builder.getKeys();
+ if (storeKey) {
+ encKeys = builder.getKeys();
+ // When encKeys is empty, the login actually fails.
+ // For compatibility, exception is thrown in commit().
+ }
} else {
- builder = new KrbAsReqBuilder(principal, encKeys);
+ builder = new KrbAsReqBuilder(principal, ktab);
if (isInitiator) {
cred = builder.action().getCreds();
}
@@ -705,10 +736,15 @@ public class Krb5LoginModule implements LoginModule {
if (debug) {
System.out.println("principal is " + principal);
HexDumpEncoder hd = new HexDumpEncoder();
- for (int i = 0; i < encKeys.length; i++) {
- System.out.println("EncryptionKey: keyType=" +
- encKeys[i].getEType() + " keyBytes (hex dump)=" +
- hd.encodeBuffer(encKeys[i].getBytes()));
+ if (ktab != null) {
+ System.out.println("Will use keytab");
+ } else if (storeKey) {
+ for (int i = 0; i < encKeys.length; i++) {
+ System.out.println("EncryptionKey: keyType=" +
+ encKeys[i].getEType() +
+ " keyBytes (hex dump)=" +
+ hd.encodeBuffer(encKeys[i].getBytes()));
+ }
}
}
@@ -989,8 +1025,8 @@ public class Krb5LoginModule implements LoginModule {
kerbTicket = Krb5Util.credsToTicket(cred);
}
- if (storeKey) {
- if (encKeys == null || encKeys.length <= 0) {
+ if (storeKey && encKeys != null) {
+ if (encKeys.length == 0) {
succeeded = false;
throw new LoginException("Null Server Key ");
}
@@ -1006,10 +1042,11 @@ public class Krb5LoginModule implements LoginModule {
}
}
- // Let us add the kerbClientPrinc,kerbTicket and kerbKey (if
+ // Let us add the kerbClientPrinc,kerbTicket and KeyTab/KerbKey (if
// storeKey is true)
- if (!princSet.contains(kerbClientPrinc))
+ if (!princSet.contains(kerbClientPrinc)) {
princSet.add(kerbClientPrinc);
+ }
// add the TGT
if (kerbTicket != null) {
@@ -1018,19 +1055,29 @@ public class Krb5LoginModule implements LoginModule {
}
if (storeKey) {
- for (int i = 0; i < kerbKeys.length; i++) {
- if (!privCredSet.contains(kerbKeys[i])) {
- privCredSet.add(kerbKeys[i]);
+ if (encKeys == null) {
+ if (!privCredSet.contains(ktab)) {
+ privCredSet.add(ktab);
+ // Compatibility; also add keys to privCredSet
+ for (KerberosKey key: ktab.getKeys(kerbClientPrinc)) {
+ privCredSet.add(new Krb5Util.KeysFromKeyTab(key));
+ }
}
- encKeys[i].destroy();
- encKeys[i] = null;
- if (debug) {
- System.out.println("Added server's key"
- + kerbKeys[i]);
- System.out.println("\t\t[Krb5LoginModule] " +
- "added Krb5Principal " +
- kerbClientPrinc.toString()
- + " to Subject");
+ } else {
+ for (int i = 0; i < kerbKeys.length; i ++) {
+ if (!privCredSet.contains(kerbKeys[i])) {
+ privCredSet.add(kerbKeys[i]);
+ }
+ encKeys[i].destroy();
+ encKeys[i] = null;
+ if (debug) {
+ System.out.println("Added server's key"
+ + kerbKeys[i]);
+ System.out.println("\t\t[Krb5LoginModule] " +
+ "added Krb5Principal " +
+ kerbClientPrinc.toString()
+ + " to Subject");
+ }
}
}
}
@@ -1106,7 +1153,8 @@ public class Krb5LoginModule implements LoginModule {
while (it.hasNext()) {
Object o = it.next();
if (o instanceof KerberosTicket ||
- o instanceof KerberosKey) {
+ o instanceof KerberosKey ||
+ o instanceof KeyTab) {
it.remove();
}
}
@@ -1161,6 +1209,7 @@ public class Krb5LoginModule implements LoginModule {
} else {
// remove temp results for the next try
encKeys = null;
+ ktab = null;
principal = null;
}
username = null;
diff --git a/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..56ed40eed3e391fd8897d1d5c360076bce1d805a
--- /dev/null
+++ b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011, 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 javax.security.auth.kerberos;
+
+import sun.misc.JavaxSecurityAuthKerberosAccess;
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.PrincipalName;
+
+class JavaxSecurityAuthKerberosAccessImpl
+ implements JavaxSecurityAuthKerberosAccess {
+ public EncryptionKey[] keyTabGetEncryptionKeys(
+ KeyTab ktab, PrincipalName principal) {
+ return ktab.getEncryptionKeys(principal);
+ }
+}
diff --git a/src/share/classes/javax/security/auth/kerberos/KerberosKey.java b/src/share/classes/javax/security/auth/kerberos/KerberosKey.java
index 7eb7118a5bcc7cb889e505f0b989a45a4bb33099..ba530f9583c849a69d08527206814f648de91561 100644
--- a/src/share/classes/javax/security/auth/kerberos/KerberosKey.java
+++ b/src/share/classes/javax/security/auth/kerberos/KerberosKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, 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,14 +35,16 @@ import javax.security.auth.DestroyFailedException;
* principal.
*
* All Kerberos JAAS login modules that obtain a principal's password and
- * generate the secret key from it should use this class. Where available,
- * the login module might even read this secret key directly from a
- * Kerberos "keytab". Sometimes, such as when authenticating a server in
+ * generate the secret key from it should use this class.
+ * Sometimes, such as when authenticating a server in
* the absence of user-to-user authentication, the login module will store
* an instance of this class in the private credential set of a
* {@link javax.security.auth.Subject Subject} during the commit phase of the
* authentication process.
*
+ * A Kerberos service using a keytab to read secret keys should use
+ * the {@link KeyTab} class, where latest keys can be read when needed.
+ *
* It might be necessary for the application to be granted a
* {@link javax.security.auth.PrivateCredentialPermission
* PrivateCredentialPermission} if it needs to access the KerberosKey
diff --git a/src/share/classes/javax/security/auth/kerberos/KeyTab.java b/src/share/classes/javax/security/auth/kerberos/KeyTab.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2f8dd79478baccae14b76c19a02033ddb0a9b7c
--- /dev/null
+++ b/src/share/classes/javax/security/auth/kerberos/KeyTab.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2011, 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 javax.security.auth.kerberos;
+
+import java.io.File;
+import java.util.Objects;
+import sun.misc.SharedSecrets;
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.RealmException;
+
+/**
+ * This class encapsulates a keytab file.
+ *
+ * A Kerberos JAAS login module that obtains long term secret keys from a
+ * keytab file should use this class. The login module will store
+ * an instance of this class in the private credential set of a
+ * {@link javax.security.auth.Subject Subject} during the commit phase of the
+ * authentication process.
+ *
+ * It might be necessary for the application to be granted a
+ * {@link javax.security.auth.PrivateCredentialPermission
+ * PrivateCredentialPermission} if it needs to access the KeyTab
+ * instance from a Subject. This permission is not needed when the
+ * application depends on the default JGSS Kerberos mechanism to access the
+ * KeyTab. In that case, however, the application will need an appropriate
+ * {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.
+ *
+ * The keytab file format is described at
+ *
+ * http://www.ioplex.com/utilities/keytab.txt.
+ *
+ * @since 1.7
+ */
+public final class KeyTab {
+
+ /*
+ * Impl notes:
+ *
+ * This class is only a name, a permanent link to the keytab source
+ * (can be missing). Itself has no content. In order to read content,
+ * take a snapshot and read from it.
+ *
+ * The snapshot is of type sun.security.krb5.internal.ktab.KeyTab, which
+ * contains the content of the keytab file when the snapshot is taken.
+ * Itself has no refresh function and mostly an immutable class (except
+ * for the create/add/save methods only used by the ktab command).
+ */
+
+ // Source, null if using the default one. Note that the default name
+ // is maintained in snapshot, this field is never "resolved".
+ private final File file;
+
+ // Set up JavaxSecurityAuthKerberosAccess in SharedSecrets
+ static {
+ SharedSecrets.setJavaxSecurityAuthKerberosAccess(
+ new JavaxSecurityAuthKerberosAccessImpl());
+ }
+
+ private KeyTab(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Returns a {@code KeyTab} instance from a {@code File} object.
+ *
+ * The result of this method is never null. This method only associates
+ * the returned {@code KeyTab} object with the file and does not read it.
+ * @param file the keytab {@code File} object, must not be null
+ * @return the keytab instance
+ * @throws NullPointerException if the {@code file} argument is null
+ */
+ public static KeyTab getInstance(File file) {
+ if (file == null) {
+ throw new NullPointerException("file must be non null");
+ }
+ return new KeyTab(file);
+ }
+
+ /**
+ * Returns the default {@code KeyTab} instance.
+ *
+ * The result of this method is never null. This method only associates
+ * the returned {@code KeyTab} object with the default keytab file and
+ * does not read it.
+ * @return the default keytab instance.
+ */
+ public static KeyTab getInstance() {
+ return new KeyTab(null);
+ }
+
+ //Takes a snapshot of the keytab content
+ private sun.security.krb5.internal.ktab.KeyTab takeSnapshot() {
+ return sun.security.krb5.internal.ktab.KeyTab.getInstance(file);
+ }
+
+ /**
+ * Returns fresh keys for the given Kerberos principal.
+ *
+ * Implementation of this method should make sure the returned keys match
+ * the latest content of the keytab file. The result is a newly created
+ * copy that can be modified by the caller without modifying the keytab
+ * object. The caller should {@link KerberosKey#destroy() destroy} the
+ * result keys after they are used.
+ *
+ * Please note that the keytab file can be created after the
+ * {@code KeyTab} object is instantiated and its content may change over
+ * time. Therefore, an application should call this method only when it
+ * needs to use the keys. Any previous result from an earlier invocation
+ * could potentially be expired.
+ *
+ * If there is any error (say, I/O error or format error)
+ * during the reading process of the KeyTab file, a saved result should be
+ * returned. If there is no saved result (say, this is the first time this
+ * method is called, or, all previous read attempts failed), an empty array
+ * should be returned. This can make sure the result is not drastically
+ * changed during the (probably slow) update of the keytab file.
+ *
+ * Each time this method is called and the reading of the file succeeds
+ * with no exception (say, I/O error or file format error),
+ * the result should be saved for {@code principal}. The implementation can
+ * also save keys for other principals having keys in the same keytab object
+ * if convenient.
+ *
+ * Any unsupported key read from the keytab is ignored and not included
+ * in the result.
+ *
+ * @param principal the Kerberos principal, must not be null.
+ * @return the keys (never null, may be empty)
+ * @throws NullPointerException if the {@code principal}
+ * argument is null
+ * @throws SecurityException if a security manager exists and the read
+ * access to the keytab file is not permitted
+ */
+ public KerberosKey[] getKeys(KerberosPrincipal principal) {
+ try {
+ EncryptionKey[] keys = takeSnapshot().readServiceKeys(
+ new PrincipalName(principal.getName()));
+ KerberosKey[] kks = new KerberosKey[keys.length];
+ for (int i=0; i
+ * The caller can use the result to determine if it should fallback to
+ * another mechanism to read the keys.
+ * @return true if the keytab file exists; false otherwise.
+ * @throws SecurityException if a security manager exists and the read
+ * access to the keytab file is not permitted
+ */
+ public boolean exists() {
+ return !takeSnapshot().isMissing();
+ }
+
+ public String toString() {
+ return file == null ? "Default keytab" : file.toString();
+ }
+
+ /**
+ * Returns a hashcode for this KeyTab.
+ *
+ * @return a hashCode() for the KeyTab
+ */
+ public int hashCode() {
+ return Objects.hash(file);
+ }
+
+ /**
+ * Compares the specified Object with this KeyTab for equality.
+ * Returns true if the given object is also a
+ * KeyTab and the two
+ * KeyTab instances are equivalent.
+ *
+ * @param other the Object to compare to
+ * @return true if the specified object is equal to this KeyTab
+ */
+ public boolean equals(Object other) {
+ if (other == this)
+ return true;
+
+ if (! (other instanceof KeyTab)) {
+ return false;
+ }
+
+ KeyTab otherKtab = (KeyTab) other;
+ return Objects.equals(otherKtab.file, file);
+ }
+}
diff --git a/src/share/classes/sun/misc/JavaxSecurityAuthKerberosAccess.java b/src/share/classes/sun/misc/JavaxSecurityAuthKerberosAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c8f9f2d3d20a8e66f27db2e8a61bac0606010ca
--- /dev/null
+++ b/src/share/classes/sun/misc/JavaxSecurityAuthKerberosAccess.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011, 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.misc;
+
+import javax.security.auth.kerberos.KeyTab;
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.PrincipalName;
+
+/**
+ * An unsafe tunnel to get non-public access to classes in the
+ * javax.security.auth.kerberos package.
+ */
+public interface JavaxSecurityAuthKerberosAccess {
+ /**
+ * Returns keys for a principal in a keytab.
+ * @return the keys, never null, can be empty.
+ */
+ public EncryptionKey[] keyTabGetEncryptionKeys(
+ KeyTab ktab, PrincipalName principal);
+}
diff --git a/src/share/classes/sun/misc/SharedSecrets.java b/src/share/classes/sun/misc/SharedSecrets.java
index 0bd39b4a5d42f0b12478143bad17dce21db3ba11..969da9a744fe8dfadf558e8e220f1856fc5c52f5 100644
--- a/src/share/classes/sun/misc/SharedSecrets.java
+++ b/src/share/classes/sun/misc/SharedSecrets.java
@@ -29,6 +29,7 @@ import java.util.jar.JarFile;
import java.io.Console;
import java.io.FileDescriptor;
import java.security.ProtectionDomain;
+import javax.security.auth.kerberos.KeyTab;
import java.security.AccessController;
@@ -51,6 +52,7 @@ public class SharedSecrets {
private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess;
private static JavaSecurityAccess javaSecurityAccess;
+ private static JavaxSecurityAuthKerberosAccess javaxSecurityAuthKerberosAccess;
public static JavaUtilJarAccess javaUtilJarAccess() {
if (javaUtilJarAccess == null) {
@@ -139,4 +141,16 @@ public class SharedSecrets {
}
return javaSecurityAccess;
}
+
+ public static void setJavaxSecurityAuthKerberosAccess
+ (JavaxSecurityAuthKerberosAccess jsaka) {
+ javaxSecurityAuthKerberosAccess = jsaka;
+ }
+
+ public static JavaxSecurityAuthKerberosAccess
+ getJavaxSecurityAuthKerberosAccess() {
+ if (javaxSecurityAuthKerberosAccess == null)
+ unsafe.ensureClassInitialized(KeyTab.class);
+ return javaxSecurityAuthKerberosAccess;
+ }
}
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java b/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
index 7a7156d9b1cad0b1b093e8c1ed3cb9f9c65d8e38..1df500bc5242a8bd217bb818a499c4659b1efde6 100644
--- a/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, 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
@@ -29,7 +29,6 @@ import org.ietf.jgss.*;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import sun.security.krb5.*;
-import javax.security.auth.kerberos.*;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.AccessController;
@@ -43,40 +42,23 @@ import javax.security.auth.DestroyFailedException;
* @since 1.4
*/
public class Krb5AcceptCredential
- extends KerberosKey
implements Krb5CredElement {
private static final long serialVersionUID = 7714332137352567952L;
private Krb5NameElement name;
- /**
- * We cache an EncryptionKey representation of this key because many
- * Krb5 operation require a key in that form. At some point we might do
- * away with EncryptionKey altogether and use the base class
- * KerberosKey everywhere.
- */
- private EncryptionKey[] krb5EncryptionKeys;
+ private Krb5Util.ServiceCreds screds;
- private Krb5AcceptCredential(Krb5NameElement name, KerberosKey[] keys) {
+ private Krb5AcceptCredential(Krb5NameElement name, Krb5Util.ServiceCreds creds) {
/*
* Initialize this instance with the data from the acquired
* KerberosKey. This class needs to be a KerberosKey too
* hence we can't just store a reference.
*/
- super(keys[0].getPrincipal(),
- keys[0].getEncoded(),
- keys[0].getKeyType(),
- keys[0].getVersionNumber());
this.name = name;
- // Cache this for later use by the sun.security.krb5 package.
- krb5EncryptionKeys = new EncryptionKey[keys.length];
- for (int i = 0; i < keys.length; i++) {
- krb5EncryptionKeys[i] = new EncryptionKey(keys[i].getEncoded(),
- keys[i].getKeyType(),
- new Integer(keys[i].getVersionNumber()));
- }
+ this.screds = creds;
}
static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
@@ -86,12 +68,12 @@ public class Krb5AcceptCredential
name.getKrb5PrincipalName().getName());
final AccessControlContext acc = AccessController.getContext();
- KerberosKey[] keys;
+ Krb5Util.ServiceCreds creds = null;
try {
- keys = AccessController.doPrivileged(
- new PrivilegedExceptionAction() {
- public KerberosKey[] run() throws Exception {
- return Krb5Util.getKeys(
+ creds = AccessController.doPrivileged(
+ new PrivilegedExceptionAction() {
+ public Krb5Util.ServiceCreds run() throws Exception {
+ return Krb5Util.getServiceCreds(
caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
serverPrinc, acc);
}});
@@ -103,17 +85,17 @@ public class Krb5AcceptCredential
throw ge;
}
- if (keys == null || keys.length == 0)
+ if (creds == null)
throw new GSSException(GSSException.NO_CRED, -1,
- "Failed to find any Kerberos Key");
+ "Failed to find any Kerberos credentails");
if (name == null) {
- String fullName = keys[0].getPrincipal().getName();
+ String fullName = creds.getName();
name = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
- return new Krb5AcceptCredential(name, keys);
+ return new Krb5AcceptCredential(name, creds);
}
/**
@@ -171,7 +153,7 @@ public class Krb5AcceptCredential
}
EncryptionKey[] getKrb5EncryptionKeys() {
- return krb5EncryptionKeys;
+ return screds.getEKeys();
}
/**
@@ -193,13 +175,6 @@ public class Krb5AcceptCredential
* destroy in the base class.
*/
public void destroy() throws DestroyFailedException {
- if (krb5EncryptionKeys != null) {
- for (int i = 0; i < krb5EncryptionKeys.length; i++) {
- krb5EncryptionKeys[i].destroy();
- }
- krb5EncryptionKeys = null;
- }
-
- super.destroy();
+ screds.destroy();
}
}
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java
index d5b172f92497ecd41b6b2faffb50b64fc0da1356..22552a71c3fff910153721985e9b98ffe667da5a 100644
--- a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, 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
@@ -28,6 +28,7 @@ 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 javax.security.auth.login.LoginException;
import java.security.AccessControlContext;
@@ -38,7 +39,13 @@ 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.Iterator;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import sun.misc.SharedSecrets;
+import sun.security.krb5.PrincipalName;
/**
* Utilities for obtaining and converting Kerberos tickets.
*
@@ -75,7 +82,7 @@ public class Krb5Util {
// 1. Try to find service ticket in acc subject
Subject accSubj = Subject.getSubject(acc);
- KerberosTicket ticket = (KerberosTicket) SubjectComber.find(accSubj,
+ KerberosTicket ticket = SubjectComber.find(accSubj,
serverPrincipal, clientPrincipal, KerberosTicket.class);
if (ticket != null) {
@@ -87,7 +94,7 @@ public class Krb5Util {
// 2. Try to get ticket from login
try {
loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
- ticket = (KerberosTicket) SubjectComber.find(loginSubj,
+ ticket = SubjectComber.find(loginSubj,
serverPrincipal, clientPrincipal, KerberosTicket.class);
if (ticket != null) {
return ticket; // found it
@@ -102,13 +109,13 @@ public class Krb5Util {
// Try to get TGT to acquire service ticket
// 3. Try to get TGT from acc subject
- KerberosTicket tgt = (KerberosTicket) SubjectComber.find(accSubj,
+ KerberosTicket tgt = SubjectComber.find(accSubj,
tgsPrincipal, clientPrincipal, KerberosTicket.class);
boolean fromAcc;
if (tgt == null && loginSubj != null) {
// 4. Try to get TGT from login subject
- tgt = (KerberosTicket) SubjectComber.find(loginSubj,
+ tgt = SubjectComber.find(loginSubj,
tgsPrincipal, clientPrincipal, KerberosTicket.class);
fromAcc = false;
} else {
@@ -145,14 +152,14 @@ public class Krb5Util {
// Try to get ticket from acc's Subject
Subject accSubj = Subject.getSubject(acc);
- KerberosTicket ticket = (KerberosTicket)
+ KerberosTicket ticket =
SubjectComber.find(accSubj, serverPrincipal, clientPrincipal,
KerberosTicket.class);
// Try to get ticket from Subject obtained from GSSUtil
if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) {
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
- ticket = (KerberosTicket) SubjectComber.find(subject,
+ ticket = SubjectComber.find(subject,
serverPrincipal, clientPrincipal, KerberosTicket.class);
}
return ticket;
@@ -182,37 +189,152 @@ public class Krb5Util {
return subject;
}
+ // A special KerberosKey, used as keys read from a KeyTab object.
+ // Each time new keys are read from KeyTab objects in the private
+ // credentials set, old ones are removed and new ones added.
+ public static class KeysFromKeyTab extends KerberosKey {
+ public KeysFromKeyTab(KerberosKey key) {
+ super(key.getPrincipal(), key.getEncoded(),
+ key.getKeyType(), key.getVersionNumber());
+ }
+ }
+
/**
- * Retrieves the keys for the specified server principal from
- * the Subject in the specified AccessControlContext.
- * If the ticket can not be found in the Subject, and if
- * useSubjectCredsOnly is false, then obtain keys from
- * a LoginContext.
+ * 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 a user2user service. Not supported yet)
*
- * NOTE: This method is used by JSSE Kerberos Cipher Suites
+ * 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 KerberosKey[] getKeys(GSSCaller caller,
+ public static class ServiceCreds {
+ private KerberosPrincipal kp;
+ private List ktabs;
+ private List kk;
+ private Subject subj;
+ //private KerberosTicket tgt; // user2user, not supported yet
+
+ 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 keys = SubjectComber.findMany(
+ subj, null, 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);
+ if (sc.ktabs.isEmpty() && sc.kk.isEmpty()) {
+ return null;
+ }
+ return sc;
+ }
+
+ public String getName() {
+ return kp.getName();
+ }
+
+ public KerberosKey[] getKKeys() {
+ if (ktabs.isEmpty()) {
+ return kk.toArray(new KerberosKey[kk.size()]);
+ } else {
+ List keys = new ArrayList<>();
+ for (KeyTab ktab: ktabs) {
+ for (KerberosKey k: ktab.getKeys(kp)) {
+ keys.add(k);
+ }
+ }
+ // Compatibility: also add keys to privCredSet. Remove old
+ // ones first, only remove those from keytab.
+ if (!subj.isReadOnly()) {
+ Set