From 573e93e5a665915eb0ba172eec70955fe6565805 Mon Sep 17 00:00:00 2001 From: roman-simakov Date: Tue, 8 Jul 2014 07:35:27 +0000 Subject: [PATCH] Fixed CORE-735: User rights for metadata changes. We check DDL in DDL nodes and skip at vio level. vio level still exists for direct metadata editing. --- doc/sql.extensions/README.ddl_access.txt | 35 ++ lang_helpers/gds_codes.ftn | 4 + lang_helpers/gds_codes.pas | 2 + src/dsql/DdlNodes.epp | 461 +++++++++++++++++++++-- src/dsql/DdlNodes.h | 53 ++- src/dsql/Nodes.h | 24 +- src/dsql/PackageNodes.epp | 38 ++ src/dsql/PackageNodes.h | 4 + src/dsql/parse.y | 70 +++- src/include/gen/codetext.h | 2 + src/include/gen/iberror.h | 8 +- src/include/gen/ids.h | 2 + src/include/gen/msgs.h | 2 + src/include/gen/sql_code.h | 2 + src/include/gen/sql_state.h | 2 + src/jrd/Database.h | 2 +- src/jrd/acl.h | 8 +- src/jrd/constants.h | 2 + src/jrd/drq.h | 3 + src/jrd/filters.cpp | 5 +- src/jrd/grant.epp | 29 +- src/jrd/ini.epp | 43 +++ src/jrd/intl.h | 3 + src/jrd/jrd.h | 1 + src/jrd/met.epp | 2 +- src/jrd/obj.h | 60 ++- src/jrd/relations.h | 2 + src/jrd/scl.epp | 291 ++++++++++++-- src/jrd/scl.h | 30 +- src/jrd/scl_proto.h | 6 + src/jrd/vio.cpp | 18 +- src/msgs/facilities2.sql | 2 +- src/msgs/messages2.sql | 2 + src/msgs/system_errors2.sql | 2 + 34 files changed, 1119 insertions(+), 101 deletions(-) create mode 100644 doc/sql.extensions/README.ddl_access.txt diff --git a/doc/sql.extensions/README.ddl_access.txt b/doc/sql.extensions/README.ddl_access.txt new file mode 100644 index 00000000000..c1811d80ad5 --- /dev/null +++ b/doc/sql.extensions/README.ddl_access.txt @@ -0,0 +1,35 @@ +SQL Language Extension: GRANT/REVOKE permissions on DDL operations + + Implements capability to manage permissions on DDL operations. + +Author: + Red Soft Corporation, roman.simakov(at)red-soft.biz + +Syntax is: + +GRANT CREATE TO USER|ROLE [with grant option]; +GRANT ALTER ANY TO USER|ROLE [with grant option]; +GRANT DROP ANY TO USER|ROLE [with grant option]; + +REVOKE [grant option for] CREATE FROM USER|ROLE; +REVOKE [grant option for] ALTER ANY FROM USER|ROLE; +REVOKE [grant option for] DROP ANY FROM USER|ROLE; + +Where could be: +TABLE, VIEW, PROCEDURE, FUNCTION, PACKAGE, GENERATOR, SEQUENCE, DOMAIN, +EXCEPTION, ROLE, SHADOW, DATABASE, CHARACTER SET, COLLATION, FILTER + +Description: + +Makes it possible to grant and revoke privileges on DDL operations. + +DDL operations for managing triggers and indices re-use table privileges. + +If ANY keyword is used a user will be able to perform operation on any object. Otherwise only on object which he owns. +If ANY keyword was used due GRANT operation it also must be used in according REVOKE operation. + +Sample: + +GRANT CREATE TABLE TO Joe; +GRANT ALTER ANY TABLE TO Joe; +REVOKE CREATE TABLE FROM Joe; diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index b9e015cf085..8c2c6020029 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1598,6 +1598,10 @@ C -- PARAMETER (GDS__cursor_not_positioned = 335545092) INTEGER*4 GDS__dup_attribute PARAMETER (GDS__dup_attribute = 335545093) + INTEGER*4 GDS__dyn_no_priv + PARAMETER (GDS__dyn_no_priv = 335545094) + INTEGER*4 GDS__dsql_cant_grant_option + PARAMETER (GDS__dsql_cant_grant_option = 335545095) INTEGER*4 GDS__gfix_db_name PARAMETER (GDS__gfix_db_name = 335740929) INTEGER*4 GDS__gfix_invalid_sw diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index 769812d50b5..245da29c9e5 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -806,6 +806,8 @@ gds_set_invalid_role = 335545091; gds_cursor_not_positioned = 335545092; gds_dup_attribute = 335545093; + gds_dyn_no_priv = 335545094; + gds_dsql_cant_grant_option = 335545095; gds_gfix_db_name = 335740929; gds_gfix_invalid_sw = 335740930; gds_gfix_incmp_sw = 335740932; diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index c031fafa30e..058eb7c5f90 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -747,6 +747,15 @@ static void updateRdbFields(const TypeClause* type, //---------------------- +SecureDdlNodeExecute::SecureDdlNodeExecute(thread_db* tdbb, DdlNode* ddlNode, + DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction): _tdbb(tdbb) +{ + if (ddlNode->checkPermission(tdbb, transaction)) + tdbb->tdbb_flags |= TDBB_trusted_ddl; + + ddlNode->execute(tdbb, dsqlScratch, transaction); +} + // Delete a security class. bool DdlNode::deleteSecurityClass(thread_db* tdbb, jrd_tra* transaction, @@ -962,6 +971,12 @@ void AlterCharSetNode::print(string& text) const charSet.c_str(), defaultCollation.c_str()); } +bool AlterCharSetNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_charset(tdbb, charSet, SCL_alter); + return true; +} + void AlterCharSetNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -1030,6 +1045,13 @@ void CommentOnNode::print(string& text) const objType, objName.c_str(), this->text.c_str()); } +bool CommentOnNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + //DDL_TODO + // Probably requires migration code from execute with caching some query results for reuse in execute later. + return false; +} + // select rdb$relation_name from rdb$relation_fields where rdb$field_name = 'RDB$DESCRIPTION'; // gives the list of objects that accept descriptions. At FB2 time, the only // subobjects with descriptions are relation's fields and procedure's parameters. @@ -1357,6 +1379,17 @@ DdlNode* CreateAlterFunctionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return DdlNode::dsqlPass(dsqlScratch); } +bool CreateAlterFunctionNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + if (alter) + SCL_check_function(tdbb, &dscName, SCL_alter); + else + SCL_check_create_access(tdbb, SCL_object_function); + return true; +} + void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -1975,6 +2008,14 @@ void AlterExternalFunctionNode::print(string& text) const name.c_str(), clauses.name.c_str(), clauses.udfModule.c_str()); } +bool AlterExternalFunctionNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + SCL_check_function(tdbb, &dscName, SCL_alter); + return true; +} + // Allow changing the entry point and/or the module name of a UDF. void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) @@ -2092,6 +2133,14 @@ DdlNode* DropFunctionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return DdlNode::dsqlPass(dsqlScratch); } +bool DropFunctionNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + SCL_check_function(tdbb, &dscName, SCL_drop); + return true; +} + void DropFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -2277,6 +2326,17 @@ DdlNode* CreateAlterProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return DdlNode::dsqlPass(dsqlScratch); } +bool CreateAlterProcedureNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + if (alter) + SCL_check_procedure(tdbb, &dscName, SCL_alter); + else + SCL_check_create_access(tdbb, SCL_object_procedure); + return true; +} + void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -2820,6 +2880,14 @@ DdlNode* DropProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return DdlNode::dsqlPass(dsqlScratch); } +bool DropProcedureNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + SCL_check_procedure(tdbb, &dscName, SCL_drop); + return true; +} + void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -3075,6 +3143,14 @@ DdlNode* CreateAlterTriggerNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return DdlNode::dsqlPass(dsqlScratch); } +bool CreateAlterTriggerNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(relationName.length(), CS_METADATA, (UCHAR*)relationName.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); + return true; +} + void CreateAlterTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -3082,9 +3158,6 @@ void CreateAlterTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS Attachment* const attachment = transaction->getAttachment(); - if (relationName.isEmpty() && !attachment->locksmith()) - status_exception::raise(Arg::Gds(isc_adm_task_denied)); - source.ltrim("\n\r\t "); // run all statements under savepoint control @@ -3248,6 +3321,32 @@ DdlNode* DropTriggerNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return DdlNode::dsqlPass(dsqlScratch); } +bool DropTriggerNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + MetaName relationName; + + AutoCacheRequest request(tdbb, drq_l_trigger_relname, DYN_REQUESTS); + + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + X IN RDB$TRIGGERS + WITH X.RDB$TRIGGER_NAME EQ name.c_str() + { + relationName = X.RDB$RELATION_NAME; + } + END_FOR + + if (relationName.isEmpty()) + { + // msg 48: "Index not found" + status_exception::raise(Arg::PrivateDyn(48)); + } + + dscName.makeText(relationName.length(), CS_METADATA, (UCHAR*)relationName.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); + return true; +} + void DropTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -3371,6 +3470,12 @@ void CreateCollationNode::print(string& text) const attributesOn, attributesOff); } +bool CreateCollationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_create_access(tdbb, SCL_object_collation); + return true; +} + void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -3583,6 +3688,12 @@ void DropCollationNode::print(string& text) const name.c_str()); } +bool DropCollationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_collation(tdbb, name, SCL_drop); + return true; +} + void DropCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -3732,6 +3843,12 @@ void CreateDomainNode::print(string& text) const " " + nameTypeStr + "\n"; } +bool CreateDomainNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_create_access(tdbb, SCL_object_domain); + return true; +} + void CreateDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -4250,6 +4367,12 @@ void AlterDomainNode::print(string& text) const " %s\n", name.c_str()); } +bool AlterDomainNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_domain(tdbb, name, SCL_alter); + return true; +} + void AlterDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -4266,6 +4389,7 @@ void AlterDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, WITH FLD.RDB$FIELD_NAME EQ name.c_str() { found = true; + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_DOMAIN, name); @@ -4565,6 +4689,12 @@ void DropDomainNode::print(string& text) const name.c_str()); } +bool DropDomainNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_domain(tdbb, name, SCL_drop); + return true; +} + void DropDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -4685,6 +4815,15 @@ void CreateAlterExceptionNode::print(string& text) const name.c_str(), create, alter, message.c_str()); } +bool CreateAlterExceptionNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + if (alter) + SCL_check_exception(tdbb, name, SCL_alter); + else + SCL_check_create_access(tdbb, SCL_object_exception); + return true; +} + void CreateAlterExceptionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -4720,7 +4859,6 @@ void CreateAlterExceptionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc { Attachment* const attachment = transaction->getAttachment(); const string& userName = attachment->att_user->usr_user_name; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_EXCEPTION, name); @@ -4811,6 +4949,12 @@ void DropExceptionNode::print(string& text) const name.c_str()); } +bool DropExceptionNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_exception(tdbb, name, SCL_drop); + return true; +} + void DropExceptionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -4869,6 +5013,15 @@ void CreateAlterSequenceNode::print(string& text) const name.c_str()); } +bool CreateAlterSequenceNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + if (alter) + SCL_check_generator(tdbb, name, SCL_alter); + else + SCL_check_create_access(tdbb, SCL_object_generator); + return true; +} + void CreateAlterSequenceNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -5116,6 +5269,12 @@ void DropSequenceNode::print(string& text) const name.c_str()); } +bool DropSequenceNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_generator(tdbb, name, SCL_drop); + return true; +} + void DropSequenceNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { // run all statements under savepoint control @@ -6552,6 +6711,12 @@ void CreateRelationNode::print(string& text) const name.c_str()); } +bool CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_create_access(tdbb, SCL_object_table); + return true; +} + void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -6676,11 +6841,30 @@ void AlterRelationNode::print(string& text) const name.c_str()); } +bool AlterRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); + return true; +} + void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { saveRelation(tdbb, dsqlScratch, name, false, false); + dsql_rel* relation; + relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name); + + if (!relation || (relation && (relation->rel_flags & REL_view))) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_table_not_found) << name); + } + if (!dsqlScratch->relation) { //// TODO: @@ -7353,6 +7537,17 @@ void DropRelationNode::print(string& text) const name.c_str()); } +bool DropRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + if (view) + SCL_check_view(tdbb, &dscName, SCL_drop); + else + SCL_check_relation(tdbb, &dscName, SCL_drop); + return true; +} + void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -7587,6 +7782,17 @@ DdlNode* CreateAlterViewNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return DdlNode::dsqlPass(dsqlScratch); } +bool CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + if (alter) + SCL_check_view(tdbb, &dscName, SCL_alter); + else + SCL_check_create_access(tdbb, SCL_object_view); + return true; +} + void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -7642,7 +7848,6 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra REL.RDB$VIEW_BLR NOT MISSING { found = true; - MODIFY REL attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source); attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, @@ -8709,6 +8914,15 @@ void CreateIndexNode::print(string& text) const name.c_str()); } +bool CreateIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + const MetaName &relationName = relation->dsqlName; + dscName.makeText(relationName.length(), CS_METADATA, (UCHAR*)relationName.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); + return true; +} + // Define an index. void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -8768,6 +8982,32 @@ void AlterIndexNode::print(string& text) const name.c_str(), active); } +bool AlterIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + MetaName relationName; + + AutoCacheRequest request(tdbb, drq_l_index_relname, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES + WITH IDX.RDB$INDEX_NAME EQ name.c_str() + { + relationName = IDX.RDB$RELATION_NAME; + } + END_FOR + + if (relationName.isEmpty()) + { + // msg 48: "Index not found" + status_exception::raise(Arg::PrivateDyn(48)); + } + + dscName.makeText(relationName.length(), CS_METADATA, (UCHAR*)relationName.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); + return true; +} + void AlterIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { // run all statements under savepoint control @@ -8814,6 +9054,32 @@ void SetStatisticsNode::print(string& text) const name.c_str()); } +bool SetStatisticsNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + MetaName relationName; + + AutoCacheRequest request(tdbb, drq_l_index_relname, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES + WITH IDX.RDB$INDEX_NAME EQ name.c_str() + { + relationName = IDX.RDB$RELATION_NAME; + } + END_FOR + + if (relationName.isEmpty()) + { + // msg 48: "Index not found" + status_exception::raise(Arg::PrivateDyn(48)); + } + + dscName.makeText(relationName.length(), CS_METADATA, (UCHAR*)relationName.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); + return true; +} + void SetStatisticsNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { // run all statements under savepoint control @@ -8879,6 +9145,32 @@ void DropIndexNode::print(string& text) const name.c_str()); } +bool DropIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + MetaName relationName; + + AutoCacheRequest request(tdbb, drq_l_index_relname, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES + WITH IDX.RDB$INDEX_NAME EQ name.c_str() + { + relationName = IDX.RDB$RELATION_NAME; + } + END_FOR + + if (relationName.isEmpty()) + { + // msg 48: "Index not found" + status_exception::raise(Arg::PrivateDyn(48)); + } + + dscName.makeText(relationName.length(), CS_METADATA, (UCHAR*)relationName.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); + return true; +} + void DropIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { // run all statements under savepoint control @@ -8929,6 +9221,12 @@ void CreateFilterNode::print(string& text) const name.c_str()); } +bool CreateFilterNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_create_access(tdbb, SCL_object_filter); + return true; +} + // Define a blob filter. void CreateFilterNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* transaction) { @@ -8994,6 +9292,12 @@ void DropFilterNode::print(string& text) const name.c_str()); } +bool DropFilterNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_filter(tdbb, name, SCL_drop); + return true; +} + void DropFilterNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* transaction) { // run all statements under savepoint control @@ -9032,11 +9336,14 @@ void CreateShadowNode::print(string& text) const number); } -void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* transaction) +bool CreateShadowNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - if (!tdbb->getAttachment()->locksmith()) - status_exception::raise(Arg::Gds(isc_adm_task_denied)); + SCL_check_database(tdbb, SCL_alter); + return true; +} +void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* transaction) +{ // Should be caught by the parser. if (number == 0) { @@ -9097,11 +9404,14 @@ void DropShadowNode::print(string& text) const number); } -void DropShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* transaction) +bool DropShadowNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - if (!tdbb->getAttachment()->locksmith()) - status_exception::raise(Arg::Gds(isc_adm_task_denied)); + SCL_check_database(tdbb, SCL_alter); + return true; +} +void DropShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* transaction) +{ // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); @@ -9132,6 +9442,12 @@ void CreateRoleNode::print(string& text) const name.c_str()); } +bool CreateRoleNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_create_access(tdbb, SCL_object_role); + return true; +} + void CreateRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { MetaName ownerName(tdbb->getAttachment()->att_user->usr_user_name); @@ -9269,6 +9585,12 @@ void MappingNode::addItem(string& ddl, const char* text) ddl += '"'; } +bool MappingNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + //DDL_TODO + return false; +} + // It's purpose is to add/drop mapping from any security name to DB security object. void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -9660,6 +9982,12 @@ void DropRoleNode::print(string& text) const name.c_str()); } +bool DropRoleNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_role(tdbb, name, SCL_drop); + return true; +} + void DropRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { MetaName user(tdbb->getAttachment()->att_user->usr_user_name); @@ -9678,7 +10006,7 @@ void DropRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jr executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_ROLE, name); - const MetaName roleOwner(ROL.RDB$OWNER_NAME); + if (ROL.RDB$SYSTEM_FLAG != 0) { @@ -9686,28 +10014,21 @@ void DropRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jr status_exception::raise(Arg::PrivateDyn(284) << name); } - if (tdbb->getAttachment()->locksmith() || roleOwner == user) - { - AutoCacheRequest request2(tdbb, drq_del_role_1, DYN_REQUESTS); - - // The first OR clause finds all members of the role. - // The 2nd OR clause finds all privileges granted to the role - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - PRIV IN RDB$USER_PRIVILEGES - WITH (PRIV.RDB$RELATION_NAME EQ name.c_str() AND PRIV.RDB$OBJECT_TYPE = obj_sql_role) OR - (PRIV.RDB$USER EQ name.c_str() AND PRIV.RDB$USER_TYPE = obj_sql_role) - { - ERASE PRIV; - } - END_FOR + + AutoCacheRequest request2(tdbb, drq_del_role_1, DYN_REQUESTS); - ERASE ROL; - } - else + // The first OR clause finds all members of the role. + // The 2nd OR clause finds all privileges granted to the role + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + PRIV IN RDB$USER_PRIVILEGES + WITH (PRIV.RDB$RELATION_NAME EQ name.c_str() AND PRIV.RDB$OBJECT_TYPE = obj_sql_role) OR + (PRIV.RDB$USER EQ name.c_str() AND PRIV.RDB$USER_TYPE = obj_sql_role) { - // msg 191: "only owner of SQL role or USR_locksmith could drop SQL role" - status_exception::raise(Arg::PrivateDyn(191)); + ERASE PRIV; } + END_FOR + + ERASE ROL; found = true; } @@ -9759,6 +10080,13 @@ static void setCharField(Auth::CharField& field, const string* value) } +bool CreateAlterUserNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + //DDL_TODO + return false; +} + + void CreateAlterUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { if (mode != USER_ADD && !password && !firstName && !middleName && !lastName && @@ -9872,6 +10200,12 @@ void DropUserNode::print(string& text) const name.c_str()); } +bool DropUserNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + //DDL_TODO + return false; +} + void DropUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { // run all statements under savepoint control @@ -9912,6 +10246,12 @@ void GrantRevokeNode::print(string& text) const isGrant); } +bool GrantRevokeNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + // GRANT OPTION will be checked in grantRevoke method + return false; +} + void GrantRevokeNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { // run all statements under savepoint control @@ -10033,6 +10373,26 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G break; } + if (options == 1) { // with grant option + switch (userType) { + case obj_procedure: { + ERRD_post(Arg::Gds(isc_dsql_cant_grant_option) << Arg::Str("procedures")); + break; + } + case obj_trigger: { + ERRD_post(Arg::Gds(isc_dsql_cant_grant_option) << Arg::Str("triggers")); + break; + } + case obj_view: { + ERRD_post(Arg::Gds(isc_dsql_cant_grant_option) << Arg::Str("views")); + break; + } + default: { + break; + } + } + } + MetaName grantorRevoker(grantor ? *grantor : tdbb->getAttachment()->att_user->usr_user_name); @@ -10171,6 +10531,10 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G tdbb->getAttachment()->att_user->usr_user_name.c_str(), priv, objName, field, true); } + else if (objType >= obj_database) { + checkGrantorCanGrantDdl(tdbb, transaction, + tdbb->getAttachment()->att_user->usr_user_name.c_str(), priv, objName); + } } storePrivilege(tdbb, transaction, objName, user, field, pr, userType, objType, @@ -10506,6 +10870,35 @@ void GrantRevokeNode::checkGrantorCanGrantRole(thread_db* tdbb, jrd_tra* transac } } +// Check if the grantor has grant option on DDL privilege +void GrantRevokeNode::checkGrantorCanGrantDdl(thread_db* tdbb, jrd_tra* transaction, + const Firebird::MetaName& grantor, const char* privilege, const Firebird::MetaName& objName) +{ + if (tdbb->getAttachment()->locksmith()) + return; + + AutoCacheRequest request(tdbb, drq_l_grant_option, DYN_REQUESTS); + bool grantable = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + PRV IN RDB$USER_PRIVILEGES WITH + PRV.RDB$USER = UPPERCASE(grantor.c_str()) AND + PRV.RDB$USER_TYPE = obj_user AND + PRV.RDB$RELATION_NAME EQ objName.c_str() AND + PRV.RDB$OBJECT_TYPE >= obj_database AND + PRV.RDB$PRIVILEGE EQ privilege + { + grantable = PRV.RDB$GRANT_OPTION == 1; + } + END_FOR + + if (!grantable) + { + // no .. privilege with grant option on DDL .. + status_exception::raise(Arg::PrivateDyn(174) << privilege << objName.c_str()); + } +} + void GrantRevokeNode::storePrivilege(thread_db* tdbb, jrd_tra* transaction, const MetaName& object, const MetaName& user, const MetaName& field, const TEXT* privilege, SSHORT userType, SSHORT objType, int option, const MetaName& grantor) @@ -10582,6 +10975,12 @@ void AlterDatabaseNode::print(string& text) const "AlterDatabaseNode\n"); } +bool AlterDatabaseNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_database(tdbb, SCL_alter); + return true; +} + void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 5553200c953..8ae6f086c60 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -153,6 +153,11 @@ class RecreateNode : public DdlNode text.printf("RecreateNode\n"); } + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction) + { + return dropNode.checkPermission(tdbb, transaction) && createNode->checkPermission(tdbb, transaction); + } + virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { // run all statements under savepoint control @@ -196,6 +201,7 @@ class AlterCharSetNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -226,6 +232,7 @@ class CommentOnNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -275,6 +282,7 @@ class CreateAlterFunctionNode : public DdlNode public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -334,6 +342,7 @@ class AlterExternalFunctionNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -366,6 +375,7 @@ class DropFunctionNode : public DdlNode public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -410,6 +420,7 @@ class CreateAlterProcedureNode : public DdlNode public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -467,6 +478,7 @@ class DropProcedureNode : public DdlNode public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -551,6 +563,7 @@ class CreateAlterTriggerNode : public DdlNode, public TriggerDefinition public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -624,6 +637,7 @@ class DropTriggerNode : public DdlNode public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -663,6 +677,7 @@ class CreateCollationNode : public DdlNode public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); void setAttribute(USHORT attribute) @@ -721,6 +736,7 @@ class DropCollationNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -747,6 +763,7 @@ class CreateDomainNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -785,6 +802,7 @@ class AlterDomainNode : public DdlNode const Firebird::MetaName& newFieldName); virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -822,6 +840,7 @@ class DropDomainNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -853,6 +872,7 @@ class CreateAlterExceptionNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -889,6 +909,7 @@ class DropExceptionNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -940,6 +961,7 @@ class CreateAlterSequenceNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) @@ -982,6 +1004,7 @@ class DropSequenceNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1335,6 +1358,7 @@ class CreateRelationNode : public RelationNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1362,6 +1386,7 @@ class AlterRelationNode : public RelationNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1392,6 +1417,7 @@ class DropRelationNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1429,6 +1455,7 @@ class CreateAlterViewNode : public RelationNode public: virtual void print(Firebird::string& text) const; virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1510,6 +1537,7 @@ class CreateIndexNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1540,6 +1568,7 @@ class AlterIndexNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1565,6 +1594,7 @@ class SetStatisticsNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1593,6 +1623,7 @@ class DropIndexNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1640,6 +1671,7 @@ class CreateFilterNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1668,6 +1700,7 @@ class DropFilterNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1695,6 +1728,7 @@ class CreateShadowNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1723,6 +1757,7 @@ class DropShadowNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1747,6 +1782,7 @@ class CreateRoleNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1787,6 +1823,7 @@ class MappingNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1825,6 +1862,7 @@ class DropRoleNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1857,6 +1895,7 @@ class CreateAlterUserNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1915,6 +1954,7 @@ class DropUserNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1942,12 +1982,14 @@ class GrantRevokeNode : public DdlNode object(NULL), users(p), grantAdminOption(false), - grantor(NULL) + grantor(NULL), + isDdl(false) { } public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -1966,6 +2008,8 @@ class GrantRevokeNode : public DdlNode const Firebird::MetaName& fieldName, bool topLevel); static void checkGrantorCanGrantRole(thread_db* tdbb, jrd_tra* transaction, const Firebird::MetaName& grantor, const Firebird::MetaName& roleName); + static void checkGrantorCanGrantDdl(thread_db* tdbb, jrd_tra* transaction, + const Firebird::MetaName& grantor, const char* privilege, const Firebird::MetaName& objName); static void storePrivilege(thread_db* tdbb, jrd_tra* transaction, const Firebird::MetaName& object, const Firebird::MetaName& user, const Firebird::MetaName& field, const TEXT* privilege, SSHORT userType, @@ -1987,6 +2031,10 @@ class GrantRevokeNode : public DdlNode case 'G': return "Usage"; case 'M': return "Role"; case 'R': return "Reference"; + //ddl + case 'C': return "Create"; + case 'L': return "Alter"; + case 'O': return "DROP"; } return ""; @@ -2000,6 +2048,8 @@ class GrantRevokeNode : public DdlNode Firebird::Array users; bool grantAdminOption; NestConst grantor; + // ddl rights + bool isDdl; }; @@ -2038,6 +2088,7 @@ class AlterDatabaseNode : public DdlNode } virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 5d8da231125..d44df357070 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -152,6 +152,23 @@ class Node : public Firebird::PermanentStorage }; +class DdlNode; + +class SecureDdlNodeExecute +{ +public: + explicit SecureDdlNodeExecute(thread_db* tdbb, DdlNode* ddlNode, + DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); + + ~SecureDdlNodeExecute() + { + _tdbb->tdbb_flags &= ~TDBB_trusted_ddl; + } + +private: + thread_db* _tdbb; +}; + class DdlNode : public Node { public: @@ -167,6 +184,11 @@ class DdlNode : public Node const Firebird::MetaName& name, int type, const char* privileges); public: + // Check permission on DDL operation. Return true if everything is OK. + // Raise an exception for bad permission. + // If returns false permissions will be check in old style at vio level as well as while direct RDB$ tables modify. + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction) = 0; + // Set the scratch's transaction when executing a node. Fact of accessing the scratch during // execution is a hack. void executeDdl(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) @@ -175,7 +197,7 @@ class DdlNode : public Node if (dsqlScratch) dsqlScratch->setTransaction(transaction); - execute(tdbb, dsqlScratch, transaction); + SecureDdlNodeExecute(tdbb, this, dsqlScratch, transaction); } virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index e64735ac7fa..ee59cfd648d 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -34,6 +34,7 @@ #include "../dsql/pass1_proto.h" #include "../common/StatusArg.h" #include "../jrd/Attachment.h" +#include "../jrd/scl_proto.h" using namespace Firebird; @@ -458,6 +459,18 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } +bool CreateAlterPackageNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + if (alter) + SCL_check_package(tdbb, &dscName, SCL_alter); + else + SCL_check_create_access(tdbb, SCL_object_package); + return true; +} + + void CreateAlterPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -619,6 +632,15 @@ void DropPackageNode::print(string& text) const } +bool DropPackageNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + SCL_check_package(tdbb, &dscName, SCL_drop); + return true; +} + + void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -838,6 +860,13 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } +bool CreatePackageBodyNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_create_access(tdbb, SCL_object_package); + return true; +} + + void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { @@ -1063,6 +1092,15 @@ void DropPackageBodyNode::print(string& text) const } +bool DropPackageBodyNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*)name.c_str()); + SCL_check_package(tdbb, &dscName, SCL_drop); + return true; +} + + void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 5be4be74ea7..59a013aede6 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -84,6 +84,7 @@ class CreateAlterPackageNode : public DdlNode public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -126,6 +127,7 @@ class DropPackageNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -160,6 +162,7 @@ class CreatePackageBodyNode : public DdlNode public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: @@ -191,6 +194,7 @@ class DropPackageBodyNode : public DdlNode public: virtual void print(Firebird::string& text) const; + virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: diff --git a/src/dsql/parse.y b/src/dsql/parse.y index f0a9d7e6c82..7ee5cc5c32d 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -838,6 +838,14 @@ grant0($node) $node->grantAdminOption = $7; $node->grantor = $8; } + | ddl_privileges(NOTRIAL(&$node->privileges)) object + TO non_role_grantee_list(NOTRIAL(&$node->users)) grant_option granted_by + { + $node->object = $2; + $node->grantAdminOption = $5; + $node->grantor = $6; + $node->isDdl = true; + } | role_name_list(NOTRIAL(&$node->roles)) TO role_grantee_list(NOTRIAL(&$node->users)) role_admin_option granted_by { @@ -846,6 +854,39 @@ grant0($node) } ; +%type object +object : TABLE + { $$ = newNode(obj_relations, get_object_name(obj_relations)); } + | VIEW + { $$ = newNode(obj_views, get_object_name(obj_views)); } + | PROCEDURE + { $$ = newNode(obj_procedures, get_object_name(obj_procedures)); } + | FUNCTION + { $$ = newNode(obj_functions, get_object_name(obj_functions)); } + | PACKAGE + { $$ = newNode(obj_packages, get_object_name(obj_packages)); } + | GENERATOR + { $$ = newNode(obj_generators, get_object_name(obj_generators)); } + | SEQUENCE + { $$ = newNode(obj_generators, get_object_name(obj_generators)); } + | KW_DOMAIN + { $$ = newNode(obj_domains, get_object_name(obj_domains)); } + | EXCEPTION + { $$ = newNode(obj_exceptions, get_object_name(obj_exceptions)); } + | ROLE + { $$ = newNode(obj_roles, get_object_name(obj_roles)); } + | SHADOW + { $$ = newNode(obj_shadows, get_object_name(obj_shadows)); } + | DATABASE + { $$ = newNode(obj_database, get_object_name(obj_database)); } + | CHARACTER SET + { $$ = newNode(obj_charsets, get_object_name(obj_charsets)); } + | COLLATION + { $$ = newNode(obj_collations, get_object_name(obj_collations)); } + | FILTER + { $$ = newNode(obj_filters, get_object_name(obj_filters)); } + ; + table_noise : // nothing | TABLE @@ -872,7 +913,6 @@ execute_privilege($privilegeArray) %type usage_privilege() usage_privilege($privilegeArray) : USAGE { $privilegeArray->add(PrivilegeClause('G', NULL)); } - ; %type privilege() privilege($privilegeArray) @@ -883,6 +923,26 @@ privilege($privilegeArray) | REFERENCES column_parens_opt { $privilegeArray->add(PrivilegeClause('R', $2)); } ; +%type ddl_privileges() +ddl_privileges($privilegeArray) + : ALL { $privilegeArray->add(PrivilegeClause('C', NULL)); $privilegeArray->add(PrivilegeClause('L', NULL)); $privilegeArray->add(PrivilegeClause('O', NULL)); } + | ALL PRIVILEGES { $privilegeArray->add(PrivilegeClause('C', NULL)); $privilegeArray->add(PrivilegeClause('L', NULL)); $privilegeArray->add(PrivilegeClause('O', NULL)); } + | ddl_privilege_list($privilegeArray) + ; + +%type ddl_privilege_list() +ddl_privilege_list($privilegeArray) + : ddl_privilege($privilegeArray) + | ddl_privilege_list ',' ddl_privilege($privilegeArray) + ; + +%type ddl_privilege() +ddl_privilege($privilegeArray) + : CREATE { $privilegeArray->add(PrivilegeClause('C', NULL)); } + | ALTER ANY { $privilegeArray->add(PrivilegeClause('L', NULL)); } + | DROP ANY { $privilegeArray->add(PrivilegeClause('O', NULL)); } + ; + %type grant_option grant_option : /* nothing */ { $$ = false; } @@ -996,6 +1056,14 @@ revoke0($node) $node->grantAdminOption = $1; $node->grantor = $8; } + | rev_grant_option ddl_privileges(NOTRIAL(&$node->privileges)) object + FROM non_role_grantee_list(NOTRIAL(&$node->users)) granted_by + { + $node->object = $3; + $node->grantAdminOption = $1; + $node->grantor = $6; + $node->isDdl = true; + } | rev_admin_option role_name_list(NOTRIAL(&$node->roles)) FROM role_grantee_list(NOTRIAL(&$node->users)) granted_by { diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index e777e17217e..9c2c992c4a4 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -795,6 +795,8 @@ static const struct { {"set_invalid_role", 335545091}, {"cursor_not_positioned", 335545092}, {"dup_attribute", 335545093}, + {"dyn_no_priv", 335545094}, + {"dsql_cant_grant_option", 335545095}, {"gfix_db_name", 335740929}, {"gfix_invalid_sw", 335740930}, {"gfix_incmp_sw", 335740932}, diff --git a/src/include/gen/iberror.h b/src/include/gen/iberror.h index 5755990929a..c40d5432715 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -829,6 +829,8 @@ const ISC_STATUS isc_miss_trusted_role = 335545090L; const ISC_STATUS isc_set_invalid_role = 335545091L; const ISC_STATUS isc_cursor_not_positioned = 335545092L; const ISC_STATUS isc_dup_attribute = 335545093L; +const ISC_STATUS isc_dyn_no_priv = 335545094L; +const ISC_STATUS isc_dsql_cant_grant_option = 335545095L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1287,7 +1289,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 = 1231; +const ISC_STATUS isc_err_max = 1233; #else /* c definitions */ @@ -2086,6 +2088,8 @@ const ISC_STATUS isc_err_max = 1231; #define isc_set_invalid_role 335545091L #define isc_cursor_not_positioned 335545092L #define isc_dup_attribute 335545093L +#define isc_dyn_no_priv 335545094L +#define isc_dsql_cant_grant_option 335545095L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2544,7 +2548,7 @@ const ISC_STATUS isc_err_max = 1231; #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 1231 +#define isc_err_max 1233 #endif diff --git a/src/include/gen/ids.h b/src/include/gen/ids.h index ce06ed20b64..8716b42a378 100644 --- a/src/include/gen/ids.h +++ b/src/include/gen/ids.h @@ -277,6 +277,7 @@ const USHORT f_flt_input = 4; const USHORT f_flt_output = 5; const USHORT f_flt_sys_flag = 6; + const USHORT f_flt_class = 7; // Relation 17 (RDB$TRIGGER_MESSAGES) @@ -449,6 +450,7 @@ const USHORT f_rol_owner = 1; const USHORT f_rol_desc = 2; const USHORT f_rol_sys_flag = 3; + const USHORT f_rol_class = 4; // Relation 32 (RDB$BACKUP_HISTORY) diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 6bd5e9e15a8..bac1e921b3c 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -798,6 +798,8 @@ Data source : @4"}, /* eds_statement */ {335545091, "Role @1 is invalid or unavailable"}, /* set_invalid_role */ {335545092, "Cursor @1 is not positioned in a valid record"}, /* cursor_not_positioned */ {335545093, "Duplicated user attribute @1"}, /* dup_attribute */ + {335545094, "There is no privilege for this operation"}, /* dyn_no_priv */ + {335545095, "Using GRANT OPTION on @1 not allowed"}, /* dsql_cant_grant_option */ {335740929, "data base file name (@1) already given"}, /* gfix_db_name */ {335740930, "invalid switch @1"}, /* gfix_invalid_sw */ {335740932, "incompatible switch combination"}, /* gfix_incmp_sw */ diff --git a/src/include/gen/sql_code.h b/src/include/gen/sql_code.h index bf021bb6a4b..ba3259e1f02 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -794,6 +794,8 @@ static const struct { {335545091, -901}, /* 771 set_invalid_role */ {335545092, -596}, /* 772 cursor_not_positioned */ {335545093, -901}, /* 773 dup_attribute */ + {335545094, -901}, /* 774 dyn_no_priv */ + {335545095, -901}, /* 775 dsql_cant_grant_option */ {335740929, -901}, /* 1 gfix_db_name */ {335740930, -901}, /* 2 gfix_invalid_sw */ {335740932, -901}, /* 4 gfix_incmp_sw */ diff --git a/src/include/gen/sql_state.h b/src/include/gen/sql_state.h index 24d2d0aad59..610bfecf7bd 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -794,6 +794,8 @@ static const struct { {335545091, "0P000"}, // 771 set_invalid_role {335545092, "HY109"}, // 772 cursor_not_positioned {335545093, "42702"}, // 773 dup_attribute + {335545094, "42000"}, // 774 dyn_no_priv + {335545095, "42000"}, // 775 dsql_cant_grant_option {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/jrd/Database.h b/src/jrd/Database.h index d064decb695..6fff20932c7 100644 --- a/src/jrd/Database.h +++ b/src/jrd/Database.h @@ -411,7 +411,6 @@ class Database : public pool_alloc Firebird::PathName dbb_filename; // filename string Firebird::PathName dbb_database_name; // database ID (file name or alias) - Firebird::MetaName dbb_owner; // database owner Firebird::SyncObject dbb_pools_sync; @@ -494,6 +493,7 @@ class Database : public pool_alloc dbb_modules(*p), dbb_extManager(*p), dbb_filename(*p), + dbb_owner(*p), dbb_database_name(*p), dbb_pools(*p, 4), dbb_sort_buffers(*p), diff --git a/src/jrd/acl.h b/src/jrd/acl.h index 47d2388f760..54456c0d1dd 100644 --- a/src/jrd/acl.h +++ b/src/jrd/acl.h @@ -50,7 +50,10 @@ const int priv_references = 10; // REFERENCES for foreign key const int priv_execute = 11; // EXECUTE (procedure, function, package) // New in FB3 const int priv_usage = 12; // USAGE (domain, exception, sequence, collation) -const int priv_max = 13; +const int priv_create = 13; // Create object +const int priv_alter_any = 14; // Alter any object +const int priv_drop_any = 15; // Drop any object +const int priv_max = 16; // Identification criterias @@ -69,7 +72,8 @@ const int id_sql_role = 11; // SQL role // New in FB3 const int id_package = 12; // Package name const int id_function = 13; // Function name -const int id_max = 14; +const int id_filter = 14; // Filter name +const int id_max = 15; /* Format of access control list: diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 0caae818501..39d33317f23 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -132,6 +132,8 @@ const char* const SQL_SECCLASS_PREFIX = "SQL$"; const int SQL_SECCLASS_PREFIX_LEN = 4; const char* const SQL_FLD_SECCLASS_PREFIX = "SQL$GRANT"; const int SQL_FLD_SECCLASS_PREFIX_LEN = 9; +const char* const GEN_SECCLASS_PREFIX = "GEN$"; +const int GEN_SECCLASS_PREFIX_LEN = 4; // Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations. const char* const IMPLICIT_INTEGRITY_PREFIX = "INTEG_"; diff --git a/src/jrd/drq.h b/src/jrd/drq.h index 9fe4d34d6fb..3802bae5939 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -232,6 +232,9 @@ enum drq_type_t drq_e_gen_prvs, // erase generator privileges drq_e_gfld_prvs, // erase domain privileges drq_g_nxt_nbakhist_id, // generate next history ID for nbackup + drq_l_index_relname, // lookup relation name for index + drq_l_trigger_relname, // loopup relation name for trigger + drq_l_grant_option, // loopup grant option for privilege drq_MAX }; diff --git a/src/jrd/filters.cpp b/src/jrd/filters.cpp index 88f349669c4..224d9f4d8db 100644 --- a/src/jrd/filters.cpp +++ b/src/jrd/filters.cpp @@ -94,7 +94,10 @@ static const TEXT* acl_privs[priv_max] = "update", "references", "execute", - "usage" + "usage", + "create", + "alter_any", + "drop_any" }; static const TEXT* acl_ids[id_max] = diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index 8a3c24d5693..5946b00e4c0 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -141,7 +141,11 @@ void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, break; default: - break; + if (id >= obj_database && + id < obj_type_MAX) + { + priv = OWNER_PRIVS; + } } grant_user(acl, owner, obj_user, priv); @@ -472,6 +476,20 @@ static void get_object_info(thread_db* tdbb, } END_FOR } + else if (obj_type == obj_database) + { + s_class = attachment->att_security_class->scl_name; + default_class = ""; + owner = tdbb->getDatabase()->dbb_owner; + view = false; + } + else + { + s_class = get_object_name(obj_type); + default_class = ""; + owner = tdbb->getDatabase()->dbb_owner; + view = false; + } } @@ -882,6 +900,15 @@ static SecurityClass::flags_t trans_sql_priv(const TEXT* privileges) case 'G': priv |= SCL_usage; break; + case 'C': + priv |= SCL_create; + break; + case 'L': + priv |= SCL_alter; + break; + case 'O': + priv |= SCL_drop; + break; } return priv; diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index c5e9805255d..77bbf4d98e8 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -72,6 +72,7 @@ const int FB_MAX_ACL_SIZE = 4096; static void add_index_set(thread_db*); static void add_security_to_sys_obj(thread_db*, const MetaName&, USHORT, const MetaName&, USHORT = 0, const UCHAR* = NULL); +static void add_security_class(thread_db* tdbb, const MetaName& class_name, USHORT acl_length, const UCHAR* acl); static void add_security_to_sys_rel(thread_db*, const MetaName&, const TEXT*, const USHORT, const UCHAR*); static void store_generator(thread_db*, const gen*, AutoRequest&, const MetaName&); @@ -477,6 +478,13 @@ void INI_format(const MetaName& owner, const MetaName& charset) add_security_to_sys_obj(tdbb, ownerName, obj_collation, collation->name, length, buffer); } + for (int ddl_obj = obj_database + 1; ddl_obj < obj_type_MAX; ddl_obj++) + { + add_security_class(tdbb, get_object_name(ddl_obj), length, buffer); + } + + add_security_to_sys_obj(tdbb, ownerName, obj_database, "", length, buffer); + // Add security on system tables const UCHAR REL_OWNER_ACL[] = @@ -1137,6 +1145,17 @@ static void add_security_to_sys_obj(thread_db* tdbb, } END_FOR } + else if (obj_type == obj_database) + { + FOR(REQUEST_HANDLE handle) DB IN RDB$DATABASE + { + MODIFY DB USING + DB.RDB$SECURITY_CLASS.NULL = FALSE; + PAD(security_class.c_str(), DB.RDB$SECURITY_CLASS); + END_MODIFY + } + END_FOR + } handle.reset(); @@ -1155,6 +1174,30 @@ static void add_security_to_sys_obj(thread_db* tdbb, } +// Add security class. +static void add_security_class(thread_db* tdbb, const MetaName& class_name, USHORT acl_length, const UCHAR* acl) +{ + SET_TDBB(tdbb); + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + bid blob_id; + attachment->storeBinaryBlob(tdbb, attachment->getSysTransaction(), &blob_id, + ByteChunk(acl, acl_length)); + + AutoRequest handle; + + STORE(REQUEST_HANDLE handle) + CLS IN RDB$SECURITY_CLASSES + { + PAD(class_name.c_str(), CLS.RDB$SECURITY_CLASS); + CLS.RDB$ACL = blob_id; + } + END_STORE + + handle.reset(); +} + + static void store_generator(thread_db* tdbb, const gen* generator, AutoRequest& handle, const MetaName& owner) { diff --git a/src/jrd/intl.h b/src/jrd/intl.h index ab66f0a66e8..caaa032912d 100644 --- a/src/jrd/intl.h +++ b/src/jrd/intl.h @@ -70,6 +70,9 @@ #define COLLATE_NONE 0 // No special collation, use codepoint order +#define INTL_ASSIGN_DSC(dsc, cs, coll) \ + { (dsc)->dsc_sub_type = (SSHORT) ((coll) << 8 | (cs)); } + #define INTL_GET_TTYPE(dsc) \ ((dsc)->dsc_sub_type) diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 63ed88d08d4..e4d87a121e6 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -369,6 +369,7 @@ const USHORT TDBB_use_db_page_space = 256; // use database (not temporary) page const USHORT TDBB_detaching = 512; // detach is in progress const USHORT TDBB_wait_cancel_disable = 1024; // don't cancel current waiting operation const USHORT TDBB_cache_unwound = 2048; // page cache was unwound +const USHORT TDBB_trusted_ddl = 4096; // skip DDL permission checks. set after DDL permission check and clear after DDL execution class thread_db : public Firebird::ThreadData { diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 18ed4ff8f03..e70e5e6d12d 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -1043,7 +1043,7 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc { relation_name.makeText(sizeof(X.RDB$RELATION_NAME), CS_METADATA, (UCHAR*) X.RDB$RELATION_NAME); - SCL_check_relation(tdbb, &relation_name, SCL_control); + SCL_check_relation(tdbb, &relation_name, SCL_alter); dw = DFW_post_work(transaction, dfw_update_format, &relation_name, 0); AutoCacheRequest request2(tdbb, irq_m_fields4, IRQ_REQUESTS); diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 7644af17809..42fd8397840 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -48,12 +48,62 @@ const int obj_collation = 17; const int obj_package_header = 18; const int obj_package_body = 19; -const int obj_type_MAX = 20; // keep this last! +//objects types for ddl operations +const int obj_database = 20; +const int obj_relations = 21; +const int obj_views = 22; +const int obj_procedures = 23; +const int obj_functions = 24; +const int obj_packages = 25; +const int obj_generators = 26; +const int obj_domains = 27; +const int obj_exceptions = 28; +const int obj_roles = 29; +const int obj_shadows = 30; +const int obj_charsets = 31; +const int obj_collations = 32; +const int obj_filters = 33; + +const int obj_type_MAX = 34; // keep this last! // used in the parser only / no relation with obj_type_MAX -const int obj_user_or_role = 20; -const int obj_database = 21; -const int obj_schema = 22; -const int obj_parameter = 23; +const int obj_user_or_role = 35; +const int obj_schema = 36; +const int obj_parameter = 37; + +static char* get_object_name(int object_type) +{ + switch (object_type) + { + case obj_relations: + return "OBJ$TABLES"; + case obj_views: + return "OBJ$VIEWS"; + case obj_procedures: + return "OBJ$PROCEDURES"; + case obj_functions: + return "OBJ$FUNCTIONS"; + case obj_packages: + return "OBJ$PACKAGES"; + case obj_generators: + return "OBJ$GENERATORS"; + case obj_filters: + return "OBJ$FILTERS"; + case obj_domains: + return "OBJ$DOMAINS"; + case obj_exceptions: + return "OBJ$EXCEPTIONS"; + case obj_roles: + return "OBJ$ROLES"; + case obj_shadows: + return "OBJ$SHADOWS"; + case obj_charsets: + return "OBJ$CHARSETS"; + case obj_collations: + return "OBJ$COLLATIONS"; + default: + return ""; + } +} #endif // JRD_OBJ_H diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 14e885d876b..81d24f3e061 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -276,6 +276,7 @@ RELATION(nam_filters, rel_filters, ODS_8_0, rel_persistent) FIELD(f_flt_input, nam_in_type, fld_sub_type, 1, ODS_8_0) FIELD(f_flt_output, nam_out_type, fld_sub_type, 1, ODS_8_0) FIELD(f_flt_sys_flag, nam_sys_flag, fld_flag, 0, ODS_8_0) + FIELD(f_flt_class, nam_class, fld_class, 1, ODS_12_0) END_RELATION // Relation 17 (RDB$TRIGGER_MESSAGES) @@ -448,6 +449,7 @@ RELATION(nam_roles, rel_roles, ODS_9_0, rel_persistent) FIELD(f_rol_owner, nam_owner, fld_user, 1, ODS_9_0) FIELD(f_rol_desc, nam_description, fld_description, 1, ODS_11_0) FIELD(f_rol_sys_flag, nam_sys_flag, fld_flag, 1, ODS_11_0) + FIELD(f_rol_class, nam_class, fld_class, 1, ODS_12_0) END_RELATION // Relation 32 (RDB$BACKUP_HISTORY) diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 2f210e72518..78bc8954078 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -96,6 +96,7 @@ namespace { SCL_references, priv_references, "REFERENCES" }, { SCL_execute, priv_execute, "EXECUTE" }, { SCL_usage, priv_usage, "USAGE" }, + { SCL_create, priv_create, "CREATE" }, { 0, 0, "" } }; @@ -113,6 +114,9 @@ namespace const char* const object_str_domain = "DOMAIN"; const char* const object_str_exception = "EXCEPTION"; const char* const object_str_generator = "GENERATOR"; + const char* const object_str_view = "VIEW"; + const char* const object_str_role = "ROLE"; + const char* const object_str_filter = "FILTER"; struct SecObjectNamePriority @@ -135,6 +139,9 @@ namespace {object_str_domain, SCL_object_domain}, {object_str_exception, SCL_object_exception}, {object_str_generator, SCL_object_generator}, + {object_str_view, SCL_object_view}, + {object_str_role, SCL_object_role}, + {object_str_filter, SCL_object_filter}, {"", 0} }; @@ -189,6 +196,9 @@ void SCL_check_access(thread_db* tdbb, **************************************/ SET_TDBB(tdbb); + if (tdbb->tdbb_flags & TDBB_trusted_ddl) + return; + if (s_class && (s_class->scl_flags & SCL_corrupt)) { ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("(ACL unrecognized)") << @@ -209,27 +219,22 @@ void SCL_check_access(thread_db* tdbb, if (attachment.locksmith()) return; - bool denied_db = false; + // 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) + return; - const SecurityClass* const att_class = attachment.att_security_class; - if (att_class && !(att_class->scl_flags & mask)) - { - denied_db = true; + if (!s_class || (mask & s_class->scl_flags)) { + return; } - else + 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 (!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)) - { - return; - } + return; } // Allow recursive procedure/function call @@ -244,30 +249,37 @@ void SCL_check_access(thread_db* tdbb, const P_NAMES* names; for (names = p_names; names->p_names_priv; names++) - { if (names->p_names_priv & mask) - { break; - } - } - if (denied_db) - { - fb_assert(type == SCL_object_database); - ERR_post(Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << - Arg::Str(object_str_database) << - Arg::Str("")); - } - else - { - fb_assert(type != SCL_object_database); - const char* const typeAsStr = accTypeNumToStr(type); - const Firebird::string fullName = r_name.hasData() ? - r_name.c_str() + Firebird::string(".") + name.c_str() : name.c_str(); - ERR_post(Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << - Arg::Str(typeAsStr) << - Arg::Str(fullName)); - } + const char* const typeAsStr = accTypeNumToStr(type); + const Firebird::string fullName = r_name.hasData() ? + r_name.c_str() + Firebird::string(".") + name.c_str() : name.c_str(); + ERR_post(Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << + Arg::Str(typeAsStr) << + Arg::Str(fullName)); +} + + +void SCL_check_create_access(thread_db* tdbb, int type) +{ +/************************************** + * + * S C L _ c h e c k _ c r e a t e _ a c c e s s + * + ************************************** + * + * Functional description + * Check create access on a database object (DDL access) + * + **************************************/ + SET_TDBB(tdbb); + + const SecurityClass::flags_t obj_mask = SCL_get_object_mask(type); + if (SCL_create & obj_mask) + return; + + ERR_post(Arg::Gds(isc_dyn_no_priv)); } @@ -333,6 +345,43 @@ void SCL_check_collation(thread_db* tdbb, const MetaName& name, SecurityClass::f } +void SCL_check_database(thread_db* tdbb, SecurityClass::flags_t mask) +{ +/************************************** + * + * S C L _ c h e c k _ d a t a b a s e + * + ************************************** + * + * Functional description + * Check for a set of privileges of current database. + * + **************************************/ + SET_TDBB(tdbb); + + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + // Allow the locksmith any access to database + if (attachment->locksmith()) + return; + + const SecurityClass* const att_class = attachment->att_security_class; + if (att_class && (att_class->scl_flags & mask)) + return; + + const P_NAMES* names; + for (names = p_names; names->p_names_priv; names++) + { + if (names->p_names_priv & mask) + break; + } + + ERR_post(Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << + Arg::Str(object_str_database) << + Arg::Str("")); +} + + void SCL_check_domain(thread_db* tdbb, const MetaName& name, SecurityClass::flags_t mask) { /************************************** @@ -653,6 +702,40 @@ void SCL_check_function(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla } +void SCL_check_filter(thread_db* tdbb, const MetaName &name, SecurityClass::flags_t mask) +{ +/************************************** + * + * S C L _ c h e c k _ f i l t e r + * + ************************************** + * + * Functional description + * Given a filter name, check for a set of privileges. The + * filter in question may or may not have been created, let alone + * scanned. This is used exclusively for meta-data operations. + * + **************************************/ + SET_TDBB(tdbb); + + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + const SecurityClass* s_class = NULL; + AutoCacheRequest request(tdbb, irq_f_security, IRQ_REQUESTS); + + FOR (REQUEST_HANDLE request) + F IN RDB$FILTERS + WITH F.RDB$FUNCTION_NAME EQ name.c_str() + { + if (!F.RDB$SECURITY_CLASS.NULL) + s_class = SCL_get_class(tdbb, F.RDB$SECURITY_CLASS); + } + END_FOR + + SCL_check_access(tdbb, s_class, 0, id_filter, name, mask, SCL_object_filter, false, name); +} + + void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t mask) { /************************************** @@ -683,13 +766,85 @@ void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla WITH REL.RDB$RELATION_NAME EQ name.c_str() { if (!REL.RDB$SECURITY_CLASS.NULL) + { s_class = SCL_get_class(tdbb, REL.RDB$SECURITY_CLASS); + } } END_FOR SCL_check_access(tdbb, s_class, 0, 0, NULL, mask, SCL_object_table, false, name); } +void SCL_check_view(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t mask) +{ +/************************************** + * + * S C L _ c h e c k _ v i e w + * + ************************************** + * + * Functional description + * Given a view name, check for a set of privileges. The + * relation in question may or may not have been created, let alone + * scanned. This is used exclusively for meta-data operations. + * + **************************************/ + SET_TDBB(tdbb); + + // Get the name in CSTRING format, ending on NULL or SPACE + fb_assert(dsc_name->dsc_dtype == dtype_text); + const Firebird::MetaName name(reinterpret_cast(dsc_name->dsc_address), + dsc_name->dsc_length); + + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + const SecurityClass* s_class = NULL; + AutoCacheRequest request(tdbb, irq_v_security, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) REL IN RDB$RELATIONS + WITH REL.RDB$RELATION_NAME EQ name.c_str() + { + if (!REL.RDB$SECURITY_CLASS.NULL) + { + s_class = SCL_get_class(tdbb, REL.RDB$SECURITY_CLASS); + } + } + END_FOR + + SCL_check_access(tdbb, s_class, 0, 0, NULL, mask, SCL_object_view, false, name); +} + +void SCL_check_role(thread_db* tdbb, const Firebird::MetaName& name, SecurityClass::flags_t mask) +{ +/************************************** + * + * S C L _ c h e c k _ r o l e + * + ************************************** + * + * Functional description + * Given a role name, check for a set of privileges. + * + **************************************/ + SET_TDBB(tdbb); + + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + const SecurityClass* s_class = NULL; + AutoCacheRequest request(tdbb, irq_v_security, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) R IN RDB$ROLES + WITH R.RDB$ROLE_NAME EQ name.c_str() + { + if (!R.RDB$SECURITY_CLASS.NULL) + { + s_class = SCL_get_class(tdbb, R.RDB$SECURITY_CLASS); + } + } + END_FOR + + SCL_check_access(tdbb, s_class, 0, 0, NULL, mask, SCL_object_role, false, name); +} SecurityClass* SCL_get_class(thread_db* tdbb, const TEXT* par_string) { @@ -1087,6 +1242,60 @@ void SCL_release_all(SecurityClassList*& list) } +SecurityClass::flags_t SCL_get_object_mask(const int object_type) +{ +/************************************** + * + * S C L _ g e t _ o b j e c t _ m a s k + * + ************************************** + * + * Functional description + * Get a protection mask for database object. + * + **************************************/ + thread_db* tdbb = JRD_get_thread_data(); + Database* dbb = tdbb->getDatabase(); + + UserId* user = tdbb->getAttachment()->att_user; + + /*if (object_type == obj_roles) + { + if (user->isSecAdmin()) + return -1 & ~SCL_corrupt; + }*/ + + const TEXT* object_name = get_object_name(object_type); + const Jrd::SecurityClass* s_class = SCL_recompute_class(tdbb, object_name); + + if (s_class) + return s_class->scl_flags; + + return -1 & ~SCL_corrupt; +} + +void SCL_set_user(const Firebird::MetaName& user_name, UserId& user) +{ +/************************************** + * + * S C L _ s e t _ u s e r + * + ************************************** + * + * Functional description + * Set user attributes. + * + **************************************/ + thread_db* tdbb = JRD_get_thread_data(); + Database* dbb = tdbb->getDatabase(); + + user.usr_user_name = user_name.c_str(); + + if (!user.usr_user_name.compare(dbb->dbb_owner.c_str())) { + user.usr_flags |= USR_owner; + } +} + static bool check_number(const UCHAR* acl, USHORT number) { /************************************** @@ -1471,6 +1680,10 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, // unused break; + case priv_create: + privilege |= SCL_create; + break; + default: return SCL_corrupt; } diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 453eee0ce49..c789dfe8458 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -27,6 +27,7 @@ #include "../common/classes/MetaName.h" #include "../common/classes/tree.h" #include "../common/security.h" +#include "../jrd/obj.h" namespace Firebird { class ClumpletWriter; @@ -41,7 +42,7 @@ const size_t ACL_BLOB_BUFFER_SIZE = MAX_USHORT; // used to read/write acl blob class SecurityClass { public: - typedef USHORT flags_t; + typedef ULONG flags_t; SecurityClass(Firebird::MemoryPool &pool, const Firebird::MetaName& name) : scl_flags(0), scl_name(pool, name) @@ -76,7 +77,7 @@ const SecurityClass::flags_t SCL_update = 256; // UPDATE access const SecurityClass::flags_t SCL_references = 512; // REFERENCES access const SecurityClass::flags_t SCL_execute = 1024; // EXECUTE access const SecurityClass::flags_t SCL_usage = 2048; // USAGE access - +const SecurityClass::flags_t SCL_create = 4096; // information about the user @@ -158,17 +159,20 @@ class UserId // These numbers are arbitrary and only used at run-time. Can be changed if necessary at any moment. // We need to include here the new objects that accept ACLs. -const SLONG SCL_object_database = 1; -const SLONG SCL_object_table = 2; -const SLONG SCL_object_package = 3; -const SLONG SCL_object_procedure = 4; -const SLONG SCL_object_function = 5; -const SLONG SCL_object_column = 6; -const SLONG SCL_object_collation = 7; -const SLONG SCL_object_exception = 8; -const SLONG SCL_object_generator = 9; -const SLONG SCL_object_charset = 10; -const SLONG SCL_object_domain = 11; +const SLONG SCL_object_column = 1; +const SLONG SCL_object_database = obj_database; +const SLONG SCL_object_table = obj_relations; +const SLONG SCL_object_package = obj_packages; +const SLONG SCL_object_procedure = obj_procedures; +const SLONG SCL_object_function = obj_functions; +const SLONG SCL_object_collation = obj_collations; +const SLONG SCL_object_exception = obj_exceptions; +const SLONG SCL_object_generator = obj_generators; +const SLONG SCL_object_charset = obj_charsets; +const SLONG SCL_object_domain = obj_domains; +const SLONG SCL_object_view = obj_views; +const SLONG SCL_object_role = obj_roles; +const SLONG SCL_object_filter = obj_filters; } //namespace Jrd diff --git a/src/jrd/scl_proto.h b/src/jrd/scl_proto.h index 3a09407abf5..4f1551f3a3d 100644 --- a/src/jrd/scl_proto.h +++ b/src/jrd/scl_proto.h @@ -36,8 +36,10 @@ struct dsc; void SCL_check_access(Jrd::thread_db*, const Jrd::SecurityClass*, SLONG, 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); void SCL_check_charset(Jrd::thread_db* tdbb, const Firebird::MetaName&, Jrd::SecurityClass::flags_t); void SCL_check_collation(Jrd::thread_db* tdbb, const Firebird::MetaName&, Jrd::SecurityClass::flags_t); +void SCL_check_database(Jrd::thread_db* tdbb, Jrd::SecurityClass::flags_t mask); void SCL_check_domain(Jrd::thread_db* tdbb, const Firebird::MetaName&, Jrd::SecurityClass::flags_t); void SCL_check_exception(Jrd::thread_db* tdbb, const Firebird::MetaName&, Jrd::SecurityClass::flags_t); void SCL_check_generator(Jrd::thread_db* tdbb, const Firebird::MetaName&, Jrd::SecurityClass::flags_t); @@ -45,7 +47,10 @@ void SCL_check_index(Jrd::thread_db*, const Firebird::MetaName&, UCHAR, Jrd::Sec void SCL_check_package(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); void SCL_check_procedure(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); void SCL_check_function(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); +void SCL_check_filter(Jrd::thread_db* tdbb, const Firebird::MetaName &name, Jrd::SecurityClass::flags_t); void SCL_check_relation(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); +void SCL_check_view(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); +void SCL_check_role(Jrd::thread_db* tdbb, const Firebird::MetaName&, Jrd::SecurityClass::flags_t); Jrd::SecurityClass* SCL_get_class(Jrd::thread_db*, const TEXT*); Jrd::SecurityClass::flags_t SCL_get_mask(Jrd::thread_db* tdbb, const TEXT*, const TEXT*); void SCL_init(Jrd::thread_db* tdbb, bool, const Jrd::UserId& tempId); @@ -53,6 +58,7 @@ Jrd::SecurityClass* SCL_recompute_class(Jrd::thread_db*, const TEXT*); void SCL_release_all(Jrd::SecurityClassList*&); bool SCL_role_granted(Jrd::thread_db* tdbb, const Jrd::UserId& usr, const TEXT* sql_role); bool SCL_admin_role(Jrd::thread_db* tdbb, const TEXT* sql_role); +Jrd::SecurityClass::flags_t SCL_get_object_mask(const int object_type); namespace Jrd { typedef Firebird::Array Acl; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 95c4c65256d..2629b7c03c2 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -1396,6 +1396,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { SCL_check_relation(tdbb, &desc, SCL_drop); } + if (EVL_field(0, rpb->rpb_record, f_rel_id, &desc2)) { id = MOV_get_long(&desc2, 0); @@ -1577,6 +1578,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_arg_fun_name, &desc); SCL_check_function(tdbb, &desc, SCL_control); } + break; case rel_prc_prms: @@ -1621,6 +1623,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_files: protect_system_table_delupd(tdbb, relation, "DELETE"); { + SCL_check_database(tdbb, SCL_alter); const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); const USHORT file_flags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ? MOV_get_long(&desc2, 0) : 0; @@ -2677,6 +2680,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_files: protect_system_table_delupd(tdbb, relation, "UPDATE"); { + SCL_check_database(tdbb, SCL_alter); SSHORT new_rel_flags, old_rel_flags; EVL_field(0, new_rpb->rpb_record, f_file_name, &desc1); if (EVL_field(0, new_rpb->rpb_record, f_file_flags, &desc2) && @@ -3036,6 +3040,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) DeferredWork* work = NULL; MetaName package_name; USHORT object_id; + MetaName object_name; #ifdef VIO_DEBUG VIO_trace(DEBUG_WRITES, @@ -3055,7 +3060,6 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) switch ((RIDS) relation->rel_id) { case rel_pages: - case rel_database: case rel_formats: case rel_trans: case rel_rcon: @@ -3083,6 +3087,10 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_log: case rel_global_auth_mapping: protect_system_table_insert(tdbb, request, relation, true); + + case rel_database: + if (set_security_class(tdbb, rpb->rpb_record, f_dat_class)) + DFW_post_work(transaction, dfw_grant, &desc, obj_database); break; case rel_relations: @@ -3188,6 +3196,8 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_fields: EVL_field(0, rpb->rpb_record, f_fld_name, &desc); + MOV_get_metaname(&desc, object_name); + SCL_check_domain(tdbb, object_name, SCL_create); DFW_post_work(transaction, dfw_create_field, &desc, 0); set_system_flag(tdbb, rpb->rpb_record, f_fld_sys_flag); set_owner_name(tdbb, rpb->rpb_record, f_fld_owner); @@ -3195,6 +3205,12 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) DFW_post_work(transaction, dfw_grant, &desc, obj_field); break; + case rel_filters: + EVL_field(0, rpb->rpb_record, f_flt_name, &desc); + if (set_security_class(tdbb, rpb->rpb_record, f_flt_class)) + DFW_post_work(transaction, dfw_grant, &desc, obj_blob_filter); + break; + case rel_files: { const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index ca694b8d38b..6976751c88d 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -1,7 +1,7 @@ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); -- -('2014-06-24 10:13:56', 'JRD', 0, 774) +('2014-05-14 23:48:00', 'JRD', 0, 775) ('2012-01-23 20:10:30', 'QLI', 1, 532) ('2013-11-13 15:59:10', 'GFIX', 3, 122) ('1996-11-07 13:39:40', 'GPRE', 4, 1) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index e7fc5e0423a..fb75f3cd505 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -881,6 +881,8 @@ Data source : @4', NULL, NULL) ('set_invalid_role', 'SetRoleNode::execute', 'StmtNodes.cpp', NULL, 0, 771, NULL, 'Role @1 is invalid or unavailable', NULL, NULL); ('cursor_not_positioned', NULL, NULL, NULL, 0, 772, NULL, 'Cursor @1 is not positioned in a valid record', NULL, NULL); ('dup_attribute', 'UserManagement::execute', 'UserManagement.cpp', NULL, 0, 773, NULL, 'Duplicated user attribute @1', NULL, NULL); +('dyn_no_priv', NULL, 'scl.epp', NULL, 0, 774, NULL, 'There is no privilege for this operation', NULL, NULL); +('dsql_cant_grant_option', NULL, 'DdlNodes.epp', NULL, 0, 775, NULL, 'Using GRANT OPTION on @1 not allowed', NULL, NULL); -- QLI (NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL); (NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index d10471a1133..3112b20d054 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -780,6 +780,8 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, '0P', '000', 0, 771, 'set_invalid_role', NULL, NULL); (-596, 'HY', '109', 0, 772, 'cursor_not_positioned', NULL, NULL) (-901, '42', '702', 0, 773, 'dup_attribute', NULL, NULL); +(-901, '42', '000', 0, 774, 'dyn_no_priv', NULL, NULL) +(-901, '42', '000', 0, 775, 'dsql_cant_grant_option', NULL, NULL); -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)