8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:43:03 +01:00

SQL SECURITY Feature (#42)

* Added SQL SECURITY clause to various DDL statements
This commit is contained in:
Roman Simakov 2016-09-28 17:24:04 +03:00 committed by Alexander Peshkov
parent d3a0723ae4
commit 9aab6ed8cc
44 changed files with 821 additions and 154 deletions

View File

@ -0,0 +1,162 @@
SQL SECURITY.
Implements capability to run executable objects regarding SQL SECURITY clause.
SQL Standard (2003, 2011) Feature.
Author:
Red Soft, roman.simakov(at)red-soft.ru
Syntax is:
CREATE TABLE <TABLENAME> (...) [SQL SECURITY {DEFINER | INVOKER}]
ALTER TABLE <TABLENAME> ... [{ALTER SQL SECURITY {DEFINER | INVOKER} | DROP SQL SECURITY}]
CREATE [OR ALTER] FUNCTION <FUNCTIONNAME> ... [SQL SECURITY {DEFINER | INVOKER}] AS ...
CREATE [OR ALTER] PROCEDURE <PROCEDURENAME> ... [SQL SECURITY {DEFINER | INVOKER}] AS ...
CREATE [OR ALTER] TRIGGER <TRIGGERNAME> ... [SQL SECURITY {DEFINER | INVOKER} | DROP SQL SECURITY] [AS ...]
CREATE [OR ALTER] PACKAGE <PACKAGENAME> [SQL SECURITY {DEFINER | INVOKER}] AS ...
Description:
Makes it possible to execute some objects with permissions of either definer or invoker.
By default INVOKER is used to keep backword compatibility.
If INVOKER is specified a current set of privileges of the current user will be used.
If DEFINER - a set of privileges of object owner will be used to check an access to database objects used by this object.
Trigger inherits SQL SECURITY option from TABLE but can overwrite it by explicit specifying. If SQL SECURITY option
will be changed for table, existing triggers without explicitly specified option will not use new value immediately
it will take effect next time trigger will be loaded into metadata cache.
For procedures and functions defined in package explicit SQL SECURITY clause is prohibit.
Example 1. It's enought to grant only SELECT privilege to user US for table T.
In case of INVOKER it will require also EXECUTE for function F.
set term ^;
create function f() returns int
as
begin
return 3;
end^
set term ;^
create table t (i integer, c computed by (i + f())) sql security definer;
insert into t values (2);
grant select on table t to user us;
commit;
connect 'localhost:/tmp/7.fdb' user us password 'pas';
select * from t;
Example 2. It's enough to grant EXECUTE privilege to user US for function F.
In case of INVOKER it will require also INSERT for table T.
set term ^;
create function f (i integer) returns int sql security definer
as
begin
insert into t values (:i);
return i + 1;
end^
set term ;^
grant execute on function f to user us;
commit;
connect 'localhost:/tmp/59.fdb' user us password 'pas';
select f(3) from rdb$database;
Example 3. It's enought to grant only EXECUTE privilege to user US for procedure P.
In case of INVOKER it will require also INSERT for table T to either user US or procedure P.
set term ^;
create procedure p (i integer) sql security definer
as
begin
insert into t values (:i);
end^
set term ;^
grant execute on procedure p to user us;
commit;
connect 'localhost:/tmp/17.fdb' user us password 'pas';
execute procedure p(1);
Example 4. It's enought to grant only INSERT privilege to user US for table TR.
In case of INVOKER it will require also INSERT for table T to user US.
create table tr (i integer);
create table t (i integer);
set term ^;
create trigger tr_ins for tr after insert sql security definer
as
begin
insert into t values (NEW.i);
end^
set term ;^
grant insert on table tr to user us;
commit;
connect 'localhost:/tmp/29.fdb' user us password 'pas';
insert into tr values(2);
the same result if specify SQL SECURITY DEFINER for table TR.
create table tr (i integer) sql security definer;
create table t (i integer);
set term ^;
create trigger tr_ins for tr after insert
as
begin
insert into t values (NEW.i);
end^
set term ;^
grant insert on table tr to user us;
commit;
connect 'localhost:/tmp/29.fdb' user us password 'pas';
insert into tr values(2);
Example 5. It's enought to grant only EXECUTE privilege to user US for package PK.
In case of INVOKER it will require also INSERT for table T to user US.
create table t (i integer);
set term ^;
create package pk sql security definer
as
begin
function f(i integer) returns int;
end^
create package body pk
as
begin
function f(i integer) returns int
as
begin
insert into t values (:i);
return i + 1;
end
end^
set term ;^
grant execute on package pk to user us;
commit;
connect 'localhost:/tmp/69.fdb' user us password 'pas';
select pk.f(3) from rdb$database;
Example 6. Altering explicit option SQL SECURITY for triggers.
To remove explicit SQL SECURITY OPTION from trigger you can execute:
alter trigger tr_ins drop sql security;
To set it again to SQL SECURITY INVOKER you can:
alter trigger tr_ins sql security invoker;

View File

@ -23,7 +23,7 @@
#define MAXCHAR 255
#define MAXSHORT ((int)0x7FFFFFFF)
#define MINSHORT ((int)0x80000000)
#define MAXTABLE 120000
#define MAXTABLE 200000
#ifdef __MSDOS__
#define BITS_PER_WORD 16

View File

@ -1792,6 +1792,8 @@ C --
PARAMETER (GDS__dsql_no_output_sqlda = 336003110)
INTEGER*4 GDS__dsql_wrong_param_num
PARAMETER (GDS__dsql_wrong_param_num = 336003111)
INTEGER*4 GDS__dsql_invalid_drop_ss_clause
PARAMETER (GDS__dsql_invalid_drop_ss_clause = 336003112)
INTEGER*4 GDS__dyn_filter_not_found
PARAMETER (GDS__dyn_filter_not_found = 336068645)
INTEGER*4 GDS__dyn_func_not_found

View File

@ -1787,6 +1787,8 @@ const
gds_dsql_no_output_sqlda = 336003110;
isc_dsql_wrong_param_num = 336003111;
gds_dsql_wrong_param_num = 336003111;
isc_dsql_invalid_drop_ss_clause = 336003112;
gds_dsql_invalid_drop_ss_clause = 336003112;
isc_dyn_filter_not_found = 336068645;
gds_dyn_filter_not_found = 336068645;
isc_dyn_func_not_found = 336068649;

View File

@ -117,7 +117,8 @@ void put_data(burp_rel*);
void put_index(burp_rel*);
int put_message(att_type, att_type, const TEXT*, const ULONG);
void put_int32(att_type, SLONG);
void put_int64( att_type attribute, SINT64 value);
void put_int64(att_type attribute, SINT64 value);
void put_boolean(att_type, FB_BOOLEAN value);
void put_relation(burp_rel*);
bool put_source_blob(att_type, att_type, ISC_QUAD&);
int put_text(att_type, const TEXT*, SSHORT);
@ -1984,7 +1985,7 @@ void put_int32( att_type attribute, SLONG value)
}
void put_int64( att_type attribute, SINT64 value)
void put_int64(att_type attribute, SINT64 value)
{
/**************************************
*
@ -2009,6 +2010,24 @@ void put_int64( att_type attribute, SINT64 value)
}
void put_boolean(att_type attribute, const FB_BOOLEAN value)
{
/**************************************
*
* p u t _ b o o l e a n
*
**************************************
*
* Functional description
* Write a FB_BOOLEAN value as an attribute.
**************************************/
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
put(tdgbl, attribute);
put(tdgbl, value ? 1u : 0u);
}
void put_relation( burp_rel* relation)
{
/**************************************
@ -2946,6 +2965,8 @@ void write_functions()
put_int32(att_function_legacy_flag, X.RDB$LEGACY_FLAG);
if (!X.RDB$DETERMINISTIC_FLAG.NULL)
put_int32(att_function_deterministic_flag, X.RDB$DETERMINISTIC_FLAG);
if (!X.RDB$SQL_SECURITY.NULL)
put_boolean(att_function_sql_security, X.RDB$SQL_SECURITY);
put(tdgbl, att_end);
@ -3523,6 +3544,9 @@ void write_packages()
if (!X.RDB$DESCRIPTION.NULL)
put_source_blob(att_package_description, att_package_description, X.RDB$DESCRIPTION);
if (!X.RDB$SQL_SECURITY.NULL)
put_boolean(att_package_sql_security, X.RDB$SQL_SECURITY);
put(tdgbl, att_end);
}
END_FOR
@ -3602,6 +3626,9 @@ void write_procedures()
if (!X.RDB$PRIVATE_FLAG.NULL)
put_int32(att_procedure_private_flag, X.RDB$PRIVATE_FLAG);
if (!X.RDB$SQL_SECURITY.NULL)
put_boolean(att_procedure_sql_security, X.RDB$SQL_SECURITY);
put(tdgbl, att_end);
COPY(X.RDB$PROCEDURE_NAME, proc);
write_procedure_prms ((X.RDB$PACKAGE_NAME.NULL ? "" : X.RDB$PACKAGE_NAME), proc);
@ -3872,6 +3899,9 @@ void write_relations()
if (!X.RDB$RELATION_TYPE.NULL)
put_int32 (att_relation_type, X.RDB$RELATION_TYPE);
if (!X.RDB$SQL_SECURITY.NULL)
put_boolean(att_relation_sql_security, X.RDB$SQL_SECURITY);
put(tdgbl, att_end);
burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel));
relation->rel_next = tdgbl->relations;
@ -4226,6 +4256,9 @@ void write_triggers()
if (!X.RDB$ENTRYPOINT.NULL)
PUT_TEXT(att_trig_entrypoint, X.RDB$ENTRYPOINT);
if (!X.RDB$SQL_SECURITY.NULL)
put_boolean(att_trig_sql_security, X.RDB$SQL_SECURITY);
put(tdgbl, att_end);
END_FOR;

View File

@ -197,9 +197,11 @@ Version 9: FB2.5.
Version 10: FB3.0.
See backup_capabilities in OdsDetection.h.
Version 11: FB 4.0
*/
const int ATT_BACKUP_FORMAT = 10;
const int ATT_BACKUP_FORMAT = 11;
// format version number for ranges for arrays
@ -267,6 +269,7 @@ enum att_type {
att_relation_flags,
att_relation_ext_file_name, // name of file for external tables
att_relation_type,
att_relation_sql_security,
// Field attributes (used for both global and local fields)
@ -400,6 +403,7 @@ enum att_type {
att_trig_engine_name,
att_trig_entrypoint,
att_trig_type2,
att_trig_sql_security,
// Function attributes
@ -423,6 +427,7 @@ enum att_type {
att_function_owner_name,
att_function_legacy_flag,
att_function_deterministic_flag,
att_function_sql_security,
// Function argument attributes
@ -518,6 +523,7 @@ enum att_type {
att_procedure_entrypoint,
att_procedure_package_name,
att_procedure_private_flag,
att_procedure_sql_security,
// Stored procedure parameter attributes
@ -617,7 +623,8 @@ enum att_type {
att_package_valid_body_flag,
att_package_security_class,
att_package_owner_name,
att_package_description
att_package_description,
att_package_sql_security
};

View File

@ -182,6 +182,11 @@ static inline int get(BurpGlobals* tdgbl)
return MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr);
}
static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl)
{
return get(tdgbl) ? FB_TRUE : FB_FALSE;
}
static inline att_type get_attribute(att_type* att, BurpGlobals* tdgbl)
{
*att = (att_type) get(tdgbl);
@ -4181,6 +4186,8 @@ bool get_function(BurpGlobals* tdgbl)
X.RDB$LEGACY_FLAG.NULL = FALSE;
X.RDB$LEGACY_FLAG = 1;
X.RDB$SQL_SECURITY.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
@ -4350,6 +4357,16 @@ bool get_function(BurpGlobals* tdgbl)
bad_attribute(scan_next_attr, attribute, 89);
break;
case att_function_sql_security:
if (tdgbl->RESTORE_format >= 11)
{
X.RDB$SQL_SECURITY = get_boolean(tdgbl);
X.RDB$SQL_SECURITY.NULL = FALSE;
}
else
bad_attribute(scan_next_attr, attribute, 89);
break;
default:
bad_attribute(scan_next_attr, attribute, 89);
// msg 89 function
@ -4471,6 +4488,13 @@ bool get_function(BurpGlobals* tdgbl)
bad_attribute(scan_next_attr, attribute, 89);
break;
case att_function_sql_security:
if (tdgbl->RESTORE_format >= 11)
get_boolean(tdgbl);
else
bad_attribute(scan_next_attr, attribute, 89);
break;
default:
bad_attribute(scan_next_attr, attribute, 89);
// msg 89 function
@ -6551,6 +6575,7 @@ bool get_package(BurpGlobals* tdgbl)
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SQL_SECURITY.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
@ -6594,6 +6619,16 @@ bool get_package(BurpGlobals* tdgbl)
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_package_sql_security:
if (tdgbl->RESTORE_format >= 11)
{
X.RDB$SQL_SECURITY = get_boolean(tdgbl);
X.RDB$SQL_SECURITY.NULL = FALSE;
}
else
bad_attribute(scan_next_attr, attribute, 338);
break;
default:
bad_attribute(scan_next_attr, attribute, 338); // msg 338 package
break;
@ -6656,6 +6691,7 @@ bool get_procedure(BurpGlobals* tdgbl)
X.RDB$ENTRYPOINT.NULL = TRUE;
X.RDB$PACKAGE_NAME.NULL = TRUE;
X.RDB$PRIVATE_FLAG.NULL = TRUE;
X.RDB$SQL_SECURITY.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
@ -6800,6 +6836,16 @@ bool get_procedure(BurpGlobals* tdgbl)
bad_attribute(scan_next_attr, attribute, 290);
break;
case att_procedure_sql_security:
if (tdgbl->RESTORE_format >= 11)
{
X.RDB$SQL_SECURITY = get_boolean(tdgbl);
X.RDB$SQL_SECURITY.NULL = FALSE;
}
else
bad_attribute(scan_next_attr, attribute, 290);
break;
default:
bad_attribute(scan_next_attr, attribute, 290);
// msg 290 procedure
@ -6915,6 +6961,13 @@ bool get_procedure(BurpGlobals* tdgbl)
bad_attribute(scan_next_attr, attribute, 290);
break;
case att_procedure_sql_security:
if (tdgbl->RESTORE_format >= 11)
get_boolean(tdgbl);
else
bad_attribute(scan_next_attr, attribute, 290);
break;
default:
bad_attribute(scan_next_attr, attribute, 290);
// msg 290 procedure
@ -7280,6 +7333,8 @@ bool get_relation(BurpGlobals* tdgbl)
rel_desc = isc_blob_null, ext_desc = isc_blob_null;
bool view_blr_null = true, view_src_null = true, rel_desc_null = true,
ext_desc_null = true;
FB_BOOLEAN sql_security = 0;
bool sql_security_null = true;
BASED_ON RDB$RELATIONS.RDB$SECURITY_CLASS sec_class;
sec_class[0] = '\0';
@ -7399,6 +7454,11 @@ bool get_relation(BurpGlobals* tdgbl)
bad_attribute(scan_next_attr, attribute, 111);
break;
case att_relation_sql_security:
sql_security_null = false;
sql_security = get_boolean(tdgbl);
break;
default:
bad_attribute(scan_next_attr, attribute, 111);
// msg 111 table
@ -7429,6 +7489,7 @@ bool get_relation(BurpGlobals* tdgbl)
X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null;
X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null;
X.RDB$RELATION_TYPE.NULL = type_null;
X.RDB$SQL_SECURITY.NULL = sql_security_null;
X.RDB$SYSTEM_FLAG = (USHORT) sys_flag;
X.RDB$FLAGS = (USHORT) rel_flags;
@ -7442,6 +7503,7 @@ bool get_relation(BurpGlobals* tdgbl)
strcpy(X.RDB$EXTERNAL_FILE, ext_file_name);
X.RDB$RELATION_TYPE = (USHORT) type;
X.RDB$SQL_SECURITY = (FB_BOOLEAN) sql_security;
END_STORE;
ON_ERROR
@ -8482,6 +8544,7 @@ bool get_trigger(BurpGlobals* tdgbl)
X.RDB$DEBUG_INFO.NULL = TRUE;
X.RDB$ENGINE_NAME.NULL = TRUE;
X.RDB$ENTRYPOINT.NULL = TRUE;
X.RDB$SQL_SECURITY.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
@ -8594,6 +8657,16 @@ bool get_trigger(BurpGlobals* tdgbl)
bad_attribute(scan_next_attr, attribute, 134);
break;
case att_trig_sql_security:
if (tdgbl->RESTORE_format >= 11)
{
X.RDB$SQL_SECURITY = get_boolean(tdgbl);
X.RDB$SQL_SECURITY.NULL = FALSE;
}
else
bad_attribute(scan_next_attr, attribute, 134);
break;
default:
bad_attribute(scan_next_attr, attribute, 134);
// msg 134 trigger
@ -8712,6 +8785,13 @@ bool get_trigger(BurpGlobals* tdgbl)
bad_attribute(scan_next_attr, attribute, 134);
break;
case att_trig_sql_security:
if (tdgbl->RESTORE_format >= 11)
get_boolean(tdgbl);
else
bad_attribute(scan_next_attr, attribute, 134);
break;
default:
bad_attribute(scan_next_attr, attribute, 134);
// msg 134 trigger

View File

@ -1612,6 +1612,14 @@ DdlNode* CreateAlterFunctionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
if (returnType && returnType->type)
returnType->type->resolve(dsqlScratch);
// check SQL SECURITY is not set if function declared in package
if (package.hasData() && ssDefiner.specified)
{
// parameter without default value after parameters with default
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_invalid_clause) << Arg::Str("SQL SECURITY for functions is prohibit in packages"));
}
return DdlNode::dsqlPass(dsqlScratch);
}
@ -1803,6 +1811,14 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
FUN.RDB$DETERMINISTIC_FLAG.NULL = FALSE;
FUN.RDB$DETERMINISTIC_FLAG = deterministic ? TRUE : FALSE;
if (ssDefiner.specified)
{
FUN.RDB$SQL_SECURITY.NULL = FALSE;
FUN.RDB$SQL_SECURITY = ssDefiner.value ? 1 : 0;
}
else
FUN.RDB$SQL_SECURITY.NULL = TRUE;
if (isUdf())
{
dsql_fld* field = returnType ? returnType->type : NULL;
@ -2601,6 +2617,14 @@ DdlNode* CreateAlterProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
for (unsigned i = 0; i < returns.getCount(); ++i)
returns[i]->type->resolve(dsqlScratch);
// check SQL SECURITY is not set if function declared in package
if (package.hasData() && ssDefiner.specified)
{
// parameter without default value after parameters with default
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_invalid_clause) << Arg::Str("SQL SECURITY for procedures is prohibit in packages"));
}
return DdlNode::dsqlPass(dsqlScratch);
}
@ -2790,6 +2814,14 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch
}
else
P.RDB$PRIVATE_FLAG.NULL = TRUE;
if (ssDefiner.specified)
{
P.RDB$SQL_SECURITY.NULL = FALSE;
P.RDB$SQL_SECURITY = ssDefiner.value ? 1 : 0;
}
else
P.RDB$SQL_SECURITY.NULL = TRUE;
}
if (external)
@ -3403,6 +3435,17 @@ bool TriggerDefinition::modify(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
attachment->storeMetaDataBlob(tdbb, transaction, &TRG.RDB$TRIGGER_SOURCE, source);
}
if (ssDefiner.specified)
{
if (ssDefiner.value != SS_DROP)
{
TRG.RDB$SQL_SECURITY.NULL = FALSE;
TRG.RDB$SQL_SECURITY = ssDefiner.value == SS_DEFINER ? 1 : 0;
}
else
TRG.RDB$SQL_SECURITY.NULL = TRUE;
}
modified = true;
END_MODIFY
}
@ -3451,6 +3494,9 @@ DdlNode* CreateAlterTriggerNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
}
}
if (create && ssDefiner.specified && ssDefiner.value == TriggerDefinition::SS_DROP)
status_exception::raise(Arg::Gds(isc_dsql_command_err) << Arg::Gds(isc_dsql_invalid_drop_ss_clause));
return DdlNode::dsqlPass(dsqlScratch);
}
@ -7207,6 +7253,14 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat
REL.RDB$FLAGS = REL_sql;
REL.RDB$RELATION_TYPE = relationType;
if (ssDefiner.specified)
{
REL.RDB$SQL_SECURITY.NULL = FALSE;
REL.RDB$SQL_SECURITY = ssDefiner.value ? 1 : 0;
}
else
REL.RDB$SQL_SECURITY.NULL = TRUE;
REL.RDB$VIEW_BLR.NULL = TRUE;
REL.RDB$VIEW_SOURCE.NULL = TRUE;
REL.RDB$EXTERNAL_FILE.NULL = TRUE;
@ -7528,6 +7582,36 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
break;
}
case Clause::TYPE_ALTER_SQL_SECURITY:
{
AutoRequest request;
bool found = false;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
REL IN RDB$RELATIONS
WITH REL.RDB$RELATION_NAME EQ name.c_str()
{
found = true;
MODIFY REL
{
const Nullable<bool> ssDefiner = static_cast<const AlterSqlSecurityClause*>(
i->getObject())->ssDefiner;
if (ssDefiner.specified)
{
REL.RDB$SQL_SECURITY.NULL = FALSE;
REL.RDB$SQL_SECURITY = ssDefiner.value ? TRUE : FALSE;
}
else
REL.RDB$SQL_SECURITY.NULL = TRUE;
}
END_MODIFY
}
END_FOR
break;
}
default:
fb_assert(false);
break;

