- Alo, Alexandru?
- Da!
- Avem un P1. S-ar parea ca nu mai merge nimic la baza de date.
- Ups, zic. Hai că vin pe la firmă să văd care-i baiul.
Zis și făcut! Mă sui în mașină, bag "pedală", ajung de urgență la serviciu. Care-o fi cauza? WTF? Ce se întâmplă? Când colo, să vezi drăcie! Iaca ditamai mesajul de eroare in log:
ORA-28001: the password has expiredSuper, zic! Hai să vedem cum rezolvăm problema! Și repede, dacă se poate, că doar e un P1 (adică, pentru cei mai puțin obișnuiți cu jargonul "de specialitate", prioritate 1).
Bun, putem debloca un cont expirat? Ceva de genul, "ALTER USER ACCOUNT UNLOCK"? Of course, not! Nu funcționează pentru conturi expirate. Singura soluție e să resetăm omului parola! Bun, bun, da' care-i parola omului? O știm? Nuoooooo! Ok, nu-i bai! Putem folosi parola hash-uită pe care o extragem frumușel din tabela SYS.USER$. Sau, putem fi șmecheri, și sa obținem hash-ul cu pricina folosind DBMS_METADATA.GET_DDL. Să vedem care-i metoda cea mai bună.
SQL> select password from sys.user$ where name='MUMU'; PASSWORD ---------------- 2DB90C8E4C2DCA28Avem hash-ul omului. De-acu, ar trebui să fie simplu: "ALTER USER MUMU IDENTIFIED BY VALUES ...". Haideți să vedem și cu varianta DBMS_METADATA.GET_DDL.
SQL> set longc 300 SQL> set long 300 SQL> select dbms_metadata.get_ddl('USER', 'MUMU') def from dual; DEF ------------------------------------------------------------------------------------------ CREATE USER "MUMU" IDENTIFIED BY VALUES 'S:5905A16074D32338FC7148AA8012E5D11DB70A060CCC125C8BF839B4CB87;2DB90C8E4C2DCA28' DEFAULT TABLESPACE "USERS" TEMPORARY TABLESPACE "TEMP" PROFILE "TEST" PASSWORD EXPIREWTF? Hash-urile sunt diferite! Interesant e că sufixul hash-ului din DBMS_METADATA este fix hash-ul din tabela USER$. Intersant... N-avem timp de pierdut cu investigațiile (e un P1, deaaa?), dar vom cerceta dup-aia. Hash-ul din DBMS_METADATA e mai credibil, pentru că e obținut printr-un API public, documentat, spre deosebire de tabelele interne de gen SYS.USER$. Prin urmare, mergem pe varianta hash-ului "grăsunel" și resetăm parola folosind acest hash.
ALTER USER "MUMU" IDENTIFIED BY VALUES 'S:5905A16074D32338FC7148AA8012E5D11DB70A060CCC125C8BF839B4CB87;2DB90C8E4C2DCA28';Bun, ia sa vedem cum arata contul acum:
SQL> select username, account_status 2 from dba_users 3 where username='MUMU'; USERNAME ACCOUNT_STATUS -------- -------------- MUMU OPENFoarte frumos, arată bine! Primim confirmarea de la client că treaba-i bună și putem răsufla ușurați. Din păcate, clientul e deja supărat și ne-a solicitat să investigăm de ce s-a ajuns aici, adică de ce au expirat conturile cu pricina. Ne prindem repede ce s-a întamplat: s-ar parea că era vorba de niște baze de date de 11g care fuseseră instalate cu "Next, Next, Next" și s-a omis faptul că în 11g profilul DEFAULT are o politică de expirare a parolei de 180 de zile. Dat fiind că respectivele conturi aveau asociate profilul DEFAULT s-a ajuns, bine-înțeles, aici. Clientul s-a declarat mulțumit, mai ales că bazele de date cu pricina n-au fost instalate de noi, prin urmare nu prea mai avea ce să mai spună.
Care-i faza cu HASH-ul?
A rămas întrebarea cu hash-urile: de ce DBMS_METADATA folosește un alt hash pentru parolă, diferit de cel din tabela SYS.USER? Să testăm! Creăm un user, cobai:SQL> grant create session to cobai identified by "XXX"; Grant succeeded.Să vedem hash-urile:
SQL> select password from sys.user$ where name='COBAI'; PASSWORD ---------------- BA26C99FA9DD27DD SQL> select dbms_metadata.get_ddl('USER', 'COBAI') def from dual; DEF ------------------------------------------------------------------------------------------ CREATE USER "COBAI" IDENTIFIED BY VALUES 'S:2DAEEF52366652141EA7D8BA93008B6509EC5C7B7B3CFAEDD4EDD73342E2;BA26C99FA9DD27DD' DEFAULT TABLESPACE "USERS" TEMPORARY TABLESPACE "TEMP"Ce se întâmplă dacă folsim doar hash-ul din tabela USER$? Să vedem:
SQL> alter user cobai identified by values 'BA26C99FA9DD27DD'; User altered. SQL> connect cobai/xxx Connected.A mers! Oare? A se observa că parola lui COBAI e trei de X, majuscule. Deci, in 11g, cu setările de parolă cu "case-sensitivity", comanda de conectare de mai sus ar fi trebuit să "crape". Ei bine, avem deci un răspuns la întrebarea noastră. Dacă folosim doar hash-ul din SYS.USER$, parola respectivului utilizator se va comporta ca o parolă de 10g, adică "case-insensitive". Verificăm...
SQL> alter user cobai identified by 2 values 'S:2DAEEF52366652141EA7D8BA93008B6509EC5C7B7B3CFAEDD4EDD73342E2;BA26C99FA9DD27DD' / User altered. SQL> connect cobai/xxx ERROR: ORA-01017: invalid username/password; logon denied Warning: You are no longer connected to ORACLE. SQL> connect cobai/"XXX" Connected.Am mai învățat ceva nou!
Cum scăpăm de expirare?
La vreo două zile după P1-ul buclucaș, primim la departamentul DBA ceva de genul:"Vă rugăm să investigați dacă mai sunt si alte baze de date cu aceeași problemă și, dacă da, scoateți politica de expirare. Semnat, CLIENTUL!"
Bun, determinăm noi că mai sunt și alte baze de date care ar putea avea aceeași problemă, după care propunem o soluție banală:
ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;Nimic deosebit! Doar că, nea Clientu' ne-a întrebat:
"Dacă facem modificarea în profil, să înțeleg că toate conturile asociate nu vor mai expira? Sau, dat fiind că aceste conturi au deja un EXPIRY_DATE, va trebui să le resetăm la mână?"
Hmmm... asta mă enervează la ORACLE. Un milion de spețe la care nici nu ți-a trecut prin cap să te gândești! Bunul simț ar trebui să indice că o modificare în profil ar trebui să se reflecte asupra tuturor conturilor asociate. Și aparent așa și este. Dar, doar dacă respectivele conturi sunt "OPEN". Dacă conturile sunt în perioada de grație sau au expirat deja, atunci modificarea din profil nu va avea nici un impact asupra acelor conturi. Dovada:
SQL> select * from dba_profiles 2 where profile='DEFAULT' and resource_name='PASSWORD_LIFE_TIME'; PROFILE RESOURCE_NAME RESOURCE LIMIT ------- ------------------ -------- ----- DEFAULT PASSWORD_LIFE_TIME PASSWORD 180 SQL> select username, account_status, expiry_date 2 from dba_users 3 where username in ('COBAI', 'DBSNMP', 'BUBU'); USERNAME ACCOUNT_STATUS EXPIRY_DA -------- -------------- --------- DBSNMP EXPIRED 14-SEP-13 BUBU EXPIRED(GRACE) 15-SEP-13 COBAI OPEN 15-MAR-14 SQL> alter profile default limit password_life_time unlimited; Profile altered. SQL> select username, account_status, expiry_date 2 from dba_users 3 where username in ('COBAI', 'DBSNMP', 'BUBU'); USERNAME ACCOUNT_STATUS EXPIRY_DA -------- -------------- --------- DBSNMP EXPIRED 14-SEP-13 BUBU EXPIRED(GRACE) 15-SEP-13 COBAI OPENDeci, raspunsul corect e: "deeeepindeeee!"
Nota: Am testat scenariile de mai sus pe o masina virtuală de VirtualBox. Bine-înțeles că a trebuit să ma joc cu ceasul sistemului astfel încât să pot păcăli serverul Oracle să creadă că expirarea contului trebuie să se producă sau pentru a activa perioada de grație. Problema e că mașina virtuală își sincronizează automat ceasul cu mașina gazdă, prin urmare o operație banală de dare a ceasului înainte s-ar putea dovedi o adevărată provocare și, prin extensie, o sursă de înjurături de care, personal, nu sunt mândru deloc. Soluția a fost să opresc serviciul de "VirtualBox Additions".
0 commentarii:
Trimiteți un comentariu