jueves, 28 de agosto de 2008

Passwords en Oracle

Desde hace mucho tiempo, se conoce la forma en que el hash de oracle se calcula para las versiones menores a 11g. E incluso se conoce el algoritmo del cálculo del hash para 11g.

Lo que es importante notar es que con el poder de cómputo de estos días, y con la debilidad del password de oracle, si no usamos passwords difíciles de crackear, podemos tener un riesgo de seguridad muy grande.

A continuación expongo un script, sólo con fines ilustrativos, la forma en que oracle calcula el hash

hashoracle.pl


use warnings;
use strict;
use Crypt::CBC;
use Encode;
use Unicode::String qw(utf8 latin1 utf16);

my $llave = pack("H16", "0123456789ABCDEF");
my $usuario = 'Hugo';
my $password = 'abc123';

my $textoplano = uc($usuario.$password);
my $utf16 =utf8($textoplano);
my $octeto = $utf16->utf16;
my $IV = pack("H16", "0000000000000000");
my $cifrado = Crypt::CBC->new(
-key => $llave,
-cipher => 'DES',
-iv => $IV,
-header => 'none',
-padding => 'null',
-literal_key=>1,
);

my $textocifrado = $cifrado->encrypt($octeto);
my $cifrado1 = Crypt::CBC->new(
-key => substr($textocifrado, length($textocifrado)-8,8),
-cipher => 'DES',
-iv => $IV,
-header => 'none',
-padding => 'null',
-literal_key=>1,
);

my $textocifrado1 = $cifrado1->encrypt($octeto);
my( $hex1 ) = unpack( 'H*', $textocifrado1 );
my $temp1 = uc(substr($hex1,length($hex1)-16,16));
print "\n$temp1\n";
__END__



Si este script se pusiera en un ciclo infinito a buscar passwords para hacer un match contra un hash preestablecido, podría buscar aproximadamente 800,000 hashes por segundo, si este mismo script se pasara a C, podría buscar aproximadamente 1,300,000 hashes por segundo. Alex Kornbust tiene un programa que incluso puede hacer uso de threads en los CPUs y genera hasta 2,400,000 por segundo.

Así que pensemos que, de acuerdo a estudios realizados, un password de 8 caracteres, puede ser descubierto en tan sólo 20 días, de 9 caracteres, ya puede pasar del año, pero teniendo 40 computadoras descifrando passwords, el tiempo vuelve a 20 días, así que ¿cuál es mi recomendación?

Seguir de lleno las recomendaciones de Oracle:

