Merge
diff --git a/src/share/classes/sun/java2d/pipe/BufferedContext.java b/src/share/classes/sun/java2d/pipe/BufferedContext.java
index 4513c67..3475c43 100644
--- a/src/share/classes/sun/java2d/pipe/BufferedContext.java
+++ b/src/share/classes/sun/java2d/pipe/BufferedContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,6 +38,8 @@
 import static sun.java2d.pipe.BufferedRenderPipe.BYTES_PER_SPAN;
 
 import java.lang.annotation.Native;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 
 /**
  * Base context class for managing state in a single-threaded rendering
@@ -87,11 +89,11 @@
      */
     protected static BufferedContext currentContext;
 
-    private AccelSurface    validatedSrcData;
-    private AccelSurface    validatedDstData;
-    private Region          validatedClip;
-    private Composite       validatedComp;
-    private Paint           validatedPaint;
+    private Reference<AccelSurface> validSrcDataRef = new WeakReference<>(null);
+    private Reference<AccelSurface> validDstDataRef = new WeakReference<>(null);
+    private Reference<Region> validClipRef = new WeakReference<>(null);
+    private Reference<Composite> validCompRef = new WeakReference<>(null);
+    private Reference<Paint> validPaintRef = new WeakReference<>(null);
     // renamed from isValidatedPaintAColor as part of a work around for 6764257
     private boolean         isValidatedPaintJustAColor;
     private int             validatedRGB;
@@ -127,9 +129,9 @@
                                        int flags)
     {
         // assert rq.lock.isHeldByCurrentThread();
-        BufferedContext d3dc = dstData.getContext();
-        d3dc.validate(srcData, dstData,
-                      clip, comp, xform, paint, sg2d, flags);
+        BufferedContext context = dstData.getContext();
+        context.validate(srcData, dstData,
+                         clip, comp, xform, paint, sg2d, flags);
     }
 
     /**
@@ -200,13 +202,15 @@
                 updatePaint = true;
                 isValidatedPaintJustAColor = true;
             }
-        } else if (validatedPaint != paint) {
+        } else if (validPaintRef.get() != paint) {
             updatePaint = true;
             // this should be set when we are switching from paint to color
             // in which case this condition will be true
             isValidatedPaintJustAColor = false;
         }
 
+        final AccelSurface validatedSrcData = validSrcDataRef.get();
+        final AccelSurface validatedDstData = validDstDataRef.get();
         if ((currentContext != this) ||
             (srcData != validatedSrcData) ||
             (dstData != validatedDstData))
@@ -228,11 +232,12 @@
             setSurfaces(srcData, dstData);
 
             currentContext = this;
-            validatedSrcData = srcData;
-            validatedDstData = dstData;
+            validSrcDataRef = new WeakReference<>(srcData);
+            validDstDataRef = new WeakReference<>(dstData);
         }
 
         // validate clip
+        final Region validatedClip = validClipRef.get();
         if ((clip != validatedClip) || updateClip) {
             if (clip != null) {
                 if (updateClip ||
@@ -248,13 +253,13 @@
             } else {
                 resetClip();
             }
-            validatedClip = clip;
+            validClipRef = new WeakReference<>(clip);
         }
 
         // validate composite (note that a change in the context flags
         // may require us to update the composite state, even if the
         // composite has not changed)
-        if ((comp != validatedComp) || (flags != validatedFlags)) {
+        if ((comp != validCompRef.get()) || (flags != validatedFlags)) {
             if (comp != null) {
                 setComposite(comp, flags);
             } else {
@@ -263,7 +268,7 @@
             // the paint state is dependent on the composite state, so make
             // sure we update the color below
             updatePaint = true;
-            validatedComp = comp;
+            validCompRef = new WeakReference<>(comp);
             validatedFlags = flags;
         }
 
@@ -297,7 +302,7 @@
             } else {
                 BufferedPaints.resetPaint(rq);
             }
-            validatedPaint = paint;
+            validPaintRef = new WeakReference<>(paint);
         }
 
         // mark dstData dirty
@@ -315,9 +320,9 @@
      * @see RenderQueue#lock
      * @see RenderQueue#unlock
      */
-    public void invalidateSurfaces() {
-        validatedSrcData = null;
-        validatedDstData = null;
+    private void invalidateSurfaces() {
+        validSrcDataRef.clear();
+        validDstDataRef.clear();
     }
 
     private void setSurfaces(AccelSurface srcData,
@@ -434,9 +439,9 @@
         resetClip();
         BufferedPaints.resetPaint(rq);
         invalidateSurfaces();
-        validatedComp = null;
-        validatedClip = null;
-        validatedPaint = null;
+        validCompRef.clear();
+        validClipRef.clear();
+        validPaintRef.clear();
         isValidatedPaintJustAColor = false;
         xformInUse = false;
     }
diff --git a/src/share/classes/sun/security/krb5/KrbKdcRep.java b/src/share/classes/sun/security/krb5/KrbKdcRep.java
index dd0e951..66ce73a 100644
--- a/src/share/classes/sun/security/krb5/KrbKdcRep.java
+++ b/src/share/classes/sun/security/krb5/KrbKdcRep.java
@@ -62,7 +62,8 @@
             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
         }
 
