jdk/test/sun/security/krb5/auto/KDC.java
changeset 14413 e954df027393
parent 13247 74902cfeb9c6
child 15006 10d6aacdd67f
--- a/jdk/test/sun/security/krb5/auto/KDC.java	Tue Nov 06 18:41:01 2012 -0800
+++ b/jdk/test/sun/security/krb5/auto/KDC.java	Wed Nov 07 14:13:01 2012 +0800
@@ -178,6 +178,20 @@
          * What backend server can be delegated to
          */
         OK_AS_DELEGATE,
+        /**
+         * Allow S4U2self, List<String> of middle servers.
+         * If not set, means KDC does not understand S4U2self at all, therefore
+         * would ignore any PA-FOR-USER request and send a ticket using the
+         * cname of teh requestor. If set, it returns FORWARDABLE tickets to
+         * a server with its name in the list
+         */
+        ALLOW_S4U2SELF,
+        /**
+         * Allow S4U2proxy, Map<String,List<String>> of middle servers to
+         * backends. If not set or a backend not in a server's list,
+         * Krb5.KDC_ERR_POLICY will be send for S4U2proxy request.
+         */
+        ALLOW_S4U2PROXY,
     };
 
     static {
@@ -618,13 +632,18 @@
             int e2 = eTypes[0];     // etype for outgoing session key
             int e3 = eTypes[0];     // etype for outgoing ticket
 
-            PAData[] pas = kDCReqDotPAData(tgsReq);
+            PAData[] pas = KDCReqDotPAData(tgsReq);
 
             Ticket tkt = null;
             EncTicketPart etp = null;
+
+            PrincipalName cname = null;
+            boolean allowForwardable = true;
+
             if (pas == null || pas.length == 0) {
                 throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
             } else {
+                PrincipalName forUserCName = null;
                 for (PAData pa: pas) {
                     if (pa.getType() == Krb5.PA_TGS_REQ) {
                         APReq apReq = new APReq(pa.getValue());
@@ -636,8 +655,31 @@
                         DerInputStream derIn = new DerInputStream(bb);
                         DerValue der = derIn.getDerValue();
                         etp = new EncTicketPart(der.toByteArray());
+                        // Finally, cname will be overwritten by PA-FOR-USER
+                        // if it exists.
+                        cname = etp.cname;
+                        System.out.println(realm + "> presenting a ticket of "
+                                + etp.cname + " to " + tkt.sname);
+                    } else if (pa.getType() == Krb5.PA_FOR_USER) {
+                        if (options.containsKey(Option.ALLOW_S4U2SELF)) {
+                            PAForUserEnc p4u = new PAForUserEnc(
+                                    new DerValue(pa.getValue()), null);
+                            forUserCName = p4u.name;
+                            System.out.println(realm + "> presenting a PA_FOR_USER "
+                                    + " in the name of " + p4u.name);
+                        }
                     }
                 }
+                if (forUserCName != null) {
+                    List<String> names = (List<String>)options.get(Option.ALLOW_S4U2SELF);
+                    if (!names.contains(cname.toString())) {
+                        // Mimic the normal KDC behavior. When a server is not
+                        // allowed to send S4U2self, do not send an error.
+                        // Instead, send a ticket which is useless later.
+                        allowForwardable = false;
+                    }
+                    cname = forUserCName;
+                }
                 if (tkt == null) {
                     throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
                 }
@@ -658,7 +700,8 @@
             }
 
             boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1];
