set wng off; set term ^; execute block as begin -- do NOT remote! This can be useful later for debug purposes! --rdb$set_context('USER_SESSION','DEBUG_RDB_TABLE', 'RDB$PROCEDURES'); end ^ set term ;^ create or alter user %(non_privileged_name)s password '123'; create or alter user %(dba_privileged_name)s password '123'; commit; revoke all on all from %(non_privileged_name)s; revoke all on all from %(dba_privileged_name)s; commit; grant RDB$ADMIN to %(dba_privileged_name)s; commit; grant create TABLE to %(non_privileged_name)s, %(dba_privileged_name)s; grant create VIEW to %(non_privileged_name)s, %(dba_privileged_name)s; grant create PROCEDURE to %(non_privileged_name)s, %(dba_privileged_name)s; grant create FUNCTION to %(non_privileged_name)s, %(dba_privileged_name)s; grant create PACKAGE to %(non_privileged_name)s, %(dba_privileged_name)s; grant create GENERATOR to %(non_privileged_name)s, %(dba_privileged_name)s; grant create SEQUENCE to %(non_privileged_name)s, %(dba_privileged_name)s; grant create DOMAIN to %(non_privileged_name)s, %(dba_privileged_name)s; grant create EXCEPTION to %(non_privileged_name)s, %(dba_privileged_name)s; grant create ROLE to %(non_privileged_name)s, %(dba_privileged_name)s; grant create CHARACTER SET to %(non_privileged_name)s, %(dba_privileged_name)s; grant create COLLATION to %(non_privileged_name)s, %(dba_privileged_name)s; grant create FILTER to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any TABLE to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any VIEW to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any PROCEDURE to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any FUNCTION to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any PACKAGE to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any GENERATOR to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any SEQUENCE to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any DOMAIN to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any EXCEPTION to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any ROLE to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any CHARACTER SET to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any COLLATION to %(non_privileged_name)s, %(dba_privileged_name)s; grant alter any FILTER to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any TABLE to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any VIEW to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any PROCEDURE to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any FUNCTION to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any PACKAGE to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any GENERATOR to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any SEQUENCE to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any DOMAIN to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any EXCEPTION to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any ROLE to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any CHARACTER SET to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any COLLATION to %(non_privileged_name)s, %(dba_privileged_name)s; grant drop any FILTER to %(non_privileged_name)s, %(dba_privileged_name)s; commit; create or alter view v_passed as select 1 id from rdb$database; commit; recreate table vulnerable_on_sys_tables( sys_table rdb$relation_name, ord_pos smallint, -- for proper sorting ret_dbkey smallint default 0, -- 1 ==> this expression CONTAINS 'returning [t.]rdb$dbkey, otherwise 0 sys_dbkey char(8) character set octets, -- value of returned rdb$db_key (to be sure that change really occured) vulnerable_type char(1), vulnerable_gdscode int, vulnerable_sqlcode int, vulnerable_sqlstate char(5), vulnerable_expr varchar(1024), dml_where varchar(255), actual_role varchar(20), id int generated by default as identity constraint pk_vulnerable_on_sys_tables primary key ); commit; create or alter view v_passed as select vulnerable_expr -- || ' rollback;' || iif( v.ret_dbkey = 1, ' -- length of returned rdb$dbkey='|| coalesce(octet_length(sys_dbkey),'0'), '' ) as vulnerable_expr from vulnerable_on_sys_tables v where v.vulnerable_gdscode < 0 and ( v.ret_dbkey is distinct from 1 or coalesce(octet_length(sys_dbkey),0) > 0 ) order by sys_table ,vulnerable_expr -- before 23.10.2015: "order by sys_table, id" --> FAILED on comparison with expected stdout because 'id' was not included in output ; commit; recreate table dml_expr(s varchar(1024)); commit; insert into dml_expr(s) select 'create sequence g_common' from rdb$database union all select 'create exception ex_bad_argument ''argument @1 is invalid''' from rdb$database union all select 'create collation nums_coll for utf8 from unicode case insensitive ''NUMERIC-SORT=1''' from rdb$database union all select 'create domain dm_int as int' from rdb$database union all select 'create domain dm_nums as varchar(20) character set utf8 collate nums_coll' from rdb$database union all select 'create table test_master(id dm_int primary key using index test_master_pk, txt dm_nums, z computed by( char_length(txt) ), constraint chk_01 check(txt is not null) )' from rdb$database union all select 'create table test_detail(id dm_int primary key using index test_detail_pk, pid dm_int, constraint fk_01 foreign key(pid) references test_master(id) on delete cascade using index test_detail_fk )' from rdb$database union all select 'create index test_master_txt on test_master(txt)' from rdb$database union all select 'create view v_test_master as select * from test_master' from rdb$database union all select 'create procedure sp_test_master(a_id dm_int) returns(o_txt dm_nums) as begin for select txt from test_master where id=:a_id into o_txt do suspend; end' from rdb$database union all select 'create function fn_test_master(a_id dm_int) returns dm_nums as begin return (select txt from test_master where id=:a_id); end' from rdb$database union all select 'create trigger test_master_bi for test_master active before insert as begin new.id=gen_id(g_common,1); end' from rdb$database union all select 'create package test_pkg as begin procedure pkg_test(a_id dm_int) returns(o_txt dm_nums); end' from rdb$database union all select 'create package body test_pkg as begin procedure pkg_test(a_id dm_int) returns(o_txt dm_nums) as begin for select txt from test_master where id=:a_id into o_txt do suspend; end end' from rdb$database ; commit; set term ^; execute block as declare v_dbname type of column mon$database.mon$database_name; declare v_usr type of column sec$users.sec$user_name = '%(dba_privileged_name)s'; declare v_pwd varchar(20) = '123'; declare v_role varchar(20) = 'RDB$ADMIN'; begin v_dbname = 'localhost:' || rdb$get_context('SYSTEM','DB_NAME'); for select s from dml_expr as cursor c do execute statement (c.s) on external (v_dbname) as user upper(v_usr) password (v_pwd) role (v_role); end ^ set term ;^ commit; -- ################################################################## -- [ 1 ] attempts to create new tables with FKs ref. to SYSTEM tables -- ################################################################## set term ^; create or alter procedure sp_gen_expr_for_creating_fkeys as declare rel_name varchar(31) character set unicode_fss collate unicode_fss; declare vulnerable_expr varchar(1024) character set unicode_fss collate unicode_fss; declare v_fk1 varchar(8192) character set unicode_fss collate unicode_fss; declare v_fks varchar(8192) character set unicode_fss collate unicode_fss; declare v_mf varchar(8192) character set unicode_fss collate unicode_fss; begin for with a as ( select lower(rc.rdb$relation_name) tab_name --,rc.rdb$constraint_name cnt_name ,lower(rc.rdb$index_name) idx_name --,rc.rdb$constraint_type --,ri.rdb$index_id ,rs.rdb$field_position fld_pos ,lower(rs.rdb$field_name) fld_name --,rf.rdb$field_name ,rf.rdb$field_source fld_src ,case ff.rdb$field_type when 7 then 'smallint' when 8 then 'integer' when 10 then 'float' when 14 then 'char' --(' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')' when 16 then -- dialect 3 only --case ff.rdb$field_sub_type case coalesce(ff.rdb$field_sub_type,0) -- null found for rdb$generators.rdb$initial_value AND rdb$triggers.rdb$trigger_type when 0 then 'bigint' when 1 then 'numeric' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')' when 2 then 'decimal' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')' else 'unknown' end when 12 then 'date' when 13 then 'time' when 27 then -- dialect 1 only case ff.rdb$field_scale when 0 then 'double precision' else 'numeric' -- (15,' || cast(-f.rdb$field_scale as varchar(6)) || ')' end when 35 then iif(m.dia=1, 'date', 'timestamp') when 37 then 'varchar' -- (' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')' when 261 then 'blob' -- sub_type ' || f.rdb$field_sub_type || ' segment size ' || f.rdb$segment_length else 'unknown' end as fld_base_type --,ff.rdb$field_length fld_len ,cast(ff.rdb$field_length / iif(ce.rdb$character_set_name in ( upper('utf8'), upper('un~icode_fss') ), 4, 1) as int) fld_len ,lower(ce.rdb$character_set_name) cset ,lower(co.rdb$collation_name) coll ,dense_rank()over(partition by rc.rdb$relation_name order by rc.rdb$index_name, rs.rdb$field_position) dr1 ,dense_rank()over(partition by rc.rdb$relation_name, rc.rdb$index_name order by rs.rdb$field_position) dr2 ,dense_rank()over(partition by rc.rdb$relation_name, rc.rdb$index_name order by rs.rdb$field_position desc) dr2d ,count(*)over(partition by rc.rdb$relation_name) fld_cnt from (select m.mon$sql_dialect as dia from mon$database m) m cross join rdb$relation_constraints rc join rdb$relations rr on rc.rdb$relation_name = rr.rdb$relation_name join rdb$indices ri on rc.rdb$index_name = ri.rdb$index_name join rdb$index_segments rs on ri.rdb$index_name = rs.rdb$index_name left join rdb$relation_fields rf on rc.rdb$relation_name = rf.rdb$relation_name and rs.rdb$field_name = rf.rdb$field_name left join rdb$fields ff on rf.rdb$field_source = ff.rdb$field_name left join rdb$collations co on ff.rdb$character_set_id=co.rdb$character_set_id and ff.rdb$collation_id=co.rdb$collation_id left join rdb$character_sets ce on co.rdb$character_set_id=ce.rdb$character_set_id where --rc.rdb$relation_name --starting with upper('rdb$') coalesce(rr.rdb$system_flag,0) is distinct from 0 -- ### S Y S T E M t a b l e s ### and rr.rdb$relation_type = 0 -- exclude: views, GTTs, external tables and rr.rdb$relation_name not starting with 'MON$' AND (RR.RDB$RELATION_NAME = rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') or rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') is null) ) ,b as ( select tab_name, idx_name, fld_pos, fld_name, fld_src, fld_base_type, fld_len, cset, coll, dr1, dr2, dr2d, fld_cnt, trim( replace( fld_name, '$','_') )||' '||trim(fld_base_type) ||iif( fld_base_type in ('char','varchar', 'numeric'), '(' || fld_len || ')' || iif( fld_base_type='numeric', '', ' '||trim(coalesce(' character set '||cset, ''))||' '||trim(coalesce(' collate '||coll, ''))) ,'' ) fld_full from a ) ,c as ( select tab_name, idx_name, fld_pos, fld_name, fld_src, fld_base_type, fld_len, cset, coll, dr1, dr2, dr2d, fld_cnt, fld_full, (select list(distinct fld_full) from b x where x.tab_name = b.tab_name) fld_list from b ) select * from c order by tab_name, idx_name, dr1 as cursor c do begin if ( c.dr1 = 1 ) then begin vulnerable_expr = 'recreate table ' || trim(replace(c.tab_name,'$','_')) || '(' || c.fld_list; v_fks = ''; end if ( c.dr2 = 1 ) then begin v_fk1 = ''; v_mf = ''; end if ( c.dr2 = 1 ) then v_fk1 = v_fk1 || ' ,constraint fk_' || lower(trim(replace(c.idx_name,'$','_'))) || ' foreign key('; v_fk1 = v_fk1 || iif( c.dr2 = 1, '', ', ') || trim( replace( c.fld_name, '$', '_' ) ); v_mf = v_mf || iif( c.dr2 = 1, '', ', ') || trim( c.fld_name ); if ( c.dr2d = 1 ) then begin v_fk1 = v_fk1 || ')' || ' references ' || trim(lower(c.tab_name)) || '(' || v_mf || ')'; v_fks = v_fks || v_fk1; end if ( c.dr1 = c.fld_cnt ) then begin vulnerable_expr = vulnerable_expr || v_fks || ');'; rel_name = c.tab_name; insert into vulnerable_on_sys_tables( sys_table, vulnerable_type, vulnerable_expr) values( :rel_name, 'R', :vulnerable_expr); end end end ^ -- ################################################################# -- [ 2 ] attempts to change SYSTEM tables with DML or DDL statements -- ################################################################# create or alter procedure sp_gen_expr_for_direct_change as begin for ------------------ with recursive c0 as( select r.rdb$relation_name rel_name ,rf.rdb$field_position fld_pos ,rf.rdb$field_name fld_name ,case f.rdb$field_type when 7 then 'smallint' when 8 then 'integer' when 10 then 'float' when 14 then 'char' --(' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')' when 16 then -- dialect 3 only case coalesce(f.rdb$field_sub_type,0) -- null found for rdb$generators.rdb$initial_value AND rdb$triggers.rdb$trigger_type when 0 then 'bigint' when 1 then 'numeric' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')' when 2 then 'decimal' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')' else 'unknown' end when 12 then 'date' when 13 then 'time' when 27 then -- dialect 1 only case f.rdb$field_scale when 0 then 'double precision' else 'numeric' -- (15,' || cast(-f.rdb$field_scale as varchar(6)) || ')' end when 35 then iif(m.dia=1, 'date', 'timestamp') when 37 then 'varchar' -- (' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')' when 261 then 'blob' -- sub_type ' || f.rdb$field_sub_type || ' segment size ' || f.rdb$segment_length else 'unknown' end as fld_base_type ,iif( coalesce(rf.rdb$null_flag,0) = 0, 1, 0) fld_nullable ,count(*)over(partition by r.rdb$relation_name) fld_count ,sum(iif(rf.rdb$field_name = upper('rdb$system_flag'),1,0))over(partition by r.rdb$relation_name) has_sys_flag from (select m.mon$sql_dialect as dia from mon$database m) m join rdb$relations r on 1=1 join rdb$relation_fields rf on r.rdb$relation_name = rf.rdb$relation_name join rdb$fields f on rf.rdb$field_source = f.rdb$field_name left join rdb$collations co on f.rdb$character_set_id=co.rdb$character_set_id and f.rdb$collation_id=co.rdb$collation_id left join rdb$character_sets ce on co.rdb$character_set_id=ce.rdb$character_set_id where coalesce(r.rdb$system_flag,0) is distinct from 0 --------------- ### S Y S T E M t a b l e s ### and r.rdb$relation_type = 0 -- exclude: views, GTTs, external tables and r.rdb$relation_name not starting with 'MON$' and f.rdb$computed_source is null ------------------------------ ### exclude COMPUTED BY fields! ### AND (R.RDB$RELATION_NAME = rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') or rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') is null) order by rel_name,fld_pos ) ,c1 as ( select rel_name ,has_sys_flag ,fld_pos ,fld_name ,fld_base_type ,fld_nullable ,fld_count -- these are values which we'll try to write in insert or update statements -- if corresponding fields are NOT null: ,decode( fld_base_type, 'smallint', '32767', 'integer', '2147483647', 'bigint', '9223372036854775807', 'float', '0e0', 'numeric', '0.00', 'decimal', '0.00', 'double precision', '0e0', 'date', 'current_date', 'time', 'current_time', 'timestamp', 'current_timestamp', 'char', '''C''', 'varchar', '''V''', 'blob', '''test_for_blob''' ) ins_default from c0 ) -- select * from c1 ,c2 as ( select rel_name ,fld_pos ,fld_name ,fld_base_type ,fld_nullable ,fld_count ,ins_default ,cast( 'select ' || trim(fld_name) || iif( c1.fld_pos+1 = c1.fld_count, ' from '||trim(rel_name)||' rows 1 with lock;', '') as varchar(1024) ) as lok_stt ,cast( 'insert into ' || trim(rel_name) || '(' || trim(fld_name) || iif( c1.fld_pos+1 = c1.fld_count, ')', '') as varchar(1024) ) as ins_stt ,cast( 'values(' || trim(iif(fld_nullable=1, 'null', ins_default)) || iif( c1.fld_pos+1 = c1.fld_count, ') returning rdb$db_key;', '') as varchar(1024) ) as ins_val ,cast( 'delete from ' || trim(rel_name) || ' t ' || trim(iif(has_sys_flag=1, 'where coalesce(rdb$system_flag,0)=0', '')) || ' rows 1' || ' returning t.rdb$db_key' || iif( c1.fld_pos+1 = c1.fld_count, ';', '') as varchar(1024) ) del_stt ,cast( 'alter table '|| trim(rel_name) || ' add rdb$my_field char(31)' as varchar(1024)) add_fld ,cast( 'alter table '|| trim(rel_name) || ' alter '||trim(fld_name)|| ' DROP NOT null' as varchar(1024)) set_nul ,cast( 'alter table '|| trim(rel_name) || ' add constraint '||left(trim(fld_name)||'_dummy',31) ||' check ('||trim(fld_name) ||' is not distinct from '||trim(fld_name)||')' as varchar(1024)) set_chk ,cast( 'alter table '|| trim(rel_name) || ' alter '||trim(fld_name)|| ' set default '||ins_default as varchar(1024)) set_def ,cast( 'create descending index '||trim(replace(rel_name,'$','_'))||' on '||trim(rel_name)||'('||trim(fld_name)||')' as varchar(1024)) idx_tab ,cast( 'alter table '|| trim(rel_name) || ' drop '||trim(fld_name) as varchar(1024)) kil_fld ,cast( 'drop table '|| trim(rel_name) as varchar(1024)) kil_tab ,iif( c1.fld_pos+1 = c1.fld_count, 1, 0) cmd_end from c1 where fld_pos = 0 UNION ALL select c1.rel_name ,c1.fld_pos ,c1.fld_name ,c1.fld_base_type ,c1.fld_nullable ,c1.fld_count ,c1.ins_default ,c2.lok_stt || ', ' || trim(c1.fld_name) || iif( c1.fld_pos+1 = c1.fld_count, ' from '||trim(c2.rel_name)||' rows 1 with lock;', '') ,c2.ins_stt || ', ' || trim(c1.fld_name) || iif( c1.fld_pos+1 = c1.fld_count, ')', '') ,trim(c2.ins_val) || ', ' || trim(iif(c1.fld_nullable=1, 'null', c1.ins_default)) || iif( c1.fld_pos+1 = c1.fld_count, ') returning rdb$db_key;', '') ,trim(c2.del_stt) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,trim(c2.add_fld) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,trim(c2.set_nul) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,trim(c2.set_chk) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,trim(c2.set_def) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,trim(c2.idx_tab) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,trim(c2.kil_fld) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,trim(c2.kil_tab) || iif( c1.fld_pos+1 = c1.fld_count, ';', '') ,iif( c1.fld_pos+1 = c1.fld_count, 1, 0) cmd_end from c2 join c1 on c2.rel_name = c1.rel_name and c1.fld_pos = c2.fld_pos + 1 ) --select * from c2 ,c3 as ( select c2.rel_name --, c2.ins_stt || ' ' ||c2.ins_val insert_statement, c2.upd_stt update_statement, c2.del_stt delete_statement ,n.op ,decode( n.op ,'I' ,c2.ins_stt || ' ' ||c2.ins_val ,'L' ,c2.lok_stt --,'U' ,c2.upd_stt ,'D' ,c2.del_stt ,'A' ,c2.add_fld ,'N' ,c2.set_nul ,'C' ,c2.set_chk ,'F' ,c2.set_def ,'X' ,c2.idx_tab ,'K' ,c2.kil_fld ,'Z' ,c2.kil_tab ) vulnerable_expr ,n.ord_pos ,n.ret_dbkey from c2 cross join( select 'I' as op, 10 as ord_pos, 1 as ret_dbkey from rdb$database -- DML, insert union all select 'L', 20, 0 from rdb$database -- DML, select WITH LOCK union all select 'D', 40, 1 from rdb$database -- DML, delete union all select 'A', 50, 0 from rdb$database -- DDL, add column union all select 'N', 60, 0 from rdb$database -- DDL, alter column set NULL flag union all select 'C', 65, 0 from rdb$database -- DDL, alter column add new constraint on it union all select 'F', 70, 0 from rdb$database -- DDL, alter column set DEFAULT value -- ### commented 11-04-2018: no need anymore because now one may to create index on system tables: -- ### union all select 'X', 75, 0 from rdb$database -- DDL, CREATE INDEX union all select 'K', 80, 0 from rdb$database -- DDL, drop column union all select 'Z', 90, 0 from rdb$database -- DDL, drop RDB$-table ) n where cmd_end=1 order by rel_name, n.ord_pos ) ,r1 as( -- Add statements which will try to drop constrains, starting from FK: select rc.rdb$relation_name rel_name, rc.rdb$constraint_name cnt_name, rc.rdb$constraint_type cnt_type from rdb$relation_constraints rc join rdb$relations rr on rc.rdb$relation_name = rr.rdb$relation_name where rc.rdb$relation_name not starting with 'MON$' and coalesce(rr.rdb$system_flag,0) is distinct from 0 --------------- ### S Y S T E M t a b l e s ### AND (RC.RDB$RELATION_NAME = rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') or rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') is null) order by decode(rc.rdb$constraint_type, 'FOREIGN KEY', 0, 1) ) ,r2 as ( select trim(r1.rel_name) as rel_name ,'R' as op ,'alter table '||trim(r1.rel_name)||' drop constraint '||trim(r1.cnt_name)||';' as vulnerable_expr ,67 as ord_pos ,0 as ret_dbkey from r1 ) --select * from r2 ,cu as ( select rel_name ,has_sys_flag ,fld_pos ,fld_name ,fld_base_type ,fld_nullable ,fld_count ,ins_default ,cast( 'update ' || trim(rel_name) || ' t ' || ' set t.'||trim(fld_name)||' = ' || trim(iif(n.i=0, ins_default, 'null'))||' ' || trim(iif(has_sys_flag=1, 'where coalesce(rdb$system_flag,0)='||m.k, '')) || ' rows 1' || ' returning t.rdb$db_key' || ';' as varchar(1024) ) upd_stt ,trim(iif(has_sys_flag=1, 'where coalesce(rdb$system_flag,0)='||m.k, '')) dml_where ,30 as ord_pos ,1 as ret_dbkey from c1 join (select 0 i from rdb$database union all select 1 from rdb$database) n on n.i=0 or c1.fld_nullable=1 and n.i=1 join (select 0 k from rdb$database union all select 1 from rdb$database) m on m.k=0 or c1.has_sys_flag=1 and m.k=1 -- core-4772: fields like rdb$procedure_source have to be updated AFTER all previous (except RDB$SYSTEM_FLAG - this is the LATEST): -- select 1 from rdb$database where 'RDB$TRIGGER_SOURCE' similar to upper( ascii_char(37) || '#_SOURCE' ) escape '#'; ==> must return 1 order by rel_name, iif( trim( upper(fld_name) ) = upper('RDB$SYSTEM_FLAG'), 3, iif( trim( upper(fld_name) ) similar to upper( ascii_char(37) || '#_SOURCE') escape '#', 2, 1) ) ) -- select * from cu select rel_name, ord_pos, ret_dbkey, 'U' as vulnerable_type, dml_where, upd_stt as vulnerable_expr from cu UNION ALL select rel_name, ord_pos, ret_dbkey, op, '' as dml_where, vulnerable_expr from c3 UNION ALL select rel_name, ord_pos, ret_dbkey, op, '' as dml_where, vulnerable_expr from r2 -- alter table drop FK UNION ALL select 'RDB$GENERATORS', 99, 0, 'G', '', 'alter sequence '||trim(g.rdb$generator_name)||' restart with -1;' from rdb$generators g where g.rdb$system_flag = 1 ---------- as cursor c do insert into vulnerable_on_sys_tables( sys_table, ord_pos, ret_dbkey, vulnerable_type, vulnerable_expr, dml_where) values( c.rel_name, c.ord_pos, c.ret_dbkey, c.vulnerable_type, c.vulnerable_expr, c.dml_where); end ^ set term ;^ commit; -- ################################################################################### -- E X E C U T E S T A T E M E N T S: -- A T T E M P T S O F D I R E C T W R I T E T O S Y S T E M T A B L E S -- ################################################################################### set term ^; create or alter procedure sp_run_vulnerable_expressions( a_usr type of column sec$users.sec$user_name ,a_pwd varchar(20) ,a_role varchar(20) ) as declare v_dbname type of column mon$database.mon$database_name; declare v_actual_role varchar(20); declare v_dbkey char(8) character set octets; begin update vulnerable_on_sys_tables set vulnerable_gdscode = null ,vulnerable_sqlcode = null ,vulnerable_sqlstate = null ,sys_dbkey = null ,actual_role = null ; v_dbname = 'localhost:' || rdb$get_context('SYSTEM','DB_NAME'); execute statement 'select current_role from rdb$database' on external (v_dbname) as user upper(a_usr) password (a_pwd) role (a_role) into v_actual_role; for select sys_table, vulnerable_expr, dml_where, vulnerable_type, ret_dbkey from vulnerable_on_sys_tables order by sys_table, ord_pos, id as cursor c do begin v_dbkey = null; if ( c.ret_dbkey = 1) then execute statement c.vulnerable_expr -- this statement ALWAYS should FAIL! on external (v_dbname) as user (a_usr) password (a_pwd) role (a_role) into v_dbkey; else execute statement c.vulnerable_expr -- this statement ALWAYS should FAIL! on external (v_dbname) as user (a_usr) password (a_pwd) role (a_role); -- this statement must NOT occur at all. If this record would be added then it means -- that system table can be DIRECTLY modified by user! update vulnerable_on_sys_tables set vulnerable_gdscode = -1 ,vulnerable_sqlcode = -1 ,vulnerable_sqlstate = 'PASS!' ,sys_dbkey = :v_dbkey ,actual_role = :v_actual_role where current of c; when any do begin update vulnerable_on_sys_tables set vulnerable_gdscode = gdscode ,vulnerable_sqlcode = sqlcode ,vulnerable_sqlstate = sqlstate ,sys_dbkey = null ,actual_role = :v_actual_role where current of c; end end end ^ set term ;^ commit; execute procedure sp_gen_expr_for_creating_fkeys; execute procedure sp_gen_expr_for_direct_change; -- result: table 'vulnerable_on_sys_tables ' will be filled up by ~1560 rows. commit; -- |||||||||||||||||||||||||||| -- ###################################||| FB 4.0+, SS and SC |||############################## -- |||||||||||||||||||||||||||| -- If we check SS or SC and ExtConnPoolLifeTime > 0 (config parameter FB 4.0+) then current -- DB (bugs.core_NNNN.fdb) will be 'captured' by firebird.exe process and fbt_run utility -- will not able to drop this database at the final point of test. -- Moreover, DB file will be hold until all activity in firebird.exe completed and AFTER this -- we have to wait for seconds after it (discussion and small test see -- in the letter to hvlad and dimitr 13.10.2019 11:10). -- This means that one need to kill all connections to prevent from exception on cleanup phase: -- SQLCODE: -901 / lock time-out on wait transaction / object is in use -- ############################################################################################# delete from mon$attachments where mon$attachment_id != current_connection; commit;