6853328: Support OK-AS-DELEGATE flag
authorweijun
Fri, 27 Nov 2009 08:51:28 +0800
changeset 4336 4c792c19266e
parent 4335 365eb4449319
child 4337 2a6d13ebbbed
6853328: Support OK-AS-DELEGATE flag Reviewed-by: valeriep
jdk/src/share/classes/com/sun/security/jgss/ExtendedGSSContext.java
jdk/src/share/classes/org/ietf/jgss/GSSContext.java
jdk/src/share/classes/sun/net/www/protocol/http/spnego/NegotiatorImpl.java
jdk/src/share/classes/sun/security/jgss/GSSContextImpl.java
jdk/src/share/classes/sun/security/jgss/krb5/InitialToken.java
jdk/src/share/classes/sun/security/jgss/krb5/Krb5Context.java
jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java
jdk/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java
jdk/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java
jdk/src/share/classes/sun/security/krb5/Credentials.java
jdk/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java
jdk/test/sun/security/krb5/auto/Context.java
jdk/test/sun/security/krb5/auto/KDC.java
jdk/test/sun/security/krb5/auto/OkAsDelegate.java
jdk/test/sun/security/krb5/auto/OkAsDelegateXRealm.java
jdk/test/sun/security/krb5/auto/ok-as-delegate-xrealm.sh
jdk/test/sun/security/krb5/auto/ok-as-delegate.sh
--- 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 <code>initSecContext</code>.
+     * <p>
+     * When this flag is false, delegation will only be tried when the
+     * {@link GSSContext#requestCredDeleg(boolean) credentials delegation flag}
+     * is true.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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, <code>requestDelegPolicy</code>
+     * should return silently without throwing an exception.
+     * <p>
+     * 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();
 }
--- 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.<p>
      *
-     * 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.<p>
      *
@@ -826,7 +826,7 @@
      * are not definitive then the method will attempt to treat all
      * available bytes as part of the token.<p>
      *
-     * 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.<p>
      *
@@ -917,7 +917,7 @@
      * getMutualAuthState} method.<p>
      *
      * @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
      * <code>initSecContext</code>. 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
      * <code>initSecContext</code>. During context establishment sequence
--- 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);
     }
 
--- 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;
+    }
 }
--- 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)
--- 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.
--- 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();
--- 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.");
         }
     }
+}
 
-}
--- 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);
     }
--- 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 {
--- 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,
--- 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) {
--- 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 <code>Config.refresh()</code> to
  * make sure your changes are reflected in the <code>Config</code> object.
  * </ol>
+ * System properties recognized:
+ * <ul>
+ * <li>test.kdc.save.ccache
+ * </ul>
+ * Support policies:
+ * <ul>
+ * <li>ok-as-delegate
+ * </ul>
  * Issues and TODOs:
  * <ol>
  * <li> 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<String,String> policies = new HashMap<String,String>();
+
+    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);
             }
--- /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");
+        }
+    }
+}
--- /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());
+            }
+        }
+    }
+}
+
--- /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
--- /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