8048030: Expectations should be consistent

Reviewed-by: valeriep, mullan, ahgross
This commit is contained in:
Weijun Wang 2015-05-24 16:35:12 +08:00
parent 78d9d8c7df
commit 041dbf030e
13 changed files with 238 additions and 76 deletions

View File

@ -101,6 +101,18 @@ package javax.security.auth;
* login Configuration.
* </pre>
*
* <p>Please note that granting this permission with the "modifyPrincipals",
* "modifyPublicCredentials" or "modifyPrivateCredentials" target allows
* a JAAS login module to populate principal or credential objects into
* the Subject. Although reading information inside the private credentials
* set requires a {@link PrivateCredentialPermission} of the credential type to
* be granted, reading information inside the principals set and the public
* credentials set requires no additional permission. These objects can contain
* potentially sensitive information. For example, login modules that read
* local user information or perform a Kerberos login are able to add
* potentially sensitive information such as user ids, groups and domain names
* to the principals set.
*
* <p> The following target name has been deprecated in favor of
* {@code createLoginContext.{name}}.
*

View File

@ -100,9 +100,16 @@ public final class KerberosPrincipal
* <p>If the input name does not contain a realm, the default realm
* is used. The default realm can be specified either in a Kerberos
* configuration file or via the java.security.krb5.realm
* system property. For more information,
* system property. For more information, see
* <a href="../../../../../technotes/guides/security/jgss/tutorials/index.html">
* Kerberos Requirements </a>
* Kerberos Requirements</a>. Additionally, if a security manager is
* installed, a {@link ServicePermission} must be granted and the service
* principal of the permission must minimally be inside the
* {@code KerberosPrincipal}'s realm. For example, if the result of
* {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
* then a {@code ServicePermission} with service principal
* {@code host/www.example.com@EXAMPLE.COM} (and any action)
* must be granted.
*
* @param name the principal name
* @throws IllegalArgumentException if name is improperly
@ -110,20 +117,12 @@ public final class KerberosPrincipal
* the realm to use and the default realm is not specified
* in either a Kerberos configuration file or via the
* java.security.krb5.realm system property.
* @throws SecurityException if a security manager is installed and
* {@code name} does not contain the realm to use, and a proper
* {@link ServicePermission} as described above is not granted.
*/
public KerberosPrincipal(String name) {
PrincipalName krb5Principal = null;
try {
// Appends the default realm if it is missing
krb5Principal = new PrincipalName(name, KRB_NT_PRINCIPAL);
} catch (KrbException e) {
throw new IllegalArgumentException(e.getMessage());
}
nameType = KRB_NT_PRINCIPAL; // default name type
fullName = krb5Principal.toString();
realm = krb5Principal.getRealmString();
this(name, KRB_NT_PRINCIPAL);
}
/**
@ -138,12 +137,19 @@ public final class KerberosPrincipal
* name type, KRB_NT_PRINCIPAL where <i>duke</i>
* represents a principal, and <i>FOO.COM</i> represents a realm).
*
* <p> If the input name does not contain a realm, the default realm
* <p>If the input name does not contain a realm, the default realm
* is used. The default realm can be specified either in a Kerberos
* configuration file or via the java.security.krb5.realm
* system property. For more information, see
* <a href="../../../../../technotes/guides/security/jgss/tutorials/index.html">
* Kerberos Requirements</a>.
* Kerberos Requirements</a>. Additionally, if a security manager is
* installed, a {@link ServicePermission} must be granted and the service
* principal of the permission must minimally be inside the
* {@code KerberosPrincipal}'s realm. For example, if the result of
* {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
* then a {@code ServicePermission} with service principal
* {@code host/www.example.com@EXAMPLE.COM} (and any action)
* must be granted.
*
* @param name the principal name
* @param nameType the name type of the principal
@ -152,6 +158,9 @@ public final class KerberosPrincipal
* or if name does not contain the realm to use and the default
* realm is not specified in either a Kerberos configuration
* file or via the java.security.krb5.realm system property.
* @throws SecurityException if a security manager is installed and
* {@code name} does not contain the realm to use, and a proper
* {@link ServicePermission} as described above is not granted.
*/
public KerberosPrincipal(String name, int nameType) {
@ -165,6 +174,18 @@ public final class KerberosPrincipal
throw new IllegalArgumentException(e.getMessage());
}
if (krb5Principal.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkPermission(new ServicePermission(
"@" + krb5Principal.getRealmAsString(), "-"));
} catch (SecurityException se) {
// Swallow the actual exception to hide info
throw new SecurityException("Cannot read realm info");
}
}
}
this.nameType = nameType;
fullName = krb5Principal.toString();
realm = krb5Principal.getRealmString();