-        for (int i = 1; i < 6; i++) {
+        // We allow KDC to return a non-forwardable ticket if request has -f
+        for (int i = 2; i < 6; i++) {
             if (req.reqBody.kdcOptions.get(i) !=
                    rep.encKDCRepPart.flags.get(i)) {
                 if (Krb5.DEBUG) {
diff --git a/src/share/classes/sun/security/krb5/KrbTgsReq.java b/src/share/classes/sun/security/krb5/KrbTgsReq.java
index 798b78d..85b7cb2 100644
--- a/src/share/classes/sun/security/krb5/KrbTgsReq.java
+++ b/src/share/classes/sun/security/krb5/KrbTgsReq.java
@@ -150,19 +150,11 @@
         ctime = KerberosTime.now();
 
         // check if they are valid arguments. The optional fields
-        // should be  consistent with settings in KDCOptions.
-
-        // TODO: Is this necessary? If the TGT is not FORWARDABLE,
-        // you can still request for a FORWARDABLE ticket, just the
-        // KDC will give you a non-FORWARDABLE one. Even if you
-        // cannot use the ticket expected, it still contains info.
-        // This means there will be problem later. We already have
-        // flags check in KrbTgsRep. Of course, sometimes the KDC
-        // will not issue the ticket at all.
+        // should be consistent with settings in KDCOptions.
 
         if (options.get(KDCOptions.FORWARDABLE) &&
                 (!(asCreds.flags.get(Krb5.TKT_OPTS_FORWARDABLE)))) {
-            throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
+            options.set(KDCOptions.FORWARDABLE, false);
         }
         if (options.get(KDCOptions.FORWARDED)) {
             if (!(asCreds.flags.get(KDCOptions.FORWARDABLE)))
diff --git a/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java b/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java
index b6a3677..14620e9 100644
--- a/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java
+++ b/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java
@@ -58,6 +58,9 @@
             // TODO: we do not support kerberos referral now
             throw new KrbException("Cross realm impersonation not supported");
         }
+        if (!ccreds.isForwardable()) {
+            throw new KrbException("S4U2self needs a FORWARDABLE ticket");
+        }
         KrbTgsReq req = new KrbTgsReq(
                 ccreds,
                 ccreds.getClient(),
@@ -68,6 +71,9 @@
         if (!creds.getClient().equals(client)) {
             throw new KrbException("S4U2self request not honored by KDC");
         }
+        if (!creds.isForwardable()) {
+            throw new KrbException("S4U2self ticket must be FORWARDABLE");
+        }
         return creds;
     }
 
diff --git a/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp b/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp
index 825a69c..b7ab7e0 100644
--- a/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp
+++ b/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp
@@ -875,6 +875,9 @@
     // Only want to call this once per session
     make_uns_ordered_dither_array(img_oda_alpha, 256);
 
+    // workaround JDK-6477756, ignore return value to keep dll in memory
+    JDK_LoadSystemLibrary("opengl32.dll");
+
     CATCH_BAD_ALLOC;
 }
 
diff --git a/test/java/awt/Window/WindowsLeak/WindowsLeak.java b/test/java/awt/Window/WindowsLeak/WindowsLeak.java
index dd776a8..3f44687 100644
--- a/test/java/awt/Window/WindowsLeak/WindowsLeak.java
+++ b/test/java/awt/Window/WindowsLeak/WindowsLeak.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,41 +23,40 @@
 
 /*
  * @test
- * @bug 8013563
+ * @bug 8013563 8028486
  * @summary Tests that windows are removed from windows list
+ * @library /javax/swing/regtesthelpers
+ * @build Util
  * @run main/othervm -Xms32M -Xmx32M WindowsLeak
 */
 
-import java.awt.*;
-import sun.awt.AppContext;
-
+import java.awt.Frame;
+import java.awt.Robot;
+import java.awt.Window;
 import java.lang.ref.WeakReference;
-
 import java.util.Vector;
 
+import sun.awt.AppContext;
+import sun.java2d.Disposer;
+
 public class WindowsLeak {
 
-    public static void main(String[] args) {
-        for (int i = 0; i < 100; i++)
-        {
+    private static volatile boolean disposerPhantomComplete;
+
+    public static void main(String[] args) throws Exception {
+        Robot r = new Robot();
+        for (int i = 0; i < 100; i++) {
             Frame f = new Frame();
             f.pack();
             f.dispose();
         }
+        r.waitForIdle();
 
-        Vector garbage = new Vector();
-        while (true)
-        {
-            try
-            {
-                garbage.add(new byte[1000]);
-            }
-            catch (OutOfMemoryError e)
-            {
-                break;
-            }
+        Disposer.addRecord(new Object(), () -> disposerPhantomComplete = true);
+
+        while (!disposerPhantomComplete) {
+            Util.generateOOME();
         }
-        garbage = null;
 
         Vector<WeakReference<Window>> windowList =
                         (Vector<WeakReference<Window>>) AppContext.getAppContext().get(Window.class);
diff --git a/test/sun/security/krb5/auto/Context.java b/test/sun/security/krb5/auto/Context.java
index 715a1ad..f664605 100644
--- a/test/sun/security/krb5/auto/Context.java
+++ b/test/sun/security/krb5/auto/Context.java
@@ -23,6 +23,7 @@
 
 import com.sun.security.auth.module.Krb5LoginModule;
 import java.security.Key;
+import java.lang.reflect.InvocationTargetException;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.util.Arrays;
@@ -581,7 +582,12 @@
             out.name = name + " as " + out.cred.getName().toString();
             return out;
         } catch (PrivilegedActionException pae) {
-            throw pae.getException();
+            Exception e = pae.getException();
+            if (e instanceof InvocationTargetException) {
+                throw (Exception)((InvocationTargetException) e).getTargetException();
+            } else {
+                throw e;
+            }
         }
     }
 
diff --git a/test/sun/security/krb5/auto/ForwardableCheck.java b/test/sun/security/krb5/auto/ForwardableCheck.java
new file mode 100644
index 0000000..df6e49e
--- /dev/null
+++ b/test/sun/security/krb5/auto/ForwardableCheck.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8022582
+ * @summary Relax response flags checking in sun.security.krb5.KrbKdcRep.check.
+ * @compile -XDignore.symbol.file ForwardableCheck.java
+ * @run main/othervm ForwardableCheck
+ */
+
+import org.ietf.jgss.GSSException;
+import sun.security.jgss.GSSUtil;
+
+import java.util.Arrays;
+
+public class ForwardableCheck {
+
+    public static void main(String[] args) throws Exception {
+        OneKDC kdc = new OneKDC(null);
+        kdc.writeJAASConf();
+
+        // USER can impersonate someone else
+        kdc.setOption(KDC.Option.ALLOW_S4U2SELF,
+                Arrays.asList(OneKDC.USER + "@" + OneKDC.REALM));
+        // USER2 is sensitive
+        kdc.setOption(KDC.Option.SENSITIVE_ACCOUNTS,
+                Arrays.asList(OneKDC.USER2 + "@" + OneKDC.REALM));
+
+        Context c;
+
+        // USER2 is sensitive but it's still able to get a normal ticket
+        c = Context.fromUserPass(OneKDC.USER2, OneKDC.PASS2, false);
+
+        // ... and connect to another account
+        c.startAsClient(OneKDC.USER, GSSUtil.GSS_KRB5_MECH_OID);
+        c.x().requestCredDeleg(true);
+        c.x().requestMutualAuth(false);
+
+        c.take(new byte[0]);
+
+        if (!c.x().isEstablished()) {
+            throw new Exception("Context should have been established");
+        }
+
+        // ... but will not be able to delegate itself
+        if (c.x().getCredDelegState()) {
+            throw new Exception("Impossible");
+        }
+
+        // Although USER is allowed to impersonate other people,
+        // it cannot impersonate USER2 coz it's sensitive.
+        c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
+        try {
+            c.impersonate(OneKDC.USER2);
+            throw new Exception("Should fail");
+        } catch (GSSException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java
index e3d63d0..15b1612 100644
--- a/test/sun/security/krb5/auto/KDC.java
+++ b/test/sun/security/krb5/auto/KDC.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -145,6 +145,9 @@
     private List<String> conf = new ArrayList<>();
 
     private Thread thread1, thread2, thread3;
+    private volatile boolean udpConsumerReady = false;
+    private volatile boolean tcpConsumerReady = false;
+    private volatile boolean dispatcherReady = false;
     DatagramSocket u1 = null;
     ServerSocket t1 = null;
 
@@ -194,6 +197,10 @@
          * Krb5.KDC_ERR_POLICY will be send for S4U2proxy request.
          */
         ALLOW_S4U2PROXY,
+        /**
+         * Sensitive accounts can never be delegated.
+         */
+        SENSITIVE_ACCOUNTS,
     };
 
     static {
@@ -638,7 +645,7 @@
         try {
             System.out.println(realm + "> " + tgsReq.reqBody.cname +
                     " sends TGS-REQ for " +
-                    service);
+                    service + ", " + tgsReq.reqBody.kdcOptions);
             KDCReqBody body = tgsReq.reqBody;
             int[] eTypes = KDCReqBodyDotEType(body);
             int e2 = eTypes[0];     // etype for outgoing session key
@@ -714,7 +721,13 @@
             boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1];
             if (body.kdcOptions.get(KDCOptions.FORWARDABLE)
                     && allowForwardable) {
-                bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
+                List<String> sensitives = (List<String>)
+                        options.get(Option.SENSITIVE_ACCOUNTS);
+                if (sensitives != null && sensitives.contains(cname.toString())) {
+                    // Cannot make FORWARDABLE
+                } else {
+                    bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
+                }
             }
             if (body.kdcOptions.get(KDCOptions.FORWARDED) ||
                     etp.flags.get(Krb5.TKT_OPTS_FORWARDED)) {
@@ -819,7 +832,8 @@
                     t,
                     edata);
             System.out.println("     Return " + tgsRep.cname
-                    + " ticket for " + tgsRep.ticket.sname);
+                    + " ticket for " + tgsRep.ticket.sname + ", flags "
+                    + tFlags);
 
             DerOutputStream out = new DerOutputStream();
             out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
@@ -865,7 +879,7 @@
         try {
             System.out.println(realm + "> " + asReq.reqBody.cname +
                     " sends AS-REQ for " +
-                    service);
+                    service + ", " + asReq.reqBody.kdcOptions);
 
             KDCReqBody body = asReq.reqBody;
 
@@ -908,7 +922,13 @@
             //body.from
             boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1];
             if (body.kdcOptions.get(KDCOptions.FORWARDABLE)) {
-                bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
+                List<String> sensitives = (List<String>)
+                        options.get(Option.SENSITIVE_ACCOUNTS);
+                if (sensitives != null && sensitives.contains(body.cname.toString())) {
+                    // Cannot make FORWARDABLE
+                } else {
+                    bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
+                }
             }
             if (body.kdcOptions.get(KDCOptions.RENEWABLE)) {
                 bFlags[Krb5.TKT_OPTS_RENEWABLE] = true;
@@ -1084,7 +1104,8 @@
                     edata);
 
             System.out.println("     Return " + asRep.cname
-                    + " ticket for " + asRep.ticket.sname);
+                    + " ticket for " + asRep.ticket.sname + ", flags "
+                    + tFlags);
 
             DerOutputStream out = new DerOutputStream();
             out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
@@ -1192,6 +1213,7 @@
         // The UDP consumer
         thread1 = new Thread() {
             public void run() {
+                udpConsumerReady = true;
                 while (true) {
                     try {
                         byte[] inbuf = new byte[8192];
@@ -1212,6 +1234,7 @@
         // The TCP consumer
         thread2 = new Thread() {
             public void run() {
+                tcpConsumerReady = true;
                 while (true) {
                     try {
                         Socket socket = tcp.accept();
@@ -1234,6 +1257,7 @@
         // The dispatcher
         thread3 = new Thread() {
             public void run() {
+                dispatcherReady = true;
                 while (true) {
                     try {
                         q.take().send();
@@ -1244,6 +1268,19 @@
         };
         thread3.setDaemon(true);
         thread3.start();
+
+        // wait for the KDC is ready
+        try {
+            while (!isReady()) {
+                Thread.sleep(100);
+            }
+        } catch(InterruptedException e) {
+            throw new IOException(e);
+        }
+    }
+
+    boolean isReady() {
+        return udpConsumerReady && tcpConsumerReady && dispatcherReady;
     }
 
     public void terminate() {
diff --git a/test/sun/security/krb5/auto/UnboundSSL.java b/test/sun/security/krb5/auto/UnboundSSL.java
new file mode 100644
index 0000000..0862f07
--- /dev/null
+++ b/test/sun/security/krb5/auto/UnboundSSL.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedActionException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.security.auth.login.LoginException;
+
+/*
+ * @test
+ * @bug 8025123
+ * @summary Checks if an unbound server can handle connections
+ *          only for allowed service principals
+ * @run main/othervm/policy=unbound.ssl.policy UnboundSSL
+ *                              unbound.ssl.jaas.conf server_star
+ * @run main/othervm/policy=unbound.ssl.policy UnboundSSL
+ *                              unbound.ssl.jaas.conf server_multiple_principals
+ */
+public class UnboundSSL {
+
+    public static void main(String[] args) throws IOException,
+            NoSuchAlgorithmException,LoginException, PrivilegedActionException,
+            InterruptedException {
+        UnboundSSL test = new UnboundSSL();
+        test.start(args[0], args[1]);
+    }
+
+    private void start(String jaacConfigFile, String serverJaasConfig)
+            throws IOException, NoSuchAlgorithmException,LoginException,
+            PrivilegedActionException, InterruptedException {
+
+        // define principals
+        String service1host = "service1." + UnboundSSLUtils.HOST;
+        String service2host = "service2." + UnboundSSLUtils.HOST;
+        String service3host = "service3." + UnboundSSLUtils.HOST;
+        String service1Principal = "host/" + service1host + "@"
+                + UnboundSSLUtils.REALM;
+        String service2Principal = "host/" + service2host + "@"
+                + UnboundSSLUtils.REALM;
+        String service3Principal = "host/" + service3host + "@"
+                + UnboundSSLUtils.REALM;
+
+        Map<String, String> principals = new HashMap<>();
+        principals.put(UnboundSSLUtils.USER_PRINCIPAL,
+                UnboundSSLUtils.USER_PASSWORD);
+        principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, null);
+        principals.put(service1Principal, null);
+        principals.put(service2Principal, null);
+        principals.put(service3Principal, null);
+
+        System.setProperty("java.security.krb5.conf",
+               UnboundSSLUtils.KRB5_CONF_FILENAME);
+
+        // start a local KDC instance
+        UnboundSSLUtils.startKDC(UnboundSSLUtils.REALM, principals,
+                UnboundSSLUtils.KTAB_FILENAME, UnboundSSLUtils.KtabMode.APPEND);
+
+        System.setProperty("java.security.auth.login.config",
+                UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile);
+        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+        try (final SSLEchoServer server = SSLEchoServer.init(
+                UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) {
+
+            // start a server instance
+            UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig);
+
+            // wait for the server is ready
+            while (!server.isReady()) {
+                Thread.sleep(UnboundSSLUtils.DELAY);
+            }
+
+            int port = server.getPort();
+
+            // run clients
+
+            // the server should have a permission to handle a request
+            // with this service principal (there should be an appropriate
+            // javax.security.auth.kerberos.ServicePermission in policy file)
+            System.out.println("Connect: SNI hostname = " + service1host
+                    + ", successful connection is expected");
+            SSLClient.init(UnboundSSLUtils.HOST, port,
+                    UnboundSSLUtils.TLS_KRB5_FILTER, service1host).connect();
+
+            // the server should NOT have a permission to handle a request
+            // with this service principal (there should be an appropriate
+            // javax.security.auth.kerberos.ServicePermission in policy file)
+            // handshake failures is expected
+            System.out.println("Connect: SNI hostname = " + service2host
+                    + ", connection failure is expected");
+            try {
+                SSLClient.init(UnboundSSLUtils.HOST, port,
+                        UnboundSSLUtils.TLS_KRB5_FILTER, service2host)
+                            .connect();
+                throw new RuntimeException("Test failed: "
+                        + "expected IOException not thrown");
+            } catch (IOException e) {
+                System.out.println("Expected exception: " + e);
+            }
+
+            // the server should have a permission to handle a request
+            // with this service principal (there should be an appropriate
+            // javax.security.auth.kerberos.ServicePermission in policy file)
+            System.out.println("Connect: SNI hostname = " + service3host
+                    + ", successful connection is expected");
+            SSLClient.init(UnboundSSLUtils.HOST, port,
+                    UnboundSSLUtils.TLS_KRB5_FILTER, service3host).connect();
+        }
+
+        System.out.println("Test passed");
+    }
+}
diff --git a/test/sun/security/krb5/auto/UnboundSSLMultipleKeys.java b/test/sun/security/krb5/auto/UnboundSSLMultipleKeys.java
new file mode 100644
index 0000000..255a40c
--- /dev/null
+++ b/test/sun/security/krb5/auto/UnboundSSLMultipleKeys.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedActionException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.security.auth.login.LoginException;
+
+/*
+ * @test
+ * @bug 8025123
+ * @summary Checks if an unbound server pick up a correct key from keytab
+ * @run main/othervm UnboundSSLMultipleKeys
+ *                              unbound.ssl.jaas.conf server_star
+ * @run main/othervm UnboundSSLMultipleKeys
+ *                              unbound.ssl.jaas.conf server_multiple_principals
+ */
+public class UnboundSSLMultipleKeys {
+
+    public static void main(String[] args)
+            throws IOException, NoSuchAlgorithmException, LoginException,
+            PrivilegedActionException, InterruptedException {
+        UnboundSSLMultipleKeys test = new UnboundSSLMultipleKeys();
+        test.start(args[0], args[1]);
+    }
+
+    private void start(String jaacConfigFile, String serverJaasConfig)
+            throws IOException, NoSuchAlgorithmException, LoginException,
+            PrivilegedActionException, InterruptedException {
+
+        // define service principals
+        String service1host = "service1." + UnboundSSLUtils.HOST;
+        String service2host = "service2." + UnboundSSLUtils.HOST;
+        String service3host = "service3." + UnboundSSLUtils.HOST;
+        String service1Principal = "host/" + service1host + "@"
+                + UnboundSSLUtils.REALM;
+        String service2Principal = "host/" + service2host + "@"
+                + UnboundSSLUtils.REALM;
+        String service3Principal = "host/" + service3host + "@"
+                + UnboundSSLUtils.REALM;
+
+        Map<String, String> principals = new HashMap<>();
+        principals.put(UnboundSSLUtils.USER_PRINCIPAL,
+                UnboundSSLUtils.USER_PASSWORD);
+        principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, "pass");
+        principals.put(service1Principal, "pass0");
+        principals.put(service1Principal, "pass1");
+        principals.put(service1Principal, "pass2");
+        principals.put(service2Principal, "pass");
+        principals.put(service3Principal, "pass");
+
+        System.setProperty("java.security.krb5.conf",
+                UnboundSSLUtils.KRB5_CONF_FILENAME);
+
+        /*
+         * Start a local KDC instance
+         *
+         * Keytab file contains 3 keys (with different KVNO) for service1
+         * principal, but password for only one key is the same with the record
+         * for service1 principal in KDC.
+         */
+        UnboundSSLUtils.startKDC(UnboundSSLUtils.REALM, principals,
+                UnboundSSLUtils.KTAB_FILENAME, UnboundSSLUtils.KtabMode.APPEND);
+
+        System.setProperty("java.security.auth.login.config",
+                UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile);
+        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+        // start an SSL server instance
+        try (SSLEchoServer server = SSLEchoServer.init(
+                UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) {
+
+            UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig);
+
+            //  wait for the server is ready
+            while (!server.isReady()) {
+                Thread.sleep(UnboundSSLUtils.DELAY);
+            }
+
+            // run a client
+            System.out.println("Successful connection is expected");
+            SSLClient.init(UnboundSSLUtils.HOST, server.getPort(),
+                    UnboundSSLUtils.TLS_KRB5_FILTER, service1host).connect();
+        }
+
+        System.out.println("Test passed");
+    }
+
+}
diff --git a/test/sun/security/krb5/auto/UnboundSSLPrincipalProperty.java b/test/sun/security/krb5/auto/UnboundSSLPrincipalProperty.java
new file mode 100644
index 0000000..3ccb4bf
--- /dev/null
+++ b/test/sun/security/krb5/auto/UnboundSSLPrincipalProperty.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedActionException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.security.auth.login.LoginException;
+
+/*
+ * @test
+ * @bug 8025123
+ * @summary Checks if an unbound server uses a service principal
+ *          from sun.security.krb5.principal system property if specified
+ * @run main/othervm UnboundSSLPrincipalProperty
+ *                              unbound.ssl.jaas.conf server_star
+ * @run main/othervm UnboundSSLPrincipalProperty
+ *                              unbound.ssl.jaas.conf server_multiple_principals
+ */
+public class UnboundSSLPrincipalProperty {
+
+    public static void main(String[] args) throws IOException,
+            NoSuchAlgorithmException,LoginException, PrivilegedActionException,
+            InterruptedException {
+        UnboundSSLPrincipalProperty test = new UnboundSSLPrincipalProperty();
+        test.start(args[0], args[1]);
+    }
+
+    public void start(String jaacConfigFile, String serverJaasConfig)
+            throws IOException, NoSuchAlgorithmException,LoginException,
+            PrivilegedActionException, InterruptedException {
+
+        // define principals
+        String service1host = "service1." + UnboundSSLUtils.HOST;
+        String service3host = "service3." + UnboundSSLUtils.HOST;
+        String service1Principal = "host/" + service1host + "@"
+                + UnboundSSLUtils.REALM;
+        String service3Principal = "host/" + service3host
+                + "@" + UnboundSSLUtils.REALM;
+
+        Map<String, String> principals = new HashMap<>();
+        principals.put(UnboundSSLUtils.USER_PRINCIPAL,
+                UnboundSSLUtils.USER_PASSWORD);
+        principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, null);
+        principals.put(service1Principal, null);
+        principals.put(service3Principal, null);
+
+        System.setProperty("java.security.krb5.conf",
+                UnboundSSLUtils.KRB5_CONF_FILENAME);
+
+        // start a local KDC instance
+        UnboundSSLUtils.startKDC(UnboundSSLUtils.REALM, principals,
+                UnboundSSLUtils.KTAB_FILENAME, UnboundSSLUtils.KtabMode.APPEND);
+
+        System.setProperty("java.security.auth.login.config",
+                UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile);
+        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+        // start an SSL server instance
+        try (final SSLEchoServer server = SSLEchoServer.init(
+                UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) {
+
+            // specify a service principal for the server
+            System.setProperty("sun.security.krb5.principal",
+                    service3Principal);
+
+            UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig);
+
+            // wait for the server is ready
+            while (!server.isReady()) {
+                Thread.sleep(UnboundSSLUtils.DELAY);
+            }
+
+            int port = server.getPort();
+
+            // connetion failure is expected
+            // since service3 principal was specified to use by the server
+            System.out.println("Connect: SNI hostname = " + service1host
+                    + ", connection failure is expected");
+            try {
+                SSLClient.init(UnboundSSLUtils.HOST, port,
+                        UnboundSSLUtils.TLS_KRB5_FILTER, service1host)
+                            .connect();
+                throw new RuntimeException("Test failed: "
+                        + "expected IOException not thrown");
+            } catch (IOException e) {
+                System.out.println("Expected exception: " + e);
+            }
+
+            System.out.println("Connect: SNI hostname = " + service3host
+                    + ", successful connection is expected");
+            SSLClient.init(UnboundSSLUtils.HOST, port,
+                    UnboundSSLUtils.TLS_KRB5_FILTER, service3host).connect();
+        }
+
+        System.out.println("Test passed");
+    }
+}
diff --git a/test/sun/security/krb5/auto/UnboundSSLUtils.java b/test/sun/security/krb5/auto/UnboundSSLUtils.java
new file mode 100644
index 0000000..ab55e1f
--- /dev/null
+++ b/test/sun/security/krb5/auto/UnboundSSLUtils.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIMatcher;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+/*
+ * Helper class for unbound krb5 tests.
+ */
+class UnboundSSLUtils {
+
+    static enum KtabMode { APPEND, EXISTING };
+
+    static final String KTAB_FILENAME = "krb5.keytab.data";
+    static final String HOST = "localhost";
+    static final String REALM = "TEST.REALM";
+    static final String KRBTGT_PRINCIPAL = "krbtgt/" + REALM;
+    static final String TEST_SRC = System.getProperty("test.src", ".");
+    static final String TLS_KRB5_FILTER = "TLS_KRB5";
+    static final String USER = "USER";
+    static final String USER_PASSWORD = "password";
+    static final String FS = System.getProperty("file.separator");
+    static final String SNI_PATTERN = ".*";
+    static final String USER_PRINCIPAL = USER + "@" + REALM;
+    static final String KRB5_CONF_FILENAME = "krb5.conf";
+    static final int DELAY = 1000;
+
+   static String[] filterStringArray(String[] src, String filter) {
+        return Arrays.stream(src).filter((item) -> item.startsWith(filter))
+                .toArray(size -> new String[size]);
+    }
+
+    /*
+     * The method does JAAS login,
+     * and runs an SSL server in the JAAS context.
+     */
+    static void startServerWithJaas(final SSLEchoServer server,
+            String config) throws LoginException, PrivilegedActionException {
+        LoginContext context = new LoginContext(config);
+        context.login();
+        System.out.println("Server: successful authentication");
+        Subject.doAs(context.getSubject(),
+                (PrivilegedExceptionAction<Object>) () -> {
+            SSLEchoServer.startServer(server);
+            return null;
+        });
+    }
+
+    /*
+     * Start a KDC server:
+     *   - create a KDC instance
+     *   - create Kerberos principals
+     *   - save Kerberos configuration
+     *   - save keys to keytab file
+     *   - no pre-auth required
+     */
+    static void startKDC(String realm, Map<String, String> principals,
+            String ktab, KtabMode mode) {
+        try {
+            KDC kdc = KDC.create(realm, HOST, 0, true);
+            kdc.setOption(KDC.Option.PREAUTH_REQUIRED, Boolean.FALSE);
+            if (principals != null) {
+                for (Map.Entry<String, String> entry : principals.entrySet()) {
+                    String name = entry.getKey();
+                    String password = entry.getValue();
+                    if (password == null || password.isEmpty()) {
+                        System.out.println("KDC: add a principal '" + name +
+                                "' with a random password");
+                        kdc.addPrincipalRandKey(name);
+                    } else {
+                        System.out.println("KDC: add a principal '" + name +
+                                "' with '" + password + "' password");
+                        kdc.addPrincipal(name, password.toCharArray());
+                    }
+                }
+            }
+
+            KDC.saveConfig(KRB5_CONF_FILENAME, kdc);
+
+            if (ktab != null) {
+                File ktabFile = new File(ktab);
+                if (mode == KtabMode.APPEND) {
+                    if (ktabFile.exists()) {
+                        System.out.println("KDC: append keys to an exising " +
+                                "keytab file " + ktab);
+                        kdc.appendKtab(ktab);
+                    } else {
+                        System.out.println("KDC: create a new keytab file " +
+                                ktab);
+                        kdc.writeKtab(ktab);
+                    }
+                } else if (mode == KtabMode.EXISTING) {
+                    System.out.println("KDC: use an existing keytab file "
+                            + ktab);
+                } else {
+                    throw new RuntimeException("KDC: unsupported keytab mode: "
+                            + mode);
+                }
+            }
+
+            System.out.println("KDC: started on " + HOST + ":" + kdc.getPort()
+                    + " with '" + realm + "' realm");
+        } catch (Exception e) {
+            throw new RuntimeException("KDC: unexpected exception", e);
+        }
+    }
+
+}
+
+class SSLClient {
+
+    private final static byte[][] arrays = {
+        new byte[] {-1, 0, 2},
+        new byte[] {}
+    };
+
+    private final SSLSocket socket;
+
+    private SSLClient(SSLSocket socket) {
+        this.socket = socket;
+    }
+
+    void connect() throws IOException {
+        System.out.println("Client: connect to server");
+        try (BufferedInputStream bis = new BufferedInputStream(
+                        socket.getInputStream());
+                BufferedOutputStream bos = new BufferedOutputStream(
+                        socket.getOutputStream())) {
+
+            for (byte[] bytes : arrays) {
+                System.out.println("Client: send byte array: "
+                        + Arrays.toString(bytes));
+
+                bos.write(bytes);
+                bos.flush();
+
+                byte[] recieved = new byte[bytes.length];
+                int read = bis.read(recieved, 0, bytes.length);
+                if (read < 0) {
+                    throw new IOException("Client: couldn't read a response");
+                }
+
+                System.out.println("Client: recieved byte array: "
+                        + Arrays.toString(recieved));
+
+                if (!Arrays.equals(bytes, recieved)) {
+                    throw new IOException("Client: sent byte array "
+                                + "is not equal with recieved byte array");
+                }
+            }
+            socket.getSession().invalidate();
+        } finally {
+            if (!socket.isClosed()) {
+                socket.close();
+            }
+        }
+    }
+
+    static SSLClient init(String host, int port, String cipherSuiteFilter,
+            String sniHostName) throws NoSuchAlgorithmException, IOException {
+        SSLContext sslContext = SSLContext.getDefault();
+        SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
+        SSLSocket socket = (SSLSocket) ssf.createSocket(host, port);
+        SSLParameters params = new SSLParameters();
+
+        if (cipherSuiteFilter != null) {
+            String[] cipherSuites = UnboundSSLUtils.filterStringArray(
+                    ssf.getSupportedCipherSuites(), cipherSuiteFilter);
+            System.out.println("Client: enabled cipher suites: "
+                    + Arrays.toString(cipherSuites));
+            params.setCipherSuites(cipherSuites);
+        }
+
+        if (sniHostName != null) {
+            System.out.println("Client: set SNI hostname: " + sniHostName);
+            SNIHostName serverName = new SNIHostName(sniHostName);
+            List<SNIServerName> serverNames = new ArrayList<>();
+            serverNames.add(serverName);
+            params.setServerNames(serverNames);
+        }
+
+        socket.setSSLParameters(params);
+
+        return new SSLClient(socket);
+    }
+
+}
+
+class SSLEchoServer implements Runnable, AutoCloseable {
+
+    private final SSLServerSocket ssocket;
+    private volatile boolean stopped = false;
+    private volatile boolean ready = false;
+
+    /*
+     * Starts the server in a separate thread.
+     */
+    static void startServer(SSLEchoServer server) {
+        Thread serverThread = new Thread(server, "SSL echo server thread");
+        serverThread.setDaemon(true);
+        serverThread.start();
+    }
+
+    private SSLEchoServer(SSLServerSocket ssocket) {
+        this.ssocket = ssocket;
+    }
+
+    /*
+     * Main server loop.
+     */
+    @Override
+    public void run() {
+        System.out.println("Server: started");
+        while (!stopped) {
+            ready = true;
+            try (SSLSocket socket = (SSLSocket) ssocket.accept()) {
+                System.out.println("Server: client connection accepted");
+                try (
+                    BufferedInputStream bis = new BufferedInputStream(
+                            socket.getInputStream());
+                    BufferedOutputStream bos = new BufferedOutputStream(
+                            socket.getOutputStream())
+                ) {
+                    byte[] buffer = new byte[1024];
+                    int read;
+                    while ((read = bis.read(buffer)) > 0) {
+                        bos.write(buffer, 0, read);
+                        System.out.println("Server: recieved " + read
+                                + " bytes: "
+                                + Arrays.toString(Arrays.copyOf(buffer, read)));
+                        bos.flush();
+                    }
+                }
+            } catch (IOException e) {
+                if (stopped) {
+                    // stopped == true means that stop() method was called,
+                    // so just ignore the exception, and finish the loop
+                    break;
+                }
+                System.out.println("Server: couldn't accept client connection: "
+                        + e);
+            }
+        }
+        System.out.println("Server: finished");
+    }
+
+    boolean isReady() {
+        return ready;
+    }
+
+    void stop() {
+        stopped = true;
+        ready = false;
+
+        // close the server socket to interupt accept() method
+        try {
+            if (!ssocket.isClosed()) {
+                ssocket.close();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Unexpected exception: " + e);
+        }
+    }
+
+    @Override
+    public void close() {
+        stop();
+    }
+
+    int getPort() {
+        return ssocket.getLocalPort();
+    }
+
+    /*
+     * Creates server instance.
+     *
+     * @param cipherSuiteFilter Filter for enabled cipher suites
+     * @param sniMatcherPattern Pattern for SNI server hame
+     */
+    static SSLEchoServer init(String cipherSuiteFilter,
+            String sniPattern) throws NoSuchAlgorithmException, IOException {
+        SSLContext context = SSLContext.getDefault();
+        SSLServerSocketFactory ssf =
+                (SSLServerSocketFactory) context.getServerSocketFactory();
+        SSLServerSocket ssocket =
+                (SSLServerSocket) ssf.createServerSocket(0);
+
+        // specify enabled cipher suites
+        if (cipherSuiteFilter != null) {
+            String[] ciphersuites = UnboundSSLUtils.filterStringArray(
+                    ssf.getSupportedCipherSuites(), cipherSuiteFilter);
+            System.out.println("Server: enabled cipher suites: "
+                    + Arrays.toString(ciphersuites));
+            ssocket.setEnabledCipherSuites(ciphersuites);
+        }
+
+        // specify SNI matcher pattern
+        if (sniPattern != null) {
+            System.out.println("Server: set SNI matcher: " + sniPattern);
+            SNIMatcher matcher = SNIHostName.createSNIMatcher(sniPattern);
+            List<SNIMatcher> matchers = new ArrayList<>();
+            matchers.add(matcher);
+            SSLParameters params = ssocket.getSSLParameters();
+            params.setSNIMatchers(matchers);
+            ssocket.setSSLParameters(params);
+        }
+
+        return new SSLEchoServer(ssocket);
+    }
+
+}
+
diff --git a/test/sun/security/krb5/auto/unbound.ssl.jaas.conf b/test/sun/security/krb5/auto/unbound.ssl.jaas.conf
new file mode 100644
index 0000000..bee0a67
--- /dev/null
+++ b/test/sun/security/krb5/auto/unbound.ssl.jaas.conf
@@ -0,0 +1,40 @@
+com.sun.net.ssl.client {
+    com.sun.security.auth.module.Krb5LoginModule required
+    principal="USER@TEST.REALM"
+    doNotPrompt=true
+    useKeyTab=true
+    keyTab="krb5.keytab.data";
+};
+
+server_star {
+    com.sun.security.auth.module.Krb5LoginModule required
+    principal="*"
+    isInitiator=false
+    useKeyTab=true
+    keyTab="krb5.keytab.data"
+    storeKey=true;
+};
+
+server_multiple_principals {
+    com.sun.security.auth.module.Krb5LoginModule required
+    principal="host/service1.localhost@TEST.REALM"
+    isInitiator=false
+    useKeyTab=true
+    keyTab="krb5.keytab.data"
+    storeKey=true;
+
+    com.sun.security.auth.module.Krb5LoginModule required
+    principal="host/service2.localhost@TEST.REALM"
+    isInitiator=false
+    useKeyTab=true
+    keyTab="krb5.keytab.data"
+    storeKey=true;
+
+    com.sun.security.auth.module.Krb5LoginModule required
+    principal="host/service3.localhost@TEST.REALM"
+    isInitiator=false
+    useKeyTab=true
+    keyTab="krb5.keytab.data"
+    storeKey=true;
+};
+
diff --git a/test/sun/security/krb5/auto/unbound.ssl.policy b/test/sun/security/krb5/auto/unbound.ssl.policy
new file mode 100644
index 0000000..0df839c
--- /dev/null
+++ b/test/sun/security/krb5/auto/unbound.ssl.policy
@@ -0,0 +1,29 @@
+grant {
+    permission java.util.PropertyPermission "*", "read,write";
+    permission java.net.SocketPermission "*:*", "listen,resolve,accept,connect";
+    permission java.io.FilePermission "*", "read,write,delete";
+    permission java.lang.RuntimePermission "accessDeclaredMembers";
+    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+    permission java.lang.RuntimePermission "accessClassInPackage.*";
+    permission javax.security.auth.AuthPermission "doAs";
+    permission javax.security.auth.AuthPermission "getSubject";
+    permission javax.security.auth.AuthPermission
+                    "createLoginContext.server_star";
+    permission javax.security.auth.AuthPermission
+                    "createLoginContext.server_multiple_principals";
+    permission javax.security.auth.AuthPermission "modifyPrincipals";
+    permission javax.security.auth.PrivateCredentialPermission "javax.security.auth.kerberos.KeyTab java.security.Principal \"krb5.keytab.data\"", "read";
+
+    // clients have a permission to use all service principals
+    permission javax.security.auth.kerberos.ServicePermission "*", "initiate";
+
+    // server has a service permission
+    // to accept only service1 and service3 principals
+    permission javax.security.auth.kerberos.ServicePermission
+                    "host/service1.localhost@TEST.REALM", "accept";
+    permission javax.security.auth.kerberos.ServicePermission
+                    "host/service3.localhost@TEST.REALM", "accept";
+
+    // permission to use SunJCE provider
+    permission java.security.SecurityPermission "putProviderProperty.SunJCE";
+};