# HG changeset patch # User weijun # Date 1259283088 -28800 # Node ID 4c792c19266e321290fd771e670cccf302a1091a # Parent 365eb44493197ba6846a4fac9c60a78fe147ef3b 6853328: Support OK-AS-DELEGATE flag Reviewed-by: valeriep diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/com/sun/security/jgss/ExtendedGSSContext.java --- a/jdk/src/share/classes/com/sun/security/jgss/ExtendedGSSContext.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/com/sun/security/jgss/ExtendedGSSContext.java Fri Nov 27 08:51:28 2009 +0800 @@ -99,4 +99,58 @@ */ public Object inquireSecContext(InquireType type) throws GSSException; + + /** + * Requests that the delegation policy be respected. When a true value is + * requested, the underlying context would use the delegation policy + * defined by the environment as a hint to determine whether credentials + * delegation should be performed. This request can only be made on the + * context initiator's side and it has to be done prior to the first + * call to initSecContext. + *

+ * When this flag is false, delegation will only be tried when the + * {@link GSSContext#requestCredDeleg(boolean) credentials delegation flag} + * is true. + *

+ * When this flag is true but the + * {@link GSSContext#requestCredDeleg(boolean) credentials delegation flag} + * is false, delegation will be only tried if the delegation policy permits + * delegation. + *

+ * When both this flag and the + * {@link GSSContext#requestCredDeleg(boolean) credentials delegation flag} + * are true, delegation will be always tried. However, if the delegation + * policy does not permit delegation, the value of + * {@link #getDelegPolicyState} will be false, even + * if delegation is performed successfully. + *

+ * In any case, if the delegation is not successful, the value returned + * by {@link GSSContext#getCredDelegState()} is false, and the value + * returned by {@link #getDelegPolicyState()} is also false. + *

+ * Not all mechanisms support delegation policy. Therefore, the + * application should check to see if the request was honored with the + * {@link #getDelegPolicyState() getDelegPolicyState} method. When + * delegation policy is not supported, requestDelegPolicy + * should return silently without throwing an exception. + *

+ * Note: for the Kerberos 5 mechanism, the delegation policy is expressed + * through the OK-AS-DELEGATE flag in the service ticket. When it's true, + * the KDC permits delegation to the target server. In a cross-realm + * environment, in order for delegation be permitted, all cross-realm TGTs + * on the authentication path must also have the OK-AS-DELAGATE flags set. + * @param state true if the policy should be respected + * @throws GSSException containing the following + * major error codes: + * {@link GSSException#FAILURE GSSException.FAILURE} + */ + public void requestDelegPolicy(boolean state) throws GSSException; + + /** + * Returns the delegation policy response. Called after a security context + * is established. This method can be only called on the initiator's side. + * See {@link ExtendedGSSContext#requestDelegPolicy}. + * @return the delegation policy response + */ + public boolean getDelegPolicyState(); } diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/org/ietf/jgss/GSSContext.java --- a/jdk/src/share/classes/org/ietf/jgss/GSSContext.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/org/ietf/jgss/GSSContext.java Fri Nov 27 08:51:28 2009 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2009 Sun Microsystems, Inc. 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 @@ -678,7 +678,7 @@ * are not definitive then the method will attempt to treat all * available bytes as part of the token.

* - * Other than the possible blocking behaviour described above, this + * Other than the possible blocking behavior described above, this * method is equivalent to the byte array based {@link #unwrap(byte[], * int, int, MessageProp) unwrap} method.

* @@ -826,7 +826,7 @@ * are not definitive then the method will attempt to treat all * available bytes as part of the token.

* - * Other than the possible blocking behaviour described above, this + * Other than the possible blocking behavior described above, this * method is equivalent to the byte array based {@link #verifyMIC(byte[], * int, int, byte[], int, int, MessageProp) verifyMIC} method.

* @@ -917,7 +917,7 @@ * getMutualAuthState} method.

* * @param state a boolean value indicating whether mutual - * authentication shouls be used or not. + * authentication should be used or not. * @see #getMutualAuthState() * * @throws GSSException containing the following @@ -928,7 +928,7 @@ /** * Requests that replay detection be enabled for the - * per-message security services after context establishemnt. This + * per-message security services after context establishment. This * request can only be made on the context initiator's side and it has * to be done prior to the first call to * initSecContext. During context establishment replay @@ -958,7 +958,7 @@ /** * Requests that sequence checking be enabled for the - * per-message security services after context establishemnt. This + * per-message security services after context establishment. This * request can only be made on the context initiator's side and it has * to be done prior to the first call to * initSecContext. During context establishment sequence diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/net/www/protocol/http/spnego/NegotiatorImpl.java --- a/jdk/src/share/classes/sun/net/www/protocol/http/spnego/NegotiatorImpl.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/net/www/protocol/http/spnego/NegotiatorImpl.java Fri Nov 27 08:51:28 2009 +0800 @@ -25,6 +25,7 @@ package sun.net.www.protocol.http.spnego; +import com.sun.security.jgss.ExtendedGSSContext; import java.io.IOException; import org.ietf.jgss.GSSContext; @@ -100,15 +101,10 @@ null, GSSContext.DEFAULT_LIFETIME); - // In order to support credential delegation in HTTP/SPNEGO, - // we always request it before initSecContext. The current - // implementation will check the OK-AS-DELEGATE flag inside - // the service ticket of the web server, and only enable - // delegation when this flag is set. This check is only - // performed when the GSS caller is CALLER_HTTP_NEGOTIATE, - // so all other normal GSS-API calls are not affected. - - context.requestCredDeleg(true); + // Always respect delegation policy in HTTP/SPNEGO. + if (context instanceof ExtendedGSSContext) { + ((ExtendedGSSContext)context).requestDelegPolicy(true); + } oneToken = context.initSecContext(new byte[0], 0, 0); } diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/jgss/GSSContextImpl.java --- a/jdk/src/share/classes/sun/security/jgss/GSSContextImpl.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/jgss/GSSContextImpl.java Fri Nov 27 08:51:28 2009 +0800 @@ -89,7 +89,8 @@ */ class GSSContextImpl implements ExtendedGSSContext { - private GSSManagerImpl gssManager = null; + private final GSSManagerImpl gssManager; + private final boolean initiator; // private flags for the context state private static final int PRE_INIT = 1; @@ -99,14 +100,12 @@ // instance variables private int currentState = PRE_INIT; - private boolean initiator; private GSSContextSpi mechCtxt = null; private Oid mechOid = null; private ObjectIdentifier objId = null; private GSSCredentialImpl myCred = null; - private GSSCredentialImpl delegCred = null; private GSSNameImpl srcName = null; private GSSNameImpl targName = null; @@ -121,6 +120,7 @@ private boolean reqSequenceDetState = true; private boolean reqCredDelegState = false; private boolean reqAnonState = false; + private boolean reqDelegPolicyState = false; /** * Creates a GSSContextImp on the context initiator's side. @@ -221,6 +221,7 @@ mechCtxt.requestSequenceDet(reqSequenceDetState); mechCtxt.requestAnonymity(reqAnonState); mechCtxt.setChannelBinding(channelBindings); + mechCtxt.requestDelegPolicy(reqDelegPolicyState); objId = new ObjectIdentifier(mechOid.toString()); @@ -465,42 +466,42 @@ } public void requestMutualAuth(boolean state) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqMutualAuthState = state; } public void requestReplayDet(boolean state) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqReplayDetState = state; } public void requestSequenceDet(boolean state) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqSequenceDetState = state; } public void requestCredDeleg(boolean state) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqCredDelegState = state; } public void requestAnonymity(boolean state) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqAnonState = state; } public void requestConf(boolean state) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqConfState = state; } public void requestInteg(boolean state) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqIntegState = state; } public void requestLifetime(int lifetime) throws GSSException { - if (mechCtxt == null) + if (mechCtxt == null && initiator) reqLifetime = lifetime; } @@ -630,6 +631,8 @@ targName = null; } + // ExtendedGSSContext methods: + @Override public Object inquireSecContext(InquireType type) throws GSSException { SecurityManager security = System.getSecurityManager(); @@ -641,4 +644,18 @@ } return mechCtxt.inquireSecContext(type); } + + @Override + public void requestDelegPolicy(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqDelegPolicyState = state; + } + + @Override + public boolean getDelegPolicyState() { + if (mechCtxt != null) + return mechCtxt.getDelegPolicyState(); + else + return reqDelegPolicyState; + } } diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/jgss/krb5/InitialToken.java --- a/jdk/src/share/classes/sun/security/jgss/krb5/InitialToken.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/jgss/krb5/InitialToken.java Fri Nov 27 08:51:28 2009 +0800 @@ -85,34 +85,41 @@ int size = CHECKSUM_LENGTH_SIZE + CHECKSUM_BINDINGS_SIZE + CHECKSUM_FLAGS_SIZE; - if (context.getCredDelegState()) { - if (context.getCaller() instanceof HttpCaller && - !serviceTicket.getFlags()[Krb5.TKT_OPTS_DELEGATE]) { - // When the caller is HTTP/SPNEGO and OK-AS-DELEGATE - // is not present in the service ticket, delegation - // is disabled. - context.setCredDelegState(false); - } else if (!tgt.isForwardable()) { - // XXX log this resetting of delegation state - context.setCredDelegState(false); + if (!tgt.isForwardable()) { + context.setCredDelegState(false); + context.setDelegPolicyState(false); + } else if (context.getCredDelegState()) { + if (context.getDelegPolicyState()) { + if (!serviceTicket.checkDelegate()) { + // delegation not permitted by server policy, mark it + context.setDelegPolicyState(false); + } + } + } else if (context.getDelegPolicyState()) { + if (serviceTicket.checkDelegate()) { + context.setCredDelegState(true); } else { - KrbCred krbCred = null; - CipherHelper cipherHelper = - context.getCipherHelper(serviceTicket.getSessionKey()); - if (useNullKey(cipherHelper)) { - krbCred = new KrbCred(tgt, serviceTicket, - EncryptionKey.NULL_KEY); - } else { - krbCred = new KrbCred(tgt, serviceTicket, - serviceTicket.getSessionKey()); - } - krbCredMessage = krbCred.getMessage(); - size += CHECKSUM_DELEG_OPT_SIZE + - CHECKSUM_DELEG_LGTH_SIZE + - krbCredMessage.length; + context.setDelegPolicyState(false); } } + if (context.getCredDelegState()) { + KrbCred krbCred = null; + CipherHelper cipherHelper = + context.getCipherHelper(serviceTicket.getSessionKey()); + if (useNullKey(cipherHelper)) { + krbCred = new KrbCred(tgt, serviceTicket, + EncryptionKey.NULL_KEY); + } else { + krbCred = new KrbCred(tgt, serviceTicket, + serviceTicket.getSessionKey()); + } + krbCredMessage = krbCred.getMessage(); + size += CHECKSUM_DELEG_OPT_SIZE + + CHECKSUM_DELEG_LGTH_SIZE + + krbCredMessage.length; + } + checksumBytes = new byte[size]; checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[0]; @@ -296,6 +303,7 @@ return delegCreds; } + // Only called by acceptor public void setContextFlags(Krb5Context context) { // default for cred delegation is false if ((flags & CHECKSUM_DELEG_FLAG) > 0) diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/jgss/krb5/Krb5Context.java --- a/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Context.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5Context.java Fri Nov 27 08:51:28 2009 +0800 @@ -78,6 +78,7 @@ private boolean sequenceDetState = true; private boolean confState = true; private boolean integState = true; + private boolean delegPolicyState = false; private int mySeqNumber; private int peerSeqNumber; @@ -299,6 +300,21 @@ return sequenceDetState || replayDetState; } + /** + * Requests that the deleg policy be respected. + */ + public final void requestDelegPolicy(boolean value) { + if (state == STATE_NEW && isInitiator()) + delegPolicyState = value; + } + + /** + * Is deleg policy respected? + */ + public final boolean getDelegPolicyState() { + return delegPolicyState; + } + /* * Anonymity is a little different in that after an application * requests anonymity it will want to know whether the mechanism @@ -422,6 +438,10 @@ integState = state; } + final void setDelegPolicyState(boolean state) { + delegPolicyState = state; + } + /** * Sets the channel bindings to be used during context * establishment. diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java --- a/jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java Fri Nov 27 08:51:28 2009 +0800 @@ -124,6 +124,8 @@ public void requestInteg(boolean state) throws GSSException; + public void requestDelegPolicy(boolean state) throws GSSException; + public void setChannelBinding(ChannelBinding cb) throws GSSException; public boolean getCredDelegState(); @@ -136,6 +138,8 @@ public boolean getAnonymityState(); + public boolean getDelegPolicyState(); + public boolean isTransferable() throws GSSException; public boolean isProtReady(); diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java --- a/jdk/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java Fri Nov 27 08:51:28 2009 +0800 @@ -63,6 +63,7 @@ private boolean sequenceDetState = true; private boolean confState = true; private boolean integState = true; + private boolean delegPolicyState = false; private GSSNameSpi peerName = null; private GSSNameSpi myName = null; @@ -154,6 +155,14 @@ } /** + * Requests that deleg policy be respected. + */ + public final void requestDelegPolicy(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + delegPolicyState = value; + } + + /** * Is integrity available? */ public final boolean getIntegState() { @@ -161,6 +170,19 @@ } /** + * Is deleg policy respected? + */ + public final boolean getDelegPolicyState() { + if (isInitiator() && mechContext != null && + mechContext instanceof ExtendedGSSContext && + (state == STATE_IN_PROCESS || state == STATE_DONE)) { + return ((ExtendedGSSContext)mechContext).getDelegPolicyState(); + } else { + return delegPolicyState; + } + } + + /** * Requests that credential delegation be done during context * establishment. */ @@ -173,7 +195,7 @@ * Is credential delegation enabled? */ public final boolean getCredDelegState() { - if (mechContext != null && + if (isInitiator() && mechContext != null && (state == STATE_IN_PROCESS || state == STATE_DONE)) { return mechContext.getCredDelegState(); } else { @@ -201,30 +223,6 @@ return mutualAuthState; } - final void setCredDelegState(boolean state) { - credDelegState = state; - } - - final void setMutualAuthState(boolean state) { - mutualAuthState = state; - } - - final void setReplayDetState(boolean state) { - replayDetState = state; - } - - final void setSequenceDetState(boolean state) { - sequenceDetState = state; - } - - final void setConfState(boolean state) { - confState = state; - } - - final void setIntegState(boolean state) { - integState = state; - } - /** * Returns the mechanism oid. * @@ -653,6 +651,10 @@ throw gssException; } + if (state == STATE_DONE) { + // now set the context flags for acceptor + setContextFlags(); + } return retVal; } @@ -703,28 +705,31 @@ return out; } + // Only called on acceptor side. On the initiator side, most flags + // are already set at request. For those that might get chanegd, + // state from mech below is used. private void setContextFlags() { if (mechContext != null) { // default for cred delegation is false if (mechContext.getCredDelegState()) { - setCredDelegState(true); + credDelegState = true; } // default for the following are true if (!mechContext.getMutualAuthState()) { - setMutualAuthState(false); + mutualAuthState = false; } if (!mechContext.getReplayDetState()) { - setReplayDetState(false); + replayDetState = false; } if (!mechContext.getSequenceDetState()) { - setSequenceDetState(false); + sequenceDetState = false; } if (!mechContext.getIntegState()) { - setIntegState(false); + integState = false; } if (!mechContext.getConfState()) { - setConfState(false); + confState = false; } } } @@ -837,6 +842,10 @@ mechContext.requestMutualAuth(mutualAuthState); mechContext.requestReplayDet(replayDetState); mechContext.requestSequenceDet(sequenceDetState); + if (mechContext instanceof ExtendedGSSContext) { + ((ExtendedGSSContext)mechContext).requestDelegPolicy( + delegPolicyState); + } } // pass token @@ -1202,5 +1211,5 @@ "inquireSecContext not supported by underlying mech."); } } +} -} diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java --- a/jdk/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java Fri Nov 27 08:51:28 2009 +0800 @@ -549,6 +549,9 @@ public void requestInteg(boolean state) throws GSSException { changeFlags(GSS_C_INTEG_FLAG, state); } + public void requestDelegPolicy(boolean state) throws GSSException { + // Not supported, ignore + } public void requestLifetime(int lifetime) throws GSSException { if (isInitiator && pContext == 0) { this.lifetime = lifetime; @@ -590,6 +593,9 @@ public boolean getIntegState() { return checkFlags(GSS_C_INTEG_FLAG); } + public boolean getDelegPolicyState() { + return false; + } public int getLifetime() { return cStub.getContextTime(pContext); } diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/krb5/Credentials.java --- a/jdk/src/share/classes/sun/security/krb5/Credentials.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/krb5/Credentials.java Fri Nov 27 08:51:28 2009 +0800 @@ -234,7 +234,19 @@ * @return true if OK-AS_DELEGATE flag is set, otherwise, return false. */ public boolean checkDelegate() { - return (flags.get(Krb5.TKT_OPTS_DELEGATE)); + return flags.get(Krb5.TKT_OPTS_DELEGATE); + } + + /** + * Reset TKT_OPTS_DELEGATE to false, called at credentials acquirement + * when one of the cross-realm TGTs does not have the OK-AS-DELEGATE + * flag set. This info must be preservable and restorable through + * the Krb5Util.credsToTicket/ticketToCreds() methods so that even if + * the service ticket is cached it still remembers the cross-realm + * authentication result. + */ + public void resetDelegate() { + flags.set(Krb5.TKT_OPTS_DELEGATE, false); } public Credentials renew() throws KrbException, IOException { diff -r 365eb4449319 -r 4c792c19266e jdk/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java --- a/jdk/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java Fri Nov 27 08:51:28 2009 +0800 @@ -1,5 +1,5 @@ /* - * Portions Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * Portions Copyright 2001-2009 Sun Microsystems, Inc. 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 @@ -117,6 +117,7 @@ // Get a list of realms to traverse String[] realms = Realm.getRealmsList(localRealm, serviceRealm); + boolean okAsDelegate = true; if (realms == null || realms.length == 0) { @@ -194,6 +195,15 @@ */ newTgtRealm = newTgt.getServer().getInstanceComponent(); + if (okAsDelegate && !newTgt.checkDelegate()) { + if (DEBUG) + { + System.out.println(">>> Credentials acquireServiceCreds: " + + "global OK-AS-DELEGATE turned off at " + + newTgt.getServer()); + } + okAsDelegate = false; + } if (DEBUG) { @@ -283,6 +293,9 @@ System.out.println(">>> Credentials acquireServiceCreds: returning creds:"); Credentials.printDebug(theCreds); } + if (!okAsDelegate) { + theCreds.resetDelegate(); + } return theCreds; } throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED, diff -r 365eb4449319 -r 4c792c19266e jdk/test/sun/security/krb5/auto/Context.java --- a/jdk/test/sun/security/krb5/auto/Context.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/test/sun/security/krb5/auto/Context.java Fri Nov 27 08:51:28 2009 +0800 @@ -72,7 +72,7 @@ public class Context { private Subject s; - private GSSContext x; + private ExtendedGSSContext x; private boolean f; // context established? private String name; private GSSCredential cred; // see static method delegated(). @@ -147,8 +147,8 @@ @Override public byte[] run(Context me, byte[] dummy) throws Exception { GSSManager m = GSSManager.getInstance(); - me.x = m.createContext( - target.indexOf('@') < 0 ? + me.x = (ExtendedGSSContext)m.createContext( + target.indexOf('@') < 0 ? m.createName(target, null) : m.createName(target, GSSName.NT_HOSTBASED_SERVICE), mech, @@ -170,7 +170,7 @@ @Override public byte[] run(Context me, byte[] dummy) throws Exception { GSSManager m = GSSManager.getInstance(); - me.x = m.createContext(m.createCredential( + me.x = (ExtendedGSSContext)m.createContext(m.createCredential( null, GSSCredential.INDEFINITE_LIFETIME, mech, @@ -193,7 +193,7 @@ * * @return the GSSContext object */ - public GSSContext x() { + public ExtendedGSSContext x() { return x; } @@ -255,6 +255,11 @@ if (x.getSequenceDetState()) { sb.append("seq det, "); } + if (x instanceof ExtendedGSSContext) { + if (((ExtendedGSSContext)x).getDelegPolicyState()) { + sb.append("deleg policy, "); + } + } System.out.println("Context status of " + name + ": " + sb.toString()); System.out.println(x.getSrcName() + " -> " + x.getTargName()); } catch (Exception e) { diff -r 365eb4449319 -r 4c792c19266e jdk/test/sun/security/krb5/auto/KDC.java --- a/jdk/test/sun/security/krb5/auto/KDC.java Wed Nov 25 08:24:58 2009 -0800 +++ b/jdk/test/sun/security/krb5/auto/KDC.java Fri Nov 27 08:51:28 2009 +0800 @@ -63,6 +63,14 @@ * settings after calling a KDC method, call Config.refresh() to * make sure your changes are reflected in the Config object. * + * System properties recognized: + *

+ * Support policies: + * * Issues and TODOs: *
    *
  1. Generates krb5.conf to be used on another machine, currently the kdc is @@ -151,7 +159,7 @@ * A standalone KDC server. */ public static void main(String[] args) throws Exception { - KDC kdc = create("RABBIT.HOLE", "kdc.rabbit,hole", 0, false); + KDC kdc = create("RABBIT.HOLE", "kdc.rabbit.hole", 0, false); kdc.addPrincipal("dummy", "bogus".toCharArray()); kdc.addPrincipal("foo", "bar".toCharArray()); kdc.addPrincipalRandKey("krbtgt/RABBIT.HOLE"); @@ -426,14 +434,17 @@ * @throws sun.security.krb5.KrbException when the principal is not inside * the database. */ - private char[] getPassword(PrincipalName p) throws KrbException { + private char[] getPassword(PrincipalName p, boolean server) + throws KrbException { String pn = p.toString(); if (p.getRealmString() == null) { pn = pn + "@" + getRealm(); } char[] pass = passwords.get(pn); if (pass == null) { - throw new KrbException(Krb5.KDC_ERR_C_PRINCIPAL_UNKNOWN); + throw new KrbException(server? + Krb5.KDC_ERR_S_PRINCIPAL_UNKNOWN: + Krb5.KDC_ERR_C_PRINCIPAL_UNKNOWN); } return pass; } @@ -457,10 +468,12 @@ * Returns the key for a given principal of the given encryption type * @param p the principal * @param etype the encryption type + * @param server looking for a server principal? * @return the key * @throws sun.security.krb5.KrbException for unknown/unsupported etype */ - private EncryptionKey keyForUser(PrincipalName p, int etype) throws KrbException { + private EncryptionKey keyForUser(PrincipalName p, int etype, boolean server) + throws KrbException { try { // Do not call EncryptionKey.acquireSecretKeys(), otherwise // the krb5.conf config file would be loaded. @@ -469,22 +482,71 @@ Integer kvno = null; // For service whose password ending with a number, use it as kvno if (p.toString().indexOf('/') >= 0) { - char[] pass = getPassword(p); + char[] pass = getPassword(p, server); if (Character.isDigit(pass[pass.length-1])) { kvno = pass[pass.length-1] - '0'; } } return new EncryptionKey((byte[]) stringToKey.invoke( - null, getPassword(p), getSalt(p), null, etype), + null, getPassword(p, server), getSalt(p), null, etype), etype, kvno); } catch (InvocationTargetException ex) { KrbException ke = (KrbException)ex.getCause(); throw ke; + } catch (KrbException ke) { + throw ke; } catch (Exception e) { throw new RuntimeException(e); // should not happen } } + private Map policies = new HashMap(); + + public void setPolicy(String rule, String value) { + if (value == null) { + policies.remove(rule); + } else { + policies.put(rule, value); + } + } + /** + * If the provided client/server pair matches a rule + * + * A system property named test.kdc.policy.RULE will be consulted. + * If it's unset, returns false. If its value is "", any pair is + * matched. Otherwise, it should contains the server name matched. + * + * TODO: client name is not used currently. + * + * @param c client name + * @param s server name + * @param rule rule name + * @return if a match is found + */ + private boolean configMatch(String c, String s, String rule) { + String policy = policies.get(rule); + boolean result = false; + if (policy == null) { + result = false; + } else if (policy.length() == 0) { + result = true; + } else { + String[] names = policy.split("\\s+"); + for (String name: names) { + if (name.equals(s)) { + result = true; + break; + } + } + } + if (result) { + System.out.printf(">>>> Policy match result (%s vs %s on %s) %b\n", + c, s, rule, result); + } + return result; + } + + /** * Processes an incoming request and generates a response. * @param in the request @@ -530,7 +592,7 @@ tkt = apReq.ticket; etype = tkt.encPart.getEType(); tkt.sname.setRealm(tkt.realm); - EncryptionKey kkey = keyForUser(tkt.sname, etype); + EncryptionKey kkey = keyForUser(tkt.sname, etype, true); byte[] bb = tkt.encPart.decrypt(kkey, KeyUsage.KU_TICKET); DerInputStream derIn = new DerInputStream(bb); DerValue der = derIn.getDerValue(); @@ -541,7 +603,7 @@ throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP); } } - EncryptionKey skey = keyForUser(body.sname, etype); + EncryptionKey skey = keyForUser(body.sname, etype, true); if (skey == null) { throw new KrbException(Krb5.KDC_ERR_SUMTYPE_NOSUPP); // TODO } @@ -581,6 +643,10 @@ if (body.kdcOptions.get(KDCOptions.ALLOW_POSTDATE)) { bFlags[Krb5.TKT_OPTS_MAY_POSTDATE] = true; } + + if (configMatch("", body.sname.getNameString(), "ok-as-delegate")) { + bFlags[Krb5.TKT_OPTS_DELEGATE] = true; + } bFlags[Krb5.TKT_OPTS_INITIAL] = true; TicketFlags tFlags = new TicketFlags(bFlags); @@ -671,8 +737,8 @@ eTypes = (int[])f.get(body); int eType = eTypes[0]; - EncryptionKey ckey = keyForUser(body.cname, eType); - EncryptionKey skey = keyForUser(body.sname, eType); + EncryptionKey ckey = keyForUser(body.cname, eType, false); + EncryptionKey skey = keyForUser(body.sname, eType, true); if (ckey == null) { throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP); } diff -r 365eb4449319 -r 4c792c19266e jdk/test/sun/security/krb5/auto/OkAsDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/OkAsDelegate.java Fri Nov 27 08:51:28 2009 +0800 @@ -0,0 +1,104 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import com.sun.security.jgss.ExtendedGSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.Oid; +import sun.security.jgss.GSSUtil; +import sun.security.krb5.Config; + +public class OkAsDelegate { + + public static void main(String[] args) + throws Exception { + OkAsDelegate ok = new OkAsDelegate(); + ok.go( + Boolean.valueOf(args[0]), // FORWARDABLE in krb5.conf on? + Boolean.valueOf(args[1]), // requestDelegState + Boolean.valueOf(args[2]), // requestDelegPolicyState + Boolean.valueOf(args[3]), // DelegState in response + Boolean.valueOf(args[4]), // DelegPolicyState in response + Boolean.valueOf(args[5]) // getDelegCred OK? + ); + } + + void go( + boolean forwardable, + boolean requestDelegState, + boolean requestDelegPolicyState, + boolean delegState, + boolean delegPolicyState, + boolean delegated + ) throws Exception { + OneKDC kdc = new OneKDC(null); + kdc.setPolicy("ok-as-delegate", + System.getProperty("test.kdc.policy.ok-as-delegate")); + kdc.writeJAASConf(); + if (!forwardable) { + // The default OneKDC always includes "forwardable = true" + // in krb5.conf, override it. + KDC.saveConfig(OneKDC.KRB5_CONF, kdc, + "default_keytab_name = " + OneKDC.KTAB); + Config.refresh(); + } + + Context c, s; + c = Context.fromJAAS("client"); + s = Context.fromJAAS("server"); + + Oid mech = GSSUtil.GSS_KRB5_MECH_OID; + if (System.getProperty("test.spnego") != null) { + mech = GSSUtil.GSS_SPNEGO_MECH_OID; + } + c.startAsClient(OneKDC.SERVER, mech); + ExtendedGSSContext cx = (ExtendedGSSContext)c.x(); + cx.requestCredDeleg(requestDelegState); + cx.requestDelegPolicy(requestDelegPolicyState); + s.startAsServer(mech); + ExtendedGSSContext sx = (ExtendedGSSContext)s.x(); + + Context.handshake(c, s); + + if (cx.getCredDelegState() != delegState) { + throw new Exception("Initiator cred state error"); + } + if (sx.getCredDelegState() != delegState) { + throw new Exception("Acceptor cred state error"); + } + if (cx.getDelegPolicyState() != delegPolicyState) { + throw new Exception("Initiator cred policy state error"); + } + + GSSCredential cred = null; + try { + cred = s.x().getDelegCred(); + } catch (GSSException e) { + // leave cred as null + } + + if (delegated != (cred != null)) { + throw new Exception("get cred error"); + } + } +} diff -r 365eb4449319 -r 4c792c19266e jdk/test/sun/security/krb5/auto/OkAsDelegateXRealm.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/OkAsDelegateXRealm.java Fri Nov 27 08:51:28 2009 +0800 @@ -0,0 +1,156 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import com.sun.security.jgss.ExtendedGSSContext; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.Security; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import sun.security.jgss.GSSUtil; +import sun.security.krb5.Config; + +public class OkAsDelegateXRealm implements CallbackHandler { + + /** + * @param args boolean if the program should succeed + */ + public static void main(String[] args) + throws Exception { + + // Create and start the KDCs. Here we have 3 realms: R1, R2 and R3. + // R1 is trusted by R2, and R2 trusted by R3. + KDC kdc1 = KDC.create("R1"); + kdc1.setPolicy("ok-as-delegate", + System.getProperty("test.kdc.policy.ok-as-delegate")); + kdc1.addPrincipal("dummy", "bogus".toCharArray()); + kdc1.addPrincipalRandKey("krbtgt/R1"); + kdc1.addPrincipal("krbtgt/R2@R1", "r1->r2".toCharArray()); + + KDC kdc2 = KDC.create("R2"); + kdc2.setPolicy("ok-as-delegate", + System.getProperty("test.kdc.policy.ok-as-delegate")); + kdc2.addPrincipalRandKey("krbtgt/R2"); + kdc2.addPrincipal("krbtgt/R2@R1", "r1->r2".toCharArray()); + kdc2.addPrincipal("krbtgt/R3@R2", "r2->r3".toCharArray()); + + KDC kdc3 = KDC.create("R3"); + kdc3.setPolicy("ok-as-delegate", + System.getProperty("test.kdc.policy.ok-as-delegate")); + kdc3.addPrincipalRandKey("krbtgt/R3"); + kdc3.addPrincipal("krbtgt/R3@R2", "r2->r3".toCharArray()); + kdc3.addPrincipalRandKey("host/host.r3.local"); + + KDC.saveConfig("krb5-localkdc.conf", kdc1, kdc2, kdc3, + "forwardable=true", + "[capaths]", + "R1 = {", + " R2 = .", + " R3 = R2", + "}", + "[domain_realm]", + ".r3.local=R3" + ); + + System.setProperty("java.security.krb5.conf", "krb5-localkdc.conf"); + kdc3.writeKtab("localkdc.ktab"); + + FileOutputStream fos = new FileOutputStream("jaas-localkdc.conf"); + + // Defines the client and server on R1 and R3 respectively. + fos.write(("com.sun.security.jgss.krb5.initiate {\n" + + " com.sun.security.auth.module.Krb5LoginModule\n" + + " required\n" + + " principal=dummy\n" + + " doNotPrompt=false\n" + + " useTicketCache=false\n" + + " ;\n};\n" + + "com.sun.security.jgss.krb5.accept {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " principal=\"host/host.r3.local@R3\"\n" + + " useKeyTab=true\n" + + " keyTab=localkdc.ktab\n" + + " isInitiator=false\n" + + " storeKey=true;\n};\n" + + "\n").getBytes()); + fos.close(); + + Security.setProperty("auth.login.defaultCallbackHandler", + "OkAsDelegateXRealm"); + + System.setProperty("java.security.auth.login.config", "jaas-localkdc.conf"); + + new File("krb5-localkdc.conf").deleteOnExit(); + new File("localkdc.ktab").deleteOnExit(); + new File("jaas-localkdc.conf").deleteOnExit(); + Config.refresh(); + + Context c = Context.fromJAAS("com.sun.security.jgss.krb5.initiate"); + Context s = Context.fromJAAS("com.sun.security.jgss.krb5.accept"); + + // Test twice. The frist time the whole cross realm process is tried, + // the second time the cached service ticket is used. This is to make sure + // the behaviors are the same, especailly for the case when one of the + // cross-realm TGTs does not have OK-AS-DELEGATE on. + + for (int i=0; i<2; i++) { + c.startAsClient("host@host.r3.local", GSSUtil.GSS_KRB5_MECH_OID); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + c.x().requestDelegPolicy(true); + + Context.handshake(c, s); + boolean succeed = true; + try { + s.x().getDelegCred(); + } catch (GSSException gsse) { + succeed = false; + } + if (succeed != Boolean.parseBoolean(args[0])) { + throw new Exception("Test fail at round #" + i); + } + } + } + + @Override + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + ((NameCallback) callback).setName("dummy"); + } + if (callback instanceof PasswordCallback) { + ((PasswordCallback) callback).setPassword("bogus".toCharArray()); + } + } + } +} + diff -r 365eb4449319 -r 4c792c19266e jdk/test/sun/security/krb5/auto/ok-as-delegate-xrealm.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/ok-as-delegate-xrealm.sh Fri Nov 27 08:51:28 2009 +0800 @@ -0,0 +1,79 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# @test +# @bug 6853328 +# @summary Support OK-AS-DELEGATE flag +# @run shell/timeout=600 ok-as-delegate-xrealm.sh +# + +if [ "${TESTSRC}" = "" ] ; then + TESTSRC=`dirname $0` +fi + +if [ "${TESTJAVA}" = "" ] ; then + JAVAC_CMD=`which javac` + TESTJAVA=`dirname $JAVAC_CMD`/.. +fi + +# set platform-dependent variables +OS=`uname -s` +case "$OS" in + Windows_* ) + FS="\\" + SEP=";" + ;; + CYGWIN* ) + FS="/" + SEP=";" + ;; + * ) + FS="/" + SEP=":" + ;; +esac + +${TESTJAVA}${FS}bin${FS}javac -XDignore.symbol.file -d . \ + ${TESTSRC}${FS}OkAsDelegateXRealm.java \ + ${TESTSRC}${FS}KDC.java \ + ${TESTSRC}${FS}OneKDC.java \ + ${TESTSRC}${FS}Action.java \ + ${TESTSRC}${FS}Context.java \ + || exit 10 + +# Add $TESTSRC to classpath so that customized nameservice can be used +J="${TESTJAVA}${FS}bin${FS}java -cp $TESTSRC${SEP}." + +# KDC no OK-AS-DELEGATE, fail +$J OkAsDelegateXRealm false || exit 1 + +# KDC set OK-AS-DELEGATE for all, succeed +$J -Dtest.kdc.policy.ok-as-delegate OkAsDelegateXRealm true || exit 2 + +# KDC set OK-AS-DELEGATE for host/host.r3.local only, fail +$J -Dtest.kdc.policy.ok-as-delegate=host/host.r3.local OkAsDelegateXRealm false || exit 3 + +# KDC set OK-AS-DELEGATE for all, succeed +$J "-Dtest.kdc.policy.ok-as-delegate=host/host.r3.local krbtgt/R2 krbtgt/R3" OkAsDelegateXRealm true || exit 4 + +exit 0 diff -r 365eb4449319 -r 4c792c19266e jdk/test/sun/security/krb5/auto/ok-as-delegate.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/ok-as-delegate.sh Fri Nov 27 08:51:28 2009 +0800 @@ -0,0 +1,118 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# @test +# @bug 6853328 +# @summary Support OK-AS-DELEGATE flag +# @run shell/timeout=600 ok-as-delegate.sh +# + +if [ "${TESTSRC}" = "" ] ; then + TESTSRC=`dirname $0` +fi + +if [ "${TESTJAVA}" = "" ] ; then + JAVAC_CMD=`which javac` + TESTJAVA=`dirname $JAVAC_CMD`/.. +fi + +# set platform-dependent variables +OS=`uname -s` +case "$OS" in + Windows_* ) + FS="\\" + SEP=";" + ;; + CYGWIN* ) + FS="/" + SEP=";" + ;; + * ) + FS="/" + SEP=":" + ;; +esac + +${TESTJAVA}${FS}bin${FS}javac -XDignore.symbol.file -d . \ + ${TESTSRC}${FS}OkAsDelegate.java \ + ${TESTSRC}${FS}KDC.java \ + ${TESTSRC}${FS}OneKDC.java \ + ${TESTSRC}${FS}Action.java \ + ${TESTSRC}${FS}Context.java \ + || exit 10 + +# Testing Kerberos 5 + +# Add $TESTSRC to classpath so that customized nameservice can be used +J="${TESTJAVA}${FS}bin${FS}java -cp $TESTSRC${SEP}. OkAsDelegate" +JOK="${TESTJAVA}${FS}bin${FS}java -cp $TESTSRC${SEP}. -Dtest.kdc.policy.ok-as-delegate OkAsDelegate" + +# FORWARDABLE ticket not allowed, always fail +$J false true true false false false || exit 1 + +# Service ticket no OK-AS-DELEGATE + +# Request nothing, gain nothing +$J true false false false false false || exit 2 +# Request deleg policy, gain nothing +$J true false true false false false || exit 3 +# Request deleg, granted +$J true true false true false true || exit 4 +# Request deleg and deleg policy, granted, with info not by policy +$J true true true true false true || exit 5 + +# Service ticket has OK-AS-DELEGATE + +# Request deleg policy, granted +$JOK true false true true true true || exit 6 +# Request deleg and deleg policy, granted, with info by policy +$JOK true true true true true true || exit 7 + +# Testing SPNEGO + +# Add $TESTSRC to classpath so that customized nameservice can be used +J="${TESTJAVA}${FS}bin${FS}java -cp $TESTSRC${SEP}. -Dtest.spnego OkAsDelegate" +JOK="${TESTJAVA}${FS}bin${FS}java -cp $TESTSRC${SEP}. -Dtest.spnego -Dtest.kdc.policy.ok-as-delegate OkAsDelegate" + +# FORWARDABLE ticket not allowed, always fail +$J false true true false false false || exit 11 + +# Service ticket no OK-AS-DELEGATE + +# Request nothing, gain nothing +$J true false false false false false || exit 12 +# Request deleg policy, gain nothing +$J true false true false false false || exit 13 +# Request deleg, granted +$J true true false true false true || exit 14 +# Request deleg and deleg policy, granted, with info not by policy +$J true true true true false true || exit 15 + +# Service ticket has OK-AS-DELEGATE + +# Request deleg policy, granted +$JOK true false true true true true || exit 16 +# Request deleg and deleg policy, granted, with info by policy +$JOK true true true true true true || exit 17 + +exit 0