View File

@ -51,7 +51,7 @@ import java.util.concurrent.ConcurrentHashMap;
* used within.
* <p>
* The service principal name is the canonical name of the
* {@code KereberosPrincipal} supplying the service, that is
* {@code KerberosPrincipal} supplying the service, that is
* the KerberosPrincipal represents a Kerberos service
* principal. This name is treated in a case sensitive manner.
* An asterisk may appear by itself, to signify any service principal.
@ -62,6 +62,10 @@ import java.util.concurrent.ConcurrentHashMap;
* permission also implies that the TGT can be obtained by an
* Authentication Service exchange.
* <p>
* Granting this permission also implies creating {@link KerberosPrincipal}
* or {@link org.ietf.jgss.GSSName GSSName} without providing a Kerberos
* realm, as long as the permission's service principal is in this realm.
* <p>
* The possible actions are:
*
* <pre>
@ -146,6 +150,9 @@ public final class ServicePermission extends Permission
* @param action the action string
*/
public ServicePermission(String servicePrincipal, String action) {
// Note: servicePrincipal can be "@REALM" which means any principal in
// this realm implies it. action can be "-" which means any
// action implies it.
super(servicePrincipal);
init(servicePrincipal, getMask(action));
}
@ -208,7 +215,9 @@ public final class ServicePermission extends Permission
boolean impliesIgnoreMask(ServicePermission p) {
return ((this.getName().equals("*")) ||
this.getName().equals(p.getName()));
this.getName().equals(p.getName()) ||
(p.getName().startsWith("@") &&
this.getName().endsWith(p.getName())));
}
/**
@ -318,7 +327,10 @@ public final class ServicePermission extends Permission
/**
* Convert an action string to an integer actions mask.
*
* @param action the action string
* Note: if action is "-", action will be NONE, which means any
* action implies it.
*
* @param action the action string.
* @return the action mask
*/
private static int getMask(String action) {
@ -335,9 +347,11 @@ public final class ServicePermission extends Permission
char[] a = action.toCharArray();
int i = a.length - 1;
if (i < 0)
if (a.length == 1 && a[0] == '-') {
return mask;
}
int i = a.length - 1;
while (i != -1) {
char c;
@ -501,6 +515,17 @@ final class KrbServicePermissionCollection extends PermissionCollection
ServicePermission np = (ServicePermission) permission;
int desired = np.getMask();
if (desired == 0) {
for (Permission p: perms.values()) {
ServicePermission sp = (ServicePermission)p;
if (sp.impliesIgnoreMask(np)) {
return true;
}
}
return false;
}
// first, check for wildcard principal
ServicePermission x = (ServicePermission)perms.get("*");
if (x != null) {

View File

@ -81,6 +81,18 @@ package org.ietf.jgss;
* GSSName.NT_EXPORT_NAME);
*
* </pre>
* If a security manager is installed, in order to create a {@code GSSName}
* that contains a Kerberos name element without providing its realm,
* a {@link javax.security.auth.kerberos.ServicePermission ServicePermission}
* must be granted and the service principal of the permission must minimally
* be inside the Kerberos name element's realm. For example, if the result of
* {@link GSSManager#createName(String, Oid) createName("user", NT_USER_NAME)}
* contains a Kerberos name element {@code user@EXAMPLE.COM}, then
* a {@code ServicePermission} with service principal
* {@code host/www.example.com@EXAMPLE.COM} (and any action) must be granted.
* Otherwise, the creation will throw a {@link GSSException} containing the
* {@code GSSException.FAILURE} error code.
*
* @see #export()
* @see #equals(GSSName)
* @see GSSManager#createName(String, Oid)

View File

@ -28,7 +28,10 @@ package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.Realm;
import sun.security.krb5.KrbException;
import javax.security.auth.kerberos.ServicePermission;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
@ -126,6 +129,18 @@ public class Krb5NameElement
throw new GSSException(GSSException.BAD_NAME, -1, e.getMessage());
}
if (principalName.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkPermission(new ServicePermission(
"@" + principalName.getRealmAsString(), "-"));
} catch (SecurityException se) {
// Do not chain the actual exception to hide info
throw new GSSException(GSSException.FAILURE);
}
}
}
return new Krb5NameElement(principalName, gssNameStr, gssNameType);
}