- Crea los passwords entre 8 y 30 caracteres (yo recomendaría de 10 a 30 caracteres).
- Utiliza símbolos del Character Set para definir los passwords (#$_).
- Además de incluir un dígito y un alfanumérico, también incluye un signo de puntuación.
- No inicies un password con un número.
- No utilices palabras reservadas de oracle.
- No uses palabras de diccionario.

Adicionalmente podemos crear passwords más complejos, pero esto también nos limita el uso un poco:

Empecemos con passwords entre comillas


C:\>sqlplus / as sysdba

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 11:56:39 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.


Conectado a:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production
With the Partitioning, OLAP and Data Mining options


SQL> alter user hugo identified by "de la sierra morena cielito";

Usuario modificado.

SQL> conn hugo/"de la sierra morena cielito";
Conectado.



En este punto tenemos un pequeño problema, o quizás sólo yo, pero no he podido pasar a nivel sistema operativo mi password, es decir, la conexión la debo de realizar con /nolog, o bien usando únicamente el usuario y esperar a que sqlplus nos pregunte el password.


C:\>sqlplus hugo/"de\ la\ sierra\ morena\ cielito"

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 12:00:23 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.

ERROR:
ORA-01017: invalid username/password; logon denied


C:\>sqlplus hugo

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 12:01:43 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.

Introduzca la contraseña:

Conectado a:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production
With the Partitioning, OLAP and Data Mining options


SQL>


C:\>sqlplus /nolog

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 12:02:43 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.


SQL> conn hugo/"de la sierra morena cielito";
Conectado.



Algo de lo que podemos hacer uso, como bien nos recomienda oracle, es el uso de caracteres especiales, por ejemplo:



C:\>set NLS_LANG=spanish_america.we8pc850

C:\>sqlplus / as sysdba

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 12:05:04 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.


Conectado a:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production
With the Partitioning, OLAP and Data Mining options


SQL> alter user hugo identified by querétaro;

Usuario modificado.



Al hacer esto, nos aseguramos que los passwords sólo funcionan con nuestro caracter set o alguno muy similar:


C:\>set NLS_LANG=spanish_america.we8iso8859p1

C:\>sqlplus /nolog

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 12:08:18 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.


SQL> conn hugo/querétaro
ERROR:
ORA-01017: invalid username/password; logon denied

SQL> exit

C:\>set NLS_LANG=spanish_america.utf8

C:\>sqlplus /nolog

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 12:09:14 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.


SQL> conn hugo/querétaro
ERROR:
ORA-01017: invalid username/password; logon denied

SQL> exit

C:\>set NLS_LANG=spanish_america.we8pc850

C:\>sqlplus /nolog

SQL*Plus: Release 10.2.0.3.0 - Production on Jue Ago 28 12:09:37 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.


SQL> conn hugo/querétaro
Conectado.


De esta forma podemos asegurar más nuestros passwords y asegurar de una mejor forma nuestra base de datos.

lunes, 25 de agosto de 2008

Rman y cómo catalogar Backups

Los esquemas de respaldo de todo mundo suelen ser muy variados, muy rara vez te encuentras con configuraciones iguales (BCVs, scripts, rman, veritas, HP, Tivoli, cartucheras virtuales, etc...). De manera personal, creo que lo más sencillo o fácil de llevar como DBA, es tener un software que administre la parte de backups y de restore de forma integrada con RMAN.

Suponiendo que tengamos Dataprotector, TSM, Netbackup, etc. integrado a RMAN, lo más sencillo es correr scripts de backup hacia canales de cinta de forma directa, mantener nuestras políticas de retención en RMAN y poder expirar y borrar de las cintas desde RMAN. Esto como DBAs nos da mucha visión de cómo se van llevando nuestros Backups, podemos generar los reportes necesarios con comandos sencillos de RMAN, etc...

Me he encontrado con algunos clientes, y recientemente a través de unos correos, con que mucha gente hace los respaldos a disco, una vez que el respaldo está en disco, se utiliza una cartuchera para llevarse el respaldo a cinta.

En este ejemplo en particular, imaginemos que el espacio en disco es reducido, y que sólo se mantiene el último backup en disco, y como se tiene respaldado en cinta la información, se pueden expirar y borrar los backups de disco anteriores.

Este es un ejemplo práctico de cómo llevar un backup, pero hay que tener varias consideraciones al hacerlos:

Debes de asegurarte de llevar un backup del controlfile. Recuerda que si tienes activado el backup automático de controlfile, y no incluyes el controlfile en tu backup, el resplado del controlfile pudier estar en otro PATH distinto a tus backupsets, considera añadir ese path a tu respaldo en cinta. Si ejecutas


RMAN> backup database include current controlfile;


El controlfile estará en el mismo path que tus backupsets.

Una vez expirado y eliminado el backup del día anterior, imaginemos que nos damos cuenta que un error de datos se tiene desde ayer y queremos recuperar la base de datos a un punto anterior (hace dos días), por lo cual el backup del día de hoy no nos sirve... ¿Qué se puede hacer para recuperar la información?, vamos a poner el ejemplo que se va a restaurar el respaldo de hace 2 días en un servidor nuevo para consultar la información sin afectar producción.

Como primer punto es, en un area de storage (en el server de desarrollo) hay que bajar el respaldo de cinta, eso lo puedes hacer con herramientas de sistema operativo o el software que usas.

Una vez que tienes tus backupsets en disco, existen 2 posibilidades, que hayas respaldado el controlfile o que no lo hayas respaldado.

En caso de que lo hayas respaldado, lo más sencillo es restaurarlo del backupset:


RMAN> SET DBID 524232147;

ejecutando el comando: SET DBID

RMAN> RUN
2> {
3> RESTORE CONTROLFILE FROM 'D:\RESPALDO\TAG20080823T172009_48DQF74D_.BKP
4> }



Este comando nos restaurará el controlfile contenido en el backupset (revisen sus logs de RMAN para poder saber cual es el backupset piece correcto), el destino del o de los controlfiles será el asignado en el parámetro control_files del archivo de inicialización (init o spfile).

Si no se respaldó el controlfile, se puede usar uno de producción, no importa que nuestro respaldo de hace 2 días no esté en el catálogo.

Una vez que ya tenemos un controlfile (el restaurado, o bien, el copiado de producción), tenemos 2 posibles escenarios:

El primero es que tengamos el controlfile del respaldo y que el path usado en el respaldo, sea el mismo usado para depositar los archivos en el server de desarrollo (en caso de Linux o Unix se puede usar un link simbólico). Si este es el caso, procedemos a montar la base de datos y empezar nuestro restore y recover:


RMAN> alter database mount;

base de datos montada
canal liberado: ORA_DISK_1

RMAN> RESTORE DATABASE;

RMAN> RECOVER DATABASE;

RMAN> ALTER DATABASE OPEN RESETLOGS;



Pero no siempre podemos tener la suerte de tener el mismo path, o tampoco contamos con un controlfile sin el registro de nuestro backup. ¿Qué podemos hacer en este caso?

Catalogar nuestro backup

No importa si se usa una base de datos de catálogo de rman o no, lo importante es que nuestro controlfile sepa que el respaldo de nuestra base de datos existe, la forma es la siguiente.

Reviso que no tenga un backup registrado, o por lo menos no uno que me sirva en mi controlfile:


RMAN> list backup of database;

RMAN>


Y ahora usando el controlfile actual de mi base de datos (restaurado o copiado), registro la copia del respaldo que tengo.



RMAN> CATALOG START WITH 'd:\RESPALDO';

buscando todos los archivos que coincidan con el patr¾n C:\oracle\product\flash_recovery_area\ORCL\BAC

Lista de Archivos Desconocidos para la Base de Datos
=====================================
Nombre de Archivo: d:\RESPALDO\respaldo.bkp

+Seguro que desea catalogar los archivos anteriores (introduzca SÝ o NO)? y
catalogando archivos...
catalogaci¾n realizada

Lista de Archivos Catalogados
=======================
Nombre de Archivo: d:\RESPALDO\respaldo.bkp



En este punto reviso que mi backup exista en la base de datos


RMAN> list backup of database;


Lista de Juegos de Copias de Seguridad
===================

Clave BS Tipo LV Tama±o Tipo de Dispositivo Tiempo Transcurrido Hora de Finalizaci¾n
------- ---- -- ---------- ----------- ------------ --------------------
56 Full 1.01G DISK 00:02:17 23/08/08
Clave BP: 58 Estado: AVAILABLE Comprimido: NO Etiqueta: TAG20080823T120433
Nombre de Parte: d:\RESPALDO\RESPALDO.BKP
Lista de Archivos de Datos en el juego de copias de seguridad 56
Tipo de Archivo LV SCN Pto. Ctrl. Hora de Punto de Control Nombre
---- -- ---- ---------- ------------------------ ----
1 Full 4921504 23/08/08 C:\ORACLE\PRODUCT\ORADATA\ORCL\SYSTEM01.DBF
2 Full 4921504 23/08/08 C:\ORACLE\PRODUCT\ORADATA\ORCL\UNDOTBS01.DBF
3 Full 4921504 23/08/08 C:\ORACLE\PRODUCT\ORADATA\ORCL\SYSAUX01.DBF
4 Full 4921504 23/08/08 C:\ORACLE\PRODUCT\ORADATA\ORCL\USERS01.DBF
5 Full 4921504 23/08/08 C:\ORACLE\PRODUCT\ORADATA\ORCL\EXAMPLE01.DBF
6 Full 4921504 23/08/08 C:\CRYPTO\ORCL\DATAFILE\O1_MF_ORCL_42K6N37T_.DBF
7 Full 4921504 23/08/08 C:\PRUEBA01.DBF


Aquí puedo comenzar con mi restore desde mis backupsets recientemente registrados


RMAN> shutdown abort

RMAN> startup mount

conectado a la base de datos destino (no iniciada)
instancia Oracle iniciada
base de datos montada

Total del -rea Global del Sistema 314572800 bytes

Fixed Size 1290328 bytes
Variable Size 142610344 bytes
Database Buffers 163577856 bytes
Redo Buffers 7094272 bytes

RMAN> restore database;

Iniciando restore en 23/08/08
canal asignado: ORA_DISK_1
canal ORA_DISK_1: sid=540 devtype=DISK

canal ORA_DISK_1: iniciando restauraci¾n del juego de copias de seguridad de archivos de datos
canal ORA_DISK_1: especificando archivo(s) de datos para restaurar del juego de copias de seguridad
restaurando el archivo de datos 00001 en C:\ORACLE\PRODUCT\ORADATA\ORCL\SYSTEM01.DBF
restaurando el archivo de datos 00002 en C:\ORACLE\PRODUCT\ORADATA\ORCL\UNDOTBS01.DBF
restaurando el archivo de datos 00003 en C:\ORACLE\PRODUCT\ORADATA\ORCL\SYSAUX01.DBF
restaurando el archivo de datos 00004 en C:\ORACLE\PRODUCT\ORADATA\ORCL\USERS01.DBF
restaurando el archivo de datos 00005 en C:\ORACLE\PRODUCT\ORADATA\ORCL\EXAMPLE01.DBF
restaurando el archivo de datos 00006 en C:\CRYPTO\ORCL\DATAFILE\O1_MF_ORCL_42K6N37T_.DBF
restaurando el archivo de datos 00007 en C:\PRUEBA01.DBF
canal ORA_DISK_1: leyendo desde la parte de copia de seguridad d:\RESPALDO\RESPALDO.BKP


Consideración adicional, usen el comando set new name en caso de que deban restaurar a un path distinto.

Espero que les sea de utilidad, ya sea para clonar una base de datos o simplemente recuperar la información.

viernes, 15 de agosto de 2008

Query para ver lo que está sucediendo

Recientemente Tanel Poder en su blog publicó esta entrada. Vale mucho la pena el enfoque qué él le da.

Generalmente cuando me toca ir con algún cliente por algún tema de performance, siempre suele haber dos situaciones distintas:


1) Están por correr procesos pesados y se quiere una foto de todo lo que está sucediendo en el sistema.

