OJVM and the Mitigation Patch – Things to know in 2020

Today my headline sounds a bit like “Little Red Riding Hood and the Big Bad Wolf” – but there is a reason why I’d like to write about about OJVM and the Mitigation Patch – Things to know in 2020. We are working with a customer right now where preupgrade.jar gave a warning about the existence of the Mitigation patch in an 11.2.0.4 home – despite the fact that JAVAVM is not configured in this database. And this looked strange to us.

OJVM and the Mitigation Patch - Things to know

Photo by Šárka Jonášová on Unsplash

Mitigation Patch?

A quick search on my blog shows me that I wrote about the Mitigation Patch a while ago:

If you don’t want to read my post from September 2016, no worries. I summarize the details in brief:

  • The Mitigation Patch allows you to disable (and enable) the Java subsystem
  • It can be used where you have JAVAVM, don’t use it but can’t deinstall it
  • This MOS Note:1929745.1 – Oracle JavaVM Component Database Patches gives you an overview about:
    • What is the Mitigation Patch?
    • Using the Mitigation Patch
    • And it has two important details:
      • [..] has been reviewed each cycle from January 2015 through January 2017 [..]
      • “[..] Note: Patch 19721304 is now included in the following 12.1.0.2 and 11.2.0.4 patches and therefore, in the following steps, Step 1 should be skipped (otherwise OPatch will report Patch 19721304 as a Conflict/Subset):
        • Database Patch Set Update
        • [..]”

So at first, it is not 100% clear if the mitigation patch is still supported in 2020. And second, it is not obvious (at least to me) whether it gets delivered with a PSU for Oracle 11.2.0.4 in 2020, or not. It reads to me as if this is the case.

And this is the situation I’d like to examine.

Is the Mitigation Patch part of my PSU?

In my Oracle 11.2.0.4 environment I use the January 2020 PSU. In Oracle 11.2.0.4 there is no choice unless you have an Exadata. Otherwise I would use BPs and RUs.

Check:

$ORACLE_HOME/OPatch/opatch lsinventory > file.txt

And when I search for the patch number, 19721304, I get this result in the output:

OJVM and the Mitigation Patch - Things to know in 2020

Contents of 11.2.0.4 January 2020 PSU

 

The patch binary is installed.

Important: If you have a PSU or BP newer than late 2017, the Mitigation Patch should be included already. Hence, there is no need to install it separately.

Install the Mitigation Patch

Wait – didn’t I write there is no need to install it? When we speak about binaries, this is correct. But you need to install it in the database. And this important information is not included in MOS Note:1929745.1 – Oracle JavaVM Component Database Patches. But I mentioned this in my Part III: The Mitigation Patch blog post from 2016:

  • @?/rdbms/admin/dbmsjdev.sql

If you don’t run this script, you can’t execute the call do disable the Java subsystem:

  • exec dbms_java_dev.disable

If you didn’t run the above script, you will receive:

SQL> exec dbms_java_dev.disable            
BEGIN dbms_java_dev.disable; END;

      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'DBMS_JAVA_DEV.DISABLE' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

If you ran the above script, the above exec command will succeed – partially.
I run it in a database where I have no JAVAVM installed. And the script completes – see below.

dbmsjdev.sql Execution

Running dbmsjdev.sql in my database with no JAVAVM gives the following output – and this is the reason why I copy the entire output here:

SQL> @?/rdbms/admin/dbmsjdev.sql
SQL> SET FEEDBACK 1
SQL> SET NUMWIDTH 10
SQL> SET LINESIZE 80
SQL> SET TRIMSPOOL ON
SQL> SET TAB OFF
SQL> SET PAGESIZE 100
SQL> 
SQL> declare
  2    role_exists exception;
  3    pragma exception_init(role_exists, -1921);
  4  begin
  5    execute immediate 'create role oracle_java_dev';
  6  exception
  7    when role_exists then
  8      null;
  9  end;
 10  /

PL/SQL procedure successfully completed.

SQL> 
SQL> create or replace trigger sys.dbms_java_dev_trg before create
  2  on database disable
  3  begin
  4    if (ora_dict_obj_type='JAVA')
  5    then
  6      raise_application_error(-20031,'Java Development Disabled');
  7    end if;
  8  end;
  9  /

Trigger created.

SQL> 
SQL> create or replace view sys.java_dev_status as
  2  select decode(status,'ENABLED','NO','YES') JAVA_DEV_ALLOWED
  3  from dba_triggers
  4  where trigger_name='DBMS_JAVA_DEV_TRG'
  5    and owner='SYS';

View created.

SQL> 
SQL> 
SQL> create or replace public synonym java_dev_status for sys.java_dev_status;

Synonym created.

SQL> grant select on java_dev_status to public;

Grant succeeded.

