diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java
index 19629b06a86..10e24399c8b 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java
@@ -214,10 +214,48 @@ public boolean implies(Permission p) {
boolean impliesIgnoreMask(ServicePermission p) {
- return ((this.getName().equals("*")) ||
- this.getName().equals(p.getName()) ||
- (p.getName().startsWith("@") &&
- this.getName().endsWith(p.getName())));
+ if ((this.getName().equals("*")) ||
+ this.getName().equals(p.getName()) ||
+ (p.getName().startsWith("@") &&
+ this.getName().endsWith(p.getName())))
+ return true;
+
+ /*
+ * Empty realm in this or p is a wild-card. This is needed to support
+ * non-Kerberos ServicePermissions for GSS (a band-aid until we can
+ * implement a proper GssAcceptorPermission class), but also because
+ * users may not know and might not care what realm the service is in,
+ * especially when they are using a keytab.
+ *
+ * If the user is using a password, then the realm matters more. An
+ * untrusted actor could cause KDCs for a realm they control to see
+ * material they could attack offline, but that was already the case
+ * anyways, and the answer is the same in all cases: use stronger
+ * passwords, use randomized keys in a keytab, or let us implement
+ * SPAKE or similar alternatives to the venerable PA-ENC-TIMESTAMP.
+ */
+ if ((this.getName().equals("krbtgt/@") &&
+ p.getName().startsWith("krbtgt/")) ||
+ (p.getName().equals("krbtgt/@") &&
+ this.getName().startsWith("krbtgt/")))
+ return true;
+
+ char[] n = this.getName().toCharArray();
+ int i;
+ for (i = 0; i < n.length; i++) {
+ if (n[i] == '\\') {
+ i++;
+ continue;
+ }
+ if (n[i] == '@') {
+ String s = new String(n, 0, i + 1);
+ return (p.getName().startsWith(s) &&
+ (p.getName().equals(s) || this.getName().equals(s)));
+ }
+ }
+
+ // No realm, not even empty -> fail
+ return false;
}
/**
diff --git a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java
index f9ae3efafee..ab3181302c2 100644
--- a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java
+++ b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java
@@ -423,6 +423,58 @@ public abstract GSSCredential createCredential (GSSName name,
int lifetime, Oid mech, int usage)
throws GSSException;
+ /**
+ * Factory method for acquiring a single mechanism credential with a
+ * password.
+ *
+ * GSS-API mechanism providers must impose a local access-control
+ * policy on callers to prevent unauthorized callers from acquiring
+ * credentials to which they are not entitled. The kinds of permissions
+ * needed by different mechanism providers will be documented on a
+ * per-mechanism basis. A failed permission check might cause a {@link
+ * java.lang.SecurityException SecurityException} to be thrown from
+ * this method.
+ *
+ * Non-default values for lifetime cannot always be honored by the
+ * underlying mechanisms, thus applications should be prepared to call
+ * {@link GSSCredential#getRemainingLifetime() getRemainingLifetime}
+ * on the returned credential.
+ *
+ * @param name the name of the principal for whom this credential is to be
+ * acquired. Use null
to specify the default principal.
+ * @param lifetime The number of seconds that credentials should remain
+ * valid. Use {@link GSSCredential#INDEFINITE_LIFETIME
+ * GSSCredential.INDEFINITE_LIFETIME} to request that the credentials
+ * have the maximum permitted lifetime. Use {@link
+ * GSSCredential#DEFAULT_LIFETIME GSSCredential.DEFAULT_LIFETIME} to
+ * request default credential lifetime.
+ * @param mech the Oid of the desired mechanism. Use (Oid) null
+ *
to request the default mechanism.
+ * @param usage The intended usage for this credential object. The value
+ * of this parameter must be one of:
+ * {@link GSSCredential#INITIATE_AND_ACCEPT
+ * GSSCredential.INITIATE_AND_ACCEPT},
+ * {@link GSSCredential#ACCEPT_ONLY GSSCredential.ACCEPT_ONLY}, and
+ * {@link GSSCredential#INITIATE_ONLY GSSCredential.INITIATE_ONLY}.
+ * @return a GSSCredential of the requested type.
+ *
+ * @see GSSCredential
+ *
+ * @throws GSSException containing the following
+ * major error codes:
+ * {@link GSSException#BAD_MECH GSSException.BAD_MECH},
+ * {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
+ * {@link GSSException#BAD_NAME GSSException.BAD_NAME},
+ * {@link GSSException#CREDENTIALS_EXPIRED
+ * GSSException.CREDENTIALS_EXPIRED},
+ * {@link GSSException#NO_CRED GSSException.NO_CRED},
+ * {@link GSSException#FAILURE GSSException.FAILURE}
+ */
+ public abstract GSSCredential createCredential (GSSName name,
+ String password, int lifetime, Oid mech,
+ int usage)
+ throws GSSException;
+
/**
* Factory method for acquiring credentials over a set of
* mechanisms. This method attempts to acquire credentials for
@@ -480,6 +532,64 @@ public abstract GSSCredential createCredential(GSSName name,
int lifetime, Oid mechs[], int usage)
throws GSSException;
+ /**
+ * Factory method for acquiring credentials with a password over a set of
+ * mechanisms. This method attempts to acquire credentials for
+ * each of the mechanisms specified in the array called mechs. To
+ * determine the list of mechanisms for which the acquisition of
+ * credentials succeeded, the caller should use the {@link
+ * GSSCredential#getMechs() GSSCredential.getMechs} method.
+ *
+ * GSS-API mechanism providers must impose a local access-control
+ * policy on callers to prevent unauthorized callers from acquiring
+ * credentials to which they are not entitled. The kinds of permissions
+ * needed by different mechanism providers will be documented on a
+ * per-mechanism basis. A failed permission check might cause a {@link
+ * java.lang.SecurityException SecurityException} to be thrown from
+ * this method.
+ *
+ * Non-default values for lifetime cannot always be honored by the
+ * underlying mechanisms, thus applications should be prepared to call
+ * {@link GSSCredential#getRemainingLifetime() getRemainingLifetime}
+ * on the returned credential.
+ *
+ * @param name the name of the principal for whom this credential is to
+ * be acquired. Use null
to specify the default
+ * principal.
+ * @param lifetime The number of seconds that credentials should remain
+ * valid. Use {@link GSSCredential#INDEFINITE_LIFETIME
+ * GSSCredential.INDEFINITE_LIFETIME} to request that the credentials
+ * have the maximum permitted lifetime. Use {@link
+ * GSSCredential#DEFAULT_LIFETIME GSSCredential.DEFAULT_LIFETIME} to
+ * request default credential lifetime.
+ * @param mechs an array of Oid's indicating the mechanisms over which
+ * the credential is to be acquired. Use (Oid[]) null
for
+ * requesting a system specific default set of mechanisms.
+ * @param usage The intended usage for this credential object. The value
+ * of this parameter must be one of:
+ * {@link GSSCredential#INITIATE_AND_ACCEPT
+ * GSSCredential.INITIATE_AND_ACCEPT},
+ * {@link GSSCredential#ACCEPT_ONLY GSSCredential.ACCEPT_ONLY}, and
+ * {@link GSSCredential#INITIATE_ONLY GSSCredential.INITIATE_ONLY}.
+ * @return a GSSCredential of the requested type.
+ *
+ * @see GSSCredential
+ *
+ * @throws GSSException containing the following
+ * major error codes:
+ * {@link GSSException#BAD_MECH GSSException.BAD_MECH},
+ * {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
+ * {@link GSSException#BAD_NAME GSSException.BAD_NAME},
+ * {@link GSSException#CREDENTIALS_EXPIRED
+ * GSSException.CREDENTIALS_EXPIRED},
+ * {@link GSSException#NO_CRED GSSException.NO_CRED},
+ * {@link GSSException#FAILURE GSSException.FAILURE}
+ */
+ public abstract GSSCredential createCredential(GSSName name,
+ String password, int lifetime,
+ Oid mechs[], int usage)
+ throws GSSException;
+
/**
* Factory method for creating a context on the initiator's
* side.
diff --git a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java
index d55bd6abb47..d0013762ca8 100644
--- a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java
+++ b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java
@@ -24,6 +24,7 @@
*/
package org.ietf.jgss;
+import java.security.Principal;
/**
* This interface encapsulates a single GSS-API principal entity. The
@@ -102,7 +103,7 @@
* @author Mayank Upadhyay
* @since 1.4
*/
-public interface GSSName {
+public interface GSSName extends Principal {
/**
* Oid indicating a host-based service name form. It is used to
@@ -261,6 +262,16 @@ public interface GSSName {
*/
public byte[] export() throws GSSException;
+ /**
+ * Returns a local username form of a mechanism name, if available.
+ */
+ public String getLocalName() throws GSSException;
+
+ /**
+ * Returns a local username form of a mechanism name, if available.
+ */
+ public String getLocalName(Oid mech) throws GSSException;
+
/**
* Returns a textual representation of the GSSName
object. To retrieve
* the printed name format, which determines the syntax of the returned
@@ -271,6 +282,26 @@ public interface GSSName {
*/
public String toString();
+ /**
+ * Returns a textual representation of the GSSName
object.
+ *
+ * If this
is not an MN then the returned name should be the
+ * same as the generic name used to construct it. Otherwise the returned
+ * name may be a mechanism-specific name string.
+ *
+ * @return a String representing this name in printable form.
+ */
+ public String getName();
+
+ /**
+ * Returns a textual representation of the GSSName
object
+ * element corresponding to the given mech
. This will be a
+ * mechanism-specific representation of this
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java
index 6d0d711325e..18eccb14b18 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java
@@ -66,17 +66,33 @@ protected GSSCredentialImpl(GSSCredentialImpl src) {
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
- int lifetime, Oid mech, int usage)
+ int lifetime, Oid mech, int usage)
throws GSSException {
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
init(gssManager);
- add(name, lifetime, lifetime, mech, usage);
+ String password = null;
+ add(name, password, lifetime, lifetime, mech, usage);
+ }
+
+ GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, String password,
+ int lifetime, Oid mech, int usage)
+ throws GSSException {
+ if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
+
+ init(gssManager);
+ add(name, password, lifetime, lifetime, mech, usage);
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
int lifetime, Oid[] mechs, int usage)
throws GSSException {
+ this(gssManager, name, (String)null, lifetime, mechs, usage);
+ }
+
+ GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
+ String password, int lifetime, Oid mechs[], int usage)
+ throws GSSException {
init(gssManager);
boolean defaultList = false;
if (mechs == null) {
@@ -86,7 +102,7 @@ protected GSSCredentialImpl(GSSCredentialImpl src) {
for (int i = 0; i < mechs.length; i++) {
try {
- add(name, lifetime, lifetime, mechs[i], usage);
+ add(name, password, lifetime, lifetime, mechs[i], usage);
} catch (GSSException e) {
if (defaultList) {
// Try the next mechanism
@@ -417,6 +433,13 @@ public Oid[] getMechs() throws GSSException {
public void add(GSSName name, int initLifetime, int acceptLifetime,
Oid mech, int usage) throws GSSException {
+ String password = null;
+ add(name, password, initLifetime, acceptLifetime, mech, usage);
+ }
+
+ public void add(GSSName name, String password, int initLifetime,
+ int acceptLifetime, Oid mech, int usage)
+ throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
@@ -437,6 +460,7 @@ public void add(GSSName name, int initLifetime, int acceptLifetime,
((GSSNameImpl)name).getElement(mech));
tempCred = gssManager.getCredentialElement(nameElement,
+ password,
initLifetime,
acceptLifetime,
mech,
@@ -474,11 +498,20 @@ public void add(GSSName name, int initLifetime, int acceptLifetime,
key = new SearchKey(mech, currentUsage);
hashtable.put(key, tempCred);
- tempCred = gssManager.getCredentialElement(nameElement,
- initLifetime,
- acceptLifetime,
- mech,
- desiredUsage);
+ if (password == null) {
+ tempCred = gssManager.getCredentialElement(nameElement,
+ initLifetime,
+ acceptLifetime,
+ mech,
+ desiredUsage);
+ } else {
+ tempCred = gssManager.getCredentialElement(nameElement,
+ password,
+ initLifetime,
+ acceptLifetime,
+ mech,
+ desiredUsage);
+ }
key = new SearchKey(mech, desiredUsage);
hashtable.put(key, tempCred);
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java
index e804a831e5a..8298be9926b 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java
@@ -135,12 +135,26 @@ public GSSCredential createCredential(GSSName aName,
return wrap(new GSSCredentialImpl(this, aName, lifetime, mech, usage));
}
+ public GSSCredential createCredential(GSSName aName, String password,
+ int lifetime, Oid mech, int usage)
+ throws GSSException {
+ return new GSSCredentialImpl(this, aName, password,
+ lifetime, mech, usage);
+ }
+
public GSSCredential createCredential(GSSName aName,
int lifetime, Oid[] mechs, int usage)
throws GSSException {
return wrap(new GSSCredentialImpl(this, aName, lifetime, mechs, usage));
}
+ public GSSCredential createCredential(GSSName aName, String password,
+ int lifetime, Oid mechs[], int usage)
+ throws GSSException {
+ return new GSSCredentialImpl(this, aName, password,
+ lifetime, mechs, usage);
+ }
+
public GSSContext createContext(GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException {
@@ -175,6 +189,17 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name, int initLifetime,
acceptLifetime, usage);
}
+ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
+ String password,
+ int initLifetime,
+ int acceptLifetime,
+ Oid mech, int usage)
+ throws GSSException {
+ MechanismFactory factory = list.getMechFactory(mech);
+ return factory.getCredentialElement(name, password, initLifetime,
+ acceptLifetime, usage);
+ }
+
// Used by java SPNEGO impl
public GSSNameSpi getNameElement(String name, Oid nameType, Oid mech)
throws GSSException {
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java
index e1e89059c5f..01f39816266 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java
@@ -447,8 +447,57 @@ public byte[] export() throws GSSException {
}
public String toString() {
- return printableName;
+ return printableName;
+ }
+
+ public String getName() {
+ return printableName;
+ }
+
+ public String getName(Oid mech) throws GSSException {
+ GSSNameSpi element = elements.get(mech);
+ if (element == null) {
+ throw new GSSExceptionImpl(GSSException.UNAVAILABLE,
+ "GSSName object does not have an element for the " +
+ "given mechanism");
+ }
+ return element.toString();
+ }
+ public String getLocalName() throws GSSException {
+ String lname = null;
+ Oid mech = null;
+ Oid mech2 = null;
+
+ for (GSSNameSpi v : elements.values()) {
+ String mname = v.getLocalName();
+ if (mname == null)
+ continue;
+ if (lname == null) {
+ mech = v.getMechanism();
+ lname = mname;
+ continue;
+ }
+ if (!lname.equals(mname)) {
+ mech2 = v.getMechanism();
+ break;
+ }
+ }
+ if (mech2 == null)
+ return lname;
+ throw new GSSExceptionImpl(GSSException.UNAVAILABLE,
+ "Localname conflict between mechanisms " +
+ mech + " and " + mech2);
+ }
+
+ public String getLocalName(Oid mech) throws GSSException {
+ GSSNameSpi element = elements.get(mech);
+ if (element == null) {
+ throw new GSSExceptionImpl(GSSException.UNAVAILABLE,
+ "GSSName object does not have an element for the " +
+ "given mechanism");
+ }
+ return element.getLocalName();
}
public Oid getStringNameType() throws GSSException {
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java
index 936883c9c67..4c1df31e9de 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java
@@ -124,24 +124,8 @@ public static Subject getSubject(GSSName name,
Set gssCredentials = null;
- Set krb5Principals =
- new HashSet();
-
- if (name instanceof GSSNameImpl) {
- try {
- GSSNameSpi ne = ((GSSNameImpl) name).getElement
- (GSS_KRB5_MECH_OID);
- String krbName = ne.toString();
- if (ne instanceof Krb5NameElement) {
- krbName =
- ((Krb5NameElement) ne).getKrb5PrincipalName().getName();
- }
- KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName);
- krb5Principals.add(krbPrinc);
- } catch (GSSException ge) {
- debug("Skipped name " + name + " due to " + ge);
- }
- }
+ Set names = new HashSet();
+ names.add(name);
if (creds instanceof GSSCredentialImpl) {
gssCredentials = ((GSSCredentialImpl) creds).getElements();
@@ -151,12 +135,11 @@ public static Subject getSubject(GSSName name,
privCredentials = new HashSet(); // empty Set
}
debug("Created Subject with the following");
- debug("principals=" + krb5Principals);
+ debug("principals=" + names);
debug("public creds=" + pubCredentials);
debug("private creds=" + privCredentials);
- return new Subject(false, krb5Principals, pubCredentials,
- privCredentials);
+ return new Subject(false, names, pubCredentials, privCredentials);
}
@@ -321,30 +304,44 @@ public static boolean useMSInterop() {
public Vector run() throws Exception {
Subject accSubj = Subject.getSubject(acc);
Vector result = null;
- if (accSubj != null) {
- result = new Vector();
- Iterator iterator =
- accSubj.getPrivateCredentials
- (GSSCredentialImpl.class).iterator();
- while (iterator.hasNext()) {
- GSSCredentialImpl cred = iterator.next();
- debug("...Found cred" + cred);
- try {
- GSSCredentialSpi ce =
- cred.getElement(mech, initiate);
- debug("......Found element: " + ce);
- if (ce.getClass().equals(credCls) &&
- (name == null ||
- name.equals((Object) ce.getName()))) {
+ if (accSubj == null) {
+ debug("No Subject");
+ return result;
+ }
+
+ result = new Vector();
+ Iterator iterator =
+ accSubj.getPrivateCredentials
+ (GSSCredentialImpl.class).iterator();
+ while (iterator.hasNext()) {
+ GSSCredentialImpl cred = iterator.next();
+ debug("...Found cred" + cred);
+ try {
+ GSSCredentialSpi ce =
+ cred.getElement(mech, initiate);
+ debug("......Found element: " + ce);
+ if (!ce.getClass().equals(credCls)) {
+ debug("......Discard element (class mismatch)");
+ } else if (name == null) {
+ /*
+ * If the caller doesn't care about
+ * the specific name, then prefer
+ * default credentials to
+ * non-default credentials.
+ */
+ if (ce.isDefaultCredential())
+ result.add(0, credCls.cast(ce));
+ else
result.add(credCls.cast(ce));
- } else {
- debug("......Discard element");
- }
- } catch (GSSException ge) {
- debug("...Discard cred (" + ge + ")");
+ } else if (name.equals((Object) ce.getName())) {
+ result.add(credCls.cast(ce));
+ } else {
+ debug("......Discard element (name mismatch)");
}
+ } catch (GSSException ge) {
+ debug("...Discard cred (exception: " + ge + ")");
}
- } else debug("No Subject");
+ }
return result;
}
});
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java
index c2b07ecfa79..951f573d544 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java
@@ -42,6 +42,7 @@ public class LoginConfigImpl extends Configuration {
private final Configuration config;
private final GSSCaller caller;
private final String mechName;
+ private final boolean useNative;
private static final sun.security.util.Debug debug =
sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]");
@@ -65,8 +66,17 @@ public LoginConfigImpl(GSSCaller caller, Oid mech) {
this.caller = caller;
- if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID)) {
+ useNative = "true".equalsIgnoreCase(
+ System.getProperty("sun.security.jgss.native"));
+
+ if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID) ||
+ mech.equals(GSSUtil.GSS_KRB5_MECH_OID2) ||
+ mech.equals(GSSUtil.GSS_KRB5_MECH_OID_MS)) {
mechName = "krb5";
+ } else if (useNative) {
+ // We don't really need a mechName, nor do we have any sort of
+ // standard notion of mechanism name (other than OIDs).
+ mechName = mech.toString();
} else {
throw new IllegalArgumentException(mech.toString() + " not supported");
}
@@ -98,7 +108,7 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
// For the 4 old callers, old entry names will be used if the new
// entry name is not provided.
- if ("krb5".equals(mechName)) {
+ if ("krb5".equals(mechName) || useNative) {
if (caller == GSSCaller.CALLER_INITIATE) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
@@ -118,7 +128,7 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
}
} else {
throw new IllegalArgumentException(mechName + " not supported");
- // No other mech at the moment, maybe --
+ // No other Java-coded mech at the moment, maybe --
/*
switch (caller) {
case GSSUtil.CALLER_INITIATE:
@@ -165,30 +175,42 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
* the system-wide Configuration object.
*/
private AppConfigurationEntry[] getDefaultConfigurationEntry() {
- HashMap options = new HashMap (2);
+ HashMap gssOptions = new HashMap (2);
+ HashMap krb5Options = new HashMap (2);
- if (mechName == null || mechName.equals("krb5")) {
+ if (mechName == null || mechName.equals("krb5") || useNative) {
if (isServerSide(caller)) {
+ gssOptions.put("useDefaultCreds", "true");
+ gssOptions.put("doNotPrompt", "true");
+ gssOptions.put("accept", "true");
// Assuming the keytab file can be found through
// krb5 config file or under user home directory
- options.put("useKeyTab", "true");
- options.put("storeKey", "true");
- options.put("doNotPrompt", "true");
- options.put("principal", "*");
- options.put("isInitiator", "false");
+ krb5Options.put("useKeyTab", "true");
+ krb5Options.put("storeKey", "true");
+ krb5Options.put("doNotPrompt", "true");
+ krb5Options.put("principal", "*");
+ krb5Options.put("isInitiator", "false");
} else {
if (caller instanceof HttpCaller && !HTTP_USE_GLOBAL_CREDS) {
- options.put("useTicketCache", "false");
+ gssOptions.put("tryDefaultCreds", "false");
+ krb5Options.put("useTicketCache", "false");
} else {
- options.put("useTicketCache", "true");
+ gssOptions.put("tryDefaultCreds", "true");
+ krb5Options.put("useTicketCache", "true");
}
- options.put("doNotPrompt", "false");
+ gssOptions.put("initiate", "true");
+ gssOptions.put("doNotPrompt", "false");
+ krb5Options.put("doNotPrompt", "false");
}
return new AppConfigurationEntry[] {
new AppConfigurationEntry(
- "com.sun.security.auth.module.Krb5LoginModule",
+ "com.sun.security.auth.module.GssLoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
- options)
+ gssOptions),
+ new AppConfigurationEntry(
+ "com.sun.security.auth.module.Krb5LoginModule",
+ AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT,
+ krb5Options)
};
}
return null;
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
index 262debcaf40..13e5b24f196 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java
@@ -47,6 +47,7 @@ public class Krb5AcceptCredential
private final Krb5NameElement name;
private final ServiceCreds screds;
+ private boolean isDefCred = false;
private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) {
/*
@@ -57,6 +58,8 @@ private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) {
this.name = name;
this.screds = creds;
+ if (name == null)
+ isDefCred = true;
}
static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
@@ -148,6 +151,15 @@ public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
+ /**
+ * Returns true if the credential is a default credential.
+ *
+ * @return true if the credential is a default credential, else false.
+ */
+ public boolean isDefaultCredential() {
+ return isDefCred;
+ }
+
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java
index 44a0c992a98..ed518b9a381 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java
@@ -55,6 +55,7 @@ public class Krb5InitCredential
private Krb5NameElement name;
private Credentials krb5Credentials;
+ private boolean isDefCred;
private Krb5InitCredential(Krb5NameElement name,
byte[] asn1Encoding,
@@ -82,6 +83,8 @@ private Krb5InitCredential(Krb5NameElement name,
clientAddresses);
this.name = name;
+ if (name == null)
+ isDefCred = true;
try {
// Cache this for later use by the sun.security.krb5 package.
@@ -134,7 +137,8 @@ private Krb5InitCredential(Krb5NameElement name,
this.name = name;
// A delegated cred does not have all fields set. So do not try to
- // creat new Credentials out of the delegatedCred.
+ // creat new Credentials out of the delegatedCred. Also, a delegated
+ // credential is not a default credential.
this.krb5Credentials = delegatedCred;
}
@@ -271,6 +275,15 @@ public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
+ /**
+ * Returns true if the credential is a default credential.
+ *
+ * @return true if the credential is a default credential, else false.
+ */
+ public boolean isDefaultCredential() {
+ return isDefCred;
+ }
+
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java
index cc9626c14f7..7a857865eca 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java
@@ -108,9 +108,17 @@ public GSSNameSpi getNameElement(byte[] name, Oid nameType)
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
- int initLifetime, int acceptLifetime,
+ String password, int initLifetime, int acceptLifetime,
int usage) throws GSSException {
+ if (password != null) {
+ // XXX Implement! Shouldn't be too hard...
+ throw new GSSException(GSSException.UNAVAILABLE, -1,
+ "The Kerberos mechanism Java implementation does not " +
+ "currently support acquiring GSS credentials handle " +
+ "elements with a password");
+ }
+
if (name != null && !(name instanceof Krb5NameElement)) {
name = Krb5NameElement.getInstance(name.toString(),
name.getStringNameType());
@@ -139,6 +147,12 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
return credElement;
}
+ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
+ int initLifetime, int acceptLifetime, int usage)
+ throws GSSException {
+ return getCredentialElement(name, null, initLifetime, acceptLifetime, usage);
+ }
+
public static void checkInitCredPermission(Krb5NameElement name) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
@@ -151,7 +165,7 @@ public static void checkInitCredPermission(Krb5NameElement name) {
sm.checkPermission(perm);
} catch (SecurityException e) {
if (DEBUG) {
- System.out.println("Permission to initiate" +
+ System.out.println("Permission to initiate " +
"kerberos init credential" + e.getMessage());
}
throw e;
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java
index 1d0217e9187..7d26691e89e 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java
@@ -316,6 +316,32 @@ public String toString() {
// For testing: return (super.toString());
}
+ /**
+ * Returns a local username (platform-specific) corresponding to the
+ * principal name, and may return null when no username is known for the
+ * principal name.
+ *
+ * @return username corresponding to this principal name, if any
+ */
+ public String getLocalName() throws GSSException {
+ throw new GSSException(GSSException.UNAVAILABLE, -1,
+ "Mapping Kerberos principal names to usernames is not " +
+ "currently supported");
+ }
+
+ /**
+ * Returns a local username (platform-specific) corresponding to the
+ * principal name, and may return null when no username is known for the
+ * principal name.
+ *
+ * @return username corresponding to this principal name, if any
+ */
+ public String getLocalName(Oid mech) throws GSSException {
+ throw new GSSException(GSSException.UNAVAILABLE, -1,
+ "Mapping Kerberos principal names to usernames is not " +
+ "currently supported");
+ }
+
/**
* Returns the name type oid.
*/
@@ -341,6 +367,10 @@ public boolean isAnonymousName() {
return (gssNameType.equals(GSSName.NT_ANONYMOUS));
}
+ public boolean isDefaultCredentialName() {
+ return false;
+ }
+
public Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java
index 4c5690b3942..7ef357c1770 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java
@@ -84,6 +84,11 @@ public boolean isAcceptorCredential() throws GSSException {
return false;
}
+ @Override
+ public final boolean isDefaultCredential() {
+ return false;
+ }
+
@Override
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java
index 69646d7a960..c417c9999b0 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java
@@ -88,6 +88,13 @@ public interface GSSCredentialSpi {
*/
public boolean isAcceptorCredential() throws GSSException;
+ /**
+ * Returns true if the credential is a default credential.
+ *
+ * @return true if the credential is a default credential, else false.
+ */
+ public boolean isDefaultCredential();
+
/**
* Returns the oid representing the underlying credential
* mechanism oid.
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java
index 24362d0074f..ed4af830001 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java
@@ -100,6 +100,23 @@ public interface GSSNameSpi {
*/
public String toString();
+ /**
+ * Returns a local username (platform-specific) corresponding to the
+ * principal name, and may return null when no username is known for the
+ * principal name.
+ *
+ * @return username corresponding to this name, if any
+ */
+ public String getLocalName() throws GSSException;
+
+ /**
+ * Returns a local username (platform-specific) corresponding to the
+ * principal name, and may return null when no username is known for the
+ * principal name.
+ *
+ * @return username corresponding to this name, if any
+ */
+ public String getLocalName(Oid mech) throws GSSException;
/**
* Returns the oid describing the format of the printable name.
@@ -112,4 +129,10 @@ public interface GSSNameSpi {
* Indicates if this name object represents an Anonymous name.
*/
public boolean isAnonymousName();
+
+ /**
+ * Indicates whether this name object refers to whatever name(s) the
+ * default credentials respond to.
+ */
+ public boolean isDefaultCredentialName();
}
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java
index e5bba5f2931..51ce06614a8 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java
@@ -118,6 +118,54 @@ public interface MechanismFactory {
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime, int usage) throws GSSException;
+ /**
+ * Creates a credential element using a password for this mechanism to be
+ * included as part of a GSSCredential implementation. A GSSCredential is
+ * conceptually a container class of several credential elements from
+ * different mechanisms. A GSS-API credential can be used either for
+ * initiating GSS security contexts or for accepting them. This method
+ * also accepts parameters that indicate what usage is expected and how
+ * long the life of the credential should be. It is not necessary that
+ * the mechanism honor the request for lifetime. An application will
+ * always query an acquired GSSCredential to determine what lifetime it
+ * got back.
+ *
+ * Not all mechanisms support the concept of one credential element
+ * that can be used for both initiating and accepting a context. In the
+ * event that an application requests usage INITIATE_AND_ACCEPT for a
+ * credential from such a mechanism, the GSS framework will need to
+ * obtain two different credential elements from the mechanism, one
+ * that will have usage INITIATE_ONLY and another that will have usage
+ * ACCEPT_ONLY. The mechanism will help the GSS-API realize this by
+ * returning a credential element with usage INITIATE_ONLY or
+ * ACCEPT_ONLY prompting it to make another call to
+ * getCredentialElement, this time with the other usage mode. The
+ * mechanism indicates the missing mode by returning a 0 lifetime for
+ * it.
+ *
+ * @param name the mechanism level name element for the entity whose
+ * credential is desired. A null value indicates that a mechanism
+ * dependent default choice is to be made.
+ * @param initLifetime indicates the lifetime (in seconds) that is
+ * requested for this credential to be used at the context initiator's
+ * end. This value should be ignored if the usage is
+ * ACCEPT_ONLY. Predefined contants are available in the
+ * org.ietf.jgss.GSSCredential interface.
+ * @param acceptLifetime indicates the lifetime (in seconds) that is
+ * requested for this credential to be used at the context acceptor's
+ * end. This value should be ignored if the usage is
+ * INITIATE_ONLY. Predefined contants are available in the
+ * org.ietf.jgss.GSSCredential interface.
+ * @param usage One of the values GSSCredential.INIATE_ONLY,
+ * GSSCredential.ACCEPT_ONLY, and GSSCredential.INITIATE_AND_ACCEPT.
+ * @see org.ietf.jgss.GSSCredential
+ * @throws GSSException if one of the error situations described in RFC
+ * 2743 with the GSS_Acquire_Cred or GSS_Add_Cred calls occurs.
+ */
+ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
+ String password, int initLifetime, int acceptLifetime, int usage)
+ throws GSSException;
+
/**
* Creates a name element for this mechanism to be included as part of
* a GSSName implementation. A GSSName is conceptually a container
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java
index c7cf9477424..a832fcc3079 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java
@@ -91,4 +91,8 @@ public Oid getMechanism() {
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
return cred.impersonate(name);
}
+
+ public boolean isDefaultCredential() {
+ return cred.isDefaultCredential();
+ }
}
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java
index 511547b8a78..336f88e8237 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java
@@ -79,6 +79,10 @@ private static SpNegoCredElement getCredFromSubject(GSSNameSpi name,
null : creds.firstElement());
// Force permission check before returning the cred to caller
+ //
+ // FIXME This code assumes that the Kerberos mechanism is Java-coded,
+ // whereas it should be possible to mix Java-coded SPNEGO with a native
+ // (C-coded) mechanism. For now this assumption stands.
if (result != null) {
GSSCredentialSpi cred = result.getInternalCred();
if (GSSUtil.isKerberosMech(cred.getMechanism())) {
@@ -149,6 +153,22 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
return credElement;
}
+ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
+ String password, int initLifetime, int acceptLifetime,
+ int usage) throws GSSException {
+
+ SpNegoCredElement credElement = getCredFromSubject
+ (name, (usage != GSSCredential.ACCEPT_ONLY));
+
+ if (credElement == null) {
+ // get CredElement for the default Mechanism
+ credElement = new SpNegoCredElement
+ (manager.getCredentialElement(name, password, initLifetime,
+ acceptLifetime, null, usage));
+ }
+ return credElement;
+ }
+
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred, int lifetime)
throws GSSException {
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java
index 019fa6f8052..c2fc3152684 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java
@@ -42,9 +42,14 @@ public class GSSCredElement implements GSSCredentialSpi {
long pCred; // Pointer to the gss_cred_id_t structure
private GSSNameElement name = null;
private GSSLibStub cStub;
+ public boolean isDefCred;
// Perform the necessary ServicePermission check on this cred
+ // FIXME Don't use any Krb5-specific code here.
void doServicePermCheck() throws GSSException {
+ // FIXME We need only do this check in initSecContext() and
+ // acceptSecContext(), so gut this here, and never ever do the
+ // Krb5Util.getTGSName(name) check.
if (GSSUtil.isKerberosMech(cStub.getMech())) {
if (System.getSecurityManager() != null) {
if (isInitiatorCredential()) {
@@ -69,22 +74,31 @@ void doServicePermCheck() throws GSSException {
name = srcName;
}
- GSSCredElement(GSSNameElement name, int lifetime, int usage,
- GSSLibStub stub) throws GSSException {
+ GSSCredElement(GSSNameElement name, String password, int lifetime,
+ int usage, GSSLibStub stub) throws GSSException {
cStub = stub;
this.usage = usage;
if (name != null) { // Could be GSSNameElement.DEF_ACCEPTOR
this.name = name;
doServicePermCheck();
- pCred = cStub.acquireCred(this.name.pName, lifetime, usage);
+ pCred = cStub.acquireCred(this.name.pName, password, lifetime,
+ usage);
+ if (name == GSSNameElement.DEF_ACCEPTOR)
+ isDefCred = true;
} else {
- pCred = cStub.acquireCred(0, lifetime, usage);
- this.name = new GSSNameElement(cStub.getCredName(pCred), cStub);
+ pCred = cStub.acquireCred(0, password, lifetime, usage);
+ this.name = new GSSNameElement(cStub.getCredName(pCred), cStub.getMech(), cStub);
doServicePermCheck();
+ isDefCred = true;
}
}
+ GSSCredElement(GSSNameElement name, int lifetime, int usage,
+ GSSLibStub stub) throws GSSException {
+ this(name, (String)null, lifetime, usage, stub);
+ }
+
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
@@ -125,6 +139,10 @@ public Oid getMechanism() {
return cStub.getMech();
}
+ public boolean isDefaultCredential() {
+ return isDefCred;
+ }
+
public String toString() {
// No hex bytes available for native impl
return "N/A";
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java
index 900532459bf..d6089781b28 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java
@@ -65,11 +65,12 @@ class GSSLibStub {
native boolean compareName(long pName1, long pName2);
native long canonicalizeName(long pName);
native byte[] exportName(long pName) throws GSSException;
+ native String localName(long pName, Oid mech) throws GSSException;
native Object[] displayName(long pName) throws GSSException;
// Credential related routines
- native long acquireCred(long pName, int lifetime, int usage)
- throws GSSException;
+ native long acquireCred(long pName, String password,
+ int lifetime, int usage) throws GSSException;
native long releaseCred(long pCred);
native long getCredName(long pCred);
native int getCredTime(long pCred);
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java
index 88274c1601b..2027f58377b 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java
@@ -53,6 +53,7 @@ public class GSSNameElement implements GSSNameSpi {
long pName = 0; // Pointer to the gss_name_t structure
private String printableName;
private Oid printableType;
+ private Oid mech;
private GSSLibStub cStub;
static final GSSNameElement DEF_ACCEPTOR = new GSSNameElement();
@@ -97,13 +98,14 @@ private GSSNameElement() {
printableName = "";
}
- GSSNameElement(long pNativeName, GSSLibStub stub) throws GSSException {
+ GSSNameElement(long pNativeName, Oid mech, GSSLibStub stub) throws GSSException {
assert(stub != null);
if (pNativeName == 0) {
throw new GSSException(GSSException.BAD_NAME);
}
// Note: pNativeName is assumed to be a MN.
pName = pNativeName;
+ this.mech = mech;
cStub = stub;
setPrintables();
}
@@ -116,6 +118,7 @@ private GSSNameElement() {
}
cStub = stub;
byte[] name = nameBytes;
+ mech = cStub.getMech();
if (nameType != null) {
// Special handling the specified name type if
@@ -128,7 +131,6 @@ private GSSNameElement() {
// method) for "NT_EXPORT_NAME"
byte[] mechBytes = null;
DerOutputStream dout = new DerOutputStream();
- Oid mech = cStub.getMech();
try {
dout.putOID(new ObjectIdentifier(mech.toString()));
} catch (IOException e) {
@@ -195,7 +197,28 @@ private void setPrintables() throws GSSException {
public String getKrbName() throws GSSException {
long mName = 0;
GSSLibStub stub = cStub;
- if (!GSSUtil.isKerberosMech(cStub.getMech())) {
+ if (!GSSUtil.isKerberosMech(mech)) {
+ // XXX We can't expect this to work generally. We should
+ // generalize the permission checks so that they can deal
+ // with name forms other than those of Kerberos.
+ //
+ // Alternatively we could have a method in GSSLibStub for
+ // mapping a non-Kerberos MN to a Kerberos MN, but depending
+ // on the specifics of the non-Kerberos mechanism we would
+ // either end up needing new conventions for Kerberos naming
+ // or else having cases where we can end up failing to
+ // support unconventional name forms.
+ //
+ // Consider a SAML assertion with a variety of identifying
+ // attributes and a variety of non-identifying attributes
+ // that are relevant to authorization. How should a
+ // Kerberos-equivalent be constructed? GSS does have
+ // extensions for decorating name objects with attributes,
+ // so that's not an issue, but if there are multiple
+ // identifying attributes then we'd have to pick one. Now,
+ // suppose the identifying attribute has a form like
+ // -- we'd need a Kerberos convention for
+ // that, which might be PHONE/@.
stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID);
}
mName = stub.canonicalizeName(pName);
@@ -267,13 +290,23 @@ public byte[] export() throws GSSException {
}
public Oid getMechanism() {
- return cStub.getMech();
+ return (mech != null) ? mech : cStub.getMech();
}
public String toString() {
return printableName;
}
+ public String getLocalName() throws GSSException {
+ return cStub.localName(pName, mech);
+ }
+
+ public String getLocalName(Oid mech) throws GSSException {
+ if (mech.equals(this.mech))
+ return cStub.localName(pName, mech);
+ throw new GSSException(GSSException.BAD_MECH);
+ }
+
public Oid getStringNameType() {
return printableType;
}
@@ -282,6 +315,10 @@ public boolean isAnonymousName() {
return (GSSName.NT_ANONYMOUS.equals(printableType));
}
+ public boolean isDefaultCredentialName() {
+ return (this == DEF_ACCEPTOR);
+ }
+
public void dispose() {
if (pName != 0) {
cStub.releaseName(pName);
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java
index d2d5367f1b8..2d99c9517cf 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java
@@ -63,18 +63,20 @@ class NativeGSSContext implements GSSContextSpi {
private GSSNameElement srcName;
private GSSNameElement targetName;
private GSSCredElement cred;
+ private GSSCredElement disposeCred;
private boolean isInitiator;
private boolean isEstablished;
private Oid actualMech; // Assigned during context establishment
private ChannelBinding cb;
private GSSCredElement delegatedCred;
+ private GSSCredElement disposeDelegatedCred;
private int flags;
private int lifetime = GSSCredential.DEFAULT_LIFETIME;
private final GSSLibStub cStub;
- private boolean skipDelegPermCheck;
- private boolean skipServicePermCheck;
+ private boolean skipDelegPermCheck = false;
+ private boolean skipServicePermCheck = false;
// Retrieve the (preferred) mech out of SPNEGO tokens, i.e.
// NegTokenInit & NegTokenTarg
@@ -109,26 +111,20 @@ private static Oid getMechFromSpNegoToken(byte[] token,
// Perform the Service permission check
private void doServicePermCheck() throws GSSException {
if (System.getSecurityManager() != null) {
- String action = (isInitiator? "initiate" : "accept");
- // Need to check Service permission for accessing
- // initiator cred for SPNEGO during context establishment
- if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator
- && !isEstablished) {
- if (srcName == null) {
- // Check by creating default initiator KRB5 cred
- GSSCredElement tempCred =
- new GSSCredElement(null, lifetime,
- GSSCredential.INITIATE_ONLY,
- GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID));
- tempCred.dispose();
- } else {
+ try {
+ if (isInitiator && srcName != null) {
String tgsName = Krb5Util.getTGSName(srcName);
- Krb5Util.checkServicePermission(tgsName, action);
+ Krb5Util.checkServicePermission(tgsName, "initiate");
+ skipServicePermCheck = true;
+ } else if (!isInitiator && targetName != null) {
+ String targetStr = targetName.getKrbName();
+ Krb5Util.checkServicePermission(targetStr, "accept");
+ skipServicePermCheck = true;
}
+ } catch (GSSException ge) {
+ dispose();
+ throw ge;
}
- String targetStr = targetName.getKrbName();
- Krb5Util.checkServicePermission(targetStr, action);
- skipServicePermCheck = true;
}
}
@@ -136,6 +132,8 @@ private void doServicePermCheck() throws GSSException {
private void doDelegPermCheck() throws GSSException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
+ if (targetName == null)
+ return;
String targetStr = targetName.getKrbName();
String tgsStr = Krb5Util.getTGSName(targetName);
StringBuilder sb = new StringBuilder("\"");
@@ -192,17 +190,19 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen)
}
cStub = stub;
cred = myCred;
+ disposeCred = null;
targetName = peer;
isInitiator = true;
lifetime = time;
if (GSSUtil.isKerberosMech(cStub.getMech())) {
- doServicePermCheck();
if (cred == null) {
- cred = new GSSCredElement(null, lifetime,
- GSSCredential.INITIATE_ONLY, cStub);
+ disposeCred = cred =
+ new GSSCredElement(null, lifetime,
+ GSSCredential.INITIATE_ONLY, cStub);
}
srcName = cred.getName();
+ doServicePermCheck();
}
}
@@ -211,6 +211,7 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen)
throws GSSException {
cStub = stub;
cred = myCred;
+ disposeCred = null;
if (cred != null) targetName = cred.getName();
@@ -236,15 +237,22 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen)
if (info.length != NUM_OF_INQUIRE_VALUES) {
throw new RuntimeException("Bug w/ GSSLibStub.inquireContext()");
}
- srcName = new GSSNameElement(info[0], cStub);
- targetName = new GSSNameElement(info[1], cStub);
isInitiator = (info[2] != 0);
isEstablished = (info[3] != 0);
flags = (int) info[4];
lifetime = (int) info[5];
+ if (isEstablished) {
+ srcName = new GSSNameElement(info[0], actualMech, cStub);
+ targetName = new GSSNameElement(info[1], actualMech, cStub);
+ } else {
+ srcName = null;
+ targetName = null;
+ }
// Do Service Permission check when importing SPNEGO context
- // just to be safe
+ // just to be safe. WAT, no. If the caller has an exported sec
+ // context token, it's because someone gave it to it, therefore there's
+ // no need to do any further permission checking. REMOVE!
Oid mech = cStub.getMech();
if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) {
doServicePermCheck();
@@ -281,26 +289,40 @@ public byte[] initSecContext(InputStream is, int mechTokenLen)
// Only inspect the token when the permission check
// has not been performed
- if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) {
+ if (!GSSUtil.isSpNegoMech(cStub.getMech())) {
+ actualMech = cStub.getMech();
+ } else if (actualMech == null && outToken != null) {
// WORKAROUND for SEAM bug#6287358
- actualMech = getMechFromSpNegoToken(outToken, true);
+ //
+ // This is where some C GSS SPNEGO implementations fail to make
+ // the real actual mechanism available.
+ // getMechFromSpNegoToken() does the horrible, no good, very
+ // bad thing its name says it does. For now we retain this bit
+ // of evil.
+ //
+ // XXX Time to remove this workaround. It's been 20
+ // years.
+ try {
+ actualMech = getMechFromSpNegoToken(outToken, true);
+ } catch (GSSException e) { }
+ }
- if (GSSUtil.isKerberosMech(actualMech)) {
- if (!skipServicePermCheck) doServicePermCheck();
- if (!skipDelegPermCheck) doDelegPermCheck();
- }
+ if (actualMech != null && GSSUtil.isKerberosMech(actualMech)) {
+ if (!skipServicePermCheck) doServicePermCheck();
+ if (!skipDelegPermCheck) doDelegPermCheck();
}
if (isEstablished) {
+ // XXX We should attempt to get actualMech from the cStub here,
+ // and take it even if we got a semblance of an actualMech from
+ // the SPNEGO token, as long as the one returned by the cStub
+ // isn't the SPNEGO OID.
if (srcName == null) {
srcName = new GSSNameElement
- (cStub.getContextName(pContext, true), cStub);
- }
- if (cred == null) {
- cred = new GSSCredElement(srcName, lifetime,
- GSSCredential.INITIATE_ONLY,
- cStub);
+ (cStub.getContextName(pContext, true), actualMech,
+ cStub);
}
+ if (!skipServicePermCheck) doServicePermCheck();
}
}
return outToken;
@@ -315,27 +337,22 @@ public byte[] acceptSecContext(InputStream is, int mechTokenLen)
inToken.length);
long pCred = (cred == null? 0 : cred.pCred);
outToken = cStub.acceptContext(pCred, cb, inToken, this);
+ disposeDelegatedCred = delegatedCred;
SunNativeProvider.debug("acceptSecContext=> outToken len=" +
(outToken == null? 0 : outToken.length));
- if (targetName == null) {
+ if (isEstablished && targetName == null) {
targetName = new GSSNameElement
- (cStub.getContextName(pContext, false), cStub);
- // Replace the current default acceptor cred now that
- // the context acceptor name is available
- if (cred != null) cred.dispose();
- cred = new GSSCredElement(targetName, lifetime,
- GSSCredential.ACCEPT_ONLY, cStub);
+ (cStub.getContextName(pContext, false), actualMech, cStub);
}
-
- // Only inspect token when the permission check has not
- // been performed
- if (GSSUtil.isSpNegoMech(cStub.getMech()) &&
- (outToken != null) && !skipServicePermCheck) {
- if (GSSUtil.isKerberosMech(getMechFromSpNegoToken
- (outToken, false))) {
- doServicePermCheck();
- }
+ if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null &&
+ actualMech == null) {
+ try {
+ actualMech = getMechFromSpNegoToken(outToken, true);
+ } catch (GSSException e) { }
+ }
+ if (isEstablished && targetName != null && !skipServicePermCheck) {
+ doServicePermCheck();
}
}
return outToken;
@@ -346,9 +363,13 @@ public boolean isEstablished() {
}
public void dispose() throws GSSException {
+ if (disposeCred != null)
+ disposeCred.dispose();
+ if (disposeDelegatedCred != null)
+ disposeDelegatedCred.dispose();
+ disposeDelegatedCred = disposeCred = cred = null;
srcName = null;
targetName = null;
- cred = null;
delegatedCred = null;
if (pContext != 0) {
pContext = cStub.deleteContext(pContext);
@@ -612,6 +633,7 @@ public Oid getMech() throws GSSException {
}
}
public GSSCredentialSpi getDelegCred() throws GSSException {
+ disposeDelegatedCred = null;
return delegatedCred;
}
public boolean isInitiator() {
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java
index 89012727989..66dd30660c8 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java
@@ -96,6 +96,7 @@ public GSSNameSpi getNameElement(byte[] name, Oid nameType)
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
+ String password,
int initLifetime,
int acceptLifetime,
int usage)
@@ -118,14 +119,26 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
if (credElement == null) {
// No cred in the Subject
if (usage == GSSCredential.INITIATE_ONLY) {
- credElement = new GSSCredElement(nname, initLifetime,
- usage, cStub);
+ if (password == null) {
+ credElement = new GSSCredElement(nname, initLifetime,
+ usage, cStub);
+ } else {
+ credElement = new GSSCredElement(nname, password,
+ initLifetime,
+ usage, cStub);
+ }
} else if (usage == GSSCredential.ACCEPT_ONLY) {
if (nname == null) {
nname = GSSNameElement.DEF_ACCEPTOR;
}
- credElement = new GSSCredElement(nname, acceptLifetime,
- usage, cStub);
+ if (password == null) {
+ credElement = new GSSCredElement(nname, acceptLifetime,
+ usage, cStub);
+ } else {
+ credElement = new GSSCredElement(nname, password,
+ acceptLifetime,
+ usage, cStub);
+ }
} else {
throw new GSSException(GSSException.FAILURE, -1,
"Unknown usage mode requested");
@@ -134,6 +147,15 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
return credElement;
}
+ public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
+ int initLifetime,
+ int acceptLifetime,
+ int usage)
+ throws GSSException {
+ return getCredentialElement(name, null, initLifetime,
+ acceptLifetime, usage);
+ }
+
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myCred,
int lifetime)
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java
index 25ee2a693d0..e5b6edf2a76 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java
@@ -105,6 +105,12 @@ public HashMap run() {
Oid[] mechs = GSSLibStub.indicateMechs();
HashMap map =
new HashMap();
+ // If the GSSLibStub does not support SPNEGO,
+ // we could use ours, but ours has too much
+ // knowledge of the Java Krb5 GSS mechanism, so
+ // we can't, but if we could we'd do it thusly:
+ // map.put("GssApiMechanism.1.3.6.1.5.5.2",
+ // "sun.security.jgss.spnego.SpNegoMechFactory");
for (int i = 0; i < mechs.length; i++) {
debug("Native MF for " + mechs[i]);
map.put("GssApiMechanism." + mechs[i],
diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java
index 953bb557041..cb16286ba15 100644
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java
@@ -397,6 +397,9 @@ public void destroy() {
}
}
+ // XXX It'd be nice to implement AutoCloseable with a close() that calls
+ // destroy()
+
/**
* Checks if the current state is the specified one.
* @param st the expected state
diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c
index acb9a914f1a..0e72bdf250a 100644
--- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c
+++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c
@@ -149,21 +149,20 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getMechPtr(JNIEnv *env,
* Utility routine which releases the specified gss_channel_bindings_t
* structure.
*/
-void deleteGSSCB(gss_channel_bindings_t cb) {
-
+static void deleteGSSCB(JNIEnv *env, gss_channel_bindings_t cb) {
if (cb == GSS_C_NO_CHANNEL_BINDINGS) return;
/* release initiator address */
if (cb->initiator_addrtype != GSS_C_AF_NULLADDR) {
- resetGSSBuffer(&(cb->initiator_address));
+ resetGSSBuffer(env, NULL, &(cb->initiator_address));
}
/* release acceptor address */
if (cb->acceptor_addrtype != GSS_C_AF_NULLADDR) {
- resetGSSBuffer(&(cb->acceptor_address));
+ resetGSSBuffer(env, NULL, &(cb->acceptor_address));
}
/* release application data */
- if (cb->application_data.length != 0) {
- resetGSSBuffer(&(cb->application_data));
+ if (cb->application_data.value != NULL) {
+ resetGSSBuffer(env, NULL, &(cb->application_data));
}
free(cb);
}
@@ -188,9 +187,11 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) {
return NULL;
}
- // initialize addrtype in CB first
+ /* Fully initialize to a state safe for cleanup */
cb->initiator_addrtype = GSS_C_AF_NULLADDR;
cb->acceptor_addrtype = GSS_C_AF_NULLADDR;
+ cb->application_data.length = 0;
+ cb->application_data.value = NULL;
// addresses needs to be initialized to empty
memset(&cb->initiator_address, 0, sizeof(cb->initiator_address));
@@ -208,11 +209,11 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) {
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
- cb->initiator_addrtype = GSS_C_AF_INET;
- initGSSBuffer(env, value, &(cb->initiator_address));
+ initGSSBuffer(env, value, &(cb->initiator_address), JNI_TRUE);
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
+ cb->initiator_addrtype = GSS_C_AF_INET;
}
/* set up acceptor address */
jinetAddr = (*env)->CallObjectMethod(env, jcb,
@@ -226,11 +227,11 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) {
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
- cb->acceptor_addrtype = GSS_C_AF_INET;
- initGSSBuffer(env, value, &(cb->acceptor_address));
+ initGSSBuffer(env, value, &(cb->acceptor_address), JNI_TRUE);
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
+ cb->acceptor_addrtype = GSS_C_AF_INET;
}
/* set up application data */
value = (*env)->CallObjectMethod(env, jcb,
@@ -238,13 +239,13 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) {
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
- initGSSBuffer(env, value, &(cb->application_data));
+ initGSSBuffer(env, value, &(cb->application_data), JNI_TRUE);
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
return cb;
cleanup:
- deleteGSSCB(cb);
+ deleteGSSCB(env, cb);
return NULL;
}
@@ -295,7 +296,7 @@ JNIEXPORT jobjectArray JNICALL
Java_sun_security_jgss_wrapper_GSSLibStub_inquireNamesForMech(JNIEnv *env,
jobject jobj)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
gss_OID mech;
gss_OID_set nameTypes;
jobjectArray result;
@@ -309,7 +310,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireNamesForMech(JNIEnv *env,
/* release intermediate buffers before checking status */
result = getJavaOIDArray(env, nameTypes);
- deleteGSSOIDSet(nameTypes);
+ (*ftab->releaseOidSet)(&dummy, &nameTypes);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
@@ -366,14 +367,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importName(JNIEnv *env,
TRACE0("[GSSLibStub_importName]");
- initGSSBuffer(env, jnameVal, &nameVal);
+ initGSSBuffer(env, jnameVal, &nameVal, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
return jlong_zero;
}
nameType = newGSSOID(env, jnameType);
if ((*env)->ExceptionCheck(env)) {
- resetGSSBuffer(&nameVal);
+ resetGSSBuffer(env, jnameVal, &nameVal);
return jlong_zero;
}
@@ -385,7 +386,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importName(JNIEnv *env,
/* release intermediate buffers */
deleteGSSOID(nameType);
- resetGSSBuffer(&nameVal);
+ resetGSSBuffer(env, jnameVal, &nameVal);
checkStatus(env, jobj, major, minor, "[GSSLibStub_importName]");
if ((*env)->ExceptionCheck(env)) {
@@ -472,10 +473,9 @@ JNIEXPORT jbyteArray JNICALL
Java_sun_security_jgss_wrapper_GSSLibStub_exportName(JNIEnv *env,
jobject jobj,
jlong pName) {
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
gss_name_t nameHdl, mNameHdl;
- gss_buffer_desc outBuf;
- jbyteArray jresult;
+ gss_buffer_desc outBuf = GSS_C_EMPTY_BUFFER;
nameHdl = (gss_name_t) jlong_to_ptr(pName);
@@ -500,26 +500,79 @@ Java_sun_security_jgss_wrapper_GSSLibStub_exportName(JNIEnv *env,
}
major = (*ftab->exportName)(&minor, mNameHdl, &outBuf);
- Java_sun_security_jgss_wrapper_GSSLibStub_releaseName
- (env, jobj, ptr_to_jlong(mNameHdl));
- if ((*env)->ExceptionCheck(env)) {
- /* release intermediate buffers */
- (*ftab->releaseBuffer)(&minor, &outBuf);
- return NULL;
- }
+ (void) (*ftab->releaseName)(&dummy, &mNameHdl);
}
- /* release intermediate buffers before checking status */
- jresult = getJavaBuffer(env, &outBuf);
+ checkStatus(env, jobj, major, minor, "[GSSLibStub_exportName]");
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseBuffer)(&dummy, &outBuf);
return NULL;
}
+ /* Map outBuf to byteArray result and release */
+ return getJavaBuffer(env, &outBuf, JNI_TRUE);
+}
- checkStatus(env, jobj, major, minor, "[GSSLibStub_exportName]");
- if ((*env)->ExceptionCheck(env)) {
+/*
+ * Class: sun_security_jgss_wrapper_GSSLibStub
+ * Method: localName
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_sun_security_jgss_wrapper_GSSLibStub_localName(JNIEnv *env,
+ jobject jobj,
+ jlong pName,
+ jobject jOid)
+{
+ OM_uint32 minor, major, dummy;
+ gss_name_t nameHdl, mnNameHdl;
+ gss_buffer_desc outBuf = GSS_C_EMPTY_BUFFER;
+ gss_OID mech;
+
+ nameHdl = (gss_name_t) jlong_to_ptr(pName);
+
+ if (ftab->localName == NULL) {
+ TRACE0("GSSLibStub_localName not supported by GSS provider");
+ checkStatus(env, jobj, GSS_S_UNAVAILABLE, minor=0,
+ "[GSSLibStub_localName]");
return NULL;
}
- return jresult;
+ mech = newGSSOID(env, jOid);
+
+ /* gss_localname(...) => GSS_S_NAME_NOT_MN, GSS_S_BAD_NAMETYPE,
+ GSS_S_BAD_NAME */
+ major = (*ftab->localName)(&minor, nameHdl, mech, &outBuf);
+ if (major == GSS_S_COMPLETE) {
+ deleteGSSOID(mech);
+ return getJavaString(env, &outBuf);
+ }
+ (*ftab->releaseBuffer)(&minor, &outBuf);
+
+ if (major != GSS_S_NAME_NOT_MN) {
+ checkStatus(env, jobj, major, minor, "[GSSLibStub_localName]");
+ goto err;
+ }
+
+ /* canonicalize the internal name to MN and retry */
+ TRACE0("[GSSLibStub_localName] canonicalize and re-try");
+
+ major = (*ftab->canonicalizeName)(&minor, nameHdl, mech, &mnNameHdl);
+ checkStatus(env, jobj, major, minor, "[GSSLibStub_localName]");
+ if ((*env)->ExceptionCheck(env))
+ goto err;
+
+ major = (*ftab->localName)(&minor, mnNameHdl, mech, &outBuf);
+ (void) (*ftab->releaseName)(&dummy, &mnNameHdl);
+
+ checkStatus(env, jobj, major, minor, "[GSSLibStub_localName]");
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE && major == GSS_S_COMPLETE) {
+ deleteGSSOID(mech);
+ return getJavaString(env, &outBuf);
+ }
+ (*ftab->releaseBuffer)(&minor, &outBuf);
+
+err:
+ deleteGSSOID(mech);
+ return NULL;
}
/*
@@ -533,7 +586,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_displayName(JNIEnv *env,
jlong pName) {
OM_uint32 minor, major;
gss_name_t nameHdl;
- gss_buffer_desc outNameBuf;
+ gss_buffer_desc outNameBuf = GSS_C_EMPTY_BUFFER;
gss_OID outNameType;
jstring jname;
jobject jtype;
@@ -588,17 +641,19 @@ Java_sun_security_jgss_wrapper_GSSLibStub_displayName(JNIEnv *env,
/*
* Class: sun_security_jgss_wrapper_GSSLibStub
* Method: acquireCred
- * Signature: (JII)J
+ * Signature: (JLjava/lang/String;II)J
*/
JNIEXPORT jlong JNICALL
Java_sun_security_jgss_wrapper_GSSLibStub_acquireCred(JNIEnv *env,
jobject jobj,
jlong pName,
+ jstring jPassword,
jint reqTime,
jint usage)
{
OM_uint32 minor, major;
gss_OID mech;
+ gss_OID_set_desc singleton;
gss_OID_set mechs;
gss_cred_usage_t credUsage;
gss_name_t nameHdl;
@@ -608,7 +663,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acquireCred(JNIEnv *env,
TRACE0("[GSSLibStub_acquireCred]");
mech = (gss_OID) jlong_to_ptr((*env)->GetLongField(env, jobj, FID_GSSLibStub_pMech));
- mechs = newGSSOIDSet(mech);
credUsage = (gss_cred_usage_t) usage;
nameHdl = (gss_name_t) jlong_to_ptr(pName);
@@ -616,11 +670,29 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acquireCred(JNIEnv *env,
/* gss_acquire_cred(...) => GSS_S_BAD_MECH, GSS_S_BAD_NAMETYPE,
GSS_S_BAD_NAME, GSS_S_CREDENTIALS_EXPIRED, GSS_S_NO_CRED */
- major =
- (*ftab->acquireCred)(&minor, nameHdl, reqTime, mechs,
- credUsage, &credHdl, NULL, NULL);
- /* release intermediate buffers */
- deleteGSSOIDSet(mechs);
+ if (jPassword == NULL) {
+ mechs = makeGSSOIDSet(&singleton, mech);
+ major = (*ftab->acquireCred)(&minor, nameHdl, reqTime, mechs, credUsage,
+ &credHdl, NULL, NULL);
+ } else {
+ gss_buffer_desc password;
+
+ if (ftab->acquireCredWithPassword == NULL) {
+ const char *msg = "GSSLibStub_acquireCred with password not supported "
+ "by GSS provider";
+
+ TRACE0(msg);
+ checkStatus(env, jobj, GSS_S_UNAVAILABLE, minor=0, msg);
+ return ptr_to_jlong(NULL);
+ }
+
+ initGSSBufferString(env, jPassword, &password);
+ mechs = makeGSSOIDSet(&singleton, mech);
+ major = (*ftab->acquireCredWithPassword)(&minor, nameHdl, &password,
+ reqTime, mechs, credUsage,
+ &credHdl, NULL, NULL);
+ resetGSSBufferString(env, jPassword, &password);
+ }
TRACE1("[GSSLibStub_acquireCred] pCred=%" PRIuPTR "", (uintptr_t) credHdl);
@@ -792,7 +864,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importContext(JNIEnv *env,
TRACE0("[GSSLibStub_importContext]");
contextHdl = GSS_C_NO_CONTEXT;
- initGSSBuffer(env, jctxtToken, &ctxtToken);
+ initGSSBuffer(env, jctxtToken, &ctxtToken, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
@@ -804,7 +876,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importContext(JNIEnv *env,
TRACE1("[GSSLibStub_importContext] pContext=%" PRIuPTR "", (uintptr_t) contextHdl);
/* release intermediate buffers */
- resetGSSBuffer(&ctxtToken);
+ resetGSSBuffer(env, jctxtToken, &ctxtToken);
checkStatus(env, jobj, major, minor, "[GSSLibStub_importContext]");
/* return immediately if an exception has occurred */
@@ -834,7 +906,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importContext(JNIEnv *env,
} else {
/* mech mismatch - clean up then return null */
major = (*ftab->deleteSecContext)(&minor, &contextHdl, GSS_C_NO_BUFFER);
- checkStatus(env, jobj, major, minor,
+ checkStatus(env, jobj, GSS_S_FAILURE, minor,
"[GSSLibStub_importContext] cleanup");
return NULL;
}
@@ -854,7 +926,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env,
jbyteArray jinToken,
jobject jcontextSpi)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
gss_cred_id_t credHdl ;
gss_ctx_id_t contextHdl, contextHdlSave;
gss_name_t targetName;
@@ -863,12 +935,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env,
OM_uint32 time, aTime;
gss_channel_bindings_t cb;
gss_buffer_desc inToken;
- gss_buffer_desc outToken;
- jbyteArray jresult;
-/* UNCOMMENT after SEAM bug#6287358 is backported to S10
+ gss_buffer_desc outToken = GSS_C_EMPTY_BUFFER;
gss_OID aMech;
jobject jMech;
-*/
TRACE0("[GSSLibStub_initContext]");
@@ -886,9 +955,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env,
return NULL;
}
- initGSSBuffer(env, jinToken, &inToken);
+ initGSSBuffer(env, jinToken, &inToken, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
- deleteGSSCB(cb);
+ deleteGSSCB(env, cb);
return NULL;
}
@@ -902,7 +971,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env,
GSS_S_BAD_NAMETYPE, GSS_S_BAD_NAME(!), GSS_S_BAD_MECH */
major = (*ftab->initSecContext)(&minor, credHdl,
&contextHdl, targetName, mech,
- flags, time, cb, &inToken, NULL /*aMech*/,
+ flags, time, cb, &inToken, &aMech,
&outToken, &aFlags, &aTime);
TRACE2("[GSSLibStub_initContext] after: pContext=%" PRIuPTR ", outToken len=%ld",
@@ -931,30 +1000,28 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env,
FID_NativeGSSContext_isEstablished,
JNI_TRUE);
-/* UNCOMMENT after SEAM bug#6287358 is backported to S10
- jMech = getJavaOID(env, aMech);
- (*env)->SetObjectField(env, jcontextSpi,
- FID_NativeGSSContext_actualMech, jMech);
-*/
+ jMech = (aMech == GSS_C_NO_OID) ? NULL : getJavaOID(env, aMech);
+ if (!(*env)->ExceptionCheck(env)) {
+ (*env)->SetObjectField(env, jcontextSpi,
+ FID_NativeGSSContext_actualMech, jMech);
+ }
} else if (major & GSS_S_CONTINUE_NEEDED) {
TRACE0("[GSSLibStub_initContext] context not established");
- major -= GSS_S_CONTINUE_NEEDED;
+ major &= ~GSS_S_CONTINUE_NEEDED;
}
}
/* release intermediate buffers before checking status */
- deleteGSSCB(cb);
- resetGSSBuffer(&inToken);
- jresult = getJavaBuffer(env, &outToken);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
- }
+ deleteGSSCB(env, cb);
+ resetGSSBuffer(env, jinToken, &inToken);
checkStatus(env, jobj, major, minor, "[GSSLibStub_initContext]");
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseBuffer)(&dummy, &outToken);
return NULL;
}
- return jresult;
+ /* Map outToken to byteArray result and release */
+ return getJavaBuffer(env, &outToken, JNI_TRUE);
}
/*
@@ -970,23 +1037,23 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
jbyteArray jinToken,
jobject jcontextSpi)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
OM_uint32 minor2, major2;
gss_ctx_id_t contextHdl, contextHdlSave;
gss_cred_id_t credHdl;
gss_buffer_desc inToken;
gss_channel_bindings_t cb;
- gss_name_t srcName;
- gss_buffer_desc outToken;
+ gss_name_t srcName = GSS_C_NO_NAME;
+ gss_buffer_desc outToken = GSS_C_EMPTY_BUFFER;
gss_OID aMech;
OM_uint32 aFlags;
OM_uint32 aTime;
- gss_cred_id_t delCred;
- jobject jsrcName=GSS_C_NO_NAME;
+ gss_cred_id_t delCred = GSS_C_NO_CREDENTIAL;
+ jobject jsrcName = NULL;
jobject jdelCred;
- jobject jMech;
+ jobject jMech = NULL;
jboolean setTarget;
- gss_name_t targetName;
+ gss_name_t targetName = GSS_C_NO_NAME;
jobject jtargetName;
TRACE0("[GSSLibStub_acceptContext]");
@@ -994,17 +1061,15 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
contextHdl = contextHdlSave = (gss_ctx_id_t)jlong_to_ptr(
(*env)->GetLongField(env, jcontextSpi, FID_NativeGSSContext_pContext));
credHdl = (gss_cred_id_t) jlong_to_ptr(pCred);
- initGSSBuffer(env, jinToken, &inToken);
+ initGSSBuffer(env, jinToken, &inToken, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
cb = newGSSCB(env, jcb);
if ((*env)->ExceptionCheck(env)) {
- resetGSSBuffer(&inToken);
+ resetGSSBuffer(env, jinToken, &inToken);
return NULL;
}
- srcName = targetName = GSS_C_NO_NAME;
- delCred = GSS_C_NO_CREDENTIAL;
setTarget = (credHdl == GSS_C_NO_CREDENTIAL);
aFlags = 0;
@@ -1022,8 +1087,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
&aFlags, &aTime, &delCred);
/* release intermediate buffers before checking status */
- deleteGSSCB(cb);
- resetGSSBuffer(&inToken);
+ deleteGSSCB(env, cb);
+ resetGSSBuffer(env, jinToken, &inToken);
TRACE3("[GSSLibStub_acceptContext] after: pCred=%" PRIuPTR ", pContext=%" PRIuPTR ", pDelegCred=%" PRIuPTR "",
(uintptr_t)credHdl, (uintptr_t)contextHdl, (uintptr_t) delCred);
@@ -1039,9 +1104,19 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
if (GSS_ERROR(major) == GSS_S_COMPLETE) {
/* update member values if needed */
- // WORKAROUND for a Heimdal bug
+
+ jMech = (aMech == GSS_C_NO_OID) ? NULL : getJavaOID(env, aMech);
+ if (!(*env)->ExceptionCheck(env)) {
+ (*env)->SetObjectField(env, jcontextSpi,
+ FID_NativeGSSContext_actualMech, jMech);
+ }
+ if ((*env)->ExceptionCheck(env)) {
+ goto error;
+ }
+
+ /* WORKAROUND for an old Heimdal bug */
if (delCred == GSS_C_NO_CREDENTIAL) {
- aFlags &= 0xfffffffe;
+ aFlags &= ~GSS_C_DELEG_FLAG;
}
(*env)->SetIntField(env, jcontextSpi, FID_NativeGSSContext_flags, aFlags);
TRACE1("[GSSLibStub_acceptContext] set flags=0x%x", aFlags);
@@ -1058,13 +1133,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
jtargetName = (*env)->NewObject(env, CLS_GSSNameElement,
MID_GSSNameElement_ctor,
- ptr_to_jlong(targetName), jobj);
+ ptr_to_jlong(targetName), jMech, jobj);
if ((*env)->ExceptionCheck(env)) {
goto error;
}
TRACE1("[GSSLibStub_acceptContext] set targetName=%" PRIuPTR "",
(uintptr_t)targetName);
+ targetName = GSS_C_NO_NAME;
(*env)->SetObjectField(env, jcontextSpi, FID_NativeGSSContext_targetName,
jtargetName);
@@ -1075,12 +1151,13 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
if (srcName != GSS_C_NO_NAME) {
jsrcName = (*env)->NewObject(env, CLS_GSSNameElement,
MID_GSSNameElement_ctor,
- ptr_to_jlong(srcName), jobj);
+ ptr_to_jlong(srcName), jMech, jobj);
if ((*env)->ExceptionCheck(env)) {
goto error;
}
TRACE1("[GSSLibStub_acceptContext] set srcName=%" PRIuPTR "", (uintptr_t)srcName);
+ srcName = GSS_C_NO_NAME;
(*env)->SetObjectField(env, jcontextSpi, FID_NativeGSSContext_srcName,
jsrcName);
@@ -1096,12 +1173,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
(*env)->SetBooleanField(env, jcontextSpi,
FID_NativeGSSContext_isEstablished,
JNI_TRUE);
- jMech = getJavaOID(env, aMech);
- if ((*env)->ExceptionCheck(env)) {
- goto error;
- }
- (*env)->SetObjectField(env, jcontextSpi,
- FID_NativeGSSContext_actualMech, jMech);
if ((*env)->ExceptionCheck(env)) {
goto error;
}
@@ -1112,11 +1183,12 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
if ((*env)->ExceptionCheck(env)) {
goto error;
}
+ TRACE1("[GSSLibStub_acceptContext] set delegatedCred=%" PRIuPTR "",
+ (uintptr_t) delCred);
+ delCred = GSS_C_NO_CREDENTIAL;
(*env)->SetObjectField(env, jcontextSpi,
FID_NativeGSSContext_delegatedCred,
jdelCred);
- TRACE1("[GSSLibStub_acceptContext] set delegatedCred=%" PRIuPTR "",
- (uintptr_t) delCred);
if ((*env)->ExceptionCheck(env)) {
goto error;
@@ -1129,10 +1201,18 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env,
(*env)->SetIntField(env, jcontextSpi, FID_NativeGSSContext_lifetime,
getJavaTime(aTime));
}
- major -= GSS_S_CONTINUE_NEEDED;
+ major &= ~GSS_S_CONTINUE_NEEDED;
}
}
- return getJavaBuffer(env, &outToken);
+
+ checkStatus(env, jobj, major, minor, "[GSSLibStub_acceptContext]");
+ if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseBuffer)(&dummy, &outToken);
+ return NULL;
+ }
+
+ /* Map outToken to byteArray result and release */
+ return getJavaBuffer(env, &outToken, JNI_TRUE);
error:
(*ftab->releaseBuffer)(&minor, &outToken);
@@ -1158,12 +1238,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env,
jobject jobj,
jlong pContext)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
gss_ctx_id_t contextHdl;
- gss_name_t srcName, targetName;
- OM_uint32 time;
- OM_uint32 flags;
- int isInitiator, isEstablished;
+ gss_name_t srcName = GSS_C_NO_NAME;
+ gss_name_t targetName = GSS_C_NO_NAME;
+ OM_uint32 time = 0;
+ OM_uint32 flags = 0;
+ int isInitiator = 0;
+ int isEstablished = 0;
jlong result[6];
jlongArray jresult;
@@ -1171,10 +1253,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env,
TRACE1("[GSSLibStub_inquireContext] %" PRIuPTR "", (uintptr_t)contextHdl);
- srcName = targetName = GSS_C_NO_NAME;
- time = 0;
- flags = isInitiator = isEstablished = 0;
-
/* gss_inquire_context(...) => GSS_S_NO_CONTEXT(!) */
major = (*ftab->inquireContext)(&minor, contextHdl, &srcName,
&targetName, &time, NULL, &flags,
@@ -1185,6 +1263,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env,
checkStatus(env, jobj, major, minor, "[GSSLibStub_inquireContext]");
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseName)(&dummy, &srcName);
+ (void) (*ftab->releaseName)(&dummy, &targetName);
return NULL;
}
result[0] = ptr_to_jlong(srcName);
@@ -1195,11 +1275,12 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env,
result[5] = (jlong) getJavaTime(time);
jresult = (*env)->NewLongArray(env, 6);
- if (jresult == NULL) {
- return NULL;
+ if (jresult != NULL) {
+ (*env)->SetLongArrayRegion(env, jresult, 0, 6, result);
}
- (*env)->SetLongArrayRegion(env, jresult, 0, 6, result);
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseName)(&dummy, &srcName);
+ (void) (*ftab->releaseName)(&dummy, &targetName);
return NULL;
}
return jresult;
@@ -1245,7 +1326,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getContextName(JNIEnv *env,
jobject jobj, jlong pContext, jboolean isSrc)
{
OM_uint32 minor, major;
- gss_name_t nameHdl;
+ gss_name_t nameHdl = GSS_C_NO_NAME;
gss_ctx_id_t contextHdl;
contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext);
@@ -1253,7 +1334,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getContextName(JNIEnv *env,
TRACE2("[GSSLibStub_getContextName] %" PRIuPTR ", isSrc=%d",
(uintptr_t)contextHdl, isSrc);
- nameHdl = GSS_C_NO_NAME;
if (isSrc == JNI_TRUE) {
major = (*ftab->inquireContext)(&minor, contextHdl, &nameHdl, NULL,
NULL, NULL, NULL, NULL, NULL);
@@ -1351,6 +1431,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrapSizeLimit(JNIEnv *env,
gss_ctx_id_t contextHdl;
OM_uint32 outSize, maxInSize;
gss_qop_t qop;
+ jint result;
contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext);
@@ -1374,7 +1455,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrapSizeLimit(JNIEnv *env,
if ((*env)->ExceptionCheck(env)) {
return 0;
}
- return (jint) maxInSize;
+
+ /* Right-shift maxInSize until it fits into jint */
+ result = (jint)maxInSize;
+ while (result < 0 || maxInSize != (OM_uint32)result) {
+ result = (jint)(maxInSize >>= 1);
+ }
+
+ return result;
}
/*
@@ -1387,10 +1475,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_exportContext(JNIEnv *env,
jobject jobj,
jlong pContext)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
gss_ctx_id_t contextHdl;
- gss_buffer_desc interProcToken;
- jbyteArray jresult;
+ gss_buffer_desc interProcToken = GSS_C_EMPTY_BUFFER;
contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext);
@@ -1406,17 +1493,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_exportContext(JNIEnv *env,
major =
(*ftab->exportSecContext)(&minor, &contextHdl, &interProcToken);
- /* release intermediate buffers */
- jresult = getJavaBuffer(env, &interProcToken);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
- }
checkStatus(env, jobj, major, minor, "[GSSLibStub_exportContext]");
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseBuffer)(&dummy, &interProcToken);
return NULL;
}
- return jresult;
+ /* Map interProcToken to byteArray result and release */
+ return getJavaBuffer(env, &interProcToken, JNI_TRUE);
}
/*
@@ -1429,12 +1513,11 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getMic(JNIEnv *env, jobject jobj,
jlong pContext, jint jqop,
jbyteArray jmsg)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
gss_ctx_id_t contextHdl;
gss_qop_t qop;
gss_buffer_desc msg;
- gss_buffer_desc msgToken;
- jbyteArray jresult;
+ gss_buffer_desc msgToken = GSS_C_EMPTY_BUFFER;
contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext);
@@ -1446,28 +1529,25 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getMic(JNIEnv *env, jobject jobj,
return NULL;
}
qop = (gss_qop_t) jqop;
- initGSSBuffer(env, jmsg, &msg);
+ initGSSBuffer(env, jmsg, &msg, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
/* gss_get_mic(...) => GSS_S_CONTEXT_EXPIRED, GSS_S_NO_CONTEXT(!),
GSS_S_BAD_QOP */
- major =
- (*ftab->getMic)(&minor, contextHdl, qop, &msg, &msgToken);
+ major = (*ftab->getMic)(&minor, contextHdl, qop, &msg, &msgToken);
/* release intermediate buffers */
- resetGSSBuffer(&msg);
- jresult = getJavaBuffer(env, &msgToken);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
- }
+ resetGSSBuffer(env, jmsg, &msg);
checkStatus(env, jobj, major, minor, "[GSSLibStub_getMic]");
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseBuffer)(&dummy, &msgToken);
return NULL;
}
- return jresult;
+ /* Map msgToken to byteArray result and release */
+ return getJavaBuffer(env, &msgToken, JNI_TRUE);
}
/*
@@ -1503,12 +1583,12 @@ Java_sun_security_jgss_wrapper_GSSLibStub_verifyMic(JNIEnv *env,
qop = (gss_qop_t) (*env)->CallIntMethod(env, jprop, MID_MessageProp_getQOP);
if ((*env)->ExceptionCheck(env)) { return; }
- initGSSBuffer(env, jmsg, &msg);
+ initGSSBuffer(env, jmsg, &msg, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) { return; }
- initGSSBuffer(env, jmsgToken, &msgToken);
+ initGSSBuffer(env, jmsgToken, &msgToken, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
- resetGSSBuffer(&msg);
+ resetGSSBuffer(env, jmsg, &msg);
return;
}
@@ -1519,9 +1599,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_verifyMic(JNIEnv *env,
(*ftab->verifyMic)(&minor, contextHdl, &msg, &msgToken, &qop);
/* release intermediate buffers */
- resetGSSBuffer(&msg);
- resetGSSBuffer(&msgToken);
-
+ resetGSSBuffer(env, jmsg, &msg);
+ resetGSSBuffer(env, jmsgToken, &msgToken);
+
+ /*
+ * We don't throw on supplementary status codes here, instead we pass only
+ * GSS_ERROR(major) to checkStatus() and set the supplementary status in the
+ * message properties.
+ */
checkStatus(env, jobj, GSS_ERROR(major), minor, "[GSSLibStub_verifyMic]");
if ((*env)->ExceptionCheck(env)) {
return;
@@ -1534,9 +1619,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_verifyMic(JNIEnv *env,
setSupplementaryInfo(env, jobj, jprop, GSS_SUPPLEMENTARY_INFO(major),
minor);
- if ((*env)->ExceptionCheck(env)) {
- return;
- }
}
/*
@@ -1551,11 +1633,11 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env,
jbyteArray jmsg,
jobject jprop)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
jboolean confFlag;
gss_qop_t qop;
gss_buffer_desc msg;
- gss_buffer_desc msgToken;
+ gss_buffer_desc msgToken = GSS_C_EMPTY_BUFFER;
int confState;
gss_ctx_id_t contextHdl;
jbyteArray jresult;
@@ -1582,7 +1664,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env,
return NULL;
}
- initGSSBuffer(env, jmsg, &msg);
+ initGSSBuffer(env, jmsg, &msg, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
@@ -1593,13 +1675,16 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env,
&msgToken);
/* release intermediate buffers */
- resetGSSBuffer(&msg);
- jresult = getJavaBuffer(env, &msgToken);
+ resetGSSBuffer(env, jmsg, &msg);
+ checkStatus(env, jobj, major, minor, "[GSSLibStub_wrap]");
+
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseBuffer)(&dummy, &msgToken);
return NULL;
}
- checkStatus(env, jobj, major, minor, "[GSSLibStub_wrap]");
+ /* Map msgToken to byteArray result and release */
+ jresult = getJavaBuffer(env, &msgToken, JNI_TRUE);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
@@ -1607,7 +1692,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env,
(*env)->CallVoidMethod(env, jprop, MID_MessageProp_setPrivacy,
(confState? JNI_TRUE:JNI_FALSE));
if ((*env)->ExceptionCheck(env)) {
- return NULL;
+ (*env)->DeleteLocalRef(env, jresult);
+ jresult = NULL;
}
return jresult;
}
@@ -1624,10 +1710,10 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env,
jbyteArray jmsgToken,
jobject jprop)
{
- OM_uint32 minor, major;
+ OM_uint32 minor, major, dummy;
gss_ctx_id_t contextHdl;
gss_buffer_desc msgToken;
- gss_buffer_desc msg;
+ gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
int confState;
gss_qop_t qop;
jbyteArray jresult;
@@ -1642,7 +1728,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env,
return NULL;
}
- initGSSBuffer(env, jmsgToken, &msgToken);
+ initGSSBuffer(env, jmsgToken, &msgToken, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
@@ -1656,13 +1742,24 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env,
(*ftab->unwrap)(&minor, contextHdl, &msgToken, &msg, &confState, &qop);
/* release intermediate buffers */
- resetGSSBuffer(&msgToken);
- jresult = getJavaBuffer(env, &msg);
+ resetGSSBuffer(env, jmsgToken, &msgToken);
+
+ /*
+ * We don't throw on supplementary status codes here, instead we pass only
+ * GSS_ERROR(major) to checkStatus() and set the supplementary status in the
+ * message properties.
+ */
+ checkStatus(env, jobj, GSS_ERROR(major), minor, "[GSSLibStub_unwrap]");
if ((*env)->ExceptionCheck(env)) {
+ (void) (*ftab->releaseBuffer)(&dummy, &msg);
return NULL;
}
- checkStatus(env, jobj, GSS_ERROR(major), minor, "[GSSLibStub_unwrap]");
+ /*
+ * Map msg to byteArray result and release, zero length msg maps to empty
+ * byte array, not null.
+ */
+ jresult = getJavaBuffer(env, &msg, JNI_FALSE);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
@@ -1671,15 +1768,18 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env,
(*env)->CallVoidMethod(env, jprop, MID_MessageProp_setPrivacy,
(confState != 0));
if ((*env)->ExceptionCheck(env)) {
+ (*env)->DeleteLocalRef(env, jresult);
return NULL;
}
(*env)->CallVoidMethod(env, jprop, MID_MessageProp_setQOP, qop);
if ((*env)->ExceptionCheck(env)) {
+ (*env)->DeleteLocalRef(env, jresult);
return NULL;
}
setSupplementaryInfo(env, jobj, jprop, GSS_SUPPLEMENTARY_INFO(major),
minor);
if ((*env)->ExceptionCheck(env)) {
+ (*env)->DeleteLocalRef(env, jresult);
return NULL;
}
diff --git a/src/java.security.jgss/share/native/libj2gss/NativeFunc.c b/src/java.security.jgss/share/native/libj2gss/NativeFunc.c
index da7bc0445b9..650520d1732 100644
--- a/src/java.security.jgss/share/native/libj2gss/NativeFunc.c
+++ b/src/java.security.jgss/share/native/libj2gss/NativeFunc.c
@@ -34,7 +34,9 @@ static const char COMPARE_NAME[] = "gss_compare_name";
static const char CANONICALIZE_NAME[] = "gss_canonicalize_name";
static const char EXPORT_NAME[] = "gss_export_name";
static const char DISPLAY_NAME[] = "gss_display_name";
+static const char LOCAL_NAME[] = "gss_localname";
static const char ACQUIRE_CRED[] = "gss_acquire_cred";
+static const char ACQUIRE_CRED_WITH_PASSWORD[] = "gss_acquire_cred_with_password";
static const char RELEASE_CRED[] = "gss_release_cred";
static const char INQUIRE_CRED[] = "gss_inquire_cred";
static const char IMPORT_SEC_CONTEXT[] = "gss_import_sec_context";
@@ -122,12 +124,25 @@ int loadNative(const char *libName) {
goto out;
}
+ /*
+ * This one may not be available for a given GSS library, as it's an
+ * extension, therefore we don't fail if it's missing.
+ */
+ ftab->localName = (LOCAL_NAME_FN_PTR)GETFUNC(gssLib, LOCAL_NAME);
+
ftab->acquireCred = (ACQUIRE_CRED_FN_PTR)GETFUNC(gssLib, ACQUIRE_CRED);
if (ftab->acquireCred == NULL) {
failed = TRUE;
goto out;
}
+ /*
+ * This one may not be available for a given GSS library, as it's an
+ * extension, therefore we don't fail if it's missing.
+ */
+ ftab->acquireCredWithPassword = (ACQUIRE_CRED_WITH_PASSWORD_FN_PTR)
+ GETFUNC(gssLib, ACQUIRE_CRED_WITH_PASSWORD);
+
ftab->releaseCred = (RELEASE_CRED_FN_PTR)GETFUNC(gssLib, RELEASE_CRED);
if (ftab->releaseCred == NULL) {
failed = TRUE;
diff --git a/src/java.security.jgss/share/native/libj2gss/NativeFunc.h b/src/java.security.jgss/share/native/libj2gss/NativeFunc.h
index bf4ca99c176..5b65ee742c3 100644
--- a/src/java.security.jgss/share/native/libj2gss/NativeFunc.h
+++ b/src/java.security.jgss/share/native/libj2gss/NativeFunc.h
@@ -84,6 +84,12 @@ typedef OM_uint32 (*DISPLAY_NAME_FN_PTR)
gss_buffer_t output_name_buffer,
gss_OID *output_name_type);
+typedef OM_uint32 (*LOCAL_NAME_FN_PTR)
+ (OM_uint32 *minor_status,
+ gss_name_t input_name,
+ gss_OID mech,
+ gss_buffer_t output_name_buffer);
+
typedef OM_uint32 (*ACQUIRE_CRED_FN_PTR)
(OM_uint32 *minor_status,
gss_name_t desired_name,
@@ -94,6 +100,17 @@ typedef OM_uint32 (*ACQUIRE_CRED_FN_PTR)
gss_OID_set *actual_mechs,
OM_uint32 *time_rec);
+typedef OM_uint32 (*ACQUIRE_CRED_WITH_PASSWORD_FN_PTR)
+ (OM_uint32 *minor_status,
+ gss_name_t desired_name,
+ gss_buffer_t password,
+ OM_uint32 time_req,
+ gss_OID_set desired_mech,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec);
+
typedef OM_uint32 (*RELEASE_CRED_FN_PTR)
(OM_uint32 *minor_status,
gss_cred_id_t *cred_handle);
@@ -249,7 +266,9 @@ typedef struct GSS_FUNCTION_TABLE {
CANONICALIZE_NAME_FN_PTR canonicalizeName;
EXPORT_NAME_FN_PTR exportName;
DISPLAY_NAME_FN_PTR displayName;
+ LOCAL_NAME_FN_PTR localName;
ACQUIRE_CRED_FN_PTR acquireCred;
+ ACQUIRE_CRED_WITH_PASSWORD_FN_PTR acquireCredWithPassword;
RELEASE_CRED_FN_PTR releaseCred;
INQUIRE_CRED_FN_PTR inquireCred;
IMPORT_SEC_CONTEXT_FN_PTR importSecContext;
diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c
index 8e40177babf..a9e2cf507be 100644
--- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c
+++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c
@@ -145,7 +145,7 @@ DEF_JNI_OnLoad(JavaVM *jvm, void *reserved) {
return JNI_ERR;
}
CLS_GSSNameElement = (*env)->NewGlobalRef(env, cls);
- if (CLS_GSSException == NULL) {
+ if (CLS_GSSNameElement == NULL) {
return JNI_ERR;
}
cls = (*env)->FindClass(env, "sun/security/jgss/wrapper/GSSCredElement");
@@ -271,9 +271,9 @@ DEF_JNI_OnLoad(JavaVM *jvm, void *reserved) {
}
MID_GSSNameElement_ctor =
(*env)->GetMethodID(env, CLS_GSSNameElement,
- "", "(JLsun/security/jgss/wrapper/GSSLibStub;)V");
+ "", "(JLorg/ietf/jgss/Oid;Lsun/security/jgss/wrapper/GSSLibStub;)V");
if (MID_GSSNameElement_ctor == NULL) {
- printf("Couldn't find GSSNameElement(long, GSSLibStub) constructor\n");
+ printf("Couldn't find GSSNameElement(long, Oid, GSSLibStub) constructor\n");
return JNI_ERR;
}
MID_GSSCredElement_ctor =
@@ -455,6 +455,14 @@ void throwOutOfMemoryError(JNIEnv *env, const char *message) {
throwByName(env, "java/lang/OutOfMemoryError", message);
}
+static jsize
+safe_jsize(size_t n)
+{
+ jsize res = (jsize)n;
+
+ return (res >= 0 && (size_t)res == n) ? res : -1;
+}
+
/*
* Utility routine for creating a java.lang.String object
* using the specified gss_buffer_t structure. The specified
@@ -463,31 +471,33 @@ void throwOutOfMemoryError(JNIEnv *env, const char *message) {
jstring getJavaString(JNIEnv *env, gss_buffer_t bytes) {
jstring result = NULL;
OM_uint32 minor;
- int len;
+ jsize len;
jbyteArray jbytes;
- if (bytes != NULL) {
- /* constructs the String object with new String(byte[])
- NOTE: do NOT include the trailing NULL */
- len = (int) bytes->length;
- jbytes = (*env)->NewByteArray(env, len);
- if (jbytes == NULL) {
- goto finish;
- }
+ if (bytes == NULL) {
+ return NULL;
+ }
- (*env)->SetByteArrayRegion(env, jbytes, 0, len, (jbyte *) bytes->value);
- if ((*env)->ExceptionCheck(env)) {
- goto finish;
- }
+ /* constructs the String object with new String(byte[]) */
+ if ((len = safe_jsize(bytes->length)) < 0) {
+ (*ftab->releaseBuffer)(&minor, bytes);
+ return NULL;
+ }
+ jbytes = (*env)->NewByteArray(env, len);
+ if (jbytes == NULL) {
+ (*ftab->releaseBuffer)(&minor, bytes);
+ return NULL;
+ }
+ (*env)->SetByteArrayRegion(env, jbytes, 0, len, (jbyte *) bytes->value);
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE) {
result = (*env)->NewObject(env, CLS_String, MID_String_ctor,
jbytes);
- finish:
- (*env)->DeleteLocalRef(env, jbytes);
- (*ftab->releaseBuffer)(&minor, bytes);
- return result;
- } /* else fall through */
- return NULL;
+ }
+
+ (*env)->DeleteLocalRef(env, jbytes);
+ (*ftab->releaseBuffer)(&minor, bytes);
+ return result;
}
/*
* Utility routine for generate message for the specified minor
@@ -519,7 +529,7 @@ jstring getMinorMessage(JNIEnv *env, jobject jstub, OM_uint32 statusValue) {
* not GSS_S_COMPLETE (i.e. 0).
*/
void checkStatus(JNIEnv *env, jobject jstub, OM_uint32 major,
- OM_uint32 minor, char* methodName) {
+ OM_uint32 minor, const char *methodName) {
int callingErr, routineErr, supplementaryInfo;
jint jmajor, jminor;
char* msg;
@@ -580,76 +590,128 @@ void checkStatus(JNIEnv *env, jobject jstub, OM_uint32 major,
* Utility routine for initializing gss_buffer_t structure
* with the byte[] in the specified jbyteArray object.
* NOTE: must call resetGSSBuffer() to free up the resources
- * inside the gss_buffer_t structure.
*/
void initGSSBuffer(JNIEnv *env, jbyteArray jbytes,
- gss_buffer_t cbytes) {
-
- int len;
+ gss_buffer_t cbytes, jboolean wantCopy)
+{
+ jboolean isCopy;
+ jint len;
void* value;
- if (jbytes != NULL) {
- len = (*env)->GetArrayLength(env, jbytes);
- value = malloc(len);
- if (value == NULL) {
+ cbytes->length = 0;
+ cbytes->value = NULL;
+
+ if (jbytes == NULL ||
+ (len = (*env)->GetArrayLength(env, jbytes)) == 0)
+ return;
+
+ cbytes->length = len;
+
+ if (wantCopy == JNI_FALSE) {
+ cbytes->value = (*env)->GetByteArrayElements(env, jbytes, &isCopy);
+ if (cbytes->value == NULL) {
throwOutOfMemoryError(env, NULL);
- return;
- } else {
- (*env)->GetByteArrayRegion(env, jbytes, 0, len, value);
- if ((*env)->ExceptionCheck(env)) {
- free(value);
- return;
- } else {
- cbytes->length = len;
- cbytes->value = value;
- }
}
- } else {
- cbytes->length = 0;
- cbytes->value = NULL;
+ return;
+ }
+
+ value = malloc(len);
+ if (value == NULL) {
+ throwOutOfMemoryError(env, NULL);
+ return;
}
+
+ (*env)->GetByteArrayRegion(env, jbytes, 0, len, value);
+ if ((*env)->ExceptionCheck(env)) {
+ free(value);
+ return;
+ }
+ cbytes->value = value;
}
/*
- * Utility routine for freeing the bytes malloc'ed
- * in initGSSBuffer() method.
- * NOTE: used in conjunction with initGSSBuffer(...).
+ * Utility routine for freeing the buffer obtained via initGSSBuffer().
+ * If jbytes is null this is a malloced copy.
*/
-void resetGSSBuffer(gss_buffer_t cbytes) {
- if ((cbytes != NULL) && (cbytes != GSS_C_NO_BUFFER)) {
+void resetGSSBuffer(JNIEnv *env, jbyteArray jbytes, gss_buffer_t cbytes)
+{
+ if (cbytes->value == NULL)
+ return;
+ if (jbytes != NULL) {
+ (*env)->ReleaseByteArrayElements(env, jbytes, cbytes->value, JNI_ABORT);
+ } else if (cbytes->length > 0) {
free(cbytes->value);
- cbytes->length = 0;
cbytes->value = NULL;
+ cbytes->length = 0;
}
}
+/*
+ * Utility routine for initializing gss_buffer_t structure
+ * with a String.
+ * NOTE: need to call resetGSSBufferString(...) to free up
+ * the resources.
+ */
+void initGSSBufferString(JNIEnv* env, jstring jstr, gss_buffer_t buf)
+{
+ const char *s;
+
+ buf->length = 0;
+ buf->value = NULL;
+ if (jstr != NULL) {
+ s = (*env)->GetStringUTFChars(env, jstr, NULL);
+ if (s == NULL) {
+ throwOutOfMemoryError(env, NULL);
+ } else {
+ buf->length = strlen(s);
+ buf->value = (char *)s; /* Drop const */
+ }
+ }
+}
+
+/*
+ * Utility routine for unpinning/releasing the String
+ * associated with the specified jstring object.
+ * NOTE: used in conjunction with initGSSBufferString(...).
+ */
+void resetGSSBufferString(JNIEnv *env, jstring jstr, gss_buffer_t buf)
+{
+ if (jstr != NULL && buf->value != NULL)
+ (*env)->ReleaseStringUTFChars(env, jstr, buf->value);
+}
+
/*
* Utility routine for creating a jbyteArray object using
* the byte[] value in specified gss_buffer_t structure.
* NOTE: the specified gss_buffer_t structure is always
* released.
*/
-jbyteArray getJavaBuffer(JNIEnv *env, gss_buffer_t cbytes) {
+jbyteArray getJavaBuffer(JNIEnv *env, gss_buffer_t cbytes, jboolean isToken) {
jbyteArray result = NULL;
- OM_uint32 minor; // don't care, just so it compiles
-
- if (cbytes != NULL) {
- if ((cbytes != GSS_C_NO_BUFFER) && (cbytes->length != 0)) {
- result = (*env)->NewByteArray(env, (int) cbytes->length);
- if (result == NULL) {
- goto finish;
- }
- (*env)->SetByteArrayRegion(env, result, 0, (int) cbytes->length,
+ OM_uint32 dummy;
+
+ /*
+ * Zero length tokens map to NULL outputs, but otherwise to a zero-length
+ * Java byte array.
+ */
+ if (cbytes != GSS_C_NO_BUFFER &&
+ (isToken == JNI_FALSE || cbytes->length > 0)) {
+ jsize len = safe_jsize(cbytes->length);
+
+ if (len >= 0) {
+ result = (*env)->NewByteArray(env, len);
+ }
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len,
cbytes->value);
if ((*env)->ExceptionCheck(env)) {
+ (*env)->DeleteLocalRef(env, result);
result = NULL;
}
}
- finish:
- (*ftab->releaseBuffer)(&minor, cbytes);
- return result;
}
- return NULL;
+ (void) (*ftab->releaseBuffer)(&dummy, cbytes);
+ return result;
}
/*
@@ -724,66 +786,47 @@ jobject getJavaOID(JNIEnv *env, gss_OID cOid) {
if (jbytes == NULL) {
return NULL;
}
- (*env)->SetByteArrayRegion(env, jbytes, 0, 2, (jbyte *) oidHdr);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
+ if (!(*env)->ExceptionCheck(env)) {
+ (*env)->SetByteArrayRegion(env, jbytes, 0, 2, (jbyte *) oidHdr);
}
- (*env)->SetByteArrayRegion(env, jbytes, 2, cLen, (jbyte *) cOid->elements);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
+ if (!(*env)->ExceptionCheck(env)) {
+ (*env)->SetByteArrayRegion(env, jbytes, 2, cLen, (jbyte *) cOid->elements);
}
- result = (*env)->NewObject(env, CLS_Oid, MID_Oid_ctor1, jbytes);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
+ if (!(*env)->ExceptionCheck(env)) {
+ result = (*env)->NewObject(env, CLS_Oid, MID_Oid_ctor1, jbytes);
}
(*env)->DeleteLocalRef(env, jbytes);
return result;
}
/*
- * Utility routine for creating a gss_OID_set structure
- * using the specified gss_OID.
- * NOTE: need to call deleteGSSOIDSet(...) afterwards
- * to release the created gss_OID_set structure.
- */
-gss_OID_set newGSSOIDSet(gss_OID oid) {
- gss_OID_set oidSet;
- OM_uint32 minor; // don't care; just so it compiles
-
- if (oid->length != 6 ||
- memcmp(oid->elements, SPNEGO_BYTES, 6) != 0) {
- (*ftab->createEmptyOidSet)(&minor, &oidSet);
- (*ftab->addOidSetMember)(&minor, oid, &oidSet);
- return oidSet;
- } else {
- // Use all mechs for SPNEGO in order to work with
- // various native GSS impls
- return (ftab->mechs);
- }
-}
-/*
- * Utility routine for releasing a gss_OID_set structure.
- * NOTE: used in conjunction with newGSSOIDSet(...).
+ * Utility routine for filling in a 1-element gss_OID_set structure using the
+ * specified gss_OID (storage owned by caller). However, with SPNEGO we return
+ * a static set containing all the available mechanisms.
*/
-void deleteGSSOIDSet(gss_OID_set oidSet) {
- OM_uint32 minor; /* don't care; just so it compiles */
-
- if ((oidSet != ftab->mechs) &&
- (oidSet != NULL) && (oidSet != GSS_C_NO_OID_SET)) {
- (*ftab->releaseOidSet)(&minor, &oidSet);
- }
+gss_OID_set makeGSSOIDSet(gss_OID_set mechs, gss_OID oid) {
+ if (oid->length != 6 || memcmp(oid->elements, SPNEGO_BYTES, 6) != 0) {
+ mechs->count = 1;
+ mechs->elements = oid;
+ return mechs;
+ }
+ /* Use all mechs for SPNEGO in order to work with various native GSS impls */
+ return (ftab->mechs);
}
/*
* Utility routine for creating a org.ietf.jgss.Oid[]
* using the specified gss_OID_set structure.
*/
jobjectArray getJavaOIDArray(JNIEnv *env, gss_OID_set cOidSet) {
- int numOfOids = 0;
+ jsize numOfOids = 0;
jobjectArray jOidSet;
jobject jOid;
- int i;
+ jsize i;
if (cOidSet != NULL && cOidSet != GSS_C_NO_OID_SET) {
- numOfOids = (int) cOidSet->count;
+ numOfOids = safe_jsize(cOidSet->count);
+ if (numOfOids < 0) {
+ return NULL;
+ }
jOidSet = (*env)->NewObjectArray(env, numOfOids, CLS_Oid, NULL);
if ((*env)->ExceptionCheck(env)) {
return NULL;
diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h
index 04dee5fbc54..5a3a11e08ee 100644
--- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h
+++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h
@@ -36,18 +36,19 @@ extern "C" {
#endif
extern jint getJavaTime(OM_uint32);
extern OM_uint32 getGSSTime(jint);
- extern void checkStatus(JNIEnv *, jobject, OM_uint32, OM_uint32, char*);
+ extern void checkStatus(JNIEnv *, jobject, OM_uint32, OM_uint32, const char *);
extern jint checkTime(OM_uint32);
extern void throwOutOfMemoryError(JNIEnv *, const char*);
- extern void initGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t);
- extern void resetGSSBuffer(gss_buffer_t);
+ extern void initGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t, jboolean);
+ void resetGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t);
+ void initGSSBufferString(JNIEnv *, jstring, gss_buffer_t);
+ void resetGSSBufferString(JNIEnv *, jstring, gss_buffer_t);
extern gss_OID newGSSOID(JNIEnv *, jobject);
extern void deleteGSSOID(gss_OID);
- extern gss_OID_set newGSSOIDSet(gss_OID);
- extern void deleteGSSOIDSet(gss_OID_set);
+ extern gss_OID_set makeGSSOIDSet(gss_OID_set, gss_OID);
- extern jbyteArray getJavaBuffer(JNIEnv *, gss_buffer_t);
+ extern jbyteArray getJavaBuffer(JNIEnv *, gss_buffer_t, jboolean);
extern jstring getJavaString(JNIEnv *, gss_buffer_t);
extern jobject getJavaOID(JNIEnv *, gss_OID);
extern jobjectArray getJavaOIDArray(JNIEnv *, gss_OID_set);
diff --git a/src/java.security.jgss/share/native/libj2gss/gssapi.h b/src/java.security.jgss/share/native/libj2gss/gssapi.h
index a1f1e3456c1..c1838161bf1 100644
--- a/src/java.security.jgss/share/native/libj2gss/gssapi.h
+++ b/src/java.security.jgss/share/native/libj2gss/gssapi.h
@@ -686,6 +686,32 @@ GSS_DLLIMP OM_uint32 gss_canonicalize_name(
# pragma pack(pop)
#endif
+/* Common extension (NOT in RFC2744) */
+OM_uint32
+gss_add_cred_with_password(
+ OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* input_cred_handle */
+ const gss_name_t, /* desired_name */
+ const gss_OID, /* desired_mech */
+ const gss_buffer_t, /* password */
+ gss_cred_usage_t, /* cred_usage */
+ OM_uint32, /* initiator_time_req */
+ OM_uint32, /* acceptor_time_req */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 *, /* initiator_time_rec */
+ OM_uint32 * /* acceptor_time_rec */
+);
+
+/* Common extension for aname2lname (NOT in RFC2744) */
+OM_uint32
+gss_localname(
+ OM_uint32 *, /* minor_status */
+ const gss_name_t, /* name */
+ gss_OID, /* mech_type */
+ gss_buffer_t /* localname */
+);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h b/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h
index 9561112457d..1caae50c679 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h
@@ -41,6 +41,7 @@ void dbgsysBuildLibName(char *, int, const char *, const char *);
void * dbgsysLoadLibrary(const char *, char *err_buf, int err_buflen);
void dbgsysUnloadLibrary(void *);
void * dbgsysFindLibraryEntry(void *, const char *);
+int dbgsysGetLastErrorString(char *, int);
/* Implemented in exec_md.c */
int dbgsysExec(char *cmdLine);
diff --git a/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c b/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c
index 47b6d74dfa7..0b69d62215b 100644
--- a/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c
+++ b/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c
@@ -78,6 +78,28 @@ static void dll_build_name(char* buffer, size_t buflen,
free(paths_copy);
}
+int
+dbgsysGetLastErrorString(char *buf, int len)
+{
+ const char *s = dlerror();
+ size_t n;
+ size_t l = (size_t)len;
+
+ if (len <= 0)
+ return 0;
+
+ *buf = '\0';
+ if (s == NULL)
+ return 0;
+
+ n = strlen(s);
+ if (n >= l)
+ n = l - 1;
+ strncpy(buf, s, n);
+ buf[n] = '\0'; /* not actually needed */
+ return n;
+}
+
/*
* create a string for the JNI native function name by adding the
* appropriate decorations.
diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java
new file mode 100644
index 00000000000..2e81f7fbfc4
--- /dev/null
+++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 2000, 2017-2018 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.security.auth.module;
+
+import java.io.*;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.security.auth.*;
+import javax.security.auth.callback.*;
+import javax.security.auth.login.*;
+import javax.security.auth.spi.*;
+
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+import static sun.security.util.ResourcesMgr.getAuthResourceString;
+
+/**
+ * This LoginModule
authenticates users using the
+ * GSS-API.
+ */
+
+public class GssLoginModule implements LoginModule {
+
+ // From initialize
+ private Subject subject;
+ private CallbackHandler callbackHandler;
+ private Map sharedState;
+ private Map options;
+
+ // Configuration option
+ private boolean debug;
+ private boolean doNotPrompt;
+ private String defName;
+ private String name;
+ private String nametype; // username, hostbased, unspecified
+ private Oid nametypeOid;
+
+ private GSSManager manager;
+ private GSSName gssName;
+ private GSSCredential gssICred;
+ private GSSCredential gssACred;
+
+ private boolean useNative; // sun.security.jgss.native property
+
+ private boolean useFirstPass;
+ private boolean tryFirstPass;
+ private boolean storePass;
+ private boolean clearPass;
+ private boolean initiate;
+ private boolean accept;
+ private boolean tryDefaultCreds;
+ private boolean useDefaultCreds;
+
+ // Module state
+ private boolean succeeded;
+ private boolean commitSucceeded;
+
+ private String password;
+
+ private static final String NAME = "javax.security.auth.login.name";
+ private static final String PWD = "javax.security.auth.login.password";
+
+ private String getWithDefault(String key, String defval) {
+ String value = (String)options.get(key);
+ return value != null ? value : defval;
+ }
+
+ /**
+ * Initialize this LoginModule
.
+ *
+ *
+ * @param subject the Subject
to be authenticated.
+ *
+ *
+ * @param callbackHandler a CallbackHandler
for
+ * communication with the end user (prompting for
+ * usernames and passwords, for example).
+ *
+ *
+ * @param sharedState shared LoginModule
state.
+ *
+ *
+ * @param options options specified in the login
+ * Configuration
for this particular
+ * LoginModule
.
+ */
+ // Unchecked warning from (Map)sharedState is safe
+ // since javax.security.auth.login.LoginContext passes a raw HashMap.
+ // Unchecked warnings from options.get(String) are safe since we are
+ // passing known keys.
+ @SuppressWarnings("unchecked")
+ public void initialize(Subject subject,
+ CallbackHandler callbackHandler,
+ Map sharedState,
+ Map options) {
+
+ this.subject = subject;
+ this.callbackHandler = callbackHandler;
+ this.sharedState = (Map)sharedState;
+ this.options = options;
+
+ /*
+ * When sun.security.jgss.native=false (i.e., not using the system's
+ * native C/ELF/DLL GSS implementation) then there's nothing for this
+ * login module to do. Otherwise we'd get into an infinite recursion
+ * problem due to re-entering GssLoginModule like this:
+ *
+ * Application -> LoginContext -> GssLoginModule -> Krb5 ->
+ * GSSUtil.login -> LoginContext -> GssLoginModule -> ...
+ *
+ * It stands to reason that when sun.security.jgss.native=false the
+ * login modules corresponding to the actual GSS mechanisms coded in
+ * Java are the ones that should be acquiring their corresponding
+ * credentials.
+ *
+ * A policy like "let the application use GSS credentials but not the
+ * raw, underlying Krb5 credentials" when
+ * sun.security.jgss.native=false" could be expressible by adding a
+ * module option to Krb5LoginModule that causes it to add only GSS
+ * credentials to the Subject, not Krb5 credentials.
+ *
+ * (It has never been possible to express such a policy, so we lose
+ * nothing by punting here when sun.security.jgss.native=false.)
+ */
+ useNative = "true".equalsIgnoreCase(
+ System.getProperty("sun.security.jgss.native"));
+ if (!useNative)
+ return;
+
+ manager = GSSManager.getInstance();
+
+ // initialize any configured options
+
+ debug = "true".equalsIgnoreCase((String)options.get("debug"));
+ doNotPrompt =
+ "true".equalsIgnoreCase(getWithDefault("doNotPrompt", "true"));
+ defName = (String)options.get("name");
+ nametype = (String)options.get("nametype");
+
+ if (defName == null)
+ defName = System.getProperty("sun.security.gss.name");
+ if (nametype == null)
+ nametype = System.getProperty("sun.security.gss.nametype");
+ if (nametype == null || nametype.equals("username")) {
+ nametypeOid = GSSName.NT_USER_NAME;
+ } else if (nametype.equals("hostbased")) {
+ nametypeOid = GSSName.NT_HOSTBASED_SERVICE;
+ } else if (!nametype.equals("")) {
+ try {
+ nametypeOid = new Oid(nametype);
+ } catch (GSSException e) {
+ if (debug)
+ System.out.print("Unknown name type OID " + nametype);
+ nametypeOid = null;
+ }
+ } else {
+ nametype = "";
+ nametypeOid = GSSName.NT_USER_NAME;
+ }
+
+ tryFirstPass =
+ "true".equalsIgnoreCase(getWithDefault("tryFirstPass", "true"));
+ useFirstPass =
+ "true".equalsIgnoreCase(
+ getWithDefault("useFirstPass",
+ doNotPrompt ? "true" : "false"));
+ storePass =
+ "true".equalsIgnoreCase((String)options.get("storePass"));
+ clearPass =
+ "true".equalsIgnoreCase((String)options.get("clearPass"));
+ initiate =
+ "true".equalsIgnoreCase((String)options.get("initiate"));
+ accept =
+ "true".equalsIgnoreCase((String)options.get("accept"));
+ tryDefaultCreds =
+ "true".equalsIgnoreCase(getWithDefault("tryDefaultCreds", "true"));
+ useDefaultCreds =
+ "true".equalsIgnoreCase(
+ getWithDefault("useDefaultCreds",
+ doNotPrompt ? "true" : "false"));
+ if (!initiate && !accept)
+ initiate = true;
+ if (debug) {
+ System.out.print("Debug is " + debug
+ + " doNotPrompt " + doNotPrompt
+ + " defName is " + defName
+ + " nametype is " + nametype
+ + " tryFirstPass is " + tryFirstPass
+ + " useFirstPass is " + useFirstPass
+ + " storePass is " + storePass
+ + " clearPass is " + clearPass
+ + " initiate is " + initiate
+ + " accept is " + accept
+ + " tryDefaultCreds is " + tryDefaultCreds
+ + " useDefaultCreds is " + useDefaultCreds + "\n");
+ }
+ }
+
+
+ /**
+ * Authenticate the user
+ *
+ *
+ *
+ * @return true in all cases since this LoginModule
+ * should not be ignored.
+ *
+ *
+ * @exception FailedLoginException if the authentication fails.
+ *
+ *
+ * @exception LoginException if this LoginModule
+ * is unable to perform the authentication.
+ */
+ public boolean login() throws LoginException {
+ succeeded = false;
+
+ /*
+ * See commentary in initialize(). By returning false we cause
+ * LoginContext to ignore this module.
+ */
+ if (!useNative)
+ return false;
+ try {
+ if (tryFirstPass || useFirstPass) {
+ attemptAuthentication(true);
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] " +
+ "authentication succeeded");
+ succeeded = true;
+ cleanState();
+ return true;
+ }
+ } catch (LoginException le) {
+ // authentication failed -- try again below by prompting
+ cleanState();
+ if (debug) {
+ System.out.println("\t\t[GssLoginModule] " +
+ (tryFirstPass ? "tryFirstPass " : "") +
+ "authentication failed with:" +
+ le.getMessage());
+ }
+ if (useFirstPass)
+ throw le;
+ }
+
+ // The first password didn't work or we didn't try it, try prompting
+ try {
+ attemptAuthentication(false);
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] " +
+ "authentication succeeded");
+ succeeded = true;
+ cleanState();
+ return true;
+ } catch (LoginException le2) {
+ cleanState();
+ if (debug) {
+ System.out.println("\t\t[GssLoginModule] " +
+ (tryFirstPass ? "tryFirstPass " : "") +
+ "authentication failed with:" +
+ le2.getMessage());
+ }
+ throw le2;
+ }
+ }
+
+ private void getcreds() throws GSSException {
+ if (initiate) {
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] acquiring" +
+ ((gssName == null) ? " default" : "") +
+ " initiator credentials...");
+ gssICred = manager.createCredential(gssName, password,
+ GSSCredential.DEFAULT_LIFETIME, (Oid[])null,
+ GSSCredential.INITIATE_ONLY);
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] acquired" +
+ " initiator credentials: " + gssName);
+ }
+ if (accept) {
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] acquiring" +
+ ((gssName == null) ? " default" : "") +
+ " acceptor credentials...");
+ gssACred = manager.createCredential(gssName, password,
+ GSSCredential.DEFAULT_LIFETIME, (Oid[])null,
+ GSSCredential.ACCEPT_ONLY);
+ // Default acceptor credentials retain a null name
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] acquired" +
+ " acceptor credentials");
+ }
+ if (gssName == null && gssICred != null)
+ gssName = gssICred.getName();
+ if (gssName == null && gssACred != null)
+ gssName = gssACred.getName();
+ }
+
+ private void attemptAuthentication(boolean getPasswdFromSharedState)
+ throws LoginException {
+
+ // Get a name, maybe
+ if (name == null) {
+ if (useDefaultCreds) {
+ try {
+ getcreds();
+ return;
+ } catch (GSSException e) {
+ throw new LoginException(e.getMessage());
+ }
+ }
+ if (tryDefaultCreds) {
+ try {
+ getcreds();
+ return;
+ } catch (GSSException e) { }
+ }
+
+ promptForName(getPasswdFromSharedState);
+ if (name == null)
+ throw new LoginException ("Unable to determine a GSS name");
+ }
+
+ try {
+ gssName = manager.createName(name, nametypeOid);
+ } catch (GSSException e) {
+ throw new LoginException ("Unable to import GSS name");
+ }
+
+ promptForPass(getPasswdFromSharedState);
+
+ try {
+ getcreds();
+ } catch (GSSException e) {
+ throw new LoginException(e.getMessage());
+ }
+ }
+
+ private void promptForName(boolean getPasswdFromSharedState)
+ throws LoginException {
+ if (getPasswdFromSharedState) {
+ // use the name saved by a module earlier in the stack
+ name = (String)sharedState.get(NAME);
+ if (name == null || name.length() == 0)
+ name = defName;
+ if (debug) {
+ System.out.println("\t\t[GssLoginModule] username from" +
+ " shared state is " + name);
+ }
+ if (name != null && name.length() > 0)
+ return;
+ }
+
+ if (doNotPrompt)
+ return; // name may be null
+
+ if (callbackHandler == null)
+ throw new LoginException("No CallbackHandler "
+ + "available "
+ + "to prompt for authentication "
+ + "information from the user");
+
+ try {
+ String defUsername = System.getProperty("user.name");
+
+ Callback[] callbacks = new Callback[1];
+ MessageFormat form = new MessageFormat(
+ getAuthResourceString(
+ "GSS.name.defName."));
+ Object[] source = {defUsername};
+ callbacks[0] = new NameCallback(form.format(source));
+ callbackHandler.handle(callbacks);
+ name = ((NameCallback)callbacks[0]).getName();
+ if (name != null && name.length() == 0)
+ name = null;
+ if (name == null && defUsername != null &&
+ defUsername.length() != 0)
+ name = defUsername;
+ } catch (java.io.IOException ioe) {
+ throw new LoginException(ioe.getMessage());
+ } catch (UnsupportedCallbackException uce) {
+ throw new LoginException
+ (uce.getMessage()
+ +" not available to garner "
+ +" authentication information "
+ +" from the user");
+ }
+ // name may still be null, which we take to mean "use default
+ // credentials"
+ }
+
+ private void promptForPass(boolean getPasswdFromSharedState)
+ throws LoginException {
+
+ char[] pw;
+
+ if (getPasswdFromSharedState) {
+ // use the password saved by the first module in the stack
+ pw = (char[])sharedState.get(PWD);
+ if (pw == null) {
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] password from" +
+ " shared state is null");
+ throw new LoginException
+ ("Password can not be obtained from sharedstate ");
+ }
+ password = new String(pw);
+ return;
+ }
+ if (doNotPrompt)
+ throw new LoginException("Unable to prompt for password\n");
+
+ if (callbackHandler == null) {
+ throw new LoginException("No CallbackHandler "
+ + "available "
+ + "to garner authentication "
+ + "information from the user");
+ }
+ try {
+ Callback[] callbacks = new Callback[1];
+ MessageFormat form = new MessageFormat(
+ getAuthResourceString(
+ "Kerberos.password.for.username."));
+ Object[] source = {name};
+ callbacks[0] = new PasswordCallback(form.format(source), false);
+ callbackHandler.handle(callbacks);
+ char[] tmpPassword = ((PasswordCallback)
+ callbacks[0]).getPassword();
+ if (tmpPassword == null)
+ throw new LoginException("No password provided");
+ password = new String(tmpPassword);
+ ((PasswordCallback)callbacks[0]).clearPassword();
+
+ // clear tmpPassword
+ for (int i = 0; i < tmpPassword.length; i++)
+ tmpPassword[i] = ' ';
+ } catch (java.io.IOException ioe) {
+ throw new LoginException(ioe.getMessage());
+ } catch (UnsupportedCallbackException uce) {
+ throw new LoginException(uce.getMessage()
+ +" not available to garner "
+ +" authentication information "
+ + "from the user");
+ }
+ }
+
+ /**
+ * This method is called if the LoginContext's
+ * overall authentication succeeded
+ * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
+ * LoginModules succeeded).
+ *
+ * If this LoginModule's own authentication attempt
+ * succeeded (checked by retrieving the private state saved by the
+ * login
method), then this method associates a
+ * GSSName
+ * with the Subject
located in the
+ * LoginModule
. It adds GSS Credentials to the
+ * the Subject's private credentials set. If this LoginModule's own
+ * authentication attempted failed, then this method removes
+ * any state that was originally saved.
+ *
+ *
+ *
+ * @exception LoginException if the commit fails.
+ *
+ *
+ * @return true if this LoginModule's own login and commit
+ * attempts succeeded, or false otherwise.
+ */
+
+ public boolean commit() throws LoginException {
+ if (succeeded == false)
+ return false;
+
+ if (!useNative)
+ return false;
+
+ succeeded = false;
+ if (initiate && (gssICred == null)) {
+ gssName = null;
+ gssICred = null;
+ gssACred = null;
+ throw new LoginException("Null Initiator Credential");
+ }
+ if (accept && (gssACred == null)) {
+ gssName = null;
+ gssICred = null;
+ gssACred = null;
+ throw new LoginException("Null Acceptor Credential");
+ }
+ if (subject.isReadOnly()) {
+ gssName = null;
+ gssICred = null;
+ gssACred = null;
+ throw new LoginException("Subject is Readonly");
+ }
+
+ try {
+ if (initiate && gssName == null)
+ gssName = gssICred.getName();
+ } catch (GSSException e) {}
+ try {
+ if (accept && gssName == null)
+ gssName = gssACred.getName();
+ } catch (GSSException e) {}
+
+ Set privCredSet = subject.getPrivateCredentials();
+ Set princSet = subject.getPrincipals();
+
+ if (gssName != null && !princSet.contains(gssName))
+ princSet.add(gssName);
+ if (gssICred != null && !privCredSet.contains(gssICred))
+ privCredSet.add(gssICred);
+ if (gssACred != null && !privCredSet.contains(gssACred))
+ privCredSet.add(gssACred);
+
+ succeeded = true;
+ commitSucceeded = true;
+ if (debug)
+ System.out.println("\t\t[GssLoginModule] commit Succeeded");
+ return true;
+ }
+
+ /**
+ * This method is called if the LoginContext's
+ * overall authentication failed.
+ * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
+ * LoginModules did not succeed).
+ *
+ * If this LoginModule's own authentication attempt
+ * succeeded (checked by retrieving the private state saved by the
+ * login
and commit
methods),
+ * then this method cleans up any state that was originally
+ * saved.
+ *
+ *
+ *
+ * @exception LoginException if the abort fails.
+ *
+ *
+ * @return false if this LoginModule's own login and/or commit attempts
+ * failed, and true otherwise.
+ */
+
+ public boolean abort() throws LoginException {
+ if (succeeded == false) {
+ return false;
+ } else if (succeeded == true && commitSucceeded == false) {
+ // login succeeded but overall authentication failed
+ succeeded = false;
+ } else {
+ // overall authentication succeeded and commit succeeded,
+ // but someone else's commit failed
+ logout();
+ }
+ return true;
+ }
+
+ /**
+ * Logout the user.
+ *
+ * This method removes the GSSName
and
+ * GSSCredential
added by the commit
method.
+ *
+ *
+ *
+ * @exception LoginException if the logout fails.
+ *
+ *
+ * @return true in all cases since this LoginModule
+ * should not be ignored.
+ */
+ public boolean logout() throws LoginException {
+ /*
+ * See commentary in initialize(). By returning false we cause
+ * LoginContext to ignore this module.
+ */
+ if (!useNative)
+ return false;
+
+ if (subject.isReadOnly())
+ throw new LoginException("Subject is Readonly");
+
+ subject.getPrincipals().remove(gssName);
+ Iterator it = subject.getPrivateCredentials().iterator();
+ while (it.hasNext()) {
+ Object o = it.next();
+ if (o instanceof GSSCredential)
+ it.remove();
+ }
+
+ succeeded = false;
+ commitSucceeded = false;
+ if (debug)
+ System.out.println("\t\t[GSSLoginModule]: logged out Subject");
+ return true;
+ }
+
+ /**
+ * Clean out the state
+ */
+ private void cleanState() {
+
+ // save input as shared state only if
+ // authentication succeeded
+ if (succeeded) {
+ if (storePass &&
+ !sharedState.containsKey(NAME) &&
+ !sharedState.containsKey(PWD)) {
+ sharedState.put(NAME, name);
+ sharedState.put(PWD, password);
+ }
+ } else {
+ // remove temp results for the next try
+ gssName = null;
+ gssICred = null;
+ gssACred = null;
+ }
+ name = null;
+ password = null;
+ if (clearPass) {
+ sharedState.remove(NAME);
+ sharedState.remove(PWD);
+ }
+ }
+}
diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
index 62138dee284..97a951f28f4 100644
--- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
+++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
@@ -27,6 +27,7 @@
package com.sun.security.auth.module;
import java.io.*;
+import java.security.Principal;
import java.text.MessageFormat;
import java.util.*;
@@ -409,7 +410,6 @@ public class Krb5LoginModule implements LoginModule {
private Credentials cred = null;
private PrincipalName principal = null;
- private KerberosPrincipal kerbClientPrinc = null;
private KerberosTicket kerbTicket = null;
private KerberosKey[] kerbKeys = null;
private StringBuffer krb5PrincName = null;
@@ -520,6 +520,16 @@ public void initialize(Subject subject,
*/
public boolean login() throws LoginException {
+ /*
+ * Perhaps we should wrap this in a method that returns false if this
+ * throws and sun.security.jgss.native=true. Or perhaps the wrapper
+ * could see if it can acquire comparable GSS credentials and then
+ * store those in the subject in commit() in that case (and then
+ * GSSUtil/Krb5Util code could be changed to look for those).
+ *
+ * See related commentary in GssLoginModule.
+ */
+
if (refreshKrb5Config) {
try {
if (debug) {
@@ -532,18 +542,30 @@ public boolean login() throws LoginException {
throw le;
}
}
+
+ // -Dsun.security.krb5.principal takes precedence over login module
+ // "principal" option
+ //
+ // XXX This seems misplaced. This is configuration reading, and that
+ // clearly belongs in initialize(). It's not like it's very likely
+ // that this sequence of events takes place anywhere, much less that we
+ // should cater to it:
+ //
+ // lc.initialize();
+ // System.setProperty("sun.security.krb5.principal", ...);
+ // lc.login();
String principalProperty = System.getProperty
("sun.security.krb5.principal");
if (principalProperty != null) {
krb5PrincName = new StringBuffer(principalProperty);
- } else {
- if (princName != null) {
- krb5PrincName = new StringBuffer(princName);
- }
+ } else if (princName != null) {
+ krb5PrincName = new StringBuffer(princName);
}
+ // XXX This really belongs in initialize()
validateConfiguration();
+ // XXX This really belongs in validateConfiguration()
if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
unboundServer = true;
}
@@ -632,51 +654,31 @@ private void attemptAuthentication(boolean getPasswdFromSharedState)
}
try {
+ // This means "from the traditional FILE ccache"
if (useTicketCache) {
- // ticketCacheName == null implies the default cache
if (debug)
- System.out.println("Acquire TGT from Cache");
- cred = Credentials.acquireTGTFromCache
- (principal, ticketCacheName);
-
+ System.out.println("Trying to acquire TGT from Cache");
+ cred = getCredsFromCCache(principal, renewTGT, ticketCacheName);
if (cred != null) {
- if (renewTGT && isOld(cred)) {
- // renew if ticket is old.
- Credentials newCred = renewCredentials(cred);
- if (newCred != null) {
- cred = newCred;
- }
- }
+ if (principal == null)
+ principal = cred.getClient();
if (!isCurrent(cred)) {
- // credentials have expired
cred = null;
if (debug)
- System.out.println("Credentials are" +
- " no longer valid");
- }
- }
-
- if (cred != null) {
- // get the principal name from the ticket cache
- if (principal == null) {
- principal = cred.getClient();
- }
- }
- if (debug) {
- System.out.println("Principal is " + principal);
- if (cred == null) {
- System.out.println
- ("null credentials from Ticket Cache");
+ System.out.println("Found expired cached " +
+ "credentials for " + principal);
+ } else if (debug) {
+ System.out.println("Found cached credentials for "
+ + principal);
}
+ } else if (debug) {
+ System.out.println("Could not find cached credentials");
}
}
- // cred = null indicates that we didn't get the creds
- // from the cache or useTicketCache was false
-
if (cred == null) {
- // We need the principal name whether we use keytab
- // or AS Exchange
+ // !useTicketCache || credentials not found || expired
+
if (principal == null) {
promptForName(getPasswdFromSharedState);
principal = new PrincipalName
@@ -684,49 +686,19 @@ private void attemptAuthentication(boolean getPasswdFromSharedState)
PrincipalName.KRB_NT_PRINCIPAL);
}
- /*
- * Before dynamic KeyTab support (6894072), here we check if
- * the keytab contains keys for the principal. If no, keytab
- * will not be used and password is prompted for.
- *
- * After 6894072, we normally don't check it, and expect the
- * keys can be populated until a real connection is made. The
- * check is still done when isInitiator == true, where the keys
- * will be used right now.
- *
- * Probably tricky relations:
- *
- * useKeyTab is config flag, but when it's true but the ktab
- * does not contains keys for principal, we would use password
- * and keep the flag unchanged (for reuse?). In this method,
- * we use (ktab != null) to check whether keytab is used.
- * After this method (and when storeKey == true), we use
- * (encKeys == null) to check.
- */
if (useKeyTab) {
- if (!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
+ ktab = getKtab(keyTabName, principal, unboundServer);
+ if (isInitiator &&
+ Krb5Util.keysFromJavaxKeyTab(ktab, principal).length
== 0) {
- ktab = null;
- if (debug) {
- System.out.println
- ("Key for the principal " +
- principal +
- " not available in " +
- ((keyTabName == null) ?
- "default key tab" : keyTabName));
- }
+ ktab = null;
+ if (debug) {
+ System.out.println
+ ("Key for the principal " +
+ principal +
+ " not available in " +
+ ((keyTabName == null) ?
+ "default key tab" : keyTabName));
}
}
}
@@ -736,16 +708,21 @@ private void attemptAuthentication(boolean getPasswdFromSharedState)
if (ktab == null) {
promptForPass(getPasswdFromSharedState);
builder = new KrbAsReqBuilder(principal, password);
- if (isInitiator) {
- // XXX Even if isInitiator=false, it might be
- // better to do an AS-REQ so that keys can be
- // updated with PA info
+ if (isInitiator || storeKey) {
+ // Even if isInitiator=false, if we want to accept with
+ // long-term key derived from the password, then in
+ // principle (and decidedly for new enctypes) we need
+ // to do an AS exchange to get the PA etype info for
+ // the derivation. (For older enctypes this is bad, as
+ // we will attempt to talk the a KDC we might not be
+ // able to reach, then timeout... If this is not
+ // desired, the user can reconfigure the module.)
cred = builder.action().getCreds();
- }
- if (storeKey) {
- encKeys = builder.getKeys(isInitiator);
- // When encKeys is empty, the login actually fails.
- // For compatibility, exception is thrown in commit().
+ if (storeKey) {
+ encKeys = builder.getKeys(isInitiator);
+ // When encKeys is empty, the login actually fails.
+ // For compatibility, exception is thrown in commit().
+ }
}
} else {
builder = new KrbAsReqBuilder(principal, ktab);
@@ -762,15 +739,15 @@ private void attemptAuthentication(boolean getPasswdFromSharedState)
System.out.println("Will use keytab");
} else if (storeKey) {
for (int i = 0; i < encKeys.length; i++) {
+ // Printing keys here just because debug is a bad
+ // idea: stdout might be a file that gets sent to
+ // loggers, and... yeah, no.
System.out.println("EncryptionKey: keyType=" +
- encKeys[i].getEType() +
- " keyBytes (hex dump)=" +
- hd.encodeBuffer(encKeys[i].getBytes()));
+ encKeys[i].getEType());
}
}
}
- // we should hava a non-null cred
if (isInitiator && (cred == null)) {
throw new LoginException
("TGT Can not be obtained from the KDC ");
@@ -961,6 +938,29 @@ private void validateConfiguration() throws LoginException {
}
}
+ private Credentials getCredsFromCCache(PrincipalName princ, boolean renewTGT, String ccacheName)
+ throws KrbException, IOException {
+ // ticketCacheName == null implies the default cache
+ // princ == null implies the cache's default princ(XXX?)
+ Credentials creds = Credentials.acquireTGTFromCache(princ, ccacheName);
+ if (creds == null)
+ return null;
+ if (renewTGT && timeToRenew(creds))
+ creds = possiblyRenewCreds(creds);
+ // It's the caller's job to deal with expired creds
+ return creds;
+ }
+
+ private KeyTab getKtab(String keyTabName, PrincipalName principal,
+ boolean unboundServer)
+ {
+ KerberosPrincipal kp = unboundServer ? null :
+ new KerberosPrincipal(principal.getName());;
+ return (keyTabName == null)
+ ? KeyTab.getInstance(kp) // default keytab
+ : KeyTab.getInstance(kp, new File(keyTabName));
+ }
+
private static boolean isCurrent(Credentials creds)
{
Date endTime = creds.getEndTime();
@@ -970,47 +970,60 @@ private static boolean isCurrent(Credentials creds)
return true;
}
- private static boolean isOld(Credentials creds)
+ private static boolean timeToRenew(Credentials creds)
{
+ if (!creds.isRenewable())
+ return false;
+
Date endTime = creds.getEndTime();
- if (endTime != null) {
- Date authTime = creds.getAuthTime();
- long now = System.currentTimeMillis();
- if (authTime != null) {
- // pass the mid between auth and end
- return now - authTime.getTime() > endTime.getTime() - now;
- } else {
- // will expire in less than 2 hours
- return now <= endTime.getTime() - 1000*3600*2L;
- }
- }
- return false;
+
+ // endtime is required, so it can't be null. We only have to check
+ // because it's Java and we could express that this can't be null.
+ // Strictly speaking we can leave out this test.
+ if (endTime == null)
+ return false;
+
+ // There's no point trying to renew a TGT we will be able to renew but
+ // with no additional lifetime. And there's no point trying to renew
+ // non-renewable tickets.
+ Date renewTill = creds.getRenewTill();
+ if (renewTill == null || renewTill.getTime() <= endTime.getTime())
+ return false;
+
+ // NOTE WELL: We must use the *start* time, not the auth time, because
+ // the auth time refers to when the AS exchange was done,
+ // not to when the TGS exchange was done. For very
+ // long-lived TGTs using authTime here means renewing all
+ // the time!
+ Date startTime = creds.getStartTime();
+ long now = System.currentTimeMillis();
+ // Start time can be null
+ if (startTime != null)
+ // past the mid between start and end
+ return now - startTime.getTime() > endTime.getTime() - now;
+ // will it expire in less than 2 hours?
+ return now <= endTime.getTime() - 1000*3600*2L;
}
- private Credentials renewCredentials(Credentials creds)
+ private Credentials possiblyRenewCreds(Credentials creds)
+ throws KrbException, IOException
{
- Credentials lcreds;
+ if (!creds.isRenewable())
+ return creds;
+
+ if (System.currentTimeMillis() > cred.getRenewTill().getTime())
+ return creds;
+
try {
- if (!creds.isRenewable())
- throw new RefreshFailedException("This ticket" +
- " is not renewable");
- if (creds.getRenewTill() == null) {
- // Renewable ticket without renew-till. Illegal and ignored.
- return creds;
- }
- if (System.currentTimeMillis() > cred.getRenewTill().getTime())
- throw new RefreshFailedException("This ticket is past "
- + "its last renewal time.");
- lcreds = creds.renew();
+ creds = creds.renew();
if (debug)
System.out.println("Renewed Kerberos Ticket");
} catch (Exception e) {
- lcreds = null;
if (debug)
System.out.println("Ticket could not be renewed : "
+ e.getMessage());
}
- return lcreds;
+ return creds;
}
/**
@@ -1036,107 +1049,108 @@ private Credentials renewCredentials(Credentials creds)
*/
public boolean commit() throws LoginException {
-
/*
* Let us add the Krb5 Creds to the Subject's
* private credentials. The credentials are of type
* KerberosKey or KerberosTicket
*/
if (succeeded == false) {
+ cleanKerberosCred();
return false;
- } else {
+ }
- if (isInitiator && (cred == null)) {
- succeeded = false;
- throw new LoginException("Null Client Credential");
- }
+ if (isInitiator && (cred == null)) {
+ cleanKerberosCred();
+ succeeded = false;
+ throw new LoginException("Null Client Credential");
+ }
- if (subject.isReadOnly()) {
- cleanKerberosCred();
- throw new LoginException("Subject is Readonly");
- }
+ if (subject.isReadOnly()) {
+ cleanKerberosCred();
+ succeeded = false;
+ throw new LoginException("Subject is Readonly");
+ }
- /*
- * Add the Principal (authenticated identity)
- * to the Subject's principal set and
- * add the credentials (TGT or Service key) to the
- * Subject's private credentials
- */
+ try {
+ setupSubject(subject, unboundServer ? null : principal, ktab,
+ isInitiator ? Krb5Util.credsToTicket(cred) : null,
+ storeKey && encKeys != null ? encKeys : null);
+ if (debug)
+ System.out.println("Added Kerberos credentials to subject");
+ return true;
+ } catch (Exception e) {
+ cleanKerberosCred();
+ succeeded = false;
+ throw new LoginException(e.getMessage());
+ }
+ }
- Set privCredSet = subject.getPrivateCredentials();
- Set princSet = subject.getPrincipals();
- kerbClientPrinc = new KerberosPrincipal(principal.getName());
+ /**
+ * Store the given Kerberos crendentials in the given subject.
+ *
+ * @param subject the {@code Subject} to store the credentials into
+ *
+ * @param principal the {@code PrincipalName} for the credentials; use null to refer to all principals in the keytab
+ *
+ * @param ktab a {@code KeyTab} keytab to use for acting as a service (may be null)
+ *
+ * @param kerbTicket the TGT for the principal (if acting as a client)
+ *
+ * @param encKeys long-term secret keys for the principal (if acting as a server with the keys derived from a password)
+ *
+ */
+ private static void setupSubject(Subject subject, PrincipalName principal,
+ KeyTab ktab, KerberosTicket kerbTicket, EncryptionKey[] encKeys)
+ throws LoginException {
- // create Kerberos Ticket
- if (isInitiator) {
- kerbTicket = Krb5Util.credsToTicket(cred);
- }
+ /*
+ * Add the Principal (authenticated identity)
+ * to the Subject's principal set and
+ * add the credentials (TGT or Service key) to the
+ * Subject's private credentials
+ */
- if (storeKey && encKeys != null) {
- if (encKeys.length == 0) {
- succeeded = false;
- throw new LoginException("Null Server Key ");
- }
+ if (kerbTicket == null && encKeys == null && ktab == null)
+ throw new LoginException("No Kerberos credentials provided to " +
+ "store in subject");
- kerbKeys = new KerberosKey[encKeys.length];
- for (int i = 0; i < encKeys.length; i ++) {
- Integer temp = encKeys[i].getKeyVersionNumber();
- kerbKeys[i] = new KerberosKey(kerbClientPrinc,
- encKeys[i].getBytes(),
- encKeys[i].getEType(),
- (temp == null?
- 0: temp.intValue()));
- }
+ Set privCredSet = subject.getPrivateCredentials();
+ Set princSet = subject.getPrincipals();
- }
- // Let us add the kerbClientPrinc,kerbTicket and KeyTab/KerbKey (if
- // storeKey is true)
+ KerberosPrincipal princ = null;
+ if (principal != null) {
+ princ = new KerberosPrincipal(principal.getName());
+ if (!princSet.contains(princ))
+ princSet.add(princ);
+ }
- // We won't add "*" as a KerberosPrincipal
- if (!unboundServer &&
- !princSet.contains(kerbClientPrinc)) {
- princSet.add(kerbClientPrinc);
- }
+ if (kerbTicket != null && !privCredSet.contains(kerbTicket))
+ privCredSet.add(kerbTicket);
- // add the TGT
- if (kerbTicket != null) {
- if (!privCredSet.contains(kerbTicket))
- privCredSet.add(kerbTicket);
- }
+ if (ktab != null && !privCredSet.contains(ktab))
+ privCredSet.add(ktab);
- if (storeKey) {
- if (encKeys == null) {
- if (ktab != null) {
- if (!privCredSet.contains(ktab)) {
- privCredSet.add(ktab);
- }
- } else {
- succeeded = false;
- throw new LoginException("No key to store");
- }
- } else {
- for (int i = 0; i < kerbKeys.length; i ++) {
- if (!privCredSet.contains(kerbKeys[i])) {
- privCredSet.add(kerbKeys[i]);
- }
- encKeys[i].destroy();
- encKeys[i] = null;
- if (debug) {
- System.out.println("Added server's key"
- + kerbKeys[i]);
- System.out.println("\t\t[Krb5LoginModule] " +
- "added Krb5Principal " +
- kerbClientPrinc.toString()
- + " to Subject");
- }
- }
- }
- }
+ if (encKeys == null)
+ return;
+
+ if (encKeys.length == 0)
+ throw new LoginException("Cannot store empty long-term " +
+ "keyset in Subject");
+
+ if (princ == null)
+ throw new LoginException("Cannot store Kerberos long-term keys " +
+ "for wild-card principal in Subject");
+
+ for (int i = 0; i < encKeys.length; i ++) {
+ Integer temp = encKeys[i].getKeyVersionNumber();
+ KerberosKey kerbKey = new KerberosKey(princ,
+ encKeys[i].getBytes(),
+ encKeys[i].getEType(),
+ (temp == null?
+ 0: temp.intValue()));
+ if (!privCredSet.contains(kerbKey))
+ privCredSet.add(kerbKey);
}
- commitSucceeded = true;
- if (debug)
- System.out.println("Commit Succeeded \n");
- return true;
}
/**
@@ -1194,8 +1208,13 @@ public boolean logout() throws LoginException {
throw new LoginException("Subject is Readonly");
}
- subject.getPrincipals().remove(kerbClientPrinc);
- // Let us remove all Kerberos credentials stored in the Subject
+ Iterator itp = subject.getPrincipals().iterator();
+ while (itp.hasNext()) {
+ Object o = itp.next();
+ if (o instanceof KerberosPrincipal)
+ itp.remove();
+ }
+
Iterator it = subject.getPrivateCredentials().iterator();
while (it.hasNext()) {
Object o = it.next();
@@ -1234,9 +1253,12 @@ private void cleanKerberosCred() throws LoginException {
throw new LoginException
("Destroy Failed on Kerberos Private Credentials");
}
+ for (int i = 0; i < kerbKeys.length; i++) {
+ encKeys[i].destroy();
+ encKeys[i] = null;
+ }
kerbTicket = null;
kerbKeys = null;
- kerbClientPrinc = null;
}
/**
diff --git a/test/jdk/sun/security/jgss/DefaultGssConfig.java b/test/jdk/sun/security/jgss/DefaultGssConfig.java
index e80a2aaf365..de8bf0b8697 100644
--- a/test/jdk/sun/security/jgss/DefaultGssConfig.java
+++ b/test/jdk/sun/security/jgss/DefaultGssConfig.java
@@ -58,6 +58,10 @@ public static void main(String[] argv) throws Exception {
Configuration.getConfiguration();
// 3. Make sure there're default entries for GSS krb5 client/server
+ //
+ // FIXME Why be so Kerberos-specific? This is wrong. Instead we could
+ // use a command-line argument to deal with a specific (or all)
+ // mechanisms.
LoginConfigImpl lc = new LoginConfigImpl(GSSCaller.CALLER_INITIATE, GSSUtil.GSS_KRB5_MECH_OID);
if (lc.getAppConfigurationEntry("").length == 0) {
throw new Exception("No default config for GSS krb5 client");