View File

@ -409,6 +409,7 @@ public:
bool privateScope;
bool preserveDefaults;
SLONG udfReturnPos;
Nullable<bool> ssDefiner;
};
@ -542,6 +543,7 @@ public:
Firebird::MetaName packageOwner;
bool privateScope;
bool preserveDefaults;
Nullable<bool> ssDefiner;
};
@ -586,6 +588,13 @@ typedef RecreateNode<CreateAlterProcedureNode, DropProcedureNode, isc_dsql_recre
class TriggerDefinition
{
public:
enum SqlSecurity
{
SS_INVOKER,
SS_DEFINER,
SS_DROP
};
explicit TriggerDefinition(MemoryPool& p)
: name(p),
relationName(p),
@ -626,6 +635,7 @@ public:
Firebird::ByteChunk debugData;
USHORT systemFlag;
bool fkTrigger;
Nullable<SqlSecurity> ssDefiner;
};
@ -1230,7 +1240,8 @@ public:
TYPE_ALTER_COL_POS,
TYPE_ALTER_COL_TYPE,
TYPE_DROP_COLUMN,
TYPE_DROP_CONSTRAINT
TYPE_DROP_CONSTRAINT,
TYPE_ALTER_SQL_SECURITY
};
explicit Clause(MemoryPool& p, Type aType)
@ -1401,6 +1412,16 @@ public:
Firebird::MetaName name;
};
struct AlterSqlSecurityClause : public Clause
{
explicit AlterSqlSecurityClause(MemoryPool& p)
: Clause(p, TYPE_ALTER_SQL_SECURITY)
{
}
Nullable<bool> ssDefiner;
};
RelationNode(MemoryPool& p, RelationSourceNode* aDsqlNode);
static void deleteLocalField(thread_db* tdbb, jrd_tra* transaction,
@ -1448,6 +1469,7 @@ public:
NestConst<RelationSourceNode> dsqlNode;
Firebird::MetaName name;
Firebird::Array<NestConst<Clause> > clauses;
Nullable<bool> ssDefiner;
};
@ -2299,4 +2321,14 @@ public:
} // namespace
template <>
class NullableClear<Jrd::TriggerDefinition::SqlSecurity> // TriggerDefinition::SqlSecurity especialization for NullableClear
{
public:
static void clear(Jrd::TriggerDefinition::SqlSecurity& v)
{
v = Jrd::TriggerDefinition::SS_INVOKER;
}
};
#endif // DSQL_DDL_NODES_H

