-
Notifications
You must be signed in to change notification settings - Fork 77
Allow use (with deliberate opt-in) with no security policy enforcement, as on Java >= 24 #512
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
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
JEP 486 finally disallows any means of installing a security manager, as of Java 24, by making 'disallow' the only allowable property value for java.security.manager (other than unset, which is the default and means the same thing, but is distinguishable). Therefore, PL/Java can require the admin to set this explicitly to 'disallow' to indicate "I want to use Java 24+ and, yes, I understand there will be no policy enforcement of any kind." Revise the error / warning / notice messages about the change, now that it's no longer unclear just what form the disabling of functionality will take. Avoid exposing sensitive values such as datadir, libdir, codesource as properties in the no-enforcement mode, as nothing will protect those properties anymore from being read or changed.
In the no-enforcement mode selected with java.security.manager=disallow, enforce (in the validator) that function creation even in a PL flagged 'trusted' is reserved to superusers. This check is made unconditionally, right after CheckFunctionValidatorAccess but before the short-circuit return made if that returns false (a reserved possible future case), and before consulting check_function_bodies. Naturally, any functions already defined in 'trusted' PLs, prior to a PL/Java and/or Java update newly configured for no enforcement, remain defined. Selectively enabling those to be executable in no-enforcement mode will be the subject of the next patch.
The value is a comma-separated list of PL names (such as java, javau, possibly others created with sqlj.alias_java_language). Only functions defined in the PLs named here will be allowed to execute when the java.security.manager=disallow property setting is in effect. This will also be checked in the validator, thus preventing creation of a new function if the PL is not named here. This is checked in the validator only when check_function_bodies is on, so it is better viewed as a reminder than as meaningful security. The check at function invocation is the important one.
The JVM has long has hooks for both, just as sparsely documented as the vfprintf hook, which PL/Java has already long used. This is a good time to start hooking abort and exit also, in part because of the seemingly ever-growing set of JVM startup issues that get reported with an abort instead of a civilized error return from JNI_CreateJavaVM, and in part because if there's to be no policy enforcement around System.exit(), one likes at least to know what has happened if it gets called. (Regrettably, the exit hook is not given the option to just say "no".)
This one is a simple boolean. The Java readSQL/writeSQL methods of a MappedUDT get called by PL/Java directly, not through declared SQL functions with an associated language name, so they slip through the cracks of pljava.allow_unenforced based on language name. No MappedUDT readSQL/writeSQL methods will be executed if this setting is off. They'll be executed normally (to the extent "without enforcement" can be called "normally") when it is on.
Maybe also phrase the Backend.c JEP 411 warning a little bit less apocalyptically. Having suggested --limit-modules=org.postgresql.pljava.internal and claimed that it doesn't break the tests, add it to the CI script to make sure it stays that way.
There are so many things resting on assumptions that various properties aren't just freely modifiable by any code anywhere. Add an unmodifiable FrozenProperties final subclass of Properties, and populate it with a defensive copy just after InstallHelper has set the PL/Java-specific properties. Make this frozen copy available to user code through the Session API. The above is not enough by itself, because PL/Java internally uses some properties in code that executes before that point in PL/Java startup. So those places have to be fixed to do their own defensive caching. ELogFormatter is easy because writing a newline can be done with PrintWriter.println(), and that uses a copy of line.separator already cached early in the JVM startup. The determination of the byte ordering for SQLInputFromChunk and SQLOutputToChunk is more work. Just move all that logic out of InstallHelper and into a new class that just serves as a cache for that, and arrange for the new class to be initialized at the right point in PL/Java startup.
It is distinguishable whether a property was defaulted rather than explicitly set (getProperty(n) finds it but containsKey(n) does not), so the same should be true of the FrozenProperties.
The hardening advice in unenforced.md refers to the number of modules resolved into the boot layer, but didn't offer an easy way to check.
The table from the pull request #512 comment would be useful to have in the actual docs.
jcflack
added a commit
that referenced
this pull request
Mar 10, 2025
A couple documentation changes and a diagnostic message improved, overlooked in PR #512 (d28e9fa). Pushing these without another PR. The Java-version condition determining the exact message shown when beginEnforcing fails is now harmonized with what install/smproperty.md says about use of the java.security.manager property by Java version. The property must be supplied starting with Java 18, so the useful hint should be given for failures on 18 through 23, rather than calling the failure unexpected.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Java 24, with JEP 486, completes the process, begun with JEP 411, of completely gutting Java's ability to enforce security policy, heavily relied on by PL/Java.
With this PR, PL/Java can still be used with all of its familiar policy-enforcement features, as long as
pljava.libjvm_locationpoints to a Java 23 or earlier JVM, and can also be used—with explicit variable settings to deliberately opt in—with no policy enforcement at all. Only this 'unenforced' mode is available whenpljava.libjvm_locationpoints to a Java 24 or later JVM.This, at least, gives a DBA a choice. If a 'trusted' PL with policy enforcement is more important than the very latest Java language features, continue to use PL/Java with a Java 23 or earlier JVM, such as the long-term-support Java 21 release. On the other hand, if the latest Java features are of interest and an 'untrusted' PL with few guardrails is acceptable, the same PL/Java installation can be used in that mode with a Java 24 or later JVM.
It is possible to switch modes at will, simply by changing the JVM that
pljava.libjvm_locationpoints to, and a few GUCs.Because of that flexibility, PL/Java continues to create two PL entries, one called
javauand the other calledjavaand markedTRUSTED, even when running in unenforced mode. There may be functions already declared of both kinds carried over from earlier, and the distinction may be enforced again simply by pointing PL/Java at a Java 23 or earlier JVM again.However, in unenforced mode, execution is equally unrestricted regardless of the PL name used or its
TRUSTEDdesignation. Also, in unenforced mode, only database superusers may create functions, even when the PL is markedTRUSTED, regardless of any grant ofUSAGEon the PL.Opting in
It would clearly be inappropriate for PL/Java to silently switch to unenforced mode just because the JVM has been updated from a pre-24 to a 24-or-later version. An admin needs to think deliberately about the implications, and perhaps audit some existing Java code, before deciding whether to use a 24-or-later JVM and opt in to unenforced execution.
-Djava.security.manager=...The primary choice of mode is whether
-Djava.security.manager=allow(for the familiar, policy-enforcing mode) or-Djava.security.manager=disallow(for unenforced mode) appears in thepljava.vmoptionssetting:-Djava.security.managerinpljava.vmoptions. Mode will be policy-enforcing.-Djava.security.manager=allowor-Djava.security.manager=disallowmay appear inpljava.vmoptions. Default is policy-enforcing (same asallow) if neither appears.-Djava.security.manager=allowor-Djava.security.manager=disallowmust appear inpljava.vmoptions, or PL/Java will fail to start. There is no default.-Djava.security.manager=disallowmust appear inpljava.vmoptions, or PL/Java will fail to start.However, simply starting PL/Java with
-Djava.security.manager=disallowby itself will not allow any Java functions, or Java-based user-defined-type data conversion methods, to execute in unenforced mode. Those must be further approved, as follows.pljava.allow_unenforced=PL name,...This GUC is a list of PL names, such as
javauandjava(there can be more, as PL./Java's policy-enforcing mode has allowed additional names to be created withSQLJ.ALIAS_JAVA_LANGUAGEand associated with differently-tailored policies).No PL/Java function will be allowed to execute in unenforced mode unless the PL name in which it is declared appears in this list.
The list is empty by default. Not even
javau—which should be the easiest call, as that PL was already considered untrusted—is allowed until added to this list by the admin. That is because evenjavauwas subject to a security policy, albeit a more relaxed one, in policy-enforcing mode. In unenforced mode, even those guardrails are removed.The per-PL-name granularity of this setting allows an admin to divide the work of auditing existing Java code, and add each PL to this list after the functions declared in it have been reviewed.
CREATE FUNCTIONin a PL that is not named here will usually be reported as an error in unenforced mode, too. It won't be ifcheck_function_bodiesisoff, though, so this should be seen more as a friendly reminder than as a form of security. If theCREATE FUNCTIONsucceeds, the function still cannot execute until the PL name is added to this list.pljava.allow_unenforced_udt=on/offThe
pljava.allow_unenforcedsetting based on PL names will not affect the data-conversionreadSQL/writeSQLJava methods of PL/Java-based mapped user-defined types, because those do not have corresponding SQL declarations to associate a PL name. Theon/offsetting ofpljava.allow_unenforced_udtcontrols the execution of all such UDT methods. Its default setting isoff, so an admin can change it toonafter review of any affected Java code.Hardening
New documentation on the unenforced mode includes hardening advice, essentially to milk every remaining Java integrity-supporting feature for all it's worth. Suggestions include the
--limit-modulesVM option to reduce the number of Java system modules visible to user code, and using the--sun-misc-unsafe-memory-access=denyand--illegal-native-access=denyVM options whenever the JVM version in use recognizes them. (Even in policy-enforcing mode, these techniques can still be recommended as part of a defense-in-depth approach.)All Java system properties are writable in unenforced mode, so defensive early copying is recommended. There are many system properties that are often assumed to convey reliable information about the environment. PL/Java itself now creates an unmodifiable early copy of the system properties, before user code has executed, and makes it available to user code through a new method on the
Sessionobject.PL/Java does not yet make it convenient to deploy user code as named modules (the SQL/JRT standard predated Java's module system), but it is possible, and can be advantageous if only a subset of the code requires some special treatment. Per-module VM options now seem to be the finest granularity of permission the developers of Java intend to support.