-            if (body.kdcOptions.get(KDCOptions.FORWARDABLE)) {
+            if (body.kdcOptions.get(KDCOptions.FORWARDABLE)
+                    && allowForwardable) {
                 bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
             }
             if (body.kdcOptions.get(KDCOptions.FORWARDED) ||
@@ -678,6 +721,37 @@
             if (body.kdcOptions.get(KDCOptions.ALLOW_POSTDATE)) {
                 bFlags[Krb5.TKT_OPTS_MAY_POSTDATE] = true;
             }
+            if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT)) {
+                if (!options.containsKey(Option.ALLOW_S4U2PROXY)) {
+                    // Don't understand CNAME_IN_ADDL_TKT
+                    throw new KrbException(Krb5.KDC_ERR_BADOPTION);
+                } else {
+                    Map<String,List<String>> map = (Map<String,List<String>>)
+                            options.get(Option.ALLOW_S4U2PROXY);
+                    Ticket second = KDCReqBodyDotFirstAdditionalTicket(body);
+                    EncryptionKey key2 = keyForUser(second.sname, second.encPart.getEType(), true);
+                    byte[] bb = second.encPart.decrypt(key2, KeyUsage.KU_TICKET);
+                    DerInputStream derIn = new DerInputStream(bb);
+                    DerValue der = derIn.getDerValue();
+                    EncTicketPart tktEncPart = new EncTicketPart(der.toByteArray());
+                    if (!tktEncPart.flags.get(Krb5.TKT_OPTS_FORWARDABLE)) {
+                        //throw new KrbException(Krb5.KDC_ERR_BADOPTION);
+                    }
+                    PrincipalName client = tktEncPart.cname;
+                    System.out.println(realm + "> and an additional ticket of "
+                            + client + " to " + second.sname);
+                    if (map.containsKey(cname.toString())) {
+                        if (map.get(cname.toString()).contains(service.toString())) {
+                            System.out.println(realm + "> S4U2proxy OK");
+                        } else {
+                            throw new KrbException(Krb5.KDC_ERR_BADOPTION);
+                        }
+                    } else {
+                        throw new KrbException(Krb5.KDC_ERR_BADOPTION);
+                    }
+                    cname = client;
+                }
+            }
 
             String okAsDelegate = (String)options.get(Option.OK_AS_DELEGATE);
             if (okAsDelegate != null && (
@@ -691,7 +765,7 @@
             EncTicketPart enc = new EncTicketPart(
                     tFlags,
                     key,
-                    etp.cname,
+                    cname,
                     new TransitedEncoding(1, new byte[0]),  // TODO
                     new KerberosTime(new Date()),
                     body.from,
@@ -729,7 +803,7 @@
                     );
             EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
             TGSRep tgsRep = new TGSRep(null,
-                    etp.cname,
+                    cname,
                     t,
                     edata);
             System.out.println("     Return " + tgsRep.cname
@@ -942,7 +1016,7 @@
                 outPAs.add(new PAData(Krb5.PA_ETYPE_INFO, eid.toByteArray()));
             }
 
-            PAData[] inPAs = kDCReqDotPAData(asReq);
+            PAData[] inPAs = KDCReqDotPAData(asReq);
             if (inPAs == null || inPAs.length == 0) {
                 Object preauth = options.get(Option.PREAUTH_REQUIRED);
                 if (preauth == null || preauth.equals(Boolean.TRUE)) {
@@ -1252,6 +1326,7 @@
     private static final Field getEType;
     private static final Constructor<EncryptedData> ctorEncryptedData;
     private static final Method stringToKey;
+    private static final Field getAddlTkt;
 
     static {
         try {
@@ -1265,6 +1340,8 @@
                     "stringToKey",
                     char[].class, String.class, byte[].class, Integer.TYPE);
             stringToKey.setAccessible(true);
+            getAddlTkt = KDCReqBody.class.getDeclaredField("additionalTickets");
+            getAddlTkt.setAccessible(true);
         } catch (NoSuchFieldException nsfe) {
             throw new AssertionError(nsfe);
         } catch (NoSuchMethodException nsme) {
@@ -1278,7 +1355,7 @@
             throw new AssertionError(e);
         }
     }
-    private static PAData[] kDCReqDotPAData(KDCReq req) {
+    private static PAData[] KDCReqDotPAData(KDCReq req) {
         try {
             return (PAData[])getPADataField.get(req);
         } catch (Exception e) {
@@ -1303,4 +1380,11 @@
             throw new AssertionError(e);
         }
     }
+    private static Ticket KDCReqBodyDotFirstAdditionalTicket(KDCReqBody body) {
+        try {
+            return ((Ticket[])getAddlTkt.get(body))[0];
+        } catch (Exception e) {
+            throw new AssertionError(e);
+        }
+    }
 }