1 Session ID 36652 Database Oracle Sleuth: Who Did It? Sitansu S. Mittra, Computer Sciences Corporation Abstract The security of a database depends on three main factors: Prevention of unauthorized activities with the database, Detection of harmful activities against the database, intentional or unintentional, and Correction of impact of such activities, as far as possible. A DBA addresses the first issue by controlling the user access to data through passwords, roles, privileges, profile, etc.; the second issue by setting up and monitoring audit trails, as well as via the LogMiner utility; and the third issue primarily by using the ‘sql_undo’ and ‘sql redo’ entries of v$logmnr_contents in LogMiner, where possible. The presentation discusses each issue with scripts, examples, and reference materials from the author’s experience as a Senior Oracle DBA. The relevant features of auditing and usage of LogMiner under Oracle 9i are covered with references to Oracle 8i for the users who still use 8i. A bibliography appears at the end of the paper. Table of Contents 1. Prevention of unauthorized user access – roles, privileges, profiles 2. Detection of harmful activities – AUDIT TRAIL, LogMiner 3. Correction of impact of harmful activities via ‘undo’ and ‘redo’ entries – LogMiner 4. Roadmap for Ensuring Data Security in Oracle Databases 5. Bibliography Appendix 1 – LogMiner Scripts for Oracle 8i Appendix 2 – LogMiner Scripts for Oracle 9i 1. Prevention of Unauthorized User Access Oracle administers the user access to the database through a hierarchy as described below: 2 Create a user account with an account name and a password, though a password is not mandatory. Assign one or more roles to the user. Some or all of these roles can be designated as DEFAULT role(s) for the user. Assign system level and object level privileges to each role. Set up a profile for the user and assign it to the user. If no profile is created and assigned, Oracle assigns the DEFAULT profile to the user. The relationship among users, roles, privileges, and profiles are as follows: M:N USER <<---------------------->> ROLE M:N ROLE <<---------------->> PRIVILEGE M:1 USER <<--------------------> PROFILE 1.1 Roles A role is a named collection of privileges that are granted to the role. Developers and DBAs use roles to assign system and object level privileges to users. A role can be granted to a user or to another role. Roles help the administration of the privileges granted to users. 1.2 Privileges A privilege is the capability to perform authorized function(s) in the database. A privilege can be assigned directly to a user, or can be assigned to a role. In the latter case, all users who are granted the role get the privilege. A system privilege enables a user to perform designated tasks in the database. There are 126 such privileges that can be listed with the command: select distinct NAME from system_privilege_map order by 1; We can find out which of these privileges are actually granted to roles or users in a specific database by the following command: select distinct PRIVILEGE from dba_sys_privs order by 1; An object privilege enables a user to perform primarily DML type tasks on designated objects (tables, views, sequences, procedures) in the database. There are 8 such privileges that can be listed with the command: select distinct privilege from dba_tab_privs order by 1; 1.3 GRANT and REVOKE 3 Privileges are assigned to roles or users and roles are assigned to other roles or users via the GRANT command. Session Transcript SQL>create role A; Role created. SQL>create role B; Role created. SQL>create user X identified by ***** 2 default tablespace USER_DATA 3 temporary tablespace TEMP_DATA 4 quota 400M on USER_DATA 5 quota 400M on TEMP_DATA 6 quota 0 on SYSTEM; User created. (NOTE: Always include the clauses “default tablespace” and “temporary tablespace”. Otherwise, the SYSTEM tablespace will be assigned as the default and temporary tablespaces for the user X. Line 6 ensures that X cannot use the SYSTEM tablespace for any work.) SQL> grant select any table, execute any procedure, insert any table, 2 delete any table, update any table to A; Grant succeeded. SQL> grant create session to A; Grant succeeded. SQL> select privilege from dba_sys_privs where grantee = 'A' order by 1; PRIVILEGE ---------------------------------------CREATE SESSION DELETE ANY TABLE EXECUTE ANY PROCEDURE INSERT ANY TABLE SELECT ANY TABLE UPDATE ANY TABLE 6 rows selected. SQL> select privilege from dba_tab_privs where grantee = 'A' order by 1; no rows selected (NOTE: A does not have any object level privilege.) Privileges granted directly to a role or a user can be displayed by querying dba_sys_privs or dba_tab_privs, as the case may be. But privileges granted indirectly to a role or a user cannot be so displayed. 4 Session Transcript SQL> grant create any table, insert any table, create session to B; Grant succeeded. SQL> create role C; Role created. SQL> grant B to C; Grant succeeded. SQL> grant C to X; Grant succeeded. SQL> select grantee, privilege from dba_sys_privs where grantee in ('A', 'B', 'C', 'X') 2 order by grantee, privilege; GRANTEE -----------------------------A A A A A A B B B PRIVILEGE ---------------------------------------CREATE SESSION DELETE ANY TABLE EXECUTE ANY PROCEDURE SELECT ANY TABLE UPDATE ANY TABLE INSERT ANY TABLE CREATE ANY TABLE CREATE SESSION INSERT ANY TABLE 8 rows selected. (Note that C and X are not listed since they were granted the privileges indirectly. But X does have the privileges granted to B. For example, X can create a table and insert rows into the table, as shown below.) SQL> connect x/***** Connected. SQL> show user USER is "X" SQL> create table P 2 (m number not null, 3 n varchar2 (10)); Table created. SQL> insert into P values (17, 'sample'); 1 row created. SQL> select * from P; M N --------- ---------- 5 17 sample SQL> drop table P cascade constraints; Table dropped. SQL> commit; Commit complete. Privileges granted directly to a role or a user can be taken away from that role or user via the REVOKE command. But privileges granted indirectly to a role or a user cannot be so taken away. You must find the direct grantee from whom you can revoke the privileges. Session Transcript SQL> revoke insert any table from A; Revoke succeeded. SQL> revoke insert any table from X; revoke insert any table from X * ERROR at line 1: ORA-01952: system privileges not granted to 'X' The following data dictionary views contain information about roles and privileges: DBA_ROLES DBA_ROLE_PRIVS ROLE_ROLE_PRIVS DBA_SYS_PRIVS DBA_TAB_PRIVS 1.4 all roles that exist in the database roles granted to users/roles roles granted to other roles system privileges granted to users/roles object level privileges granted to users/roles Object Privilege: GRANT WITH GRANT OPTION One can grant an object privilege to a user WITH GRANT OPTION whereby the user can grant those privileges to other users or roles. These are called grantable privileges and can be assigned only to users, but not to roles. Session Transcript SQL> grant select, insert, update, delete on invoice_status to X with grant option; Grant succeeded. SQL> connect x/***** Connected. SQL> show user USER is "X" SQL> grant select, insert, update, delete on invoice_status to B; 6 Grant succeeded. SQL> grant select any table to X with grant option; grant select any table to X with grant option * ERROR at line 1: ORA-01939: only the ADMIN OPTION can be specified (NOTE: ‘select any table’ is NOT an object privilege. It is a system privilege and must be granted WITH ADMIN OPTION. So, it cannot be granted WITH GRANT OPTION.) SQL> select grantee, table_name, privilege, grantable from dba_tab_privs 2 where grantee in ('A', 'B', 'C', 'X') 3 order by grantee, privilege; GRANTEE ---------B B B B X X X X TABLE_NAME -----------------------------INVOICE_STATUS INVOICE_STATUS INVOICE_STATUS INVOICE_STATUS INVOICE_STATUS INVOICE_STATUS INVOICE_STATUS INVOICE_STATUS PRIVILEGE ---------DELETE INSERT SELECT UPDATE DELETE INSERT SELECT UPDATE GRA --NO NO NO NO YES YES YES YES 8 rows selected. 1.5 System Privilege: GRANT WITH ADMIN OPTION One can grant a system privilege to a user or a role WITH ADMIN OPTION whereby the user can grant those privileges to other users or roles. Session Transcript SQL> grant select any table to X with admin option; Grant succeeded. SQL> connect x/***** Connected. SQL> show user USER is "X" SQL> grant select any table to B; Grant succeeded. NOTE: WITH GRANT OPTION can be used for object privileges alone and WITH ADMIN OPTION can be used for system privileges alone. 1.6 DEFAULT ROLE A user can have one or more roles. By default, when a user logs in, all of his/her roles are activated. One can designate a subset of these roles as default roles. In that case, only the default roles are activated at logon time. 7 Session Transcript SQL> create role D; Role created. SQL> grant D to X; Grant succeeded. SQL> select * from DBA_ROLE_PRIVS where grantee = 'X' 2 order by GRANTEE, GRANTED_ROLE; GRANTEE ---------X X X GRANTED_ROLE -----------------------------B C D ADM --NO NO NO DEF --YES YES YES SQL> alter user X default role C, D; User altered. SQL> select * from DBA_ROLE_PRIVS where grantee = 'X' 2 order by GRANTEE, GRANTED_ROLE; GRANTEE ---------X X X 1.7 GRANTED_ROLE -----------------------------B C D ADM --NO NO NO DEF --NO YES YES PROFILE for RESOURCE LIMITS and PASSWORD MANAGEMENT A profile is a collection of quotas and privileges related to the following system resource limits and password management: System Resource CPU time I/O operation Idle time Connect time Memory space as private SQL area for MTS only Concurrent sessions Password Management Password aging and expiration Password history Password complexity verification Account locking A user can have only one profile at a time. Oracle provides a DEFAULT profile that is assigned to all users unless a different profile is created via CREATE 8 PROFILE command and then assigned to the user via ALTER USER command. Any created profile can be dropped, but not the DEFAULT profile created by Oracle. All limits of the DEFAULT profile are UNLIMITED. But the DBA can change the values via ALTER PROFILE command so that the modified values apply to all users who are assigned the DEFAULT profile. Session Transcript SQL> col profile format a15 SQL> col limit format a10 SQL> select * from dba_profiles order by RESOURCE_TYPE; PROFILE ---------DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT RESOURCE_NAME -------------------------------COMPOSITE_LIMIT SESSIONS_PER_USER CPU_PER_CALL LOGICAL_READS_PER_CALL CONNECT_TIME IDLE_TIME LOGICAL_READS_PER_SESSION CPU_PER_SESSION PRIVATE_SGA FAILED_LOGIN_ATTEMPTS PASSWORD_LIFE_TIME PASSWORD_REUSE_MAX PASSWORD_LOCK_TIME PASSWORD_GRACE_TIME PASSWORD_VERIFY_FUNCTION PASSWORD_REUSE_TIME RESOURCE_TYPE ------------KERNEL KERNEL KERNEL KERNEL KERNEL KERNEL KERNEL KERNEL KERNEL PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD LIMIT ----UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED 16 rows selected. SQL> select username, profile from dba_users where username = 'X'; USERNAME PROFILE ------------------------------ ---------X DEFAULT SQL> 2 3 4 5 create profile NEW_PROFILE limit CONNECT_TIME 480 SESSIONS_PER_USER 4 FAILED_LOGIN_ATTEMPTS 3 PASSWORD_GRACE_TIME 5; Profile created. SQL> select * from dba_profiles order by profile, resource_type; PROFILE --------------DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT RESOURCE_NAME -------------------------------COMPOSITE_LIMIT SESSIONS_PER_USER CPU_PER_CALL LOGICAL_READS_PER_CALL CONNECT_TIME IDLE_TIME LOGICAL_READS_PER_SESSION RESOURCE -------KERNEL KERNEL KERNEL KERNEL KERNEL KERNEL KERNEL LIMIT ---------UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED 9 DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE CPU_PER_SESSION PRIVATE_SGA FAILED_LOGIN_ATTEMPTS PASSWORD_LIFE_TIME PASSWORD_REUSE_MAX PASSWORD_LOCK_TIME PASSWORD_GRACE_TIME PASSWORD_VERIFY_FUNCTION PASSWORD_REUSE_TIME COMPOSITE_LIMIT PRIVATE_SGA CONNECT_TIME IDLE_TIME LOGICAL_READS_PER_CALL KERNEL KERNEL PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD KERNEL KERNEL KERNEL KERNEL KERNEL UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED UNLIMITED DEFAULT DEFAULT 480 DEFAULT DEFAULT PROFILE --------------NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE NEW_PROFILE RESOURCE_NAME -------------------------------LOGICAL_READS_PER_SESSION SESSIONS_PER_USER CPU_PER_CALL CPU_PER_SESSION FAILED_LOGIN_ATTEMPTS PASSWORD_LIFE_TIME PASSWORD_REUSE_MAX PASSWORD_LOCK_TIME PASSWORD_GRACE_TIME PASSWORD_VERIFY_FUNCTION PASSWORD_REUSE_TIME RESOURCE -------KERNEL KERNEL KERNEL KERNEL PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD LIMIT ---------DEFAULT 4 DEFAULT DEFAULT 3 DEFAULT DEFAULT DEFAULT 5 DEFAULT DEFAULT 32 rows selected. SQL> alter user X profile new_profile; User altered. SQL> select username, profile from dba_users where username = 'X'; USERNAME PROFILE ------------------------------ --------------X NEW_PROFILE SQL> 2 3 4 alter profile DEFAULT limit SESSIONS_PER_USER 6 CPU_PER_CALL 3600 PASSWORD_REUSE_MAX 2; Profile altered. SQL> select * from dba_profiles where profile = 'DEFAULT' 2 order by RESOURCE_TYPE; PROFILE --------------DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT RESOURCE_NAME RESOURCE_TYPE LIMIT ------------------------------- ------------- -------COMPOSITE_LIMIT KERNEL UNLIMITED SESSIONS_PER_USER KERNEL 6 CPU_PER_SESSION KERNEL UNLIMITED CPU_PER_CALL KERNEL 3600 LOGICAL_READS_PER_SESSION KERNEL UNLIMITED LOGICAL_READS_PER_CALL KERNEL UNLIMITED IDLE_TIME KERNEL UNLIMITED CONNECT_TIME KERNEL UNLIMITED 10 DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT DEFAULT PRIVATE_SGA FAILED_LOGIN_ATTEMPTS PASSWORD_LIFE_TIME PASSWORD_REUSE_TIME PASSWORD_REUSE_MAX PASSWORD_VERIFY_FUNCTION PASSWORD_LOCK_TIME PASSWORD_GRACE_TIME KERNEL PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD PASSWORD UNLIMITED UNLIMITED UNLIMITED UNLIMITED 2 UNLIMITED UNLIMITED UNLIMITED 16 rows selected. Any changes made to a profile via ALTER PROFILE command are NOT effective for the current session. They take effect for all subsequent sessions. A profile that has users assigned to it can be dropped only via the command DROP PROFILE profile CASCADE. In that case, Oracle assigns the DEFAULT profile to all users who had the dropped profile as their default profile. Session Transcript SQL> drop profile new_profile; drop profile new_profile * ERROR at line 1: ORA-02382: profile NEW_PROFILE has users assigned, cannot drop without CASCADE SQL> drop profile new_profile cascade; Profile dropped. SQL> select username, profile from dba_users where username = 'X'; USERNAME PROFILE ------------------------------ --------------X DEFAULT 1.8 PUBLIC as User PUBLIC is a special user in Oracle. Any system level or object level privilege granted to PUBLIC can be used by all current and future users of the database. Ideally, privileges should not be granted to PUBLIC. If any privilege is granted temporarily to PUBLIC, it should be revoked as soon as such privilege is no longer needed. The privileges granted to PUBLIC can be determined by running the following queries: select privilege from dba_sys_privs where grantee = 'PUBLIC' order by 1; select privilege from dba_tab_privs where grantee = 'PUBLIC' order by 1; 11 2. Detection of harmful activities A DBA can track all user activities via the auditing capability provided by Oracle. Three different types of action can be audited: login attempts, database actions, and access (retrieval and update) to database objects. The prerequisite to enable any auditing is to set the initialization parameter AUDIT_TRAIL to the value TRUE or DB (default is FALSE or NONE). Then audit records are written to the table SYS.AUD$. Oracle offers several DBA_ views containing the various types of audit records depending on the audit type. 2.1 Login Attempts Proceed as follows: (a) (b) (c) (d) Login as a DBA privileged account. Type “audit session;” Allow users to login as needed. Run the following query to see the login and logout activities, both successful and unsuccessful select username, os_username, to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Login Time", to_char (LOGOFF_TIME, 'dd-mon-yyyy hh24:mi:ss') "Logout Time", decode (returncode, 0, 'Connected', 01017, 'invalid username/password', 01005, 'null password given', returncode) "Login Status" from dba_audit_session order by 1, 3 desc; Note that "Login Status" will display the value 'null password given' only if you enter blanks for the password by hitting the space bar and then press ‘Enter’. If you press only ‘Enter’ for the password, then Oracle ignores that action and does not create any record in dba_audit_session. Partial Session Transcript SQL> audit session; Audit succeeded. SQL> commit; Commit complete. SQL> connect dqsmith@monami.dev01 Enter password: ********** Connected. 12 SQL> show user USER is "DQSMITH" SQL> connect dqsmith@monami.dev01 Enter password: **** ERROR: ORA-01005: null password given; logon denied Warning: You are no longer connected to ORACLE. SQL> connect sekhar/******@monami.dev01 Connected. . . . . . . . . . . . . SQL> SQL> SQL> 2 3 4 5 6 7 8 9 col OS_USERNAME format a12 col username format a10 select username, os_username, to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Login Time", to_char (LOGOFF_TIME, 'dd-mon-yyyy hh24:mi:ss') "Logout Time", decode (returncode, 0, 'Connected', 01017, 'invalid username/password', 01005, 'null password given', returncode) "Login Status" from dba_audit_session order by 1, 3 desc; USERNAME OS_USERNAME Login Time Logout Time ---------- ------------ -------------------- -------------------Login Status ---------------------------------------DQSMITH Mittra 22-jul-2003 09:45:49 null password given DQSMITH Connected Mittra 22-jul-2003 09:40:42 22-jul-2003 09:45:45 DQSMITH Connected Mittra 18-jul-2003 15:36:38 18-jul-2003 16:34:46 DQSMITH Mittra 18-jul-2003 14:04:44 invalid username/password DQSMITH Connected Mittra 18-jul-2003 12:50:06 18-jul-2003 12:51:03 . . . . . . . 14 rows selected. If a user appears repeatedly with failed login attempts, the DBA can investigate the account by looking into its profile and privileges. 2.2 Database Actions A DBA can audit any action affecting a database object such as a table, view, synonym, database link, tablespace, index, etc. The possible actions such as CREATE, ALTER, and DROP that can affect a specific object type can be grouped together during auditing. For example, the command “audit table;” will 13 audit all actions affecting a table. Oracle allows 144 actions each with a numeric code and its description. You can get a list of all these codes by running the following query: select action, name from audit_actions; Once auditing is activated for an object, all actions pertaining to that object are recorded in the view DBA_AUDIT_OBJECT. The DBA can run the following query to get a list of all such actions affecting the audit-activated objects: select username, os_username, owner, obj_name, action_name, to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time" from dba_audit_object order by 1, 4, 6 desc; The DBA can analyze the result returned by the above query to identify users responsible for harmful activities such as dropping an important table or deleting rows, even if inadvertently. 2.3 Access (Retrieval and Update) to Database Objects This category of auditing applies to the four data retrieval and update commands, SELECT, INSERT, UPDATE, and DELETE. If an activity appears to be harmful or if a complaint arises about some missing data, the DBA can further investigate the situation to find an answer. For example, an unauthorized user may have retrieved sensitive data, an authorized user may have deleted some rows or even dropped a table perhaps inadvertently, a deleted row may have caused a constraint to become invalid causing additional side effects, and so on. As an Oracle sleuth the DBA can find the “offending” user by tracking and analyzing the database audit trail as follows: (a) (b) For each table to be audited, enter the command AUDIT ALL ON table_name BY ACCESS; where table_name is the name of the table on which you want to turn on auditing on all types of access. The command activates the auditing mechanism on the table. The option BY ACCESS causes an audit record to be written to the table SYS.AUD$ once for each time the table is accessed. For example, if a user performs four update transactions (INSERT, UPDATE, DELETE) on the table, then four separate records are written to SYS.AUD$. The results can be viewed by querying the view DBA_AUDIT_OBJECT. Run the query (note that the same query was given at the end of Section 2.2): select username, os_username, owner, obj_name, action_name, to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time" 14 from dba_audit_object order by 1, 4, 6 desc; The query returns the list of actions taken on the table by all users. The DBA can identify the user(s) performing retrievals or updates against the table from the list. Partial Session Transcript SQL> audit table; Audit succeeded. SQL> audit view; Audit succeeded. SQL> audit all on dqsmith.explore by access; Audit succeeded. SQL> audit all on EXPLORE_COPY by access; Audit succeeded. SQL> audit all on EXPLORE_ORIGINAL by access; Audit succeeded. SQL> commit; Commit complete. SQL> select username, os_username, owner, obj_name, action_name, 2 to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time" 3 from dba_audit_object 4 order by 1, 4, 6 desc; USERNAME OS_USERNAME OWNER OBJ_NAME ACTION_NAME ---------- ------------ ------------------------------ -------------------------------Transaction Time -------------------DQSMITH Mittra DQSMITH EXPLORE CREATE TABLE 18-jul-2003 15:38:36 DQSMITH Mittra CREATE TABLE 18-jul-2003 15:38:01 DQSMITH EXPLORE DQSMITH Mittra SELECT 17-jul-2003 09:09:16 SEKHAR EXPLORE DQSMITH Mittra SELECT 17-jul-2003 09:09:16 SEKHAR EXPLORE DQSMITH Mittra INSERT 17-jul-2003 09:08:36 SEKHAR EXPLORE DQSMITH Mittra DELETE 17-jul-2003 09:07:58 SEKHAR EXPLORE DQSMITH Mittra INSERT 17-jul-2003 09:07:26 SEKHAR EXPLORE 15 DQSMITH Mittra UPDATE 17-jul-2003 09:05:37 SEKHAR EXPLORE DQSMITH Mittra SELECT 18-jul-2003 12:50:38 SEKHAR EXPLORE_ORIGINAL DQSMITH Mittra CREATE VIEW 18-jul-2003 15:39:43 DQSMITH EXPLORE_VIEW USERNAME OS_USERNAME OWNER OBJ_NAME ACTION_NAME ---------- ------------ ------------------------------ -------------------------------Transaction Time -------------------SEKHAR Mittra SEKHAR EXPLORE RENAME 17-jul-2003 09:12:37 SEKHAR timsdba ALTER TABLE 15-jul-2003 16:28:35 SEKHAR EXPLORE TIMS Mittra SELECT 18-jul-2003 12:49:13 DQSMITH EXPLORE_COPY TIMS timsdba COMMENT 17-jul-2003 11:23:22 DQSMITH EXPLORE_COPY . . . . . . . . . . . 25 rows selected. Oracle provides the following DBA_ views containing information about auditing at various levels. There are many common columns among these views. DBA_AUDIT_EXISTS audit trail entries produced by AUDIT NOT EXISTS and AUDIT EXISTS DBA_AUDIT_OBJECT audit trail records for all objects in the system DBA_AUDIT_SESSION audit trail records concerning CONNECT and DISCONNECT DBA_AUDIT_STATEMENT audit trail records concerning GRANT, REVOKE, AUDIT, NOAUDIT, and ALTER SYSTEM statements DBA_AUDIT_TRAIL list of all audit trail entries DBA_AUDIT_TRAIL contains eleven more columns than DBA_AUDIT_OBJECT and thus provides more information than DBA_AUDIT_OBJECT. 16 2.4 Storage Management for SYS.AUD$ Table When auditing is activated on a variety of actions and on several objects BY ACCESS, a new record is written to SYS.AUD$ for every action and every access. As a result, this table can grow very rapidly. Since it belongs to SYSTEM tablespace, that tablespace can get fragmented. To remedy this situation the DBA should archive the rows from SYS.AUD$ on a regular basis as follows: Determine the frequency of archiving based on the growth rate of SYS.AUD$. Copy the rows of SYS.AUD$ into another table SYS.AUD$_COPY, say, created in a non-SYSTEM TABLESPACE. TRUNCATE the table SYS.AUD$. The DBA can inactivate auditing by issuing the following commands, as appropriate: 3. noaudit session; noaudit all on owner.table; Correction of Impact of Harmful Activities The detection capability provided by the AUDIT TRAIL mechanism has a limited scope. First, it allows a DBA to identify the users performing harmful activities such as deleting rows, dropping objects, etc. that should not be done only if a user logs in using his/her personal account. In case of a generic account such as APPL, APPLDBA, TESTER, etc. the DBA cannot identify a single individual. Secondly, even when the DBA identifies a single user performing some destructive activity, say by mistake, the DBA cannot reverse the changes made. For instance, deleted rows cannot be re-inserted or dropped objects recreated without some scripts or data from the user or developer. The LogMiner utility available with Oracle 8i and above addresses some of these deficiencies of the AUDIT TRAIL mechanism. For example, LogMiner stores all DML commands that were executed and the corresponding DML commands to reverse the results of the executed commands. Also, LogMiner retains session level information with which a DBA can identify Oracle and Unix processes for a transaction. Under certain situations the DBA can even identify the individual user logging into Oracle under a generic username. The utility also works with the Redo Log files from an Oracle8 database. 17 3.1 Scope of LogMiner under Oracle 8i As we all know, Oracle records all transactions in the online Redo Log files which are binary files. If an instance runs in ARCHIVELOG mode, then these files are copied periodically to the Archived Log files. The Redo Log files contain a wealth of information about user activities in a database. They contain all the data needed to make a database recovery and store all transactions made to the database. LogMiner offers an interface to the contents of these files enabling DBAs to audit all user activities and to reverse erroneous changes to data or structures. The utility can read any Redo Log file, online or archived, from Oracle8 forward. Unlike the AUDIT TRAIL mechanism, it does not impose any additional data collection overhead since Oracle already collects the data in the Redo Log files. LogMiner consists of the following components owned by the SYS schema: (a) (b) (c) Package DBMS_LOGMNR with three procedures ADD_LOGFILE – includes one Redo Log file for LogMiner analysis; if multiple files are to be included, then the first file takes the argument ‘NEW’ and all other files take the argument ‘ADDFILE’ START_LOGMNR – starts a LogMiner session END_LOGMNR – ends a LogMiner session Procedure DBMS_LOGMNR_D.BUILD - exports the data dictionary as a static text file from the database that LogMiner will analyze. Four V$views as described below: V$LOGMNR_CONTENTS – the contents of the Redo Log files being analyzed V$LOGMNR_DICTIONARY– the LogMiner dictionary created by DBMS_LOGMNR_D.BUILD V$LOGMNR_LOGS – the names and other details of the Redo Log files being analyzed V$LOGMNR_PARAMETERS – current parameter settings for LogMiner The directory $ORACLE_HOME/rdbms/admin contains two scripts, dbmslm.sql and dbmslmd.sql. The first script creates the package in (a) with the three procedures. The second script creates the procedure in (b). 3.1.1 Installation and Operation of LogMiner under Oracle 8i (a) Include the initialization parameter utl_file_dir in the init.ora file. The value of this parameter will be an existing directory to which Oracle has permissions to write as needed by the PL/SQL procedure DBMS_LOGMNR_D.BUILD (see Section 3.1 (b)). 18 (b) (c) (d) (e) (f) (g) (h) If necessary, shutdown the instance and then startup for the new parameter to take effect. It cannot be changed via the ALTER SYSTEM command. Run the script $ORACLE_HOME/rdbms/admin/dbmslmd.sql to create the procedure DBMS_LOGMNR_D.BUILD allowing the LogMiner dictionary to be created. Create LogMiner dictionary. Run the script $ORACLE_HOME/rdbms/admin/dbmslm.sql to create the package DBMS_LOGMNR with the three procedures, ADD_LOGFILE, START_LOGMNR, and END_LOGMNR, needed for LogMiner analysis. Include Redo Log files for LogMiner analysis. Start a LogMiner session. End the session by executing the script DBMS_LOGMNR.END_LOGMNR. This procedure MUST be called prior to exiting the LogMiner session. Otherwise, Oracle generates a silent ORA-00600 error because the PGA is found to be bigger at logoff than it was at logon, which is considered a space leak. Appendix 1 contains all the scripts used in the session transcript below. Session Transcript SQL> select name, value from v$parameter where name = 'utl_file_dir'; NAME VALUE --------------- ------------------------------------------------------utl_file_dir /tims1/ssmittra/dev01/monami_instance/logminer/logs devmaster% cd /tims1/ssmittra/dev01/monami_instance/logminer SQL>connect internal Connected. SQL>@create_logmnr_dict_8174.sql SQL>REM File = /tims1/ssmittra/dev01/monami_instance/logminer/create_logmnr_dict_8174.s ql SQL> SQL>BEGIN 2 sys.dbms_logmnr_d.build ( 3 dictionary_filename=> 'logminer_dictionary_oracle.dic', 4 dictionary_location => '/tims1/ssmittra/dev01/monami_instance/logminer/logs' 5 ); 6 END; 7 / PL/SQL procedure successfully completed. Elapsed: 00:01:47.98 devmaster% cd logs devmaster% pwd /tims1/ssmittra/dev01/monami_instance/logminer/logs devmaster% ls -l total 3840 19 -rw-r--r-1 oracle dba logminer_dictionary_oracle.dic devmaster% sqlplus internal 1951204 Jul 24 14:56 SQL*Plus: Release 8.1.7.0.0 - Production on Thu Jul 24 15:19:21 2003 (c) Copyright 2000 Oracle Corporation. All rights reserved. Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production With the Partitioning option JServer Release 8.1.7.4.0 - Production SQL>@include_redo_logs_8174.sql SQL>REM File = /tims1/ssmittra/dev01/monami_instance/logminer/include_redo_logs_8174.sq l SQL>REM Purpose: Include all redo log files for LogMiner analysis SQL> SQL> SQL>BEGIN 2 3 SYS.DBMS_LOGMNR.ADD_LOGFILE( 4 '/home/oracle/oradata/tims/dba/ssmittra/monami_A01.rdl', 5 dbms_logmnr.NEW); 6 7 SYS.DBMS_LOGMNR.ADD_LOGFILE( 8 '/home/oracle/oradata/tims/dba/ssmittra/monami_A02.rdl', 9 dbms_logmnr.ADDFILE); 10 11 END; 12 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.14 SQL>@start_logminer_8174.sql SQL>REM File = /tims1/ssmittra/dev01/monami_instance/logminer/start_logminer_8174.sql SQL>REM Purpose: Start LogMiner for analyzing the Redo Log files. SQL> SQL> SQL>EXECUTE SYS.DBMS_LOGMNR.START_LOGMNR( > DICTFILENAME => '/tims1/ssmittra/dev01/monami_instance/logminer/logs/logminer_dictionary _oracle.dic'); PL/SQL procedure successfully completed. Elapsed: 00:00:04.02 SQL>select distinct table_space, seg_name, seg_owner from v$logmnr_contents 2 where table_space != 'SYSTEM' 3 order by 1, 2; TABLE_SPACE -------------------------------USER_MONAMI USER_MONAMI SEG_NAME --------------------EXPLORE_ORIGINAL INVOICE_STATUS SEG_OWNER --------SEKHAR SEKHAR 20 2 rows selected. Elapsed: 00:00:02.15 SQL>CREATE TABLE TEST_LOG_MINER 2 (TEST_ID NUMBER (4) NOT NULL, 3 TEST_DATE DATE DEFAULT SYSDATE NOT NULL, 4 TESTER_NAME VARCHAR2 (30) DEFAULT USER NOT NULL, 5 FINDINGS VARCHAR2 (100)) 6 TABLESPACE USER_MONAMI; Table created. Elapsed: 00:00:00.42 (INSERT FIVE ROWS INTO TEST_LOG_MINER) SQL>col findings format a32 SQL>col tester_name format a20 SQL>select * from test_log_miner order by 1; TEST_ID ---------268 856 1234 1434 errors. 6937 TEST_DATE --------09-MAY-93 17-MAY-66 24-JUL-03 24-JUL-03 TESTER_NAME -------------------Sanjukta Banerji Sakuntala Datta SYS SYS 27-OCT-01 Damayanti Mittra FINDINGS -----------------------------All success All success The test passed successfully. The test passed with two FAILED 5 rows selected. Elapsed: 00:00:00.01 SQL>select distinct table_space, seg_name from v$logmnr_contents where table_space != 'SYSTEM' 2 order by 1, 2; TABLE_SPACE -------------------------------USER_MONAMI USER_MONAMI SEG_NAME -------------------------------EXPLORE_ORIGINAL INVOICE_STATUS 2 rows selected. Elapsed: 00:00:02.19 SQL> execute dbms_logmnr.end_logmnr; PL/SQL procedure successfully completed. Elapsed: 00:00:00.01 Note that the table TEST_LOG_MINER does not appear as a SEG_NAME above, because the LogMiner dictionary is static. Any DML activity done after creating the dictionary with the script create_logmnr_dict_8174.sql is not recorded in that dictionary. We now start a new session with LogMiner after having created and populated the table TEST_LOG_MINER. 21 Session Transcript devmaster% sqlplus internal SQL*Plus: Release 8.1.7.0.0 - Production on Fri Jul 25 08:27:56 2003 (c) Copyright 2000 Oracle Corporation. All rights reserved. Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production With the Partitioning option JServer Release 8.1.7.4.0 - Production SQL>@LogMiner_Session_8174.sql SQL>REM File = /tims1/ssmittra/dev01/monami_instance/logminer/LogMiner_Session_8174.sql SQL>REM Purpose: provide a single command file to run a LogMiner Session. SQL> SQL> SQL>set scan on SQL> SQL>column get_location new_value LogMiner_Session_spool_file noprint SQL> SQL>select 'LogMiner_Session_' || TO_CHAR(SYSDATE, 'DD-MON-YYYY') || '.log' get_location from dual; 1 row selected. Elapsed: 00:00:00.02 SQL> SQL>spool /tims1/ssmittra/dev01/monami_instance/logminer/logs/&LogMiner_Session_sp ool_file SQL> SQL>@create_logmnr_dict_8174.sql SQL>REM File = /tims1/ssmittra/dev01/monami_instance/logminer/create_logmnr_dict_8174.s ql SQL> SQL>BEGIN 2 sys.dbms_logmnr_d.build ( 3 dictionary_filename=> 'logminer_dictionary_oracle.dic', 4 dictionary_location => '/tims1/ssmittra/dev01/monami_instance/logminer/logs' 5 ); 6 END; 7 / LogMnr Dictionary Procedure started LogMnr Dictionary File Opened TABLE: OBJ$ recorded in LogMnr Dictionary File (REST OF OUTPUT SUPPRESSED) Procedure executed successfully - LogMnr Dictionary Created PL/SQL procedure successfully completed. Elapsed: 00:01:56.80 SQL>@include_redo_logs_8174.sql 22 SQL>REM File = /tims1/ssmittra/dev01/monami_instance/logminer/include_redo_logs_8174.sq l SQL>REM Purpose: Include all redo log files for LogMiner analysis SQL> SQL> SQL>BEGIN 2 3 SYS.DBMS_LOGMNR.ADD_LOGFILE( 4 '/home/oracle/oradata/tims/dba/ssmittra/monami_A01.rdl', 5 dbms_logmnr.NEW); 6 7 SYS.DBMS_LOGMNR.ADD_LOGFILE( 8 '/home/oracle/oradata/tims/dba/ssmittra/monami_A02.rdl', 9 dbms_logmnr.ADDFILE); 10 11 END; 12 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.06 SQL> SQL>@start_logminer_8174.sql SQL>REM File = /tims1/ssmittra/dev01/monami_instance/logminer/start_logminer_8174.sql SQL>REM Purpose: Start LogMiner for analyzing the Redo Log files. SQL> SQL> SQL>EXECUTE SYS.DBMS_LOGMNR.START_LOGMNR( > DICTFILENAME => '/tims1/ssmittra/dev01/monami_instance/logminer/logs/logminer_dictionary _oracle.dic'); PL/SQL procedure successfully completed. Elapsed: 00:00:03.86 SQL> SQL>select count (*) from v$logmnr_contents; COUNT(*) ---------3690 1 row selected. Elapsed: 00:00:02.35 SQL> SQL>select distinct table_space, seg_name, seg_type_name from v$logmnr_contents 2 where table_space != 'SYSTEM' 3 order by 1, 2; TABLE_SPACE -------------------------------USER_MONAMI USER_MONAMI USER_MONAMI 3 rows selected. SEG_NAME ------------------------EXPLORE_ORIGINAL INVOICE_STATUS TEST_LOG_MINER SEG_TYPE_NAME ------------TABLE TABLE TABLE 23 Elapsed: 00:00:02.18 SQL> SQL>spool off We now see that TEST_LOG_MINER is included in the LogMiner dictionary. 3.1.2 Output from LogMiner under Oracle 8i (a) SQL_REDO and SQL_UNDO The column V$LOGMNR_CONTENTS.SQL_REDO returns the actual DML statement that was executed and V$LOGMNR_CONTENTS.SQL_UNDO returns the statement that reverses the REDO-transaction. Here are a few examples. Partial Session Transcript SQL>select to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time", sql_redo 2 from v$logmnr_contents 3 where table_space != 'SYSTEM' and timestamp > '15-JUL-2003' 4 order by 1 desc; Transaction Time -------------------SQL_REDO -----------------------------------------------------------------------24-jul-2003 15:51:29 insert into "SYS"."TEST_LOG_MINER"("TEST_ID","TEST_DATE","TESTER_NAME","FINDINGS") values (856,TO_DATE('17-MAY-1966 00:00:00', 'DD-M ON-YYYY HH24:MI:SS'),'Sakuntala Datta','All success'); 24-jul-2003 15:50:24 insert into "SYS"."TEST_LOG_MINER"("TEST_ID","TEST_DATE","TESTER_NAME","FINDINGS") values (268,TO_DATE('09-MAY-1993 00:00:00', 'DD-M ON-YYYY HH24:MI:SS'),'Sanjukta Banerji','All success'); SQL>select to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time", sql_undo 2 from v$logmnr_contents 3 where table_space != 'SYSTEM' and timestamp > '15-JUL-2003' 4 order by 1 desc; Transaction Time -------------------SQL_UNDO -----------------------------------------------------------------------24-jul-2003 15:51:29 delete from "SYS"."TEST_LOG_MINER" where "TEST_ID" = 856 and "TEST_DATE" = TO_DATE('17-MAY-1966 00:00:00', 'DD-MON-YYYY HH24:MI:SS') and "TESTER_NAME" = 'Sakuntala Datta' and "FINDINGS" = 'All success' and ROWID = 'AAAA7NAAGAAAACPAAE'; 24-jul-2003 15:50:24 delete from "SYS"."TEST_LOG_MINER" where "TEST_ID" = 268 and "TEST_DATE" = TO_DATE('09-MAY-1993 00:00:00', 'DD-MON-YYYY HH24:MI:SS') 24 and "TESTER_NAME" = 'Sanjukta Banerji' and "FINDINGS" = 'All success' and ROWID = 'AAAA7NAAGAAAACPAAD'; (b) SESSION_INFO and Related Columns The column V$LOGMNR_CONTENTS.SESSION_INFO returns user identifying information such as LoginUserName and OsUserName. Unless both of them are generic such as SYS, ORACLE, TESTER, etc., the DBA can identify the individual user. If both are generic, there is no way to identify the user logging under the generic names. Session Transcript 09:38:57 SQL>select to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time", username, 09:39:00 2 session#, serial#, operation, session_info 09:39:14 3 from v$logmnr_contents 09:39:28 4 where timestamp > '07-AUG-2003' 09:40:14 5 and seg_name in ('EXPLORE', 'EXPLORE_COPY', 'EXPLORE_ORIGINAL') 09:40:19 6 order by 1; Transaction Time USERNAME SESSION# SERIAL# OPERATION -------------------- -------------------------------- ---------- --------- -------------------------------SESSION_INFO ----------------------------------------------------------------------------------------------------------------------------------07-aug-2003 09:00:05 TIMS 8 4975 INSERT LoginUserName = TIMS, ClientInfo = , OsUserName = Mittra, MachineName = DTS-57\CSC-TIMS33 . . . . . . . . . . . 2 rows selected. 3.2 Scope of LogMiner under Oracle 9i LogMiner under 9i has several new features beyond what are available under 8i. I am describing below only two of them that I find useful. (a) The LogMiner dictionary is dynamic instead of being static as under 8i. Under 8i, any transactions entered after the start of a LogMiner session are not recorded into the dictionary. But under 9i, the LogMiner dictionary is dynamically updated from the online data dictionary via the option DICT_FROM_ONLINE_CATALOG. This feature is explained further with examples in the Session Transcript under Section 3.2.2 (a). (b) LogMiner supports DDL statements by ensuring that its internal dictionary is updated if a DDL event is found in the Redo Log files. This ensures that SQL_REDO and SQL_UNDO information is correct for objects that 25 are created or modified in the Redo Log files after the LogMiner internal dictionary was built. The column V$LOGMNR_CONTENTS.OPERATION returns the value “DDL” and the column SQL_REDO contains the actual DDL statement for the operation. But this feature is not available with the option DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG that we have used. It is available in only two cases: The dictionary in use is a flat file created via the procedure DBMS_LOGMNR_D.BUILD The dictionary is built from the redo log files via the procedure DBMS_LOGMNR. START_LOGMNR.DICT_FROM_REDO_LOGS 3.2.1 Installation and Operation of LogMiner under Oracle 9i (a) (b) (c) (d) (e) (f) Include the initialization parameter utl_file_dir in the init.ora file. The value of this parameter will be an existing directory to which Oracle has permissions to write as needed by the PL/SQL procedure DBMS_LOGMNR_D.BUILD, if used. If necessary, shutdown the instance and then startup for the new parameter to take effect. It cannot be changed via the ALTER SYSTEM command. Run the script $ORACLE_HOME/rdbms/admin/dbmslm.sql to create the package DBMS_LOGMNR with the five procedures, ADD_LOGFILE, START_LOGMNR, END_LOGMNR, MINE_VALUE, and COLUMN_PRESENT, needed for LogMiner analysis. Include Redo Log files for LogMiner analysis. Start a LogMiner session. End the session by executing the script DBMS_LOGMNR.END_LOGMNR. This procedure MUST be called prior to exiting the LogMiner session. Otherwise, Oracle generates a silent ORA-00600 error because the PGA is found to be bigger at logoff than it was at logon, which is considered a space leak. Appendix 2 contains all the scripts used in the session transcript below. Comparing the above steps with those in Section 3.1.1 we find that steps (c) and (d) of Section 3.1.1 are not needed. This happens because the LogMiner dictionary is not created as a flat file. The option SYS.DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG used in step (e) creates the LogMiner dictionary from the online data dictionary and keeps it continually updated. If we want to create the LogMiner dictionary as a flat file, then we need the procedure DBMS_LOGMNR_D to execute the following procedure: 26 BEGIN sys.dbms_logmnr_d.build ( dictionary_filename=> 'logminer_dictionary_oracle.dic', dictionary_location => 'LOCATION' ); END; / Here 'LOCATION' will be the value returned by the following query: select value from v$parameter where name = 'utl_file_dir'; But the dictionary file created as a flat file will remain static. It will not reflect changes made after the start of a LogMiner session as under Oracle 8i. 3.2.2 Output from LogMiner under Oracle 9i (a) SQL_REDO and SQL_UNDO The output below is very similar to the one provided in Section 3.1.2. Note that after the LogMiner session was started, one insert, two updates, and one delete were made. They were all included in the SQL_REDO and SQL_UNDO values generated after these transactions. We may contrast this situation with one we encountered in Section 3.1.1 where the table TEST_LOG_MINER was not included in the LogMiner dictionary because the table was created and populated after the static LogMiner dictionary was created. Session Transcript 09:25:42 SQL> select to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time", username, 09:25:49 2 sql_redo, sql_undo from v$logmnr_contents 09:25:58 3 where TABLE_SPACE = 'TIMSDATA' 09:26:08 4 and to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') > '04aug-2003 11:24:27' 09:27:05 5 order by 1; Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------04-aug-2003 11:25:06 SYS insert into "TIMS"."EXPLORE"("COL01","COL02","COL03","COL04") values ('35128','a vav',TO_DATE('02-AUG-03', 'DD-MON-RR'),'17'); delete from "TIMS"."EXPLORE" where "COL01" = '35128' and "COL02" = 'avav' and "C OL03" = TO_DATE('02-AUG-03', 'DD-MON-RR') and "COL04" = '17' and ROWID = 'AAAHw/ 27 AMgAAAM/7AAE'; Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------04-aug-2003 12:10:26 TIMS insert into "TIMS"."EXPLORE_COPY"("COL01","COL02","COL03","COL04") values ('8766 ','Mittra',TO_DATE('06-AUG-03', 'DD-MON-RR'),'11'); delete from "TIMS"."EXPLORE_COPY" where "COL01" = '8766' and "COL02" = 'Mittra' and "COL03" = TO_DATE('06-AUG-03', 'DD-MON-RR') and "COL04" = '11' and ROWID = ' AAAHxBAMgAAASOVAAA'; . . . . . . . 4 rows selected. 09:40:48 SQL> insert into tims.explore values (6513, 'Indumati', sysdate - 3, 16); 1 row created. 09:41:24 SQL> update tims.explore 09:41:31 2 set col02 = sysdate where col01 = 35128; 2 rows updated. 09:43:31 SQL> delete from tims.explore_copy where col02 = 'cxxc'; 1 row deleted. 09:44:14 SQL> select to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time", username, 09:45:14 2 sql_redo, sql_undo from v$logmnr_contents 09:45:27 3 where TABLE_SPACE = 'TIMSDATA' 09:45:39 4 and to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') > '04aug-2003 11:24:27' 09:45:53 5 order by 1; Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------04-aug-2003 11:25:06 SYS insert into "TIMS"."EXPLORE"("COL01","COL02","COL03","COL04") values ('35128','a vav',TO_DATE('02-AUG-03', 'DD-MON-RR'),'17'); delete from "TIMS"."EXPLORE" where "COL01" = '35128' and "COL02" = 'avav' and "C 28 OL03" = TO_DATE('02-AUG-03', 'DD-MON-RR') and "COL04" = '17' and ROWID = 'AAAHw/ AMgAAAM/7AAE'; Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------04-aug-2003 12:10:26 TIMS insert into "TIMS"."EXPLORE_COPY"("COL01","COL02","COL03","COL04") values ('8766 ','Mittra',TO_DATE('06-AUG-03', 'DD-MON-RR'),'11'); delete from "TIMS"."EXPLORE_COPY" where "COL01" = '8766' and "COL02" = 'Mittra' and "COL03" = TO_DATE('06-AUG-03', 'DD-MON-RR') and "COL04" = '11' and ROWID = ' AAAHxBAMgAAASOVAAA'; . . . . . . . . 05-aug-2003 09:41:11 SYS insert into "TIMS"."EXPLORE"("COL01","COL02","COL03","COL04") values ('6513','In dumati',TO_DATE('02-AUG-03', 'DD-MON-RR'),'16'); Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------delete from "TIMS"."EXPLORE" where "COL01" = '6513' and "COL02" = 'Indumati' and "COL03" = TO_DATE('02-AUG-03', 'DD-MON-RR') and "COL04" = '16' and ROWID = 'AAA Hw/AMgAAAM/7AAG'; 05-aug-2003 09:42:30 SYS update "TIMS"."EXPLORE" set "COL02" = '05-AUG-03' where "COL02" = 'avav' and ROW ID = 'AAAHw/AMgAAAM/7AAD'; Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------update "TIMS"."EXPLORE" set "COL02" = 'avav' where "COL02" = '05-AUG-03' and ROW ID = 'AAAHw/AMgAAAM/7AAD'; 29 05-aug-2003 09:42:30 SYS update "TIMS"."EXPLORE" set "COL02" = '05-AUG-03' where "COL02" = 'avav' and ROW ID = 'AAAHw/AMgAAAM/7AAE'; update "TIMS"."EXPLORE" set "COL02" = 'avav' where "COL02" = '05-AUG-03' and ROW Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------ID = 'AAAHw/AMgAAAM/7AAE'; 05-aug-2003 09:44:16 SYS delete from "TIMS"."EXPLORE_COPY" where "COL01" = '2013' and "COL02" = 'cxxc' an d "COL03" = TO_DATE('04-AUG-03', 'DD-MON-RR') and "COL04" = '11' and ROWID = 'AA AHxBAMgAAASOUAAB'; insert into "TIMS"."EXPLORE_COPY"("COL01","COL02","COL03","COL04") values ('2013 Transaction Time USERNAME -------------------- -----------------------------SQL_REDO ------------------------------------------------------------------------------SQL_UNDO ------------------------------------------------------------------------------','cxxc',TO_DATE('04-AUG-03', 'DD-MON-RR'),'11'); 8 rows selected. Note that the INSERT, UPDATE, and DELETE transactions that were made after the start of the LogMiner session have been picked up by the last query above. This shows that the LogMiner dictionary is dynamically updated during the session. (b) SESSION_INFO and Related Columns The column V$LOGMNR_CONTENTS.SESSION_INFO returns user identifying information such as LoginUserName and OsUserName. Unless both of them are generic such as SYS, ORACLE, TESTER, APPLDBA, etc., the DBA can identify the individual user. If both are generic, there is no way to identify the user logging under the generic names. Therefore, such generic user names should not be allowed at least at the operating system level. Session Transcript 30 10:34:41 SQL> select to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time", username, 10:36:01 2 session#, serial#, operation, session_info 10:36:53 3 from v$logmnr_contents 10:37:04 4 where TABLE_SPACE = 'TIMSDATA' 10:37:21 5 and to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') > '04aug-2003 11:24:27' 10:38:02 6 order by 1; Transaction Time USERNAME SESSION# SERIAL# -------------------- ------------------------------ ---------- --------OPERATION -------------------------------SESSION_INFO ------------------------------------------------------------------------------04-aug-2003 11:25:06 SYS 19 23394 INSERT login_username=TIMS client_info= OS_username=Mittra Machine_name=DTS57\CSC-TIMS 33 04-aug-2003 12:10:26 TIMS 23394 INSERT 19 Transaction Time USERNAME SESSION# SERIAL# -------------------- ------------------------------ ---------- --------OPERATION -------------------------------SESSION_INFO ------------------------------------------------------------------------------login_username=TIMS client_info= OS_username=Mittra Machine_name=DTS57\CSC-TIMS 33 04-aug-2003 13:42:56 SYS 15 13040 INSERT login_username=TIMS client_info= OS_username=oracle Machine_name=dba8 OS_termina l=pts/1 OS_process_id=12524 OS_program name=sqlplus@dba8 (TNS V1-V3) 3.3 Two Issues Worth Mentioning Two specific issues need some mention here. (a) LogMiner must be invoked afresh with each new session. All LogMiner session information resides in the PGA (Program Global Area) which is a component of the SGA (System Global Area). PGA is flushed when a user session ends normally or abnormally and all the session information is lost. This holds for both Oracle 8i and 9i. 31 A session ends normally when the user executes the procedure DBMS_LOGMNR.END_LOGMNR to end the session. A session ends abnormally generating a silent internal Oracle error ORA-00600 when the user types “exit” or issues the command “connect username/password” to login as a different user without exiting. Session Transcript dba8% sqlplus /nolog SQL*Plus: Release 9.2.0.3.0 - Production on Fri Aug 1 11:53:07 2003 Copyright (c) 1982, 2002, Oracle Corporation. SQL> connect / as sysdba Connected. SQL> show user USER is "SYS" SQL> @include_redo_logs_monami.sql All rights reserved. (SESSION AS USER “SYS” STARTS) (LOG MINER IS INVOKED HERE) PL/SQL procedure successfully completed. SQL> @start_logminer_monami.sql PL/SQL procedure successfully completed. SQL> connect tims/******* (SESSION AS USER “SYS” ENDS Connected. ABNORMALLY. THE PGA IS FLUSHED.) SQL> show user (NEW SESSION AS USER “TIMS” STARTS) USER is "TIMS" SQL> select to_char (timestamp, 'dd-mon-yyyy hh24:mi:ss') "Transaction Time", username, 2 sql_redo, sql_undo from v$logmnr_contents 3 where TABLE_SPACE = 'TIMSDATA' 4 order by 1 desc; sql_redo, sql_undo from v$logmnr_contents * ERROR at line 2: ORA-01306: dbms_logmnr.start_logmnr() must be invoked before selecting from v$logmnr_contents (NOTE: (b) PGA FOR SESSION “SYS” HAS BEEN FLUSHED. NEW SESSION “TIMS” NEEDS A NEW LOG MINER SESSION.) By default all LogMiner tables are created in the SYSTEM tablespace. As a result, the SYSTEM tablespace may get fragmented over time. To avoid this problem Oracle recommends that a separate tablespace be created to store the tables created and used by LogMiner. The procedure DBMS_LOGMNR_D.SET_TABLESPACE can be used to create such a tablespace. In the example below a tablespace named LOGMNR_TBLSPC is created for storing the LogMiner segments. As a result, 60 of the LOGMNR 32 segments are stored in LOGMNR_TBLSPC tablespace and the remaining 27 segments in SYSTEM tablespace. Session Transcript SQL> create tablespace logmnr_tblspc 2 datafile '/tims1/ssmittra/dev01/monami_instance/logminer/LOGMNR_TBLSPC01.dbf' 3 size 200M; Tablespace created. SQL> commit; Commit complete. SQL> EXEC DBMS_LOGMNR_D.SET_TABLESPACE ('logmnr_tblspc'); PL/SQL procedure successfully completed. SQL> @LogMiner_Session_9203.sql PL/SQL procedure successfully completed. PL/SQL procedure successfully completed. SQL> col TABLESPACE_NAME format a15 SQL> col segment_name format a60 SQL> select tablespace_name, segment_name from dba_segments where segment_name like 'LOGMNR%' 2 order by 1, 2; (Partial output shown below) TABLESPACE_NAME --------------LOGMNR_TBLSPC LOGMNR_TBLSPC LOGMNR_TBLSPC LOGMNR_TBLSPC LOGMNR_TBLSPC LOGMNR_TBLSPC LOGMNR_TBLSPC LOGMNR_TBLSPC LOGMNR_TBLSPC SEGMENT_NAME -------------------------------------------------------LOGMNRC_DBNAME_UID_MAP LOGMNRC_DBNAME_UID_MAP_PK LOGMNRC_GSII LOGMNRC_GSII_PK LOGMNRC_GTCS LOGMNRC_GTCS_PK LOGMNRC_GTLO LOGMNRC_GTLO_PK LOGMNR_AGE_SPILL$ . . . . . . . . . . . TABLESPACE_NAME --------------SYSTEM SYSTEM SYSTEM SEGMENT_NAME -------------------------------------------------------LOGMNRG_TABCOMPART$ LOGMNRG_TABPART$ LOGMNRG_TABSUBPART$ 33 SYSTEM SYSTEM SYSTEM SYSTEM SYSTEM SYSTEM SYSTEM LOGMNRG_TS$ LOGMNRG_TYPE$ LOGMNRG_USER$ LOGMNR_HEADER1$ LOGMNR_HEADER2$ LOGMNR_INTERESTING_COLS LOGMNR_SESSION$ 87 rows selected. 4. Roadmap for Ensuring Data Security in Oracle Databases A DBA needs to be vigilant to protect the contents of a database. Prevention, detection, and correction of harmful user activities, intentional or unintentional, as discussed above provide a roadmap for such an endeavor. Both AUDIT TRAIL and LogMiner provide a timestamp for every user activity. As a result, a DBA can identify the time of occurrence of a malicious activity up to the second. To bring the database back to a consistent state the DBA can restore it to a point in time prior to the occurrence of that event. In addition, it is necessary for any organization to have a database security plan in place and to review it on a regular basis, say every three months. As far as possible, generic user accounts for the operating system or for Oracle should not be allowed. Also, no privilege should be granted to the user PUBLIC (see Section 1.8). 5. Bibliography 1. Kevin Loney and Marlene Theriault – Oracle 8i DBA Handbook, Chapter 9, Oracle Press, 2000. Kevin Loney and Marlene Theriault – Oracle 9i DBA Handbook, Chapters 10 and 11, Oracle Press, 2002. Sitansu S. Mittra – Database Performance Tuning and Optimization Using Oracle, Chapter 5, Springer-Verlag, 2003. Oracle MetaLink – papers on LogMiner; e.g., Notes 62508.1, 111886.1, etc. Marlene Theriault and Aaron Newman – Oracle Security Handbook, Chapters 16 and 17, Oracle Press, 2001. 2. 3. 4. 5. 34 Appendix 1 LogMiner Scripts for Oracle 8i 1. LogMiner_Session_8174.sql REM File = /tims1/ssmittra/dev01/monami_instance/logminer/LogMiner_Session_8174.sql REM Purpose: provide a single command file to run a Log Miner Session under Oracle 8i. set scan on column get_location new_value LogMiner_Session_spool_file noprint select 'LogMiner_Session_' || TO_CHAR(SYSDATE, 'DD-MON-YYYY') || '.log' get_location from dual; spool /tims1/ssmittra/dev01/monami_instance/logminer/logs/&LogMiner_Session_sp ool_file @create_logmnr_dict_8174.sql @include_redo_logs_8174.sql @start_logminer_8174.sql spool off 2. create_logmnr_dict_8174.sql REM File = /tims1/ssmittra/dev01/monami_instance/logminer/create_logmnr_dict_8174.s ql BEGIN sys.dbms_logmnr_d.build ( dictionary_filename=> 'logminer_dictionary_oracle.dic', dictionary_location => '/tims1/ssmittra/dev01/monami_instance/logminer/logs' ); END; / 3. include_redo_logs_8174.sql REM File = /tims1/ssmittra/dev01/monami_instance/logminer/include_redo_logs_8174.sq l REM Purpose: Include all redo log files for Log Miner analysis under Oracle 8i BEGIN SYS.DBMS_LOGMNR.ADD_LOGFILE( 35 '/home/oracle/oradata/tims/dba/ssmittra/monami_A01.rdl', dbms_logmnr.NEW); SYS.DBMS_LOGMNR.ADD_LOGFILE( '/home/oracle/oradata/tims/dba/ssmittra/monami_A02.rdl', dbms_logmnr.ADDFILE); END; / 4. start_logminer_8174.sql REM File = /tims1/ssmittra/dev01/monami_instance/logminer/start_logminer_8174.sql REM Purpose: Start Log Miner for analyzing the Redo Log files under Oracle 8i. EXECUTE SYS.DBMS_LOGMNR.START_LOGMNR( DICTFILENAME => '/tims1/ssmittra/dev01/monami_instance/logminer/logs/logminer_dictionary _oracle.dic'); 36 Appendix 2 LogMiner Scripts for Oracle 9i 1. LogMiner_Session_9203.sql REM File = /tims1/ssmittra/dev01/monami_instance/logminer/LogMiner_Session_9203.sql REM Purpose: provide a single command file to run a Log Miner Session under Oracle 9i. set scan on column get_location new_value LogMiner_Session_spool_file noprint select 'LogMiner_Session_' || TO_CHAR(SYSDATE, 'DD-MON-YYYY') || '.log' get_location from dual; spool /tims1/ssmittra/dev01/monami_instance/logminer/logs/&LogMiner_Session_sp ool_file @include_redo_logs_9203.sql @start_logminer_9203.sql spool off 2. include_redo_logs_9203.sql REM File = /tims1/ssmittra/dev01/monami_instance/logminer/include_redo_logs_9203.sq l REM Purpose: Include all redo log files for Log Miner analysis under Oracle 9i BEGIN SYS.DBMS_LOGMNR.ADD_LOGFILE( '/home/oracle/oradata/tims/admin/system/redoA01.log', SYS.dbms_logmnr.NEW); SYS.DBMS_LOGMNR.ADD_LOGFILE( '/home/oracle/oradata/tims/admin/system/redoA02.log', SYS.dbms_logmnr.ADDFILE); SYS.DBMS_LOGMNR.ADD_LOGFILE( '/home/oracle/oradata/tims/admin/system/redoA03.log', SYS.dbms_logmnr.ADDFILE); SYS.DBMS_LOGMNR.ADD_LOGFILE( '/home/oracle/oradata/tims/admin/system/redoA04.log', SYS.dbms_logmnr.ADDFILE); SYS.DBMS_LOGMNR.ADD_LOGFILE( '/home/oracle/oradata/tims/admin/system/redoA05.log', 37 SYS.dbms_logmnr.ADDFILE); END; / 3. start_logminer_9203.sql REM File = /tims1/ssmittra/dev01/monami_instance/logminer/start_logminer_9203.sql REM Purpose: Start Log Miner for analyzing the Redo Log files under Oracle 9i. EXECUTE SYS.DBMS_LOGMNR.START_LOGMNR( OPTIONS => SYS.DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);