From d32746ef4a0ce6fec558274244321991be141698 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Wed, 20 Mar 2024 21:25:41 +0000 Subject: [PATCH] =?UTF-8?q?8296244:=20Alternate=20implementation=20of=20us?= =?UTF-8?q?er-based=20authorization=20Subject=20APIs=20that=20doesn?= =?UTF-8?q?=E2=80=99t=20depend=20on=20Security=20Manager=20APIs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: mullan --- .../share/classes/java/lang/System.java | 5 + .../classes/javax/security/auth/Subject.java | 307 +++++++++++++----- .../jdk/internal/access/JavaLangAccess.java | 6 + .../management/monitor/ThreadPoolAccTest.java | 4 +- .../NotificationAccessControllerTest.java | 4 +- .../NonJMXPrincipalsTest.java | 4 +- .../PasswordAccessFileTest.java | 4 +- .../passwordAuthenticator/RMIAltAuthTest.java | 2 +- .../RMIPasswdAuthTest.java | 2 +- .../SubjectDelegation1Test.java | 14 +- .../security/AuthorizationTest.java | 8 +- .../auth/Subject/CallAsWithScopedValue.java | 106 ++++++ .../javax/security/auth/Subject/Compat.java | 128 ++++++++ .../security/auth/Subject/CurrentSubject.java | 56 ++-- .../javax/security/auth/Subject/FromACC.java | 3 +- .../security/auth/Subject/UnsupportedSV.java | 73 +++++ .../jmxremote/bootstrap/RmiBootstrapTest.java | 6 +- 17 files changed, 591 insertions(+), 141 deletions(-) create mode 100644 test/jdk/javax/security/auth/Subject/CallAsWithScopedValue.java create mode 100644 test/jdk/javax/security/auth/Subject/Compat.java create mode 100644 test/jdk/javax/security/auth/Subject/UnsupportedSV.java diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index bd77a3149ae..d6a0faa4200 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2690,6 +2690,11 @@ public final class System { public boolean bytesCompatible(String string, Charset charset) { return string.bytesCompatible(charset); } + + @Override + public boolean allowSecurityManager() { + return System.allowSecurityManager(); + } }); } } diff --git a/src/java.base/share/classes/javax/security/auth/Subject.java b/src/java.base/share/classes/javax/security/auth/Subject.java index 60e6d5fe30e..203c0d163fd 100644 --- a/src/java.base/share/classes/javax/security/auth/Subject.java +++ b/src/java.base/share/classes/javax/security/auth/Subject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, 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 @@ -35,6 +35,7 @@ import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.CompletionException; +import jdk.internal.access.SharedSecrets; import sun.security.util.ResourcesMgr; /** @@ -90,6 +91,41 @@ import sun.security.util.ResourcesMgr; * {@code Principal} implementations associated with Subjects * must implement {@code Serializable}. * + *

Deprecated Methods and Replacements

+ * + *

The following methods in this class for user-based authorization + * that are dependent on Security Manager APIs are deprecated for removal: + *

+ * Methods {@link #current()} and {@link #callAs(Subject, Callable)} + * are replacements for these methods, where {@code current} + * is mostly equivalent to {@code getSubject(AccessController.getContext())} + * and {@code callAs} is similar to {@code doAs} except that the + * input type and exceptions thrown are slightly different. + * + *

These methods behave differently depending on + * whether a security manager is + * allowed or disallowed: + *

+ * * @since 1.4 * @see java.security.Principal * @see java.security.DomainCombiner @@ -258,7 +294,9 @@ public final class Subject implements java.io.Serializable { /** * Get the {@code Subject} associated with the provided - * {@code AccessControlContext}. + * {@code AccessControlContext}. This method is intended to be used with + * a security manager. It throws an {@code UnsupportedOperationException} + * if a security manager is not allowed. * *

The {@code AccessControlContext} may contain many * Subjects (from nested {@code doAs} calls). @@ -273,6 +311,9 @@ public final class Subject implements java.io.Serializable { * if no {@code Subject} is associated * with the provided {@code AccessControlContext}. * + * @throws UnsupportedOperationException if a security manager is + * not allowed + * * @throws SecurityException if a security manager is installed and the * caller does not have an * {@link AuthPermission#AuthPermission(String) @@ -302,36 +343,45 @@ public final class Subject implements java.io.Serializable { Objects.requireNonNull(acc, ResourcesMgr.getString ("invalid.null.AccessControlContext.provided")); - // return the Subject from the DomainCombiner of the provided context - return AccessController.doPrivileged - (new java.security.PrivilegedAction<>() { - public Subject run() { - DomainCombiner dc = acc.getDomainCombiner(); - if (!(dc instanceof SubjectDomainCombiner)) { - return null; - } - SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc; - return sdc.getSubject(); - } - }); + if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) { + throw new UnsupportedOperationException( + "getSubject is supported only if a security manager is allowed"); + } else { + // return the Subject from the DomainCombiner of the provided context + return AccessController.doPrivileged + (new java.security.PrivilegedAction<>() { + public Subject run() { + DomainCombiner dc = acc.getDomainCombiner(); + if (!(dc instanceof SubjectDomainCombiner)) { + return null; + } + SubjectDomainCombiner sdc = (SubjectDomainCombiner) dc; + return sdc.getSubject(); + } + }); + } } + private static final ScopedValue SCOPED_SUBJECT = + ScopedValue.newInstance(); + /** * Returns the current subject. - *

- * The current subject is installed by the {@link #callAs} method. + * + *

The current subject is installed by the {@link #callAs} method. * When {@code callAs(subject, action)} is called, {@code action} is * executed with {@code subject} as its current subject which can be * retrieved by this method. After {@code action} is finished, the current * subject is reset to its previous value. The current * subject is {@code null} before the first call of {@code callAs()}. * - * @implNote - * This method returns the same value as - * {@code Subject.getSubject(AccessController.getContext())}. This - * preserves compatibility with code that may still be calling {@code doAs} - * which installs the subject in an {@code AccessControlContext}. This - * behavior is subject to change in a future version. + *

If a security manager is allowed, this + * method is equivalent to calling {@link #getSubject} with the current + * {@code AccessControlContext}. + * + *

If a security manager is not allowed, this method returns the + * {@code Subject} bound to the period of the execution of the current + * thread. * * @return the current subject, or {@code null} if a current subject is * not installed or the current subject is set to {@code null}. @@ -340,23 +390,32 @@ public final class Subject implements java.io.Serializable { */ @SuppressWarnings("removal") public static Subject current() { - return getSubject(AccessController.getContext()); + if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) { + return SCOPED_SUBJECT.orElse(null); + } else { + return getSubject(AccessController.getContext()); + } } /** * Executes a {@code Callable} with {@code subject} as the * current subject. * - * @implNote - * This method calls {@link #doAs(Subject, PrivilegedExceptionAction) - * Subject.doAs(subject, altAction)} which stores the subject in - * a new {@code AccessControlContext}, where {@code altAction.run()} - * is equivalent to {@code action.call()} and the exception thrown is - * modified to match the specification of this method. This preserves - * compatibility with code that may still be calling - * {@code getSubject(AccessControlContext)} which retrieves the subject - * from an {@code AccessControlContext}. This behavior is subject - * to change in a future version. + *

If a security manager is allowed, + * this method first retrieves the current Thread's + * {@code AccessControlContext} via + * {@code AccessController.getContext}, + * and then instantiates a new {@code AccessControlContext} + * using the retrieved context along with a new + * {@code SubjectDomainCombiner} (constructed using + * the provided {@code Subject}). + * Finally, this method invokes {@code AccessController.doPrivileged}, + * passing it the provided {@code PrivilegedAction}, + * as well as the newly constructed {@code AccessControlContext}. + * + *

If a security manager is not allowed, + * this method launches {@code action} and binds {@code subject} to the + * period of its execution. * * @param subject the {@code Subject} that the specified {@code action} * will run as. This parameter may be {@code null}. @@ -375,22 +434,31 @@ public final class Subject implements java.io.Serializable { public static T callAs(final Subject subject, final Callable action) throws CompletionException { Objects.requireNonNull(action); - try { - PrivilegedExceptionAction pa = () -> action.call(); - @SuppressWarnings("removal") - var result = doAs(subject, pa); - return result; - } catch (PrivilegedActionException e) { - throw new CompletionException(e.getCause()); - } catch (Exception e) { - throw new CompletionException(e); + if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) { + try { + return ScopedValue.callWhere(SCOPED_SUBJECT, subject, action); + } catch (Exception e) { + throw new CompletionException(e); + } + } else { + try { + PrivilegedExceptionAction pa = () -> action.call(); + @SuppressWarnings("removal") + var result = doAs(subject, pa); + return result; + } catch (PrivilegedActionException e) { + throw new CompletionException(e.getCause()); + } catch (Exception e) { + throw new CompletionException(e); + } } } /** * Perform work as a particular {@code Subject}. * - *

This method first retrieves the current Thread's + *

If a security manager is allowed, + * this method first retrieves the current Thread's * {@code AccessControlContext} via * {@code AccessController.getContext}, * and then instantiates a new {@code AccessControlContext} @@ -401,6 +469,10 @@ public final class Subject implements java.io.Serializable { * passing it the provided {@code PrivilegedAction}, * as well as the newly constructed {@code AccessControlContext}. * + *

If a security manager is not allowed, + * this method launches {@code action} and binds {@code subject} to the + * period of its execution. + * * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. @@ -444,20 +516,36 @@ public final class Subject implements java.io.Serializable { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); - // set up the new Subject-based AccessControlContext - // for doPrivileged - final AccessControlContext currentAcc = AccessController.getContext(); + if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) { + try { + return callAs(subject, action::run); + } catch (CompletionException ce) { + var cause = ce.getCause(); + if (cause instanceof RuntimeException re) { + throw re; + } else if (cause instanceof Error er) { + throw er; + } else { + throw new AssertionError(ce); + } + } + } else { + // set up the new Subject-based AccessControlContext + // for doPrivileged + final AccessControlContext currentAcc = AccessController.getContext(); - // call doPrivileged and push this new context on the stack - return java.security.AccessController.doPrivileged - (action, - createContext(subject, currentAcc)); + // call doPrivileged and push this new context on the stack + return java.security.AccessController.doPrivileged + (action, + createContext(subject, currentAcc)); + } } /** * Perform work as a particular {@code Subject}. * - *

This method first retrieves the current Thread's + *

If a security manager is allowed, + * this method first retrieves the current Thread's * {@code AccessControlContext} via * {@code AccessController.getContext}, * and then instantiates a new {@code AccessControlContext} @@ -468,6 +556,10 @@ public final class Subject implements java.io.Serializable { * passing it the provided {@code PrivilegedExceptionAction}, * as well as the newly constructed {@code AccessControlContext}. * + *

If a security manager is not allowed, + * this method launches {@code action} and binds {@code subject} to the + * period of its execution. + * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. @@ -517,19 +609,37 @@ public final class Subject implements java.io.Serializable { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); - // set up the new Subject-based AccessControlContext for doPrivileged - final AccessControlContext currentAcc = AccessController.getContext(); + if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) { + try { + return callAs(subject, action::run); + } catch (CompletionException ce) { + var cause = ce.getCause(); + if (cause instanceof RuntimeException re) { + throw re; + } else if (cause instanceof Error er) { + throw er; + } else if (cause instanceof Exception e) { + throw new PrivilegedActionException(e); + } else { + throw new PrivilegedActionException(ce); + } + } + } else { + // set up the new Subject-based AccessControlContext for doPrivileged + final AccessControlContext currentAcc = AccessController.getContext(); - // call doPrivileged and push this new context on the stack - return java.security.AccessController.doPrivileged - (action, - createContext(subject, currentAcc)); + // call doPrivileged and push this new context on the stack + return java.security.AccessController.doPrivileged + (action, + createContext(subject, currentAcc)); + } } /** * Perform privileged work as a particular {@code Subject}. * - *

This method behaves exactly as {@code Subject.doAs}, + *

If a security manager is allowed, + * this method behaves exactly as {@code Subject.doAs}, * except that instead of retrieving the current Thread's * {@code AccessControlContext}, it uses the provided * {@code AccessControlContext}. If the provided @@ -537,6 +647,10 @@ public final class Subject implements java.io.Serializable { * this method instantiates a new {@code AccessControlContext} * with an empty collection of ProtectionDomains. * + *

If a security manager is not allowed, + * this method ignores the {@code acc} argument, launches {@code action}, + * and binds {@code subject} to the period of its execution. + * * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. @@ -583,23 +697,39 @@ public final class Subject implements java.io.Serializable { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); - // set up the new Subject-based AccessControlContext - // for doPrivileged - final AccessControlContext callerAcc = - (acc == null ? - new AccessControlContext(NULL_PD_ARRAY) : - acc); + if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) { + try { + return callAs(subject, action::run); + } catch (CompletionException ce) { + var cause = ce.getCause(); + if (cause instanceof RuntimeException re) { + throw re; + } else if (cause instanceof Error er) { + throw er; + } else { + throw new AssertionError(ce); + } + } + } else { + // set up the new Subject-based AccessControlContext + // for doPrivileged + final AccessControlContext callerAcc = + (acc == null ? + new AccessControlContext(NULL_PD_ARRAY) : + acc); - // call doPrivileged and push this new context on the stack - return java.security.AccessController.doPrivileged - (action, - createContext(subject, callerAcc)); + // call doPrivileged and push this new context on the stack + return java.security.AccessController.doPrivileged + (action, + createContext(subject, callerAcc)); + } } /** * Perform privileged work as a particular {@code Subject}. * - *

This method behaves exactly as {@code Subject.doAs}, + *

If a security manager is allowed, + * this method behaves exactly as {@code Subject.doAs}, * except that instead of retrieving the current Thread's * {@code AccessControlContext}, it uses the provided * {@code AccessControlContext}. If the provided @@ -607,6 +737,10 @@ public final class Subject implements java.io.Serializable { * this method instantiates a new {@code AccessControlContext} * with an empty collection of ProtectionDomains. * + *

If a security manager is not allowed, + * this method ignores the {@code acc} argument, launches {@code action}, + * and binds {@code subject} to the period of its execution. + * * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. @@ -659,16 +793,33 @@ public final class Subject implements java.io.Serializable { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); - // set up the new Subject-based AccessControlContext for doPrivileged - final AccessControlContext callerAcc = - (acc == null ? - new AccessControlContext(NULL_PD_ARRAY) : - acc); + if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) { + try { + return callAs(subject, action::run); + } catch (CompletionException ce) { + var cause = ce.getCause(); + if (cause instanceof RuntimeException re) { + throw re; + } else if (cause instanceof Error er) { + throw er; + } else if (cause instanceof Exception e) { + throw new PrivilegedActionException(e); + } else { + throw new PrivilegedActionException(ce); + } + } + } else { + // set up the new Subject-based AccessControlContext for doPrivileged + final AccessControlContext callerAcc = + (acc == null ? + new AccessControlContext(NULL_PD_ARRAY) : + acc); - // call doPrivileged and push this new context on the stack - return java.security.AccessController.doPrivileged - (action, - createContext(subject, callerAcc)); + // call doPrivileged and push this new context on the stack + return java.security.AccessController.doPrivileged + (action, + createContext(subject, callerAcc)); + } } @SuppressWarnings("removal") diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 86fd4c8414d..c6200520fdd 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -592,4 +592,10 @@ public interface JavaLangAccess { * Are the string bytes compatible with the given charset? */ boolean bytesCompatible(String string, Charset charset); + + /** + * Is a security manager already set or allowed to be set + * (using -Djava.security.manager=allow)? + */ + boolean allowSecurityManager(); } diff --git a/test/jdk/javax/management/monitor/ThreadPoolAccTest.java b/test/jdk/javax/management/monitor/ThreadPoolAccTest.java index 8218a58b410..542fa9c657c 100644 --- a/test/jdk/javax/management/monitor/ThreadPoolAccTest.java +++ b/test/jdk/javax/management/monitor/ThreadPoolAccTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, 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 @@ -30,7 +30,7 @@ * * @run clean ThreadPoolAccTest * @run build ThreadPoolAccTest - * @run main ThreadPoolAccTest + * @run main/othervm -Djava.security.manager=allow ThreadPoolAccTest */ import java.security.AccessController; diff --git a/test/jdk/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java b/test/jdk/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java index 99e5460325c..352faea27d1 100644 --- a/test/jdk/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java +++ b/test/jdk/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, 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 @@ -30,7 +30,7 @@ * java.management/com.sun.jmx.remote.security * @run clean NotificationAccessControllerTest * @run build NotificationAccessControllerTest - * @run main NotificationAccessControllerTest + * @run main/othervm -Djava.security.manager=allow NotificationAccessControllerTest */ import com.sun.jmx.remote.security.NotificationAccessController; diff --git a/test/jdk/javax/management/remote/mandatory/passwordAccessFile/NonJMXPrincipalsTest.java b/test/jdk/javax/management/remote/mandatory/passwordAccessFile/NonJMXPrincipalsTest.java index 2726120e253..f9e04a373f6 100644 --- a/test/jdk/javax/management/remote/mandatory/passwordAccessFile/NonJMXPrincipalsTest.java +++ b/test/jdk/javax/management/remote/mandatory/passwordAccessFile/NonJMXPrincipalsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, 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 @@ -30,7 +30,7 @@ * * @run clean NonJMXPrincipalsTest SimpleStandard SimpleStandardMBean * @run build NonJMXPrincipalsTest SimpleStandard SimpleStandardMBean - * @run main NonJMXPrincipalsTest + * @run main/othervm -Djava.security.manager=allow NonJMXPrincipalsTest */ import java.io.File; diff --git a/test/jdk/javax/management/remote/mandatory/passwordAccessFile/PasswordAccessFileTest.java b/test/jdk/javax/management/remote/mandatory/passwordAccessFile/PasswordAccessFileTest.java index 5e794366b3a..d073553d3f5 100644 --- a/test/jdk/javax/management/remote/mandatory/passwordAccessFile/PasswordAccessFileTest.java +++ b/test/jdk/javax/management/remote/mandatory/passwordAccessFile/PasswordAccessFileTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -30,7 +30,7 @@ * * @run clean PasswordAccessFileTest SimpleStandard SimpleStandardMBean * @run build PasswordAccessFileTest SimpleStandard SimpleStandardMBean - * @run main PasswordAccessFileTest + * @run main/othervm -Djava.security.manager=allow PasswordAccessFileTest */ import java.io.File; diff --git a/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIAltAuthTest.java b/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIAltAuthTest.java index 03a69735ae4..e86f9cc8f1a 100644 --- a/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIAltAuthTest.java +++ b/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIAltAuthTest.java @@ -30,7 +30,7 @@ * java.management/com.sun.jmx.remote.security * @run clean RMIAltAuthTest * @run build RMIAltAuthTest SimpleStandard SimpleStandardMBean - * @run main RMIAltAuthTest + * @run main/othervm -Djava.security.manager=allow RMIAltAuthTest */ import java.io.File; diff --git a/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIPasswdAuthTest.java b/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIPasswdAuthTest.java index 45b10bfb3ec..87384e070cd 100644 --- a/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIPasswdAuthTest.java +++ b/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/RMIPasswdAuthTest.java @@ -30,7 +30,7 @@ * java.management/com.sun.jmx.remote.security * @run clean RMIPasswdAuthTest * @run build RMIPasswdAuthTest SimpleStandard SimpleStandardMBean - * @run main RMIPasswdAuthTest + * @run main/othervm -Djava.security.manager=allow RMIPasswdAuthTest */ import java.io.File; diff --git a/test/jdk/javax/management/remote/mandatory/subjectDelegation/SubjectDelegation1Test.java b/test/jdk/javax/management/remote/mandatory/subjectDelegation/SubjectDelegation1Test.java index 77bae21438c..31228b5d030 100644 --- a/test/jdk/javax/management/remote/mandatory/subjectDelegation/SubjectDelegation1Test.java +++ b/test/jdk/javax/management/remote/mandatory/subjectDelegation/SubjectDelegation1Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -31,12 +31,12 @@ * java.management/com.sun.jmx.remote.security * @run clean SubjectDelegation1Test SimpleStandard SimpleStandardMBean * @run build SubjectDelegation1Test SimpleStandard SimpleStandardMBean - * @run main SubjectDelegation1Test policy11 ok - * @run main SubjectDelegation1Test policy12 ko - * @run main SubjectDelegation1Test policy13 ko - * @run main SubjectDelegation1Test policy14 ko - * @run main SubjectDelegation1Test policy15 ok - * @run main SubjectDelegation1Test policy16 ko + * @run main/othervm -Djava.security.manager=allow SubjectDelegation1Test policy11 ok + * @run main/othervm -Djava.security.manager=allow SubjectDelegation1Test policy12 ko + * @run main/othervm -Djava.security.manager=allow SubjectDelegation1Test policy13 ko + * @run main/othervm -Djava.security.manager=allow SubjectDelegation1Test policy14 ko + * @run main/othervm -Djava.security.manager=allow SubjectDelegation1Test policy15 ok + * @run main/othervm -Djava.security.manager=allow SubjectDelegation1Test policy16 ko */ import com.sun.jmx.remote.security.JMXPluggableAuthenticator; diff --git a/test/jdk/javax/management/security/AuthorizationTest.java b/test/jdk/javax/management/security/AuthorizationTest.java index 4eade30aa50..2c48dbffb56 100644 --- a/test/jdk/javax/management/security/AuthorizationTest.java +++ b/test/jdk/javax/management/security/AuthorizationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -29,9 +29,9 @@ * @modules java.management.rmi * @library /test/lib * @compile Simple.java - * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials - * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException - * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300 -Djava.security.manager=allow -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials + * @run main/othervm/timeout=300 -Djava.security.manager=allow -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300 -Djava.security.manager=allow -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedGetException * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException diff --git a/test/jdk/javax/security/auth/Subject/CallAsWithScopedValue.java b/test/jdk/javax/security/auth/Subject/CallAsWithScopedValue.java new file mode 100644 index 00000000000..b1d80eb1705 --- /dev/null +++ b/test/jdk/javax/security/auth/Subject/CallAsWithScopedValue.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024, 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 8296244 + * @enablePreview + * @summary Implement Subject.current and Subject.callAs using scoped values. + * Need enablePreview to use StructuredTaskScope. + * @run main/othervm -Djava.security.manager=allow CallAsWithScopedValue false + * @run main/othervm -Djava.security.manager=disallow CallAsWithScopedValue true + */ +import com.sun.security.auth.UserPrincipal; + +import javax.security.auth.Subject; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.StructuredTaskScope; + +public class CallAsWithScopedValue { + + private static Map results = new ConcurrentHashMap(); + + public static void main(String[] args) throws Exception { + + boolean usv = Boolean.parseBoolean(args[0]); + + Subject subject = new Subject(); + subject.getPrincipals().add(new UserPrincipal("Duke")); + + // Always observable in the same thread + Subject.callAs(subject, () -> check(0, Subject.current(), "Duke")); + + // Observable in a new platform thread in ACC mode, but not in the SV mode + Subject.callAs(subject, () -> { + Thread.ofPlatform().start(() -> check(1, Subject.current(), usv ? null : "Duke")).join(); + return null; + }); + + // Never observable in a new virtual thread + Subject.callAs(subject, () -> { + Thread.ofVirtual().start(() -> check(2, Subject.current(), null)).join(); + return null; + }); + + // Observable in structured concurrency in SV mode, but not in ACC mode + Subject.callAs(subject, () -> { + try (var scope = new StructuredTaskScope<>()) { + scope.fork(() -> check(3, Subject.current(), usv ? "Duke" : null)); + scope.join(); + } + return null; + }); + + // Suggested way to pass the current subject into arbitrary + // threads. Grab one using current() and explicitly pass it + // into the new thread. + Subject.callAs(subject, () -> { + Subject current = Subject.current(); + Thread.ofPlatform().start(() -> { + Subject.callAs(current, () -> check(4, Subject.current(), "Duke")); + }).join(); + return null; + }); + + if (results.size() != 5 || results.containsValue(false)) { + System.out.println(results); + throw new RuntimeException("Failed"); + } + } + + static String check(int type, Subject current, String expected) { + String actual; + if (current == null) { + actual = null; + } else { + var set = current.getPrincipals(UserPrincipal.class); + actual = set.isEmpty() + ? null + : set.iterator().next().getName(); + } + results.put(type, Objects.equals(actual, expected)); + return actual; + } +} diff --git a/test/jdk/javax/security/auth/Subject/Compat.java b/test/jdk/javax/security/auth/Subject/Compat.java new file mode 100644 index 00000000000..e95a9cba5ed --- /dev/null +++ b/test/jdk/javax/security/auth/Subject/Compat.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, 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 com.sun.security.auth.UserPrincipal; + +import javax.security.auth.Subject; +import javax.security.auth.SubjectDomainCombiner; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.util.Objects; + +/* + * @test + * @bug 8296244 + * @run main/othervm -Djava.security.manager=allow Compat + * @summary ensures the old implementation still works when SM is allowed + */ +public class Compat { + + static PrivilegedExceptionAction action + = () -> AccessController.getContext(); + + static boolean failed = false; + + public static void main(String[] args) throws Exception { + main0(null); + var t = new Thread(() -> { + try { + main0(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + t.start(); + t.join(); + } + public static void main0(String[] args) throws Exception { + System.out.println(">>> bare run"); + run(null); + System.out.println(">>> run inside"); + Subject subject = makeSubject("three"); + Subject.doAs(subject, (PrivilegedExceptionAction) + () -> run("three")); + if (failed) { + throw new RuntimeException(); + } + } + + public static Void run(String from) throws Exception { + Subject subject = makeSubject("one"); + var a1 = Subject.doAs(subject, action); + Subject subject2 = makeSubject("two"); + var a2 = Subject.doAs(subject2, action); + + test("from ether", AccessController.getContext(), from); + test("from a1", a1, "one"); + test("from a2", a2, "two"); + + var a3 = Subject.doAsPrivileged(subject, action, a1); + test("doAsPriv with one and a1", a3, "one"); + + var a4 = Subject.doAsPrivileged(subject, action, a2); + test("doAsPriv with one and a2", a4, "one"); + + var a5 = Subject.doAsPrivileged(null, action, a2); + test("doAsPriv with null and a2", a5, null); + + var a6 = Subject.doAs(null, action); + test("doAsPriv with null and this", a6, null); + + var ax = new AccessControlContext(a2, new SubjectDomainCombiner(subject)); + test("a2 plus subject", ax, "one"); + + ax = AccessController.doPrivileged(action, a2); + test("doPriv on a2", ax, "two"); + + ax = AccessController.doPrivilegedWithCombiner(action); + test("doPrivWC", ax, from == null ? null : from); + + ax = AccessController.doPrivilegedWithCombiner(action, a2); + test("doPrivWC on a2", ax, from == null ? "two" : from); + return null; + } + + static Subject makeSubject(String name) { + Subject subject = new Subject(); + subject.getPrincipals().add(new UserPrincipal(name)); + return subject; + } + + static String getSubject(AccessControlContext acc) { + var subj = Subject.getSubject(acc); + if (subj == null) return null; + var princ = subj.getPrincipals(UserPrincipal.class); + return (princ == null || princ.isEmpty()) + ? null + : princ.iterator().next().getName(); + } + + static void test(String label, AccessControlContext acc, String expected) { + var actual = getSubject(acc); + System.out.println(label + ": " + actual); + if (!Objects.equals(actual, expected)) { + System.out.println(" Expect " + expected + ", but see " + actual); + failed = true; + } + } +} diff --git a/test/jdk/javax/security/auth/Subject/CurrentSubject.java b/test/jdk/javax/security/auth/Subject/CurrentSubject.java index 67e19012d1f..295f8aa76f5 100644 --- a/test/jdk/javax/security/auth/Subject/CurrentSubject.java +++ b/test/jdk/javax/security/auth/Subject/CurrentSubject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -22,28 +22,24 @@ */ import javax.security.auth.Subject; -import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; +import java.security.PrivilegedExceptionAction; /* * @test * @bug 8267108 * @summary confirm current subject specification - * @run main/othervm CurrentSubject + * @run main/othervm -Djava.security.manager=allow CurrentSubject + * @run main/othervm -Djava.security.manager=disallow CurrentSubject */ public class CurrentSubject { - static transient boolean failed = false; - static CountDownLatch cl = new CountDownLatch(1); - static AtomicInteger count = new AtomicInteger(); + static boolean failed = false; public static void main(String[] args) throws Exception { // At the beginning, current subject is null test("", null); - cl.await(); if (failed) { throw new Exception("Failed"); } @@ -57,12 +53,6 @@ public class CurrentSubject { */ synchronized static void check(String label, Subject expected) { Subject cas = Subject.current(); - Subject accs = Subject.getSubject(AccessController.getContext()); - if (cas != accs) { - failed = true; - System.out.println(label + ": current " + s2s(cas) - + " but getSubject is " + s2s(accs)); - } Subject interested = cas; if (interested != expected) { failed = true; @@ -89,31 +79,23 @@ public class CurrentSubject { // run with a new subject, inside current subject will be the new subject Subject.callAs(another, () -> test(name + 'c', another)); Subject.doAs(another, (PrivilegedAction) () -> test(name + 'd', another)); + Subject.doAsPrivileged(another, (PrivilegedAction) () -> test(name + 'e', another), null); + try { + Subject.doAs(another, (PrivilegedExceptionAction) () -> test(name + 'f', another)); + Subject.doAsPrivileged(another, (PrivilegedExceptionAction) () -> test(name + 'g', another), null); + } catch (Exception e) { + throw new RuntimeException(e); + } // run with null, inside current subject will be null Subject.callAs(null, () -> test(name + 'C', null)); Subject.doAs(null, (PrivilegedAction) () -> test(name + 'D', null)); - // new thread, inside current subject is unchanged - count.incrementAndGet(); - new Thread(() -> { - try { - test(name + 't', expected); - try { - Thread.sleep(500); - } catch (Exception e) { - throw new AssertionError(e); - } - // by this time, parent thread should have exited the - // action and current subject reset, but here - // current subject unchanged. - test(name + 'T', expected); - } finally { - var n = count.decrementAndGet(); - if (n == 0) { - cl.countDown(); - } - assert n >= 0; - } - }).start(); + Subject.doAsPrivileged(null, (PrivilegedAction) () -> test(name + 'E', null), null); + try { + Subject.doAs(null, (PrivilegedExceptionAction) () -> test(name + 'F', null)); + Subject.doAsPrivileged(null, (PrivilegedExceptionAction) () -> test(name + 'G', null), null); + } catch (Exception e) { + throw new RuntimeException(e); + } } // Now it's reset to original check(" ".repeat(name.length()) + "<- " + name, expected); diff --git a/test/jdk/javax/security/auth/Subject/FromACC.java b/test/jdk/javax/security/auth/Subject/FromACC.java index 07216cb228b..5bb45f0f923 100644 --- a/test/jdk/javax/security/auth/Subject/FromACC.java +++ b/test/jdk/javax/security/auth/Subject/FromACC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -32,7 +32,6 @@ import java.security.PrivilegedAction; * @bug 8267108 * @summary confirm current installed subject specification * @run main/othervm -Djava.security.manager=allow FromACC - * @run main/othervm -Djava.security.manager=disallow FromACC */ public class FromACC { public static void main(String[] args) throws Exception { diff --git a/test/jdk/javax/security/auth/Subject/UnsupportedSV.java b/test/jdk/javax/security/auth/Subject/UnsupportedSV.java new file mode 100644 index 00000000000..6fb18e1e986 --- /dev/null +++ b/test/jdk/javax/security/auth/Subject/UnsupportedSV.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024, 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 8296244 + * @library /test/lib + * @summary Implement Subject.current and Subject.callAs using scoped values + * @run main/othervm -Djava.security.manager=disallow UnsupportedSV t1 + * @run main/othervm -Djava.security.manager=allow UnsupportedSV t2 + */ +import com.sun.security.auth.UserPrincipal; +import jdk.test.lib.Utils; + +import javax.security.auth.Subject; +import javax.security.auth.SubjectDomainCombiner; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public class UnsupportedSV { + public static void main(String[] args) throws Exception { + switch (args[0]) { + case "t1" -> t1(); + case "t2" -> t2(); + } + } + + // ScopedValue-based implementation is used + static void t1() throws Exception { + AccessControlContext acc = AccessController.getContext(); + Utils.runAndCheckException(() -> Subject.getSubject(acc), + UnsupportedOperationException.class); + + Subject s = new Subject(); + s.getPrincipals().add(new UserPrincipal("Duke")); + + // TODO: Still has no way to reject the following code. + // Here, AccessController::getContext returns a plain ACC without + // the subject inside. + AccessControlContext acc2 = Subject.callAs(s, AccessController::getContext); + Subject ns = AccessController.doPrivileged( + (PrivilegedAction) Subject::current, acc2); + System.out.println(ns); + } + + // When a security manager is set, ScopedValue-based implementation + // will not be used + static void t2() { + AccessControlContext acc = AccessController.getContext(); + Subject.getSubject(acc); + } +} diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java index 298431db90a..770b642088a 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -61,7 +61,7 @@ import java.util.Set; * * @library /test/lib * - * @run main/othervm/timeout=300 RmiBootstrapTest .*_test.*.in + * @run main/othervm/timeout=300 -Djava.security.manager=allow RmiBootstrapTest .*_test.*.in * */ /* @@ -72,7 +72,7 @@ import java.util.Set; * * @library /test/lib * - * @run main/othervm/timeout=300 RmiBootstrapTest .*_ssltest.*.in + * @run main/othervm/timeout=300 -Djava.security.manager=allow RmiBootstrapTest .*_ssltest.*.in * */ /**