View File

@ -5481,38 +5481,41 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb)
// the nodes in the subtree are involved in a validation
// clause only, the subtree is a validate_subtree in our notation.
const SLONG viewId = tail->csb_view ?
SLONG ssRelationId = tail->csb_view ?
tail->csb_view->rel_id : (csb->csb_view ? csb->csb_view->rel_id : 0);
if (!ssRelationId && relation->rel_ss_definer.value)
ssRelationId = relation->rel_id;
if (tail->csb_flags & csb_modify)
{
if (!csb->csb_validate_expr)
{
SecurityClass::flags_t priv = csb->csb_returning_expr ?
SCL_select : SCL_update;
CMP_post_access(tdbb, csb, relation->rel_security_name, viewId,
CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId,
priv, SCL_object_table, relation->rel_name);
CMP_post_access(tdbb, csb, field->fld_security_name, viewId,
CMP_post_access(tdbb, csb, field->fld_security_name, ssRelationId,
priv, SCL_object_column, field->fld_name, relation->rel_name);
}
}
else if (tail->csb_flags & csb_erase)
{
CMP_post_access(tdbb, csb, relation->rel_security_name, viewId,
CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId,
SCL_delete, SCL_object_table, relation->rel_name);
}
else if (tail->csb_flags & csb_store)
{
CMP_post_access(tdbb, csb, relation->rel_security_name, viewId,
CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId,
SCL_insert, SCL_object_table, relation->rel_name);
CMP_post_access(tdbb, csb, field->fld_security_name, viewId,
CMP_post_access(tdbb, csb, field->fld_security_name, ssRelationId,
SCL_insert, SCL_object_column, field->fld_name, relation->rel_name);
}
else
{
CMP_post_access(tdbb, csb, relation->rel_security_name, viewId,
CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId,
SCL_select, SCL_object_table, relation->rel_name);
CMP_post_access(tdbb, csb, field->fld_security_name, viewId,
CMP_post_access(tdbb, csb, field->fld_security_name, ssRelationId,
SCL_select, SCL_object_column, field->fld_name, relation->rel_name);
}
@ -5578,6 +5581,8 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb)
sub = cast;
}
AutoSetRestore<jrd_rel*> autoRelationStream(&csb->csb_parent_relation, relation->rel_ss_definer.value ? relation : 0);
if (relation->rel_view_rse)
{
// dimitr: if we reference view columns, we need to pass them
@ -11191,8 +11196,15 @@ ValueExprNode* UdfCallNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
if (function->getName().package.isEmpty())
{
CMP_post_access(tdbb, csb, function->getSecurityName(),
(csb->csb_view ? csb->csb_view->rel_id : 0),
SLONG ssRelationId = csb->csb_view ? csb->csb_view->rel_id : 0;
if (!ssRelationId && csb->csb_parent_relation)
{
fb_assert(csb->csb_parent_relation->rel_ss_definer.value);
ssRelationId = csb->csb_parent_relation->rel_id;
}
CMP_post_access(tdbb, csb, function->getSecurityName(), ssRelationId,
SCL_execute, SCL_object_function, function->getName().identifier);
}
else

View File

@ -512,6 +512,14 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch*
PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source);
if (ssDefiner.specified)
{
PKG.RDB$SQL_SECURITY.NULL = FALSE;
PKG.RDB$SQL_SECURITY = ssDefiner.value ? 1 : 0;
}
else
PKG.RDB$SQL_SECURITY.NULL = TRUE;
}
END_STORE
@ -577,6 +585,15 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
if (!PKG.RDB$VALID_BODY_FLAG.NULL)
PKG.RDB$VALID_BODY_FLAG = FALSE;
if (ssDefiner.specified)
{
PKG.RDB$SQL_SECURITY.NULL = FALSE;
PKG.RDB$SQL_SECURITY = ssDefiner.value ? 1 : 0;
}
else
PKG.RDB$SQL_SECURITY.NULL = TRUE;
END_MODIFY
owner = PKG.RDB$OWNER_NAME;

View File

@ -110,6 +110,7 @@ public:
Firebird::Array<Item>* items;
Firebird::SortedArray<Firebird::MetaName> functionNames;
Firebird::SortedArray<Firebird::MetaName> procedureNames;
Nullable<bool> ssDefiner;
private:
Firebird::MetaName owner;

View File

@ -607,6 +607,10 @@ using namespace Firebird;
%token <metaNamePtr> TIES
%token <metaNamePtr> UNBOUNDED
%token <metaNamePtr> WINDOW
%token <metaNamePtr> SQL
%token <metaNamePtr> SECURITY
%token <metaNamePtr> INVOKER
%token <metaNamePtr> DEFINER
// precedence declarations for expression evaluation
@ -638,6 +642,7 @@ using namespace Firebird;
{
BaseNullable<int> nullableIntVal;
BaseNullable<bool> nullableBoolVal;
BaseNullable<Jrd::TriggerDefinition::SqlSecurity> nullableSqlSecurityVal;
bool boolVal;
int intVal;
unsigned uintVal;
@ -2057,22 +2062,38 @@ page_noise
%type <createRelationNode> table_clause
table_clause
: simple_table_name external_file
{ $<createRelationNode>$ = newNode<CreateRelationNode>($1, $2); }
'(' table_elements($3) ')'
{ $$ = $3; }
{
$<createRelationNode>$ = newNode<CreateRelationNode>($1, $2);
}
'(' table_elements($3) ')' sql_security_clause
{
$$ = $3;
$$->ssDefiner = $7;
}
;
%type <createRelationNode> gtt_table_clause
gtt_table_clause
: simple_table_name
{ $<createRelationNode>$ = newNode<CreateRelationNode>($1); }
'(' table_elements($2) ')' gtt_scope
'(' table_elements($2) ')' gtt_ops($2)
{
$$ = $2;
$$->relationType = static_cast<rel_t>($6);
}
;
%type gtt_ops(<createRelationNode>)
gtt_ops($createRelationNode)
: gtt_op($createRelationNode)
| gtt_ops ',' gtt_op($createRelationNode)
;
%type gtt_op(<createRelationNode>)
gtt_op($createRelationNode)
: sql_security_clause { $createRelationNode->ssDefiner = $1; }
| gtt_scope { $createRelationNode->relationType = static_cast<rel_t>($1); }
;
%type <intVal> gtt_scope
gtt_scope
: /* nothing */ { $$ = rel_global_temp_delete; }
@ -2099,7 +2120,6 @@ table_element($createRelationNode)
| table_constraint_definition($createRelationNode)
;
// column definition
%type column_def(<relationNode>)
@ -2457,12 +2477,13 @@ procedure_clause
%type <createAlterProcedureNode> psql_procedure_clause
psql_procedure_clause
: procedure_clause_start AS local_declaration_list full_proc_block
: procedure_clause_start sql_security_clause AS local_declaration_list full_proc_block
{
$$ = $1;
$$->source = makeParseStr(YYPOSNARG(3), YYPOSNARG(4));
$$->localDeclList = $3;
$$->body = $4;
$$->ssDefiner = $2;
$$->source = makeParseStr(YYPOSNARG(4), YYPOSNARG(5));
$$->localDeclList = $4;
$$->body = $5;
}
;
@ -2480,11 +2501,23 @@ external_procedure_clause
%type <createAlterProcedureNode> procedure_clause_start
procedure_clause_start
: symbol_procedure_name
{ $$ = newNode<CreateAlterProcedureNode>(*$1); }
input_parameters(NOTRIAL(&$2->parameters)) output_parameters(NOTRIAL(&$2->returns))
{
$$ = newNode<CreateAlterProcedureNode>(*$1);
}
input_parameters(NOTRIAL(&$2->parameters)) output_parameters(NOTRIAL(&$2->returns))
{ $$ = $2; }
;
%type <nullableBoolVal> sql_security_clause
sql_security_clause
: SQL SECURITY DEFINER
{ $$ = Nullable<bool>::val(true); }
| SQL SECURITY INVOKER
{ $$ = Nullable<bool>::val(false); }
| // nothing
{ $$ = Nullable<bool>::empty(); }
;
%type <createAlterProcedureNode> alter_procedure_clause
alter_procedure_clause
: procedure_clause
@ -2580,12 +2613,13 @@ function_clause
%type <createAlterFunctionNode> psql_function_clause
psql_function_clause
: function_clause_start AS local_declaration_list full_proc_block
: function_clause_start sql_security_clause AS local_declaration_list full_proc_block
{
$$ = $1;
$$->source = makeParseStr(YYPOSNARG(3), YYPOSNARG(4));
$$->localDeclList = $3;
$$->body = $4;
$$->ssDefiner = $2;
$$->source = makeParseStr(YYPOSNARG(4), YYPOSNARG(5));
$$->localDeclList = $4;
$$->body = $5;
}
;
@ -2603,7 +2637,9 @@ external_function_clause
%type <createAlterFunctionNode> function_clause_start
function_clause_start
: symbol_UDF_name
{ $$ = newNode<CreateAlterFunctionNode>(*$1); }
{
$$ = newNode<CreateAlterFunctionNode>(*$1);
}
input_parameters(NOTRIAL(&$2->parameters))
RETURNS domain_or_non_array_type collate_clause deterministic_opt
{
@ -2665,11 +2701,12 @@ replace_function_clause
%type <createAlterPackageNode> package_clause
package_clause
: symbol_package_name AS BEGIN package_items_opt END
: symbol_package_name sql_security_clause AS BEGIN package_items_opt END
{
CreateAlterPackageNode* node = newNode<CreateAlterPackageNode>(*$1);
node->source = makeParseStr(YYPOSNARG(3), YYPOSNARG(5));
node->items = $4;
node->ssDefiner = $2;
node->source = makeParseStr(YYPOSNARG(4), YYPOSNARG(6));
node->items = $5;
$$ = node;
}
;
@ -3505,16 +3542,17 @@ check_opt
%type <createAlterTriggerNode> trigger_clause
trigger_clause
: symbol_trigger_name trigger_active trigger_type trigger_position
: symbol_trigger_name trigger_active trigger_type trigger_position trg_sql_security_clause
AS local_declaration_list full_proc_block
{
$$ = newNode<CreateAlterTriggerNode>(*$1);
$$->active = $2;
$$->type = $3;
$$->position = $4;
$$->source = makeParseStr(YYPOSNARG(5), YYPOSNARG(7));
$$->localDeclList = $6;
$$->body = $7;
$$->ssDefiner = $5;
$$->source = makeParseStr(YYPOSNARG(6), YYPOSNARG(8));
$$->localDeclList = $7;
$$->body = $8;
}
| symbol_trigger_name trigger_active trigger_type trigger_position
external_clause external_body_clause_opt
@ -3527,7 +3565,7 @@ trigger_clause
if ($6)
$$->source = *$6;
}
| symbol_trigger_name trigger_active trigger_type trigger_position ON symbol_table_name
| symbol_trigger_name trigger_active trigger_type trigger_position ON symbol_table_name trg_sql_security_clause
AS local_declaration_list full_proc_block
{
$$ = newNode<CreateAlterTriggerNode>(*$1);
@ -3535,9 +3573,10 @@ trigger_clause
$$->type = $3;
$$->position = $4;
$$->relationName = *$6;
$$->source = makeParseStr(YYPOSNARG(7), YYPOSNARG(9));
$$->localDeclList = $8;
$$->body = $9;
$$->ssDefiner = $7;
$$->source = makeParseStr(YYPOSNARG(8), YYPOSNARG(10));
$$->localDeclList = $9;
$$->body = $10;
}
| symbol_trigger_name trigger_active trigger_type trigger_position ON symbol_table_name
external_clause external_body_clause_opt
@ -3551,7 +3590,7 @@ trigger_clause
if ($8)
$$->source = *$8;
}
| symbol_trigger_name FOR symbol_table_name trigger_active trigger_type trigger_position
| symbol_trigger_name FOR symbol_table_name trigger_active trigger_type trigger_position trg_sql_security_clause
AS local_declaration_list full_proc_block
{
$$ = newNode<CreateAlterTriggerNode>(*$1);
@ -3559,9 +3598,10 @@ trigger_clause
$$->type = $5;
$$->position = $6;
$$->relationName = *$3;
$$->source = makeParseStr(YYPOSNARG(7), YYPOSNARG(9));
$$->localDeclList = $8;
$$->body = $9;
$$->ssDefiner = $7;
$$->source = makeParseStr(YYPOSNARG(8), YYPOSNARG(10));
$$->localDeclList = $9;
$$->body = $10;
}
| symbol_trigger_name FOR symbol_table_name trigger_active trigger_type trigger_position
external_clause external_body_clause_opt
@ -3882,6 +3922,27 @@ alter_op($relationNode)
clause->identityRestartValue = $4;
$relationNode->clauses.add(clause);
}
| ALTER SQL SECURITY DEFINER
{
RelationNode::AlterSqlSecurityClause* clause =
newNode<RelationNode::AlterSqlSecurityClause>();
clause->ssDefiner = Nullable<bool>::val(true);
$relationNode->clauses.add(clause);
}
| ALTER SQL SECURITY INVOKER
{
RelationNode::AlterSqlSecurityClause* clause =
newNode<RelationNode::AlterSqlSecurityClause>();
clause->ssDefiner = Nullable<bool>::val(false);
$relationNode->clauses.add(clause);
}
| DROP SQL SECURITY
{
RelationNode::AlterSqlSecurityClause* clause =
newNode<RelationNode::AlterSqlSecurityClause>();
clause->ssDefiner = Nullable<bool>::empty();
$relationNode->clauses.add(clause);
}
;
%type <metaNamePtr> alter_column_name
@ -4105,7 +4166,7 @@ crypt_key_clause($alterDatabaseNode)
%type <createAlterTriggerNode> alter_trigger_clause
alter_trigger_clause
: symbol_trigger_name trigger_active trigger_type_opt trigger_position
: symbol_trigger_name trigger_active trigger_type_opt trigger_position trg_sql_security_clause
AS local_declaration_list full_proc_block
{
$$ = newNode<CreateAlterTriggerNode>(*$1);
@ -4114,9 +4175,10 @@ alter_trigger_clause
$$->active = $2;
$$->type = $3;
$$->position = $4;
$$->source = makeParseStr(YYPOSNARG(5), YYPOSNARG(7));
$$->localDeclList = $6;
$$->body = $7;
$$->ssDefiner = $5;
$$->source = makeParseStr(YYPOSNARG(6), YYPOSNARG(8));
$$->localDeclList = $7;
$$->body = $8;
}
| symbol_trigger_name trigger_active trigger_type_opt trigger_position
external_clause external_body_clause_opt
@ -4131,7 +4193,7 @@ alter_trigger_clause
if ($6)
$$->source = *$6;
}
| symbol_trigger_name trigger_active trigger_type_opt trigger_position
| symbol_trigger_name trigger_active trigger_type_opt trigger_position trg_sql_security_clause
{
$$ = newNode<CreateAlterTriggerNode>(*$1);
$$->alter = true;
@ -4139,6 +4201,7 @@ alter_trigger_clause
$$->active = $2;
$$->type = $3;
$$->position = $4;
$$->ssDefiner = $5;
}
;
@ -4150,6 +4213,17 @@ trigger_type_opt // we do not allow alter database triggers, hence we do not use
{ $$ = Nullable<FB_UINT64>::empty(); }
;
%type <nullableSqlSecurityVal> trg_sql_security_clause
trg_sql_security_clause
: SQL SECURITY DEFINER
{ $$ = Nullable<TriggerDefinition::SqlSecurity>::val(TriggerDefinition::SS_DEFINER); }
| SQL SECURITY INVOKER
{ $$ = Nullable<TriggerDefinition::SqlSecurity>::val(TriggerDefinition::SS_INVOKER); }
| DROP SQL SECURITY
{ $$ = Nullable<TriggerDefinition::SqlSecurity>::val(TriggerDefinition::SS_DROP); }
| // nothing
{ $$ = Nullable<TriggerDefinition::SqlSecurity>::empty(); }
;
// DROP metadata operations
@ -8029,7 +8103,11 @@ non_reserved_word
| SYSTEM
| ERROR_MESSAGE
| TIES
;
| SQL
| SECURITY
| INVOKER
| DEFINER
;
%%

