Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove --enable-all-security-services option. #3258

Merged
merged 1 commit into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions substratevm/JCASecurityServices.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
# JCA Security Services in Native Image

This section refers to the use of the services provided by the [Java Cryptography Architecture (JCA)](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) framework.
The JCA framework relies on reflection to achieve algorithm independence and extensibility, therefore it requires a custom configuration in Native Image.
Additionally, seed generators that use system files like `/dev/random` or `/dev/urandom` need to be reinitialized at run time.
This section refers to the use of the services provided by the Java Cryptography Architecture (JCA) framework.
For more details see the following guides: [JDK8](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html), [JDK11](https://docs.oracle.com/en/java/javase/11/security/java-cryptography-architecture-jca-reference-guide.html).

By default a native image is built with support for the `SecureRandom` and `MessageDigest` engines from the `SUN` provider.
The JCA framework uses a provider architecture to access security services such as digital signatures, message digests, certificates and certificate validation, encryption (symmetric/asymmetric block/stream ciphers), key generation and management, and secure random number generation, etc.
To achieve algorithm independence and extensibility it relies on reflection, therefore it requires a custom configuration in Native Image.
The Native Image builder uses static analysis to discover which of these services are used.

Each provider registers concrete implementation classes for the algorithms it supports.
Each of the service classes (`Signature`, `Cipher`, `Mac`, `KeyPair`, `KeyGenerator`, `KeyFactory`, `KeyStore`, etc.,) declares a series of `getInstance(<algorithm>, <provider>)` factory methods which provide a concrete service implementation.
When a specific algorithm is requested the framework searches the registered providers for the corresponding implementation classes and it dynamically allocates objects for concrete service implementations.
The Native Image builder uses static analysis to discover which of these services are used.
It does so by registering reachability handlers for each of the `getInstance()` factory methods.
When it determines that a `getInstance()` method is reachable at run time it automatically performs the reflection registration for all the concrete implementations of the corresponding service type.
This mechanism is implemented in the `com.oracle.svm.hosted.SecurityServicesFeature` class.

The simplest images contain support for the `SecureRandom` and `MessageDigest` engines from the `SUN` provider.
These are core security services needed by the VM itself.
All the other Java security services (`Signature`, `Cipher`, `Mac`, `KeyPair`, `KeyGenerator`, `KeyFactory`, `KeyStore`, etc.,) must be enabled by adding the `--enable-all-security-services` option to the `native-image` command.
The reason behind enabling only core security services by default is that you can start with a basic image and add more security services as you need them.
This helps keep the overall image size small.

Note: the `--enable-all-security-services` option is enabled by default when `https` support is enabled.
See the [URL Protocols in Native Image](URLProtocols.md) guide for more details.
Note: The `--enable-all-security-services` option is now deprecated and it will be removed in a future release.

## Provider Registration
The native image builder captures the list of providers and their preference order from the underlying JVM.
The provider order is specified in the `java.security` file under `<java-home>/lib/security/java.security`.
New security providers cannot be registered at run time; all providers must be statically configured during a native image building.

### Alternative to `--enable-all-security-services`
Registering *all* security services does not come for free.
The additional code increases the native image size.
If your application only requires a subset of the security services, you can manually register the corresponding classes for reflection and push the initialization of some seed generators to run time.
However this requires deep knowledge of the JCA architecture.
We are investigating the possibility of providing a finer-grain declarative configuration of security services for future releases.
If you want to take on this task yourself, you can start by reading the `com.oracle.svm.hosted.SecurityServicesFeature` class.
This is where most of the code behind the `--enable-all-security-services` option is implemented.
## SecureRandom

The SecureRandom implementations open the `/dev/random` and `/dev/urandom` files which are used as sources for entropy.
These files are usually opened in class initializers.
To avoid capturing state from the machine that runs the Native Image builder these classes need to be initialized at run time.
4 changes: 0 additions & 4 deletions substratevm/Limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ Native Image employs a simple static analysis that intercepts calls to `java.lan
Where the analysis fails the lists of interfaces can be specified in a [configuration file](BuildConfiguration.md).
For more details, read the [Dynamic Proxies support](DynamicProxy.md) guide.

### JCA (Java Cryptography Architecture)
The JCA security services must be enabled using the option `--enable-all-security-services`.
They require a custom configuration in Native Image since the JCA framework relies on reflection to achieve algorithm extensibility. For more details, read the [Security Services](JCASecurityServices.md) guide.

### JNI (Java Native Interface)
Native code may access Java objects, classes, methods and fields by name, in a similar way to using the reflection API in Java code.
For the same reasons, any Java artifacts accessed by name via JNI must be specified during a native image generation in a [configuration file](BuildConfiguration.md). For more details, read the [JNI Implementation](JNI.md) guide.
Expand Down
1 change: 0 additions & 1 deletion substratevm/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ The following options are equally supported with both GraalVM Communty and Enter
* `--help-extra`: print help on non-standard options.
* `--allow-incomplete-classpath`: allow the image build with an incomplete class path. Report type resolution errors at runtime when they are accessed the first time, instead of during the image build.
* `--auto-fallback`: build a standalone image if possible.
* `--enable-all-security-services`: add all security service classes to a generated image.
* `--enable-http`: enable http support in a generated image.
* `--enable-https`: enable https support in a generated image.
* `--enable-url-protocols`: list comma-separated URL protocols to enable.
Expand Down
3 changes: 1 addition & 2 deletions substratevm/URLProtocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ They can be enabled using the `--enable-http` and `--enable-https` options.

### HTTPS Support
Support for the `https` URL protocol relies on the Java Cryptography Architecture (JCA) framework.
Thus when `https` is enabled, `--enable-all-security-services` is set by default.
This adds to the generated image the code required by the JCA, including statically linking native libraries that the JCA may depend on.
Thus enabling `https` will add to the generated image the code required by the JCA, including statically linking native libraries that the JCA may depend on.
See the [documentation on security services](JCASecurityServices.md) for more details.

## Not Tested
Expand Down
3 changes: 1 addition & 2 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,7 @@ def native_unittests_task():
# GR-24075
mx_unittest.add_global_ignore_glob('com.oracle.svm.test.ProcessPropertiesTest')

# We need the -H:+EnableAllSecurityServices for com.oracle.svm.test.SecurityServiceTest
native_unittest(['--build-args', _native_unittest_features, '-H:+EnableAllSecurityServices'])
native_unittest(['--build-args', _native_unittest_features])


def javac_image_command(javac_path):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,10 @@
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;

import com.oracle.svm.core.jdk.JavaNetSubstitutions;
import com.oracle.svm.core.option.APIOption;
import com.oracle.svm.core.option.APIOptionGroup;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.LocatableMultiOptionValue;
import com.oracle.svm.core.option.OptionUtils;
import com.oracle.svm.core.option.RuntimeOptionKey;
import com.oracle.svm.core.option.XOptions;
import com.oracle.svm.core.util.UserError;
Expand Down Expand Up @@ -243,32 +241,12 @@ public Boolean getValue(OptionValues values) {
@APIOption(name = "enable-https", fixedValue = "https", customHelp = "enable https support in the generated image")//
@APIOption(name = "enable-url-protocols")//
@Option(help = "List of comma separated URL protocols to enable.")//
public static final HostedOptionKey<LocatableMultiOptionValue.Strings> EnableURLProtocols = new HostedOptionKey<LocatableMultiOptionValue.Strings>(new LocatableMultiOptionValue.Strings()) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, LocatableMultiOptionValue.Strings oldValue, LocatableMultiOptionValue.Strings newValue) {
for (String protocol : OptionUtils.flatten(",", newValue)) {
if (protocol.equals(JavaNetSubstitutions.HTTPS_PROTOCOL)) {
EnableAllSecurityServices.update(values, true);
}
}
}
};
public static final HostedOptionKey<LocatableMultiOptionValue.Strings> EnableURLProtocols = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings());

@SuppressWarnings("unused") //
@APIOption(name = "enable-all-security-services")//
@Option(help = "Add all security service classes to the generated image.")//
public static final HostedOptionKey<Boolean> EnableAllSecurityServices = new HostedOptionKey<Boolean>(false) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
if (newValue) {
/*
* Some providers like SunEC and SunSASL are implemented in native libs. These
* providers are added to the image when EnableAllSecurityServices is set. If they
* are actually used at runtime the user must provide and load the native libs.
*/
JNI.update(values, true);
}
}
};
@Option(help = "Add all security service classes to the generated image.", deprecated = true)//
public static final HostedOptionKey<Boolean> EnableAllSecurityServices = new HostedOptionKey<>(false);

@Option(help = "Enable Java Native Interface (JNI) support.")//
public static final HostedOptionKey<Boolean> JNI = new HostedOptionKey<>(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.core;

import java.util.function.Consumer;

import com.oracle.svm.core.util.VMError;

/**
Expand Down Expand Up @@ -62,6 +64,12 @@ public boolean isPresent() {
return type != null;
}

public void ifPresent(Consumer<? super T> consumer) {
if (type != null) {
consumer.accept(type);
}
}

public T get() {
return type;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,11 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;

import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.Pointer;

import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.Delete;
Expand All @@ -60,8 +58,6 @@
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;

Expand Down Expand Up @@ -235,76 +231,6 @@ final class Target_javax_crypto_CryptoAllPermission {
static Target_javax_crypto_CryptoAllPermission INSTANCE;
}

final class EnableAllSecurityServicesIsSet implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return SubstrateOptions.EnableAllSecurityServices.getValue();
}
}

/**
* This substitution is enabled only when EnableAllSecurityServices is set since the functionality
* that it currently provides, i.e., loading security native libraries, is not needed by default.
*/
@TargetClass(value = java.security.Provider.class, onlyWith = EnableAllSecurityServicesIsSet.class)
final class Target_java_security_Provider {

@Alias //
private transient boolean initialized;

@Alias//
private String name;

/*
* Provider.checkInitialized() is called from the other Provider API methods, before any
* computation, thus is a convenient location to do our own initialization, i.e., make sure that
* the required libraries are loaded.
*/
@Substitute
private void checkInitialized() {
if (this.name.equals("SunEC")) {
ProviderUtil.initSunEC();
}

if (!initialized) {
throw new IllegalStateException();
}
}

}

final class ProviderUtil {
private static volatile boolean initialized = false;

static void initSunEC() {
if (initialized) {
return;
}
/* Lazy initialization. */
initOnce();
}

// Checkstyle: stop
private static synchronized void initOnce() {
// Checkstyle: resume
if (!initialized) {
try {
System.loadLibrary("sunec");
} catch (UnsatisfiedLinkError e) {
/*
* SunEC has a mode where it can function without the full ECC implementation when
* native library is absent, however, then fewer EC algorithms are available). If
* those algorithms are actually used an java.lang.UnsatisfiedLinkError will be
* thrown. Just warn the user that the library could not be loaded.
*/
Log.log().string("WARNING: The sunec native library, required by the SunEC provider, could not be loaded.").newline();
}
initialized = true;
}
}

}

@TargetClass(className = "javax.crypto.ProviderVerifier", onlyWith = JDK11OrLater.class)
@SuppressWarnings({"unused"})
final class Target_javax_crypto_ProviderVerifier {
Expand Down Expand Up @@ -386,10 +312,7 @@ static Exception getVerificationResult(Provider p) {
* getVerificationResult() allows for a better error message.
*/
throw VMError.unsupportedFeature("Trying to verify a provider that was not registered at build time: " + p + ". " +
"All providers must be registered and verified in the Native Image builder. " +
"Only the SUN provider is registered and verified by default. " +
"All other built-in providers are processed when all security services are enabled using the " + JceSecurityUtil.enableAllSecurityServices + " option. " +
"Third party providers must be configured in the Native Image builder VM. ");
"All providers must be registered and verified in the Native Image builder. ");
}

}
Expand Down Expand Up @@ -423,8 +346,6 @@ private static synchronized SecureRandom initializeOnce() {
}

final class JceSecurityUtil {
static final String enableAllSecurityServices = SubstrateOptionsParser.commandArgument(SubstrateOptions.EnableAllSecurityServices, "+");

static RuntimeException shouldNotReach(String method) {
throw VMError.shouldNotReachHere(method + " is reached at runtime. " +
"This should not happen. The contents of JceSecurity.verificationResults " +
Expand Down
Loading