--- a/jdk/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java Sat Feb 09 16:43:49 2013 +0800
@@ -52,16 +52,19 @@
* principal set and private credentials set are updated only when
* <code>commit</code> is called.
* When <code>commit</code> is called, the <code>KerberosPrincipal</code>
- * is added to the <code>Subject</code>'s
- * principal set and <code>KerberosTicket</code> is
+ * is added to the <code>Subject</code>'s principal set (unless the
+ * <code>principal</code> is specified as "*"). If <code>isInitiator</code>
+ * is true, the <code>KerberosTicket</code> is
* added to the <code>Subject</code>'s private credentials.
*
* <p> If the configuration entry for <code>KerberosLoginModule</code>
* has the option <code>storeKey</code> set to true, then
- * <code>KerberosKey</code> will also be added to the
+ * <code>KerberosKey</code> or <code>KeyTab</code> will also be added to the
* subject's private credentials. <code>KerberosKey</code>, the principal's
- * key will be either obtained from the keytab or
- * derived from user's password.
+ * key(s) will be derived from user's password, and <code>KeyTab</code> is
+ * the keytab used when <code>useKeyTab</code> is set to true. The
+ * <code>KeyTab</code> object is restricted to be used by the specified
+ * principal unless the principal value is "*".
*
* <p> This <code>LoginModule</code> recognizes the <code>doNotPrompt</code>
* option. If set to true the user will not be prompted for the password.
@@ -75,8 +78,8 @@
*
* <p> The principal name can be specified in the configuration entry
* by using the option <code>principal</code>. The principal name
- * can either be a simple user name or a service name such as
- * <code>host/mission.eng.sun.com</code>. The principal can also
+ * can either be a simple user name, a service name such as
+ * <code>host/mission.eng.sun.com</code>, or "*". The principal can also
* be set using the system property <code>sun.security.krb5.principal</code>.
* This property is checked during login. If this property is not set, then
* the principal name from the configuration is used. In the
@@ -87,11 +90,10 @@
*
* <p> The following is a list of configuration options supported
* for <code>Krb5LoginModule</code>:
- * <dl>
- * <blockquote><dt><b><code>refreshKrb5Config</code></b>:</dt>
+ * <blockquote><dl>
+ * <dt><b><code>refreshKrb5Config</code></b>:</dt>
* <dd> Set this to true, if you want the configuration
* to be refreshed before the <code>login</code> method is called.</dd>
- * <P>
* <dt><b><code>useTicketCache</code></b>:</dt>
* <dd>Set this to true, if you want the
* TGT to be obtained
@@ -112,19 +114,16 @@
* <code>ticketCache</code>.
* For Windows, if a ticket cannot be retrieved from the file ticket cache,
* it will use Local Security Authority (LSA) API to get the TGT.
- * <P>
* <dt><b><code>ticketCache</code></b>:</dt>
* <dd>Set this to the name of the ticket
* cache that contains user's TGT.
* If this is set, <code>useTicketCache</code>
* must also be set to true; Otherwise a configuration error will
* be returned.</dd>
- * <P>
* <dt><b><code>renewTGT</code></b>:</dt>
* <dd>Set this to true, if you want to renew
* the TGT. If this is set, <code>useTicketCache</code> must also be
* set to true; otherwise a configuration error will be returned.</dd>
- * <p>
* <dt><b><code>doNotPrompt</code></b>:</dt>
* <dd>Set this to true if you do not want to be
* prompted for the password
@@ -132,7 +131,6 @@
* or through shared state.(Default is false)
* If set to true, credential must be obtained through cache, keytab,
* or shared state. Otherwise, authentication will fail.</dd>
- * <P>
* <dt><b><code>useKeyTab</code></b>:</dt>
* <dd>Set this to true if you
* want the module to get the principal's key from the
@@ -144,15 +142,15 @@
* If it is not specified in the Kerberos configuration file
* then it will look for the file
* <code>{user.home}{file.separator}</code>krb5.keytab.</dd>
- * <P>
* <dt><b><code>keyTab</code></b>:</dt>
* <dd>Set this to the file name of the
* keytab to get principal's secret key.</dd>
- * <P>
* <dt><b><code>storeKey</code></b>:</dt>
- * <dd>Set this to true to if you want the
- * principal's key to be stored in the Subject's private credentials. </dd>
- * <p>
+ * <dd>Set this to true to if you want the keytab or the
+ * principal's key to be stored in the Subject's private credentials.
+ * For <code>isInitiator</code> being false, if <code>principal</code>
+ * is "*", the {@link KeyTab} stored can be used by anyone, otherwise,
+ * it's restricted to be used by the specified principal only.</dd>
* <dt><b><code>principal</code></b>:</dt>
* <dd>The name of the principal that should
* be used. The principal can be a simple username such as
@@ -165,8 +163,13 @@
* <code>sun.security.krb5.principal</code>. In addition, if this
* system property is defined, then it will be used. If this property
* is not set, then the principal name from the configuration will be
- * used.</dd>
- * <P>
+ * used.
+ * The principal name can be set to "*" when <code>isInitiator</code> is false.
+ * In this case, the acceptor is not bound to a single principal. It can
+ * act as any principal an initiator requests if keys for that principal
+ * can be found. When <code>isInitiator</code> is true, the principal name
+ * cannot be set to "*".
+ * </dd>
* <dt><b><code>isInitiator</code></b>:</dt>
* <dd>Set this to true, if initiator. Set this to false, if acceptor only.
* (Default is true).
@@ -177,18 +180,20 @@
* <code>Configuration</code>
* options that enable you to share username and passwords across different
* authentication modules:
- * <pre>
+ * <blockquote><dl>
*
- * useFirstPass if, true, this LoginModule retrieves the
+ * <dt><b><code>useFirstPass</code></b>:</dt>
+ * <dd>if, true, this LoginModule retrieves the
* username and password from the module's shared state,
* using "javax.security.auth.login.name" and
* "javax.security.auth.login.password" as the respective
* keys. The retrieved values are used for authentication.
* If authentication fails, no attempt for a retry
* is made, and the failure is reported back to the
- * calling application.
+ * calling application.</dd>
*
- * tryFirstPass if, true, this LoginModule retrieves the
+ * <dt><b><code>tryFirstPass</code></b>:</dt>
+ * <dd>if, true, this LoginModule retrieves the
* the username and password from the module's shared
* state using "javax.security.auth.login.name" and
* "javax.security.auth.login.password" as the respective
@@ -198,26 +203,28 @@
* CallbackHandler to retrieve a new username
* and password, and another attempt to authenticate
* is made. If the authentication fails,
- * the failure is reported back to the calling application
+ * the failure is reported back to the calling application</dd>
*
- * storePass if, true, this LoginModule stores the username and
+ * <dt><b><code>storePass</code></b>:</dt>
+ * <dd>if, true, this LoginModule stores the username and
* password obtained from the CallbackHandler in the
* modules shared state, using
* "javax.security.auth.login.name" and
* "javax.security.auth.login.password" as the respective
* keys. This is not performed if existing values already
* exist for the username and password in the shared
- * state, or if authentication fails.
+ * state, or if authentication fails.</dd>
*
- * clearPass if, true, this LoginModule clears the
+ * <dt><b><code>clearPass</code></b>:</dt>
+ * <dd>if, true, this LoginModule clears the
* username and password stored in the module's shared
* state after both phases of authentication
- * (login and commit) have completed.
- * </pre>
+ * (login and commit) have completed.</dd>
+ * </dl></blockquote>
* <p>If the principal system property or key is already provided, the value of
* "javax.security.auth.login.name" in the shared state is ignored.
* <p>When multiple mechanisms to retrieve a ticket or key is provided, the
- * preference order looks like this:
+ * preference order is:
* <ol>
* <li>ticket cache
* <li>keytab
@@ -225,7 +232,7 @@
* <li>user prompt
* </ol>
* <p>Note that if any step fails, it will fallback to the next step.
- * There's only one exception, it the shared state step fails and
+ * There's only one exception, if the shared state step fails and
* <code>useFirstPass</code>=true, no user prompt is made.
* <p>Examples of some configuration values for Krb5LoginModule in
* JAAS config file and the results are:
@@ -318,7 +325,7 @@
* <p> <code>useKeyTab</code> = true
* <code>keyTab</code>=<keytabname>
* <code>storeKey</code>=true
- * <code>doNotPrompt</code>=true;
+ * <code>doNotPrompt</code>=false;
*</ul>
* <p>The user will be prompted for the service principal name.
* If the principal's
@@ -328,6 +335,14 @@
* If successful the TGT will be added to the
* Subject's private credentials set. Otherwise the authentication will
* fail.
+ * <ul>
+ * <p> <code>isInitiator</code> = false <code>useKeyTab</code> = true
+ * <code>keyTab</code>=<keytabname>
+ * <code>storeKey</code>=true
+ * <code>principal</code>=*;
+ *</ul>
+ * <p>The acceptor will be an unbound acceptor and it can act as any principal
+ * as long that principal has keys in the keytab.
*<ul>
* <p>
* <code>useTicketCache</code>=true
@@ -409,6 +424,7 @@
private KerberosTicket kerbTicket = null;
private KerberosKey[] kerbKeys = null;
private StringBuffer krb5PrincName = null;
+ private boolean unboundServer = false;
private char[] password = null;
private static final String NAME = "javax.security.auth.login.name";
@@ -520,8 +536,6 @@
*/
public boolean login() throws LoginException {
- int len;
- validateConfiguration();
if (refreshKrb5Config) {
try {
if (debug) {
@@ -544,6 +558,12 @@
}
}
+ validateConfiguration();
+
+ if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
+ unboundServer = true;
+ }
+
if (tryFirstPass) {
try {
attemptAuthentication(true);
@@ -698,9 +718,17 @@
* (encKeys == null) to check.
*/
if (useKeyTab) {
- ktab = (keyTabName == null)
- ? KeyTab.getInstance()
- : KeyTab.getInstance(new File(keyTabName));
+ if (!unboundServer) {
+ KerberosPrincipal kp =
+ new KerberosPrincipal(principal.getName());
+ ktab = (keyTabName == null)
+ ? KeyTab.getInstance(kp)
+ : KeyTab.getInstance(kp, new File(keyTabName));
+ } else {
+ ktab = (keyTabName == null)
+ ? KeyTab.getUnboundInstance()
+ : KeyTab.getUnboundInstance(new File(keyTabName));
+ }
if (isInitiator) {
if (Krb5Util.keysFromJavaxKeyTab(ktab, principal).length
== 0) {
@@ -939,6 +967,13 @@
("Configuration Error"
+ " - either useTicketCache should be "
+ " true or renewTGT should be false");
+ if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
+ if (isInitiator) {
+ throw new LoginException
+ ("Configuration Error"
+ + " - principal cannot be * when isInitiator is true");
+ }
+ }
}
private boolean isCurrent(Credentials creds)
@@ -1052,7 +1087,10 @@
}
// Let us add the kerbClientPrinc,kerbTicket and KeyTab/KerbKey (if
// storeKey is true)
- if (!princSet.contains(kerbClientPrinc)) {
+
+ // We won't add "*" as a KerberosPrincipal
+ if (!unboundServer &&
+ !princSet.contains(kerbClientPrinc)) {
princSet.add(kerbClientPrinc);
}
--- a/jdk/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Sat Feb 09 16:43:49 2013 +0800
@@ -31,8 +31,8 @@
class JavaxSecurityAuthKerberosAccessImpl
implements JavaxSecurityAuthKerberosAccess {
- public EncryptionKey[] keyTabGetEncryptionKeys(
- KeyTab ktab, PrincipalName principal) {
- return ktab.getEncryptionKeys(principal);
+ public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
+ KeyTab ktab) {
+ return ktab.takeSnapshot();
}
}
--- a/jdk/src/share/classes/javax/security/auth/kerberos/KeyTab.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/javax/security/auth/kerberos/KeyTab.java Sat Feb 09 16:43:49 2013 +0800
@@ -41,6 +41,20 @@
* {@link javax.security.auth.Subject Subject} during the commit phase of the
* authentication process.
* <p>
+ * If a {@code KeyTab} object is obtained from {@link #getUnboundInstance()}
+ * or {@link #getUnboundInstance(java.io.File)}, it is unbound and thus can be
+ * used by any service principal. Otherwise, if it's obtained from
+ * {@link #getInstance(KerberosPrincipal)} or
+ * {@link #getInstance(KerberosPrincipal, java.io.File)}, it is bound to the
+ * specific service principal and can only be used by it.
+ * <p>
+ * Please note the constructors {@link #getInstance()} and
+ * {@link #getInstance(java.io.File)} were created when there was no support
+ * for unbound keytabs. These methods should not be used anymore. An object
+ * created with either of these methods are considered to be bound to an
+ * unknown principal, which means, its {@link #isBound()} returns true and
+ * {@link #getPrincipal()} returns null.
+ * <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
@@ -52,7 +66,7 @@
* The keytab file format is described at
* <a href="http://www.ioplex.com/utilities/keytab.txt">
* http://www.ioplex.com/utilities/keytab.txt</a>.
- *
+ * <p>
* @since 1.7
*/
public final class KeyTab {
@@ -74,21 +88,33 @@
// is maintained in snapshot, this field is never "resolved".
private final File file;
+ // Bound user: normally from the "principal" value in a JAAS krb5
+ // login conf. Will be null if it's "*".
+ private final KerberosPrincipal princ;
+
+ private final boolean bound;
+
// Set up JavaxSecurityAuthKerberosAccess in KerberosSecrets
static {
KerberosSecrets.setJavaxSecurityAuthKerberosAccess(
new JavaxSecurityAuthKerberosAccessImpl());
}
- private KeyTab(File file) {
+ private KeyTab(KerberosPrincipal princ, File file, boolean bound) {
+ this.princ = princ;
this.file = file;
+ this.bound = bound;
}
/**
- * Returns a {@code KeyTab} instance from a {@code File} object.
+ * Returns a {@code KeyTab} instance from a {@code File} object
+ * that is bound to an unknown service principal.
* <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.
+ * <p>
+ * Developers should call {@link #getInstance(KerberosPrincipal,File)}
+ * when the bound service principal is known.
* @param file the keytab {@code File} object, must not be null
* @return the keytab instance
* @throws NullPointerException if the {@code file} argument is null
@@ -97,23 +123,99 @@
if (file == null) {
throw new NullPointerException("file must be non null");
}
- return new KeyTab(file);
+ return new KeyTab(null, file, true);
+ }
+
+ /**
+ * Returns an unbound {@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 file argument is null
+ * @since 1.8
+ */
+ public static KeyTab getUnboundInstance(File file) {
+ if (file == null) {
+ throw new NullPointerException("file must be non null");
+ }
+ return new KeyTab(null, file, false);
}
/**
- * Returns the default {@code KeyTab} instance.
+ * Returns a {@code KeyTab} instance from a {@code File} object
+ * that is bound to the specified service principal.
+ * <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 princ the bound service principal, must not be null
+ * @param file the keytab {@code File} object, must not be null
+ * @return the keytab instance
+ * @throws NullPointerException if either of the arguments is null
+ * @since 1.8
+ */
+ public static KeyTab getInstance(KerberosPrincipal princ, File file) {
+ if (princ == null) {
+ throw new NullPointerException("princ must be non null");
+ }
+ if (file == null) {
+ throw new NullPointerException("file must be non null");
+ }
+ return new KeyTab(princ, file, true);
+ }
+
+ /**
+ * Returns the default {@code KeyTab} instance that is bound
+ * to an unknown service principal.
* <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.
+ * <p>
+ * Developers should call {@link #getInstance(KerberosPrincipal)}
+ * when the bound service principal is known.
* @return the default keytab instance.
*/
public static KeyTab getInstance() {
- return new KeyTab(null);
+ return new KeyTab(null, null, true);
+ }
+
+ /**
+ * Returns the default unbound {@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
+ * @since 1.8
+ */
+ public static KeyTab getUnboundInstance() {
+ return new KeyTab(null, null, false);
+ }
+
+ /**
+ * Returns the default {@code KeyTab} instance that is bound
+ * to the specified service principal.
+ * <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.
+ * @param princ the bound service principal, must not be null
+ * @return the default keytab instance
+ * @throws NullPointerException if {@code princ} is null
+ * @since 1.8
+ */
+ public static KeyTab getInstance(KerberosPrincipal princ) {
+ if (princ == null) {
+ throw new NullPointerException("princ must be non null");
+ }
+ return new KeyTab(princ, null, true);
}
//Takes a snapshot of the keytab content
- private sun.security.krb5.internal.ktab.KeyTab takeSnapshot() {
+ sun.security.krb5.internal.ktab.KeyTab takeSnapshot() {
return sun.security.krb5.internal.ktab.KeyTab.getInstance(file);
}
@@ -147,6 +249,9 @@
* <p>
* Any unsupported key read from the keytab is ignored and not included
* in the result.
+ * <p>
+ * If this keytab is bound to a specific principal, calling this method on
+ * another principal will return an empty array.
*
* @param principal the Kerberos principal, must not be null.
* @return the keys (never null, may be empty)
@@ -157,8 +262,11 @@
*/
public KerberosKey[] getKeys(KerberosPrincipal principal) {
try {
- EncryptionKey[] keys = takeSnapshot().readServiceKeys(
- new PrincipalName(principal.getName()));
+ if (princ != null && !principal.equals(princ)) {
+ return new KerberosKey[0];
+ }
+ PrincipalName pn = new PrincipalName(principal.getName());
+ EncryptionKey[] keys = takeSnapshot().readServiceKeys(pn);
KerberosKey[] kks = new KerberosKey[keys.length];
for (int i=0; i<kks.length; i++) {
Integer tmp = keys[i].getKeyVersionNumber();
@@ -195,7 +303,10 @@
}
public String toString() {
- return file == null ? "Default keytab" : file.toString();
+ String s = (file == null) ? "Default keytab" : file.toString();
+ if (!bound) return s;
+ else if (princ == null) return s + " for someone";
+ else return s + " for " + princ;
}
/**
@@ -204,7 +315,7 @@
* @return a hashCode() for the <code>KeyTab</code>
*/
public int hashCode() {
- return Objects.hash(file);
+ return Objects.hash(file, princ, bound);
}
/**
@@ -225,6 +336,31 @@
}
KeyTab otherKtab = (KeyTab) other;
- return Objects.equals(otherKtab.file, file);
+ return Objects.equals(otherKtab.princ, princ) &&
+ Objects.equals(otherKtab.file, file) &&
+ bound == otherKtab.bound;
+ }
+
+ /**
+ * Returns the service principal this {@code KeyTab} object
+ * is bound to. Returns {@code null} if it's not bound.
+ * <p>
+ * Please note the deprecated constructors create a KeyTab object bound for
+ * some unknown principal. In this case, this method also returns null.
+ * User can call {@link #isBound()} to verify this case.
+ * @return the service principal
+ * @since 1.8
+ */
+ public KerberosPrincipal getPrincipal() {
+ return princ;
+ }
+
+ /**
+ * Returns if the keytab is bound to a principal
+ * @return if the keytab is bound to a principal
+ * @since 1.8
+ */
+ public boolean isBound() {
+ return bound;
}
}
--- a/jdk/src/share/classes/sun/security/jgss/LoginConfigImpl.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/sun/security/jgss/LoginConfigImpl.java Sat Feb 09 16:43:49 2013 +0800
@@ -175,6 +175,7 @@
options.put("useKeyTab", "true");
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
+ options.put("principal", "*");
options.put("isInitiator", "false");
} else {
options.put("useTicketCache", "true");
--- a/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Util.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Util.java Sat Feb 09 16:43:49 2013 +0800
@@ -243,14 +243,24 @@
}
/**
+ * A helper method to get a sun..KeyTab from a javax..KeyTab
+ * @param ktab the javax..KeyTab object
+ * @return the sun..KeyTab object
+ */
+ public static sun.security.krb5.internal.ktab.KeyTab
+ snapshotFromJavaxKeyTab(KeyTab ktab) {
+ return KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+ .keyTabTakeSnapshot(ktab);
+ }
+
+ /**
* A helper method to get EncryptionKeys from a javax..KeyTab
- * @param ktab the javax..KeyTab class
+ * @param ktab the javax..KeyTab object
* @param cname the PrincipalName
* @return the EKeys, never null, might be empty
*/
public static EncryptionKey[] keysFromJavaxKeyTab(
KeyTab ktab, PrincipalName cname) {
- return KerberosSecrets.getJavaxSecurityAuthKerberosAccess().
- keyTabGetEncryptionKeys(ktab, cname);
+ return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname);
}
}
--- a/jdk/src/share/classes/sun/security/jgss/krb5/ServiceCreds.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/ServiceCreds.java Sat Feb 09 16:43:49 2013 +0800
@@ -101,9 +101,22 @@
if (serverPrincipal != null) { // A named principal
sc.kp = new KerberosPrincipal(serverPrincipal);
} else {
- if (sc.allPrincs.size() == 1) { // choose the only one
- sc.kp = sc.allPrincs.iterator().next();
- serverPrincipal = sc.kp.getName();
+ // For compatibility reason, we set the name of default principal
+ // to the "only possible" name it can take, which means there is
+ // only one KerberosPrincipal and there is no unbound keytabs
+ if (sc.allPrincs.size() == 1) {
+ boolean hasUnbound = false;
+ for (KeyTab ktab: SubjectComber.findMany(
+ subj, null, null, KeyTab.class)) {
+ if (!ktab.isBound()) {
+ hasUnbound = true;
+ break;
+ }
+ }
+ if (!hasUnbound) {
+ sc.kp = sc.allPrincs.iterator().next();
+ serverPrincipal = sc.kp.getName();
+ }
}
}
@@ -131,20 +144,35 @@
}
/**
- * Gets keys for someone unknown.
- * Used by TLS or as a fallback in getEKeys(). Can still return an
- * empty array.
+ * Gets keys for "someone". Used in 2 cases:
+ * 1. By TLS because it needs to get keys before client comes in.
+ * 2. As a fallback in getEKeys() below.
+ * This method can still return an empty array.
*/
public KerberosKey[] getKKeys() {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
- if (kp != null) {
- return getKKeys(kp);
- } else if (!allPrincs.isEmpty()) {
- return getKKeys(allPrincs.iterator().next());
+ KerberosPrincipal one = kp; // named principal
+ if (one == null && !allPrincs.isEmpty()) { // or, a known principal
+ one = allPrincs.iterator().next();
}
- return new KerberosKey[0];
+ if (one == null) { // Or, some random one
+ for (KeyTab ktab: ktabs) {
+ // Must be unbound keytab, otherwise, allPrincs is not empty
+ PrincipalName pn =
+ Krb5Util.snapshotFromJavaxKeyTab(ktab).getOneName();
+ if (pn != null) {
+ one = new KerberosPrincipal(pn.getName());
+ break;
+ }
+ }
+ }
+ if (one != null) {
+ return getKKeys(one);
+ } else {
+ return new KerberosKey[0];
+ }
}
/**
@@ -152,15 +180,13 @@
* @param princ the target name initiator requests. Not null.
* @return keys for the princ, never null, might be empty
*/
- private KerberosKey[] getKKeys(KerberosPrincipal princ) {
- ArrayList<KerberosKey> keys = new ArrayList<>();
- if (kp != null && !princ.equals(kp)) {
- return new KerberosKey[0]; // Not me
+ public KerberosKey[] getKKeys(KerberosPrincipal princ) {
+ if (destroyed) {
+ throw new IllegalStateException("This object is destroyed");
}
- if (!allPrincs.contains(princ)) {
- return new KerberosKey[0]; // Not someone I know, This check
- // is necessary but a KeyTab has
- // no principal name recorded.
+ ArrayList<KerberosKey> keys = new ArrayList<>();
+ if (kp != null && !princ.equals(kp)) { // named principal
+ return new KerberosKey[0];
}
for (KerberosKey k: kk) {
if (k.getPrincipal().equals(princ)) {
@@ -168,6 +194,13 @@
}
}
for (KeyTab ktab: ktabs) {
+ if (ktab.getPrincipal() == null && ktab.isBound()) {
+ // legacy bound keytab. although we don't know who
+ // the bound principal is, it must be in allPrincs
+ if (!allPrincs.contains(princ)) {
+ continue; // skip this legacy bound keytab
+ }
+ }
for (KerberosKey k: ktab.getKeys(princ)) {
keys.add(k);
}
@@ -186,12 +219,12 @@
}
KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName()));
if (kkeys.length == 0) {
- // Note: old JDK does not perform real name checking. If the
- // acceptor starts by name A but initiator requests for B,
- // as long as their keys match (i.e. A's keys can decrypt B's
- // service ticket), the authentication is OK. There are real
- // customers depending on this to use different names for a
- // single service.
+ // Fallback: old JDK does not perform real name checking. If the
+ // acceptor has host.sun.com but initiator requests for host,
+ // as long as their keys match (i.e. keys for one can decrypt
+ // the other's service ticket), the authentication is OK.
+ // There are real customers depending on this to use different
+ // names for a single service.
kkeys = getKKeys();
}
EncryptionKey[] ekeys = new EncryptionKey[kkeys.length];
--- a/jdk/src/share/classes/sun/security/jgss/krb5/SubjectComber.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/SubjectComber.java Sat Feb 09 16:43:49 2013 +0800
@@ -86,36 +86,39 @@
List<T> answer = (oneOnly ? null : new ArrayList<T>());
if (credClass == KeyTab.class) {
- // TODO: There is currently no good way to filter out keytabs
- // not for serverPrincipal. We can only check the principal
- // set. If the server is not there, we can be sure none of the
- // keytabs should be used, otherwise, use all for safety.
- boolean useAll = false;
- if (serverPrincipal != null) {
- for (KerberosPrincipal princ:
- subject.getPrincipals(KerberosPrincipal.class)) {
- if (princ.getName().equals(serverPrincipal)) {
- useAll = true;
- break;
+ Iterator<KeyTab> iterator =
+ subject.getPrivateCredentials(KeyTab.class).iterator();
+ while (iterator.hasNext()) {
+ KeyTab t = iterator.next();
+ if (serverPrincipal != null && t.isBound()) {
+ KerberosPrincipal name = t.getPrincipal();
+ if (name != null) {
+ if (!serverPrincipal.equals(name.getName())) {
+ continue;
+ }
+ } else {
+ // legacy bound keytab. although we don't know who
+ // the bound principal is, it must be in allPrincs
+ boolean found = false;
+ for (KerberosPrincipal princ:
+ subject.getPrincipals(KerberosPrincipal.class)) {
+ if (princ.getName().equals(serverPrincipal)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) continue;
}
}
- } else {
- useAll = true;
- }
- if (useAll) {
- Iterator<KeyTab> iterator =
- subject.getPrivateCredentials(KeyTab.class).iterator();
- while (iterator.hasNext()) {
- KeyTab t = iterator.next();
- if (DEBUG) {
- System.out.println("Found " + credClass.getSimpleName()
- + " " + t);
- }
- if (oneOnly) {
- return t;
- } else {
- answer.add(credClass.cast(t));
- }
+ // Check passed, we can add now
+ if (DEBUG) {
+ System.out.println("Found " + credClass.getSimpleName()
+ + " " + t);
+ }
+ if (oneOnly) {
+ return t;
+ } else {
+ answer.add(credClass.cast(t));
}
}
} else if (credClass == KerberosKey.class) {
--- a/jdk/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Sat Feb 09 16:43:49 2013 +0800
@@ -35,9 +35,8 @@
*/
public interface JavaxSecurityAuthKerberosAccess {
/**
- * Returns keys for a principal in a keytab.
- * @return the keys, never null, can be empty.
+ * Returns a snapshot to the backing keytab
*/
- public EncryptionKey[] keyTabGetEncryptionKeys(
- KeyTab ktab, PrincipalName principal);
+ public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
+ KeyTab ktab);
}
--- a/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java Sat Feb 09 16:43:49 2013 +0800
@@ -46,6 +46,7 @@
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
+import sun.security.jgss.krb5.ServiceCreds;
/**
* This class represents key table. The key table functions deal with storing
@@ -268,6 +269,15 @@
}
/**
+ * Returns a principal name in this keytab. Used by
+ * {@link ServiceCreds#getKKeys()}.
+ */
+ public PrincipalName getOneName() {
+ int size = entries.size();
+ return size > 0 ? entries.elementAt(size-1).service : null;
+ }
+
+ /**
* Reads all keys for a service from the keytab file that have
* etypes that have been configured for use. If there are multiple
* keys with same etype, the one with the highest kvno is returned.
--- a/jdk/src/share/classes/sun/security/provider/ConfigSpiFile.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/src/share/classes/sun/security/provider/ConfigSpiFile.java Sat Feb 09 16:43:49 2013 +0800
@@ -404,6 +404,7 @@
st.wordChars('$', '$');
st.wordChars('_', '_');
st.wordChars('-', '-');
+ st.wordChars('*', '*');
st.lowerCaseMode(false);
st.slashSlashComments(true);
st.slashStarComments(true);
--- a/jdk/test/sun/security/krb5/ServiceCredsCombination.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/test/sun/security/krb5/ServiceCredsCombination.java Sat Feb 09 16:43:49 2013 +0800
@@ -62,11 +62,38 @@
check("b", "b", princ("a"), princ("b"), oldktab(), oldktab());
check(null, null, princ("a"), princ("b"), oldktab(), oldktab());
check("x", "NOCRED", princ("a"), princ("b"), oldktab(), oldktab());
+ // bound ktab
+ check("c", "c", princ("c"), ktab("c"));
+ check(null, "c", princ("c"), ktab("c"));
+ // unbound ktab
+ check("x", "x", ktab());
+ check(null, null, ktab());
+ // Two bound ktab
+ check("c1", "c1", princ("c1"), princ("c2"), ktab("c1"), ktab("c2"));
+ check("c2", "c2", princ("c1"), princ("c2"), ktab("c1"), ktab("c2"));
+ check("x", "NOCRED", princ("c1"), princ("c2"), ktab("c1"), ktab("c2"));
+ check(null, null, princ("c1"), princ("c2"), ktab("c1"), ktab("c2"));
+ // One bound, one unbound
+ check("c1", "c1", princ("c1"), ktab("c1"), ktab());
+ check("x", "x", princ("c1"), ktab("c1"), ktab());
+ check(null, null, princ("c1"), ktab("c1"), ktab());
+ // Two unbound ktab
+ check("x", "x", ktab(), ktab());
+ check(null, null, ktab(), ktab());
// pass + old ktab
check("a", "a", princ("a"), princ("b"), key("a"), oldktab());
check("b", "b", princ("a"), princ("b"), key("a"), oldktab());
check(null, null, princ("a"), princ("b"), key("a"), oldktab());
check("x", "NOCRED", princ("a"), princ("b"), key("a"), oldktab());
+ // pass + bound ktab
+ check("a", "a", princ("a"), princ("c"), key("a"), ktab("c"));
+ check("c", "c", princ("a"), princ("c"), key("a"), ktab("c"));
+ check("x", "NOCRED", princ("a"), princ("c"), key("a"), ktab("c"));
+ check(null, null, princ("a"), princ("c"), key("a"), ktab("c"));
+ // pass + unbound ktab
+ check("a", "a", princ("a"), key("a"), ktab());
+ check("x", "x", princ("a"), key("a"), ktab());
+ check(null, null, princ("a"), key("a"), ktab());
// Compatibility, automatically add princ for keys
check(null, "a", key("a"));
check("x", "NOCRED", key("a"));
@@ -130,4 +157,10 @@
private static KeyTab oldktab() {
return KeyTab.getInstance();
}
+ static KeyTab ktab(String s) {
+ return KeyTab.getInstance(princ(s));
+ }
+ static KeyTab ktab() {
+ return KeyTab.getUnboundInstance();
+ }
}
--- a/jdk/test/sun/security/krb5/auto/AcceptPermissions.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/test/sun/security/krb5/auto/AcceptPermissions.java Sat Feb 09 16:43:49 2013 +0800
@@ -26,7 +26,8 @@
* @bug 9999999
* @summary default principal can act as anyone
* @compile -XDignore.symbol.file AcceptPermissions.java
- * @run main/othervm AcceptPermissions
+ * @run main/othervm AcceptPermissions two
+ * @run main/othervm AcceptPermissions unbound
*/
import java.nio.file.Files;
@@ -83,15 +84,20 @@
public static void main(String[] args) throws Exception {
System.setSecurityManager(new AcceptPermissions());
new OneKDC(null).writeJAASConf();
- String two = "two {\n"
+ String moreEntries = "two {\n"
+ " com.sun.security.auth.module.Krb5LoginModule required"
+ " principal=\"" + OneKDC.SERVER + "\" useKeyTab=true"
+ " isInitiator=false storeKey=true;\n"
+ " com.sun.security.auth.module.Krb5LoginModule required"
+ " principal=\"" + OneKDC.BACKEND + "\" useKeyTab=true"
+ " isInitiator=false storeKey=true;\n"
+ + "};\n"
+ + "unbound {"
+ + " com.sun.security.auth.module.Krb5LoginModule required"
+ + " principal=* useKeyTab=true"
+ + " isInitiator=false storeKey=true;\n"
+ "};\n";
- Files.write(Paths.get(OneKDC.JAAS_CONF), two.getBytes(),
+ Files.write(Paths.get(OneKDC.JAAS_CONF), moreEntries.getBytes(),
StandardOpenOption.APPEND);
Context c, s;
@@ -114,7 +120,7 @@
// Named principal (even if there are 2 JAAS modules)
initPerms(OneKDC.SERVER);
c = Context.fromJAAS("client");
- s = Context.fromJAAS("two");
+ s = Context.fromJAAS(args[0]);
c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
s.startAsServer(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
checkPerms();
@@ -136,7 +142,7 @@
// Default principal with no predictable name
initPerms(); // permission not needed for cred !!!
c = Context.fromJAAS("client");
- s = Context.fromJAAS("two");
+ s = Context.fromJAAS(args[0]);
c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
checkPerms();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/GSSUnbound.java Sat Feb 09 16:43:49 2013 +0800
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8001104
+ * @summary Unbound SASL service: the GSSAPI/krb5 mech
+ * @compile -XDignore.symbol.file GSSUnbound.java
+ * @run main/othervm GSSUnbound
+ */
+
+import java.security.Security;
+import sun.security.jgss.GSSUtil;
+
+// Testing JGSS without JAAS
+public class GSSUnbound {
+
+ public static void main(String[] args) throws Exception {
+
+ new OneKDC(null);
+
+ Context c, s;
+ c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
+ s = Context.fromThinAir();
+
+ // This is the only setting needed for JGSS without JAAS. The default
+ // JAAS config entries are already created by OneKDC.
+ System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+ c.startAsClient(OneKDC.BACKEND, GSSUtil.GSS_KRB5_MECH_OID);
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+
+ Context.handshake(c, s);
+
+ Context.transmit("i say high --", c, s);
+ Context.transmit(" you say low", s, c);
+
+ s.dispose();
+ c.dispose();
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/OneKDC.java Sat Feb 09 08:35:57 2013 +0000
+++ b/jdk/test/sun/security/krb5/auto/OneKDC.java Sat Feb 09 16:43:49 2013 +0800
@@ -76,6 +76,8 @@
Config.refresh();
writeKtab(KTAB);
+ Security.setProperty("auth.login.defaultCallbackHandler",
+ "OneKDC$CallbackForClient");
}
/**
@@ -93,7 +95,7 @@
" com.sun.security.auth.module.Krb5LoginModule required;\n};\n" +
"com.sun.security.jgss.krb5.accept {\n" +
" com.sun.security.auth.module.Krb5LoginModule required\n" +
- " principal=\"" + SERVER + "\"\n" +
+ " principal=\"*\"\n" +
" useKeyTab=true\n" +
" isInitiator=false\n" +
" storeKey=true;\n};\n" +
@@ -112,7 +114,6 @@
" isInitiator=false;\n};\n"
).getBytes());
fos.close();
- Security.setProperty("auth.login.defaultCallbackHandler", "OneKDC$CallbackForClient");
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/SaslUnbound.java Sat Feb 09 16:43:49 2013 +0800
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8001104
+ * @summary Unbound SASL service: the GSSAPI/krb5 mech
+ * @compile -XDignore.symbol.file SaslUnbound.java
+ * @run main/othervm SaslUnbound 0
+ * @run main/othervm/fail SaslUnbound 1
+ * @run main/othervm/fail SaslUnbound 2
+ * @run main/othervm/fail SaslUnbound 3
+ * @run main/othervm/fail SaslUnbound 4
+ */
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.*;
+
+public class SaslUnbound {
+
+ public static void main(String[] args) throws Exception {
+
+ String serverProtocol, serverName;
+ switch (args[0].charAt(0)) {
+ case '1': // Using another protocol, should fail
+ serverProtocol = "serv";
+ serverName = null;
+ break;
+ case '2': // Using another protocol, should fail
+ serverProtocol = "otherwise";
+ serverName = null;
+ break;
+ case '3': // Using another protocol, should fail
+ serverProtocol = "otherwise";
+ serverName = "host." + OneKDC.REALM;
+ break;
+ case '4': // Bound to another serverName, should fail.
+ serverProtocol = "server";
+ serverName = "host2." + OneKDC.REALM;
+ break;
+ default: // Good unbound server
+ serverProtocol = "server";
+ serverName = null;
+ break;
+ }
+ new OneKDC(null).writeJAASConf();
+ System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+ HashMap clntprops = new HashMap();
+ clntprops.put(Sasl.QOP, "auth-conf");
+ SaslClient sc = Sasl.createSaslClient(
+ new String[]{"GSSAPI"}, null, "server",
+ "host." + OneKDC.REALM, clntprops, null);
+
+ final HashMap srvprops = new HashMap();
+ srvprops.put(Sasl.QOP, "auth,auth-int,auth-conf");
+ SaslServer ss = Sasl.createSaslServer("GSSAPI", serverProtocol,
+ serverName, srvprops,
+ new CallbackHandler() {
+ public void handle(Callback[] callbacks)
+ throws IOException, UnsupportedCallbackException {
+ for (Callback cb : callbacks) {
+ if (cb instanceof RealmCallback) {
+ ((RealmCallback) cb).setText(OneKDC.REALM);
+ } else if (cb instanceof AuthorizeCallback) {
+ ((AuthorizeCallback) cb).setAuthorized(true);
+ }
+ }
+ }
+ });
+
+ byte[] token = new byte[0];
+ while (!sc.isComplete() || !ss.isComplete()) {
+ if (!sc.isComplete()) {
+ token = sc.evaluateChallenge(token);
+ }
+ if (!ss.isComplete()) {
+ token = ss.evaluateResponse(token);
+ }
+ }
+ System.out.println(ss.getNegotiatedProperty(Sasl.BOUND_SERVER_NAME));
+ byte[] hello = "hello".getBytes();
+ token = sc.wrap(hello, 0, hello.length);
+ token = ss.unwrap(token, 0, token.length);
+ if (!Arrays.equals(hello, token)) {
+ throw new Exception("Message altered");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/UnboundService.java Sat Feb 09 16:43:49 2013 +0800
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8001104
+ * @summary Unbound SASL service: the GSSAPI/krb5 mech
+ * @compile -XDignore.symbol.file UnboundService.java
+ * @run main/othervm UnboundService null null
+ * @run main/othervm UnboundService server/host.rabbit.hole null
+ * @run main/othervm UnboundService server/host.rabbit.hole@RABBIT.HOLE null
+ * @run main/othervm/fail UnboundService backend/host.rabbit.hole null
+ * @run main/othervm UnboundService null server@host.rabbit.hole
+ * @run main/othervm UnboundService server/host.rabbit.hole server@host.rabbit.hole
+ * @run main/othervm UnboundService server/host.rabbit.hole@RABBIT.HOLE server@host.rabbit.hole
+ * @run main/othervm/fail UnboundService backend/host.rabbit.hole server@host.rabbit.hole
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import sun.security.jgss.GSSUtil;
+
+public class UnboundService {
+
+ /**
+ * @param args JAAS config pricipal and GSSCredential creation name
+ */
+ public static void main(String[] args) throws Exception {
+
+ String principal = args[0];
+ if (principal.equals("null")) principal = null;
+
+ String server = args[1];
+ if (server.equals("null")) server = null;
+
+ new OneKDC(null).writeJAASConf();
+ File f = new File(OneKDC.JAAS_CONF);
+ try (FileOutputStream fos = new FileOutputStream(f)) {
+ fos.write((
+ "client {\n" +
+ " com.sun.security.auth.module.Krb5LoginModule required;\n};\n" +
+ "unbound {\n" +
+ " com.sun.security.auth.module.Krb5LoginModule required\n" +
+ " useKeyTab=true\n" +
+ " principal=" +
+ (principal==null? "*" :("\"" + principal + "\"")) + "\n" +
+ " doNotPrompt=true\n" +
+ " isInitiator=false\n" +
+ " storeKey=true;\n};\n"
+ ).getBytes());
+ }
+
+ Context c, s;
+ c = Context.fromJAAS("client");
+ s = Context.fromJAAS("unbound");
+
+ c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+ s.startAsServer(server, GSSUtil.GSS_KRB5_MECH_OID);
+
+ Context.handshake(c, s);
+
+ s.dispose();
+ c.dispose();
+ }
+}