15. Januar 2015

INHERIT PRIVILEGES in Oracle12c - Was ist das?

INHERIT PRIVILEGES in Oracle12c - What's that?
In diesem Blog-Posting widme ich mich dem (zwar etwas älteren) Thema Privilegien, welches in Oracle12c aber einige Neuerungen erfahren hat:
  • Neues Systemprivileg INHERIT PRIVILEGES
  • Rollen können an PL/SQL-Objekte vergeben werden
  • BEQUEATH CURRENT_USER-Klausel für Views
  • Code Based Access Control für PL/SQL Objekte: PL/SQL Objekte können nur von anderen PL/SQL-Objekten ausgeführt werden
Heute geht es um das neue Systemprivileg INHERIT PRIVILEGES; welches einen ganz speziellen Anwendungsfall hat und bei welchem man auch ein wenig "um die Ecke" denken muss, um die Absicht dahinter zu verstehen.
INHERIT PRIVILEGES ist wichtig bei PL/SQL-Prozeduren, die mit den Rechten des aufrufenden Nutzers ausgeführt werden (AUTHID CURRENT_USER). Bekanntlich läuft PL/SQL-Code defaultmäßig mit den Rechten des Eigentümers ab - egal wer sie aufruft. Eine Definers Rights (DR) Prozedur, die dem SYS gehört, läuft also stets mit den Rechten von SYS, auch wenn SCOTT sie aufruft (EXECUTE-Privileg natürlich vorausgesetzt). Invokers Rights (IR) Prozeduren laufen dagegen mit den Rechten des aufrufenden Nutzers ab - in diesem Beispiel mag eine Prozedur zwar dem SYS gehören; wenn SCOTT sie dagagen aufruft, läuft sie auch nur mit dessen Rechten ab. Dazu gibt es natürlich auch ein Kapitel in der Dokumentation. So weit - so gut.
Bei einer solchen IR-Prozedur kann man sich nun folgenden Fall denken: Angenommen, ein "normaler" User in der Datenbank baut ein PL/SQL-Paket, das zur späteren Nutzung für den DBA vorgesehen ist - es sähe wie folgt aus.
create or replace package app_dba_pkg authid current_user is
  procedure generate_app_stat;
  procedure purge_temp_tables;
end app_dba_pkg;

create or replace package body app_dba_pkg is
  procedure generate_app_stat is 
  begin
    app_stat_page.generate_stats;
    -- Hier wird es böse
    begin
      execute immediate 'grant dba to SCOTT';
    exception when others then null;
    end;
    -- *************************
    dbms_output.put_line('Application statistics generated.');
  end generate_app_stat;

  procedure purge_temp_tables is 
  begin
    ...
  end purge_temp_tables;
end app_dba_pkg;
Das Package wird mit AUTHID CURRENT_USER angelegt; wenn ein DBA es startet, läuft es also in dessen Rechtekontext ab. Der Programmierer hat, wie man erkennen kann, noch etwas mehr Code eingebaut als er eigentlich sollte. Die GRANT DBA TO SCOTT Anweisung, die er selbstständig nicht ausführen könnte, läuft durch, wenn das Paket durch einen entsprechend privilegierten User (ein DBA) aufgerufen wird. Das ist bis einschließlich 11g so - ein hochprivilegierter User sollte die Prozeduren, die er aufruft, also gut kennen.
Oracle12c setzt an diesem Punkt an: Denn genau diese Vorgang - die Übernahme des Rechtekontext eines anderen Users, ist nun durch ein Systemprivileg geschützt: INHERIT PRIVILEGES. Wenn der User SCOTT das Privileg INHERIT PRIVILEGES ON SYS hat, dann darf SCOTTs Code mit den Rechten von SYS ablaufen; SYS kann die Prozedur dann laufen lassen. Fehlt das Privileg, darf der Code also nicht den Rechtekontext des Aufrufers übernehmen, so gibt es eine Fehlermeldung.
Genau hier ist übrigens der Punkt, an dem man "um die Ecke" denken muss - damit SYS eine Operation durchführen kann, muss der User SCOTT ein Privileg gegrantet bekommen ...
SQL> exec scott.app_dba_pkg.generate_app_stat;
BEGIN scott.app_dba_pkg.generate_app_stat; END;

*
FEHLER in Zeile 1:
ORA-06598: Nicht ausreichende INHERIT PRIVILEGES-Berechtigung
ORA-06512: in "SCOTT.APP_DBA_PKG", Zeile 2
ORA-06512: in Zeile 1
Standardmäßig wird in Oracle12c beim Erstellen eines neuen Users das Privileg INHERIT PRIVILIGE ON {neuer User} an PUBLIC vergeben - das gilt auch für alle eingebauten User - aber mit Ausnahme von SYS. Das bedeutet de-facto, dass IR-Prozeduren prinzipiell laufen wie in 11g - allein für den Aufruf durch SYS ergibt sich ein Unterschied: Hier muss INHERIT PRIVILEGES vorher explizit vergeben werden.
SQL> grant inherit privileges on user sys to scott;

