提交 91443882 编写于 作者: W weijun

8001104: Unbound SASL service: the GSSAPI/krb5 mech

Reviewed-by: valeriep
上级 86916123
......@@ -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>=&lt;keytabname&gt;
* <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>=&lt;keytabname&gt;
* <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,37 +86,40 @@ 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;
}
}
} 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;
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 {
answer.add(credClass.cast(t));
// 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;
}
}
// 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) {
// We are looking for credentials for the serverPrincipal
......
......@@ -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();
......
/*
* 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");
}
/**
......
/*
* 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");
}
}
}
/*
* 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();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册