diff --git a/doc/sql.extensions/README.sql_security.txt b/doc/sql.extensions/README.sql_security.txt new file mode 100644 index 0000000000..5db61b5920 --- /dev/null +++ b/doc/sql.extensions/README.sql_security.txt @@ -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 (...) [SQL SECURITY {DEFINER | INVOKER}] +ALTER TABLE ... [{ALTER SQL SECURITY {DEFINER | INVOKER} | DROP SQL SECURITY}] +CREATE [OR ALTER] FUNCTION ... [SQL SECURITY {DEFINER | INVOKER}] AS ... +CREATE [OR ALTER] PROCEDURE ... [SQL SECURITY {DEFINER | INVOKER}] AS ... +CREATE [OR ALTER] TRIGGER ... [SQL SECURITY {DEFINER | INVOKER} | DROP SQL SECURITY] [AS ...] +CREATE [OR ALTER] PACKAGE [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; diff --git a/extern/btyacc/defs.h b/extern/btyacc/defs.h index fbe39f84e5..7adb1d9a29 100644 --- a/extern/btyacc/defs.h +++ b/extern/btyacc/defs.h @@ -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 diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index 4fb1f81f6b..a07c52fe70 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -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 diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index 69966f5a9a..e74c47205f 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -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; diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 2c5c58cf5a..3b139b6544 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -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; diff --git a/src/burp/burp.h b/src/burp/burp.h index ecb444b20e..46c91b80b3 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -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 }; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index d66d6e1243..439bfecb5c 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -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 diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 588cd8f7d1..e5f7bf96c3 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -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 ssDefiner = static_cast( + 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; diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 7e87e75e24..011dd0d8cd 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -409,6 +409,7 @@ public: bool privateScope; bool preserveDefaults; SLONG udfReturnPos; + Nullable ssDefiner; }; @@ -542,6 +543,7 @@ public: Firebird::MetaName packageOwner; bool privateScope; bool preserveDefaults; + Nullable ssDefiner; }; @@ -586,6 +588,13 @@ typedef RecreateNode 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 ssDefiner; + }; + RelationNode(MemoryPool& p, RelationSourceNode* aDsqlNode); static void deleteLocalField(thread_db* tdbb, jrd_tra* transaction, @@ -1448,6 +1469,7 @@ public: NestConst dsqlNode; Firebird::MetaName name; Firebird::Array > clauses; + Nullable ssDefiner; }; @@ -2299,4 +2321,14 @@ public: } // namespace +template <> +class NullableClear // TriggerDefinition::SqlSecurity especialization for NullableClear +{ +public: + static void clear(Jrd::TriggerDefinition::SqlSecurity& v) + { + v = Jrd::TriggerDefinition::SS_INVOKER; + } +}; + #endif // DSQL_DDL_NODES_H diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index c6cc1af2e7..c42cf36d10 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -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 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 diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 4e983b8e9f..c82d61a9d4 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -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; diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 1b20255d1b..18da5eb828 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -110,6 +110,7 @@ public: Firebird::Array* items; Firebird::SortedArray functionNames; Firebird::SortedArray procedureNames; + Nullable ssDefiner; private: Firebird::MetaName owner; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 8dbaed6aaf..67976be9ca 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -607,6 +607,10 @@ using namespace Firebird; %token TIES %token UNBOUNDED %token WINDOW +%token SQL +%token SECURITY +%token INVOKER +%token DEFINER // precedence declarations for expression evaluation @@ -638,6 +642,7 @@ using namespace Firebird; { BaseNullable nullableIntVal; BaseNullable nullableBoolVal; + BaseNullable nullableSqlSecurityVal; bool boolVal; int intVal; unsigned uintVal; @@ -2057,22 +2062,38 @@ page_noise %type table_clause table_clause : simple_table_name external_file - { $$ = newNode($1, $2); } - '(' table_elements($3) ')' - { $$ = $3; } + { + $$ = newNode($1, $2); + } + '(' table_elements($3) ')' sql_security_clause + { + $$ = $3; + $$->ssDefiner = $7; + } ; %type gtt_table_clause gtt_table_clause : simple_table_name { $$ = newNode($1); } - '(' table_elements($2) ')' gtt_scope + '(' table_elements($2) ')' gtt_ops($2) { $$ = $2; - $$->relationType = static_cast($6); } ; +%type gtt_ops() +gtt_ops($createRelationNode) + : gtt_op($createRelationNode) + | gtt_ops ',' gtt_op($createRelationNode) + ; + +%type gtt_op() +gtt_op($createRelationNode) + : sql_security_clause { $createRelationNode->ssDefiner = $1; } + | gtt_scope { $createRelationNode->relationType = static_cast($1); } + ; + %type 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() @@ -2457,12 +2477,13 @@ procedure_clause %type 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 procedure_clause_start procedure_clause_start : symbol_procedure_name - { $$ = newNode(*$1); } - input_parameters(NOTRIAL(&$2->parameters)) output_parameters(NOTRIAL(&$2->returns)) + { + $$ = newNode(*$1); + } + input_parameters(NOTRIAL(&$2->parameters)) output_parameters(NOTRIAL(&$2->returns)) { $$ = $2; } ; +%type sql_security_clause +sql_security_clause + : SQL SECURITY DEFINER + { $$ = Nullable::val(true); } + | SQL SECURITY INVOKER + { $$ = Nullable::val(false); } + | // nothing + { $$ = Nullable::empty(); } + ; + %type alter_procedure_clause alter_procedure_clause : procedure_clause @@ -2580,12 +2613,13 @@ function_clause %type 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 function_clause_start function_clause_start : symbol_UDF_name - { $$ = newNode(*$1); } + { + $$ = newNode(*$1); + } input_parameters(NOTRIAL(&$2->parameters)) RETURNS domain_or_non_array_type collate_clause deterministic_opt { @@ -2665,11 +2701,12 @@ replace_function_clause %type 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(*$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 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(*$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(*$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(*$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(); + clause->ssDefiner = Nullable::val(true); + $relationNode->clauses.add(clause); + } + | ALTER SQL SECURITY INVOKER + { + RelationNode::AlterSqlSecurityClause* clause = + newNode(); + clause->ssDefiner = Nullable::val(false); + $relationNode->clauses.add(clause); + } + | DROP SQL SECURITY + { + RelationNode::AlterSqlSecurityClause* clause = + newNode(); + clause->ssDefiner = Nullable::empty(); + $relationNode->clauses.add(clause); + } ; %type alter_column_name @@ -4105,7 +4166,7 @@ crypt_key_clause($alterDatabaseNode) %type 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(*$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(*$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::empty(); } ; +%type trg_sql_security_clause +trg_sql_security_clause + : SQL SECURITY DEFINER + { $$ = Nullable::val(TriggerDefinition::SS_DEFINER); } + | SQL SECURITY INVOKER + { $$ = Nullable::val(TriggerDefinition::SS_INVOKER); } + | DROP SQL SECURITY + { $$ = Nullable::val(TriggerDefinition::SS_DROP); } + | // nothing + { $$ = Nullable::empty(); } + ; // DROP metadata operations @@ -8029,7 +8103,11 @@ non_reserved_word | SYSTEM | ERROR_MESSAGE | TIES -; + | SQL + | SECURITY + | INVOKER + | DEFINER + ; %% diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 8ecb733a9d..810ea1e65d 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -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}, diff --git a/src/include/gen/iberror.h b/src/include/gen/iberror.h index 4cdfa1cb3d..32b0339aab 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -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 diff --git a/src/include/gen/ids.h b/src/include/gen/ids.h index 12bf808f81..e84174fc01 100644 --- a/src/include/gen/ids.h +++ b/src/include/gen/ids.h @@ -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) diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index be0ed3595b..bd76bef906 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -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 */ diff --git a/src/include/gen/sql_code.h b/src/include/gen/sql_code.h index bb6eedb596..61ffc3db98 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -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 */ diff --git a/src/include/gen/sql_state.h b/src/include/gen/sql_state.h index 9d6e8fcce8..72b879606c 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -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 diff --git a/src/isql/extract.epp b/src/isql/extract.epp index b37d2d33c8..943f23f5c1 100644 --- a/src/isql/extract.epp +++ b/src/isql/extract.epp @@ -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); diff --git a/src/isql/show.epp b/src/isql/show.epp index e812a620e6..a013d63d4e 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -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) diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 8f73c5cb6f..3b8b76cb9b 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -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 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 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 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& ssDefiner = trg->ssDefiner.specified ? trg->ssDefiner : trg->relation->rel_ss_definer; + const MetaName& userName = ssDefiner.specified && ssDefiner.value ? trg->relation->rel_owner_name : ""; ContextManager 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 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 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 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& ssDefiner = trg->ssDefiner.specified ? trg->ssDefiner : trg->relation->rel_ss_definer; + const MetaName& userName = ssDefiner.specified && ssDefiner.value ? trg->relation->rel_owner_name : ""; ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet, - CallerName(obj_trigger, trg->name)); + CallerName(obj_trigger, trg->name, userName)); ///MemoryPool& pool = *tdbb->getDefaultPool(); MemoryPool& pool = *getDefaultMemoryPool(); diff --git a/src/jrd/Function.epp b/src/jrd/Function.epp index e17ef7a4ed..002fec76a3 100644 --- a/src/jrd/Function.epp +++ b/src/jrd/Function.epp @@ -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::val(PKG.RDB$SQL_SECURITY); + END_FOR } + if (!function->ssDefiner.specified && !X.RDB$SQL_SECURITY.NULL) + function->ssDefiner = Nullable::val(X.RDB$SQL_SECURITY); + size_t count = 0; ULONG length = 0; diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/JrdStatement.cpp index 9d7dccbc76..bb73de220d 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/JrdStatement.cpp @@ -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); } } diff --git a/src/jrd/JrdStatement.h b/src/jrd/JrdStatement.h index 2e6c4d08aa..c0820da985 100644 --- a/src/jrd/JrdStatement.h +++ b/src/jrd/JrdStatement.h @@ -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 subStatements; // Array of subroutines' statements const StmtNode* topNode; // top of execution tree diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 8a8b662b7a..4b49afc846 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -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); } diff --git a/src/jrd/Relation.h b/src/jrd/Relation.h index 3947cb6638..44cef563ac 100644 --- a/src/jrd/Relation.h +++ b/src/jrd/Relation.h @@ -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 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) { } diff --git a/src/jrd/Routine.h b/src/jrd/Routine.h index 52b3d93790..7bf0c8ca00 100644 --- a/src/jrd/Routine.h +++ b/src/jrd/Routine.h @@ -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 ssDefiner; // true ? SQL DEFINER : SQL INVOKER + Firebird::MetaName owner; }; } diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index ecde45193e..b32734ede7 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -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; diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index 16a9b9a04e..b559aceefd 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -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& = ""); diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 46ccbcc2af..2f91b4687e 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -188,7 +188,7 @@ typedef Firebird::SortedArray, 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; diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index fb8f1a6967..f5a9dbc3b4 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -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 diff --git a/src/jrd/fields.h b/src/jrd/fields.h index e31be2d38d..0189ddc18f 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -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) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 90c215bc64..3c475421e1 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -844,6 +844,9 @@ void Trigger::compile(thread_db* tdbb) } statement->triggerName = name; + const Nullable& 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; diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index c1b096e844..c3cfd0ad66 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -156,6 +156,7 @@ public: Firebird::string entryPoint; // External trigger entrypoint Firebird::string extBody; // External trigger body ExtEngineManager::Trigger* extTrigger; // External trigger + Nullable 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; } diff --git a/src/jrd/met.epp b/src/jrd/met.epp index b11c79db77..cf40a1b960 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -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 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 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 ssDefiner = TRG.RDB$SQL_SECURITY.NULL ? + relation->rel_ss_definer : Nullable(static_cast(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()); } } 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::val(PKG.RDB$SQL_SECURITY); } END_FOR } + if (!procedure->ssDefiner.specified && !P.RDB$SQL_SECURITY.NULL) + procedure->ssDefiner = Nullable::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 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 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; } diff --git a/src/jrd/names.h b/src/jrd/names.h index 5051faa8b6..3110dc470c 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -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) diff --git a/src/jrd/relations.h b/src/jrd/relations.h index b08f527a56..5a50cdb759 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -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) diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 000f186139..0f87cf79cc 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -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: diff --git a/src/jrd/scl_proto.h b/src/jrd/scl_proto.h index e7b5fb7901..78f1636b83 100644 --- a/src/jrd/scl_proto.h +++ b/src/jrd/scl_proto.h @@ -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); diff --git a/src/jrd/tra.h b/src/jrd/tra.h index e38fc41cd8..751de181a5 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -108,9 +108,10 @@ typedef Firebird::BePlusTree 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 diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index ff0d13fd1f..3a3b900578 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -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); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index 53663299d8..e48981b128 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -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) diff --git a/src/yvalve/keywords.cpp b/src/yvalve/keywords.cpp index 9d01737499..ebac6911ac 100644 --- a/src/yvalve/keywords.cpp +++ b/src/yvalve/keywords.cpp @@ -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},