View File

@ -892,6 +892,7 @@ static const struct {
{"dsql_no_input_sqlda", 336003109},
{"dsql_no_output_sqlda", 336003110},
{"dsql_wrong_param_num", 336003111},
{"dsql_invalid_drop_ss_clause", 336003112},
{"dyn_filter_not_found", 336068645},
{"dyn_func_not_found", 336068649},
{"dyn_index_not_found", 336068656},

View File

@ -926,6 +926,7 @@ const ISC_STATUS isc_dsql_no_sqldata = 336003108L;
const ISC_STATUS isc_dsql_no_input_sqlda = 336003109L;
const ISC_STATUS isc_dsql_no_output_sqlda = 336003110L;
const ISC_STATUS isc_dsql_wrong_param_num = 336003111L;
const ISC_STATUS isc_dsql_invalid_drop_ss_clause = 336003112L;
const ISC_STATUS isc_dyn_filter_not_found = 336068645L;
const ISC_STATUS isc_dyn_func_not_found = 336068649L;
const ISC_STATUS isc_dyn_index_not_found = 336068656L;
@ -1332,7 +1333,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
const ISC_STATUS isc_err_max = 1276;
const ISC_STATUS isc_err_max = 1277;
#else /* c definitions */
@ -2228,6 +2229,7 @@ const ISC_STATUS isc_err_max = 1276;
#define isc_dsql_no_input_sqlda 336003109L
#define isc_dsql_no_output_sqlda 336003110L
#define isc_dsql_wrong_param_num 336003111L
#define isc_dsql_invalid_drop_ss_clause 336003112L
#define isc_dyn_filter_not_found 336068645L
#define isc_dyn_func_not_found 336068649L
#define isc_dyn_index_not_found 336068656L
@ -2634,7 +2636,7 @@ const ISC_STATUS isc_err_max = 1276;
#define isc_trace_switch_param_miss 337182758L
#define isc_trace_param_act_notcompat 337182759L
#define isc_trace_mandatory_switch_miss 337182760L
#define isc_err_max 1276
#define isc_err_max 1277
#endif

View File

@ -145,6 +145,7 @@
const USHORT f_rel_def_class = 14;
const USHORT f_rel_flags = 15;
const USHORT f_rel_type = 16;
const USHORT f_rel_sql_security = 17;
// Relation 7 (RDB$VIEW_RELATIONS)
@ -206,6 +207,7 @@
const USHORT f_trg_debug_info = 11;
const USHORT f_trg_engine_name = 12;
const USHORT f_trg_entry = 13;
const USHORT f_trg_sql_security = 14;
// Relation 13 (RDB$DEPENDENCIES)
@ -240,6 +242,7 @@
const USHORT f_fun_owner = 17;
const USHORT f_fun_legacy_flag = 18;
const USHORT f_fun_deterministic_flag = 19;
const USHORT f_fun_sql_security = 20;
// Relation 15 (RDB$FUNCTION_ARGUMENTS)
@ -383,6 +386,7 @@
const USHORT f_prc_entry = 15;
const USHORT f_prc_pkg_name = 16;
const USHORT f_prc_private_flag = 17;
const USHORT f_prc_sql_security = 18;
// Relation 27 (RDB$PROCEDURE_PARAMETERS)
@ -616,6 +620,7 @@
const USHORT f_pkg_owner = 5;
const USHORT f_pkg_sys_flag = 6;
const USHORT f_pkg_desc = 7;
const USHORT f_pkg_sql_security = 8;
// Relation 43 (SEC$USERS)

View File

@ -895,6 +895,7 @@ Data source : @4"}, /* eds_statement */
{336003109, "No SQLDA for input values provided"}, /* dsql_no_input_sqlda */
{336003110, "No SQLDA for output values provided"}, /* dsql_no_output_sqlda */
{336003111, "Wrong number of parameters (expected @1, got @2)"}, /* dsql_wrong_param_num */
{336003112, "Invalid DROP SQL SECURITY clause"}, /* dsql_invalid_drop_ss_clause */
{336068645, "BLOB Filter @1 not found"}, /* dyn_filter_not_found */
{336068649, "Function @1 not found"}, /* dyn_func_not_found */
{336068656, "Index not found"}, /* dyn_index_not_found */

View File

@ -891,6 +891,7 @@ static const struct {
{336003109, -802}, /* 37 dsql_no_input_sqlda */
{336003110, -802}, /* 38 dsql_no_output_sqlda */
{336003111, -313}, /* 39 dsql_wrong_param_num */
{336003112, -817}, /* 40 dsql_invalid_drop_ss_clause */
{336068645, -901}, /* 37 dyn_filter_not_found */
{336068649, -901}, /* 41 dyn_func_not_found */
{336068656, -901}, /* 48 dyn_index_not_found */

View File

@ -891,6 +891,7 @@ static const struct {
{336003109, "07002"}, // 37 dsql_no_input_sqlda
{336003110, "07002"}, // 38 dsql_no_output_sqlda
{336003111, "07001"}, // 39 dsql_wrong_param_num
{336003112, "42000"}, // 40 dsql_invalid_drop_ss_clause
{336068645, "42000"}, // 37 dyn_filter_not_found
{336068649, "42000"}, // 41 dyn_func_not_found
{336068656, "42000"}, // 48 dyn_index_not_found

View File

@ -303,6 +303,7 @@ int EXTRACT_list_table(const SCHAR* relation_name,
bool first = true;
SCHAR char_sets[86];
rel_t rel_type = rel_persistent;
char ss[28] = "";
// Query to obtain relation detail information
@ -350,6 +351,12 @@ int EXTRACT_list_table(const SCHAR* relation_name,
else
isqlGlob.printf("%s ", new_name ? new_name : relation_name);
if (!REL.RDB$SQL_SECURITY.NULL)
if (REL.RDB$SQL_SECURITY)
strcpy(ss, "SQL SECURITY DEFINER");
else
strcpy(ss, "SQL SECURITY INVOKER");
if (!REL.RDB$EXTERNAL_FILE.NULL)
{
IUTILS_copy_SQL_id (REL.RDB$EXTERNAL_FILE, SQL_identifier2, SINGLE_QUOTE);
@ -555,10 +562,11 @@ int EXTRACT_list_table(const SCHAR* relation_name,
if (first) // we extracted nothing
return FINI_ERROR;
if (rel_type == rel_global_temp_preserve)
isqlGlob.printf(")%sON COMMIT PRESERVE ROWS", NEWLINE);
else if (rel_type == rel_global_temp_delete)
isqlGlob.printf(")%sON COMMIT DELETE ROWS", NEWLINE);
const char* gtt_scope = (rel_type == rel_global_temp_preserve) ? "ON COMMIT PRESERVE ROWS" :
((rel_type == rel_global_temp_delete) ? "ON COMMIT DELETE ROWS" : "");
if (*gtt_scope || *ss)
isqlGlob.printf(")%s%s%s", NEWLINE, gtt_scope, ss);
else
isqlGlob.printf(")");
@ -1532,6 +1540,12 @@ static void list_procedure_bodies()
isqlGlob.printf("EXTERNAL NAME %s%s", SQL_identifier2, NEWLINE);
}
if (!PRC.RDB$SQL_SECURITY.NULL)
{
const char* ss = PRC.RDB$SQL_SECURITY ? "SQL SECURITY DEFINER" : "SQL SECURITY INVOKER";
isqlGlob.printf("%s%s", ss, NEWLINE);
}
if (!PRC.RDB$ENGINE_NAME.NULL)
{
fb_utils::exact_name(PRC.RDB$ENGINE_NAME);
@ -1658,6 +1672,12 @@ static void list_all_triggers()
SHOW_trigger_action(TRG.RDB$TRIGGER_TYPE).c_str(), TRG.RDB$TRIGGER_SEQUENCE,
NEWLINE);
if (!TRG.RDB$SQL_SECURITY.NULL)
{
const char* ss = TRG.RDB$SQL_SECURITY ? "SQL SECURITY DEFINER" : "SQL SECURITY INVOKER";
isqlGlob.printf("%s%s", ss, NEWLINE);
}
if (!TRG.RDB$ENTRYPOINT.NULL)
{
fb_utils::exact_name(TRG.RDB$ENTRYPOINT);
@ -1737,6 +1757,12 @@ static void list_all_triggers()
SHOW_trigger_action(TRG.RDB$TRIGGER_TYPE).c_str(), TRG.RDB$TRIGGER_SEQUENCE,
NEWLINE);
if (!TRG.RDB$SQL_SECURITY.NULL)
{
const char* ss = TRG.RDB$SQL_SECURITY ? "SQL SECURITY DEFINER" : "SQL SECURITY INVOKER";
isqlGlob.printf("%s%s", ss, NEWLINE);
}
if (!TRG.RDB$ENTRYPOINT.NULL)
{
fb_utils::exact_name(TRG.RDB$ENTRYPOINT);
@ -3089,6 +3115,12 @@ static void list_functions_ods12_bodies()
isqlGlob.printf("EXTERNAL NAME %s%s", SQL_identifier2, NEWLINE);
}
if (!FUN.RDB$SQL_SECURITY.NULL)
{
const char* ss = FUN.RDB$SQL_SECURITY ? "SQL SECURITY DEFINER" : "SQL SECURITY INVOKER";
isqlGlob.printf("%s%s", ss, NEWLINE);
}
if (!FUN.RDB$ENGINE_NAME.NULL)
{
fb_utils::exact_name(FUN.RDB$ENGINE_NAME);
@ -3372,7 +3404,11 @@ static void list_package_headers()
PACK.RDB$PACKAGE_NAME,
PACK.RDB$OWNER_NAME,
NEWLINE);
isqlGlob.printf("CREATE PACKAGE %s AS%s", SQL_identifier, NEWLINE);
const char* ss = PACK.RDB$SQL_SECURITY.NULL ? "" :
(PACK.RDB$SQL_SECURITY ? " SQL SECURITY DEFINER" : " SQL SECURITY INVOKER");
isqlGlob.printf("CREATE PACKAGE %s%s AS%s", SQL_identifier, ss, NEWLINE);
if (!PACK.RDB$PACKAGE_HEADER_SOURCE.NULL)
SHOW_print_metadata_text_blob(isqlGlob.Out, &PACK.RDB$PACKAGE_HEADER_SOURCE);

View File

@ -4265,6 +4265,12 @@ static processing_state show_func(const SCHAR* funcname)
isqlGlob.printf("External name: %s%s", FUN.RDB$ENTRYPOINT, NEWLINE);
}
if (!FUN.RDB$SQL_SECURITY.NULL)
{
const char* ss = FUN.RDB$SQL_SECURITY ? "DEFINER" : "INVOKER";
isqlGlob.printf("SQL SECURITY: %s%s", ss, NEWLINE);
}
if (!FUN.RDB$ENGINE_NAME.NULL)
{
fb_utils::exact_name(FUN.RDB$ENGINE_NAME);
@ -4830,6 +4836,12 @@ static processing_state show_packages(const SCHAR* package_name)
isqlGlob.printf(NEWLINE);
if (!PACK.RDB$SQL_SECURITY.NULL)
{
const char* ss = PACK.RDB$SQL_SECURITY ? "DEFINER" : "INVOKER";
isqlGlob.printf("SQL SECURITY: %s%s", ss, NEWLINE);
}
if (!PACK.RDB$PACKAGE_HEADER_SOURCE.NULL)
{
isqlGlob.printf("%s%s", "Header source:", NEWLINE);
@ -5124,6 +5136,12 @@ static processing_state show_proc(const SCHAR* procname)
isqlGlob.printf("External name: %s%s", PRC.RDB$ENTRYPOINT, NEWLINE);
}
if (!PRC.RDB$SQL_SECURITY.NULL)
{
const char* ss = PRC.RDB$SQL_SECURITY ? "DEFINER" : "INVOKER";
isqlGlob.printf("SQL SECURITY: %s%s", ss, NEWLINE);
}
if (!PRC.RDB$ENGINE_NAME.NULL)
{
fb_utils::exact_name(PRC.RDB$ENGINE_NAME);
@ -5682,6 +5700,12 @@ static processing_state show_table(const SCHAR* relation_name, bool isView)
WITH REL.RDB$RELATION_NAME EQ relation_name
if (first)
{
if (!REL.RDB$SQL_SECURITY.NULL)
{
const char* ss = REL.RDB$SQL_SECURITY ? "DEFINER" : "INVOKER";
isqlGlob.printf("SQL SECURITY: %s%s", ss, NEWLINE);
}
if (!REL.RDB$EXTERNAL_FILE.NULL)
isqlGlob.printf("External file: %s%s", REL.RDB$EXTERNAL_FILE, NEWLINE);
}
@ -6117,11 +6141,15 @@ static processing_state show_trigger(const SCHAR* object, bool show_source, bool
first = false;
}
isqlGlob.printf("%s, Sequence: %d, Type: %s, %s%s",
const char* ss = TRG.RDB$SQL_SECURITY.NULL ? "" :
(TRG.RDB$SQL_SECURITY ? ", SQL SECURITY DEFINER" : ", SQL SECURITY INVOKER");
isqlGlob.printf("%s, Sequence: %d, Type: %s, %s%s%s",
TRG.RDB$TRIGGER_NAME,
TRG.RDB$TRIGGER_SEQUENCE,
SHOW_trigger_action(TRG.RDB$TRIGGER_TYPE).c_str(),
(TRG.RDB$TRIGGER_INACTIVE ? "Inactive" : "Active"),
ss,
NEWLINE);
if (show_source)

View File

@ -722,10 +722,11 @@ ExtEngineManager::Function::~Function()
void ExtEngineManager::Function::execute(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
const MetaName& userName = udf->ssDefiner.specified && udf->ssDefiner.value ? udf->owner : "";
ContextManager<IExternalFunction> ctxManager(tdbb, attInfo, function,
(udf->getName().package.isEmpty() ?
CallerName(obj_udf, udf->getName().identifier) :
CallerName(obj_package_header, udf->getName().package)));
CallerName(obj_udf, udf->getName().identifier, userName) :
CallerName(obj_package_header, udf->getName().package, userName)));
EngineCheckout cout(tdbb, FB_FUNCTION);
@ -775,10 +776,11 @@ ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, UCHAR* inMsg, UCHAR* out
firstFetch(true)
{
attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine);
const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? procedure->prc->owner : "";
ContextManager<IExternalProcedure> ctxManager(tdbb, attInfo, procedure->procedure,
(procedure->prc->getName().package.isEmpty() ?
CallerName(obj_procedure, procedure->prc->getName().identifier) :
CallerName(obj_package_header, procedure->prc->getName().package)));
CallerName(obj_procedure, procedure->prc->getName().identifier, userName) :
CallerName(obj_package_header, procedure->prc->getName().package, userName)));
charSet = attachment->att_charset;
@ -808,10 +810,11 @@ bool ExtEngineManager::ResultSet::fetch(thread_db* tdbb)
if (!resultSet)
return wasFirstFetch;
const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? procedure->prc->owner : "";
ContextManager<IExternalProcedure> ctxManager(tdbb, attInfo, charSet,
(procedure->prc->getName().package.isEmpty() ?
CallerName(obj_procedure, procedure->prc->getName().identifier) :
CallerName(obj_package_header, procedure->prc->getName().package)));
CallerName(obj_procedure, procedure->prc->getName().identifier, userName) :
CallerName(obj_package_header, procedure->prc->getName().package, userName)));
EngineCheckout cout(tdbb, FB_FUNCTION);
@ -883,8 +886,10 @@ void ExtEngineManager::Trigger::execute(thread_db* tdbb, unsigned action,
record_param* oldRpb, record_param* newRpb) const
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
const Nullable<bool>& ssDefiner = trg->ssDefiner.specified ? trg->ssDefiner : trg->relation->rel_ss_definer;
const MetaName& userName = ssDefiner.specified && ssDefiner.value ? trg->relation->rel_owner_name : "";
ContextManager<IExternalTrigger> ctxManager(tdbb, attInfo, trigger,
CallerName(obj_trigger, trg->name));
CallerName(obj_trigger, trg->name, userName));
// ASF: Using Array instead of HalfStaticArray to not need to do alignment hacks here.
Array<UCHAR> oldMsg;
@ -1047,10 +1052,11 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd::
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
const MetaName& userName = udf->ssDefiner.specified && udf->ssDefiner.value ? udf->owner : "";
ContextManager<IExternalFunction> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
(udf->getName().package.isEmpty() ?
CallerName(obj_udf, udf->getName().identifier) :
CallerName(obj_package_header, udf->getName().package)));
CallerName(obj_udf, udf->getName().identifier, userName) :
CallerName(obj_package_header, udf->getName().package, userName)));
///MemoryPool& pool = *tdbb->getDefaultPool();
MemoryPool& pool = *getDefaultMemoryPool();
@ -1156,10 +1162,11 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
const MetaName& userName = prc->ssDefiner.specified && prc->ssDefiner.value ? prc->owner : "";
ContextManager<IExternalProcedure> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
(prc->getName().package.isEmpty() ?
CallerName(obj_procedure, prc->getName().identifier) :
CallerName(obj_package_header, prc->getName().package)));
CallerName(obj_procedure, prc->getName().identifier, userName) :
CallerName(obj_package_header, prc->getName().package, userName)));
///MemoryPool& pool = *tdbb->getDefaultPool();
MemoryPool& pool = *getDefaultMemoryPool();
@ -1267,8 +1274,10 @@ void ExtEngineManager::makeTrigger(thread_db* tdbb, CompilerScratch* csb, Jrd::T
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
Nullable<bool>& ssDefiner = trg->ssDefiner.specified ? trg->ssDefiner : trg->relation->rel_ss_definer;
const MetaName& userName = ssDefiner.specified && ssDefiner.value ? trg->relation->rel_owner_name : "";
ContextManager<IExternalTrigger> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
CallerName(obj_trigger, trg->name));
CallerName(obj_trigger, trg->name, userName));
///MemoryPool& pool = *tdbb->getDefaultPool();
MemoryPool& pool = *getDefaultMemoryPool();