2) El sistema da la apariencia de estar pasmado.

Para el primer caso, lo que puedo sugerir es la activación de statspack



SQL> @?/rdbms/admin/spcreate.sql

Creating Package STATSPACK...

Paquete creado.

No hay errores.
Creating Package Body STATSPACK...

Cuerpo del paquete creado.

No hay errores.

NOTE:
SPCPKG complete. Please check spcpkg.lis for any errors.

SQL> conn perfstat
Introduzca la contraseña:
Conectado.

SQL> exec statspack.snap(I_SNAP_LEVEL=>7);


A partir de este punto, dejo que corran todo lo que deben de correr, y después de unos 15 mins, o que los procesos han terminado, vuelvo a ejecutar


SQL> exec statspack.snap(I_SNAP_LEVEL=>7);


Genero los reportes diferenciales, y analizo la información



SQL> define begin_snap=1;
SQL> define end_snap=2;
SQL> @?/rdbms/admin/spreport.sql


Hay que recordar que toda la información es generalizada y agregada es decir, supongamos que tenemos lo siguiente:


Top 5 Timed Events
~~~~~~~~~~~~~~~~~~
Event Waits Time (s)
----------------------------------------- ------------ -----------
CPU time 15,000
db file scattered read 21,156 830
control file sequential read 1,261 10
....