View File

@ -30,6 +30,7 @@ import java.security.Provider;
import java.security.Security;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import sun.security.krb5.Realm;
import sun.security.jgss.GSSUtil;
import sun.security.util.ObjectIdentifier;
import sun.security.util.DerInputStream;
@ -38,6 +39,8 @@ import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.GSSNameSpi;
import javax.security.auth.kerberos.ServicePermission;
/**
* This class is essentially a wrapper class for the gss_name_t
* structure of the native GSS library.
@ -150,6 +153,26 @@ public class GSSNameElement implements GSSNameSpi {
pName = cStub.importName(name, nameType);
setPrintables();
SecurityManager sm = System.getSecurityManager();
if (sm != null && !Realm.AUTODEDUCEREALM) {
String krbName = getKrbName();
int atPos = krbName.lastIndexOf('@');
if (atPos != -1) {
String atRealm = krbName.substring(atPos);
if (nameType.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL)
&& new String(nameBytes).endsWith(atRealm)) {
// Created from Kerberos name with realm, no need to check
} else {
try {
sm.checkPermission(new ServicePermission(atRealm, "-"));
} catch (SecurityException se) {
// Do not chain the actual exception to hide info
throw new GSSException(GSSException.FAILURE);
}
}
}
}
SunNativeProvider.debug("Imported " + printableName + " w/ type " +
printableType);
}

View File

@ -25,6 +25,11 @@
package sun.security.krb5;
import sun.security.krb5.internal.Krb5;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Random;
@ -52,6 +57,8 @@ class KrbServiceLocator {
private static final Random random = new Random();
private static final boolean DEBUG = Krb5.DEBUG;
private KrbServiceLocator() {
}
@ -62,8 +69,7 @@ class KrbServiceLocator {
* Information on the mapping of DNS hostnames and domain names
* to Kerberos realms is stored using DNS TXT records
*
* @param domainName A string domain name.
* @param environment The possibly null environment of the context.
* @param realmName A string realm name.
* @return An ordered list of hostports for the Kerberos service or null if
* the service has not been located.
*/
@ -81,8 +87,18 @@ class KrbServiceLocator {
if (!(ctx instanceof DirContext)) {
return null; // cannot create a DNS context
}
Attributes attrs =
((DirContext)ctx).getAttributes(dnsUrl, SRV_TXT_ATTR);
Attributes attrs = null;
try {
// both connect and accept are needed since DNS is thru UDP
attrs = AccessController.doPrivileged(
(PrivilegedExceptionAction<Attributes>)
() -> ((DirContext)ctx).getAttributes(
dnsUrl, SRV_TXT_ATTR),
null,
new java.net.SocketPermission("*", "connect,accept"));
} catch (PrivilegedActionException e) {
throw (NamingException)e.getCause();
}
Attribute attr;
if (attrs != null && ((attr = attrs.get(SRV_TXT)) != null)) {
@ -124,7 +140,8 @@ class KrbServiceLocator {
* Queries DNS for a list of KERBEROS Service Location Records (SRV) for a
* given domain name.
*
* @param domainName A string domain name.
* @param realmName A string realm name.
* @param protocol the protocol string, can be "_udp" or "_tcp"
* @return An ordered list of hostports for the Kerberos service or null if
* the service has not been located.
*/
@ -142,8 +159,20 @@ class KrbServiceLocator {
if (!(ctx instanceof DirContext)) {
return null; // cannot create a DNS context
}
Attributes attrs =
((DirContext)ctx).getAttributes(dnsUrl, SRV_RR_ATTR);
Attributes attrs = null;
try {
// both connect and accept are needed since DNS is thru UDP
attrs = AccessController.doPrivileged(
(PrivilegedExceptionAction<Attributes>)
() -> ((DirContext)ctx).getAttributes(
dnsUrl, SRV_RR_ATTR),
null,
new java.net.SocketPermission("*", "connect,accept"));
} catch (PrivilegedActionException e) {
throw (NamingException)e.getCause();
}
Attribute attr;
if (attrs != null && ((attr = attrs.get(SRV_RR)) != null)) {

View File

@ -123,6 +123,13 @@ public class PrincipalName implements Cloneable {
*/
private final Realm nameRealm; // not null
/**
* When constructing a PrincipalName, whether the realm is included in
* the input, or deduced from default realm or domain-realm mapping.
*/
private final boolean realmDeduced;
// cached default salt, not used in clone
private transient String salt = null;
@ -143,6 +150,7 @@ public class PrincipalName implements Cloneable {
this.nameType = nameType;
this.nameStrings = nameStrings.clone();
this.nameRealm = nameRealm;
this.realmDeduced = false;
}
// This method is called by Windows NativeCred.c
@ -150,11 +158,6 @@ public class PrincipalName implements Cloneable {
this(KRB_NT_UNKNOWN, nameParts, new Realm(realm));
}
public PrincipalName(String[] nameParts, int type)
throws IllegalArgumentException, RealmException {
this(type, nameParts, Realm.getDefault());
}
// Validate a nameStrings argument
private static void validateNameStrings(String[] ns) {
if (ns == null) {
@ -226,7 +229,7 @@ public class PrincipalName implements Cloneable {
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*
* @param encoding a Der-encoded data.
* @param encoding DER-encoded PrincipalName (without Realm)
* @param realm the realm for this name
* @exception Asn1Exception if an error occurs while decoding
* an ASN1 encoded data.
@ -240,6 +243,7 @@ public class PrincipalName implements Cloneable {
if (realm == null) {
throw new IllegalArgumentException("Null realm not allowed");
}
realmDeduced = false;
nameRealm = realm;
DerValue der;
if (encoding == null) {
@ -394,6 +398,10 @@ public class PrincipalName implements Cloneable {
if (realm == null) {
realm = Realm.parseRealmAtSeparator(name);
}
// No realm info from parameter and string, must deduce later
realmDeduced = realm == null;
switch (type) {
case KRB_NT_SRV_HST:
if (nameParts.length >= 2) {
@ -413,8 +421,8 @@ public class PrincipalName implements Cloneable {
hostName.toLowerCase(Locale.ENGLISH)+".")) {
hostName = canonicalized;
}
} catch (UnknownHostException e) {
// no canonicalization, use old
} catch (UnknownHostException | SecurityException e) {
// not canonicalized or no permission to do so, use old
}
nameParts[1] = hostName.toLowerCase(Locale.ENGLISH);
}
@ -681,4 +689,7 @@ public class PrincipalName implements Cloneable {
return result;
}
public boolean isRealmDeduced() {
return realmDeduced;
}
}

View File

@ -46,6 +46,12 @@ import sun.security.krb5.internal.util.KerberosString;
* This class is immutable.
*/
public class Realm implements Cloneable {
public static final boolean AUTODEDUCEREALM =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction(
"sun.security.krb5.autodeducerealm"));
private final String realm; // not null nor empty
public Realm(String name) throws RealmException {

View File

@ -146,8 +146,9 @@ public class CCacheInputStream extends KrbDataInputStream implements FileCCacheC
}
try {
return new PrincipalName(
type,
result.toArray(new String[result.size()]),
type);
Realm.getDefault());
} catch (RealmException re) {
return null;
}

View File

@ -889,8 +889,9 @@ public class KDC {
PrincipalName service = asReq.reqBody.sname;
if (options.containsKey(KDC.Option.RESP_NT)) {
service = new PrincipalName(service.getNameStrings(),
(int)options.get(KDC.Option.RESP_NT));
service = new PrincipalName((int)options.get(KDC.Option.RESP_NT),
service.getNameStrings(),
Realm.getDefault());
}
try {
System.out.println(realm + "> " + asReq.reqBody.cname +

View File

@ -76,7 +76,10 @@ public class SSL extends SecurityManager {
return;
}
ServicePermission p = (ServicePermission)perm;
permChecks = permChecks + p.getActions().toUpperCase().charAt(0);
// ServicePermissions required to create GSSName are ignored
if (!p.getActions().isEmpty()) {
permChecks = permChecks + p.getActions().toUpperCase().charAt(0);
}
}
public static void main(String[] args) throws Exception {

View File

@ -41,22 +41,22 @@ public class Constructors {
// Good ones
type = PrincipalName.KRB_NT_UNKNOWN;
checkName("a", type, "R", "R", "a");
checkName("a@R2", type, "R", "R", "a");
checkName("a/b", type, "R", "R", "a", "b");
checkName("a/b@R2", type, "R", "R", "a", "b");
checkName("a/b/c", type, "R", "R", "a", "b", "c");
checkName("a/b/c@R2", type, "R", "R", "a", "b", "c");
checkName("a", type, "R", "R", false, "a");
checkName("a@R2", type, "R", "R", false, "a");
checkName("a/b", type, "R", "R", false, "a", "b");
checkName("a/b@R2", type, "R", "R", false, "a", "b");
checkName("a/b/c", type, "R", "R", false, "a", "b", "c");
checkName("a/b/c@R2", type, "R", "R", false, "a", "b", "c");
// Weird ones
checkName("a\\/b", type, "R", "R", "a/b");
checkName("a\\/b\\/c", type, "R", "R", "a/b/c");
checkName("a\\/b\\@R2", type, "R", "R", "a/b@R2");
checkName("a\\/b", type, "R", "R", false, "a/b");
checkName("a\\/b\\/c", type, "R", "R", false, "a/b/c");
checkName("a\\/b\\@R2", type, "R", "R", false, "a/b@R2");
// Bad ones
checkName("a", type, "", null);
checkName("a/", type, "R", null);
checkName("/a", type, "R", null);
checkName("a//b", type, "R", null);
checkName("a@", type, null, null);
checkName("a", type, "", null, false);
checkName("a/", type, "R", null, false);
checkName("/a", type, "R", null, false);
checkName("a//b", type, "R", null, false);
checkName("a@", type, null, null, false);
type = PrincipalName.KRB_NT_SRV_HST;
// Part 2: on realm choices
@ -78,17 +78,17 @@ public class Constructors {
if (testNoDefaultDomain) {
type = PrincipalName.KRB_NT_UNKNOWN;
checkName("a", type, "R1", "R1", "a"); // arg
checkName("a@R1", type, null, "R1", "a"); // or r in name
checkName("a@R2", type, "R1", "R1", "a"); // arg over r
checkName("a", type, null, null); // fail if none
checkName("a/b@R1", type, null, "R1", "a", "b");
checkName("a", type, "R1", "R1", false, "a"); // arg
checkName("a@R1", type, null, "R1", false, "a"); // or r in name
checkName("a@R2", type, "R1", "R1", false, "a"); // arg over r
checkName("a", type, null, null, false); // fail if none
checkName("a/b@R1", type, null, "R1", false, "a", "b");
type = PrincipalName.KRB_NT_SRV_HST;
// Let's pray "b.h" won't be canonicalized
checkName("a/b.h", type, "R1", "R1", "a", "b.h"); // arg
checkName("a/b.h@R1", type, null, "R1", "a", "b.h"); // or r in name
checkName("a/b.h@R1", type, "R2", "R2", "a", "b.h"); // arg over r
checkName("a/b.h", type, null, null); // fail if none
checkName("a/b.h", type, "R1", "R1", false, "a", "b.h"); // arg
checkName("a/b.h@R1", type, null, "R1", false, "a", "b.h"); // or r in name
checkName("a/b.h@R1", type, "R2", "R2", false, "a", "b.h"); // arg over r
checkName("a/b.h", type, null, null, false); // fail if none
}
// When there is default realm
@ -97,25 +97,25 @@ public class Constructors {
Config.refresh();
type = PrincipalName.KRB_NT_UNKNOWN;
checkName("a", type, "R1", "R1", "a"); // arg
checkName("a@R1", type, null, "R1", "a"); // or r in name
checkName("a@R2", type, "R1", "R1", "a"); // arg over r
checkName("a", type, null, "R", "a"); // default
checkName("a/b", type, null, "R", "a", "b");
checkName("a", type, "R1", "R1", false, "a"); // arg
checkName("a@R1", type, null, "R1", false, "a"); // or r in name
checkName("a@R2", type, "R1", "R1", false, "a"); // arg over r
checkName("a", type, null, "R", true, "a"); // default
checkName("a/b", type, null, "R", true, "a", "b");
type = PrincipalName.KRB_NT_SRV_HST;
checkName("a/b.h3", type, "R1", "R1", "a", "b.h3"); // arg
checkName("a/b.h@R1", type, null, "R1", "a", "b.h"); // or r in name
checkName("a/b.h3@R2", type, "R1", "R1", "a", "b.h3"); // arg over r
checkName("a/b.h2", type, "R1", "R1", "a", "b.h2"); // arg over map
checkName("a/b.h2@R1", type, null, "R1", "a", "b.h2"); // r over map
checkName("a/b.h2", type, null, "R2", "a", "b.h2"); // map
checkName("a/b.h", type, null, "R", "a", "b.h"); // default
checkName("a/b.h3", type, "R1", "R1", false, "a", "b.h3"); // arg
checkName("a/b.h@R1", type, null, "R1", false, "a", "b.h"); // or r in name
checkName("a/b.h3@R2", type, "R1", "R1", false, "a", "b.h3"); // arg over r
checkName("a/b.h2", type, "R1", "R1", false, "a", "b.h2"); // arg over map
checkName("a/b.h2@R1", type, null, "R1", false, "a", "b.h2"); // r over map
checkName("a/b.h2", type, null, "R2", true, "a", "b.h2"); // map
checkName("a/b.h", type, null, "R", true, "a", "b.h"); // default
}
// Check if the creation matches the expected output.
// Note: realm == null means creation failure
static void checkName(String n, int t, String s,
String realm, String... parts)
String realm, boolean deduced, String... parts)
throws Exception {
PrincipalName pn = null;
try {
@ -132,5 +132,8 @@ public class Constructors {
throw new Exception(pn.toString() + " vs "
+ Arrays.toString(parts) + "@" + realm);
}
if (deduced != pn.isRealmDeduced()) {
throw new Exception("pn.realmDeduced is " + pn.isRealmDeduced());
}
}
}