8001104: Unbound SASL service: the GSSAPI/krb5 mech
Reviewed-by: valeriep
This commit is contained in:
parent
724f325f44
commit
d8233ec657
@ -52,16 +52,19 @@ import sun.misc.HexDumpEncoder;
|
||||
* 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 @@ import sun.misc.HexDumpEncoder;
|
||||
*
|
||||
* <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 @@ import sun.misc.HexDumpEncoder;
|
||||
*
|
||||
* <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 @@ import sun.misc.HexDumpEncoder;
|
||||
* <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 @@ import sun.misc.HexDumpEncoder;
|
||||
* 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 @@ import sun.misc.HexDumpEncoder;
|
||||
* 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 @@ import sun.misc.HexDumpEncoder;
|
||||
* <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 @@ import sun.misc.HexDumpEncoder;
|
||||
* <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 @@ import sun.misc.HexDumpEncoder;
|
||||
* 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 @@ import sun.misc.HexDumpEncoder;
|
||||
* <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 @@ import sun.misc.HexDumpEncoder;
|
||||
* <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 @@ import sun.misc.HexDumpEncoder;
|
||||
* 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 @@ public class Krb5LoginModule implements LoginModule {
|
||||
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 class Krb5LoginModule implements LoginModule {
|
||||
*/
|
||||
public boolean login() throws LoginException {
|
||||
|
||||
int len;
|
||||
validateConfiguration();
|
||||
if (refreshKrb5Config) {
|
||||
try {
|
||||
if (debug) {
|
||||
@ -544,6 +558,12 @@ public class Krb5LoginModule implements LoginModule {
|
||||
}
|
||||
}
|
||||
|
||||
validateConfiguration();
|
||||
|
||||
if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
|
||||
unboundServer = true;
|
||||
}
|
||||
|
||||
if (tryFirstPass) {
|
||||
try {
|
||||
attemptAuthentication(true);
|
||||
@ -698,9 +718,17 @@ public class Krb5LoginModule implements LoginModule {
|
||||
* (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 @@ public class Krb5LoginModule implements LoginModule {
|
||||
("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 @@ public class Krb5LoginModule implements LoginModule {
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -31,8 +31,8 @@ import sun.security.krb5.PrincipalName;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,20 @@ import sun.security.krb5.RealmException;
|
||||
* {@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 @@ import sun.security.krb5.RealmException;
|
||||
* 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 @@ public final class KeyTab {
|
||||
// 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 @@ public final class KeyTab {
|
||||
if (file == null) {
|
||||
throw new NullPointerException("file must be non null");
|
||||
}
|
||||
return new KeyTab(file);
|
||||
return new KeyTab(null, file, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default {@code KeyTab} instance.
|
||||
* 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 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 @@ public final class KeyTab {
|
||||
* <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 final class KeyTab {
|
||||
*/
|
||||
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 final class KeyTab {
|
||||
}
|
||||
|
||||
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 @@ public final class KeyTab {
|
||||
* @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 @@ public final class KeyTab {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -175,6 +175,7 @@ public class LoginConfigImpl extends Configuration {
|
||||
options.put("useKeyTab", "true");
|
||||
options.put("storeKey", "true");
|
||||
options.put("doNotPrompt", "true");
|
||||
options.put("principal", "*");
|
||||
options.put("isInitiator", "false");
|
||||
} else {
|
||||
options.put("useTicketCache", "true");
|
||||
|
@ -242,15 +242,25 @@ public class Krb5Util {
|
||||
kerbTicket.getClientAddresses());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
@ -101,9 +101,22 @@ public final class ServiceCreds {
|
||||
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 @@ public final class ServiceCreds {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
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];
|
||||
}
|
||||
return new KerberosKey[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,15 +180,13 @@ public final class ServiceCreds {
|
||||
* @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 @@ public final class ServiceCreds {
|
||||
}
|
||||
}
|
||||
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 @@ public final class ServiceCreds {
|
||||
}
|
||||
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];
|
||||
|
@ -86,36 +86,39 @@ class SubjectComber {
|
||||
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) {
|
||||
|
@ -35,9 +35,8 @@ import sun.security.krb5.PrincipalName;
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import java.util.HashMap;
|
||||
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
|
||||
@ -267,6 +268,15 @@ public class KeyTab implements KeyTabConstants {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -404,6 +404,7 @@ public final class ConfigSpiFile extends ConfigurationSpi {
|
||||
st.wordChars('$', '$');
|
||||
st.wordChars('_', '_');
|
||||
st.wordChars('-', '-');
|
||||
st.wordChars('*', '*');
|
||||
st.lowerCaseMode(false);
|
||||
st.slashSlashComments(true);
|
||||
st.slashStarComments(true);
|
||||
|
@ -62,11 +62,38 @@ public class ServiceCredsCombination {
|
||||
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 @@ public class ServiceCredsCombination {
|
||||
private static KeyTab oldktab() {
|
||||
return KeyTab.getInstance();
|
||||
}
|
||||
static KeyTab ktab(String s) {
|
||||
return KeyTab.getInstance(princ(s));
|
||||
}
|
||||
static KeyTab ktab() {
|
||||
return KeyTab.getUnboundInstance();
|
||||
}
|
||||
}
|
||||
|
@ -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 class AcceptPermissions extends SecurityManager {
|
||||
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 @@ public class AcceptPermissions extends SecurityManager {
|
||||
// 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 @@ public class AcceptPermissions extends SecurityManager {
|
||||
// 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();
|
||||
|
61
jdk/test/sun/security/krb5/auto/GSSUnbound.java
Normal file
61
jdk/test/sun/security/krb5/auto/GSSUnbound.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -76,6 +76,8 @@ public class OneKDC extends KDC {
|
||||
Config.refresh();
|
||||
|
||||
writeKtab(KTAB);
|
||||
Security.setProperty("auth.login.defaultCallbackHandler",
|
||||
"OneKDC$CallbackForClient");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,7 +95,7 @@ public class OneKDC extends KDC {
|
||||
" 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 @@ public class OneKDC extends KDC {
|
||||
" isInitiator=false;\n};\n"
|
||||
).getBytes());
|
||||
fos.close();
|
||||
Security.setProperty("auth.login.defaultCallbackHandler", "OneKDC$CallbackForClient");
|
||||
}
|
||||
|
||||
/**
|
||||
|
113
jdk/test/sun/security/krb5/auto/SaslUnbound.java
Normal file
113
jdk/test/sun/security/krb5/auto/SaslUnbound.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
85
jdk/test/sun/security/krb5/auto/UnboundService.java
Normal file
85
jdk/test/sun/security/krb5/auto/UnboundService.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user