Y esto en un periodo de 1 Hora entre snapshots, debemos de tomar en cuenta que la información es agregada.

En este ejemplo (totalmente ficticio), pareciera que la instancia tiene problemas de CPU, pero si pensáramos que el equipo cuenta con 32 procesadores, estaríamos viendo que en promedio cada CPU se ocupó 13.5% y el problema podría venir por otro lado.


Para las situaciones en donde el sistema aparenta estar pasmado, o se quiere saber lo que está sucediendo, suelo tener 3 sentencias SQL (la primera muy similar a la de Tanel)


SQL> select count(1), event
2 from v$session_wait
3 group by event
4 order by 1 desc;

COUNT(1) EVENT
---------- -------------------------------------
19 enq: TM - contention
12 rdbms ipc message
1 jobq slave wait
1 SQL*Net message to client
1 smon timer
1 pmon timer
1 SQL*Net message from client

7 filas seleccionadas.


Esto nos dice que hay 19 sesiones sufriendo de encolamiento (pero lo mismo puede aplicar para cualquier wait != Idle)


SQL> select count(1), s.sql_hash_value, sw.event
2 from v$session s, v$session_wait sw
3 where s.sid=sw.sid
4 group by s.sql_hash_value,sw.event
5 order by 1 desc;

COUNT(1) SQL_HASH_VALUE EVENT
---------- -------------- ----------------------------
19 3864513861 enq: TM - contention
11 0 rdbms ipc message
1 2390948774 SQL*Net message to client
1 0 smon timer
1 3393152264 rdbms ipc message
1 0 pmon timer
1 0 jobq slave wait
1 0 SQL*Net message from client