View File

@ -220,6 +220,8 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT
function->setName(QualifiedName(X.RDB$FUNCTION_NAME,
(X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME)));
function->owner = X.RDB$OWNER_NAME;
if (!X.RDB$SECURITY_CLASS.NULL)
{
function->setSecurityName(X.RDB$SECURITY_CLASS);
@ -237,9 +239,16 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT
function->setSecurityName(PKG.RDB$SECURITY_CLASS);
}
// SQL SECURITY of function must be the same if it's defined in package
if (!PKG.RDB$SQL_SECURITY.NULL)
function->ssDefiner = Nullable<bool>::val(PKG.RDB$SQL_SECURITY);
END_FOR
}
if (!function->ssDefiner.specified && !X.RDB$SQL_SECURITY.NULL)
function->ssDefiner = Nullable<bool>::val(X.RDB$SQL_SECURITY);
size_t count = 0;
ULONG length = 0;

View File

@ -54,6 +54,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
accessList(*p),
resources(*p),
triggerName(*p),
triggerOwner(*p),
parentStatement(NULL),
subStatements(*p),
fors(*p),
@ -416,26 +417,31 @@ void JrdStatement::verifyAccess(thread_db* tdbb)
else
{
jrd_rel* relation = MET_lookup_relation_id(tdbb, item->exa_rel_id, false);
jrd_rel* view = NULL;
if (item->exa_view_id)
view = MET_lookup_relation_id(tdbb, item->exa_view_id, false);
if (!relation)
continue;
MetaName userName;
if (item->exa_view_id)
{
jrd_rel* view = MET_lookup_relation_id(tdbb, item->exa_view_id, false);
if (view && (view->rel_flags & REL_sql_relation))
userName = view->rel_owner_name;
}
switch (item->exa_action)
{
case ExternalAccess::exa_insert:
verifyTriggerAccess(tdbb, relation, relation->rel_pre_store, view);
verifyTriggerAccess(tdbb, relation, relation->rel_post_store, view);
verifyTriggerAccess(tdbb, relation, relation->rel_pre_store, userName);
verifyTriggerAccess(tdbb, relation, relation->rel_post_store, userName);
break;
case ExternalAccess::exa_update:
verifyTriggerAccess(tdbb, relation, relation->rel_pre_modify, view);
verifyTriggerAccess(tdbb, relation, relation->rel_post_modify, view);
verifyTriggerAccess(tdbb, relation, relation->rel_pre_modify, userName);
verifyTriggerAccess(tdbb, relation, relation->rel_post_modify, userName);
break;
case ExternalAccess::exa_delete:
verifyTriggerAccess(tdbb, relation, relation->rel_pre_erase, view);
verifyTriggerAccess(tdbb, relation, relation->rel_post_erase, view);
verifyTriggerAccess(tdbb, relation, relation->rel_pre_erase, userName);
verifyTriggerAccess(tdbb, relation, relation->rel_post_erase, userName);
break;
default:
fb_assert(false);
@ -454,17 +460,28 @@ void JrdStatement::verifyAccess(thread_db* tdbb)
{
const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str());
MetaName userName;
if (access->acc_ss_rel_id)
{
const jrd_rel* view = MET_lookup_relation_id(tdbb, access->acc_ss_rel_id, false);
if (view && (view->rel_flags & REL_sql_relation))
userName = view->rel_owner_name;
}
if (userName.isEmpty() && routine->ssDefiner.specified && routine->ssDefiner.value && routine->owner.hasData())
userName = routine->owner;
if (routine->getName().package.isEmpty())
{
SCL_check_access(tdbb, sec_class, access->acc_view_id, aclType,
SCL_check_access(tdbb, sec_class, userName, aclType,
routine->getName().identifier, access->acc_mask, access->acc_type,
true, access->acc_name, access->acc_r_name);
}
else
{
SCL_check_access(tdbb, sec_class, access->acc_view_id,
id_package, routine->getName().package,
access->acc_mask, access->acc_type,
SCL_check_access(tdbb, sec_class, userName, id_package,
routine->getName().package, access->acc_mask, access->acc_type,
true, access->acc_name, access->acc_r_name);
}
}
@ -485,6 +502,8 @@ void JrdStatement::verifyAccess(thread_db* tdbb)
MetaName objName;
SLONG objType = 0;
MetaName userName;
if (useCallerPrivs)
{
switch (transaction->tra_caller_name.type)
@ -509,9 +528,18 @@ void JrdStatement::verifyAccess(thread_db* tdbb)
}
objName = transaction->tra_caller_name.name;
userName = transaction->tra_caller_name.userName;
}
SCL_check_access(tdbb, sec_class, access->acc_view_id, objType, objName,
if (access->acc_ss_rel_id)
{
const jrd_rel* view = MET_lookup_relation_id(tdbb, access->acc_ss_rel_id, false);
if (view && (view->rel_flags & REL_sql_relation))
userName = view->rel_owner_name;
}
SCL_check_access(tdbb, sec_class, userName, objType, objName,
access->acc_mask, access->acc_type, true, access->acc_name, access->acc_r_name);
}
}
@ -588,7 +616,7 @@ void JrdStatement::release(thread_db* tdbb)
// Check that we have enough rights to access all resources this list of triggers touches.
void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation,
trig_vec* triggers, jrd_rel* view)
trig_vec* triggers, MetaName userName)
{
if (!triggers)
return;
@ -629,10 +657,18 @@ void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation,
}
// a direct access to an object from this trigger
if (access->acc_ss_rel_id)
{
const jrd_rel* view = MET_lookup_relation_id(tdbb, access->acc_ss_rel_id, false);
if (view && (view->rel_flags & REL_sql_relation))
userName = view->rel_owner_name;
}
else if (t.ssDefiner.specified && t.ssDefiner.value)
userName = ownerRelation->rel_owner_name;
const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str());
SCL_check_access(tdbb, sec_class,
(access->acc_view_id) ? access->acc_view_id : (view ? view->rel_id : 0),
id_trigger, t.statement->triggerName, access->acc_mask,
SCL_check_access(tdbb, sec_class, userName, id_trigger,
t.statement->triggerName, access->acc_mask,
access->acc_type, true, access->acc_name, access->acc_r_name);
}
}

