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");