8 filas seleccionadas.

Y finalmente, podemos ir al Shared pool a ver el plan de ejecución


SQL> select * from table(dbms_xplan.display_cursor(3864513861));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------

HASH_VALUE 3864513861, child number 0
--------------------------------------
lock table hilos in exclusive mode

Plan hash value: 2876205575

-----------------------------------
| Id | Operation | Name |
-----------------------------------
| 0 | DDL STATEMENT | |
| 1 | TABLE ACCESS FULL| HILOS |
-----------------------------------


El último query sólo se puede hacer en instancias 10g+, en caso de nueve, se deberá buscar el plan de ejecución en V$SQL_PLAN.

Si se está usando 10g, tambien se puede evitar el acceso a la vista v$session_wait, ya que v$session contiene la información de eventos de espera.


SQL> select count(1) ,sql_hash_value, event
2 from v$session
3 group by sql_hash_value, event
4 order by 1 desc;

COUNT(1) SQL_HASH_VALUE EVENT
---------- -------------- -----------------------------
19 3864513861 enq: TM - contention
11 0 rdbms ipc message
1 0 SQL*Net message from client
1 0 smon timer
1 0 pmon timer
1 2593210877 SQL*Net message to client
1 0 jobq slave wait
1 3393152264 rdbms ipc message

8 filas seleccionadas.

lunes, 4 de agosto de 2008

Sesiones colgadas de Forms en E-business

Muy a menudo, me encuentro con clientes que quieren agregar muchos nodos aplicativos a un E-business suite porque tienen mucha carga en los nodos, y aparentemente no soportan más conexiones.