View File

@ -58,7 +58,7 @@ public:
private:
static void verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, trig_vec* triggers,
jrd_rel* view);
Firebird::MetaName userName);
static void triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, trig_vec* tvec);
void buildExternalAccess(thread_db* tdbb, ExternalAccessList& list);
@ -76,6 +76,7 @@ public:
const jrd_prc* procedure; // procedure, if any
const Function* function; // function, if any
Firebird::MetaName triggerName; // name of request (trigger), if any
Firebird::MetaName triggerOwner; // user name if trigger run with SQL SECURITY DEFINER
JrdStatement* parentStatement; // Sub routine's parent statement
Firebird::Array<JrdStatement*> subStatements; // Array of subroutines' statements
const StmtNode* topNode; // top of execution tree

View File

@ -667,9 +667,9 @@ RecordSourceNode* RelationSourceNode::pass1(thread_db* tdbb, CompilerScratch* cs
if (relation)
{
int viewId = tail->csb_view ? tail->csb_view->rel_id :
SLONG ssRelationIdId = tail->csb_view ? tail->csb_view->rel_id :
view ? view->rel_id : csb->csb_view ? csb->csb_view->rel_id : 0;
CMP_post_access(tdbb, csb, relation->rel_security_name, viewId,
CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationIdId,
SCL_select, SCL_object_table, relation->rel_name);
}

View File

@ -178,6 +178,7 @@ public:
trig_vec* rel_post_store; // Post-operation store trigger
prim rel_primary_dpnds; // foreign dependencies on this relation's primary key
frgn rel_foreign_refs; // foreign references to other relations' primary keys
Nullable<bool> rel_ss_definer;
Firebird::Mutex rel_drop_mutex;
@ -319,7 +320,7 @@ const ULONG REL_gc_lockneed = 0x80000; // gc lock should be acquired
inline jrd_rel::jrd_rel(MemoryPool& p)
: rel_pool(&p), rel_flags(REL_gc_lockneed),
rel_name(p), rel_owner_name(p), rel_security_name(p),
rel_view_contexts(p), rel_gc_records(p)
rel_view_contexts(p), rel_gc_records(p), rel_ss_definer(false)
{
}

View File

@ -28,6 +28,7 @@
#include "../common/classes/QualifiedName.h"
#include "../common/classes/NestConst.h"
#include "../common/MsgMetadata.h"
#include "../common/classes/Nullable.h"
namespace Jrd
{
@ -177,6 +178,9 @@ namespace Jrd
// (it will usually be 0)
USHORT alterCount; // No. of times the routine was altered
Lock* existenceLock; // existence lock, if any
Nullable<bool> ssDefiner; // true ? SQL DEFINER : SQL INVOKER
Firebird::MetaName owner;
};
}

