6894072: always refresh keytab
Reviewed-by: valeriep
This commit is contained in:
parent
21a2cb6922
commit
68d5cd6f23
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 succeeded = false;
|
||||||
private boolean commitSucceeded = false;
|
private boolean commitSucceeded = false;
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
// Encryption keys calculated from password. Assigned when storekey == true
|
||||||
|
// and useKeyTab == false (or true but not found)
|
||||||
private EncryptionKey[] encKeys = null;
|
private EncryptionKey[] encKeys = null;
|
||||||
|
|
||||||
|
KeyTab ktab = null;
|
||||||
|
|
||||||
private Credentials cred = null;
|
private Credentials cred = null;
|
||||||
|
|
||||||
private PrincipalName principal = null;
|
private PrincipalName principal = null;
|
||||||
@ -663,28 +669,49 @@ public class Krb5LoginModule implements LoginModule {
|
|||||||
(krb5PrincName.toString(),
|
(krb5PrincName.toString(),
|
||||||
PrincipalName.KRB_NT_PRINCIPAL);
|
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) {
|
if (useKeyTab) {
|
||||||
encKeys =
|
ktab = (keyTabName == null)
|
||||||
EncryptionKey.acquireSecretKeys(principal, keyTabName);
|
? KeyTab.getInstance()
|
||||||
|
: KeyTab.getInstance(new File(keyTabName));
|
||||||
if (debug) {
|
if (isInitiator) {
|
||||||
if (encKeys != null)
|
if (Krb5Util.keysFromJavaxKeyTab(ktab, principal).length
|
||||||
System.out.println
|
== 0) {
|
||||||
("principal's key obtained from the keytab");
|
ktab = null;
|
||||||
else
|
if (debug) {
|
||||||
System.out.println
|
System.out.println
|
||||||
("Key for the principal " +
|
("Key for the principal " +
|
||||||
principal +
|
principal +
|
||||||
" not available in " +
|
" not available in " +
|
||||||
((keyTabName == null) ?
|
((keyTabName == null) ?
|
||||||
"default key tab" : keyTabName));
|
"default key tab" : keyTabName));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KrbAsReqBuilder builder;
|
KrbAsReqBuilder builder;
|
||||||
// We can't get the key from the keytab so prompt
|
|
||||||
if (encKeys == null) {
|
if (ktab == null) {
|
||||||
promptForPass(getPasswdFromSharedState);
|
promptForPass(getPasswdFromSharedState);
|
||||||
builder = new KrbAsReqBuilder(principal, password);
|
builder = new KrbAsReqBuilder(principal, password);
|
||||||
if (isInitiator) {
|
if (isInitiator) {
|
||||||
@ -693,9 +720,13 @@ public class Krb5LoginModule implements LoginModule {
|
|||||||
// updated with PA info
|
// updated with PA info
|
||||||
cred = builder.action().getCreds();
|
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 {
|
} else {
|
||||||
builder = new KrbAsReqBuilder(principal, encKeys);
|
builder = new KrbAsReqBuilder(principal, ktab);
|
||||||
if (isInitiator) {
|
if (isInitiator) {
|
||||||
cred = builder.action().getCreds();
|
cred = builder.action().getCreds();
|
||||||
}
|
}
|
||||||
@ -705,10 +736,15 @@ public class Krb5LoginModule implements LoginModule {
|
|||||||
if (debug) {
|
if (debug) {
|
||||||
System.out.println("principal is " + principal);
|
System.out.println("principal is " + principal);
|
||||||
HexDumpEncoder hd = new HexDumpEncoder();
|
HexDumpEncoder hd = new HexDumpEncoder();
|
||||||
for (int i = 0; i < encKeys.length; i++) {
|
if (ktab != null) {
|
||||||
System.out.println("EncryptionKey: keyType=" +
|
System.out.println("Will use keytab");
|
||||||
encKeys[i].getEType() + " keyBytes (hex dump)=" +
|
} else if (storeKey) {
|
||||||
hd.encodeBuffer(encKeys[i].getBytes()));
|
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);
|
kerbTicket = Krb5Util.credsToTicket(cred);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storeKey) {
|
if (storeKey && encKeys != null) {
|
||||||
if (encKeys == null || encKeys.length <= 0) {
|
if (encKeys.length == 0) {
|
||||||
succeeded = false;
|
succeeded = false;
|
||||||
throw new LoginException("Null Server Key ");
|
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)
|
// storeKey is true)
|
||||||
if (!princSet.contains(kerbClientPrinc))
|
if (!princSet.contains(kerbClientPrinc)) {
|
||||||
princSet.add(kerbClientPrinc);
|
princSet.add(kerbClientPrinc);
|
||||||
|
}
|
||||||
|
|
||||||
// add the TGT
|
// add the TGT
|
||||||
if (kerbTicket != null) {
|
if (kerbTicket != null) {
|
||||||
@ -1018,19 +1055,29 @@ public class Krb5LoginModule implements LoginModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (storeKey) {
|
if (storeKey) {
|
||||||
for (int i = 0; i < kerbKeys.length; i++) {
|
if (encKeys == null) {
|
||||||
if (!privCredSet.contains(kerbKeys[i])) {
|
if (!privCredSet.contains(ktab)) {
|
||||||
privCredSet.add(kerbKeys[i]);
|
privCredSet.add(ktab);
|
||||||
|
// Compatibility; also add keys to privCredSet
|
||||||
|
for (KerberosKey key: ktab.getKeys(kerbClientPrinc)) {
|
||||||
|
privCredSet.add(new Krb5Util.KeysFromKeyTab(key));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
encKeys[i].destroy();
|
} else {
|
||||||
encKeys[i] = null;
|
for (int i = 0; i < kerbKeys.length; i ++) {
|
||||||
if (debug) {
|
if (!privCredSet.contains(kerbKeys[i])) {
|
||||||
System.out.println("Added server's key"
|
privCredSet.add(kerbKeys[i]);
|
||||||
+ kerbKeys[i]);
|
}
|
||||||
System.out.println("\t\t[Krb5LoginModule] " +
|
encKeys[i].destroy();
|
||||||
"added Krb5Principal " +
|
encKeys[i] = null;
|
||||||
kerbClientPrinc.toString()
|
if (debug) {
|
||||||
+ " to Subject");
|
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()) {
|
while (it.hasNext()) {
|
||||||
Object o = it.next();
|
Object o = it.next();
|
||||||
if (o instanceof KerberosTicket ||
|
if (o instanceof KerberosTicket ||
|
||||||
o instanceof KerberosKey) {
|
o instanceof KerberosKey ||
|
||||||
|
o instanceof KeyTab) {
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1161,6 +1209,7 @@ public class Krb5LoginModule implements LoginModule {
|
|||||||
} else {
|
} else {
|
||||||
// remove temp results for the next try
|
// remove temp results for the next try
|
||||||
encKeys = null;
|
encKeys = null;
|
||||||
|
ktab = null;
|
||||||
principal = null;
|
principal = null;
|
||||||
}
|
}
|
||||||
username = null;
|
username = null;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,14 +35,16 @@ import javax.security.auth.DestroyFailedException;
|
|||||||
* principal.<p>
|
* principal.<p>
|
||||||
*
|
*
|
||||||
* All Kerberos JAAS login modules that obtain a principal's password and
|
* All Kerberos JAAS login modules that obtain a principal's password and
|
||||||
* generate the secret key from it should use this class. Where available,
|
* generate the secret key from it should use this class.
|
||||||
* the login module might even read this secret key directly from a
|
* Sometimes, such as when authenticating a server in
|
||||||
* Kerberos "keytab". Sometimes, such as when authenticating a server in
|
|
||||||
* the absence of user-to-user authentication, the login module will store
|
* the absence of user-to-user authentication, the login module will store
|
||||||
* an instance of this class in the private credential set of a
|
* an instance of this class in the private credential set of a
|
||||||
* {@link javax.security.auth.Subject Subject} during the commit phase of the
|
* {@link javax.security.auth.Subject Subject} during the commit phase of the
|
||||||
* authentication process.<p>
|
* authentication process.<p>
|
||||||
*
|
*
|
||||||
|
* A Kerberos service using a keytab to read secret keys should use
|
||||||
|
* the {@link KeyTab} class, where latest keys can be read when needed.<p>
|
||||||
|
*
|
||||||
* It might be necessary for the application to be granted a
|
* It might be necessary for the application to be granted a
|
||||||
* {@link javax.security.auth.PrivateCredentialPermission
|
* {@link javax.security.auth.PrivateCredentialPermission
|
||||||
* PrivateCredentialPermission} if it needs to access the KerberosKey
|
* PrivateCredentialPermission} if it needs to access the KerberosKey
|
||||||
|
230
jdk/src/share/classes/javax/security/auth/kerberos/KeyTab.java
Normal file
230
jdk/src/share/classes/javax/security/auth/kerberos/KeyTab.java
Normal file
@ -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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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}.
|
||||||
|
* <p>
|
||||||
|
* The keytab file format is described at
|
||||||
|
* <a href="http://www.ioplex.com/utilities/keytab.txt">
|
||||||
|
* http://www.ioplex.com/utilities/keytab.txt</a>.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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<kks.length; i++) {
|
||||||
|
Integer tmp = keys[i].getKeyVersionNumber();
|
||||||
|
kks[i] = new KerberosKey(
|
||||||
|
principal,
|
||||||
|
keys[i].getBytes(),
|
||||||
|
keys[i].getEType(),
|
||||||
|
tmp == null ? 0 : tmp.intValue());
|
||||||
|
keys[i].destroy();
|
||||||
|
}
|
||||||
|
return kks;
|
||||||
|
} catch (RealmException re) {
|
||||||
|
return new KerberosKey[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionKey[] getEncryptionKeys(PrincipalName principal) {
|
||||||
|
return takeSnapshot().readServiceKeys(principal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the keytab file exists. Implementation of this method
|
||||||
|
* should make sure that the result matches the latest status of the
|
||||||
|
* keytab file.
|
||||||
|
* <p>
|
||||||
|
* 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 <code>KeyTab</code>
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
* <code>KeyTab</code> and the two
|
||||||
|
* <code>KeyTab</code> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -29,6 +29,7 @@ import java.util.jar.JarFile;
|
|||||||
import java.io.Console;
|
import java.io.Console;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
|
import javax.security.auth.kerberos.KeyTab;
|
||||||
|
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
|
|
||||||
@ -51,6 +52,7 @@ public class SharedSecrets {
|
|||||||
private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
|
private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
|
||||||
private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess;
|
private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess;
|
||||||
private static JavaSecurityAccess javaSecurityAccess;
|
private static JavaSecurityAccess javaSecurityAccess;
|
||||||
|
private static JavaxSecurityAuthKerberosAccess javaxSecurityAuthKerberosAccess;
|
||||||
|
|
||||||
public static JavaUtilJarAccess javaUtilJarAccess() {
|
public static JavaUtilJarAccess javaUtilJarAccess() {
|
||||||
if (javaUtilJarAccess == null) {
|
if (javaUtilJarAccess == null) {
|
||||||
@ -139,4 +141,16 @@ public class SharedSecrets {
|
|||||||
}
|
}
|
||||||
return javaSecurityAccess;
|
return javaSecurityAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setJavaxSecurityAuthKerberosAccess
|
||||||
|
(JavaxSecurityAuthKerberosAccess jsaka) {
|
||||||
|
javaxSecurityAuthKerberosAccess = jsaka;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JavaxSecurityAuthKerberosAccess
|
||||||
|
getJavaxSecurityAuthKerberosAccess() {
|
||||||
|
if (javaxSecurityAuthKerberosAccess == null)
|
||||||
|
unsafe.ensureClassInitialized(KeyTab.class);
|
||||||
|
return javaxSecurityAuthKerberosAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.GSSCaller;
|
||||||
import sun.security.jgss.spi.*;
|
import sun.security.jgss.spi.*;
|
||||||
import sun.security.krb5.*;
|
import sun.security.krb5.*;
|
||||||
import javax.security.auth.kerberos.*;
|
|
||||||
import java.security.PrivilegedActionException;
|
import java.security.PrivilegedActionException;
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
@ -43,40 +42,23 @@ import javax.security.auth.DestroyFailedException;
|
|||||||
* @since 1.4
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
public class Krb5AcceptCredential
|
public class Krb5AcceptCredential
|
||||||
extends KerberosKey
|
|
||||||
implements Krb5CredElement {
|
implements Krb5CredElement {
|
||||||
|
|
||||||
private static final long serialVersionUID = 7714332137352567952L;
|
private static final long serialVersionUID = 7714332137352567952L;
|
||||||
|
|
||||||
private Krb5NameElement name;
|
private Krb5NameElement name;
|
||||||
|
|
||||||
/**
|
private Krb5Util.ServiceCreds screds;
|
||||||
* 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 Krb5AcceptCredential(Krb5NameElement name, KerberosKey[] keys) {
|
private Krb5AcceptCredential(Krb5NameElement name, Krb5Util.ServiceCreds creds) {
|
||||||
/*
|
/*
|
||||||
* Initialize this instance with the data from the acquired
|
* Initialize this instance with the data from the acquired
|
||||||
* KerberosKey. This class needs to be a KerberosKey too
|
* KerberosKey. This class needs to be a KerberosKey too
|
||||||
* hence we can't just store a reference.
|
* hence we can't just store a reference.
|
||||||
*/
|
*/
|
||||||
super(keys[0].getPrincipal(),
|
|
||||||
keys[0].getEncoded(),
|
|
||||||
keys[0].getKeyType(),
|
|
||||||
keys[0].getVersionNumber());
|
|
||||||
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
// Cache this for later use by the sun.security.krb5 package.
|
this.screds = creds;
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
|
static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
|
||||||
@ -86,12 +68,12 @@ public class Krb5AcceptCredential
|
|||||||
name.getKrb5PrincipalName().getName());
|
name.getKrb5PrincipalName().getName());
|
||||||
final AccessControlContext acc = AccessController.getContext();
|
final AccessControlContext acc = AccessController.getContext();
|
||||||
|
|
||||||
KerberosKey[] keys;
|
Krb5Util.ServiceCreds creds = null;
|
||||||
try {
|
try {
|
||||||
keys = AccessController.doPrivileged(
|
creds = AccessController.doPrivileged(
|
||||||
new PrivilegedExceptionAction<KerberosKey[]>() {
|
new PrivilegedExceptionAction<Krb5Util.ServiceCreds>() {
|
||||||
public KerberosKey[] run() throws Exception {
|
public Krb5Util.ServiceCreds run() throws Exception {
|
||||||
return Krb5Util.getKeys(
|
return Krb5Util.getServiceCreds(
|
||||||
caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
|
caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
|
||||||
serverPrinc, acc);
|
serverPrinc, acc);
|
||||||
}});
|
}});
|
||||||
@ -103,17 +85,17 @@ public class Krb5AcceptCredential
|
|||||||
throw ge;
|
throw ge;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keys == null || keys.length == 0)
|
if (creds == null)
|
||||||
throw new GSSException(GSSException.NO_CRED, -1,
|
throw new GSSException(GSSException.NO_CRED, -1,
|
||||||
"Failed to find any Kerberos Key");
|
"Failed to find any Kerberos credentails");
|
||||||
|
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
String fullName = keys[0].getPrincipal().getName();
|
String fullName = creds.getName();
|
||||||
name = Krb5NameElement.getInstance(fullName,
|
name = Krb5NameElement.getInstance(fullName,
|
||||||
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
|
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Krb5AcceptCredential(name, keys);
|
return new Krb5AcceptCredential(name, creds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,7 +153,7 @@ public class Krb5AcceptCredential
|
|||||||
}
|
}
|
||||||
|
|
||||||
EncryptionKey[] getKrb5EncryptionKeys() {
|
EncryptionKey[] getKrb5EncryptionKeys() {
|
||||||
return krb5EncryptionKeys;
|
return screds.getEKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,13 +175,6 @@ public class Krb5AcceptCredential
|
|||||||
* destroy in the base class.
|
* destroy in the base class.
|
||||||
*/
|
*/
|
||||||
public void destroy() throws DestroyFailedException {
|
public void destroy() throws DestroyFailedException {
|
||||||
if (krb5EncryptionKeys != null) {
|
screds.destroy();
|
||||||
for (int i = 0; i < krb5EncryptionKeys.length; i++) {
|
|
||||||
krb5EncryptionKeys[i].destroy();
|
|
||||||
}
|
|
||||||
krb5EncryptionKeys = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.KerberosTicket;
|
||||||
import javax.security.auth.kerberos.KerberosKey;
|
import javax.security.auth.kerberos.KerberosKey;
|
||||||
import javax.security.auth.kerberos.KerberosPrincipal;
|
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||||
|
import javax.security.auth.kerberos.KeyTab;
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
import javax.security.auth.login.LoginException;
|
import javax.security.auth.login.LoginException;
|
||||||
import java.security.AccessControlContext;
|
import java.security.AccessControlContext;
|
||||||
@ -38,7 +39,13 @@ import sun.security.krb5.Credentials;
|
|||||||
import sun.security.krb5.EncryptionKey;
|
import sun.security.krb5.EncryptionKey;
|
||||||
import sun.security.krb5.KrbException;
|
import sun.security.krb5.KrbException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
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.
|
* Utilities for obtaining and converting Kerberos tickets.
|
||||||
*
|
*
|
||||||
@ -75,7 +82,7 @@ public class Krb5Util {
|
|||||||
|
|
||||||
// 1. Try to find service ticket in acc subject
|
// 1. Try to find service ticket in acc subject
|
||||||
Subject accSubj = Subject.getSubject(acc);
|
Subject accSubj = Subject.getSubject(acc);
|
||||||
KerberosTicket ticket = (KerberosTicket) SubjectComber.find(accSubj,
|
KerberosTicket ticket = SubjectComber.find(accSubj,
|
||||||
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
||||||
|
|
||||||
if (ticket != null) {
|
if (ticket != null) {
|
||||||
@ -87,7 +94,7 @@ public class Krb5Util {
|
|||||||
// 2. Try to get ticket from login
|
// 2. Try to get ticket from login
|
||||||
try {
|
try {
|
||||||
loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
ticket = (KerberosTicket) SubjectComber.find(loginSubj,
|
ticket = SubjectComber.find(loginSubj,
|
||||||
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
||||||
if (ticket != null) {
|
if (ticket != null) {
|
||||||
return ticket; // found it
|
return ticket; // found it
|
||||||
@ -102,13 +109,13 @@ public class Krb5Util {
|
|||||||
// Try to get TGT to acquire service ticket
|
// Try to get TGT to acquire service ticket
|
||||||
|
|
||||||
// 3. Try to get TGT from acc subject
|
// 3. Try to get TGT from acc subject
|
||||||
KerberosTicket tgt = (KerberosTicket) SubjectComber.find(accSubj,
|
KerberosTicket tgt = SubjectComber.find(accSubj,
|
||||||
tgsPrincipal, clientPrincipal, KerberosTicket.class);
|
tgsPrincipal, clientPrincipal, KerberosTicket.class);
|
||||||
|
|
||||||
boolean fromAcc;
|
boolean fromAcc;
|
||||||
if (tgt == null && loginSubj != null) {
|
if (tgt == null && loginSubj != null) {
|
||||||
// 4. Try to get TGT from login subject
|
// 4. Try to get TGT from login subject
|
||||||
tgt = (KerberosTicket) SubjectComber.find(loginSubj,
|
tgt = SubjectComber.find(loginSubj,
|
||||||
tgsPrincipal, clientPrincipal, KerberosTicket.class);
|
tgsPrincipal, clientPrincipal, KerberosTicket.class);
|
||||||
fromAcc = false;
|
fromAcc = false;
|
||||||
} else {
|
} else {
|
||||||
@ -145,14 +152,14 @@ public class Krb5Util {
|
|||||||
|
|
||||||
// Try to get ticket from acc's Subject
|
// Try to get ticket from acc's Subject
|
||||||
Subject accSubj = Subject.getSubject(acc);
|
Subject accSubj = Subject.getSubject(acc);
|
||||||
KerberosTicket ticket = (KerberosTicket)
|
KerberosTicket ticket =
|
||||||
SubjectComber.find(accSubj, serverPrincipal, clientPrincipal,
|
SubjectComber.find(accSubj, serverPrincipal, clientPrincipal,
|
||||||
KerberosTicket.class);
|
KerberosTicket.class);
|
||||||
|
|
||||||
// Try to get ticket from Subject obtained from GSSUtil
|
// Try to get ticket from Subject obtained from GSSUtil
|
||||||
if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) {
|
if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) {
|
||||||
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
ticket = (KerberosTicket) SubjectComber.find(subject,
|
ticket = SubjectComber.find(subject,
|
||||||
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
||||||
}
|
}
|
||||||
return ticket;
|
return ticket;
|
||||||
@ -182,37 +189,152 @@ public class Krb5Util {
|
|||||||
return subject;
|
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
|
* Credentials of a service, the private secret to authenticate its
|
||||||
* the Subject in the specified AccessControlContext.
|
* identity, which can be:
|
||||||
* If the ticket can not be found in the Subject, and if
|
* 1. Some KerberosKeys (generated from password)
|
||||||
* useSubjectCredsOnly is false, then obtain keys from
|
* 2. A KeyTab (for a typical service)
|
||||||
* a LoginContext.
|
* 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<KeyTab> ktabs;
|
||||||
|
private List<KerberosKey> 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<KerberosKey> 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<KerberosKey> 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<Object> pcs = subj.getPrivateCredentials();
|
||||||
|
synchronized (pcs) {
|
||||||
|
Iterator<Object> iterator = pcs.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object obj = iterator.next();
|
||||||
|
if (obj instanceof KeysFromKeyTab) {
|
||||||
|
KerberosKey key = (KerberosKey)obj;
|
||||||
|
if (Objects.equals(key.getPrincipal(), kp)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (KerberosKey key: keys) {
|
||||||
|
subj.getPrivateCredentials().add(new KeysFromKeyTab(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 void destroy() {
|
||||||
|
kp = null;
|
||||||
|
ktabs = null;
|
||||||
|
kk = 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.
|
||||||
|
*
|
||||||
|
* NOTE: This method is also used by JSSE Kerberos Cipher Suites
|
||||||
|
*/
|
||||||
|
public static ServiceCreds getServiceCreds(GSSCaller caller,
|
||||||
String serverPrincipal, AccessControlContext acc)
|
String serverPrincipal, AccessControlContext acc)
|
||||||
throws LoginException {
|
throws LoginException {
|
||||||
|
|
||||||
Subject accSubj = Subject.getSubject(acc);
|
Subject accSubj = Subject.getSubject(acc);
|
||||||
List<KerberosKey> kkeys = (List<KerberosKey>)SubjectComber.findMany(
|
ServiceCreds sc = null;
|
||||||
accSubj, serverPrincipal, null, KerberosKey.class);
|
if (accSubj != null) {
|
||||||
|
sc = ServiceCreds.getInstance(accSubj, serverPrincipal);
|
||||||
if (kkeys == null && !GSSUtil.useSubjectCredsOnly(caller)) {
|
}
|
||||||
|
if (sc == null && !GSSUtil.useSubjectCredsOnly(caller)) {
|
||||||
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
kkeys = (List<KerberosKey>) SubjectComber.findMany(subject,
|
sc = ServiceCreds.getInstance(subject, serverPrincipal);
|
||||||
serverPrincipal, null, KerberosKey.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
int len;
|
|
||||||
if (kkeys != null && (len = kkeys.size()) > 0) {
|
|
||||||
KerberosKey[] keys = new KerberosKey[len];
|
|
||||||
kkeys.toArray(keys);
|
|
||||||
return keys;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return sc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KerberosTicket credsToTicket(Credentials serviceCreds) {
|
public static KerberosTicket credsToTicket(Credentials serviceCreds) {
|
||||||
@ -247,4 +369,17 @@ public class Krb5Util {
|
|||||||
kerbTicket.getRenewTill(),
|
kerbTicket.getRenewTill(),
|
||||||
kerbTicket.getClientAddresses());
|
kerbTicket.getClientAddresses());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper method to get EncryptionKeys from a javax..KeyTab
|
||||||
|
* @param ktab the javax..KeyTab class
|
||||||
|
* @param cname the PrincipalName
|
||||||
|
* @return the EKeys, never null, might be empty
|
||||||
|
*/
|
||||||
|
public static EncryptionKey[] keysFromJavaxKeyTab(
|
||||||
|
KeyTab ktab, PrincipalName cname) {
|
||||||
|
return SharedSecrets.getJavaxSecurityAuthKerberosAccess().
|
||||||
|
keyTabGetEncryptionKeys(ktab, cname);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -33,10 +33,11 @@ import java.util.Iterator;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.security.auth.kerberos.KeyTab;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This utility looks through the current Subject and retrieves a ticket or key
|
* This utility looks through the current Subject and retrieves private
|
||||||
* for the desired client/server principals.
|
* credentials for the desired client/server principals.
|
||||||
*
|
*
|
||||||
* @author Ram Marti
|
* @author Ram Marti
|
||||||
* @since 1.4.2
|
* @since 1.4.2
|
||||||
@ -52,58 +53,70 @@ class SubjectComber {
|
|||||||
private SubjectComber() { // Cannot create one of these
|
private SubjectComber() { // Cannot create one of these
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object find(Subject subject, String serverPrincipal,
|
static <T> T find(Subject subject, String serverPrincipal,
|
||||||
String clientPrincipal, Class credClass) {
|
String clientPrincipal, Class<T> credClass) {
|
||||||
|
|
||||||
return findAux(subject, serverPrincipal, clientPrincipal, credClass,
|
return (T)findAux(subject, serverPrincipal, clientPrincipal, credClass,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object findMany(Subject subject, String serverPrincipal,
|
static <T> List<T> findMany(Subject subject, String serverPrincipal,
|
||||||
String clientPrincipal, Class credClass) {
|
String clientPrincipal, Class<T> credClass) {
|
||||||
|
|
||||||
return findAux(subject, serverPrincipal, clientPrincipal, credClass,
|
return (List<T>)findAux(subject, serverPrincipal, clientPrincipal, credClass,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the ticket or key for the specified client/server principals
|
* Find private credentials for the specified client/server principals
|
||||||
* in the subject. Returns null if the subject is null.
|
* in the subject. Returns null if the subject is null.
|
||||||
*
|
*
|
||||||
* @return the ticket or key
|
* @return the private credentials
|
||||||
*/
|
*/
|
||||||
private static Object findAux(Subject subject, String serverPrincipal,
|
private static <T> Object findAux(Subject subject, String serverPrincipal,
|
||||||
String clientPrincipal, Class credClass, boolean oneOnly) {
|
String clientPrincipal, Class<T> credClass, boolean oneOnly) {
|
||||||
|
|
||||||
if (subject == null) {
|
if (subject == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
List<Object> answer = (oneOnly ? null : new ArrayList<Object>());
|
List<T> answer = (oneOnly ? null : new ArrayList<T>());
|
||||||
|
|
||||||
if (credClass == KerberosKey.class) {
|
if (credClass == KeyTab.class) { // Principal un-related
|
||||||
// We are looking for a KerberosKey credentials for the
|
// We are looking for credentials unrelated to serverPrincipal
|
||||||
// serverPrincipal
|
Iterator<T> iterator =
|
||||||
Iterator<KerberosKey> iterator =
|
subject.getPrivateCredentials(credClass).iterator();
|
||||||
subject.getPrivateCredentials(KerberosKey.class).iterator();
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
KerberosKey key = iterator.next();
|
T t = iterator.next();
|
||||||
if (serverPrincipal == null ||
|
if (DEBUG) {
|
||||||
serverPrincipal.equals(key.getPrincipal().getName())) {
|
System.out.println("Found " + credClass.getSimpleName());
|
||||||
|
}
|
||||||
|
if (oneOnly) {
|
||||||
|
return t;
|
||||||
|
} else {
|
||||||
|
answer.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (credClass == KerberosKey.class) {
|
||||||
|
// We are looking for credentials for the serverPrincipal
|
||||||
|
Iterator<T> iterator =
|
||||||
|
subject.getPrivateCredentials(credClass).iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
T t = iterator.next();
|
||||||
|
String name = ((KerberosKey)t).getPrincipal().getName();
|
||||||
|
if (serverPrincipal == null || serverPrincipal.equals(name)) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println("Found key for "
|
System.out.println("Found " +
|
||||||
+ key.getPrincipal() + "(" +
|
credClass.getSimpleName() + " for " + name);
|
||||||
key.getKeyType() + ")");
|
|
||||||
}
|
}
|
||||||
if (oneOnly) {
|
if (oneOnly) {
|
||||||
return key;
|
return t;
|
||||||
} else {
|
} else {
|
||||||
if (serverPrincipal == null) {
|
if (serverPrincipal == null) {
|
||||||
// Record name so that keys returned will all
|
// Record name so that keys returned will all
|
||||||
// belong to the same principal
|
// belong to the same principal
|
||||||
serverPrincipal =
|
serverPrincipal = name;
|
||||||
key.getPrincipal().getName();
|
|
||||||
}
|
}
|
||||||
answer.add(key);
|
answer.add(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,7 +180,7 @@ class SubjectComber {
|
|||||||
serverPrincipal =
|
serverPrincipal =
|
||||||
ticket.getServer().getName();
|
ticket.getServer().getName();
|
||||||
}
|
}
|
||||||
answer.add(ticket);
|
answer.add((T)ticket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,6 @@ public class Config {
|
|||||||
|
|
||||||
public static synchronized void refresh() throws KrbException {
|
public static synchronized void refresh() throws KrbException {
|
||||||
singleton = new Config();
|
singleton = new Config();
|
||||||
KeyTab.refresh();
|
|
||||||
KdcComm.initStatic();
|
KdcComm.initStatic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -138,8 +138,7 @@ public class EncryptionKey
|
|||||||
* @returns an array of secret keys or null if none were found.
|
* @returns an array of secret keys or null if none were found.
|
||||||
*/
|
*/
|
||||||
public static EncryptionKey[] acquireSecretKeys(PrincipalName princ,
|
public static EncryptionKey[] acquireSecretKeys(PrincipalName princ,
|
||||||
String keytab)
|
String keytab) {
|
||||||
throws KrbException, IOException {
|
|
||||||
|
|
||||||
if (princ == null)
|
if (princ == null)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
@ -148,11 +147,6 @@ public class EncryptionKey
|
|||||||
// KeyTab getInstance(keytab) will call KeyTab.getInstance()
|
// KeyTab getInstance(keytab) will call KeyTab.getInstance()
|
||||||
// if keytab is null
|
// if keytab is null
|
||||||
KeyTab ktab = KeyTab.getInstance(keytab);
|
KeyTab ktab = KeyTab.getInstance(keytab);
|
||||||
|
|
||||||
if (ktab == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ktab.readServiceKeys(princ);
|
return ktab.readServiceKeys(princ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,8 @@ import sun.security.krb5.internal.crypto.EType;
|
|||||||
import sun.security.util.*;
|
import sun.security.util.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import javax.security.auth.kerberos.KeyTab;
|
||||||
|
import sun.security.jgss.krb5.Krb5Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class encapsulates a AS-REP message that the KDC sends to the
|
* This class encapsulates a AS-REP message that the KDC sends to the
|
||||||
@ -90,29 +92,32 @@ class KrbAsRep extends KrbKdcRep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by KrbAsReqBuilder to resolve a AS-REP message using keys.
|
* Called by KrbAsReqBuilder to resolve a AS-REP message using a keytab.
|
||||||
* @param keys user provided keys, not null
|
* @param ktab the keytab, not null
|
||||||
* @param asReq the original AS-REQ sent, used to validate AS-REP
|
* @param asReq the original AS-REQ sent, used to validate AS-REP
|
||||||
|
* @param cname the user principal name, used to locate keys in ktab
|
||||||
*/
|
*/
|
||||||
void decryptUsingKeys(EncryptionKey[] keys, KrbAsReq asReq)
|
void decryptUsingKeyTab(KeyTab ktab, KrbAsReq asReq, PrincipalName cname)
|
||||||
throws KrbException, Asn1Exception, IOException {
|
throws KrbException, Asn1Exception, IOException {
|
||||||
EncryptionKey dkey = null;
|
EncryptionKey dkey = null;
|
||||||
int encPartKeyType = rep.encPart.getEType();
|
int encPartKeyType = rep.encPart.getEType();
|
||||||
Integer encPartKvno = rep.encPart.kvno;
|
Integer encPartKvno = rep.encPart.kvno;
|
||||||
try {
|
try {
|
||||||
dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno, keys);
|
dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno,
|
||||||
} catch (KrbException ke) {
|
Krb5Util.keysFromJavaxKeyTab(ktab, cname));
|
||||||
if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) {
|
} catch (KrbException ke) {
|
||||||
// Fallback to no kvno. In some cases, keytab is generated
|
if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) {
|
||||||
// not by sysadmin but Java's ktab command
|
// Fallback to no kvno. In some cases, keytab is generated
|
||||||
dkey = EncryptionKey.findKey(encPartKeyType, keys);
|
// not by sysadmin but Java's ktab command
|
||||||
|
dkey = EncryptionKey.findKey(encPartKeyType,
|
||||||
|
Krb5Util.keysFromJavaxKeyTab(ktab, cname));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dkey == null) {
|
||||||
|
throw new KrbException(Krb5.API_INVALID_ARG,
|
||||||
|
"Cannot find key for type/kvno to decrypt AS REP - " +
|
||||||
|
EType.toString(encPartKeyType) + "/" + encPartKvno);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (dkey == null) {
|
|
||||||
throw new KrbException(Krb5.API_INVALID_ARG,
|
|
||||||
"Cannot find key for type/kvno to decrypt AS REP - " +
|
|
||||||
EType.toString(encPartKeyType) + "/" + encPartKvno);
|
|
||||||
}
|
|
||||||
decrypt(dkey, asReq);
|
decrypt(dkey, asReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,6 +27,8 @@ package sun.security.krb5;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import javax.security.auth.kerberos.KeyTab;
|
||||||
|
import sun.security.jgss.krb5.Krb5Util;
|
||||||
import sun.security.krb5.internal.HostAddresses;
|
import sun.security.krb5.internal.HostAddresses;
|
||||||
import sun.security.krb5.internal.KDCOptions;
|
import sun.security.krb5.internal.KDCOptions;
|
||||||
import sun.security.krb5.internal.KRBError;
|
import sun.security.krb5.internal.KRBError;
|
||||||
@ -42,13 +44,16 @@ import sun.security.krb5.internal.crypto.EType;
|
|||||||
* 1. Gather information to create AS-REQ
|
* 1. Gather information to create AS-REQ
|
||||||
* 2. Create and send AS-REQ
|
* 2. Create and send AS-REQ
|
||||||
* 3. Receive AS-REP and KRB-ERROR (-KRB_ERR_RESPONSE_TOO_BIG) and parse them
|
* 3. Receive AS-REP and KRB-ERROR (-KRB_ERR_RESPONSE_TOO_BIG) and parse them
|
||||||
* 4. Emit credentials and secret keys (for JAAS storeKey=true)
|
* 4. Emit credentials and secret keys (for JAAS storeKey=true with password)
|
||||||
*
|
*
|
||||||
* This class does not:
|
* This class does not:
|
||||||
* 1. Deal with real communications (KdcComm does it, and TGS-REQ)
|
* 1. Deal with real communications (KdcComm does it, and TGS-REQ)
|
||||||
* a. Name of KDCs for a realm
|
* a. Name of KDCs for a realm
|
||||||
* b. Server availability, timeout, UDP or TCP
|
* b. Server availability, timeout, UDP or TCP
|
||||||
* d. KRB_ERR_RESPONSE_TOO_BIG
|
* d. KRB_ERR_RESPONSE_TOO_BIG
|
||||||
|
* 2. Stores its own copy of password, this means:
|
||||||
|
* a. Do not change/wipe it before Builder finish
|
||||||
|
* b. Builder will not wipe it for you
|
||||||
*
|
*
|
||||||
* With this class:
|
* With this class:
|
||||||
* 1. KrbAsReq has only one constructor
|
* 1. KrbAsReq has only one constructor
|
||||||
@ -70,19 +75,17 @@ public final class KrbAsReqBuilder {
|
|||||||
private HostAddresses addresses;
|
private HostAddresses addresses;
|
||||||
|
|
||||||
// Secret source: can't be changed once assigned, only one (of the two
|
// Secret source: can't be changed once assigned, only one (of the two
|
||||||
// sources) can be set and should be non-null
|
// sources) can be set to non-null
|
||||||
private EncryptionKey[] keys;
|
private final char[] password;
|
||||||
private char[] password;
|
private final KeyTab ktab;
|
||||||
|
|
||||||
// Used to create a ENC-TIMESTAMP in the 2nd AS-REQ
|
// Used to create a ENC-TIMESTAMP in the 2nd AS-REQ
|
||||||
private EncryptionKey pakey;
|
|
||||||
private PAData[] paList; // PA-DATA from both KRB-ERROR and AS-REP.
|
private PAData[] paList; // PA-DATA from both KRB-ERROR and AS-REP.
|
||||||
// Used by getKeys() only.
|
// Used by getKeys() only.
|
||||||
// Only AS-REP should be enough per RFC,
|
// Only AS-REP should be enough per RFC,
|
||||||
// combined in case etypes are different.
|
// combined in case etypes are different.
|
||||||
|
|
||||||
// The generated and received:
|
// The generated and received:
|
||||||
int[] eTypes;
|
|
||||||
private KrbAsReq req;
|
private KrbAsReq req;
|
||||||
private KrbAsRep rep;
|
private KrbAsRep rep;
|
||||||
|
|
||||||
@ -94,7 +97,7 @@ public final class KrbAsReqBuilder {
|
|||||||
private State state;
|
private State state;
|
||||||
|
|
||||||
// Called by other constructors
|
// Called by other constructors
|
||||||
private KrbAsReqBuilder(PrincipalName cname)
|
private void init(PrincipalName cname)
|
||||||
throws KrbException {
|
throws KrbException {
|
||||||
if (cname.getRealm() == null) {
|
if (cname.getRealm() == null) {
|
||||||
cname.setRealm(Config.getInstance().getDefaultRealm());
|
cname.setRealm(Config.getInstance().getDefaultRealm());
|
||||||
@ -114,14 +117,11 @@ public final class KrbAsReqBuilder {
|
|||||||
* This argument will neither be modified nor stored by the method.
|
* This argument will neither be modified nor stored by the method.
|
||||||
* @throws KrbException
|
* @throws KrbException
|
||||||
*/
|
*/
|
||||||
public KrbAsReqBuilder(PrincipalName cname, EncryptionKey[] keys)
|
public KrbAsReqBuilder(PrincipalName cname, KeyTab ktab)
|
||||||
throws KrbException {
|
throws KrbException {
|
||||||
this(cname);
|
init(cname);
|
||||||
this.keys = new EncryptionKey[keys.length];
|
this.ktab = ktab;
|
||||||
for (int i=0; i<keys.length; i++) {
|
this.password = null;
|
||||||
this.keys[i] = (EncryptionKey)keys[i].clone();
|
|
||||||
}
|
|
||||||
eTypes = EType.getDefaults("default_tkt_enctypes", keys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,30 +137,24 @@ public final class KrbAsReqBuilder {
|
|||||||
*/
|
*/
|
||||||
public KrbAsReqBuilder(PrincipalName cname, char[] pass)
|
public KrbAsReqBuilder(PrincipalName cname, char[] pass)
|
||||||
throws KrbException {
|
throws KrbException {
|
||||||
this(cname);
|
init(cname);
|
||||||
this.password = pass.clone();
|
this.password = pass.clone();
|
||||||
eTypes = EType.getDefaults("default_tkt_enctypes");
|
this.ktab = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an array of secret keys for the client. This is useful if
|
* Retrieves an array of secret keys for the client. This is used when
|
||||||
* the client supplies password but need keys to act as an acceptor
|
* the client supplies password but need keys to act as an acceptor
|
||||||
* (in JAAS words, isInitiator=true and storeKey=true)
|
* (in JAAS words, isInitiator=true and storeKey=true)
|
||||||
* @return original keys if initiated with keys, or generated keys if
|
* @return generated keys from password. PA-DATA from server might be used.
|
||||||
* password. In latter case, PA-DATA from server might be used to
|
* All "default_tkt_enctypes" keys will be generated, Never null.
|
||||||
* generate keys. All "default_tkt_enctypes" keys will be generated,
|
* @throws IllegalStateException if not constructed from a password
|
||||||
* Never null.
|
|
||||||
* @throws KrbException
|
* @throws KrbException
|
||||||
*/
|
*/
|
||||||
public EncryptionKey[] getKeys() throws KrbException {
|
public EncryptionKey[] getKeys() throws KrbException {
|
||||||
checkState(State.REQ_OK, "Cannot get keys");
|
checkState(State.REQ_OK, "Cannot get keys");
|
||||||
if (keys != null) {
|
if (password != null) {
|
||||||
EncryptionKey[] result = new EncryptionKey[keys.length];
|
int[] eTypes = EType.getDefaults("default_tkt_enctypes");
|
||||||
for (int i=0; i<keys.length; i++) {
|
|
||||||
result[i] = (EncryptionKey)keys[i].clone();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
EncryptionKey[] result = new EncryptionKey[eTypes.length];
|
EncryptionKey[] result = new EncryptionKey[eTypes.length];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -205,6 +199,8 @@ public final class KrbAsReqBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Required password not provided");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,12 +237,22 @@ public final class KrbAsReqBuilder {
|
|||||||
/**
|
/**
|
||||||
* Build a KrbAsReq object from all info fed above. Normally this method
|
* Build a KrbAsReq object from all info fed above. Normally this method
|
||||||
* will be called twice: initial AS-REQ and second with pakey
|
* will be called twice: initial AS-REQ and second with pakey
|
||||||
|
* @param key null (initial AS-REQ) or pakey (with preauth)
|
||||||
* @return the KrbAsReq object
|
* @return the KrbAsReq object
|
||||||
* @throws KrbException
|
* @throws KrbException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private KrbAsReq build() throws KrbException, IOException {
|
private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
|
||||||
return new KrbAsReq(pakey,
|
int[] eTypes;
|
||||||
|
if (password != null) {
|
||||||
|
eTypes = EType.getDefaults("default_tkt_enctypes");
|
||||||
|
} else {
|
||||||
|
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
|
||||||
|
eTypes = EType.getDefaults("default_tkt_enctypes",
|
||||||
|
ks);
|
||||||
|
for (EncryptionKey k: ks) k.destroy();
|
||||||
|
}
|
||||||
|
return new KrbAsReq(key,
|
||||||
options,
|
options,
|
||||||
cname,
|
cname,
|
||||||
sname,
|
sname,
|
||||||
@ -263,9 +269,10 @@ public final class KrbAsReqBuilder {
|
|||||||
* @throws Asn1Exception
|
* @throws Asn1Exception
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private KrbAsReqBuilder resolve() throws KrbException, Asn1Exception, IOException {
|
private KrbAsReqBuilder resolve()
|
||||||
if (keys != null) {
|
throws KrbException, Asn1Exception, IOException {
|
||||||
rep.decryptUsingKeys(keys, req);
|
if (ktab != null) {
|
||||||
|
rep.decryptUsingKeyTab(ktab, req, cname);
|
||||||
} else {
|
} else {
|
||||||
rep.decryptUsingPassword(password, req, cname);
|
rep.decryptUsingPassword(password, req, cname);
|
||||||
}
|
}
|
||||||
@ -292,9 +299,10 @@ public final class KrbAsReqBuilder {
|
|||||||
private KrbAsReqBuilder send() throws KrbException, IOException {
|
private KrbAsReqBuilder send() throws KrbException, IOException {
|
||||||
boolean preAuthFailedOnce = false;
|
boolean preAuthFailedOnce = false;
|
||||||
KdcComm comm = new KdcComm(cname.getRealmAsString());
|
KdcComm comm = new KdcComm(cname.getRealmAsString());
|
||||||
|
EncryptionKey pakey = null;
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
req = build();
|
req = build(pakey);
|
||||||
rep = new KrbAsRep(comm.send(req.encoding()));
|
rep = new KrbAsRep(comm.send(req.encoding()));
|
||||||
return this;
|
return this;
|
||||||
} catch (KrbException ke) {
|
} catch (KrbException ke) {
|
||||||
@ -308,7 +316,10 @@ public final class KrbAsReqBuilder {
|
|||||||
preAuthFailedOnce = true;
|
preAuthFailedOnce = true;
|
||||||
KRBError kerr = ke.getError();
|
KRBError kerr = ke.getError();
|
||||||
if (password == null) {
|
if (password == null) {
|
||||||
pakey = EncryptionKey.findKey(kerr.getEType(), keys);
|
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
|
||||||
|
pakey = EncryptionKey.findKey(kerr.getEType(), ks);
|
||||||
|
if (pakey != null) pakey = (EncryptionKey)pakey.clone();
|
||||||
|
for (EncryptionKey k: ks) k.destroy();
|
||||||
} else {
|
} else {
|
||||||
PAData.SaltAndParams snp = PAData.getSaltAndParams(
|
PAData.SaltAndParams snp = PAData.getSaltAndParams(
|
||||||
kerr.getEType(), kerr.getPA());
|
kerr.getEType(), kerr.getPA());
|
||||||
@ -317,7 +328,7 @@ public final class KrbAsReqBuilder {
|
|||||||
// does not recommend this
|
// does not recommend this
|
||||||
pakey = EncryptionKey.acquireSecretKey(password,
|
pakey = EncryptionKey.acquireSecretKey(password,
|
||||||
snp.salt == null ? cname.getSalt() : snp.salt,
|
snp.salt == null ? cname.getSalt() : snp.salt,
|
||||||
eTypes[0],
|
EType.getDefaults("default_tkt_enctypes")[0],
|
||||||
null);
|
null);
|
||||||
} else {
|
} else {
|
||||||
pakey = EncryptionKey.acquireSecretKey(password,
|
pakey = EncryptionKey.acquireSecretKey(password,
|
||||||
@ -369,15 +380,8 @@ public final class KrbAsReqBuilder {
|
|||||||
*/
|
*/
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
state = State.DESTROYED;
|
state = State.DESTROYED;
|
||||||
if (keys != null) {
|
|
||||||
for (EncryptionKey k: keys) {
|
|
||||||
k.destroy();
|
|
||||||
}
|
|
||||||
keys = null;
|
|
||||||
}
|
|
||||||
if (password != null) {
|
if (password != null) {
|
||||||
Arrays.fill(password, (char)0);
|
Arrays.fill(password, (char)0);
|
||||||
password = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ import java.io.IOException;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -50,92 +51,138 @@ import java.util.Vector;
|
|||||||
* This class represents key table. The key table functions deal with storing
|
* This class represents key table. The key table functions deal with storing
|
||||||
* and retrieving service keys for use in authentication exchanges.
|
* and retrieving service keys for use in authentication exchanges.
|
||||||
*
|
*
|
||||||
|
* A KeyTab object is always constructed, if the file specified does not
|
||||||
|
* exist, it's still valid but empty. If there is an I/O error or file format
|
||||||
|
* error, it's invalid.
|
||||||
|
*
|
||||||
|
* The class is immutable on the read side (the write side is only used by
|
||||||
|
* the ktab tool).
|
||||||
|
*
|
||||||
* @author Yanni Zhang
|
* @author Yanni Zhang
|
||||||
*/
|
*/
|
||||||
public class KeyTab implements KeyTabConstants {
|
public class KeyTab implements KeyTabConstants {
|
||||||
int kt_vno;
|
|
||||||
private static KeyTab singleton = null;
|
|
||||||
private static final boolean DEBUG = Krb5.DEBUG;
|
private static final boolean DEBUG = Krb5.DEBUG;
|
||||||
private static String name;
|
private static String defaultTabName = null;
|
||||||
|
|
||||||
|
// Attention: Currently there is no way to remove a keytab from this map,
|
||||||
|
// this might lead to a memory leak.
|
||||||
|
private static Map<String,KeyTab> map = new HashMap<>();
|
||||||
|
|
||||||
|
// KeyTab file does not exist. Note: a missing keytab is still valid
|
||||||
|
private boolean isMissing = false;
|
||||||
|
|
||||||
|
// KeyTab file is invalid, possibly an I/O error or a file format error.
|
||||||
|
private boolean isValid = true;
|
||||||
|
|
||||||
|
private final String tabName;
|
||||||
|
private long lastModified;
|
||||||
|
private int kt_vno;
|
||||||
|
|
||||||
private Vector<KeyTabEntry> entries = new Vector<>();
|
private Vector<KeyTabEntry> entries = new Vector<>();
|
||||||
|
|
||||||
private KeyTab(String filename) throws IOException, RealmException {
|
/**
|
||||||
init(filename);
|
* Constructs a KeyTab object.
|
||||||
}
|
*
|
||||||
|
* If there is any I/O error or format errot during the loading, the
|
||||||
public static KeyTab getInstance(String s) {
|
* isValid flag is set to false, and all half-read entries are dismissed.
|
||||||
name = parse(s);
|
* @param filename path name for the keytab file, must not be null
|
||||||
if (name == null) {
|
*/
|
||||||
return getInstance();
|
private KeyTab(String filename) {
|
||||||
|
tabName = filename;
|
||||||
|
try {
|
||||||
|
lastModified = new File(tabName).lastModified();
|
||||||
|
KeyTabInputStream kis =
|
||||||
|
new KeyTabInputStream(new FileInputStream(filename));
|
||||||
|
load(kis);
|
||||||
|
kis.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
entries.clear();
|
||||||
|
isMissing = true;
|
||||||
|
} catch (Exception ioe) {
|
||||||
|
entries.clear();
|
||||||
|
isValid = false;
|
||||||
}
|
}
|
||||||
return getInstance(new File(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the single instance of KeyTab class.
|
* Read a keytab file. Returns a new object and save it into cache when
|
||||||
|
* new content (modified since last read) is available. If keytab file is
|
||||||
|
* invalid, the old object will be returned. This is a safeguard for
|
||||||
|
* partial-written keytab files or non-stable network. Please note that
|
||||||
|
* a missing keytab is valid, which is equivalent to an empty keytab.
|
||||||
|
*
|
||||||
|
* @param s file name of keytab, must not be null
|
||||||
|
* @return the keytab object, can be invalid, but never null.
|
||||||
|
*/
|
||||||
|
private synchronized static KeyTab getInstance0(String s) {
|
||||||
|
long lm = new File(s).lastModified();
|
||||||
|
KeyTab old = map.get(s);
|
||||||
|
if (old != null && old.isValid() && old.lastModified == lm) {
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
KeyTab ktab = new KeyTab(s);
|
||||||
|
if (ktab.isValid()) { // A valid new keytab
|
||||||
|
map.put(s, ktab);
|
||||||
|
return ktab;
|
||||||
|
} else if (old != null) { // An existing old one
|
||||||
|
return old;
|
||||||
|
} else {
|
||||||
|
return ktab; // first read is invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a KeyTab object.
|
||||||
|
* @param s the key tab file name.
|
||||||
|
* @return the KeyTab object, never null.
|
||||||
|
*/
|
||||||
|
public static KeyTab getInstance(String s) {
|
||||||
|
if (s == null) {
|
||||||
|
return getInstance();
|
||||||
|
} else {
|
||||||
|
return getInstance0(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a KeyTab object.
|
||||||
* @param file the key tab file.
|
* @param file the key tab file.
|
||||||
* @return single instance of KeyTab;
|
* @return the KeyTab object, never null.
|
||||||
* return null if error occurs while reading data out of the file.
|
|
||||||
*/
|
*/
|
||||||
public static KeyTab getInstance(File file) {
|
public static KeyTab getInstance(File file) {
|
||||||
try {
|
if (file == null) {
|
||||||
if (!(file.exists())) {
|
return getInstance();
|
||||||
singleton = null;
|
} else {
|
||||||
} else {
|
return getInstance0(file.getPath());
|
||||||
String fname = file.getAbsolutePath();
|
|
||||||
// Since this class deals with file I/O operations,
|
|
||||||
// we want only one class instance existing.
|
|
||||||
if (singleton != null) {
|
|
||||||
File kfile = new File(singleton.name);
|
|
||||||
String kname = kfile.getAbsolutePath();
|
|
||||||
if (kname.equalsIgnoreCase(fname)) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println("KeyTab instance already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
singleton = new KeyTab(fname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
singleton = null;
|
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println("Could not obtain an instance of KeyTab" +
|
|
||||||
e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return singleton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the single instance of KeyTab class.
|
* Gets the default KeyTab object.
|
||||||
* @return single instance of KeyTab; return null if default keytab file
|
* @return the KeyTab object, never null.
|
||||||
* does not exist, or error occurs while reading data from the file.
|
|
||||||
*/
|
*/
|
||||||
public static KeyTab getInstance() {
|
public static KeyTab getInstance() {
|
||||||
try {
|
return getInstance(getDefaultTabName());
|
||||||
name = getDefaultKeyTab();
|
}
|
||||||
if (name != null) {
|
|
||||||
singleton = getInstance(new File(name));
|
public boolean isMissing() {
|
||||||
}
|
return isMissing;
|
||||||
} catch (Exception e) {
|
}
|
||||||
singleton = null;
|
|
||||||
if (DEBUG) {
|
public boolean isValid() {
|
||||||
System.out.println("Could not obtain an instance of KeyTab" +
|
return isValid;
|
||||||
e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return singleton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The location of keytab file will be read from the configuration file
|
* The location of keytab file will be read from the configuration file
|
||||||
* If it is not specified, consider user.home as the keytab file's
|
* If it is not specified, consider user.home as the keytab file's
|
||||||
* default location.
|
* default location.
|
||||||
|
* @return never null
|
||||||
*/
|
*/
|
||||||
private static String getDefaultKeyTab() {
|
private static String getDefaultTabName() {
|
||||||
if (name != null) {
|
if (defaultTabName != null) {
|
||||||
return name;
|
return defaultTabName;
|
||||||
} else {
|
} else {
|
||||||
String kname = null;
|
String kname = null;
|
||||||
try {
|
try {
|
||||||
@ -145,7 +192,7 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
StringTokenizer st = new StringTokenizer(keytab_names, " ");
|
StringTokenizer st = new StringTokenizer(keytab_names, " ");
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
kname = parse(st.nextToken());
|
kname = parse(st.nextToken());
|
||||||
if (kname != null) {
|
if (new File(kname).exists()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,19 +212,20 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
new sun.security.action.GetPropertyAction("user.dir"));
|
new sun.security.action.GetPropertyAction("user.dir"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_home != null) {
|
kname = user_home + File.separator + "krb5.keytab";
|
||||||
kname = user_home + File.separator + "krb5.keytab";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
defaultTabName = kname;
|
||||||
return kname;
|
return kname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses some common keytab name formats
|
||||||
|
* @param name never null
|
||||||
|
* @return never null
|
||||||
|
*/
|
||||||
private static String parse(String name) {
|
private static String parse(String name) {
|
||||||
String kname = null;
|
String kname;
|
||||||
if (name == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if ((name.length() >= 5) &&
|
if ((name.length() >= 5) &&
|
||||||
(name.substring(0, 5).equalsIgnoreCase("FILE:"))) {
|
(name.substring(0, 5).equalsIgnoreCase("FILE:"))) {
|
||||||
kname = name.substring(5);
|
kname = name.substring(5);
|
||||||
@ -194,18 +242,6 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
return kname;
|
return kname;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void init(String filename)
|
|
||||||
throws IOException, RealmException {
|
|
||||||
|
|
||||||
if (filename != null) {
|
|
||||||
KeyTabInputStream kis =
|
|
||||||
new KeyTabInputStream(new FileInputStream(filename));
|
|
||||||
load(kis);
|
|
||||||
kis.close();
|
|
||||||
name = filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void load(KeyTabInputStream kis)
|
private void load(KeyTabInputStream kis)
|
||||||
throws IOException, RealmException {
|
throws IOException, RealmException {
|
||||||
|
|
||||||
@ -234,14 +270,13 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
* etypes that have been configured for use. If there are multiple
|
* etypes that have been configured for use. If there are multiple
|
||||||
* keys with same etype, the one with the highest kvno is returned.
|
* keys with same etype, the one with the highest kvno is returned.
|
||||||
* @param service the PrincipalName of the requested service
|
* @param service the PrincipalName of the requested service
|
||||||
* @return an array containing all the service keys
|
* @return an array containing all the service keys, never null
|
||||||
*/
|
*/
|
||||||
public EncryptionKey[] readServiceKeys(PrincipalName service) {
|
public EncryptionKey[] readServiceKeys(PrincipalName service) {
|
||||||
KeyTabEntry entry;
|
KeyTabEntry entry;
|
||||||
EncryptionKey key;
|
EncryptionKey key;
|
||||||
int size = entries.size();
|
int size = entries.size();
|
||||||
ArrayList<EncryptionKey> keys = new ArrayList<>(size);
|
ArrayList<EncryptionKey> keys = new ArrayList<>(size);
|
||||||
|
|
||||||
for (int i = size-1; i >= 0; i--) {
|
for (int i = size-1; i >= 0; i--) {
|
||||||
entry = entries.elementAt(i);
|
entry = entries.elementAt(i);
|
||||||
if (entry.service.match(service)) {
|
if (entry.service.match(service)) {
|
||||||
@ -260,10 +295,7 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size = keys.size();
|
size = keys.size();
|
||||||
if (size == 0)
|
|
||||||
return null;
|
|
||||||
EncryptionKey[] retVal = keys.toArray(new EncryptionKey[size]);
|
EncryptionKey[] retVal = keys.toArray(new EncryptionKey[size]);
|
||||||
|
|
||||||
// Sort keys according to default_tkt_enctypes
|
// Sort keys according to default_tkt_enctypes
|
||||||
@ -328,10 +360,13 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String tabName() {
|
public String tabName() {
|
||||||
return name;
|
return tabName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////// THE WRITE SIDE ///////////////////////
|
||||||
|
/////////////// only used by ktab tool //////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new entry in the key table.
|
* Adds a new entry in the key table.
|
||||||
* @param service the service which will have a new entry in the key table.
|
* @param service the service which will have a new entry in the key table.
|
||||||
@ -394,7 +429,7 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
*/
|
*/
|
||||||
public synchronized static KeyTab create()
|
public synchronized static KeyTab create()
|
||||||
throws IOException, RealmException {
|
throws IOException, RealmException {
|
||||||
String dname = getDefaultKeyTab();
|
String dname = getDefaultTabName();
|
||||||
return create(dname);
|
return create(dname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,8 +443,7 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
new KeyTabOutputStream(new FileOutputStream(name));
|
new KeyTabOutputStream(new FileOutputStream(name));
|
||||||
kos.writeVersion(KRB5_KT_VNO);
|
kos.writeVersion(KRB5_KT_VNO);
|
||||||
kos.close();
|
kos.close();
|
||||||
singleton = new KeyTab(name);
|
return new KeyTab(name);
|
||||||
return singleton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -417,7 +451,7 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
*/
|
*/
|
||||||
public synchronized void save() throws IOException {
|
public synchronized void save() throws IOException {
|
||||||
KeyTabOutputStream kos =
|
KeyTabOutputStream kos =
|
||||||
new KeyTabOutputStream(new FileOutputStream(name));
|
new KeyTabOutputStream(new FileOutputStream(tabName));
|
||||||
kos.writeVersion(kt_vno);
|
kos.writeVersion(kt_vno);
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
kos.writeEntry(entries.elementAt(i));
|
kos.writeEntry(entries.elementAt(i));
|
||||||
@ -490,13 +524,4 @@ public class KeyTab implements KeyTabConstants {
|
|||||||
kos.write16(KRB5_KT_VNO);
|
kos.write16(KRB5_KT_VNO);
|
||||||
kos.close();
|
kos.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void refresh() {
|
|
||||||
if (singleton != null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println("Refreshing Keytab");
|
|
||||||
}
|
|
||||||
singleton = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -1285,11 +1285,12 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
|
|
||||||
// check permission to access and use the secret key of the
|
// check permission to access and use the secret key of the
|
||||||
// Kerberized "host" service
|
// Kerberized "host" service
|
||||||
if (kerberosKeys != null) {
|
if (kerberosKeys != null && kerberosKeys.length > 0) {
|
||||||
|
|
||||||
if (debug != null && Debug.isOn("handshake")) {
|
if (debug != null && Debug.isOn("handshake")) {
|
||||||
System.out.println("Using Kerberos key: " +
|
for (SecretKey k: kerberosKeys) {
|
||||||
kerberosKeys[0]);
|
System.out.println("Using Kerberos key: " +
|
||||||
|
k);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String serverPrincipal =
|
String serverPrincipal =
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -40,7 +40,7 @@ import sun.security.krb5.PrincipalName;
|
|||||||
import sun.security.ssl.Krb5Proxy;
|
import sun.security.ssl.Krb5Proxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementatin of Krb5Proxy that simply delegates to the appropriate
|
* An implementation of Krb5Proxy that simply delegates to the appropriate
|
||||||
* Kerberos APIs.
|
* Kerberos APIs.
|
||||||
*/
|
*/
|
||||||
public class Krb5ProxyImpl implements Krb5Proxy {
|
public class Krb5ProxyImpl implements Krb5Proxy {
|
||||||
@ -62,7 +62,7 @@ public class Krb5ProxyImpl implements Krb5Proxy {
|
|||||||
@Override
|
@Override
|
||||||
public SecretKey[] getServerKeys(AccessControlContext acc)
|
public SecretKey[] getServerKeys(AccessControlContext acc)
|
||||||
throws LoginException {
|
throws LoginException {
|
||||||
return Krb5Util.getKeys(GSSCaller.CALLER_SSL_SERVER, null, acc);
|
return Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc).getKKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,12 +30,15 @@
|
|||||||
|
|
||||||
package sun.security.krb5.internal.tools;
|
package sun.security.krb5.internal.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import sun.security.krb5.*;
|
import sun.security.krb5.*;
|
||||||
import sun.security.krb5.internal.*;
|
import sun.security.krb5.internal.*;
|
||||||
import sun.security.krb5.internal.ccache.*;
|
import sun.security.krb5.internal.ccache.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||||
import sun.security.util.Password;
|
import sun.security.util.Password;
|
||||||
|
import javax.security.auth.kerberos.KeyTab;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kinit tool for obtaining Kerberos v5 tickets.
|
* Kinit tool for obtaining Kerberos v5 tickets.
|
||||||
@ -153,7 +156,6 @@ public class Kinit {
|
|||||||
System.out.println("Principal is " + principal);
|
System.out.println("Principal is " + principal);
|
||||||
}
|
}
|
||||||
char[] psswd = options.password;
|
char[] psswd = options.password;
|
||||||
EncryptionKey[] skeys = null;
|
|
||||||
boolean useKeytab = options.useKeytabFile();
|
boolean useKeytab = options.useKeytabFile();
|
||||||
if (!useKeytab) {
|
if (!useKeytab) {
|
||||||
if (princName == null) {
|
if (princName == null) {
|
||||||
@ -186,17 +188,9 @@ public class Kinit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// assert princName and principal are nonnull
|
builder = new KrbAsReqBuilder(principal, ktabName == null
|
||||||
skeys = EncryptionKey.acquireSecretKeys(principal, ktabName);
|
? KeyTab.getInstance()
|
||||||
|
: KeyTab.getInstance(new File(ktabName)));
|
||||||
if (skeys == null || skeys.length == 0) {
|
|
||||||
String msg = "No supported key found in keytab";
|
|
||||||
if (princName != null) {
|
|
||||||
msg += " for principal " + princName;
|
|
||||||
}
|
|
||||||
throw new KrbException(msg);
|
|
||||||
}
|
|
||||||
builder = new KrbAsReqBuilder(principal, skeys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KDCOptions opt = new KDCOptions();
|
KDCOptions opt = new KDCOptions();
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -95,16 +95,15 @@ public class Klist {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'k':
|
case 'k':
|
||||||
if (klist.name == null) {
|
try {
|
||||||
klist.target = KeyTab.getInstance();
|
KeyTab ktab = KeyTab.getInstance(klist.name);
|
||||||
klist.name = KeyTab.tabName();
|
klist.target = ktab;
|
||||||
} else klist.target = KeyTab.getInstance(klist.name);
|
klist.name = ktab.tabName();
|
||||||
if (klist.target != null) {
|
} catch (Exception e) {
|
||||||
klist.displayTab();
|
|
||||||
} else {
|
|
||||||
klist.displayMessage("KeyTab");
|
klist.displayMessage("KeyTab");
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
|
klist.displayTab();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (klist.name != null) {
|
if (klist.name != null) {
|
||||||
@ -295,9 +294,10 @@ public class Klist {
|
|||||||
|
|
||||||
void displayMessage(String target) {
|
void displayMessage(String target) {
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
name = "";
|
System.out.println("Default " + target + " not found.");
|
||||||
|
} else {
|
||||||
|
System.out.println(target + " " + name + " not found.");
|
||||||
}
|
}
|
||||||
System.out.println(target + " " + name + " not found.");
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Reformats the date from the form -
|
* Reformats the date from the form -
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2010, 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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -321,7 +321,7 @@ public class Ktab {
|
|||||||
* Lists key table name and entries in it.
|
* Lists key table name and entries in it.
|
||||||
*/
|
*/
|
||||||
void listKt() {
|
void listKt() {
|
||||||
System.out.println("Keytab name: " + KeyTab.tabName());
|
System.out.println("Keytab name: " + table.tabName());
|
||||||
KeyTabEntry[] entries = table.getEntries();
|
KeyTabEntry[] entries = table.getEntries();
|
||||||
if ((entries != null) && (entries.length > 0)) {
|
if ((entries != null) && (entries.length > 0)) {
|
||||||
String[][] output = new String[entries.length+1][showTime?3:2];
|
String[][] output = new String[entries.length+1][showTime?3:2];
|
||||||
|
@ -44,6 +44,7 @@ import com.sun.security.jgss.InquireType;
|
|||||||
import com.sun.security.jgss.AuthorizationDataEntry;
|
import com.sun.security.jgss.AuthorizationDataEntry;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import javax.security.auth.kerberos.KeyTab;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context of a JGSS subject, encapsulating Subject and GSSContext.
|
* Context of a JGSS subject, encapsulating Subject and GSSContext.
|
||||||
@ -107,15 +108,19 @@ public class Context {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Context fromUserPass(
|
||||||
|
String user, char[] pass, boolean storeKey) throws Exception {
|
||||||
|
return fromUserPass(null, user, pass, storeKey);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Logins with a username and a password, using Krb5LoginModule directly
|
* Logins with a username and a password, using Krb5LoginModule directly
|
||||||
* @param storeKey true if key should be saved, used on acceptor side
|
* @param storeKey true if key should be saved, used on acceptor side
|
||||||
*/
|
*/
|
||||||
public static Context fromUserPass(String user, char[] pass, boolean storeKey)
|
public static Context fromUserPass(Subject s,
|
||||||
throws Exception {
|
String user, char[] pass, boolean storeKey) throws Exception {
|
||||||
Context out = new Context();
|
Context out = new Context();
|
||||||
out.name = user;
|
out.name = user;
|
||||||
out.s = new Subject();
|
out.s = s == null ? new Subject() : s;
|
||||||
Krb5LoginModule krb5 = new Krb5LoginModule();
|
Krb5LoginModule krb5 = new Krb5LoginModule();
|
||||||
Map<String, String> map = new HashMap<>();
|
Map<String, String> map = new HashMap<>();
|
||||||
Map<String, Object> shared = new HashMap<>();
|
Map<String, Object> shared = new HashMap<>();
|
||||||
@ -198,12 +203,25 @@ public class Context {
|
|||||||
* @throws java.lang.Exception
|
* @throws java.lang.Exception
|
||||||
*/
|
*/
|
||||||
public void startAsServer(final Oid mech) throws Exception {
|
public void startAsServer(final Oid mech) throws Exception {
|
||||||
|
startAsServer(null, mech);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts as a server with the specified service name
|
||||||
|
* @param name the service name
|
||||||
|
* @param mech GSS mech
|
||||||
|
* @throws java.lang.Exception
|
||||||
|
*/
|
||||||
|
public void startAsServer(final String name, final Oid mech) throws Exception {
|
||||||
doAs(new Action() {
|
doAs(new Action() {
|
||||||
@Override
|
@Override
|
||||||
public byte[] run(Context me, byte[] dummy) throws Exception {
|
public byte[] run(Context me, byte[] dummy) throws Exception {
|
||||||
GSSManager m = GSSManager.getInstance();
|
GSSManager m = GSSManager.getInstance();
|
||||||
me.x = (ExtendedGSSContext)m.createContext(m.createCredential(
|
me.x = (ExtendedGSSContext)m.createContext(m.createCredential(
|
||||||
null,
|
name == null ? null :
|
||||||
|
(name.indexOf('@') < 0 ?
|
||||||
|
m.createName(name, null) :
|
||||||
|
m.createName(name, GSSName.NT_HOSTBASED_SERVICE)),
|
||||||
GSSCredential.INDEFINITE_LIFETIME,
|
GSSCredential.INDEFINITE_LIFETIME,
|
||||||
mech,
|
mech,
|
||||||
GSSCredential.ACCEPT_ONLY));
|
GSSCredential.ACCEPT_ONLY));
|
||||||
@ -229,6 +247,14 @@ public class Context {
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the internal subject.
|
||||||
|
* @return the subject
|
||||||
|
*/
|
||||||
|
public Subject s() {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes the GSSContext within
|
* Disposes the GSSContext within
|
||||||
* @throws org.ietf.jgss.GSSException
|
* @throws org.ietf.jgss.GSSException
|
||||||
@ -297,7 +323,7 @@ public class Context {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
;// Don't care
|
;// Don't care
|
||||||
}
|
}
|
||||||
System.out.println("=====================================");
|
System.out.println("====== Private Credentials Set ======");
|
||||||
for (Object o : s.getPrivateCredentials()) {
|
for (Object o : s.getPrivateCredentials()) {
|
||||||
System.out.println(" " + o.getClass());
|
System.out.println(" " + o.getClass());
|
||||||
if (o instanceof KerberosTicket) {
|
if (o instanceof KerberosTicket) {
|
||||||
@ -315,6 +341,8 @@ public class Context {
|
|||||||
for (Object k : map.keySet()) {
|
for (Object k : map.keySet()) {
|
||||||
System.out.println(" " + k + ": " + map.get(k));
|
System.out.println(" " + k + ": " + map.get(k));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println(" " + o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x != null && x instanceof ExtendedGSSContext) {
|
if (x != null && x instanceof ExtendedGSSContext) {
|
||||||
|
140
jdk/test/sun/security/krb5/auto/DynamicKeytab.java
Normal file
140
jdk/test/sun/security/krb5/auto/DynamicKeytab.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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 6894072
|
||||||
|
* @run main/othervm DynamicKeytab
|
||||||
|
* @summary always refresh keytab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import org.ietf.jgss.GSSException;
|
||||||
|
import sun.security.jgss.GSSUtil;
|
||||||
|
import sun.security.krb5.KrbException;
|
||||||
|
import sun.security.krb5.internal.Krb5;
|
||||||
|
|
||||||
|
public class DynamicKeytab {
|
||||||
|
|
||||||
|
Context c, s;
|
||||||
|
public static void main(String[] args)
|
||||||
|
throws Exception {
|
||||||
|
new DynamicKeytab().go();
|
||||||
|
}
|
||||||
|
|
||||||
|
void go() throws Exception {
|
||||||
|
OneKDC k = new OneKDC(null);
|
||||||
|
k.writeJAASConf();
|
||||||
|
|
||||||
|
new File(OneKDC.KTAB).delete();
|
||||||
|
|
||||||
|
|
||||||
|
// Starts with no keytab
|
||||||
|
c = Context.fromJAAS("client");
|
||||||
|
s = Context.fromJAAS("com.sun.security.jgss.krb5.accept");
|
||||||
|
|
||||||
|
// Test 1: read new key 1 from keytab
|
||||||
|
k.addPrincipal(OneKDC.SERVER, "pass1".toCharArray());
|
||||||
|
k.writeKtab(OneKDC.KTAB);
|
||||||
|
connect();
|
||||||
|
|
||||||
|
// Test 2: service key cached, find 1 in keytab (now contains 1 and 2)
|
||||||
|
k.addPrincipal(OneKDC.SERVER, "pass2".toCharArray());
|
||||||
|
k.appendKtab(OneKDC.KTAB);
|
||||||
|
connect();
|
||||||
|
|
||||||
|
// Test 3: re-login. Now find 2 in keytab
|
||||||
|
c = Context.fromJAAS("client");
|
||||||
|
connect();
|
||||||
|
|
||||||
|
// Test 4: re-login, KDC use 3 this time.
|
||||||
|
c = Context.fromJAAS("client");
|
||||||
|
// Put 3 and 4 into keytab but keep the real key back to 3.
|
||||||
|
k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray());
|
||||||
|
k.appendKtab(OneKDC.KTAB);
|
||||||
|
k.addPrincipal(OneKDC.SERVER, "pass4".toCharArray());
|
||||||
|
k.appendKtab(OneKDC.KTAB);
|
||||||
|
k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray());
|
||||||
|
connect();
|
||||||
|
|
||||||
|
// Test 5: invalid keytab file, should ignore
|
||||||
|
new FileOutputStream(OneKDC.KTAB).write("BADBADBAD".getBytes());
|
||||||
|
connect();
|
||||||
|
|
||||||
|
// Test 6: delete keytab file, identical to revoke all
|
||||||
|
new File(OneKDC.KTAB).delete();
|
||||||
|
try {
|
||||||
|
connect();
|
||||||
|
throw new Exception("Should not success");
|
||||||
|
} catch (GSSException gsse) {
|
||||||
|
System.out.println(gsse);
|
||||||
|
KrbException ke = (KrbException)gsse.getCause();
|
||||||
|
// KrbApReq.authenticate(*) if (dkey == null)...
|
||||||
|
// This should have been Krb5.KRB_AP_ERR_NOKEY
|
||||||
|
if (ke.returnCode() != Krb5.API_INVALID_ARG) {
|
||||||
|
throw new Exception("Not expected failure code: " +
|
||||||
|
ke.returnCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 7: 3 revoked, should fail (now contains only 5)
|
||||||
|
k.addPrincipal(OneKDC.SERVER, "pass5".toCharArray());
|
||||||
|
k.writeKtab(OneKDC.KTAB); // overwrite keytab, which means
|
||||||
|
// old key is revoked
|
||||||
|
try {
|
||||||
|
connect();
|
||||||
|
throw new Exception("Should not success");
|
||||||
|
} catch (GSSException gsse) {
|
||||||
|
System.out.println(gsse);
|
||||||
|
KrbException ke = (KrbException)gsse.getCause();
|
||||||
|
if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) {
|
||||||
|
throw new Exception("Not expected failure code: " +
|
||||||
|
ke.returnCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 8: an empty KDC means revoke all
|
||||||
|
KDC.create("EMPTY.REALM").writeKtab(OneKDC.KTAB);
|
||||||
|
try {
|
||||||
|
connect();
|
||||||
|
throw new Exception("Should not success");
|
||||||
|
} catch (GSSException gsse) {
|
||||||
|
System.out.println(gsse);
|
||||||
|
KrbException ke = (KrbException)gsse.getCause();
|
||||||
|
// KrbApReq.authenticate(*) if (dkey == null)...
|
||||||
|
// This should have been Krb5.KRB_AP_ERR_NOKEY
|
||||||
|
if (ke.returnCode() != Krb5.API_INVALID_ARG) {
|
||||||
|
throw new Exception("Not expected failure code: " +
|
||||||
|
ke.returnCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect() throws Exception {
|
||||||
|
Thread.sleep(2000); // make sure ktab timestamp is different
|
||||||
|
c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
Context.handshake(c, s);
|
||||||
|
}
|
||||||
|
}
|
@ -228,7 +228,33 @@ public class KDC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write all principals' keys from multiple KDCsinto one keytab file.
|
* Writes or appends KDC keys into a keytab. See doc for writeMultiKtab.
|
||||||
|
* @param append true if append, otherwise, overwrite.
|
||||||
|
*/
|
||||||
|
private static void writeKtab0(String tab, boolean append, KDC... kdcs)
|
||||||
|
throws IOException, KrbException {
|
||||||
|
KeyTab ktab = append ? KeyTab.getInstance(tab) : KeyTab.create(tab);
|
||||||
|
for (KDC kdc: kdcs) {
|
||||||
|
for (String name : kdc.passwords.keySet()) {
|
||||||
|
char[] pass = kdc.passwords.get(name);
|
||||||
|
int kvno = 0;
|
||||||
|
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),
|
||||||
|
pass,
|
||||||
|
kvno,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ktab.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes all principals' keys from multiple KDCs into one keytab file.
|
||||||
* Note that the keys for the krbtgt principals will not be written.
|
* Note that the keys for the krbtgt principals will not be written.
|
||||||
* <p>
|
* <p>
|
||||||
* Attention: This method references krb5.conf settings. If you need to
|
* Attention: This method references krb5.conf settings. If you need to
|
||||||
@ -252,17 +278,16 @@ public class KDC {
|
|||||||
*/
|
*/
|
||||||
public static void writeMultiKtab(String tab, KDC... kdcs)
|
public static void writeMultiKtab(String tab, KDC... kdcs)
|
||||||
throws IOException, KrbException {
|
throws IOException, KrbException {
|
||||||
KeyTab ktab = KeyTab.create(tab);
|
writeKtab0(tab, false, kdcs);
|
||||||
for (KDC kdc: kdcs) {
|
}
|
||||||
for (String name : kdc.passwords.keySet()) {
|
|
||||||
ktab.addEntry(new PrincipalName(name,
|
/**
|
||||||
name.indexOf('/') < 0 ?
|
* Appends all principals' keys from multiple KDCs to one keytab file.
|
||||||
PrincipalName.KRB_NT_UNKNOWN :
|
* See writeMultiKtab for details.
|
||||||
PrincipalName.KRB_NT_SRV_HST),
|
*/
|
||||||
kdc.passwords.get(name), -1, true);
|
public static void appendMultiKtab(String tab, KDC... kdcs)
|
||||||
}
|
throws IOException, KrbException {
|
||||||
}
|
writeKtab0(tab, true, kdcs);
|
||||||
ktab.save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,6 +297,13 @@ public class KDC {
|
|||||||
KDC.writeMultiKtab(tab, this);
|
KDC.writeMultiKtab(tab, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends keys in this KDC to a ktab.
|
||||||
|
*/
|
||||||
|
public void appendKtab(String tab) throws IOException, KrbException {
|
||||||
|
KDC.appendMultiKtab(tab, this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new principal to this realm with a given password.
|
* Adds a new principal to this realm with a given password.
|
||||||
* @param user the principal's name. For a service principal, use the
|
* @param user the principal's name. For a service principal, use the
|
||||||
|
90
jdk/test/sun/security/krb5/auto/KeyTabCompat.java
Normal file
90
jdk/test/sun/security/krb5/auto/KeyTabCompat.java
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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 6894072
|
||||||
|
* @compile -XDignore.symbol.file KeyTabCompat.java
|
||||||
|
* @run main/othervm KeyTabCompat
|
||||||
|
* @summary always refresh keytab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.security.auth.kerberos.KerberosKey;
|
||||||
|
import sun.security.jgss.GSSUtil;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are 2 compat issues to check:
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* private credentials set.
|
||||||
|
*/
|
||||||
|
public class KeyTabCompat {
|
||||||
|
|
||||||
|
public static void main(String[] args)
|
||||||
|
throws Exception {
|
||||||
|
OneKDC kdc = new OneKDC("aes128-cts");
|
||||||
|
kdc.writeJAASConf();
|
||||||
|
kdc.addPrincipal(OneKDC.SERVER, "pass1".toCharArray());
|
||||||
|
kdc.writeKtab(OneKDC.KTAB);
|
||||||
|
|
||||||
|
Context c, s;
|
||||||
|
|
||||||
|
// Part 1
|
||||||
|
c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
|
||||||
|
s = Context.fromUserPass(OneKDC.USER2, OneKDC.PASS2, true);
|
||||||
|
|
||||||
|
s.s().getPrincipals().clear();
|
||||||
|
|
||||||
|
c.startAsClient(OneKDC.USER2, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
|
||||||
|
Context.handshake(c, s);
|
||||||
|
|
||||||
|
// Part 2
|
||||||
|
c = Context.fromJAAS("client");
|
||||||
|
s = Context.fromJAAS("server");
|
||||||
|
|
||||||
|
c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
s.status();
|
||||||
|
|
||||||
|
if (s.s().getPrivateCredentials(KerberosKey.class).size() != 1) {
|
||||||
|
throw new Exception("There should be one KerberosKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.sleep(2000); // make sure ktab timestamp is different
|
||||||
|
|
||||||
|
kdc.addPrincipal(OneKDC.SERVER, "pass2".toCharArray());
|
||||||
|
kdc.writeKtab(OneKDC.KTAB);
|
||||||
|
|
||||||
|
Context.handshake(c, s);
|
||||||
|
s.status();
|
||||||
|
|
||||||
|
if (s.s().getPrivateCredentials(KerberosKey.class).size() != 1) {
|
||||||
|
throw new Exception("There should be only one KerberosKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,6 @@
|
|||||||
* @summary Krb5LoginModule a little too restrictive, and the doc is not clear.
|
* @summary Krb5LoginModule a little too restrictive, and the doc is not clear.
|
||||||
*/
|
*/
|
||||||
import com.sun.security.auth.module.Krb5LoginModule;
|
import com.sun.security.auth.module.Krb5LoginModule;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
@ -36,7 +35,6 @@ import javax.security.auth.callback.Callback;
|
|||||||
import javax.security.auth.callback.CallbackHandler;
|
import javax.security.auth.callback.CallbackHandler;
|
||||||
import javax.security.auth.callback.NameCallback;
|
import javax.security.auth.callback.NameCallback;
|
||||||
import javax.security.auth.callback.PasswordCallback;
|
import javax.security.auth.callback.PasswordCallback;
|
||||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
|
||||||
|
|
||||||
public class LoginModuleOptions {
|
public class LoginModuleOptions {
|
||||||
|
|
||||||
@ -61,10 +59,12 @@ public class LoginModuleOptions {
|
|||||||
// 1. ccache -> keytab
|
// 1. ccache -> keytab
|
||||||
login(null, "useTicketCache", "true", "ticketCache", "krbcc_non_exists",
|
login(null, "useTicketCache", "true", "ticketCache", "krbcc_non_exists",
|
||||||
"useKeyTab", "true", "principal", "dummy");
|
"useKeyTab", "true", "principal", "dummy");
|
||||||
|
|
||||||
// 2. keytab -> shared
|
// 2. keytab -> shared
|
||||||
login(null, "useKeyTab", "true", "principal", "dummy",
|
login(null, "useKeyTab", "true", "principal", "dummy",
|
||||||
"keyTab", "ktab_non_exist",
|
"keyTab", "ktab_non_exist",
|
||||||
"tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
|
"tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
|
||||||
|
|
||||||
// 3. shared -> callback
|
// 3. shared -> callback
|
||||||
// 3.1. useFirstPass, no callback
|
// 3.1. useFirstPass, no callback
|
||||||
boolean failed = false;
|
boolean failed = false;
|
||||||
|
@ -48,7 +48,7 @@ import sun.security.krb5.internal.ktab.KeyTab;
|
|||||||
public class SSL {
|
public class SSL {
|
||||||
|
|
||||||
private static String krb5Cipher;
|
private static String krb5Cipher;
|
||||||
private static final int LOOP_LIMIT = 1;
|
private static final int LOOP_LIMIT = 3;
|
||||||
private static int loopCount = 0;
|
private static int loopCount = 0;
|
||||||
private static volatile String server;
|
private static volatile String server;
|
||||||
private static volatile int port;
|
private static volatile int port;
|
||||||
@ -98,13 +98,13 @@ public class SSL {
|
|||||||
fos.close();
|
fos.close();
|
||||||
f.deleteOnExit();
|
f.deleteOnExit();
|
||||||
|
|
||||||
final Context c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
|
Context c;
|
||||||
final Context s = Context.fromJAAS("ssl");
|
final Context s = Context.fromJAAS("ssl");
|
||||||
|
|
||||||
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
|
// There's no keytab file when server starts.
|
||||||
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
|
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
|
||||||
new Thread(new Runnable() {
|
Thread server = new Thread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
s.doAs(new JsseServerAction(), null);
|
s.doAs(new JsseServerAction(), null);
|
||||||
@ -112,12 +112,57 @@ public class SSL {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).start();
|
});
|
||||||
|
server.setDaemon(true);
|
||||||
|
server.start();
|
||||||
|
|
||||||
// Warm the server
|
// Warm the server
|
||||||
Thread.sleep(2000);
|
Thread.sleep(2000);
|
||||||
|
|
||||||
|
// Now create the keytab
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Add 3 versions of keys into keytab
|
||||||
|
KeyTab ktab = KeyTab.create(OneKDC.KTAB);
|
||||||
|
PrincipalName service = new PrincipalName(
|
||||||
|
"host/" + server, PrincipalName.KRB_NT_SRV_HST);
|
||||||
|
ktab.addEntry(service, "pass1".toCharArray(), 1);
|
||||||
|
ktab.addEntry(service, "pass2".toCharArray(), 2);
|
||||||
|
ktab.addEntry(service, "pass3".toCharArray(), 3);
|
||||||
|
ktab.save();
|
||||||
|
|
||||||
|
// and use the middle one as the real key
|
||||||
|
kdc.addPrincipal("host/" + server, "pass2".toCharArray());
|
||||||
|
*/
|
||||||
|
c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
|
||||||
|
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
c.doAs(new JsseClientAction(), null);
|
c.doAs(new JsseClientAction(), null);
|
||||||
|
|
||||||
|
// Add another version of key, make sure it can be loaded
|
||||||
|
Thread.sleep(2000);
|
||||||
|
ktab = KeyTab.getInstance(OneKDC.KTAB);
|
||||||
|
ktab.addEntry(service, "pass4".toCharArray(), 4, true);
|
||||||
|
ktab.save();
|
||||||
|
kdc.addPrincipal("host/" + server, "pass4".toCharArray());
|
||||||
|
|
||||||
|
c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
|
||||||
|
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
c.doAs(new JsseClientAction(), null);
|
||||||
|
|
||||||
|
// Revoke the old key
|
||||||
|
/*Thread.sleep(2000);
|
||||||
|
ktab = KeyTab.create(OneKDC.KTAB);
|
||||||
|
ktab.addEntry(service, "pass5".toCharArray(), 5, false);
|
||||||
|
ktab.save();
|
||||||
|
|
||||||
|
c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
|
||||||
|
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
try {
|
||||||
|
c.doAs(new JsseClientAction(), null);
|
||||||
|
throw new Exception("Should fail this time.");
|
||||||
|
} catch (SSLException e) {
|
||||||
|
// Correct behavior.
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following codes copied from
|
// Following codes copied from
|
||||||
@ -126,6 +171,7 @@ public class SSL {
|
|||||||
public byte[] run(Context s, byte[] input) throws Exception {
|
public byte[] run(Context s, byte[] input) throws Exception {
|
||||||
SSLSocketFactory sslsf =
|
SSLSocketFactory sslsf =
|
||||||
(SSLSocketFactory) SSLSocketFactory.getDefault();
|
(SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||||
|
System.out.println("Connecting " + server + ":" + port);
|
||||||
SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port);
|
SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port);
|
||||||
|
|
||||||
// Enable only a KRB5 cipher suite.
|
// Enable only a KRB5 cipher suite.
|
||||||
@ -154,6 +200,9 @@ public class SSL {
|
|||||||
System.out.println("Server is: " + peer.toString());
|
System.out.println("Server is: " + peer.toString());
|
||||||
|
|
||||||
sslSocket.close();
|
sslSocket.close();
|
||||||
|
// This line should not be needed. It's the server's duty to
|
||||||
|
// forget the old key
|
||||||
|
//sslSocket.getSession().invalidate();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,6 +214,7 @@ public class SSL {
|
|||||||
SSLServerSocket sslServerSocket =
|
SSLServerSocket sslServerSocket =
|
||||||
(SSLServerSocket) sslssf.createServerSocket(0); // any port
|
(SSLServerSocket) sslssf.createServerSocket(0); // any port
|
||||||
port = sslServerSocket.getLocalPort();
|
port = sslServerSocket.getLocalPort();
|
||||||
|
System.out.println("Listening on " + port);
|
||||||
|
|
||||||
// Enable only a KRB5 cipher suite.
|
// Enable only a KRB5 cipher suite.
|
||||||
String enabledSuites[] = {krb5Cipher};
|
String enabledSuites[] = {krb5Cipher};
|
||||||
|
102
jdk/test/sun/security/krb5/auto/TwoPrinces.java
Normal file
102
jdk/test/sun/security/krb5/auto/TwoPrinces.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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 6894072
|
||||||
|
* @compile -XDignore.symbol.file TwoPrinces.java
|
||||||
|
* @run main/othervm TwoPrinces
|
||||||
|
* @summary always refresh keytab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import sun.security.jgss.GSSUtil;
|
||||||
|
import sun.security.krb5.Config;
|
||||||
|
|
||||||
|
public class TwoPrinces {
|
||||||
|
|
||||||
|
public static void main(String[] args)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
KDC k1 = KDC.create("R1");
|
||||||
|
k1.addPrincipal("u1", "hello".toCharArray());
|
||||||
|
k1.addPrincipalRandKey("krbtgt/R1");
|
||||||
|
k1.addPrincipalRandKey("host/same.host");
|
||||||
|
|
||||||
|
KDC k2 = KDC.create("R2");
|
||||||
|
k2.addPrincipal("u2", "hello".toCharArray());
|
||||||
|
k2.addPrincipalRandKey("krbtgt/R2");
|
||||||
|
k2.addPrincipalRandKey("host/same.host");
|
||||||
|
|
||||||
|
System.setProperty("java.security.krb5.conf", "krb5.conf");
|
||||||
|
|
||||||
|
// R1 is the default realm now
|
||||||
|
KDC.saveConfig("krb5.conf", k1, k2);
|
||||||
|
Config.refresh();
|
||||||
|
|
||||||
|
k1.writeKtab("ktab1");
|
||||||
|
k2.writeKtab("ktab2");
|
||||||
|
|
||||||
|
// A JAAS config file with 2 Krb5LoginModules, after commit, the
|
||||||
|
// subject with have principals and keytabs from both sides
|
||||||
|
System.setProperty("java.security.auth.login.config", "jaas.conf");
|
||||||
|
File f = new File("jaas.conf");
|
||||||
|
FileOutputStream fos = new FileOutputStream(f);
|
||||||
|
fos.write((
|
||||||
|
"me {\n"
|
||||||
|
+ " com.sun.security.auth.module.Krb5LoginModule required"
|
||||||
|
+ " isInitiator=true principal=\"host/same.host@R1\""
|
||||||
|
+ " useKeyTab=true keyTab=ktab1 storeKey=true;\n"
|
||||||
|
+ " com.sun.security.auth.module.Krb5LoginModule required"
|
||||||
|
+ " isInitiator=true principal=\"host/same.host@R2\""
|
||||||
|
+ " useKeyTab=true keyTab=ktab2 storeKey=true;\n"
|
||||||
|
+ "};\n"
|
||||||
|
).getBytes());
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This server side context will be able to act as services in both
|
||||||
|
* realms. Please note that we still don't support a single instance
|
||||||
|
* of server to accept connections from two realms at the same time.
|
||||||
|
* Therefore, we must call startAsServer in a given realm to start
|
||||||
|
* working there. The same Subject never changes anyway.
|
||||||
|
*/
|
||||||
|
Context s = Context.fromJAAS("me");
|
||||||
|
|
||||||
|
// Default realm still R1
|
||||||
|
s.startAsServer("host@same.host", GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
Context c1 = Context.fromUserPass("u1", "hello".toCharArray(), false);
|
||||||
|
c1.startAsClient("host@same.host", GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
Context.handshake(c1, s);
|
||||||
|
|
||||||
|
KDC.saveConfig("krb5.conf", k2, k1);
|
||||||
|
Config.refresh();
|
||||||
|
|
||||||
|
// Default realm now R2
|
||||||
|
s.startAsServer("host@same.host", GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
Context c2 = Context.fromUserPass("u2", "hello".toCharArray(), false);
|
||||||
|
c2.startAsClient("host@same.host", GSSUtil.GSS_KRB5_MECH_OID);
|
||||||
|
Context.handshake(c2, s);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -44,7 +44,6 @@ public class KeyTabIndex {
|
|||||||
KeyTab.getInstance("ktab").getClass();
|
KeyTab.getInstance("ktab").getClass();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
KeyTab.refresh();
|
|
||||||
for (int i=0; i<10; i++) {
|
for (int i=0; i<10; i++) {
|
||||||
new Thread(t).start();
|
new Thread(t).start();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user