Sé que esto no va relacionado a base de datos completamente, pero para los Applications DBAs, suele ser un dolor de cabeza el estar revisando las cargas de CPU en los servidores de aplicaiones.

Me ha tocado administrar instancias de E-Business que tienen más de 2,000 usuarios concurrentes con sólo 2 servidores aplicativos, y el desempeño es excelente. Me ha tocado estar en ambientes donde la carga de un servidor de aplicación es altísima y sólo tiene 50 usuarios concurrentes.

La razón parece estar siempre ligada a procesos que se quedan en el limbo o en "stupid mode". Cada uno de estos procesos puede consumir hasta el 100% de un CPU, y si tenemos 10 de estos procesos, y sólo 6 procesadores, el servidor "estará corriendo como un perro con dos patas".



Hace un tiempo hice un script para facilitar la detección de estos procesos y finalmente eliminarlos con la tranquilidad de que no son procesos que existen en la aplicación.

sesiones_colgadas.sh


#!/usr/bin/ksh
##########################################
# Script para eliminar las sesiones
# colgadas de forms.
#
# Valida que las sesiones de forms
# Consuman mucho CPU y lleven corriendo
# mas de 5 minutos. Si cumplen estas
# caracteristicas, se evaluan a nivel
# base de datos, si no hay proceso
# relacionado, el cliente se mata.
#
#
# Elaborado por: Hugo E. Contreras
# Fecha: Abril 10, 2008
#
# Resource IT
##########################################

# Variables de ambiente
export ORACLE_HOME=/u01/oracle/PROD/apps/tech_st/10.1.2
export TWO_TASK=PROD
export USUARIO=monitor
export PASSWORD=******


for SerPid in `ps -eo pcpu,pid,etime,cmd|grep frmweb|sort -n|awk '$1 >10 && $3 > "05:00" {print $2}'`
do
ABC=`sqlplus -s $USUARIO/$PASSWORD << EOF
set head off
set pagesize 0
set feed off
select count(1) from v\\$session where process ='$SerPid';
exit;
EOF`

echo "el Sid $SerPid tiene $ABC sesion(es) en la base de datos"

if [ $ABC -lt 1 ]
then
echo "Se procede a matar el proceso $SerPid"
kill -9 $SerPid
fi

done



Hay muchas cosas a configurar en este script, y dependiendo del release del e-business, algo más puede cambiar, estas son algunas consideraciones que se deben de tener.

Si es 11i, el ORACLE_HOME a usar es el 8.0.6, en este caso el script está pensado para 12.0.3. En el caso de 11i, el proceso a buscar es f60webmx, para 12i es frmweb.

En caso de tener un RAC configurado como base de datos, lo primero es usar la vista GV$SESSION en lugar de V$SESSION. Algo adicional, es que se debe de evaluar el nombre del equipo, es decir

AND machine = `hostname`

Ya que pudiera ser que alguna instancia del RAC tuviera un proceso padre (gv$session.process) con el mismo número en una instancia distinta.

En caso de tener más de 1 application server, sea o no un RAC, también se debe poner la condición

AND machine = `hostname`

Esto es porque dos o más servidores de aplicación pudieran usar el mismo spid para un proceso de forms.

Alguna consideración en el caso de solaris; el comando

ps -eo pcpu,pid,etime,cmd

debe ser cambiado por

ps -eo pcpu,pid,etime,comm

Seguramente muchas otras cosas pueden cambiar entre HP-UX, Aix, Solaris o diferentes sabores de Linux, pero la idea es casi la misma:

Obtener la lista de procesos que contienen el ejecutable que nos interesa, buscar sólo aquellos que consumen más del 10% de 1 CPU y que lleven por lo menos 5 minutos activas.

Cuando todas estas condiciones se cumplen, se evalúa contra la base de datos para corroborar que el proceso hijo todavía existe, en caso de no existir, el proceso es eliminado a través de un kill -9.

El script se debe de ejecutar en el servidor de aplicación.