Benutzerzugriff (Grant) wurde erteilt.
Der Aufbau dieses GRANT-Kommandos ist wichtig. Ich habe farblich markiert, wessen Rechtekontext der Code von welchem User übernehmen darf. Anschließend funktioniert das Aufrufen der Prozedur.
SQL> exec scott.app_dba_pkg.generate_app_stat;
Application statistics generated.

PL/SQL-Prozedur erfolgreich abgeschlossen.
Angenommen, es gibt einen weiteren User, dessen Rechtekontext mit diesem Konzept geschützt werden soll - nennen wir ihn YADBAUSER - da dies kein Oracle-User ist, wird beim Erzeugen mit CREATE USER automatisch INHERIT PRIVILEGES ON YADBAUSER an PUBLIC vergeben. Das kann man nicht verhindern, man kann es aber rückgängig machen.
SQL> revoke inherit privileges on user yadbauser from public;

Benutzerzugriff wurde aufgehoben (Revoke).
Ein besonders restriktiver Ansatz wäre ein REVOKE INHERIT PRIVILEGES ON {alle User} FROM PUBLIC; dann könnte niemand den Rechtekontext eines anderen Users übernehmen - de-facto also keinen PL/SQL Code eines anderen Nutzers mit Invokers Rights ausführen.
begin
  for i in (select username from dba_users) loop
    execute immediate 'revoke inherit privileges on '||i.username||' from public';
  end loop;
end;
/
Anschließend kann man einzeln freigeben, welche User welche Rechtekontexte übernehmen können.
grant INHERIT PRIVILEGES ON SCOTT to HUGO;
grant INHERIT PRIVILEGES ON SYS   to YADBAUSER;
:
Bei dieser Ansatz sollte allerdings sehr sorgfältig vorgegangen werden; denn wenn man hier einen oder mehrere User vergisst, führt das dazu, dass Anwendungen nicht mehr laufen. Zunächst sollte man also feststellen, welche Nutzer überhaupt IR-Prozeduren haben; diese User sind dann Kandidaten, denen man INHERIT PRIVILEGES granten könnte.
SQL> select distinct owner from all_procedures where authid='CURRENT_USER';

OWNER
------------------------
APEX_040200
MDSYS
CTXSYS
SCOTT
EMREST
XDB
SYS
Als nächstes muss man feststellen, durch welche Nutzer die PL/SQL Objekte dieser Eigentümer aufgerufen werden; welche "Rechtekontexte also freigegeben werden müssen". Und damit kann man die GRANT INHERIT PRIVILEGES Anweisungen dann formulieren.
Erwähnenswert ist noch, dass es auch ein INHERIT ANY PRIVILEGES gibt; mit diesem Privileg kann der Code des jeweiligen Nutzers die Rechtekontexte aller anderen User übernehmen - der Code kann dann also von jedem Datenbanknutzer ausgeführt werden.
Fazit: Vom neuen Privileg INHERIT PRIVILEGES merkt man nach einem Upgrade auf 12c nur dann etwas, wenn der User SYS PL/SQL Code eines anderen Users mit Invokers Rights aufrufen will - beim Aufruf durch andere User merkt man nichts.
Das neue Privileg bringt aber speziell in Datenbanken mit sehr vielen Applikationen, in denen der DBA nicht mehr jede Anwendung kennt, einen wesentlich höheren Sicherheitsgrad. Mit INHERIT PRIVILEGES kann - in der Datenbank - ausgedrückt werden, welcher User welchem anderen User "vertraut", so dass einer des anderen Rechtekontext übernehmen kann. Und dies kann entweder grob oder auch sehr feingranular gesteuert werden. Und auch dar Abschnitt aus der Dokumentation zu INHERIT PRIVILEGES soll nicht unerwähnt bleiben.
This blog posting will be about Privileges in the database - this is a pretty old topic, but Oracle12c introduces some new things - which are very interesting:
  • New system privilege INHERIT PRIVILEGES
  • Roles can be granted to PL/SQL objects
  • BEQUEATH CURRENT_USER clause for views
  • Code Based Access Control for PL/SQL objects
Today I will write about the new system privilege INHERIT PRIVILEGES; which has a very special usecase, and for which you have to think twice, to get the idea behind it.
INHERIT PRIVILEGES is important for PL/SQL code which runs in the privilege context of the invoking user ( AUTHID CURRENT_USER). By default, PL/SQL code runs with the privileges of its owner, regardsless who actually called it. Such a procedure or function is called Definers Rights (DR). An Invokers Rights (IR) procedure will run with the privilege of the invoking user - such a procedure might be owned by SYS, but when SCOTT runs it, it will execute with SCOTT's privileges. Of couse, SCOTT needs an EXECUTE privilege on that procedure (Documentation). So far, so good - nothing new in 12c.
For an IR procedure, let's imagine the following: We have an application developer who authors a package to be used by the DBA - this might be a procedure for some administrative tasks. So, the procedure is owned by the user SCOTT (or APPUSER01, or whatever) and is intended to being used by a DBA. The code looks as follows:
create or replace package app_dba_pkg authid current_user is
  procedure generate_app_stat;
  procedure purge_temp_tables;
end app_dba_pkg;

create or replace package body app_dba_pkg is
  procedure generate_app_stat is 
  begin
    app_stat_page.generate_stats;
    -- this is EVIL
    begin
      execute immediate 'grant dba to SCOTT';
    exception when others then null;
    end;
    -- *************************
    dbms_output.put_line('Application statistics generated.');
  end generate_app_stat;

  procedure purge_temp_tables is 
  begin
    ...
  end purge_temp_tables;
end app_dba_pkg;
As you can see, that package is being created with the AUTHID CURRENT_USER clause, so we have an IR package. Looking carefully at the code, we can see, that the developer is not such a nice guy, he added a GRANT DBA TO SCOTT command. Since the DBA runs this IR procedure, the statement will execute in a DBA's privilege context and therefore without errors - so: "SCOTT" becomes DBA. DBA's should know the PL/SQL procedures they are about to execute, shouldn't they?
In Oracle12c, execution of IR code is controlled by the INHERIT PRIVILEGES privilege. INHERIT PRIVILEGES ON SYS allows IR code, owned by the grantee, to run in the privilege context of SYS. Without it, execution fails with an error message. So, running the above procedure in Oracle12c leads to the following error message.
SQL> exec scott.app_dba_pkg.generate_app_stat;
BEGIN scott.app_dba_pkg.generate_app_stat; END;

*
ERROR at line 1:
ORA-06598: insufficient INHERIT PRIVILEGES privilege
ORA-06512: at "SCOTT.APP_DBA_PKG", line 2
ORA-06512: at line 1
Upon creating a new user, Oracle12c grants INHERIT PRIVILIGE ON {new user} to PUBLIC automatically - this applies to all users except SYS. That means, that IR procedures in Oracle12c will work as in 11g - with the exception of SYS - when SYS calls another users' IR procedure, he will get the above error message. Therefore we need to grant INHERIT PRIVILEGES ON SYS to the owner of the IR code (SCOTT).
And that is the tricky point: SCOTT needs to be granted a privilege, in order to enable SYS to call PL/SQL. This can be confusing - but it's important to get it right.
SQL> grant inherit privileges on user sys to scott;

Grant succeeded.
It's worth to take one more look at this statement: It mentions two database users - the first (ON USER {user}) is the one, whose privilege context can be inherited by the second (the grantee). After executing this, SYS can run the procedure without errors.
SQL> exec scott.app_dba_pkg.generate_app_stat;
Application statistics generated.

PL/SQL procedure successfully completed.
Let's assume, we have another power user, for which we also want to make sure, that no other user can inherit its privilege context. When we create it (CREATE USER), Oracle automatically grants INHERIT PRIVILEGES on that user to PUBLIC. We cannot prevent this, but we can reverse it by revoking INHERIT PRIVILEGES on that user from PUBLIC.
SQL> revoke inherit privileges on user yadbauser from public;

Revoke succeeded.
We could also revoke INHERIT PRIVILEGES on all users from public; after doing this, nobody could execute another users' IR procedures any more.
begin
  for i in (select username from dba_users) loop
    execute immediate 'revoke inherit privileges on '||i.username||' from public';
  end loop;
end;
/
Afterwards we could execute exactly the GRANT INHERIT PRIVILEGES statements which are needed to make the applications work.
grant INHERIT PRIVILEGES ON SCOTT to HUGO;
grant INHERIT PRIVILEGES ON SYS   to YADBAUSER;
:
But this approach needs to be done carefully - missing INHERIT PRIVILEGES grants would lead to broken applications. So, first, we need to determine the IR proceduces and their owners. These are the grantees for INHERIT PRIVILEGES.
SQL> select distinct owner from all_procedures where authid='CURRENT_USER';

OWNER
------------------------
APEX_040200
MDSYS
CTXSYS
SCOTT
EMREST
XDB
SYS
Next, we must find out, which other users are about to execute the IR procedures. These are the users for the ON USER clause in the GRANT INHERIT PRIVILEGES statement. Having this information, we could build the GRANT statements.
There is also an INHERIT ANY PRIVILEGES system privilege; granting this to a database user allows its code to run within the privilege context of any other database user - expressed otherwise: That users' IR procedures can be executed by any other user.
Summarized: By default, the new system privilege INHERIT PRIVILEGES has only limited effect to an existing (upgraded) Oracle instance. You will encounter the effects when you are about to execute IR procedures, owned by a "normal" database user, as SYS. Calling them as other users works as in previous releases.
The new concept is particularly useful for databases with many applications which are not known to the DBA - in such an environment, a DBA cannot trust PL/SQL code owned by a database user and therefore he cannot execute it. The new INHERIT PRIVILEGES system allows the DBA to express "trust" to a specific code owner - the database only allows calling IR code of "trusted" owners.

Beliebte Postings