Thursday, September 11, 2014

What constitute "default auditing" under traditional auditing?

From the Oracle Documentation:

"When you use Database Configuration Assistant (DBCA) to create a new database, Oracle Database configures the database to audit the most commonly used security-relevant SQL statements and privileges"

and

"If you manually create a database, then you should run the secconf.sql script to apply the default audit settings to your database"

Oracle Database audits the following privileges by default:

  • ALTER ANY PROCEDURE
  • CREATE ANY LIBRARY
  • DROP ANY TABLE
  • ALTER ANY TABLE
  • CREATE ANY PROCEDURE
  • DROP PROFILE
  • ALTER DATABASE
  • CREATE ANY TABLE
  • DROP USER
  • ALTER PROFILE
  • CREATE EXTERNAL JOB
  • EXEMPT ACCESS POLICY
  • ALTER SYSTEM
  • CREATE PUBLIC DATABASE LINK
  • GRANT ANY OBJECT PRIVILEGE
  • ALTER USER
  • CREATE SESSION
  • GRANT ANY PRIVILEGE
  • AUDIT SYSTEM
  • CREATE USER
  • GRANT ANY ROLE
  • CREATE ANY JOB
  • DROP ANY PROCEDURE

    Oracle Database audits the following SQL shortcuts by default:

  • ROLE
  • SYSTEM AUDIT
  • PUBLIC SYNONYM
  • DATABASE LINK
  • PROFILE
  • SYSTEM GRANT

    Remember that secconf.sql turns on audting regardless of your database using Unified Auditing or Traditional Auditing.

    How?

    When executed manually, the DBA is prompted for which type of auditing style that should be considered:
    sqlplus / as sysdba @secconf.sql
    
    Session altered.
    
    
    Profile altered.
    
    Do you wish to configure 11g style Audit Configuration OR
    Do you wish to configure 12c Unified Audit Policies?
    Enter RDBMS_11G for former or RDBMS_UNIAUD for latter
    Enter value for 1: RDBMS_11G
    old   7:   USER_CHOICE := '&1';
    new   7:   USER_CHOICE := 'RDBMS_11G';
    
    What I have found, is that if you intend to harden your Traditional Auditing policies by executing the script in a database where the Unified Auditing polices exist but is disabled, or a database running in "Mixed mode" auditing, the script will fail:
    DECLARE
    *
    ERROR at line 1:
    ORA-46358: Audit policy ORA_ACCOUNT_MGMT already exists.
    ORA-06512: at line 9
    
    A simple workaround in such a case is to simply comment out the code that is irrelevant to your desired type of auditing, and rerun the script.

    You can disable default auditing if you wish, see the section "Disabling and Enabling Default Audit Settings"

    To check whether or not default auditing has been actived, you can query the view DBA_PRIV_AUDIT_OPTS, which describes current system privileges being audited across the system and by user. If the column USERNAME is NULL, you have introduced system-wide auditing.
  • Tuesday, September 2, 2014

    How to convert stored outlines to use SQL Plan Baselines

    If you have been using stored outlines for plan stability in versions prior to Oracle 11g, you should migrate them to use SQL Plan Baselines instead. Stored outlines is, according to oracle, "a legacy technique for providing plan stability".

    My database had a number of stored outlines:

    SELECT OWNER, CATEGORY, USED, MIGRATED,COUNT(*) 
    FROM   DBA_OUTLINES
    GROUP BY OWNER,CATEGORY,USED,MIGRATED
    ORDER BY MIGRATED;
    
    OWNER CATEGORY USED MIGRATED COUNT(*)
    USER1 DEFAULT USED MIGRATED
    3
    USER2 DEFAULT USED MIGRATED
    1
    USER3 DEFAULT USED NOT-MIGRATED
    7
    USER1 DEFAULT USED NOT-MIGRATED
    7
    USER4 DEFAULT USED NOT-MIGRATED
    1
    USER2 DEFAULT USED NOT-MIGRATED
    36

    I created the following pl/sql to convert them to SQL Plan Baselines using the package DBMS_SPM.MIGRATE_STORED_OUTLINE:

    SET TRIMSPOOL ON
    SET LINES 200
    SET PAGES 200
    ALTER SESSION SET NLS_LANGUAGE='AMERICAN'; <-- to get English messages during execution
    SPOOL CONVERT_OUTLINES.LOG
    SET SERVEROUTPUT ON
    DECLARE
      L_CLOB            CLOB; -- will display the resulting report
    
      CURSOR C1 IS
        SELECT OWNER,NAME
        FROM DBA_OUTLINES
        WHERE MIGRATED = 'NOT-MIGRATED';
    
        C1_REC C1%ROWTYPE;
    
     BEGIN
        DBMS_OUTPUT.ENABLE( 1000000 );
    
        IF NOT C1%ISOPEN THEN
           OPEN C1;
        END IF;
     
         LOOP
           FETCH C1 INTO C1_REC;
           EXIT WHEN C1%NOTFOUND;
           DBMS_OUTPUT.PUT_LINE('Now converting: ' || C1_REC.OWNER || '.' || C1_REC.NAME);
           L_CLOB := DBMS_SPM.MIGRATE_STORED_OUTLINE( ATTRIBUTE_NAME=>'OUTLINE_NAME', ATTRIBUTE_VALUE=>C1_REC.NAME, FIXED=>'NO');
           DBMS_OUTPUT.PUT_LINE( L_CLOB );
         END LOOP;
        CLOSE C1;
    END;
    /
    EXIT
    

    The resulting log files shows that several of the stored outlines could not be converted:
    Now converting: USER2.SYS_OUTLINE_11021513055564321
    -------------------------------------------------------------------------------
    
                   Migrate Stored Outline to SQL Plan Baseline
    
    Report
    -------------------------------------------------------------------------------
    
    Summary:
    --------
    
    Number of stored outlines to be migrated: 1
    Stored outlines migrated successfully: 0
    Stored outlines failed to be migrated: 1
    
    Summary of Reasons for failure:
    -------------------------------
    
    Number of invalid stored outlines: 1
    
    Details on stored outlines not migrated or name changes during migration:
    -------------------------------------------------------------------------
    
    * Notes on name change:
    * New SQL plan baselines are assigned the same names as their original stored 
    * outlines. If a stored outline has the same name as an existing
    * SQL plan baseline, a system generated name is used for the new
    * SQL plan baseline.
    

    I then checked with the developers. It turned out that the outlines that didn't convert properly were remnants from the database when it was totally different laid out - the schemas had by now diverged and I could simply ignore these obsolete outlines.

    So the last step was simply to generate a drop-script for the non-migrated outlines and then execute these:
    SELECT 'DROP OUTLINE ' || NAME || ';'
    FROM DBA_OUTLINES
    WHERE MIGRATED = 'NOT-MIGRATED';
    

    Friday, August 29, 2014

    Login storm against database caused exhausted library cache

    One of our databases experienced massive contention in the shared pool, in form of wait events alerted as "library cache locks".

    The database was very small indeed, so my natural instinct was to throw some more memory at the virtual host, and rearrange the memory parameters.

    This turned out to be a misconception; the resources were sufficient for the instance to work properly.

    The problem was caused by an incorrect password configuration on the application server.

    What we could observe was:

  • A totally exhausted shared pool, caused by "library cache lock"
  • The SQL that seemed to be repeatedly executed was
    SELECT /*+ connect_by_filtering */
              privilege#, LEVEL
          FROM sysauth$
    CONNECT BY grantee# = PRIOR privilege# AND privilege# > 0
    START WITH grantee# = :1 AND privilege# > 0;
    
    
    SELECT privilege#
      FROM sysauth$
    WHERE (grantee# = :1 OR grantee# = 1) AND privilege# > 0;
    
  • The V$EVENT_NAME view showed that the wait event was accompanied by the additional information found in the columns parameter1 through parameter3, which turned out to be helpful further on:
    select  name, wait_class,parameter1,parameter2,parameter3
    from v$event_name
    where wait_class = 'Concurrency'
    and name = 'library cache lock';
    

    NAME WAIT_CLASS PARAMETER1 PARAMETER2 PARAMETER3
    library cache lock Concurrency handle address lock address 100*mode+namespace

    Further research showed that the problem was due to a built-in delay between failed login attempts in Oracle 11g:

    "The 'library cache lock' wait is seen due to the fact that the account status gets updated due to incorrect login.
    To prevent password guessing attack, there's a sleep() in the code when incorrect login attempts exceed count of 3.
    And because of this sleep() you see a wait on library cache, as the process is yet to release the lock."


  • In release 11.1.0.7, patch 7715339 was released to remove this delay.
  • In release 11.2.X, the DBA must set an event to remove the delay, as follows:

    alter system set events '28401 trace name context forever, level 1'; 
    

    According to Oracle, the purpose of the built-sleep is to make it harder to succeed in a "password guessing attack", particularly in cases where FAILED_LOGIN_ATTEMPTS is set to UNLIMITED. Oracle Development is pointing out that disabling the sleep-function is not recommended. A better solution is to set the FAILED_LOGIN_ATTEMPTS to a reasonable value.
    When the number of failed login attempts for a session hits the limit, the account will be locked. Subsequent logon attempts with incorrect password will then be rejected immediately without any contention in the library cache.

    See Bug 15882590 : 'LIBRARY CACHE LOCK' DURING WRONG PASSWORD LOGON ATTEMPTS on My Oracle Support (MOS) for further information.


  • Thursday, August 28, 2014

    How to use the case statement in a shell script, with case-insensitive input

    Use the pipe (|) character for each possible case:

    echo "TABLESPACE REPORT FOR $ORACLE_SID"
    
    stty echo
       echo "All tablespaces or specific tablespace? [A|S]"
       read answer
    stty echo
    
    case $answer in
    A|a) echo "All tablespaces selected...please wait...";
        getFiles A;;
    S|s) echo "Which tablespace?";
        read wt;
        getFiles $wt;;
    *) echo "Only A or S are supported parameters.";
        exit 1;;
    esac
    

    Tuesday, August 26, 2014

    How to find the SID, serial# and their operating system process ID from the database

    A simple statement to find the SID, serial# and their operating system process ID from the database:

    SET LINES 300 PAGES 300
    SET TRIMSPOOL ON
    SPOOL SQL
    COL "Os pid" FORMAT A10
    COL MACHINE  FORMAT A30
    COL "SQL Text" FORMAT A100 WRA
    COL PROGRAM  FORMAT A20
    COL USERNAME FORMAT A12
    
    SELECT
      S.SID,
      S.SERIAL#,
      S.USERNAME,
      S.STATUS,
      P.SPID "Os pid",
      S.MACHINE,
      CONCAT(SUBSTR(A.SQL_TEXT,1,60), '...(output truncated)')  "SQL text"
    FROM V$SESSION S,
              V$SQLAREA A,
              V$PROCESS P
    WHERE A.ADDRESS = S.SQL_ADDRESS
    AND S.PADDR=P.ADDR
    ORDER BY 1
    /
    
    Example output:
    SID          SERIAL# USERNAME     Os pid     MACHINE          SQL text
    ---------- ---------- ------------ ---------- -------------  ------------------------------------------------------------------------------------
             4          3              6357052    myserver       insert into obj$(owner#,name,namespace,obj#,type#,ctime,mtim...(output truncated)
            18       7035 SYS          21496004   myserver       call DBMS_AQADM_SYS.REGISTER_DRIVER (  )...(output truncated)
           107      62143 SYS          23527460   myserver       SELECT   S.SID,   S.SERIAL#,   S.USERNAME,   P.SPID "Os pid"...(output truncated)
           303      27191 SYS          16777372   myserver       analyze table scott.man_to_stage validate structure cascade o...(output truncated)
           393          1              8716332    myserver       insert into obj$(owner#,name,namespace,obj#,type#,ctime,mtim...(output truncated)
           402       3125 BATCHUSR     18612294   myserver       call mypackage.start_dorg (  )...(output truncated)
           490          1              8257548    myserver       insert into obj$(owner#,name,namespace,obj#,type#,ctime,mtim...(output truncated)
           499      25461 SYS          13762730   myserver       call DBMS_AQADM_SYS.REGISTER_DRIVER (  )...(output truncated)
           503       2635 SYS          11272234   myserver       call DBMS_AQADM_SYS.REGISTER_DRIVER (  )...(output truncated)
           593      12091 BATCHUSR     19726590   myserver       call myprocedure.read_queue (  )...(output truncated)
           600       9193 SYS          21102724   COMP\PC1       select * from dba_locks...(output truncated)
           694      44601 BATCHUSR     20840656   myserver       call mypackage.check_job_consistency(  )...(output truncated)
           696        819 SYS          15269968   myserver       call DBMS_AQADM_SYS.REGISTER_DRIVER (  )...(output truncated)
           700       1135 BATCHUSR     21364890   myserver       DECLARE job BINARY_INTEGER := :job;  next_date TIMESTAMP WIT...(output truncated)
    


    What is the meaning of the "10G 11G" value in DBA_USERS.PASSWORD_VERSIONS?

    When the value of DBA_USERS.PASSWORD_VERSIONS is shown as "10G 11G", it means that both old and new-style hash values are available for the user.

    Note that instead of storing the hashed password values directly in the DBA_USERS table, they are from 11gR1 and onwards both stored in the table USER$, column PASSWORD for the 10G style hash value, and column SPARE4 for the 11G SHA-1 style hash value.

    A NULL value indicates that the password has not been changed since the migration and the user still has the old case insensitive password.

    In my query below, USER1 through USER4 have been migrated and will now take advantage of 11g password case-sensitivity, if enabled.

    USER5 is still using 10G style case insensitive passwords.

    USER6 is created after migration, and will also take advantage of the 11g password case-sensitivity, if available.

    SELECT U.NAME "Name",
                NVL(REGEXP_REPLACE(U.SPARE4,'^.+', 'Password changed since migration',1,0), 'Password unchanged since migration') "Action undertaken",
                DU.PASSWORD_VERSIONS "Password Version"
    FROM USER$ U LEFT OUTER JOIN DBA_USERS DU
    ON U.NAME = DU.USERNAME
    WHERE DU.USERNAME NOT IN (SELECT ROLE FROM DBA_ROLES)
    ORDER BY NAME ASC;
    


    Name Action undertaken Password Version
    USER1 Password changed since migration 10G 11G
    USER2 Password changed since migration 10G 11G
    USER3 Password changed since migration 10G 11G
    USER4 Password changed since migration 10G 11G
    USER5 Password unchanged since migration 10g
    USER6 Password changed since migration 11G

    Users that are imported from an earlier release into an 11g database, will remain case-insensitive until the password is changed.


    To generate "alter user identified by values" statements, use the following:

    SELECT 'alter user ' || NAME || ' identified by values ' || '''' || SPARE4 ||';' || PASSWORD ||''';' 
    FROM USER$ 
    WHERE  NAME IN ('USER1','USER2');
    

    Sources: Oracle Documentation

    Monday, August 25, 2014

    How to use trace event 10046


    ALTER SESSION SET max_dump_file_size = unlimited;
    ALTER SESSION SET tracefile_identifier = 'normal_run_with_trace_10046';
    ALTER SESSION SET statistics_level = ALL;
    ALTER SESSION SET events '10046 trace name context forever, level 12';
    
    Your SQL statement(s) here
    
    ALTER SYSTEM SET EVENTS '10046 trace name context off';
    EXIT
    

    Alternatively, use the oradebug utility:

    select s.sid,s.serial#,p.spid
    from v$session s, v$process p 
    where sid = 29
    and s.paddr = p.addr
    

    Output:
    SID SERIAL# SPID
    29
    9
    24510662
    Check that the process is indeed there:
    prodserver1>ps -ef | grep 24510662
      ora11g 24510662        1   0 17:24:42      -  0:00 oraclemagr (LOCAL=NO)
    prodserver1>proctree 24510662
      24510662    oraclemagr (LOCAL=NO)
    

    Enable tracing on the operating system process, inside sqlplus:
    SQL> oradebug setospid 24051998
    Oracle pid: 420, Unix process pid: 24051998, image: oracle@ystu032ma
    SQL> oradebug unlimit
    SQL> oradebug event 10046 trace name context forever, level 12
    

    When done, disable tracing:
    SYS@magr SQL> oradebug setospid 24051998
    Oracle pid: 420, Unix process pid: 24051998, image: oracle@ystu032ma
    
    SQL> oradebug unlimit
    SQL> oradebug event 10046 trace name context forever, level 12
    

    You can now parse the trace file produced through tkprof:
    tkprof mytracefile.trc mytracefile.out sys=yes waits=yes sort=exemis
    

    For an extensive list of commands to be used with tkprof, simply type tkprof at the prompt.

    An excellent note on 10046 trace event is called "How To Collect 10046 Trace (SQL_TRACE) Diagnostics for Performance Issues (Doc ID 376442.1)".

    Another great note on tracing in general is "Tracing Enhancements Using DBMS_MONITOR (In 10g, 11g and Above) (Doc ID 293661.1)"

    - both available on Oracle Supports site.