View File

@ -355,7 +355,7 @@ ULONG CMP_impure(CompilerScratch* csb, ULONG size)
void CMP_post_access(thread_db* tdbb,
CompilerScratch* csb,
const Firebird::MetaName& security_name,
SLONG view_id,
SLONG ssRelationId, // SQL SECURITY relation in which contex permissions should be check
SecurityClass::flags_t mask,
SLONG type_name,
const Firebird::MetaName& name,
@ -383,7 +383,7 @@ void CMP_post_access(thread_db* tdbb,
SET_TDBB(tdbb);
AccessItem access(security_name, view_id, name, type_name, mask, r_name);
AccessItem access(security_name, ssRelationId, name, type_name, mask, r_name);
FB_SIZE_T i;

View File

@ -49,7 +49,7 @@ Jrd::jrd_req* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool);
void CMP_mark_variant(Jrd::CompilerScratch*, StreamType stream);
Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&);
void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Firebird::MetaName&, SLONG,
void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Firebird::MetaName&, SLONG ssRelationId,
Jrd::SecurityClass::flags_t, SLONG type_name, const Firebird::MetaName&,
const Firebird::MetaName& = "");

View File

@ -188,7 +188,7 @@ typedef Firebird::SortedArray<Resource, Firebird::EmptyStorage<Resource>,
struct AccessItem
{
Firebird::MetaName acc_security_name;
SLONG acc_view_id;
SLONG acc_ss_rel_id; // Relation Id which owner will be used to check permissions
Firebird::MetaName acc_name, acc_r_name;
SLONG acc_type;
SecurityClass::flags_t acc_mask;
@ -209,8 +209,8 @@ struct AccessItem
if ((v = i1.acc_security_name.compare(i2.acc_security_name)) != 0)
return v > 0;
if (i1.acc_view_id != i2.acc_view_id)
return i1.acc_view_id > i2.acc_view_id;
if (i1.acc_ss_rel_id != i2.acc_ss_rel_id)
return i1.acc_ss_rel_id > i2.acc_ss_rel_id;
if (i1.acc_mask != i2.acc_mask)
return i1.acc_mask > i2.acc_mask;
@ -227,7 +227,7 @@ struct AccessItem
AccessItem(const Firebird::MetaName& security_name, SLONG view_id,
const Firebird::MetaName& name, SLONG type,
SecurityClass::flags_t mask, const Firebird::MetaName& relName)
: acc_security_name(security_name), acc_view_id(view_id), acc_name(name),
: acc_security_name(security_name), acc_ss_rel_id(view_id), acc_name(name),
acc_r_name(relName), acc_type(type), acc_mask(mask)
{}
};
@ -514,6 +514,7 @@ public:
// used in cmp.cpp/pass1
jrd_rel* csb_view;
StreamType csb_view_stream;
jrd_rel* csb_parent_relation;
unsigned blrVersion;
USHORT csb_remap_variable;
bool csb_validate_expr;

View File

@ -406,19 +406,20 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql)
statement = statement->parentStatement;
if (statement && statement->triggerName.hasData())
tran->getHandle()->tra_caller_name = CallerName(obj_trigger, statement->triggerName);
tran->getHandle()->tra_caller_name = CallerName(obj_trigger, statement->triggerName, statement->triggerOwner);
else if (statement && (routine = statement->getRoutine()) &&
routine->getName().identifier.hasData())
{
const MetaName& userName = routine->ssDefiner.specified && routine->ssDefiner.value ? routine->owner : "";
if (routine->getName().package.isEmpty())
{
tran->getHandle()->tra_caller_name = CallerName(routine->getObjectType(),
routine->getName().identifier);
routine->getName().identifier, userName);
}
else
{
tran->getHandle()->tra_caller_name = CallerName(obj_package_header,
routine->getName().package);
routine->getName().package, userName);
}
}
else

View File

@ -194,3 +194,4 @@
FIELD(fld_plan , nam_plan , dtype_blob , BLOB_SIZE , isc_blob_text , NULL , true)
FIELD(fld_system_privileges, nam_system_privileges, dtype_text, 8 , dsc_text_type_fixed , dflt_no_privs, true)
FIELD(fld_b_sql_security, nam_sql_security , dtype_boolean , 1 , 0 , NULL , true)

View File

@ -844,6 +844,9 @@ void Trigger::compile(thread_db* tdbb)
}
statement->triggerName = name;
const Nullable<bool>& ss = ssDefiner.specified ? ssDefiner : relation->rel_ss_definer;
if (ss.specified && ss.value)
statement->triggerOwner = relation->rel_owner_name;
if (sys_trigger)
statement->flags |= JrdStatement::FLAG_SYS_TRIGGER;

View File

