This is an automated email from the ASF dual-hosted git repository. twolf pushed a commit to branch dev_3.0 in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 55201823e04aa21a6aa874d8d1d2b4e85b1bf8fd Author: Thomas Wolf <tw...@apache.org> AuthorDate: Sat Apr 26 23:12:54 2025 +0200 GH-739: prepare code to run with or without SecurityManager Tuck away all references to SecurityManager, AccessController and related classes like PrivilegeAction, PrivilegeExceptionAction, and PrivilegeActionException in a single PrivilegedOperations class that uses these entities only if they exist at all and otherwise just executes the code directly. --- .../common/util/security/PrivilegedOperations.java | 149 +++++++++++++++++++++ .../common/util/threads/SshdThreadFactory.java | 20 +-- .../sshd/common/io/nio2/Nio2CompletionHandler.java | 14 +- 3 files changed, 160 insertions(+), 23 deletions(-) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/PrivilegedOperations.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/PrivilegedOperations.java new file mode 100644 index 000000000..c304aee4f --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/PrivilegedOperations.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sshd.common.util.security; + +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; + +/** + * A wrapper around AccessController so that our code can work on JREs that do or do not have it. + * + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public final class PrivilegedOperations { + + private PrivilegedOperations() { + throw new IllegalStateException("No instantiation of class PrivilegedOperations"); + } + + public static class PrivilegeException extends Exception { + + private static final long serialVersionUID = 945792544549913161L; + + PrivilegeException(Throwable cause) { + super(cause); + } + } + + public static ThreadGroup getPrivilegedThreadGroup() { + if (HasSecurity.withSecurityManager()) { + return HasSecurity.getPrivilegedThreadGroup(); + } + return null; + } + + public static void doPrivileged(Runnable action) { + if (HasSecurity.isAvailable()) { + HasSecurity.doPrivileged(action); + } else { + action.run(); + } + } + + public static <T> T doPrivileged(Callable<? extends T> action) throws PrivilegeException { + if (HasSecurity.isAvailable()) { + return HasSecurity.doPrivileged(action); + } + try { + return action.call(); + } catch (Exception e) { + throw new PrivilegeException(e); + } + } + + public static <T> T doPrivilegedConditional(Callable<? extends T> action) throws PrivilegeException { + if (HasSecurity.withSecurityManager()) { + return doPrivileged(action); + } + try { + return action.call(); + } catch (Exception e) { + throw new PrivilegeException(e); + } + } + + private static final class HasSecurity { + + private static final boolean HAS_SECURITY_MANAGER = haveSecurityManager(); + + private static final boolean HAS_ACCESS_CONTROLLER = haveAccessController(); + + private HasSecurity() { + throw new IllegalStateException("No instantiation of class PrivilegedOperations$HasSecurity"); + } + + private static boolean haveSecurityManager() { + try { + Method m = System.class.getDeclaredMethod("getSecurityManager"); + if (m == null) { + return false; + } + return m.invoke(null) != null; + } catch (Throwable t) { + return false; + } + } + + private static boolean haveAccessController() { + try { + HasSecurity.class.getClassLoader().loadClass("java.security.AccessController"); + return true; + } catch (Throwable t) { + return false; + } + } + + static boolean withSecurityManager() { + return HAS_SECURITY_MANAGER; + } + + static boolean isAvailable() { + return HAS_ACCESS_CONTROLLER; + } + + static ThreadGroup getPrivilegedThreadGroup() { + return System.getSecurityManager().getThreadGroup(); + } + + static void doPrivileged(Runnable action) { + AccessController.doPrivileged((PrivilegedAction<Void>) () -> { + action.run(); + return null; + }); + } + + static <T> T doPrivileged(Callable<? extends T> action) throws PrivilegeException { + try { + return AccessController.doPrivileged(new PrivilegedExceptionAction<T>() { + + @Override + public T run() throws Exception { + return action.call(); + } + }); + } catch (PrivilegedActionException e) { + throw new PrivilegeException(e.getCause()); + } + } + } +} diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java index 25389e3a7..a2336b253 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java @@ -18,13 +18,11 @@ */ package org.apache.sshd.common.util.threads; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import org.apache.sshd.common.util.logging.AbstractLoggingBean; +import org.apache.sshd.common.util.security.PrivilegedOperations; /** * Default {@link ThreadFactory} used by {@link ThreadUtils} to create thread pools if user did provide one @@ -37,8 +35,8 @@ public class SshdThreadFactory extends AbstractLoggingBean implements ThreadFact private final String namePrefix; public SshdThreadFactory(String name) { - SecurityManager s = System.getSecurityManager(); - group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); + ThreadGroup sg = PrivilegedOperations.getPrivilegedThreadGroup(); + group = (sg != null) ? sg : Thread.currentThread().getThreadGroup(); String effectiveName = name.replace(' ', '-'); namePrefix = "sshd-" + effectiveName + "-thread-"; } @@ -48,14 +46,10 @@ public class SshdThreadFactory extends AbstractLoggingBean implements ThreadFact Thread t; try { // see SSHD-668 - if (System.getSecurityManager() != null) { - t = AccessController.doPrivileged((PrivilegedExceptionAction<Thread>) () -> new Thread( - group, r, namePrefix + threadNumber.getAndIncrement(), 0)); - } else { - t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); - } - } catch (PrivilegedActionException e) { - Exception err = e.getException(); + t = PrivilegedOperations.doPrivilegedConditional(() -> new Thread( + group, r, namePrefix + threadNumber.getAndIncrement(), 0)); + } catch (PrivilegedOperations.PrivilegeException e) { + Throwable err = e.getCause(); if (err instanceof RuntimeException) { throw (RuntimeException) err; } diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2CompletionHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2CompletionHandler.java index 8d974380b..e128bcc34 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2CompletionHandler.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2CompletionHandler.java @@ -19,8 +19,8 @@ package org.apache.sshd.common.io.nio2; import java.nio.channels.CompletionHandler; -import java.security.AccessController; -import java.security.PrivilegedAction; + +import org.apache.sshd.common.util.security.PrivilegedOperations; /** * @param <V> Result type @@ -34,18 +34,12 @@ public abstract class Nio2CompletionHandler<V, A> implements CompletionHandler<V @Override public void completed(V result, A attachment) { - AccessController.doPrivileged((PrivilegedAction<Object>) () -> { - onCompleted(result, attachment); - return null; - }); + PrivilegedOperations.doPrivileged(() -> onCompleted(result, attachment)); } @Override public void failed(Throwable exc, A attachment) { - AccessController.doPrivileged((PrivilegedAction<Object>) () -> { - onFailed(exc, attachment); - return null; - }); + PrivilegedOperations.doPrivileged(() -> onFailed(exc, attachment)); } protected abstract void onCompleted(V result, A attachment);