SQL> 
SQL> declare
  2    constraint_exists exception;
  3    pragma exception_init(constraint_exists, -2264);
  4  begin
  5    execute immediate 'alter table sys.procedurejava$ add constraint
  6                       java_dev_disabled check (obj# = 0) disable';
  7  exception
  8    when constraint_exists then
  9      null;
 10  end;
 11  /

PL/SQL procedure successfully completed.

SQL> 
SQL> declare
  2    constraint_exists exception;
  3    pragma exception_init(constraint_exists, -2264);
  4  begin
  5    execute immediate 'alter table sys.javajar$ add constraint
  6                       java_dev_jars_disabled check (owner# = -1) disable';
  7  exception
  8    when constraint_exists then
  9      null;
 10  end;
 11  /
declare
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at line 5


SQL> 
SQL> 
SQL> create or replace package sys.dbms_java_dev authid definer is
  2  
  3    procedure disable;
  4  
  5    procedure enable;
  6  
  7  end dbms_java_dev;
  8  /

Package created.

SQL> 
SQL> create or replace package body sys.dbms_java_dev is
  2  
  3    procedure disable is
  4      type grant_collection is table of sys.dba_tab_privs.table_name%type;
  5      jdev_grants grant_collection;
  6      type grantee_collection is table of sys.dba_tab_privs.grantee%type;
  7      jdev_grantees grantee_collection;
  8      pkg_name sys.dba_tab_privs.table_name%type;
  9      grantee varchar2(130);
 10      stmt varchar2(2000);
 11      stmt2 varchar2(2000);
 12    begin
 13  
 14      select distinct owner, referenced_name bulk collect
 15      into jdev_grantees, jdev_grants
 16      from sys.dba_dependencies
 17      where referenced_owner in ('SYS','PUBLIC') and owner != 'PUBLIC'
 18        and owner != referenced_owner
 19        and referenced_name in ('DBMS_JAVA','DBMS_JAVA_TEST','SQLJUTL',
 20                                'SQLJUTL2','JVMRJBCINV','DBMS_JAVA_MISC');
 21  
 22      for i in 1..jdev_grants.count loop
 23        pkg_name := dbms_assert.simple_sql_name(jdev_grants(i));
 24        grantee := dbms_assert.enquote_name(jdev_grantees(i),FALSE);
 25        stmt := 'grant execute on sys.' || pkg_name || ' to ' || grantee;
 26        execute immediate stmt;
 27      end loop;
 28  
 29      select table_name bulk collect into jdev_grants from sys.dba_tab_privs
 30      where grantee='PUBLIC' and owner='SYS' and privilege='EXECUTE'
 31        and table_name in ('DBMS_JAVA','DBMS_JAVA_TEST','SQLJUTL','SQLJUTL2',
 32                           'JVMRJBCINV','DBMS_JAVA_MISC');
 33  
 34      for i in 1..jdev_grants.count loop
 35        pkg_name := dbms_assert.simple_sql_name(jdev_grants(i));
 36        stmt := 'revoke execute on sys.' || pkg_name || ' from public';
 37        stmt2 := 'grant execute on sys.' || pkg_name || ' to oracle_java_dev';
 38        execute immediate stmt;
 39        execute immediate stmt2;
 40      end loop;
 41  
 42      execute immediate 'alter trigger sys.dbms_java_dev_trg enable';
 43  
 44      execute immediate 'alter table sys.procedurejava$ enable novalidate
 45                         constraint java_dev_disabled';
 46      execute immediate 'alter table sys.javajar$ enable novalidate
 47                         constraint java_dev_jars_disabled';
 48  
 49    end disable;
 50  
 51    procedure enable is
 52      type grant_collection is table of sys.dba_tab_privs.table_name%type;
 53      jdev_grants grant_collection;
 54      pkg_name sys.dba_tab_privs.table_name%type;
 55      stmt varchar2(2000);
 56      stmt2 varchar2(2000);
 57    begin
 58  
 59      execute immediate 'alter trigger sys.dbms_java_dev_trg disable';
 60  
 61      execute immediate 'alter table sys.procedurejava$ disable constraint
 62                         java_dev_disabled';
 63      execute immediate 'alter table sys.javajar$ disable constraint
 64                         java_dev_jars_disabled';
 65  
 66      select table_name bulk collect into jdev_grants from sys.dba_tab_privs
 67      where grantee='ORACLE_JAVA_DEV' and privilege='EXECUTE' and owner='SYS'
 68        and table_name in ('DBMS_JAVA','DBMS_JAVA_TEST','SQLJUTL','SQLJUTL2',
 69                           'JVMRJBCINV','DBMS_JAVA_MISC');
 70  
 71      for i in 1..jdev_grants.count loop
 72        pkg_name := dbms_assert.simple_sql_name(jdev_grants(i));
 73        stmt := 'grant execute on sys.' || pkg_name || ' to public';
 74        stmt2 := 'revoke execute on sys.' || pkg_name || ' from oracle_java_dev';
 75        execute immediate stmt;
 76        execute immediate stmt2;
 77      end loop;
 78  
 79    end enable;
 80  end dbms_java_dev;
 81  /

Package body created.

Only one error – but most statements succeed despite the fact that my database does not have a JAVAVM.

See my DBA_REGISTRY:

column comp_id format a12
column status format a7
column version format a12
select comp_id, status, version from dba_registry order by 1;

COMP_ID      STATUS  VERSION
------------ ------- ------------
CATALOG      VALID   11.2.0.4.0
CATPROC      VALID   11.2.0.4.0

Very strange.

The failure is: You can install the objects associated with the mitigation patch despite the fact that there is no JAVAVM present in this database.

How about preupgrade.jar?

Back to the customer scenario. Our customer saw a warning about the presence of the mitigation patch by preupgrade.jar.

The execution of the script will alert you now because it uses this check:

    IF NOT select_has_rows('select null from sys.dba_triggers
            where trigger_name=''DBMS_JAVA_DEV_TRG'' and owner=''SYS''
            and status=''ENABLED''') THEN
            RETURN c_success;

Let us check the trigger’s status:

SQL> select status from dba_triggers where trigger_name='DBMS_JAVA_DEV_TRG';

STATUS
--------
DISABLED

1 row selected.

But when you call to disable the Java subsystem (see the next paragraph), the trigger will change its status to ENABLED. And then preupgrade.jar will give you an ERROR condition.

We will add an additional check in a newer version of preupgrade.jar, and of course in autoupgrade, too.

Can I disable the Java subsystem?

At first, I check what the package contains:

  • SQL> desc sys.dbms_java_dev
    PROCEDURE DISABLE
    PROCEDURE ENABLE

Let me try:

SQL> exec sys.DBMS_JAVA_DEV.DISABLE;
BEGIN sys.DBMS_JAVA_DEV.DISABLE; END;

*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "SYS.DBMS_JAVA_DEV", line 46
ORA-06512: at line 1

Fails.

But how about:

SQL> exec sys.DBMS_JAVA_DEV.ENABLE;
BEGIN sys.DBMS_JAVA_DEV.ENABLE; END;

*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "SYS.DBMS_JAVA_DEV", line 63
ORA-06512: at line 1

Fails too. And this makes sense as there is no JAVAMVM. For instance, this table sys.javajar$ does not exist in my case.

No harm can be done – and I checked in addition, all objects in DBA_OBJECTS are VALID.

But why?

But the final question remains to me – and maybe to you, too:
How could this happen?

Of course, we are all in agreement, that the dbmsjdev.sql should have a check at first. It shouldn’t create objects when there no JAVAVM component present. And preupgrade.jar will get an additional one as well. But this is just a minor problem.

Simple answer – the readme of the PSUs is the reason why our customer executed dbmsjdev.sql.

See here in the January 2020 PSU for 11.2.0.4 under:

1.3.3.2 Loading Modified SQL Files into the Database

OJVM and the Mitigation Patch - Things to know in 2020

11.2.0.3 January 2020 PSU – README – Section about Mitigation Patch

Argh! Now it is obvious. The sentence does not say: “Please check DBA_REGISTRY. If JAVAVM is not installed, ignore this paragraph.“.

Hence, no blame to the customer. It simply doesn’t get explained that the Mitigation Patch will make only sense when JAVAVM is present in your database. In all other cases, it has no meaning, and therefore dbmsjdev.sql don’t need to be run.

But this is exactly why we came across this issue. The customer followed the README, executed the dbmsjdev.sql script and called the DBMS_JAVA_DEV.DISABLE procedure.

Summary

The Mitigation Patch is included in all 11.2.0.4 and 12.1.0.2 patch bundles since at least late 2017. No extra installation is required. You can use it to disable the Java subsystem. This is helpful in cases where you have OJVM present in your database but can’t or don’t want to patch it. This way you can avoid getting exposed to the many security issues associated with OJVM.

But you do need neither the Mitigation Patch nor the activation script dbmsjdev.sql which creates the necessary framework to disable the Java subsystem if JAVAVM is not present in your database. Check DBA_REGISTRY (or CDB_REGISTRY from 12.1 on) whether you have a COMP_ID with JAVAVM.

Addition

Bug 31132048 – DBMSJDEV.SQL SHOULD BLOCK EXECUTION IF JAVAVM IS NOT PRESENT IN REGISTRY has been logged and fixed for this issue. As the code fix has been done for MAIN (which is 21c), you may please need to request a one-off on top of your RU as long as this isn’t included in an RU yet. Please upload your opatch lsinventory, too, in case you request a backport.

More Information and Links

–Mike

Share this: