Call now: 252-767-6166  
Oracle Training Oracle Support Development Oracle Apps

 
 Home
 E-mail Us
 Oracle Articles


 Oracle Training
 Oracle Tips

 Oracle Forum
 Class Catalog


 Remote DBA
 Oracle Tuning
 Emergency 911
 RAC Support
 Apps Support
 Analysis
 Design
 Implementation
 Oracle Support


 SQL Tuning
 Security

 Oracle UNIX
 Oracle Linux
 Monitoring
 Remote s
upport
 Remote plans
 Remote
services
 Application Server

 Applications
 Oracle Forms
 Oracle Portal
 App Upgrades
 SQL Server
 Oracle Concepts
 Software Support

 Remote S
upport  
 Development  

 Implementation


 Consulting Staff
 Consulting Prices
 Help Wanted!

 


 Oracle Posters
 Oracle Books

 Oracle Scripts
 Ion
 Excel-DB   


 

 

 


 

 

 

 

 

Oracle Security Virtual Private Database Policy

Oracle Security Tips by Burleson Consulting


This is an excerpt from the bestselling book "Oracle Privacy Security Auditing", a complete Oracle security reference with working Oracle security scripts.
 


Virtual Private Database (VPD) Policy

The VPD uses a filtering mechanism to filter out rows that do not match the required authorization of the user. This filter can be either dynamic or static. Static filters are not that useful, but can be used in some cases to exclude a set list of provider ids.

However, in real life the filter is going to be different and dynamic. User Nathan may be promoted or relocated to a different group, and thus he no longer handles provider 1234567, but the providers 4567891 and 5678912. This change in business should be immediately reflected in the view, and should not require a recreation of the view. Therefore, a dynamic filter is necessary. This filter is enforced by the RDBMS using a concept called a policy.

Figure 5.2 Policy Applied to a Table in VPD

Refer to Fig 5.2 for an explanation of the policy. Here the table CLAIMS is subjected to a policy that restricts the tables to those rows that satisfy the restriction. The restriction is controlled by a function such as the following.

create or replace function get_auth_providers
(
    p_schema_name    in varchar2,
    p_object_name    in varchar2
)
return varchar2
is
    l_ret        varchar2(2000);
begin
    -- if the user calling it is the same as the owner,
    -- we want to return all rows, so a where condition
    -- of 1=1 will always return TRUE.
    if p_schema_name = user
    then
        l_ret := '1=1';
    else -- if the user is different from the owner
        for prov_rec in
        (
            select provider_id
            from provider_view_policy
            where username = user
        ) loop
            l_ret := l_ret||','||prov_rec.provider_id;
        end loop;
        l_ret := ltrim(l_ret,',');
        l_ret := 'PROVIDER_ID IN ('||l_ret ||')';
    end if;
    return l_ret;
end;
/

This function returns the list of providers the current user is authorized to see. Let's see how it dynamically changes the output.

Connect NATHAN/*
Select
    get_auth_providers ('CLAIM_SCHEMA', 'CLAIMS')
From
    dual;

The output is:

PROVIDER_ID IN (1234567)

But when CLARA logins and executes the same query:

Connect CLARA/*
Select
    get_auth_providers ('CLAIM_SCHEMA', 'CLAIMS')
From
    dual;

The output is:

PROVIDER_ID IN (3456789,2345678)

Note how the output changed. The output returns the list of providers for which the logged in user is authorized.

What do we do with this function? We will apply it as a limit to the rows retrieved from a table. In other words, we will set it up as a security policy for the table CLAIMS. The following does this:

begin
dbms_rls.add_policy (
    object_schema    => 'CLAIM_SCHEMA',
    object_name     => 'CLAIMS',
    policy_name      => 'CLAIM_VIEW_POLICY',
    function_schema  => 'CLAIM_SCHEMA',
    policy_function  => 'GET_AUTH_PROVIDERS',
    statement_types  =>
        'INSERT, UPDATE, DELETE, SELECT',
    update_check     => TRUE,
    enable           => TRUE,
    static_policy    => FALSE
);
end;
/

For the time being, let's not ponder the explanation or the meaning of these parameters, they will be explained in detail later. We have added a policy to the table claims owned by CLAIM_SCHEMA. This policy will append the output of the function get_auth_providers to any query of SELECT, INSERT, UPDATE, or DELETE on claims.

When user NATHAN logs on and issues a query to select from the table as follows:

connect nathan/*
select * from claims
/

The output comes as:

C SSN       MEMBER_I PROVIDER_I CLAIM_AM PAID_AMOUNT S
- --------- -------- ---------- -------- ----------- -
1 123456789    12345    1234567     1200        1000 P
2 234567891    12345    1234567     1800        1700 P

Note, only the claims from provider 1234567 are displayed. The select statement was on the table CLAIMS, not a view that had the filtering condition, and the user did not apply a filter using a where clause. Oracle RDBMS automatically applied a where clause to the query to limit the output.

Remember, when NATHAN executed the function get_auth_providers() earlier, he got the output as:

PROVIDER_ID IN (1234567)

This string was applied automatically as a where clause to all the queries NATHAN submitted to the claims table. So the query:

select * from claims;

Was transformed to:

select * from
    (select * from claims)
where PROVIDER_ID IN (1234567);

Note how the filtering condition was automatically placed. When Clara issues the same statement:

connect CLARA/*
select * from claims;


C SSN        MEMBER_   PROVIDER_ CLAIM_AM PAID_AMOUNT S
- ---------- --------- --------- -------- ----------- - -
3 234567891    12345     2345678     1400        1300 P
4 345678912    23456     3456789     2000        1900 P
5 345678912    23456     3456789     2500        2400 P

She gets the claims for which she is authorized, not the others. The filtering condition was again applied automatically.

This filtering applies to updates and deletes as well. Say the user Nathan issues a statement:

update
    claims
set
    paid_amount = 1200;


2 rows updated.

Note, only the 2 rows to which Nathan has privileges are updated, not all 5. What if he tries to update a row that exists but for which he does not have authorization? The table has a claim of CLAIM_ID 3, but since the provider of the claim is 2345678, a provider Nathan does not have permission to see, the claim shouldn't be visible to him.

update
    claims
set
    paid_amount = 1200
where
     claim_id = 3;

0 rows updated.

Note how the rows weren't even reported as present. It is as if the rows are never there, as far as Nathan is concerned.

Finally, the issue of inserts comes up. When Nathan tries to insert a record with a provider id for which he is not authorized (e.g. 2345678), he gets an error.

insert into
    claims
values (
    6,
    '123456789',
    34567891,
    2345678,
    1200,
    1100,
    'P'
);
                         *
ERROR at line 1:
ORA-28115: policy with check option violation

Note the new type of error, ORA-28115: policy with check option violation, used to indicate this type of error.

By now, you should have a great deal of appreciation for the value of this tool. You didn't create a view or a trigger. You have defined a rule for viewing (implemented through a function), and the filtering condition is applied automatically to any query the users issue against the table.

The Package dbms_rls

With this example in place, let's explore the main implementing package dbms_rls, and in particular the procedure add_policy. It takes the following parameters.

PARAMETER

DESCRIPTION

OBJECT_SCHEMA

The owner of the object against which this policy is applied. If specified as NULL, the current user or the current schema (if defined) is used.

OBJECT_NAME

This specifies the name of the object, i.e. the table or view, or a synonym on which this policy is defined.

POLICY_NAME

Name of the policy

FUNCTION_SCHEMA

The name of the user who owns the function used as a policy enforcer, e.g. the function get_auth_providers().

POLICY_FUNCTION

The name of the function used to enforce the policy security.

STATEMENT_TYPES

A policy can be restricted to apply for a certain statement only, e.g. SELECT. This may be useful when different types of policies are defined for different actions, such as one for SELECT, one for DELETE, etc. The possible values are SELECT, INDERT, UPDATE, DELETE. The value can also contain a combination of these, separated by comma, e.g. 'SELECT, UPDATE'. The default value is all of the statement types.

UPDATE_CHECK

In the example given above, we have made the policy to apply even to the new value put by the update statement. The default is FALSE, i.e. the policy does not check for the value replaced by the update statement. It takes Boolean values TRUE or FALSE.

ENABLE

Specifies whether the policy is enabled or not. It takes Boolean value and the default is TRUE.

STATIC_POLICY

In the example given above, we have seen that policy function returns a where clause that may be different based on who invokes it. This makes the function dynamic. If the policy function was such that the where condition would be the same regardless of who accesses it, then this value should be TRUE. The default is FALSE, and that is the case in almost all cases of use.

Table 5.3 Parameters for dbms_rls_add_policy

This is a rather simple usage of this great tool. In real life, however, you would probably face a great deal of complexity in defining the security policy. For instance, the access policy of claims could be defined differently for different types of access. Nathan can view the records of providers 1234567, 2345678 and 3456789, but can create a claim record for provider 1234567 only, and update those for providers 1234567 and 2345678, and delete no records.

Another rule may specify that access be restricted based on the claim amount, in addition to the list of authorized providers. Nathan may access claims of $1,500 or less, Judy may access $5,000 or less, and so on and so forth. Keeping this requirement in mind, we created a new table called claim_provider_policy to record the permissions as follows.

USERNAME                   VARCHAR2(20)
ACCESS_TYPE                VARCHAR2(10)
PROVIDER_ID                NUMBER

The records were inserted as follows for NATHAN.

USERNAME             ACCESS_TYP PROVIDER_ID
-------------------- ---------- -----------
NATHAN               SELECT         1234567
NATHAN               SELECT         2345678
NATHAN               SELECT         3456789
NATHAN               INSERT         1234567
NATHAN               UPDATE         1234567
NATHAN               UPDATE         2345678

Similar records may be created for other users, such as Judy and Clara. For the value enforcement, we can have another table called claim_value_policy defined as:

USERNAME                   VARCHAR2(20)
MAX_CLAIM_AMOUNT           NUMBER

The typical records may be similar to the following:

USERNAME             MAX_CLAIM_AMOUNT
-------------------- ----------------
JUDY                             5000
NATHAN                           1500
CLARA                            2500

Now the function to enforce the policy will need to change, too. Since we have separate enforcements for each type of access, we will have to have different functions. Here is a function for SELECT policy.

create or replace function select_auth_claims
(
    p_schema_name    in varchar2,
    p_object_name    in varchar2
)
return varchar2
is
    l_ret        varchar2(2000);
begin
    if p_schema_name = user
    then
        l_ret := '1=1';
    else -- if the user is different from the owner
        for prov_rec in
        (
            select provider_id
            from claim_provider_policy
            where username = user
            and access_type = 'SELECT'
        ) loop
            l_ret := l_ret||','||prov_rec.provider_id;
        end loop;
        l_ret := ltrim(l_ret,',');
        l_ret := 'PROVIDER_ID IN ('||l_ret ||')';
    end if;
    return l_ret;
end;
/

Similarly, the INSERT policy is defined as follows:

create or replace function insert_auth_claims
(
    p_schema_name    in varchar2,
    p_object_name    in varchar2
)
return varchar2
is
    l_ret        varchar2(2000);
begin
    if p_schema_name = user
    then
        l_ret := '1=1';
    else -- if the user is different from the owner
        for prov_rec in
        (
            select provider_id
            from claim_provider_policy
            where username = user
            and access_type = 'INSERT'
        ) loop
            l_ret := l_ret||','||prov_rec.provider_id;
        end loop;
        l_ret := ltrim(l_ret,',');
        l_ret := 'PROVIDER_ID IN ('||l_ret ||')';
    end if;
    return l_ret;
end;
/

The update function is defined as:

create or replace function update_auth_claims
(
    p_schema_name    in varchar2,
    p_object_name    in varchar2
)
return varchar2
is
    l_ret        varchar2(2000);
begin
    if p_schema_name = user
    then
        l_ret := '1=1';
    else -- if the user is different from the owner
        for prov_rec in
        (
            select provider_id
            from claim_provider_policy
            where username = user
            and access_type = 'UPDATE'
        ) loop
            l_ret := l_ret||','||prov_rec.provider_id;
        end loop;
        l_ret := ltrim(l_ret,',');
        l_ret := 'PROVIDER_ID IN ('||l_ret ||')';
    end if;
    return l_ret;
end;
/

Finally, we will create a function to check the maximum claim amount for the claims.

create or replace function check_claim_value_policy
(
    p_schema_name    in varchar2,
    p_object_name    in varchar2
)
return varchar2
is
    l_ret        varchar2(2000);
    l_max_claim_amount number;
begin
    l_ret := NULL;
    if p_schema_name = user
    then
        l_ret := '1=1';
    else -- if the user is different from the owner
         begin
            select max_claim_amount
            into l_max_claim_amount
            from claim_value_policy
            where username = user;
            l_ret := 'CLAIM_AMOUNT <= '||
                l_max_claim_amount;
        exception
            when NO_DATA_FOUND then
                l_ret := NULL;
            when OTHERS then
                raise;
        end;
    end if;
    return l_ret;
end;
/

These functions can be as complicated as you wish them to be. The objective is to find a filtering predicate to be applied to the queries on the table.

Now we have to define three different policies on the table.

begin
dbms_rls.add_policy (
    object_schema    => 'CLAIM_SCHEMA',
    object_name      => 'CLAIMS',
    policy_name      => 'CLAIM_SELECT_POLICY',
    function_schema  => 'CLAIM_SCHEMA',
    policy_function  => 'SELECT_AUTH_CLAIMS',
    statement_types  => 'SELECT'
);
end;
/

For the INSERT statements:

begin
dbms_rls.add_policy (
    object_schema    => 'CLAIM_SCHEMA',
    object_name      => 'CLAIMS',
    policy_name      => 'CLAIM_INSERT_POLICY',
    function_schema  => 'CLAIM_SCHEMA',
    policy_function  => 'INSERT_AUTH_CLAIMS',
    statement_types  => 'INSERT',
    update_check     => TRUE
);
end;
/

For the UPDATE statements:

begin
dbms_rls.add_policy (
    object_schema    => 'CLAIM_SCHEMA',
    object_name     => 'CLAIMS',
    policy_name      => 'CLAIM_UPDATE_POLICY',
    function_schema  => 'CLAIM_SCHEMA',
    policy_function  => 'UPDATE_AUTH_CLAIMS',
    statement_types  => 'UPDATE',
    update_check     => TRUE
);
end;
/

For the claim value check:

begin
dbms_rls.add_policy (
    object_schema    => 'CLAIM_SCHEMA',
    object_name      => 'CLAIMS',
    policy_name      => 'CLAIM_VALUE_POLICY',
    function_schema  => 'CLAIM_SCHEMA',
    policy_function  => 'CHECK_CLAIM_VALUE_POLICY',
    statement_types  =>
        'SELECT, INSERT, DELETE, UPDATE',
    update_check     => TRUE
);
end;
/

Note how we have defined several policies on a single table. During a select query, two policies are applied – claim_value_policy and select_auth_claims. During execution, the predicates retuned by both queries are applied to the table. If NATHAN issues a query:

select * from claims

It is rewritten to:

select * from
(select * from claims)
where
(
    provider_id in (1234567,2345678,3456789)
    AND
    claim_amount <= 1500
)

* Important: When more than one policy is defined on a table, the output of all the policies are AND’d to arrive at the final where clause.

After the policies are defined, it's now time to test the setup.

connect nathan/*

select * from claim_schema.claims;

C SSN        MEMBER_ID PROVIDER_ID  Amou PAID_AMOUNT S
-- --------- ---------- ----------- ----- ----------- -
1 123456789      12345     1234567  1200        1200 P
3 234567891      12345     2345678  1400        1300 P

See how both the policies claim_value_policy and select_auth_claims have been applied to the table, and the filtering predicates from both the policies have been applied to the query? The same effect occurs when UPDATE and INSERT occur on the table, too.

 

This is an excerpt from the book "Oracle Privacy Security Auditing".

You can buy it direct from the publisher for 30%-off and get instant access to the code depot of Oracle security and auditing scripts.


 

 
  
 

 
 
 
 
Oracle performance tuning software
 
 

 

 
 
 
Oracle performance Tuning 10g reference poster
 
 
 
Oracle training in Linux commands
 
Oracle training Excel
 
Oracle training & performance tuning books
 

 

Burleson is the American Team

Note: This Oracle documentation was created as a support and Oracle training reference for use by our DBA performance tuning consulting professionals.  Feel free to ask questions on our Oracle forum.

Verify experience! Anyone considering using the services of an Oracle support expert should independently investigate their credentials and experience, and not rely on advertisements and self-proclaimed expertise. All legitimate Oracle experts publish their Oracle qualifications.

Errata?  Oracle technology is changing and we strive to update our BC Oracle support information.  If you find an error or have a suggestion for improving our content, we would appreciate your feedback.  Just  e-mail:  and include the URL for the page.


                    









Burleson Consulting

The Oracle of Database Support

Oracle Performance Tuning

Remote DBA Services


 

Copyright © 1996 -  2012 

All rights reserved.

Oracle © is the registered trademark of Oracle Corporation.