@ -156,6 +156,7 @@ public:
Firebird::string entryPoint; // External trigger entrypoint
Firebird::string extBody; // External trigger body
ExtEngineManager::Trigger* extTrigger; // External trigger
Nullable<bool> ssDefiner;
void compile(thread_db*); // Ensure that trigger is compiled
void release(thread_db*); // Try to free trigger request
@ -185,7 +186,7 @@ class jrd_prc : public Routine
{
public:
const Format* prc_record_format;
prc_t prc_type; // procedure type
prc_t prc_type; // procedure type
const ExtEngineManager::Procedure* getExternal() const { return prc_external; }
void setExternal(ExtEngineManager::Procedure* value) { prc_external = value; }

View File

@ -114,7 +114,7 @@ static int partners_ast_relation(void*);
static int rescan_ast_relation(void*);
static ULONG get_rel_flags_from_FLAGS(USHORT);
static void get_trigger(thread_db*, jrd_rel*, bid*, bid*, trig_vec**, const TEXT*, FB_UINT64, bool,
USHORT, const MetaName&, const string&, const bid*);
USHORT, const MetaName&, const string&, const bid*, Nullable<bool> ssDefiner);
static bool get_type(thread_db*, USHORT*, const UCHAR*, const TEXT*);
static void lookup_view_contexts(thread_db*, jrd_rel*);
static void make_relation_scope_name(const TEXT*, const USHORT, string& str);
@ -123,7 +123,7 @@ static BoolExprNode* parse_field_validation_blr(thread_db* tdbb, bid* blob_id, c
static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*);
static void save_trigger_data(thread_db*, trig_vec**, jrd_rel*, JrdStatement*, blb*, blb*,
const TEXT*, FB_UINT64, bool, USHORT, const MetaName&, const string&,
const bid*);
const bid*, Nullable<bool> ssDefiner);
static void scan_partners(thread_db*, jrd_rel*);
static void store_dependencies(thread_db*, CompilerScratch*, const jrd_rel*,
const MetaName&, int, jrd_tra*);
@ -1972,6 +1972,10 @@ void MET_load_trigger(thread_db* tdbb,
if (!TRG.RDB$ENTRYPOINT.NULL) // ODS_12_0
entryPoint = TRG.RDB$ENTRYPOINT;
const Nullable<bool> ssDefiner = TRG.RDB$SQL_SECURITY.NULL ?
relation->rel_ss_definer : Nullable<bool>(static_cast<bool>(TRG.RDB$SQL_SECURITY));
if (TRG.RDB$RELATION_NAME.NULL)
{
if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB ||
@ -1989,7 +1993,8 @@ void MET_load_trigger(thread_db* tdbb,
trig_flags,
engine,
entryPoint,
&extBodyId);
&extBodyId,
ssDefiner);
}
}
else
@ -2009,7 +2014,8 @@ void MET_load_trigger(thread_db* tdbb,
trig_flags,
engine,
entryPoint,
&extBodyId);
&extBodyId,
ssDefiner);
}
}
}
@ -3159,7 +3165,7 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation)
statement->flags |= JrdStatement::FLAG_IGNORE_PERM;
save_trigger_data(tdbb, ptr, relation, statement, NULL, NULL, NULL, type, true, 0, "",
"", NULL);
"", NULL, Nullable<bool>());
}
}
END_FOR
@ -3327,10 +3333,15 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags)
{
if (!PKG.RDB$SECURITY_CLASS.NULL)
procedure->setSecurityName(PKG.RDB$SECURITY_CLASS);
if (!PKG.RDB$SQL_SECURITY.NULL)
procedure->ssDefiner = Nullable<bool>::val(PKG.RDB$SQL_SECURITY);
}
END_FOR
}
if (!procedure->ssDefiner.specified && !P.RDB$SQL_SECURITY.NULL)
procedure->ssDefiner = Nullable<bool>::val(P.RDB$SQL_SECURITY);
procedure->owner = P.RDB$OWNER_NAME;
procedure->setImplemented(true);
procedure->getInputFields().resize(P.RDB$PROCEDURE_INPUTS);
procedure->getOutputFields().resize(P.RDB$PROCEDURE_OUTPUTS);
@ -3782,6 +3793,7 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
relation->rel_name = REL.RDB$RELATION_NAME;
relation->rel_owner_name = REL.RDB$OWNER_NAME;
relation->rel_ss_definer = REL.RDB$SQL_SECURITY;
if (!REL.RDB$VIEW_BLR.isEmpty())
{
@ -4431,7 +4443,7 @@ static void get_trigger(thread_db* tdbb, jrd_rel* relation,
const TEXT* name, FB_UINT64 type,
bool sys_trigger, USHORT flags,
const MetaName& engine, const string& entryPoint,
const bid* body)
const bid* body, Nullable<bool> ssDefiner)
{
/**************************************
*
@ -4459,7 +4471,7 @@ static void get_trigger(thread_db* tdbb, jrd_rel* relation,
debugInfoBlob = blb::open(tdbb, attachment->getSysTransaction(), debug_blob_id);
save_trigger_data(tdbb, ptr, relation, NULL, blrBlob, debugInfoBlob,
name, type, sys_trigger, flags, engine, entryPoint, body);
name, type, sys_trigger, flags, engine, entryPoint, body, ssDefiner);
}
@ -4822,7 +4834,7 @@ static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation
const TEXT* name, FB_UINT64 type,
bool sys_trigger, USHORT flags,
const MetaName& engine, const string& entryPoint,
const bid* body)
const bid* body, Nullable<bool> ssDefiner)
{
/**************************************
*
@ -4882,6 +4894,7 @@ static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation
t.relation = relation;
t.engine = engine;
t.entryPoint = entryPoint;
t.ssDefiner = ssDefiner;
}

View File

@ -407,3 +407,4 @@ NAME("SEC$USER", nam_sec_user)
NAME("SEC$USER_TYPE", nam_sec_user_type)
NAME("RDB$SYSTEM_PRIVILEGES", nam_system_privileges)
NAME("RDB$SQL_SECURITY", nam_sql_security)

View File

@ -144,6 +144,7 @@ RELATION(nam_relations, rel_relations, ODS_8_0, rel_persistent)
FIELD(f_rel_def_class, nam_def_class, fld_class, 1, ODS_8_0)
FIELD(f_rel_flags, nam_flags, fld_flag_nullable, 0, ODS_8_0)
FIELD(f_rel_type, nam_r_type, fld_r_type, 0, ODS_11_1)
FIELD(f_rel_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0)
END_RELATION
// Relation 7 (RDB$VIEW_RELATIONS)
@ -205,6 +206,7 @@ RELATION(nam_trgs, rel_triggers, ODS_8_0, rel_persistent)
FIELD(f_trg_debug_info, nam_debug_info, fld_debug_info, 1, ODS_11_1)
FIELD(f_trg_engine_name, nam_engine_name, fld_engine_name, 1, ODS_12_0)
FIELD(f_trg_entry, nam_entry, fld_ext_name, 1, ODS_12_0)
FIELD(f_trg_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0)
END_RELATION
// Relation 13 (RDB$DEPENDENCIES)
@ -239,6 +241,7 @@ RELATION(nam_funs, rel_funs, ODS_8_0, rel_persistent)
FIELD(f_fun_owner, nam_owner, fld_user, 1, ODS_12_0)
FIELD(f_fun_legacy_flag, nam_legacy_flag, fld_flag_nullable, 0, ODS_12_0)
FIELD(f_fun_deterministic_flag, nam_deterministic_flag, fld_flag_nullable, 0, ODS_12_0)
FIELD(f_fun_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0)
END_RELATION
// Relation 15 (RDB$FUNCTION_ARGUMENTS)
@ -382,6 +385,7 @@ RELATION(nam_procedures, rel_procedures, ODS_8_0, rel_persistent)
FIELD(f_prc_entry, nam_entry, fld_ext_name, 1, ODS_12_0)
FIELD(f_prc_pkg_name, nam_pkg_name, fld_pkg_name, 1, ODS_12_0)
FIELD(f_prc_private_flag, nam_private_flag, fld_flag_nullable, 1, ODS_12_0)
FIELD(f_prc_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0)
END_RELATION
// Relation 27 (RDB$PROCEDURE_PARAMETERS)
@ -615,6 +619,7 @@ RELATION(nam_packages, rel_packages, ODS_12_0, rel_persistent)
FIELD(f_pkg_owner, nam_owner, fld_user, 1, ODS_12_0)
FIELD(f_pkg_sys_flag, nam_sys_flag, fld_flag, 1, ODS_12_0)
FIELD(f_pkg_desc, nam_description, fld_description, 1, ODS_12_0)
FIELD(f_pkg_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0)
END_RELATION
// Relation 43 (SEC$USERS)

View File

@ -72,8 +72,8 @@ static bool check_number(const UCHAR*, USHORT);
static bool check_user_group(thread_db* tdbb, const UCHAR*, USHORT);
static bool check_string(const UCHAR*, const Firebird::MetaName&);
static SecurityClass::flags_t compute_access(thread_db* tdbb, const SecurityClass*,
const jrd_rel*, SLONG, const Firebird::MetaName&);
static SecurityClass::flags_t walk_acl(thread_db* tdbb, const Acl&, const jrd_rel*,
const MetaName &userName, SLONG, const Firebird::MetaName&);
static SecurityClass::flags_t walk_acl(thread_db* tdbb, const Acl&, const MetaName&,
SLONG, const Firebird::MetaName&);
static void raiseError(SecurityClass::flags_t mask, SLONG type, const Firebird::MetaName& name,
const Firebird::MetaName& r_name);
@ -198,7 +198,7 @@ static void raiseError(SecurityClass::flags_t mask, SLONG type, const Firebird::
void SCL_check_access(thread_db* tdbb,
const SecurityClass* s_class,
SLONG view_id,
MetaName userName,
SLONG obj_type,
const Firebird::MetaName& obj_name,
SecurityClass::flags_t mask,
@ -215,6 +215,7 @@ void SCL_check_access(thread_db* tdbb,
*
* Functional description
* Check security class for desired permission.
* userName a name of user in which context permissions will be checked.
*
**************************************/
SET_TDBB(tdbb);
@ -230,20 +231,14 @@ void SCL_check_access(thread_db* tdbb,
}
// Check global DDL permissions with ANY option which allow user to make changes non owned objects
const SecurityClass::flags_t obj_mask = SCL_get_object_mask(type);
if (mask & obj_mask)
if ( (type > obj_last_non_ddl) && (mask & SCL_get_object_mask(type)))
return;
if (!s_class || (userName.isEmpty() && (mask & s_class->scl_flags)))
return;
if (!s_class || (mask & s_class->scl_flags))
return;
const jrd_rel* view = NULL;
if (view_id)
view = MET_lookup_relation_id(tdbb, view_id, false);
if ((view || obj_name.hasData()) &&
(compute_access(tdbb, s_class, view, obj_type, obj_name) & mask))
if ((userName.hasData() || obj_name.hasData()) &&
(compute_access(tdbb, s_class, userName, obj_type, obj_name) & mask))
{
return;
}
@ -1421,7 +1416,7 @@ static void get_string(const UCHAR* acl, Firebird::MetaName& string)
static SecurityClass::flags_t compute_access(thread_db* tdbb,
const SecurityClass* s_class,
const jrd_rel* view,
const MetaName& userName,
SLONG obj_type,
const Firebird::MetaName& obj_name)
{
@ -1487,7 +1482,7 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb,
acl.shrink(end - buffer);
if (acl.getCount() > 0)
privileges |= walk_acl(tdbb, acl, view, obj_type, obj_name);
privileges |= walk_acl(tdbb, acl, userName, obj_type, obj_name);
}
END_FOR
@ -1497,7 +1492,7 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb,
static SecurityClass::flags_t walk_acl(thread_db* tdbb,
const Acl& acl,
const jrd_rel* view,
const MetaName& userName,
SLONG obj_type,
const Firebird::MetaName& obj_name)
{
@ -1510,6 +1505,7 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb,
* Functional description
* Walk an access control list looking for a hit. If a hit
* is found, return privileges.
* userName a name of user which privileges must be computed. NULL is current user (INVOKER)
*
**************************************/
SET_TDBB(tdbb);
@ -1517,18 +1513,12 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb,
// Munch ACL. If we find a hit, eat up privileges.
UserId user = *attachment->att_user;
UserId user;
if (view && (view->rel_flags & REL_sql_relation))
{
// Use the owner of the view to perform the sql security
// checks with: (1) The view user must have sufficient privileges
// to the view, and (2a) the view owner must have sufficient
// privileges to the base table or (2b) the view must have
// sufficient privileges on the base table.
user.setUserName(view->rel_owner_name.c_str());
}
if (userName.hasData())
user.setUserName(userName);
else
user = *attachment->att_user;
SecurityClass::flags_t privilege = 0;
const UCHAR* a = acl.begin();
@ -1582,10 +1572,6 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb,
}
case id_view:
if (!view || check_string(a, view->rel_name))
hit = false;
break;
case id_package:
case id_procedure:
case id_trigger:
@ -1601,8 +1587,8 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb,
// generated for SQL.
hit = false;
if (!view)
hit = false;
// if (!view)
// hit = false;
break;
case id_user:

View File

@ -33,7 +33,7 @@
struct dsc;
void SCL_check_access(Jrd::thread_db*, const Jrd::SecurityClass*, SLONG, SLONG, const Firebird::MetaName&,
void SCL_check_access(Jrd::thread_db*, const Jrd::SecurityClass*, Firebird::MetaName userName, SLONG, const Firebird::MetaName&,
Jrd::SecurityClass::flags_t, SLONG type, bool recursive, const Firebird::MetaName&,
const Firebird::MetaName& = "");
void SCL_check_create_access(Jrd::thread_db*, int type);

View File

@ -108,9 +108,10 @@ typedef Firebird::BePlusTree<BlobIndex, ULONG, MemoryPool, BlobIndex> BlobIndexT
struct CallerName
{
CallerName(int aType, const Firebird::MetaName& aName)
CallerName(int aType, const Firebird::MetaName& aName, const Firebird::MetaName& aUserName)
: type(aType),
name(aName)
name(aName),
userName(aUserName)
{
}
@ -121,7 +122,8 @@ struct CallerName
CallerName(const CallerName& o)
: type(o.type),
name(o.name)
name(o.name),
userName(o.userName)
{
}
@ -131,11 +133,13 @@ struct CallerName
{
type = o.type;
name = o.name;
userName = o.userName;
}
}
int type;
Firebird::MetaName name;
Firebird::MetaName userName;
};
const int DEFAULT_LOCK_TIMEOUT = -1; // infinite

View File

@ -1699,6 +1699,7 @@ COMMIT WORK;
('dsql_no_output_sqlda', NULL, NULL, NULL, 7, 38, NULL, 'No SQLDA for output values provided', NULL, NULL);
('dsql_wrong_param_num', NULL, NULL, NULL, 7, 39, NULL, 'Wrong number of parameters (expected @1, got @2)', NULL, NULL);
-- Do not change the arguments of the previous DSQL messages.
('dsql_invalid_drop_ss_clause', 'CreateAlterTriggerNode::dsqlPass', 'DdlNodes.epp', NULL, 7, 40, NULL, 'Invalid DROP SQL SECURITY clause', NULL, NULL);
-- Write the new DSQL messages here.
-- DYN
(NULL, NULL, 'dyn.c', NULL, 8, 1, NULL, 'ODS version not supported by DYN', NULL, NULL);

View File

@ -879,6 +879,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-802, '07', '002', 7, 37, 'dsql_no_input_sqlda', NULL, NULL)
(-802, '07', '002', 7, 38, 'dsql_no_output_sqlda', NULL, NULL)
(-313, '07', '001', 7, 39, 'dsql_wrong_param_num', NULL, NULL)
(-817, '42', '000', 7, 40, 'dsql_invalid_drop_ss_clause', NULL, NULL)
-- DYN
(-901, '42', '000', 8, 37, 'dyn_filter_not_found', NULL, NULL)
(-901, '42', '000', 8, 41, 'dyn_func_not_found', NULL, NULL)

View File

@ -162,6 +162,7 @@ static const TOK tokens[] =
{TOK_DECODE, "DECODE", false},
{TOK_DECRYPT, "DECRYPT", true},
{TOK_DEFAULT, "DEFAULT", false},
{TOK_DEFINER, "DEFINER", true},
{TOK_DELETE, "DELETE", false},
{TOK_DELETING, "DELETING", true},
{TOK_DENSE_RANK, "DENSE_RANK", false},
@ -235,6 +236,7 @@ static const TOK tokens[] =
{TOK_INT, "INT", false},
{TOK_INTEGER, "INTEGER", false},
{TOK_INTO, "INTO", false},
{TOK_INVOKER, "INVOKER", true},
{TOK_IS, "IS", false},
{TOK_ISOLATION, "ISOLATION", false},
{TOK_JOIN, "JOIN", false},
@ -383,6 +385,8 @@ static const TOK tokens[] =
{TOK_DATABASE, "SCHEMA", false}, // Alias of DATABASE
{TOK_SCROLL, "SCROLL", false},
{TOK_SECOND, "SECOND", false},
{TOK_SQL, "SQL", true},
{TOK_SECURITY, "SECURITY", true},
{TOK_SEGMENT, "SEGMENT", false},
{TOK_SELECT, "SELECT", false},
{TOK_SENSITIVE, "SENSITIVE", false},