From 7ad99b795ec74fb0d92db37bfa097726e4671b24 Mon Sep 17 00:00:00 2001 From: Alexander Peshkov Date: Tue, 31 May 2016 20:07:08 +0300 Subject: [PATCH] Added support for system privileges --- .../README.builtin_functions.txt | 15 + doc/sql.extensions/README.ddl.txt | 58 ++ lang_helpers/gds_codes.ftn | 4 + lang_helpers/gds_codes.pas | 4 + .../manage/SrpManagement.cpp | 24 +- .../SecureRemotePassword/server/SrpServer.cpp | 2 +- .../SecurityDatabase/LegacyManagement.epp | 2 +- src/auth/SecurityDatabase/LegacyServer.cpp | 2 +- src/burp/backup.epp | 6 + src/burp/burp.h | 1 + src/burp/restore.epp | 12 + src/common/classes/MetaName.h | 3 + src/dsql/DdlNodes.epp | 170 ++++-- src/dsql/DdlNodes.h | 39 +- src/dsql/ExprNodes.cpp | 4 +- src/dsql/PackageNodes.epp | 2 +- src/dsql/StmtNodes.cpp | 25 +- src/dsql/parse.y | 80 ++- src/gpre/jrdmet.cpp | 2 +- src/include/gen/codetext.h | 2 + src/include/gen/iberror.h | 8 +- src/include/gen/ids.h | 1 + src/include/gen/msgs.h | 2 + src/include/gen/sql_code.h | 2 + src/include/gen/sql_state.h | 2 + src/isql/extract.epp | 7 +- src/isql/isql.epp | 2 +- src/isql/isql.h | 1 + src/isql/show.epp | 101 +++- src/isql/show_proto.h | 1 + src/jrd/Attachment.h | 6 +- src/jrd/CryptoManager.cpp | 4 +- src/jrd/DbCreators.cpp | 73 ++- src/jrd/ExtEngineManager.cpp | 2 +- src/jrd/Mapping.cpp | 544 ++++++++++++------ src/jrd/Mapping.h | 18 +- src/jrd/Monitoring.cpp | 12 +- src/jrd/SysFunction.cpp | 46 +- src/jrd/SystemPrivileges.h | 49 ++ src/jrd/UserManagement.cpp | 6 +- src/jrd/acl.h | 4 +- src/jrd/cch.cpp | 2 +- src/jrd/constants.h | 2 +- src/jrd/dflt.h | 12 +- src/jrd/dfw.epp | 10 +- src/jrd/drq.h | 1 - src/jrd/extds/InternalDS.cpp | 8 +- src/jrd/fields.h | 2 + src/jrd/grant.epp | 14 + src/jrd/inf.cpp | 12 +- src/jrd/ini.epp | 155 +++-- src/jrd/ini.h | 7 + src/jrd/ini_proto.h | 1 + src/jrd/irq.h | 1 + src/jrd/jrd.cpp | 199 +++---- src/jrd/names.h | 2 + src/jrd/obj.h | 39 +- src/jrd/ods.h | 2 + src/jrd/relations.h | 1 + src/jrd/scl.epp | 329 ++++++----- src/jrd/scl.h | 231 +++++++- src/jrd/scl_proto.h | 6 +- src/jrd/shut.cpp | 8 +- src/jrd/status.h | 21 +- src/jrd/svc.cpp | 8 +- src/jrd/tra.cpp | 4 +- src/jrd/tra.h | 2 +- src/jrd/trace/TraceConfigStorage.cpp | 2 +- src/jrd/trace/TraceManager.cpp | 45 +- src/jrd/trace/TraceObjects.cpp | 4 +- src/jrd/trace/TraceService.cpp | 2 +- src/jrd/trig.h | 63 +- src/jrd/types.h | 4 + src/jrd/vio.cpp | 24 +- src/msgs/facilities2.sql | 6 +- src/msgs/messages2.sql | 8 +- src/msgs/system_errors2.sql | 2 + src/utilities/ibmgr/ibmgr.cpp | 6 +- src/utilities/ibmgr/srvrmgr.cpp | 6 +- src/yvalve/keywords.cpp | 3 + 80 files changed, 1813 insertions(+), 789 deletions(-) create mode 100644 src/jrd/SystemPrivileges.h diff --git a/doc/sql.extensions/README.builtin_functions.txt b/doc/sql.extensions/README.builtin_functions.txt index 14eeed19d5..25e41bfe3e 100644 --- a/doc/sql.extensions/README.builtin_functions.txt +++ b/doc/sql.extensions/README.builtin_functions.txt @@ -681,6 +681,21 @@ Example: select * from x order by rand(); +-------------------- +RDB$SYSTEM_PRIVILEGE +-------------------- + +(FB4 extension) +Function: + Returns true if current attachment has given system privilege. + +Format: + RDB$SYSTEM_PRIVILEGE( ) + +Example: + select rdb$system_privilege(user_management) from rdb$database; + + ------- REPLACE ------- diff --git a/doc/sql.extensions/README.ddl.txt b/doc/sql.extensions/README.ddl.txt index 9e81c31cee..ea854432d9 100644 --- a/doc/sql.extensions/README.ddl.txt +++ b/doc/sql.extensions/README.ddl.txt @@ -509,3 +509,61 @@ ignoring it). ALTER DATABASE DECRYPT Decrypts database. + + +21) New clauses in CREATE and ALTER role operators. +(Alex Peshkov) + +Provide support for system privileges. One can: + +CREATE ROLE SET SYSTEM PRIVILEGES TO {, {, ... }} +ALTER ROLE SET SYSTEM PRIVILEGES TO {, {, ... }} + +This forms assign non-empty list of system privileges to role . Privileges previously assigned +to role are cleared when using second form. + +ALTER ROLE DROP SYSTEM PRIVILEGES + +This form clears list of system privileges in role . + +System privileges make it possible to delegate part of DBO rights to other users. +Pay attention taht system privileges provide very thin level of control, therefore sometimes +you will need to give user >1 privilege to perform some task (for example add +IGNORE_DB_TRIGGERS to USE_GSTAT_UTILITY cause gstat wants to ignore database triggers). + +List of valid system privileges for FB4 is as follows: +USER_MANAGEMENT Manage users +READ_RAW_PAGES Read pages in raw format using Attachment::getInfo() +CREATE_USER_TYPES Add/change/delete non-system records in RDB$TYPES +USE_NBACKUP_UTILITY Use nbackup to create database's copies +CHANGE_SHUTDOWN_MODE Shutdown DB and bring online +TRACE_ANY_ATTACHMENT Trace other users' attachments +MONITOR_ANY_ATTACHMENT Monitor (tables MON$) other users' attachments +ACCESS_SHUTDOWN_DATABASE Access database when it's shut down +CREATE_DATABASE Create new databases (given in security.db) +DROP_DATABASE Drop this database +USE_GBAK_UTILITY Use appropriate utility +USE_GSTAT_UTILITY ... +USE_GFIX_UTILITY ... +IGNORE_DB_TRIGGERS Insruct engine not to run DB-level triggers +CHANGE_HEADER_SETTINGS Modify parameters in DB header page +SELECT_ANY_OBJECT_IN_DATABASE Use SELECT for any selectable object +ACCESS_ANY_OBJECT_IN_DATABASE Access (in any possible way) any object +MODIFY_ANY_OBJECT_IN_DATABASE Modify (up to drop) any object +CHANGE_MAPPING_RULES Change authentication mappings +USE_GRANTED_BY_CLAUSE Use GRANTED BY in GRANT and REVOKE operators +GRANT_REVOKE_ON_ANY_OBJECT GRANT and REVOKE rights on any object in database +GRANT_REVOKE_ANY_DDL_RIGHT GRANT and REVOKE any DDL rights +CREATE_PRIVILEGED_ROLES Use SET SYSTEM PRIVILEGES in roles + + +22) New grantee type in GRANT and REVOKE operators - SYSTEM PRIVILEGE. +(Alex Peshkov) + +With support for various system privileges in engine it's getting very convenient to grant some +rights to users already having specific system privilege. Therefore appropriate grantee type is +suppoprted now. Example: + +GRANT ALL ON PLG$SRP_VIEW TO SYSTEM PRIVILEGE USER_MANAGEMENT + +Grants all rights to view (used in SRP management plugin) to users having USER_MANAGEMENT privilege. diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index b67b89fdfc..c4847683e1 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1632,6 +1632,10 @@ C -- PARAMETER (GDS__encrypt_error = 335545109) INTEGER*4 GDS__max_idx_depth PARAMETER (GDS__max_idx_depth = 335545110) + INTEGER*4 GDS__wrong_prvlg + PARAMETER (GDS__wrong_prvlg = 335545111) + INTEGER*4 GDS__miss_prvlg + PARAMETER (GDS__miss_prvlg = 335545112) 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 e381dca2bc..e1e6a98c86 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1627,6 +1627,10 @@ const gds_encrypt_error = 335545109; isc_max_idx_depth = 335545110; gds_max_idx_depth = 335545110; + isc_wrong_prvlg = 335545111; + gds_wrong_prvlg = 335545111; + isc_miss_prvlg = 335545112; + gds_miss_prvlg = 335545112; isc_gfix_db_name = 335740929; gds_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp index f10894bfef..a7c88591ba 100644 --- a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -87,16 +87,18 @@ private: "CREATE VIEW PLG$SRP_VIEW AS " "SELECT PLG$USER_NAME, PLG$VERIFIER, PLG$SALT, PLG$COMMENT, " " PLG$FIRST, PLG$MIDDLE, PLG$LAST, PLG$ATTRIBUTES, PLG$ACTIVE " - "FROM PLG$SRP WHERE CURRENT_USER = 'SYSDBA' " - " OR CURRENT_ROLE = '" ADMIN_ROLE "' OR CURRENT_USER = PLG$SRP.PLG$USER_NAME" + "FROM PLG$SRP WHERE RDB$SYSTEM_PRIVILEGE(USER_MANAGEMENT) " + " OR CURRENT_USER = PLG$SRP.PLG$USER_NAME" , - "GRANT ALL ON PLG$SRP to VIEW PLG$SRP_VIEW" + "GRANT ALL ON PLG$SRP TO VIEW PLG$SRP_VIEW" , - "GRANT SELECT ON PLG$SRP_VIEW to PUBLIC" + "GRANT SELECT ON PLG$SRP_VIEW TO PUBLIC" , "GRANT UPDATE(PLG$VERIFIER, PLG$SALT, PLG$FIRST, PLG$MIDDLE, PLG$LAST, " " PLG$COMMENT, PLG$ATTRIBUTES) ON PLG$SRP_VIEW TO PUBLIC" , + "GRANT ALL ON PLG$SRP_VIEW TO SYSTEM PRIVILEGE USER_MANAGEMENT" + , NULL }; @@ -106,10 +108,18 @@ private: try { - for (const char** sql = script; *sql; ++sql) + for (const char** s = script; *s; ++s) { - att->execute(&statusWrapper, ddlTran, 0, *sql, SQL_DIALECT_V6, NULL, NULL, NULL, NULL); - check(&statusWrapper); + const char* sql = *s; + bool err = false; + if (sql[0] == '*') + { + ++sql; + err = true; + } + att->execute(&statusWrapper, ddlTran, 0, sql, SQL_DIALECT_V6, NULL, NULL, NULL, NULL); + if (!err) + check(&statusWrapper); } ddlTran->commit(&statusWrapper); diff --git a/src/auth/SecureRemotePassword/server/SrpServer.cpp b/src/auth/SecureRemotePassword/server/SrpServer.cpp index 5c321332a8..e70b0b63cc 100644 --- a/src/auth/SecureRemotePassword/server/SrpServer.cpp +++ b/src/auth/SecureRemotePassword/server/SrpServer.cpp @@ -132,7 +132,7 @@ int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWrite { ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE); dpb.insertByte(isc_dpb_sec_attach, TRUE); - dpb.insertString(isc_dpb_user_name, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME)); + dpb.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); const char* providers = "Providers=" CURRENT_ENGINE; dpb.insertString(isc_dpb_config, providers, fb_strlen(providers)); att = p->attachDatabase(status, secDbName, dpb.getBufferLength(), dpb.getBuffer()); diff --git a/src/auth/SecurityDatabase/LegacyManagement.epp b/src/auth/SecurityDatabase/LegacyManagement.epp index 9b2f4510fe..01ea19b9c5 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.epp +++ b/src/auth/SecurityDatabase/LegacyManagement.epp @@ -508,7 +508,7 @@ int SecurityDatabaseManagement::execute(Firebird::CheckStatusWrapper* st, Firebi found = false; // Do not allow SYSDBA user to be deleted - if (!fb_utils::stricmp(user->userName()->get(), SYSDBA_USER_NAME)) + if (!fb_utils::stricmp(user->userName()->get(), DBA_USER_NAME)) ret = GsecMsg23; else { diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index 1cd2d4185d..ac60d2042b 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -289,7 +289,7 @@ void SecurityDatabase::prepare() dpb.insertByte(isc_dpb_sec_attach, TRUE); // Attach as SYSDBA - dpb.insertString(isc_dpb_trusted_auth, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME)); + dpb.insertString(isc_dpb_trusted_auth, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); // Do not use other providers except current engine const char* providers = "Providers=" CURRENT_ENGINE; diff --git a/src/burp/backup.epp b/src/burp/backup.epp index f0e36219b7..af9e53afb7 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4037,6 +4037,12 @@ void write_sql_roles() if (!X.RDB$DESCRIPTION.NULL) { put_source_blob (att_role_description, att_role_description, X.RDB$DESCRIPTION); } + + const UCHAR ll = sizeof(X.RDB$SYSTEM_PRIVILEGES); + put(tdgbl, att_role_sys_priveleges); + put(tdgbl, ll); + put_block(tdgbl, (const UCHAR*) (X.RDB$SYSTEM_PRIVILEGES), ll); + put(tdgbl, att_end); MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp)); BURP_verbose (249, temp); diff --git a/src/burp/burp.h b/src/burp/burp.h index 293a91fb30..ecb444b20e 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -566,6 +566,7 @@ enum att_type { att_role_name = SERIES, att_role_owner_name, att_role_description, + att_role_sys_priveleges, // Check constraints attributes att_chk_constraint_name = SERIES, diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 5e5f31cbcf..d0ca8dbe04 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -7769,6 +7769,18 @@ bool get_sql_roles(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 250); break; + case att_role_sys_priveleges: + { + const ULONG l = get(tdgbl); + if (l > sizeof(X.RDB$SYSTEM_PRIVILEGES)) + BURP_error_redirect (NULL, 46); + // msg 46 string truncated + + if (l) + get_block(tdgbl, (UCHAR*) (X.RDB$SYSTEM_PRIVILEGES), l); + } + break; + default: // msg 250 SQL role bad_attribute(scan_next_attr, attribute, 250); diff --git a/src/common/classes/MetaName.h b/src/common/classes/MetaName.h index a418184957..60ac74e7fe 100644 --- a/src/common/classes/MetaName.h +++ b/src/common/classes/MetaName.h @@ -81,6 +81,9 @@ public: bool isEmpty() const { return count == 0; } bool hasData() const { return count != 0; } + char& operator[](unsigned n) { return data[n]; } + char operator[](unsigned n) const { return data[n]; } + const char* begin() const { return data; } const char* end() const { return data + count; } diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 72b5ee41a9..217649cca9 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -61,6 +61,7 @@ #include "../common/dsc_proto.h" #include "../common/StatusArg.h" #include "../auth/SecureRemotePassword/Message.h" +#include "../jrd/Mapping.h" namespace Jrd { @@ -903,7 +904,7 @@ void DdlNode::storePrivileges(thread_db* tdbb, jrd_tra* transaction, const char* privileges) { Attachment* const attachment = transaction->tra_attachment; - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); AutoCacheRequest request(tdbb, drq_s_usr_prvs, DYN_REQUESTS); @@ -966,7 +967,7 @@ void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, MetaName& const TypeClause* field, const string& computedSource, const BlrDebugWriter::BlrData& computedValue) { Attachment* const attachment = transaction->tra_attachment; - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); const ValueListNode* elements = field->ranges; const USHORT dims = elements ? elements->items.getCount() / 2 : 0; @@ -1674,7 +1675,7 @@ void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch jrd_tra* transaction) { Attachment* const attachment = transaction->getAttachment(); - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); if (package.isEmpty()) { @@ -2661,7 +2662,7 @@ void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc jrd_tra* transaction) { Attachment* const attachment = transaction->getAttachment(); - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); if (package.isEmpty()) { @@ -3801,7 +3802,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra jrd_tra* transaction) { Attachment* const attachment = transaction->tra_attachment; - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); @@ -5264,7 +5265,7 @@ void CreateAlterExceptionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc jrd_tra* transaction) { Attachment* const attachment = transaction->getAttachment(); - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_EXCEPTION, name, NULL); @@ -5637,7 +5638,7 @@ SSHORT CreateAlterSequenceNode::store(thread_db* tdbb, jrd_tra* transaction, con fb_sysflag sysFlag, SINT64 val, SLONG step) { Attachment* const attachment = transaction->tra_attachment; - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_generator); @@ -8304,7 +8305,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra jrd_tra* transaction) { Attachment* const attachment = transaction->tra_attachment; - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); const dsql_rel* modifyingView = NULL; @@ -9725,7 +9726,7 @@ bool CreateFilterNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) // Define a blob filter. void CreateFilterNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* transaction) { - MetaName ownerName(tdbb->getAttachment()->att_user->usr_user_name); + MetaName ownerName(tdbb->getAttachment()->att_user->getUserName()); // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); @@ -9953,30 +9954,52 @@ void DropShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch //---------------------- -string CreateRoleNode::internalPrint(NodePrinter& printer) const +USHORT PrivilegesNode::convertPrivilegeFromString(thread_db* tdbb, jrd_tra* transaction, Firebird::MetaName privilege) +{ + string priv(privilege.c_str()); + priv.upper(); + + return SCL_convert_privilege(tdbb, transaction, priv); +} + + +//---------------------- + + +string CreateAlterRoleNode::internalPrint(NodePrinter& printer) const { DdlNode::internalPrint(printer); NODE_PRINT(printer, name); - return "CreateRoleNode"; + return "CreateAlterRoleNode"; } -bool CreateRoleNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +bool CreateAlterRoleNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_role); + if (createFlag) + SCL_check_create_access(tdbb, SCL_object_role); + else + SCL_check_role(tdbb, name, SCL_alter); + return true; } -void CreateRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +void CreateAlterRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - MetaName ownerName(tdbb->getAttachment()->att_user->usr_user_name); + if (createFlag && sysPrivDrop) + { + // msg 293: DROP SYSTEM PRIVILEGES should not be used in CREATE ROLE + Arg::PrivateDyn(293).raise(); + } + + MetaName ownerName(tdbb->getAttachment()->att_user->getUserName()); // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, - DDL_TRIGGER_CREATE_ROLE, name, NULL); + createFlag ? DDL_TRIGGER_CREATE_ROLE : DDL_TRIGGER_ALTER_ROLE, name, NULL); if (name == ownerName) { @@ -9997,31 +10020,64 @@ void CreateRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, } MetaName dummyName; - if (isItSqlRole(tdbb, transaction, name, dummyName)) + if (createFlag && isItSqlRole(tdbb, transaction, name, dummyName)) { // msg 194: "SQL role @1 already exists" status_exception::raise(Arg::PrivateDyn(194) << name); } - AutoCacheRequest request(tdbb, drq_role_gens, DYN_REQUESTS); - - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - X IN RDB$ROLES + if (privileges.hasData() || sysPrivDrop) { - strcpy(X.RDB$ROLE_NAME, name.c_str()); - strcpy(X.RDB$OWNER_NAME, ownerName.c_str()); - X.RDB$SYSTEM_FLAG = 0; + if (!tdbb->getAttachment()->locksmith(tdbb, CREATE_PRIVILEGED_ROLES)) + { + // msg 294: Access to SYSTEM PRIVILEGES in ROLES denied to @1 + (Arg::PrivateDyn(294) << ownerName).raise(); + } + } + + UserId::Privileges newPrivileges; + if (privileges.hasData() && !sysPrivDrop) + { + const MetaName* const end = privileges.end(); + for (const MetaName* privName = privileges.begin(); privName < end; ++privName) + newPrivileges.set(convertPrivilegeFromString(tdbb, transaction, *privName)); + } + + Attachment* attachment = tdbb->getAttachment(); + + string p; + newPrivileges.store(p.getBuffer(newPrivileges.BYTES_COUNT)); + + if (createFlag) + { + PreparedStatement::Builder sql; + sql << "insert into rdb$roles(rdb$role_name, rdb$owner_name, rdb$system_privileges, rdb$system_flag)" + << "values(" << name << "," << ownerName << "," << p << ", 0)"; + + AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, sql)); + ps->execute(tdbb, transaction); + } + else if (privileges.hasData() || sysPrivDrop) + { + PreparedStatement::Builder sql; + sql << "update rdb$roles set rdb$system_privileges =" << p << "where rdb$role_name =" << name; + + AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, sql)); + if (ps->executeUpdate(tdbb, transaction) == 0) + { + // msg 155: "Role %s not found" + (Arg::PrivateDyn(155) << name).raise(); + } } - END_STORE executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, - DDL_TRIGGER_CREATE_ROLE, name, NULL); + createFlag ? DDL_TRIGGER_CREATE_ROLE : DDL_TRIGGER_ALTER_ROLE, name, NULL); savePoint.release(); // everything is ok } // If role name is user name returns true. Otherwise returns false. -bool CreateRoleNode::isItUserName(thread_db* tdbb, jrd_tra* transaction) +bool CreateAlterRoleNode::isItUserName(thread_db* tdbb, jrd_tra* transaction) { bool found = false; @@ -10308,8 +10364,8 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext) // 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) { - if (!(tdbb->getAttachment() && tdbb->getAttachment()->locksmith())) - status_exception::raise(Arg::Gds(isc_adm_task_denied)); + if (!(tdbb->getAttachment() && tdbb->getAttachment()->locksmith(tdbb, CHANGE_MAPPING_RULES))) + (Arg::Gds(isc_miss_prvlg) << "CHANGE_MAPPING_RULES").raise(); if (global) { @@ -10448,7 +10504,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd if (ddlTriggerAction > 0) executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL); - DFW_post_work(transaction, dfw_clear_mapping, NULL, 0); + DFW_post_work(transaction, dfw_clear_cache, NULL, MAPPING_CACHE); savePoint.release(); // everything is ok } @@ -10473,7 +10529,7 @@ bool DropRoleNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) void DropRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - MetaName user(tdbb->getAttachment()->att_user->usr_user_name); + MetaName user(tdbb->getAttachment()->att_user->getUserName()); // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); @@ -10634,7 +10690,7 @@ void CreateAlterUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra if (!usr) (Arg::Gds(isc_random) << "Missing user name for ALTER CURRENT USER").raise(); - text = usr->usr_user_name; + text = usr->getUserName(); } Firebird::LocalStatus s; @@ -10835,6 +10891,9 @@ void GrantRevokeNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, for (usersPtr = users.begin(); usersPtr != usersEnd; ++usersPtr) grantRevoke(tdbb, transaction, rolesPtr, usersPtr, "M", defaultRole ? "D" : NULL, option); } + + // Invalidate system privileges cache + DFW_post_work(transaction, dfw_clear_cache, NULL, SYSTEM_PRIVILEGES_CACHE); } } @@ -11035,6 +11094,14 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G status_exception::raise(Arg::PrivateDyn(195) << user.c_str()); } break; + + case obj_privilege: // Should convert symbolic privilege name to bit number + { + USHORT p = convertPrivilegeFromString(tdbb, transaction, user); + user.printf("%d", p); + } + break; + } if (options == 1) // with grant option @@ -11053,16 +11120,26 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G ERRD_post(Arg::Gds(isc_dsql_cant_grant_option) << Arg::Str("views")); break; + case obj_privilege: + ERRD_post(Arg::Gds(isc_dsql_cant_grant_option) << Arg::Str("system privileges")); + break; + default: break; } } MetaName grantorRevoker(grantor ? - *grantor : tdbb->getAttachment()->att_user->usr_user_name); + *grantor : tdbb->getAttachment()->att_user->getUserName()); - if (grantor && !tdbb->getAttachment()->locksmith()) - status_exception::raise(Arg::PrivateDyn(252) << SYSDBA_USER_NAME); + if (grantor && !tdbb->getAttachment()->locksmith(tdbb, USE_GRANTED_BY_CLAUSE)) + { + const Firebird::MetaName& owner(tdbb->getDatabase()->dbb_owner); + if (owner == DBA_USER_NAME) + (Arg::PrivateDyn(252) << DBA_USER_NAME).raise(); + else + (Arg::PrivateDyn(295) << DBA_USER_NAME << owner).raise(); + } if (!isGrant && !privs) // REVOKE ALL ON ALL { @@ -11077,7 +11154,8 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G PRIV.RDB$USER_TYPE = userType AND PRIV.RDB$GRANTOR NOT MISSING { - if (tdbb->getAttachment()->att_user->locksmith() || grantorRevoker == PRIV.RDB$GRANTOR) + if (tdbb->getAttachment()->att_user->locksmith(tdbb, GRANT_REVOKE_ON_ANY_OBJECT) || + grantorRevoker == PRIV.RDB$GRANTOR) { ERASE PRIV; all.grantErased = true; @@ -11175,11 +11253,13 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G if (userType == obj_sql_role) { // Check for blocking cycles of role grants. - Firebird::SortedArray grantedRoles; - SCL_find_granted_roles(tdbb, objName, true, grantedRoles, false); - // 292: role @1 can not be granted to role @2 - if (grantedRoles.exist(user.c_str())) + UserId grantedRoles; + grantedRoles.setSqlRole(objName); + if (grantedRoles.roleInUse(tdbb, user)) + { + // 292: role @1 can not be granted to role @2 status_exception::raise(Arg::PrivateDyn(292) << objName.c_str() << user.c_str()); + } } } else @@ -11199,13 +11279,13 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G { // Relation or view because we cannot distinguish at this point. checkGrantorCanGrant(tdbb, transaction, - tdbb->getAttachment()->att_user->usr_user_name.c_str(), priv, objName, + tdbb->getAttachment()->att_user->getUserName().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); + tdbb->getAttachment()->att_user->getUserName().c_str(), priv, objName); } } @@ -11368,7 +11448,7 @@ void GrantRevokeNode::checkGrantorCanGrant(thread_db* tdbb, jrd_tra* transaction // If the current user is locksmith - allow all grants to occur - if (tdbb->getAttachment()->locksmith()) + if (tdbb->getAttachment()->locksmith(tdbb, GRANT_REVOKE_ON_ANY_OBJECT)) return; // If this is a non-sql table, then the owner will probably not have any @@ -11569,7 +11649,7 @@ void GrantRevokeNode::checkGrantorCanGrantRole(thread_db* tdbb, jrd_tra* transac if (isItSqlRole(tdbb, transaction, roleName, owner)) { // Both SYSDBA and the owner of this ROLE can grant membership - if (tdbb->getAttachment()->locksmith() || owner == grantor) + if (tdbb->getAttachment()->locksmith(tdbb, GRANT_REVOKE_ON_ANY_OBJECT) || owner == grantor) return; } else @@ -11592,7 +11672,7 @@ void GrantRevokeNode::checkGrantorCanGrantRole(thread_db* tdbb, jrd_tra* transac void GrantRevokeNode::checkGrantorCanGrantDdl(thread_db* tdbb, jrd_tra* transaction, const MetaName& grantor, const char* privilege, const MetaName& objName) { - if (tdbb->getAttachment()->locksmith()) + if (tdbb->getAttachment()->locksmith(tdbb, GRANT_REVOKE_ANY_DDL_RIGHT)) return; AutoCacheRequest request(tdbb, drq_l_grant_option, DYN_REQUESTS); diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index af4b5e5810..82b6d23ffd 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -1890,12 +1890,27 @@ public: }; -class CreateRoleNode : public DdlNode +class PrivilegesNode : public DdlNode { public: - CreateRoleNode(MemoryPool& p, const Firebird::MetaName& aName) - : DdlNode(p), - name(p, aName) + PrivilegesNode(MemoryPool& p) + : DdlNode(p) + { + } + +protected: + USHORT convertPrivilegeFromString(thread_db* tdbb, jrd_tra* transaction, Firebird::MetaName privilege); +}; + +class CreateAlterRoleNode : public PrivilegesNode +{ +public: + CreateAlterRoleNode(MemoryPool& p, const Firebird::MetaName& aName) + : PrivilegesNode(p), + name(p, aName), + createFlag(false), + sysPrivDrop(false), + privileges(p) { } @@ -1907,7 +1922,8 @@ public: protected: virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) { - statusVector << Firebird::Arg::Gds(isc_dsql_create_role_failed) << name; + statusVector << Firebird::Arg::Gds(createFlag ? isc_dsql_create_role_failed : + isc_dsql_alter_role_failed) << name; } private: @@ -1915,6 +1931,15 @@ private: public: Firebird::MetaName name; + bool createFlag, sysPrivDrop; + void addPrivilege(const Firebird::MetaName* privName) + { + fb_assert(privName); + privileges.push(*privName); + } + +private: + Firebird::HalfStaticArray privileges; }; @@ -2109,11 +2134,11 @@ public: typedef Firebird::Pair > PrivilegeClause; typedef Firebird::Pair > GranteeClause; -class GrantRevokeNode : public DdlNode, private ExecInSecurityDb +class GrantRevokeNode : public PrivilegesNode, private ExecInSecurityDb { public: GrantRevokeNode(MemoryPool& p, bool aIsGrant) - : DdlNode(p), + : PrivilegesNode(p), createDbJobs(p), isGrant(aIsGrant), privileges(p), diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index e292f875e6..c8811f217d 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -3661,7 +3661,7 @@ dsc* CurrentRoleNode::execute(thread_db* tdbb, jrd_req* request) const if (tdbb->getAttachment()->att_user) { - curRole = tdbb->getAttachment()->att_user->usr_sql_role_name.c_str(); + curRole = tdbb->getAttachment()->att_user->getSqlRole().c_str(); impure->vlu_desc.dsc_address = reinterpret_cast(const_cast(curRole)); } @@ -3756,7 +3756,7 @@ dsc* CurrentUserNode::execute(thread_db* tdbb, jrd_req* request) const if (tdbb->getAttachment()->att_user) { - curUser = tdbb->getAttachment()->att_user->usr_user_name.c_str(); + curUser = tdbb->getAttachment()->att_user->getUserName().c_str(); impure->vlu_desc.dsc_address = reinterpret_cast(const_cast(curUser)); } diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 876b58f24b..4e983b8e9f 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -491,7 +491,7 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* jrd_tra* transaction) { Attachment* attachment = transaction->getAttachment(); - const MetaName& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->getUserName(); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE, name, NULL); diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index ae6ee92179..a598223da1 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -7916,36 +7916,15 @@ void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transact UserId* user = attachment->att_user; fb_assert(user); - user->usr_granted_roles.clear(); - if (trusted) - { - if (!user->usr_trusted_role.hasData()) - Arg::Gds(isc_miss_trusted_role).raise(); - user->usr_sql_role_name = user->usr_trusted_role; - } + user->setRoleTrusted(); else { if (!SCL_role_granted(tdbb, *user, roleName.c_str())) (Arg::Gds(isc_set_invalid_role) << roleName).raise(); - user->usr_sql_role_name = roleName.c_str(); + user->setSqlRole(roleName.c_str()); } - if (!user->usr_sql_role_name.isEmpty() || user->usr_sql_role_name != NULL_ROLE) - { - // Add role itself and all roles cumulatively granted to it. Not only default. - user->usr_granted_roles.add(user->usr_sql_role_name); - SCL_find_granted_roles(tdbb, user->usr_sql_role_name, true, user->usr_granted_roles, false); - } - - // Add all default roles granted to user - SCL_find_granted_roles(tdbb, user->usr_user_name, false, user->usr_granted_roles, true); - - if (SCL_admin_role(tdbb, user->usr_granted_roles)) - user->usr_flags |= USR_dba; - else - user->usr_flags &= ~USR_dba; - SCL_release_all(attachment->att_security_classes); } diff --git a/src/dsql/parse.y b/src/dsql/parse.y index a1f2ebf02c..5acd19d104 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -591,7 +591,10 @@ using namespace Firebird; // tokens added for Firebird 4.0 +%token PRIVILEGE %token RDB_ROLE_IN_USE +%token RDB_SYSTEM_PRIVILEGE +%token SYSTEM // precedence declarations for expression evaluation @@ -724,6 +727,7 @@ using namespace Firebird; Jrd::MappingNode* mappingNode; Jrd::MappingNode::OP mappingOp; Jrd::SetRoleNode* setRoleNode; + Jrd::CreateAlterRoleNode* createAlterRoleNode; } %include types.y @@ -1180,6 +1184,8 @@ grantee($granteeArray) { $granteeArray->add(GranteeClause(obj_view, *$2)); } | ROLE symbol_role_name { $granteeArray->add(GranteeClause(obj_sql_role, *$2)); } + | SYSTEM PRIVILEGE valid_symbol_name + { $granteeArray->add(GranteeClause(obj_privilege, *$3)); } ; // CVC: In the future we can deprecate the first implicit form since we'll support @@ -1390,7 +1396,7 @@ create_clause | DATABASE db_clause { $$ = $2; } | KW_DOMAIN domain_clause { $$ = $2; } | SHADOW shadow_clause { $$ = $2; } - | ROLE role_clause { $$ = $2; } + | ROLE role_clause { $2->createFlag = true; $$ = $2; } | COLLATION collation_clause { $$ = $2; } | USER create_user_clause { $$ = $2; } | PACKAGE package_clause { $$ = $2; } @@ -1788,11 +1794,40 @@ sequence_value ; -// CREATE ROLE +// CREATE / ALTER ROLE -%type role_clause +%type role_clause role_clause - : symbol_role_name { $$ = newNode(*$1); } + : symbol_role_name + { $$ = newNode(*$1); } + opt_system_privileges($2) + { $$ = $2; } + ; + +%type opt_system_privileges() +opt_system_privileges($createAlterRole) + : // nothing + | set_system_privileges($createAlterRole) + | drop_system_privileges($createAlterRole) + ; + +%type set_system_privileges() +set_system_privileges($createAlterRole) + : SET SYSTEM PRIVILEGES TO system_privileges_list($createAlterRole) + +%type drop_system_privileges() +drop_system_privileges($createAlterRole) + : DROP SYSTEM PRIVILEGES { $createAlterRole->sysPrivDrop = true; } + +%type system_privileges_list() +system_privileges_list($createAlterRole) + : system_privilege($createAlterRole) + | system_privileges_list ',' system_privilege($createAlterRole) + ; + +%type system_privilege() +system_privilege($createAlterRole) + : valid_symbol_name { $createAlterRole->addPrivilege($1); } ; @@ -3976,22 +4011,29 @@ module_op | MODULE_NAME utf_string { $$ = $2; } ; -%type alter_role_clause -alter_role_clause +%type alter_role_2X_compatibility +alter_role_2X_compatibility : symbol_role_name alter_role_enable AUTO ADMIN MAPPING { - $$ = newNode(MappingNode::MAP_RPL, "AutoAdminImplementationMapping"); - $$->op = $2 ? MappingNode::MAP_RPL : MappingNode::MAP_DROP; - $$->from = newNode(FB_DOMAIN_ANY_RID_ADMINS); - $$->fromType = newNode(FB_PREDEFINED_GROUP); - $$->mode = 'P'; - $$->plugin = newNode("Win_Sspi"); - $$->role = true; - $$->to = $1; - $$->validateAdmin(); + MappingNode* mn = newNode(MappingNode::MAP_RPL, "AutoAdminImplementationMapping"); + mn->op = $2 ? MappingNode::MAP_RPL : MappingNode::MAP_DROP; + mn->from = newNode(FB_DOMAIN_ANY_RID_ADMINS); + mn->fromType = newNode(FB_PREDEFINED_GROUP); + mn->mode = 'P'; + mn->plugin = newNode("Win_Sspi"); + mn->role = true; + mn->to = $1; + mn->validateAdmin(); + $$ = mn; } ; +%type alter_role_clause +alter_role_clause + : role_clause { $$ = $1; } + | alter_role_2X_compatibility { $$ = $1; } + ; + %type alter_role_enable alter_role_enable : SET { $$ = true; } @@ -7154,6 +7196,11 @@ system_function_special_syntax } | POSITION '(' value_list_opt ')' { $$ = newNode(*$1, $3); } + | RDB_SYSTEM_PRIVILEGE '(' valid_symbol_name ')' + { + ValueExprNode* v = MAKE_str_constant(newIntlString($3->c_str()), CS_ASCII); + $$ = newNode(*$1, newNode(v)); + } ; %type string_value_function @@ -7736,6 +7783,7 @@ non_reserved_word | PASSWORD // | PLAN // | POST_EVENT + | PRIVILEGE | PRIVILEGES | PROTECTED | READ @@ -7755,6 +7803,7 @@ non_reserved_word | STATISTICS | SUB_TYPE | SUSPEND + | SYSTEM | TRANSACTION | UNCOMMITTED // | VARIABLE @@ -7798,6 +7847,7 @@ non_reserved_word | INCREMENT | TRUSTED | RDB_ROLE_IN_USE // added in FB 4.0 + | RDB_SYSTEM_PRIVILEGE ; %% diff --git a/src/gpre/jrdmet.cpp b/src/gpre/jrdmet.cpp index f6547a20ad..856098f478 100644 --- a/src/gpre/jrdmet.cpp +++ b/src/gpre/jrdmet.cpp @@ -99,7 +99,7 @@ void JRDMET_init( gpre_dbb* db) field->fld_charset_id = CS_NONE; field->fld_collate_id = COLLATE_NONE; - field->fld_ttype = ttype_none; + field->fld_ttype = gfield->gfld_sub_type == dsc_text_type_fixed ? ttype_binary : ttype_none; } ++field->fld_length; diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 902c11451b..5ebe00f292 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -812,6 +812,8 @@ static const struct { {"bad_crypt_key", 335545108}, {"encrypt_error", 335545109}, {"max_idx_depth", 335545110}, + {"wrong_prvlg", 335545111}, + {"miss_prvlg", 335545112}, {"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 3779fcfd75..57ccbdc588 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -846,6 +846,8 @@ const ISC_STATUS isc_already_opened = 335545107L; const ISC_STATUS isc_bad_crypt_key = 335545108L; const ISC_STATUS isc_encrypt_error = 335545109L; const ISC_STATUS isc_max_idx_depth = 335545110L; +const ISC_STATUS isc_wrong_prvlg = 335545111L; +const ISC_STATUS isc_miss_prvlg = 335545112L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1317,7 +1319,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 = 1261; +const ISC_STATUS isc_err_max = 1263; #else /* c definitions */ @@ -2133,6 +2135,8 @@ const ISC_STATUS isc_err_max = 1261; #define isc_bad_crypt_key 335545108L #define isc_encrypt_error 335545109L #define isc_max_idx_depth 335545110L +#define isc_wrong_prvlg 335545111L +#define isc_miss_prvlg 335545112L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2604,7 +2608,7 @@ const ISC_STATUS isc_err_max = 1261; #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 1261 +#define isc_err_max 1263 #endif diff --git a/src/include/gen/ids.h b/src/include/gen/ids.h index 08156f9e53..12bf808f81 100644 --- a/src/include/gen/ids.h +++ b/src/include/gen/ids.h @@ -452,6 +452,7 @@ const USHORT f_rol_desc = 2; const USHORT f_rol_sys_flag = 3; const USHORT f_rol_class = 4; + const USHORT f_rol_sys_priv = 5; // Relation 32 (RDB$BACKUP_HISTORY) diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 4b57037ada..30e91e9aac 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -815,6 +815,8 @@ Data source : @4"}, /* eds_statement */ {335545108, "Invalid crypt key @1"}, /* bad_crypt_key */ {335545109, "Page requires encyption but crypt plugin is missing"}, /* encrypt_error */ {335545110, "Maximum index depth (@1 levels) is reached"}, /* max_idx_depth */ + {335545111, "System privilege @1 does not exist"}, /* wrong_prvlg */ + {335545112, "Unable to perform operation: system privilege @1 is missing"}, /* miss_prvlg */ {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 5bb1783db5..4c67dba39a 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -811,6 +811,8 @@ static const struct { {335545108, -902}, /* 788 bad_crypt_key */ {335545109, -901}, /* 789 encrypt_error */ {335545110, -904}, /* 790 max_idx_depth */ + {335545111, -901}, /* 791 wrong_prvlg */ + {335545112, -902}, /* 792 miss_prvlg */ {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 a337587faa..95a8c33acb 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -811,6 +811,8 @@ static const struct { {335545108, "08006"}, // 788 bad_crypt_key {335545109, "XX000"}, // 789 encrypt_error {335545110, "54000"}, // 790 max_idx_depth + {335545111, "0A000"}, // 791 wrong_prvlg + {335545112, "28000"}, // 792 miss_prvlg {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/isql/extract.epp b/src/isql/extract.epp index 973e282e83..b37d2d33c8 100644 --- a/src/isql/extract.epp +++ b/src/isql/extract.epp @@ -1150,10 +1150,13 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION) { IUTILS_copy_SQL_id (XX.RDB$ROLE_NAME, SQL_identifier, DBL_QUOTE); - isqlGlob.printf("CREATE ROLE %s;%s", SQL_identifier, NEWLINE); + isqlGlob.printf("CREATE ROLE %s", SQL_identifier); } else - isqlGlob.printf("CREATE ROLE %s;%s", XX.RDB$ROLE_NAME, NEWLINE); + isqlGlob.printf("CREATE ROLE %s", XX.RDB$ROLE_NAME); + + SHOW_system_privileges(XX.RDB$ROLE_NAME, " SET SYSTEM PRIVILEGES TO", false); + isqlGlob.printf(";%s", NEWLINE); } END_FOR diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 2cf53ccf02..8f9460b907 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -3639,7 +3639,7 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name) * Parameters: statement == the entire statement for processing. * * Note: SQL ROLE setting must be taken into an account no matter - * that the newly created database will not have any roles defined + * that the newly created database will not have any user roles defined * in it. Role may affect right to create new database. * **************************************/ diff --git a/src/isql/isql.h b/src/isql/isql.h index f28dccc2eb..04bab894c8 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -265,6 +265,7 @@ const int NUMBER_FREE_PAGES = 191; // Number of free DB pages = @1 const int DATABASE_CRYPTED = 192; // DB encrypted const int DATABASE_NOT_CRYPTED = 193; // DB not encrypted const int DATABASE_CRYPT_PROCESS = 194; // crypt thread not complete +const int MSG_ROLES = 195; // Roles: // Initialize types diff --git a/src/isql/show.epp b/src/isql/show.epp index bd44121db9..f2b51b6bee 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -104,7 +104,7 @@ static void show_index(SCHAR*, SCHAR*, const SSHORT, const SSHORT, const SSHORT) static processing_state show_indices(const SCHAR* const*); static processing_state show_proc(const SCHAR*); static processing_state show_packages(const SCHAR* package_name); -static processing_state show_role(const SCHAR*); +static processing_state show_role(const SCHAR*, bool, const char* msg = 0); static processing_state show_secclass(const char* object, const char* opt); static processing_state show_table(const SCHAR*, bool); static processing_state show_trigger(const SCHAR*, bool, bool); @@ -636,7 +636,7 @@ static const char* granted_by(char* buffer, const char* grantor, bool nullGranto if (reReadDbOwner) { // Get the owner name - strcpy(owner, SYSDBA_USER_NAME); + strcpy(owner, DBA_USER_NAME); FOR REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME = "RDB$DATABASE" @@ -700,6 +700,15 @@ static void set_grantee(int user_type, const char* SQL_identifier, char* user_st case obj_package_header: sprintf(user_string, "PACKAGE %s", SQL_identifier); break; + case obj_privilege: + FOR T IN RDB$TYPES + WITH T.RDB$FIELD_NAME EQ 'RDB$SYSTEM_PRIVILEGES' + AND T.RDB$TYPE EQ atoi(SQL_identifier) + { + sprintf(user_string, "SYSTEM PRIVILEGE %s", fb_utils::exact_name(T.RDB$TYPE_NAME)); + } + END_FOR + break; default: strcpy(user_string, SQL_identifier); break; @@ -770,6 +779,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR PRV IN RDB$USER_PRIVILEGES CROSS REL IN RDB$RELATIONS WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$RELATION_NAME EQ object AND REL.RDB$RELATION_NAME EQ object AND PRV.RDB$PRIVILEGE NE 'M' AND @@ -952,6 +962,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR PRV IN RDB$USER_PRIVILEGES CROSS PRC IN RDB$PROCEDURES WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_procedure AND PRV.RDB$RELATION_NAME EQ object AND PRC.RDB$PROCEDURE_NAME EQ object AND @@ -1031,6 +1042,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR FIRST 1 R IN RDB$ROLES WITH R.RDB$ROLE_NAME EQ object FOR PRV IN RDB$USER_PRIVILEGES WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE EQ obj_sql_role AND (PRV.RDB$USER_TYPE EQ obj_user OR PRV.RDB$USER_TYPE EQ obj_sql_role) AND @@ -1090,6 +1102,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR FIRST 1 P IN RDB$PACKAGES WITH P.RDB$PACKAGE_NAME EQ object FOR PRV IN RDB$USER_PRIVILEGES CROSS PACK IN RDB$PACKAGES WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_package_header AND PRV.RDB$RELATION_NAME EQ object AND PACK.RDB$PACKAGE_NAME EQ object AND @@ -1166,6 +1179,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR PRV IN RDB$USER_PRIVILEGES CROSS FUN IN RDB$FUNCTIONS WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_udf AND PRV.RDB$RELATION_NAME EQ object AND FUN.RDB$FUNCTION_NAME EQ object AND @@ -1240,6 +1254,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR FIRST 1 G IN RDB$GENERATORS WITH G.RDB$GENERATOR_NAME EQ object FOR PRV IN RDB$USER_PRIVILEGES CROSS GEN IN RDB$GENERATORS WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_generator AND PRV.RDB$RELATION_NAME EQ object AND GEN.RDB$GENERATOR_NAME EQ object AND @@ -1313,6 +1328,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR FIRST 1 E IN RDB$EXCEPTIONS WITH E.RDB$EXCEPTION_NAME EQ object FOR PRV IN RDB$USER_PRIVILEGES CROSS XCP IN RDB$EXCEPTIONS WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_exception AND PRV.RDB$RELATION_NAME EQ object AND XCP.RDB$EXCEPTION_NAME EQ object AND @@ -1387,6 +1403,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR FIRST 1 F IN RDB$FIELDS WITH F.RDB$FIELD_NAME EQ object FOR PRV IN RDB$USER_PRIVILEGES CROSS FLD IN RDB$FIELDS WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_field AND PRV.RDB$RELATION_NAME EQ object AND FLD.RDB$FIELD_NAME EQ object AND @@ -1460,6 +1477,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR FIRST 1 C IN RDB$CHARACTER_SETS WITH C.RDB$CHARACTER_SET_NAME EQ object FOR PRV IN RDB$USER_PRIVILEGES CROSS CS IN RDB$CHARACTER_SETS WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_charset AND PRV.RDB$RELATION_NAME EQ object AND CS.RDB$CHARACTER_SET_NAME EQ object AND @@ -1533,6 +1551,7 @@ processing_state SHOW_grants2 (const SCHAR* object, FOR FIRST 1 C IN RDB$COLLATIONS WITH C.RDB$COLLATION_NAME EQ object FOR PRV IN RDB$USER_PRIVILEGES CROSS COL IN RDB$COLLATIONS WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE = obj_collation AND PRV.RDB$RELATION_NAME EQ object AND COL.RDB$COLLATION_NAME EQ object AND @@ -1605,6 +1624,7 @@ processing_state SHOW_grants2 (const SCHAR* object, if (isqlGlob.major_ods >= ODS_VERSION12) { FOR PRV IN RDB$USER_PRIVILEGES WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE >= obj_database AND PRV.RDB$RELATION_NAME EQ object SORTED BY PRV.RDB$USER, PRV.RDB$GRANT_OPTION @@ -1822,6 +1842,7 @@ void SHOW_grant_roles2 (const SCHAR* terminator, // process role "object" FOR PRV IN RDB$USER_PRIVILEGES WITH + PRV.RDB$GRANTOR NOT MISSING AND PRV.RDB$OBJECT_TYPE EQ obj_sql_role AND (PRV.RDB$USER_TYPE EQ obj_user OR PRV.RDB$USER_TYPE EQ obj_sql_role) AND @@ -2090,11 +2111,11 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) if (*cmd[2] == '"') { remove_delimited_double_quotes(lcmd[2]); - ret = show_role(lcmd[2]); + ret = show_role(lcmd[2], false); } else { - ret = show_role(cmd[2]); + ret = show_role(cmd[2], false); } if (ret == OBJECT_NOT_FOUND) @@ -2102,7 +2123,7 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) } else { - ret = show_role(NULL); + ret = show_role(NULL, false); if (ret == OBJECT_NOT_FOUND) key = NO_ROLES; } @@ -2179,6 +2200,10 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) show_all_tables(1); break; + case ShowOptions::role: + show_role(NULL, true); + break; + default: return ps_ERR; } @@ -2193,6 +2218,8 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) show_sys_functions(msg); IUTILS_msg_get(MSG_COLLATIONS, msg); show_collations("", 1, msg, true); + IUTILS_msg_get(MSG_ROLES, msg); + show_role(NULL, true, msg); } break; @@ -5300,7 +5327,55 @@ static processing_state show_proc(const SCHAR* procname) } -static processing_state show_role(const SCHAR* object) +bool SHOW_system_privileges(const char* role, const char* prefix, bool lf) +{ + bool first = true; + + FOR X IN RDB$ROLES WITH + X.RDB$ROLE_NAME EQ role + { + if (!X.RDB$SYSTEM_PRIVILEGES.NULL) + { + for (unsigned byte = 0; byte < sizeof(X.RDB$SYSTEM_PRIVILEGES); ++byte) + { + char b = X.RDB$SYSTEM_PRIVILEGES[byte]; + for (int bit = 0; bit < 8; ++bit) + { + if (b & (1 << bit)) + { + FOR T IN RDB$TYPES + WITH T.RDB$FIELD_NAME EQ 'RDB$SYSTEM_PRIVILEGES' + AND T.RDB$TYPE EQ (byte * 8 + bit) + { + if (first) + { + if (lf) + isqlGlob.printf("%s", NEWLINE); + isqlGlob.printf("%s", prefix); + } + else + isqlGlob.printf(","); + + first = false; + isqlGlob.printf(" %s", fb_utils::exact_name(T.RDB$TYPE_NAME)); + } + END_FOR + } + } + } + } + } + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return false; + END_ERROR; + + return !first; +} + + +static processing_state show_role(const SCHAR* object, bool system, const char* msg) { if (isqlGlob.major_ods < ODS_VERSION9) return OBJECT_NOT_FOUND; @@ -5323,13 +5398,21 @@ static processing_state show_role(const SCHAR* object) bool system_flag = !X.RDB$SYSTEM_FLAG.NULL && X.RDB$SYSTEM_FLAG > 0; - if (!system_flag) + if (system_flag == system) { + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); first = false; + isqlGlob.printf("%38s%s", X.RDB$ROLE_NAME, (odd ? " " : NEWLINE)); odd = !odd; +/* + if (SHOW_system_privileges(X.RDB$ROLE_NAME, "System privileges:", !odd)) + { + isqlGlob.printf("%s", NEWLINE); + odd = true; + } */ } - END_FOR ON_ERROR ISQL_errmsg(fbStatus); @@ -5402,6 +5485,8 @@ static processing_state show_role(const SCHAR* object) if (first) return (OBJECT_NOT_FOUND); + else if (SHOW_system_privileges(object, "System privileges:", false)) + isqlGlob.printf("%s", NEWLINE); return (SKIP); } diff --git a/src/isql/show_proto.h b/src/isql/show_proto.h index 95073774ad..cbf0e5fe1e 100644 --- a/src/isql/show_proto.h +++ b/src/isql/show_proto.h @@ -38,5 +38,6 @@ processing_state SHOW_metadata(const SCHAR* const*, SCHAR**); void SHOW_read_owner(); const Firebird::string SHOW_trigger_action(SINT64); processing_state SHOW_maps(bool extract, const SCHAR* map_name); +bool SHOW_system_privileges(const char* role, const char* prfx, bool lf); #endif // ISQL_SHOW_PROTO_H diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 815a70fd9b..7f60eac297 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -326,7 +326,7 @@ public: /// former Database members - end - bool locksmith() const; + bool locksmith(thread_db* tdbb, SystemPrivilege sp) const; jrd_tra* getSysTransaction(); void setSysTransaction(jrd_tra* trans); // used only by TRA_init @@ -406,9 +406,9 @@ const ULONG ATT_mapping = 0x40000L; // Attachment used for mapping auth block const ULONG ATT_NO_CLEANUP = (ATT_no_cleanup | ATT_notify_gc); -inline bool Attachment::locksmith() const +inline bool Attachment::locksmith(thread_db* tdbb, SystemPrivilege sp) const { - return att_user && att_user->locksmith(); + return att_user && att_user->locksmith(tdbb, sp); } inline jrd_tra* Attachment::getSysTransaction() diff --git a/src/jrd/CryptoManager.cpp b/src/jrd/CryptoManager.cpp index b45c71b097..0ab5ec7f1f 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -677,7 +677,7 @@ namespace Jrd { // Establish temp context // Needed to take crypt thread lock UserId user; - user.usr_user_name = "(Crypt thread)"; + user.setUserName("(Crypt thread)"); Jrd::Attachment* const attachment = Jrd::Attachment::create(&dbb); RefPtr sAtt(FB_NEW SysStableAttachment(attachment)); @@ -707,7 +707,7 @@ namespace Jrd { // Establish context // Need real attachment in order to make classic mode happy ClumpletWriter writer(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); - writer.insertString(isc_dpb_user_name, SYSDBA_USER_NAME); + writer.insertString(isc_dpb_user_name, DBA_USER_NAME); writer.insertByte(isc_dpb_no_db_triggers, TRUE); RefPtr jAtt(REF_NO_INCR, dbb.dbb_provider->attachDatabase(&status_vector, diff --git a/src/jrd/DbCreators.cpp b/src/jrd/DbCreators.cpp index 18b72e5d94..8cccf972ca 100644 --- a/src/jrd/DbCreators.cpp +++ b/src/jrd/DbCreators.cpp @@ -54,6 +54,7 @@ using namespace Firebird; using namespace Jrd; +using namespace Auth; namespace { @@ -69,16 +70,15 @@ void check(const char* s, IStatus* st) bool openDb(const char* securityDb, RefPtr& att, RefPtr& tra) { - DispatcherPtr prov; - - ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, MAX_DPB_SIZE, isc_dpb_version1); - embeddedSysdba.insertString(isc_dpb_user_name, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME)); - embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE); - embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE); + ClumpletWriter embeddedAttach(ClumpletWriter::Tagged, MAX_DPB_SIZE, isc_dpb_version1); + embeddedAttach.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); + embeddedAttach.insertByte(isc_dpb_sec_attach, TRUE); + embeddedAttach.insertByte(isc_dpb_no_db_triggers, TRUE); FbLocalStatus st; + DispatcherPtr prov; att = prov->attachDatabase(&st, securityDb, - embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer()); + embeddedAttach.getBufferLength(), embeddedAttach.getBuffer()); if (st->getState() & IStatus::STATE_ERRORS) { if (!fb_utils::containsErrorCode(st->getErrors(), isc_io_error)) @@ -105,17 +105,16 @@ namespace Jrd { bool checkCreateDatabaseGrant(const MetaName& userName, const MetaName& trustedRole, const MetaName& sqlRole, const char* securityDb) { - if (userName == SYSDBA_USER_NAME) + if (userName == DBA_USER_NAME) return true; RefPtr att; RefPtr tra; - if (!openDb(securityDb, att, tra)) - return false; + bool hasDb = openDb(securityDb, att, tra); FbLocalStatus st; MetaName role(sqlRole); - if (role.hasData()) + if (hasDb && role.hasData()) { const UCHAR info[] = { isc_info_db_sql_dialect, isc_info_end }; UCHAR buffer[BUFFER_TINY]; @@ -140,9 +139,9 @@ bool checkCreateDatabaseGrant(const MetaName& userName, const MetaName& trustedR p += length; } - JRD_make_role_name(role, dialect); + UserId::makeRoleName(role, dialect); - // We need to check is admin role granted to userName in security DB + // We need to check is role granted to userName in security DB const char* sql = "select count(*) from RDB$USER_PRIVILEGES " "where RDB$USER = ? and RDB$RELATION_NAME = ? and RDB$PRIVILEGE = 'M'"; @@ -176,6 +175,10 @@ bool checkCreateDatabaseGrant(const MetaName& userName, const MetaName& trustedR if (role == ADMIN_ROLE) return true; + if (!hasDb) + return false; + + // check db creators table Message gr; Field uType(gr); Field u(gr, MAX_SQL_IDENTIFIER_LEN); @@ -204,7 +207,49 @@ bool checkCreateDatabaseGrant(const MetaName& userName, const MetaName& trustedR check("IAttachment::execute", &st); } - return cnt > 0; + if (cnt > 0) + return true; + + if (!role.hasData()) + role = "NONE"; + + Message par2; + Field uType2(par2); + Field u2(par2, MAX_SQL_IDENTIFIER_LEN); + Field r2(par2, MAX_SQL_IDENTIFIER_LEN); + uType2 = obj_user; + u2 = userName.c_str(); + r2 = role.c_str(); + + Message res2; + Field priv(res2, 8); + + const char* sql = + "with recursive role_tree as ( " + " select rdb$relation_name as nm, 0 as ur from rdb$user_privileges " + " where rdb$privilege = 'M' and rdb$field_name = 'D' and rdb$user_type = ? and rdb$user = ? " + " union all " + " select rdb$role_name as nm, 1 as ur from rdb$roles " + " where rdb$role_name = ? " + " union all " + " select p.rdb$relation_name as nm, t.ur from rdb$user_privileges p " + " join role_tree t on t.nm = p.rdb$user " + " where p.rdb$privilege = 'M' and (p.rdb$field_name = 'D' or t.ur = 1)) " + "select r.rdb$system_privileges " + " from role_tree t join rdb$roles r on t.nm = r.rdb$role_name "; + RefPtr rs(att->openCursor(&st, tra, 0, sql, + SQL_DIALECT_V6, par2.getMetadata(), par2.getBuffer(), res2.getMetadata(), NULL, 0)); + check("IAttachment::execute", &st); + + UserId::Privileges privileges, wrk; + while (rs->fetchNext(&st, res2.getBuffer()) == IStatus::RESULT_OK) + { + wrk.load(&priv); + privileges |= wrk; + } + check("IResultSet::fetchNext", &st); + + return wrk.test(CREATE_DATABASE); } diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 3dd536e32a..8f73c5cb6f 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -662,7 +662,7 @@ Firebird::ITransaction* ExtEngineManager::ExternalContextImpl::getTransaction( const char* ExtEngineManager::ExternalContextImpl::getUserName() { - return internalAttachment->att_user->usr_user_name.c_str(); + return internalAttachment->att_user->getUserName().c_str(); } const char* ExtEngineManager::ExternalContextImpl::getDatabaseName() diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index fea955f490..34735380b0 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -551,6 +551,8 @@ void resetMap(const char* securityDb) MAP_DEBUG(fprintf(stderr, "Empty cache for %s\n", securityDb)); } +void resetMap(const char* securityDb, ULONG index); + // ---------------------------------------------------- @@ -559,7 +561,8 @@ class MappingHeader : public Firebird::MemoryHeader public: SLONG currentProcess; ULONG processes; - char databaseForReset[1024]; + char databaseForReset[1024]; // database for which cache to be reset + ULONG resetIndex; // what cache to reset struct Process { @@ -617,7 +620,7 @@ public: sharedMemory->removeMapFile(); } - void clearMap(const char* dbName) + void clearCache(const char* dbName, USHORT index) { PathName target; expandDatabaseName(dbName, target, NULL); @@ -628,6 +631,7 @@ public: MappingHeader* sMem = sharedMemory->getHeader(); target.copyTo(sMem->databaseForReset, sizeof(sMem->databaseForReset)); + sMem->resetIndex = index; // Set currentProcess sMem->currentProcess = -1; @@ -648,7 +652,7 @@ public: { // did not find current process // better ignore delivery than fail in it - gds__log("MappingIpc::clearMap() failed to find current process %d in shared memory", processId); + gds__log("MappingIpc::clearCache() failed to find current process %d in shared memory", processId); return; } MappingHeader::Process* current = &sMem->process[sMem->currentProcess]; @@ -662,8 +666,8 @@ public: if (p->id == processId) { - MAP_DEBUG(fprintf(stderr, "Internal resetMap(%s)\n", sMem->databaseForReset)); - resetMap(sMem->databaseForReset); + MAP_DEBUG(fprintf(stderr, "Internal resetMap(%s, %d)\n", sMem->databaseForReset, sMem->resetIndex)); + resetMap(sMem->databaseForReset, sMem->resetIndex); continue; } @@ -770,9 +774,9 @@ private: if (p->flags & MappingHeader::FLAG_DELIVER) { - resetMap(sharedMemory->getHeader()->databaseForReset); - MappingHeader* sMem = sharedMemory->getHeader(); + resetMap(sMem->databaseForReset, sMem->resetIndex); + MappingHeader::Process* cur = &sMem->process[sMem->currentProcess]; if (sharedMemory->eventPost(&cur->callbackEvent) != FB_SUCCESS) { @@ -877,14 +881,246 @@ void setupIpc() mappingIpc->setup(); } +class DbHandle : public AutoPtr > +{ +public: + DbHandle() + : AutoPtr() + { } + + DbHandle(IAttachment* att) + : AutoPtr(att) + { + if (att) + att->addRef(); + } + + bool attach(FbLocalStatus& st, const char* aliasDb, ICryptKeyCallback* cryptCb) + { + bool down = false; // true if on attach db is shutdown + + if (hasData()) + return down; + + DispatcherPtr prov; + if (cryptCb) + { + prov->setDbCryptCallback(&st, cryptCb); + check("IProvider::setDbCryptCallback", &st); + } + + ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, 1024, isc_dpb_version1); + embeddedSysdba.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); + embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE); + embeddedSysdba.insertByte(isc_dpb_map_attach, TRUE); + embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE); + + IAttachment* att = prov->attachDatabase(&st, aliasDb, + embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer()); + + if (st->getState() & IStatus::STATE_ERRORS) + { + const ISC_STATUS* s = st->getErrors(); + bool missing = fb_utils::containsErrorCode(s, isc_io_error); + down = fb_utils::containsErrorCode(s, isc_shutdown); + if (!(missing || down)) + check("IProvider::attachDatabase", &st); + + // down/missing security DB is not a reason to fail mapping + } + else + reset(att); + + return down; + } +}; + + +const char* roleSql = +"with recursive role_tree as ( " +" select rdb$role_name as nm from rdb$roles " +" where rdb$role_name = ? " +" union all " +" select p.rdb$relation_name as nm from rdb$user_privileges p " +" join role_tree t on t.nm = p.rdb$user " +" where p.rdb$privilege = 'M') " +"select r.rdb$system_privileges from role_tree t " +" join rdb$roles r on t.nm = r.rdb$role_name " + ; + +const char* userSql = +"with recursive role_tree as ( " +" select rdb$relation_name as nm from rdb$user_privileges " +" where rdb$privilege = 'M' and rdb$field_name = 'D' and rdb$user = ? and rdb$user_type = 8 " +" union all " +" select p.rdb$relation_name as nm from rdb$user_privileges p " +" join role_tree t on t.nm = p.rdb$user " +" where p.rdb$privilege = 'M' and p.rdb$field_name = 'D') " +"select r.rdb$system_privileges from role_tree t " +" join rdb$roles r on t.nm = r.rdb$role_name " + ; + +class SysPrivCache : public PermanentStorage +{ +public: + SysPrivCache(MemoryPool& p) + : PermanentStorage(p), + databases(getPool()) + { } + + SyncObject* getSync() + { + return &sync; + } + + bool getPrivileges(const PathName& db, const string& name, const string& trusted_role, UserId::Privileges& system_privileges) + { + DbCache* c; + return databases.get(db, c) && c->getPrivileges(name, trusted_role, system_privileges); + } + + void populate(const PathName& db, DbHandle& iDb, const string& name, const string& trusted_role) + { + DbCache* c; + if (!databases.get(db, c)) + { + c = FB_NEW_POOL(getPool()) DbCache(getPool()); + *(databases.put(db)) = c; + } + c->populate(iDb, name, trusted_role); + + setupIpc(); + } + + void invalidate(const PathName& db) + { + DbCache* c; + if (databases.get(db, c)) + c->invalidate(); + } + +private: + class DbCache + { + public: + DbCache(MemoryPool& p) + : logins(p, userSql), + roles(p, roleSql) + { } + + bool getPrivileges(const string& name, const string& trusted_role, UserId::Privileges& system_privileges) + { + system_privileges.clearAll(); + return logins.getPrivileges(name, system_privileges) && + roles.getPrivileges(trusted_role, system_privileges); + } + + void populate(DbHandle& iDb, const string& name, const string& trusted_role) + { + logins.populate(name, iDb); + roles.populate(trusted_role, iDb); + } + + void invalidate() + { + logins.invalidate(); + roles.invalidate(); + } + + private: + class NameCache : private GenericMap > > + { + public: + NameCache(MemoryPool& p, const char* s) + : GenericMap(p), + sql(s) + { } + + bool getPrivileges(const string& key, UserId::Privileges& system_privileges) + { + if (!key.hasData()) + return false; + + UserId::Privileges p; + if (!get(key, p)) + return false; + + system_privileges |= p; + return true; + } + + void populate(const string& key, DbHandle& iDb) + { + if (!key.hasData()) + return; + + ThrowLocalStatus st; + RefPtr tra(REF_NO_INCR, iDb->startTransaction(&st, 0, NULL)); + + Message par; + Field user(par, MAX_SQL_IDENTIFIER_SIZE); + user = key.c_str(); + + RefPtr curs(REF_NO_INCR, iDb->openCursor(&st, tra, 0, sql, 3, + par.getMetadata(), par.getBuffer(), NULL, NULL, 0)); + + RefPtr meta(curs->getMetadata(&st)); + AutoPtr > buffer(FB_NEW UCHAR[meta->getMessageLength(&st)]); + UCHAR* bits = buffer + meta->getOffset(&st, 0); + UserId::Privileges g, l; + while(curs->fetchNext(&st, buffer) == IStatus::RESULT_OK) + { + l.load(bits); + g |= l; + } + put(key, g); + } + + void invalidate() + { + clear(); + } + + private: + const char* sql; + }; + + NameCache logins, roles; + }; + + SyncObject sync; + GenericMap > > databases; +}; + +InitInstance spCache; + +void resetMap(const char* db, ULONG index) +{ + switch(index) + { + case MAPPING_CACHE: + resetMap(db); + break; + + case SYSTEM_PRIVILEGES_CACHE: + spCache().invalidate(db); + break; + + default: + fb_assert(false); + break; + } +} + } // anonymous namespace namespace Jrd { -bool mapUser(string& name, string& trusted_role, Firebird::string* auth_method, - AuthReader::AuthBlock* newAuthBlock, const AuthReader::AuthBlock& authBlock, - const char* alias, const char* db, const char* securityAlias, - ICryptKeyCallback* cryptCb) +ULONG mapUser(const bool throwNotFoundError, + string& name, string& trusted_role, Firebird::string* auth_method, + AuthReader::AuthBlock* newAuthBlock, UserId::Privileges* system_privileges, + const AuthReader::AuthBlock& authBlock, const char* alias, const char* db, + const char* securityAlias, ICryptKeyCallback* cryptCb, Firebird::IAttachment* att) { AuthReader::Info info; @@ -903,7 +1139,7 @@ bool mapUser(string& name, string& trusted_role, Firebird::string* auth_method, } } - return false; + return 0; } // expand security database name (db is expected to be expanded, alias - original) @@ -912,6 +1148,8 @@ bool mapUser(string& name, string& trusted_role, Firebird::string* auth_method, const char* securityDb = secExpanded.c_str(); bool secDown = false; bool dbDown = false; + DbHandle iDb(att); + FbLocalStatus st; // Create new writer AuthWriter newBlock; @@ -928,161 +1166,91 @@ bool mapUser(string& name, string& trusted_role, Firebird::string* auth_method, } // Perform lock & map only when needed - if (flags != (FLAG_DB | FLAG_SEC)) + if ((flags != (FLAG_DB | FLAG_SEC)) && authBlock.hasData()) { AuthReader::Info info; SyncType syncType = SYNC_SHARED; - IAttachment* iDb = NULL; - IAttachment* iSec = NULL; - FbLocalStatus st; + DbHandle iSec; - try + for (;;) { - for (;;) + if (syncType == SYNC_EXCLUSIVE) { - if (syncType == SYNC_EXCLUSIVE) + if (!iSec) + iSec.attach(st, securityAlias, cryptCb); + + if (db && !iDb) + iDb.attach(st, alias, cryptCb); + } + + MutexEnsureUnlock g(treeMutex, FB_FUNCTION); + g.enter(); + + Cache* cDb = NULL; + if (db) + cDb = locate(alias, db); + Cache* cSec = locate(securityAlias, securityDb); + if (cDb == cSec) + cDb = NULL; + + SyncObject dummySync1, dummySync2; + Sync sDb(((!(flags & FLAG_DB)) && cDb) ? &cDb->syncObject : &dummySync1, FB_FUNCTION); + Sync sSec((!(flags & FLAG_SEC)) ? &cSec->syncObject : &dummySync2, FB_FUNCTION); + + sSec.lock(syncType); + if (!sDb.lockConditional(syncType)) + { + // Avoid deadlocks cause hell knows which db is security for which + sSec.unlock(); + // Now safely wait for sSec + sDb.lock(syncType); + // and repeat whole operation + continue; + } + + // Required cache(s) are locked somehow - release treeMutex + g.leave(); + + // Check is it required to populate caches from DB + if ((cDb && !cDb->dataFlag) || !cSec->dataFlag) + { + if (syncType != SYNC_EXCLUSIVE) { - DispatcherPtr prov; - if (cryptCb) - { - prov->setDbCryptCallback(&st, cryptCb); - check("IProvider::setDbCryptCallback", &st); - } - - ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, - MAX_DPB_SIZE, isc_dpb_version1); - embeddedSysdba.insertString(isc_dpb_user_name, SYSDBA_USER_NAME, - fb_strlen(SYSDBA_USER_NAME)); - embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE); - embeddedSysdba.insertByte(isc_dpb_map_attach, TRUE); - embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE); - - if (!iSec) - { - iSec = prov->attachDatabase(&st, securityAlias, - embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer()); - if (st->getState() & IStatus::STATE_ERRORS) - { - const ISC_STATUS* s = st->getErrors(); - bool missing = fb_utils::containsErrorCode(s, isc_io_error); - secDown = fb_utils::containsErrorCode(s, isc_shutdown); - if (!(missing || secDown)) - check("IProvider::attachDatabase", &st); - - // down/missing security DB is not a reason to fail mapping - iSec = NULL; - } - } - - if (db && !iDb) - { - iDb = prov->attachDatabase(&st, alias, - embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer()); - - if (st->getState() & IStatus::STATE_ERRORS) - { - const ISC_STATUS* s = st->getErrors(); - bool missing = fb_utils::containsErrorCode(s, isc_io_error); - dbDown = fb_utils::containsErrorCode(s, isc_shutdown); - if (!(missing || dbDown)) - check("IProvider::attachDatabase", &st); - - // down/missing DB is not a reason to fail mapping - iDb = NULL; - } - } - } - - MutexEnsureUnlock g(treeMutex, FB_FUNCTION); - g.enter(); - - Cache* cDb = NULL; - if (db) - cDb = locate(alias, db); - Cache* cSec = locate(securityAlias, securityDb); - if (cDb == cSec) - cDb = NULL; - - SyncObject dummySync1, dummySync2; - Sync sDb(((!(flags & FLAG_DB)) && cDb) ? &cDb->syncObject : &dummySync1, FB_FUNCTION); - Sync sSec((!(flags & FLAG_SEC)) ? &cSec->syncObject : &dummySync2, FB_FUNCTION); - - sSec.lock(syncType); - if (!sDb.lockConditional(syncType)) - { - // Avoid deadlocks cause hell knows which db is security for which + syncType = SYNC_EXCLUSIVE; sSec.unlock(); - // Now safely wait for sSec - sDb.lock(syncType); - // and repeat whole operation + sDb.unlock(); + continue; } - // Required cache(s) are locked somehow - release treeMutex - g.leave(); - - // Check is it required to populate caches from DB - if ((cDb && !cDb->dataFlag) || !cSec->dataFlag) - { - if (syncType != SYNC_EXCLUSIVE) - { - syncType = SYNC_EXCLUSIVE; - sSec.unlock(); - sDb.unlock(); - - continue; - } - - if (cDb) - cDb->populate(iDb, dbDown); - cSec->populate(iSec, secDown); - - sSec.downgrade(SYNC_SHARED); - sDb.downgrade(SYNC_SHARED); - } - - // use down flags from caches if (cDb) - dbDown = cDb->downFlag; - secDown = cSec->downFlag; + cDb->populate(iDb, dbDown); + cSec->populate(iSec, secDown); - // Caches are ready somehow - proceed with analysis - AuthReader auth(authBlock); + sSec.downgrade(SYNC_SHARED); + sDb.downgrade(SYNC_SHARED); + } - // Map in simple mode first main, next security db - if (cDb && cDb->map4(false, flags & FLAG_DB, auth, info, newBlock)) - break; - if (cSec->map4(false, flags & FLAG_SEC, auth, info, newBlock)) - break; + // use down flags from caches + if (cDb) + dbDown = cDb->downFlag; + secDown = cSec->downFlag; - // Map in wildcard mode first main, next security db - if (cDb && cDb->map4(true, flags & FLAG_DB, auth, info, newBlock)) - break; - cSec->map4(true, flags & FLAG_SEC, auth, info, newBlock); + // Caches are ready somehow - proceed with analysis + AuthReader auth(authBlock); + // Map in simple mode first main, next security db + if (cDb && cDb->map4(false, flags & FLAG_DB, auth, info, newBlock)) + break; + if (cSec->map4(false, flags & FLAG_SEC, auth, info, newBlock)) break; - } - if (iDb) - { - iDb->detach(&st); - check("IAttachment::detach", &st); - iDb = NULL; - } - if (iSec) - { - iSec->detach(&st); - check("IAttachment::detach", &st); - iSec = NULL; - } - } - catch (const Exception&) - { - if (iDb) - iDb->release(); - if (iSec) - iSec->release(); - throw; + // Map in wildcard mode first main, next security db + if (cDb && cDb->map4(true, flags & FLAG_DB, auth, info, newBlock)) + break; + cSec->map4(true, flags & FLAG_SEC, auth, info, newBlock); + + break; } for (AuthReader rdr(newBlock); rdr.getInfo(info); rdr.moveNext()) @@ -1134,35 +1302,69 @@ bool mapUser(string& name, string& trusted_role, Firebird::string* auth_method, } } + ULONG rc = secDown || dbDown ? MAPUSER_MAP_DOWN : 0; if (fName.found == Found::FND_NOTHING) { - Arg::Gds v(isc_sec_context); - v << alias; - if (secDown || dbDown) - v << Arg::Gds(isc_map_down); - v.raise(); + if (throwNotFoundError) + { + Arg::Gds v(isc_sec_context); + v << alias; + if (rc & MAPUSER_MAP_DOWN) + v << Arg::Gds(isc_map_down); + v.raise(); + } + rc |= MAPUSER_ERROR_NOT_THROWN; } - - name = fName.value.ToString(); - trusted_role = fRole.value.ToString(); - MAP_DEBUG(fprintf(stderr, "login=%s tr=%s\n", name.c_str(), trusted_role.c_str())); - if (auth_method) - *auth_method = fName.method.ToString(); - - if (newAuthBlock) + else { - newAuthBlock->shrink(0); - newAuthBlock->push(newBlock.getBuffer(), newBlock.getBufferLength()); - MAP_DEBUG(fprintf(stderr, "Saved to newAuthBlock %u bytes\n", - static_cast(newAuthBlock->getCount()))); + name = fName.value.ToString(); + trusted_role = fRole.value.ToString(); + MAP_DEBUG(fprintf(stderr, "login=%s tr=%s\n", name.c_str(), trusted_role.c_str())); + if (auth_method) + *auth_method = fName.method.ToString(); + + if (newAuthBlock) + { + newAuthBlock->shrink(0); + newAuthBlock->push(newBlock.getBuffer(), newBlock.getBufferLength()); + MAP_DEBUG(fprintf(stderr, "Saved to newAuthBlock %u bytes\n", + static_cast(newAuthBlock->getCount()))); + } } - return secDown || dbDown; + if (name.hasData() || trusted_role.hasData()) + { + if (system_privileges && db) + { + system_privileges->clearAll(); + + Sync sync(spCache().getSync(), FB_FUNCTION); + sync.lock(SYNC_SHARED); + + if (!spCache().getPrivileges(db, name, trusted_role, *system_privileges)) + { + sync.unlock(); + sync.lock(SYNC_EXCLUSIVE); + if (!spCache().getPrivileges(db, name, trusted_role, *system_privileges)) + { + if (!iDb) + iDb.attach(st, alias, cryptCb); + if (iDb) + { + spCache().populate(db, iDb, name, trusted_role); + spCache().getPrivileges(db, name, trusted_role, *system_privileges); + } + } + } + } + } + + return rc; } -void clearMap(const char* dbName) +void clearMappingCache(const char* dbName, USHORT index) { - mappingIpc->clearMap(dbName); + mappingIpc->clearCache(dbName, index); } const Format* GlobalMappingScan::getFormat(thread_db* tdbb, jrd_rel* relation) const @@ -1210,8 +1412,8 @@ RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) { ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, MAX_DPB_SIZE, isc_dpb_version1); - embeddedSysdba.insertString(isc_dpb_user_name, SYSDBA_USER_NAME, - fb_strlen(SYSDBA_USER_NAME)); + embeddedSysdba.insertString(isc_dpb_user_name, DBA_USER_NAME, + fb_strlen(DBA_USER_NAME)); embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE); embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE); diff --git a/src/jrd/Mapping.h b/src/jrd/Mapping.h index 01fe221a6e..8ccf54a03a 100644 --- a/src/jrd/Mapping.h +++ b/src/jrd/Mapping.h @@ -34,13 +34,23 @@ #include "../common/classes/ClumpletReader.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/Monitoring.h" +#include "../jrd/scl.h" namespace Jrd { -bool mapUser(Firebird::string& name, Firebird::string& trusted_role, Firebird::string* auth_method, - Firebird::AuthReader::AuthBlock* newAuthBlock, const Firebird::AuthReader::AuthBlock& authBlock, - const char* alias, const char* db, const char* securityDb, Firebird::ICryptKeyCallback* cryptCb); -void clearMap(const char* dbName); +ULONG mapUser(const bool throwNotFoundError, + Firebird::string& name, Firebird::string& trusted_role, Firebird::string* auth_method, + Firebird::AuthReader::AuthBlock* newAuthBlock, UserId::Privileges* system_privileges, + const Firebird::AuthReader::AuthBlock& authBlock, const char* alias, const char* db, + const char* securityDb, Firebird::ICryptKeyCallback* cryptCb, Firebird::IAttachment* att); +// bits returned by mapUser +const ULONG MAPUSER_ERROR_NOT_THROWN = 1; +const ULONG MAPUSER_MAP_DOWN = 2; + +void clearMappingCache(const char* dbName, USHORT index); +// possible index values +const USHORT MAPPING_CACHE = 0; +const USHORT SYSTEM_PRIVILEGES_CACHE = 1; class GlobalMappingScan: public VirtualTableScan { diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index 985fbdbf59..a00d15e04d 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -391,8 +391,8 @@ MonitoringSnapshot::MonitoringSnapshot(thread_db* tdbb, MemoryPool& pool) // Enumerate active sessions - const MetaName& user_name = attachment->att_user->usr_user_name; - const bool locksmith = attachment->locksmith(); + const MetaName& user_name = attachment->att_user->getUserName(); + const bool locksmith = attachment->locksmith(tdbb, MONITOR_ANY_ATTACHMENT); const char* user_name_ptr = locksmith ? NULL : user_name.c_str(); MonitoringData::SessionList sessions(pool); @@ -876,7 +876,7 @@ void Monitoring::putAttachment(SnapshotData::DumpRecord& record, const Jrd::Atta ISC_systemToUtf8(attName); // user (MUST BE ALWAYS THE FIRST ITEM PASSED!) - record.storeString(f_mon_att_user, attachment->att_user->usr_user_name); + record.storeString(f_mon_att_user, attachment->att_user->getUserName()); // attachment id record.storeInteger(f_mon_att_id, attachment->att_attachment_id); // process id @@ -886,7 +886,7 @@ void Monitoring::putAttachment(SnapshotData::DumpRecord& record, const Jrd::Atta // attachment name record.storeString(f_mon_att_name, attName); // role - record.storeString(f_mon_att_role, attachment->att_user->usr_sql_role_name); + record.storeString(f_mon_att_role, attachment->att_user->getSqlRole()); // remote protocol record.storeString(f_mon_att_remote_proto, attachment->att_network_protocol); // remote address @@ -1240,7 +1240,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) attachment->mergeStats(); const AttNumber att_id = attachment->att_attachment_id; - const MetaName& user_name = attachment->att_user->usr_user_name; + const MetaName& user_name = attachment->att_user->getUserName(); if (!dbb->dbb_monitoring_data) dbb->dbb_monitoring_data = FB_NEW_POOL(pool) MonitoringData(dbb); @@ -1309,7 +1309,7 @@ void Monitoring::publishAttachment(thread_db* tdbb) if (!dbb->dbb_monitoring_data) dbb->dbb_monitoring_data = FB_NEW_POOL(*dbb->dbb_permanent) MonitoringData(dbb); - const MetaName& user_name = attachment->att_user->usr_user_name; + const MetaName& user_name = attachment->att_user->getUserName(); MonitoringData::Guard guard(dbb->dbb_monitoring_data); dbb->dbb_monitoring_data->setup(attachment->att_attachment_id, user_name.c_str()); diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 8ed5d03b24..8d9195816e 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -45,6 +45,7 @@ #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/tra_proto.h" +#include "../jrd/scl_proto.h" #include "../common/os/guid.h" #include "../jrd/license.h" #include "../jrd/trace/TraceManager.h" @@ -186,6 +187,7 @@ dsc* evlRoleInUse(thread_db* tdbb, const SysFunction* function, const NestValueA dsc* evlRound(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlSign(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlSqrt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlSystemPrivilege(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlUuidToChar(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); @@ -2231,17 +2233,23 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar } else if (nameStr == CURRENT_USER_NAME) { - if (!attachment->att_user || attachment->att_user->usr_user_name.isEmpty()) - return NULL; + MetaName user; + if (attachment->att_user) + user = attachment->att_user->getUserName(); - resultStr = attachment->att_user->usr_user_name.c_str(); + if (user.isEmpty()) + return NULL; + resultStr = user.c_str(); } else if (nameStr == CURRENT_ROLE_NAME) { - if (!attachment->att_user || attachment->att_user->usr_sql_role_name.isEmpty()) - return NULL; + MetaName role; + if (attachment->att_user) + role = attachment->att_user->getSqlRole(); - resultStr = attachment->att_user->usr_sql_role_name.c_str(); + if (role.isEmpty()) + return NULL; + resultStr = role.c_str(); } else if (nameStr == TRANSACTION_ID_NAME) resultStr.printf("%" SQUADFORMAT, transaction->tra_number); @@ -3803,13 +3811,36 @@ dsc* evlRoleInUse(thread_db* tdbb, const SysFunction*, const NestValueArray& arg string roleStr(MOV_make_string2(tdbb, value, ttype_none)); roleStr.upper(); - impure->vlu_misc.vlu_uchar = attachment->att_user->usr_granted_roles.exist(roleStr) ? 1 : 0; + impure->vlu_misc.vlu_uchar = attachment->att_user->roleInUse(tdbb, roleStr.c_str()) ? FB_TRUE : FB_FALSE; impure->vlu_desc.makeBoolean(&impure->vlu_misc.vlu_uchar); return &impure->vlu_desc; } + +dsc* evlSystemPrivilege(thread_db* tdbb, const SysFunction*, const NestValueArray& args, + impure_value* impure) +{ + fb_assert(args.getCount() == 1); + + jrd_req* request = tdbb->getRequest(); + Jrd::Attachment* attachment = tdbb->getAttachment(); + + const dsc* value = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + string privStr(MOV_make_string2(tdbb, value, ttype_none)); + privStr.upper(); + USHORT p = SCL_convert_privilege(tdbb, tdbb->getTransaction(), privStr); + + impure->vlu_misc.vlu_uchar = attachment->att_user->locksmith(tdbb, p) ? FB_TRUE : FB_FALSE; + impure->vlu_desc.makeBoolean(&impure->vlu_misc.vlu_uchar); + + return &impure->vlu_desc; +} + } // anonymous namespace @@ -3862,6 +3893,7 @@ const SysFunction SysFunction::functions[] = {RDB_GET_CONTEXT, 2, 2, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, {"RDB$ROLE_IN_USE", 1, 1, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, {RDB_SET_CONTEXT, 3, 3, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, + {"RDB$SYSTEM_PRIVILEGE", 1, 1, setParamsAsciiVal, makeBooleanResult, evlSystemPrivilege, NULL}, {"REPLACE", 3, 3, setParamsFromList, makeReplace, evlReplace, NULL}, {"REVERSE", 1, 1, NULL, makeReverse, evlReverse, NULL}, {"RIGHT", 2, 2, setParamsSecondInteger, makeLeftRight, evlRight, NULL}, diff --git a/src/jrd/SystemPrivileges.h b/src/jrd/SystemPrivileges.h new file mode 100644 index 0000000000..f8f7317a3d --- /dev/null +++ b/src/jrd/SystemPrivileges.h @@ -0,0 +1,49 @@ +/* license? */ + +#if defined(SYSTEM_PRIVILEGE) || (!defined(FB_JRD_SYSTEM_PRIVILEGES)) + +#ifndef SYSTEM_PRIVILEGE +#define SYSTEM_PRIVILEGE(p) p, +#define FB_JRD_SYSTEM_PRIVILEGES +#define FB_JRD_SYSTEM_PRIVILEGES_TMP + +namespace Jrd { +enum SystemPrivilege +{ +NULL_PRIVILEGE, +#endif + +SYSTEM_PRIVILEGE(USER_MANAGEMENT) +SYSTEM_PRIVILEGE(READ_RAW_PAGES) +SYSTEM_PRIVILEGE(CREATE_USER_TYPES) +SYSTEM_PRIVILEGE(USE_NBACKUP_UTILITY) +SYSTEM_PRIVILEGE(CHANGE_SHUTDOWN_MODE) +SYSTEM_PRIVILEGE(TRACE_ANY_ATTACHMENT) +SYSTEM_PRIVILEGE(MONITOR_ANY_ATTACHMENT) +SYSTEM_PRIVILEGE(ACCESS_SHUTDOWN_DATABASE) +SYSTEM_PRIVILEGE(CREATE_DATABASE) +SYSTEM_PRIVILEGE(DROP_DATABASE) +SYSTEM_PRIVILEGE(USE_GBAK_UTILITY) +SYSTEM_PRIVILEGE(USE_GSTAT_UTILITY) +SYSTEM_PRIVILEGE(USE_GFIX_UTILITY) +SYSTEM_PRIVILEGE(IGNORE_DB_TRIGGERS) +SYSTEM_PRIVILEGE(CHANGE_HEADER_SETTINGS) +SYSTEM_PRIVILEGE(SELECT_ANY_OBJECT_IN_DATABASE) +SYSTEM_PRIVILEGE(ACCESS_ANY_OBJECT_IN_DATABASE) +SYSTEM_PRIVILEGE(MODIFY_ANY_OBJECT_IN_DATABASE) +SYSTEM_PRIVILEGE(CHANGE_MAPPING_RULES) +SYSTEM_PRIVILEGE(USE_GRANTED_BY_CLAUSE) +SYSTEM_PRIVILEGE(GRANT_REVOKE_ON_ANY_OBJECT) +SYSTEM_PRIVILEGE(GRANT_REVOKE_ANY_DDL_RIGHT) +SYSTEM_PRIVILEGE(CREATE_PRIVILEGED_ROLES) + +#ifdef FB_JRD_SYSTEM_PRIVILEGES_TMP +maxSystemPrivilege +}; +} // namespace Jrd + +#undef SYSTEM_PRIVILEGE +#undef FB_JRD_SYSTEM_PRIVILEGES_TMP +#endif + +#endif diff --git a/src/jrd/UserManagement.cpp b/src/jrd/UserManagement.cpp index e4f85ff7e0..08677180b6 100644 --- a/src/jrd/UserManagement.cpp +++ b/src/jrd/UserManagement.cpp @@ -47,12 +47,12 @@ namespace // ILogonInfo implementation const char* name() { - return att->att_user->usr_user_name.c_str(); + return att->att_user->getUserName().c_str(); } const char* role() { - return att->att_user->usr_sql_role_name.c_str(); + return att->att_user->getSqlRole().c_str(); } const char* networkProtocol() @@ -479,7 +479,7 @@ void UserManagement::list(IUser* u, unsigned cachePosition) const char* uname = u->userName()->get(); putField(threadDbb, record, DumpField(f_sec_user_name, VALUE_STRING, static_cast(strlen(uname)), uname)); - su = strcmp(uname, SYSDBA_USER_NAME) == 0; + su = strcmp(uname, DBA_USER_NAME) == 0; } if (u->firstName()->entered()) diff --git a/src/jrd/acl.h b/src/jrd/acl.h index 54456c0d1d..abf7540984 100644 --- a/src/jrd/acl.h +++ b/src/jrd/acl.h @@ -73,7 +73,9 @@ const int id_sql_role = 11; // SQL role const int id_package = 12; // Package name const int id_function = 13; // Function name const int id_filter = 14; // Filter name -const int id_max = 15; +// New in FB4 +const int id_privilege = 15; // System privilege +const int id_max = 16; /* Format of access control list: diff --git a/src/jrd/cch.cpp b/src/jrd/cch.cpp index f8425d1085..266f67d773 100644 --- a/src/jrd/cch.cpp +++ b/src/jrd/cch.cpp @@ -2860,7 +2860,7 @@ static THREAD_ENTRY_DECLARE cache_writer(THREAD_ENTRY_PARAM arg) try { UserId user; - user.usr_user_name = "Cache Writer"; + user.setUserName("Cache Writer"); Jrd::Attachment* const attachment = Jrd::Attachment::create(dbb); RefPtr sAtt(FB_NEW SysStableAttachment(attachment)); diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 67a66929ba..3a8f96de52 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -86,7 +86,7 @@ const char* const NULL_ROLE = "NONE"; // User name assigned to any user granted USR_locksmith rights. // If this name is changed, modify also the trigger in // jrd/grant.gdl (which turns into jrd/trig.h. -const char* const SYSDBA_USER_NAME = "SYSDBA"; +const char* const DBA_USER_NAME = "SYSDBA"; const char* const PRIMARY_KEY = "PRIMARY KEY"; const char* const FOREIGN_KEY = "FOREIGN KEY"; diff --git a/src/jrd/dflt.h b/src/jrd/dflt.h index 3487d67d81..4cdc135879 100644 --- a/src/jrd/dflt.h +++ b/src/jrd/dflt.h @@ -55,7 +55,7 @@ static const UCHAR dflt_full[] = }; /* default value of "RESTRICT" for RDB$UPDATE_RULE, RDB$DELETE_RULE in - RDB$REF_CONSTRAINTS */ + RDB$REF_CONSTRAINTS */ static const UCHAR dflt_restrict[] = { @@ -65,5 +65,15 @@ static const UCHAR dflt_restrict[] = blr_eoc }; +/* default value of all "\000" for RDB$SYSTEM_PRIVILEGES */ + +static const UCHAR dflt_no_privs[] = +{ + blr_version5, + blr_literal, blr_text2, TWOBYTES(ttype_binary), 8, 0, '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', + blr_eoc +}; + #endif // JRD_DFLT_H diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 90569b4643..10258c1b8e 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -483,7 +483,7 @@ static bool drop_package_body(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool grant_privileges(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool db_crypt(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool set_linger(thread_db*, SSHORT, DeferredWork*, jrd_tra*); -static bool clear_mapping(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool clear_cache(thread_db*, SSHORT, DeferredWork*, jrd_tra*); // ---------------------------------------------------------------- @@ -1177,7 +1177,7 @@ static const deferred_task task_table[] = { dfw_store_view_context_type, store_view_context_type }, { dfw_db_crypt, db_crypt }, { dfw_set_linger, set_linger }, - { dfw_clear_mapping, clear_mapping }, + { dfw_clear_cache, clear_cache }, { dfw_null, NULL } }; @@ -2234,11 +2234,11 @@ static bool set_linger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr return false; } -static bool clear_mapping(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) +static bool clear_cache(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) { /************************************** * - * c l e a r _ m a p p i n g + * c l e a r _ c a c h e * ************************************** * @@ -2256,7 +2256,7 @@ static bool clear_mapping(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd return true; case 3: - clearMap(dbb->dbb_filename.c_str()); + clearMappingCache(dbb->dbb_filename.c_str(), work->dfw_id); break; } diff --git a/src/jrd/drq.h b/src/jrd/drq.h index 803208d1d5..d43c32b7c8 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -130,7 +130,6 @@ enum drq_type_t drq_gcg4, // grantor_can_grant drq_gcg5, // grantor_can_grant drq_l_view_idx, // table is view? - drq_role_gens, // store SQL role drq_get_role_nm, // get SQL role drq_get_role_au, // get SQL role auth drq_del_role_1, // delete SQL role from rdb$user_privilege diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index dc8ee70fa4..fb8f1a6967 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -147,9 +147,9 @@ void InternalConnection::attach(thread_db* tdbb, const PathName& dbName, setWrapErrors(false); Jrd::Attachment* attachment = tdbb->getAttachment(); - if ((user.isEmpty() || user == attachment->att_user->usr_user_name) && + if ((user.isEmpty() || user == attachment->att_user->getUserName()) && pwd.isEmpty() && - (role.isEmpty() || role == attachment->att_user->usr_sql_role_name)) + (role.isEmpty() || role == attachment->att_user->getSqlRole())) { m_isCurrent = true; m_attachment = attachment->getInterface(); @@ -249,9 +249,9 @@ bool InternalConnection::isSameDatabase(thread_db* tdbb, const PathName& dbName, if (m_isCurrent) { const UserId* attUser = m_attachment->getHandle()->att_user; - return ((user.isEmpty() || user == attUser->usr_user_name) && + return ((user.isEmpty() || user == attUser->getUserName()) && pwd.isEmpty() && - (role.isEmpty() || role == attUser->usr_sql_role_name)); + (role.isEmpty() || role == attUser->getSqlRole())); } return Connection::isSameDatabase(tdbb, dbName, user, pwd, role); diff --git a/src/jrd/fields.h b/src/jrd/fields.h index f6576efed6..52e8d44607 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -192,3 +192,5 @@ FIELD(fld_gen_increment , nam_gen_increment , dtype_long , sizeof(SLONG) , 0 , NULL , false) 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) diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index f844e0ac69..646d0327a0 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -27,6 +27,8 @@ #include "firebird.h" #include #include +#include + #include "../jrd/jrd.h" #include "../jrd/scl.h" #include "../jrd/acl.h" @@ -624,6 +626,11 @@ static void grant_user(Acl& acl, CHECK_AND_MOVE(acl, id_view); break; + case obj_privilege: + CHECK_AND_MOVE(acl, id_privilege); + fb_assert(isdigit(user[0])); + break; + default: BUGCHECK(292); // Illegal user_type @@ -1043,6 +1050,13 @@ static SecurityClass::flags_t squeeze_acl(Acl& acl, const Firebird::MetaName& us hit = false; break; + case id_privilege: + if (user_type != obj_privilege) + hit = false; + if (check_string(a, user)) + hit = false; + break; + default: BUGCHECK(293); // bad ACL } diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index c8437e2875..003d87b95c 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -569,11 +569,11 @@ void INF_database_info(thread_db* tdbb, case isc_info_user_names: // Assumes user names will be smaller than sizeof(buffer) - 1. - if (!(tdbb->getAttachment()->locksmith())) + if (!(tdbb->getAttachment()->locksmith(tdbb, USER_MANAGEMENT))) { const UserId* user = tdbb->getAttachment()->att_user; - const char* uname = (user && user->usr_user_name.hasData()) ? - user->usr_user_name.c_str() : ""; + const char* uname = (user && user->getUserName().hasData()) ? + user->getUserName().c_str() : ""; const SSHORT len = static_cast(strlen(uname)); *p++ = len; memcpy(p, uname, len); @@ -595,8 +595,8 @@ void INF_database_info(thread_db* tdbb, if (user) { - const char* user_name = user->usr_user_name.hasData() ? - user->usr_user_name.c_str() : "(Firebird Worker Thread)"; + const char* user_name = user->getUserName().hasData() ? + user->getUserName().c_str() : "(Firebird Worker Thread)"; p = buffer; const SSHORT len = static_cast(strlen(user_name)); *p++ = len; @@ -729,7 +729,7 @@ void INF_database_info(thread_db* tdbb, break; case fb_info_page_contents: - if (tdbb->getAttachment()->locksmith()) + if (tdbb->getAttachment()->locksmith(tdbb, READ_RAW_PAGES)) { length = gds__vax_integer(items, 2); items += 2; diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 9bd9663c4a..ebf8a6abb7 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -58,6 +58,7 @@ #include "../jrd/IntlManager.h" #include "../jrd/PreparedStatement.h" #include "../jrd/constants.h" +#include "../jrd/grant_proto.h" using namespace Firebird; using namespace Jrd; @@ -80,7 +81,11 @@ static void store_intlnames(thread_db*, const MetaName&); static void store_message(thread_db*, const trigger_msg*, AutoRequest&); static void store_relation_field(thread_db*, const int*, const int*, int, AutoRequest&); static void store_trigger(thread_db*, const jrd_trg*, AutoRequest&); - +static void store_admin_grant(thread_db*, const char* grantee, USHORT grantee_type, + const char* object, USHORT object_type, const char* prvl, USHORT option = 0, bool dflt = false); +static void store_admin_role_grant(thread_db*, const char*); +static void store_admin_role(thread_db*, const MetaName&, MetaName, UserId::Privileges*); +static bool get_charset_by_text_type(SSHORT& charSet, const USHORT sub_type); // This is the table used in defining triggers; note that // RDB$TRIGGER_0 was first changed to RDB$TRIGGER_7 to make it easier to @@ -318,25 +323,18 @@ void INI_format(const char* owner, const char* charset) rdbRelationId = USER_DEF_REL_INIT_ID; ps->execute(tdbb, transaction); - // Store ADMIN_ROLE record + // Store system role - handle1.reset(); + store_admin_role(tdbb, ADMIN_ROLE, ownerName, NULL); - STORE(REQUEST_HANDLE handle1) X IN RDB$ROLES - { - PAD (ADMIN_ROLE, X.RDB$ROLE_NAME); - X.RDB$ROLE_NAME.NULL = FALSE; - if (ownerName.hasData()) - PAD(ownerName.c_str(), X.RDB$OWNER_NAME); - else - PAD(SYSDBA_USER_NAME, X.RDB$OWNER_NAME); - X.RDB$OWNER_NAME.NULL = FALSE; - X.RDB$SYSTEM_FLAG = 1; // We have single system role, always treat it as privileged - X.RDB$SYSTEM_FLAG.NULL = FALSE; - } - END_STORE + // Store grants of admin role to valuable users + + store_admin_role_grant(tdbb, DBA_USER_NAME); + if (ownerName != DBA_USER_NAME) + store_admin_role_grant(tdbb, ownerName.c_str()); // Create indices for system relations + add_index_set(tdbb); // Create parameter types @@ -549,6 +547,22 @@ void INI_format(const char* owner, const char* charset) for (const trigger_msg* message = trigger_messages; message->trigmsg_name; ++message) store_message(tdbb, message, handle1); + // Add additional grants + + MetaName buf; + + buf.printf("%d", USE_NBACKUP_UTILITY); + store_admin_grant(tdbb, buf.c_str(), obj_privilege, "RDB$BACKUP_HISTORY", obj_relation, "SIUDR"); + GRANT_privileges(tdbb, "RDB$BACKUP_HISTORY", obj_relation, attachment->getSysTransaction()); + + buf.printf("%d", CREATE_USER_TYPES); + store_admin_grant(tdbb, buf.c_str(), obj_privilege, "RDB$TYPES", obj_relation, "SIUDR"); + GRANT_privileges(tdbb, "RDB$TYPES", obj_relation, attachment->getSysTransaction()); + + buf.printf("%d", GRANT_REVOKE_ANY_DDL_RIGHT); + store_admin_grant(tdbb, buf.c_str(), obj_privilege, "RDB$DB_CREATORS", obj_relation, "SIUDR"); + GRANT_privileges(tdbb, "RDB$DB_CREATORS", obj_relation, attachment->getSysTransaction()); + DFW_perform_system_work(tdbb); } @@ -647,9 +661,7 @@ void INI_init(thread_db* tdbb) if (desc->isText()) { - if (gfield->gfld_sub_type & dsc_text_type_metadata) - desc->dsc_sub_type = CS_METADATA; - else + if (!get_charset_by_text_type(desc->dsc_sub_type, gfield->gfld_sub_type)) desc->dsc_sub_type = CS_NONE; } else @@ -758,7 +770,7 @@ void INI_init_dsql(thread_db* tdbb, dsql_dbb* database) relation->rel_id = relfld[RFLD_R_ID]; relation->rel_name = names[relfld[RFLD_R_NAME]]; - relation->rel_owner = SYSDBA_USER_NAME; + relation->rel_owner = DBA_USER_NAME; relation->rel_dbkey_length = 8; dsql_fld** ptr = &relation->rel_fields; @@ -798,20 +810,7 @@ void INI_init_dsql(thread_db* tdbb, dsql_dbb* database) } if (DTYPE_IS_TEXT(gfield->gfld_dtype)) - { - switch (gfield->gfld_sub_type) - { - case dsc_text_type_metadata: - field->charSetId = CS_METADATA; - break; - case dsc_text_type_ascii: - field->charSetId = CS_ASCII; - break; - case dsc_text_type_fixed: - field->charSetId = CS_BINARY; - break; - } - } + get_charset_by_text_type(field->charSetId, gfield->gfld_sub_type); if (gfield->gfld_nullable) field->flags |= FLD_nullable; @@ -1205,6 +1204,65 @@ static void add_security_to_sys_obj(thread_db* tdbb, } +static void store_admin_grant(thread_db* tdbb, const char* user, USHORT user_type, + const char* object, USHORT object_type, const char* prvl, USHORT option, bool dflt) +{ + Attachment* attachment = tdbb->getAttachment(); // needed for preprocessed code + AutoRequest handle; + + while (*prvl) + { + STORE(REQUEST_HANDLE handle) PRIV IN RDB$USER_PRIVILEGES + PAD(user, PRIV.RDB$USER); + PAD(object, PRIV.RDB$RELATION_NAME); + if (dflt) + { + PRIV.RDB$FIELD_NAME[0] = 'D'; + PRIV.RDB$FIELD_NAME[1] = 0; + PRIV.RDB$FIELD_NAME.NULL = FALSE; + } + else + PRIV.RDB$FIELD_NAME.NULL = TRUE; + PRIV.RDB$PRIVILEGE[0] = *prvl++; + PRIV.RDB$PRIVILEGE[1] = 0; + PRIV.RDB$GRANT_OPTION = option; + PRIV.RDB$USER_TYPE = user_type; + PRIV.RDB$OBJECT_TYPE = object_type; + PRIV.RDB$GRANTOR.NULL = TRUE; + END_STORE + } +} + + +static void store_admin_role_grant(thread_db* tdbb, const char* user) +{ + store_admin_grant(tdbb, user, obj_user, ADMIN_ROLE, obj_sql_role, "M", 2, true); +} + + +static void store_admin_role(thread_db* tdbb, const MetaName& roleName, MetaName ownerName, + UserId::Privileges* priv) +{ + if (!ownerName.hasData()) + ownerName = DBA_USER_NAME; + + string p; + if (priv) + priv->store(p.getBuffer(priv->BYTES_COUNT)); + else + p = INI_owner_privileges(); + + PreparedStatement::Builder sql; + sql << "insert into rdb$roles(rdb$role_name, rdb$owner_name, rdb$system_flag, rdb$system_privileges)" + << "values (" << roleName << "," << ownerName << ", 1," << p << ")"; + + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* transaction = attachment->getSysTransaction(); + AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, sql)); + ps->execute(tdbb, transaction); +} + + // Add security class. static void add_security_class(thread_db* tdbb, const MetaName& class_name, USHORT acl_length, const UCHAR* acl) { @@ -1574,7 +1632,7 @@ static void store_trigger(thread_db* tdbb, const jrd_trg* trigger, AutoRequest& * **************************************/ SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); + Attachment* attachment = tdbb->getAttachment(); // indicate that the relation format needs revising dsc desc; @@ -1598,3 +1656,30 @@ static void store_trigger(thread_db* tdbb, const jrd_trg* trigger, AutoRequest& } END_STORE } + + +string INI_owner_privileges() +{ + return string(gfields[fld_system_privileges].gfld_length, '\xff'); +} + + +static bool get_charset_by_text_type(SSHORT& charSet, const USHORT sub_type) +{ + switch (sub_type) + { + case dsc_text_type_metadata: + charSet = CS_METADATA; + break; + case dsc_text_type_ascii: + charSet = CS_ASCII; + break; + case dsc_text_type_fixed: + charSet = CS_BINARY; + break; + default: + return false; + } + return true; +} + diff --git a/src/jrd/ini.h b/src/jrd/ini.h index d5b4781774..b10f80fbcc 100644 --- a/src/jrd/ini.h +++ b/src/jrd/ini.h @@ -159,6 +159,13 @@ static const int relfields[] = #undef FIELD #undef END_RELATION +//****************************** +// SystemPrivileges.h +// should go before types.h +//****************************** + +#include "SystemPrivileges.h" + //****************************** // types.h //****************************** diff --git a/src/jrd/ini_proto.h b/src/jrd/ini_proto.h index 19a0137478..8c35458751 100644 --- a/src/jrd/ini_proto.h +++ b/src/jrd/ini_proto.h @@ -34,5 +34,6 @@ USHORT INI_get_trig_flags(const TEXT*); void INI_init(Jrd::thread_db*); void INI_init2(Jrd::thread_db*); void INI_init_dsql(Jrd::thread_db*, Jrd::dsql_dbb* database); +Firebird::string INI_owner_privileges(); #endif // JRD_INI_PROTO_H diff --git a/src/jrd/irq.h b/src/jrd/irq.h index f21ca71951..2347a5711a 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -106,6 +106,7 @@ enum irq_type_t irq_l_check2, // lookup constraint for index irq_c_trg_perm, // check if trig can ignore perm. checks irq_get_role_name, // get SQL role name + irq_get_priv_bit, // get privilege bit by name irq_is_admin_role, // check is current role admin or not irq_get_att_class, // get security class for current attachment irq_format6, // make a new format for a record diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 3a84e37825..99ebb888c9 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -73,6 +73,7 @@ #include "../intl/charsets.h" #include "../jrd/sort.h" #include "../jrd/PreparedStatement.h" +#include "../jrd/ResultSet.h" #include "../dsql/StmtNodes.h" #include "../jrd/blb_proto.h" @@ -727,15 +728,27 @@ namespace }; - void validateAccess(const Jrd::Attachment* attachment) + void validateAccess(thread_db* tdbb, Jrd::Attachment* attachment, SystemPrivilege sp) { - if (!attachment->locksmith()) + if (!attachment->locksmith(tdbb, sp)) { + PreparedStatement::Builder sql; + MetaName missPriv("UNKNOWN"); + sql << "select" << sql("rdb$type_name", missPriv) << "from rdb$types" + << "where rdb$field_name = 'RDB$SYSTEM_PRIVILEGES'" + << " and rdb$type =" << SSHORT(sp); + jrd_tra* transaction = attachment->getSysTransaction(); + AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, sql)); + AutoResultSet rs(ps->executeQuery(tdbb, transaction)); + rs->fetch(tdbb); + UserId* u = attachment->att_user; - if (u->usr_flags & USR_mapdown) - ERR_post(Arg::Gds(isc_adm_task_denied) << Arg::Gds(isc_map_down)); - else - ERR_post(Arg::Gds(isc_adm_task_denied)); + Arg::Gds err(isc_miss_prvlg); + err << missPriv; + if (u->testFlag(USR_mapdown)) + err << Arg::Gds(isc_map_down); + + ERR_post(err); } } @@ -981,7 +994,7 @@ public: // TraceConnection implementation unsigned getKind() { return KIND_DATABASE; }; int getProcessID() { return m_options->dpb_remote_pid; } - const char* getUserName() { return m_id.usr_user_name.c_str(); } + const char* getUserName() { return m_id.getUserName().c_str(); } const char* getRoleName() { return m_options->dpb_role_name.c_str(); } const char* getCharSet() { return m_options->dpb_lc_ctype.c_str(); } const char* getRemoteProtocol() { return m_options->dpb_network_protocol.c_str(); } @@ -1027,7 +1040,6 @@ static void rollback(thread_db*, jrd_tra*, const bool); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*, const RefPtr*, bool, ICryptKeyCallback*); -static void makeRoleName(Database*, MetaName&, DatabaseOptions&); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -1098,44 +1110,6 @@ static void successful_completion(CheckStatusWrapper* s, ISC_STATUS acceptCode = } -static void makeRoleName(Database* dbb, MetaName& userIdRole, DatabaseOptions& options) -{ - if (userIdRole.isEmpty()) - return; - - switch (options.dpb_sql_dialect) - { - case 0: - // V6 Client --> V6 Server, dummy client SQL dialect 0 was passed - // It means that client SQL dialect was not set by user - // and takes DB SQL dialect as client SQL dialect - if (dbb->dbb_flags & DBB_DB_SQL_dialect_3) - { - // DB created in IB V6.0 by client SQL dialect 3 - options.dpb_sql_dialect = SQL_DIALECT_V6; - } - else - { - // old DB was gbaked in IB V6.0 - options.dpb_sql_dialect = SQL_DIALECT_V5; - } - break; - - case 99: - // V5 Client --> V6 Server, old client has no concept of dialect - options.dpb_sql_dialect = SQL_DIALECT_V5; - break; - - default: - // V6 Client --> V6 Server, but client SQL dialect was set - // by user and was passed. - break; - } - - JRD_make_role_name(userIdRole, options.dpb_sql_dialect); -} - - // Stuff exception transliterated to the client charset. ISC_STATUS transliterateException(thread_db* tdbb, const Exception& ex, FbStatusVector* vector, const char* func) throw() @@ -1300,28 +1274,6 @@ static void trace_failed_attach(TraceManager* traceManager, const char* filename } -void JRD_make_role_name(MetaName& userIdRole, const int dialect) -{ - switch (dialect) - { - case SQL_DIALECT_V5: - // Invoke utility twice: first to strip quotes, next to uppercase if needed - // For unquoted string nothing bad happens - fb_utils::dpbItemUpper(userIdRole); - fb_utils::dpbItemUpper(userIdRole); - break; - - case SQL_DIALECT_V6_TRANSITION: - case SQL_DIALECT_V6: - fb_utils::dpbItemUpper(userIdRole); - break; - - default: - break; - } -} - - namespace Jrd { JTransaction* JAttachment::getTransactionInterface(CheckStatusWrapper* status, ITransaction* tra) @@ -1553,7 +1505,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch // Here we do not let anyone except SYSDBA (like DBO) to change dbb_page_buffers, // cause other flags is UserId can be set only when DB is opened. // No idea how to test for other cases before init is complete. - if ((config->getServerMode() != MODE_SUPER) || userId.locksmith()) + if ((config->getServerMode() != MODE_SUPER) || userId.locksmith(tdbb, CHANGE_HEADER_SETTINGS)) dbb->dbb_page_buffers = options.dpb_page_buffers; } @@ -1639,12 +1591,38 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch Arg::Gds(isc_valid_client_dialects) << Arg::Str("1, 2 or 3")); } - makeRoleName(dbb, userId.usr_sql_role_name, options); - makeRoleName(dbb, userId.usr_trusted_role, options); + switch (options.dpb_sql_dialect) + { + case 0: + // V6 Client --> V6 Server, dummy client SQL dialect 0 was passed + // It means that client SQL dialect was not set by user + // and takes DB SQL dialect as client SQL dialect + if (dbb->dbb_flags & DBB_DB_SQL_dialect_3) + { + // DB created in IB V6.0 by client SQL dialect 3 + options.dpb_sql_dialect = SQL_DIALECT_V6; + } + else + { + // old DB was gbaked in IB V6.0 + options.dpb_sql_dialect = SQL_DIALECT_V5; + } + break; - options.dpb_sql_dialect = 0; + case 99: + // V5 Client --> V6 Server, old client has no concept of dialect + options.dpb_sql_dialect = SQL_DIALECT_V5; + break; - SCL_init(tdbb, false, userId); + default: + // V6 Client --> V6 Server, but client SQL dialect was set + // by user and was passed. + break; + } + + userId.makeRoleName(options.dpb_sql_dialect); + + UserId::sclInit(tdbb, false, userId); // This pair (SHUT_database/SHUT_online) checks itself for valid user name if (options.dpb_shutdown) @@ -1686,7 +1664,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch if (dbb->dbb_ast_flags & DBB_shutdown) { // Allow only SYSDBA/owner to access database that is shut down - bool allow_access = attachment->locksmith(); + bool allow_access = attachment->locksmith(tdbb, ACCESS_SHUTDOWN_DATABASE); // Handle special shutdown modes if (allow_access) { @@ -1711,7 +1689,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch // Note we throw exception here when entering full-shutdown mode Arg::Gds v(isc_shutdown); v << Arg::Str(org_filename); - if (attachment->att_user->usr_flags & USR_mapdown) + if (attachment->att_user->testFlag(USR_mapdown)) v << Arg::Gds(isc_map_down); ERR_post(v); } @@ -1727,11 +1705,14 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch // database. smistry 10/5/98 if (attachment->isUtility()) - validateAccess(attachment); + validateAccess(tdbb, attachment, + attachment->att_utility == Attachment::UTIL_GBAK ? USE_GBAK_UTILITY : + attachment->att_utility == Attachment::UTIL_GFIX ? USE_GFIX_UTILITY : + USE_GSTAT_UTILITY); if (options.dpb_verify) { - validateAccess(attachment); + validateAccess(tdbb, attachment, USE_GFIX_UTILITY); if (!CCH_exclusive(tdbb, LCK_PW, WAIT_PERIOD, NULL)) ERR_post(Arg::Gds(isc_bad_dpb_content) << Arg::Gds(isc_cant_validate)); @@ -1745,7 +1726,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch if (options.dpb_reset_icu) { - validateAccess(attachment); + validateAccess(tdbb, attachment, USE_GFIX_UTILITY); DFW_reset_icu(tdbb); } @@ -1766,42 +1747,42 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch if (options.dpb_no_db_triggers) { - validateAccess(attachment); + validateAccess(tdbb, attachment, IGNORE_DB_TRIGGERS); attachment->att_flags |= ATT_no_db_triggers; } if (options.dpb_set_db_sql_dialect) { - validateAccess(attachment); + validateAccess(tdbb, attachment, CHANGE_HEADER_SETTINGS); PAG_set_db_SQL_dialect(tdbb, options.dpb_set_db_sql_dialect); dbb->dbb_linger_seconds = 0; } if (options.dpb_sweep_interval > -1) { - validateAccess(attachment); + validateAccess(tdbb, attachment, CHANGE_HEADER_SETTINGS); PAG_sweep_interval(tdbb, options.dpb_sweep_interval); dbb->dbb_sweep_interval = options.dpb_sweep_interval; } if (options.dpb_set_force_write) { - validateAccess(attachment); + validateAccess(tdbb, attachment, CHANGE_HEADER_SETTINGS); PAG_set_force_write(tdbb, options.dpb_force_write); } if (options.dpb_set_no_reserve) { - validateAccess(attachment); + validateAccess(tdbb, attachment, CHANGE_HEADER_SETTINGS); PAG_set_no_reserve(tdbb, options.dpb_no_reserve); } if (options.dpb_set_page_buffers) { if (dbb->dbb_flags & DBB_shared) - validateAccess(attachment); + validateAccess(tdbb, attachment, CHANGE_HEADER_SETTINGS); - if (attachment->locksmith()) + if (attachment->locksmith(tdbb, CHANGE_HEADER_SETTINGS)) { PAG_set_page_buffers(tdbb, options.dpb_page_buffers); dbb->dbb_linger_seconds = 0; @@ -1810,7 +1791,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch if (options.dpb_set_db_readonly) { - validateAccess(attachment); + validateAccess(tdbb, attachment, CHANGE_HEADER_SETTINGS); if (!CCH_exclusive(tdbb, LCK_EX, WAIT_PERIOD, NULL)) { ERR_post(Arg::Gds(isc_lock_timeout) << @@ -2578,7 +2559,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch if (options.dpb_overwrite) { // isc_dpb_no_db_triggers is required for 2 reasons - // - it disables non-DBA attaches with isc_adm_task_denied error + // - it disables non-DBA attaches with isc_adm_task_denied or isc_miss_prvlg error // - it disables any user code to be executed when we later lock // databases_mutex with OverwriteHolder ClumpletWriter dpbWriter(ClumpletReader::dpbList, MAX_DPB_SIZE, dpb, dpb_length); @@ -2590,16 +2571,20 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch JAttachment* attachment2 = internalAttach(user_status, filename, dpb_length, dpb, INTERNAL_ATT_OVERWRITE_CHECK); - if (user_status->getErrors()[1] == isc_adm_task_denied) + switch (user_status->getErrors()[1]) { - throw; + case isc_adm_task_denied: + case isc_miss_prvlg: + throw; + default: + break; } bool allow_overwrite = false; if (attachment2) { - allow_overwrite = attachment2->getHandle()->att_user->locksmith(); + allow_overwrite = attachment2->getHandle()->att_user->locksmith(tdbb, DROP_DATABASE); attachment2->detach(user_status); } else @@ -2645,7 +2630,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch INI_init(tdbb); PAG_init(tdbb); - SCL_init(tdbb, true, userId); + UserId::sclInit(tdbb, true, userId); if (options.dpb_set_page_buffers) dbb->dbb_page_buffers = options.dpb_page_buffers; @@ -2685,7 +2670,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch if (options.dpb_set_no_reserve) PAG_set_no_reserve(tdbb, options.dpb_no_reserve); - INI_format(attachment->att_user->usr_user_name.c_str(), + INI_format(attachment->att_user->getUserName().c_str(), options.dpb_set_db_charset.c_str()); // If we have not allocated first TIP page, do it now. @@ -2947,7 +2932,7 @@ void JAttachment::dropDatabase(CheckStatusWrapper* user_status) const PathName& file_name = attachment->att_filename; - if (!attachment->locksmith()) + if (!attachment->locksmith(tdbb, DROP_DATABASE)) { ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("drop") << Arg::Str("database") << @@ -5395,7 +5380,7 @@ static void check_database(thread_db* tdbb, bool async) if ((attachment->att_flags & ATT_shutdown) && (attachment->att_purge_tid != Thread::getId()) || ((dbb->dbb_ast_flags & DBB_shutdown) && - ((dbb->dbb_ast_flags & DBB_shutdown_full) || !attachment->locksmith()))) + ((dbb->dbb_ast_flags & DBB_shutdown_full) || !attachment->locksmith(tdbb, ACCESS_SHUTDOWN_DATABASE)))) { if (dbb->dbb_ast_flags & DBB_shutdown) { @@ -7104,10 +7089,11 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, } else if (options.dpb_auth_block.hasData()) { - if (mapUser(name, trusted_role, &auth_method, &user.usr_auth_block, options.dpb_auth_block, - aliasName, dbName, (config ? (*config)->getSecurityDatabase() : NULL), cryptCb)) + if (mapUser(true, name, trusted_role, &auth_method, &user.usr_auth_block, NULL, + options.dpb_auth_block, aliasName, dbName, + (config ? (*config)->getSecurityDatabase() : NULL), cryptCb, NULL) & MAPUSER_MAP_DOWN) { - user.usr_flags |= USR_mapdown; + user.setFlag(USR_mapdown); } if (creating && config) // when config is NULL we are in error handler @@ -7122,7 +7108,7 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, wheel = ISC_get_user(&name, &id, &group); ISC_systemToUtf8(name); fb_utils::dpbItemUpper(name); - if (id == 0) + if (wheel || id == 0) { auth_method = "OS user name / wheel"; wheel = true; @@ -7132,7 +7118,7 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, // if the name from the user database is defined as SYSDBA, // we define that user id as having system privileges - if (name == SYSDBA_USER_NAME) + if (name == DBA_USER_NAME) { wheel = true; } @@ -7143,7 +7129,7 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, if (wheel) { - name = SYSDBA_USER_NAME; + name = DBA_USER_NAME; } if (name.length() > USERNAME_LENGTH) @@ -7152,26 +7138,21 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, << Arg::Num(USERNAME_LENGTH)); } - user.usr_user_name = name; + user.setUserName(name); user.usr_project_name = ""; user.usr_org_name = ""; user.usr_auth_method = auth_method; user.usr_user_id = id; user.usr_group_id = group; - if (wheel) + if (trusted_role.hasData()) { - user.usr_flags |= USR_locksmith; + user.setTrustedRole(trusted_role); } if (options.dpb_role_name.hasData()) { - user.usr_sql_role_name = options.dpb_role_name; - } - - if (trusted_role.hasData()) - { - user.usr_trusted_role = trusted_role; + user.setSqlRole(options.dpb_role_name.c_str()); } } diff --git a/src/jrd/names.h b/src/jrd/names.h index 3f7505e3e1..5051faa8b6 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -405,3 +405,5 @@ NAME("RDB$DB_CREATORS", nam_db_creators) NAME("SEC$DB_CREATORS", nam_sec_db_creators) NAME("SEC$USER", nam_sec_user) NAME("SEC$USER_TYPE", nam_sec_user_type) + +NAME("RDB$SYSTEM_PRIVILEGES", nam_system_privileges) diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 378a192f5a..1bb0bf9b7e 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -47,28 +47,31 @@ const int obj_blob_filter = 16; const int obj_collation = 17; const int obj_package_header = 18; const int obj_package_body = 19; +const int obj_privilege = 20; + +const int obj_last_non_ddl = 20; // keep in sync!!! // 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_charsets = 30; -const int obj_collations = 31; -const int obj_filters = 32; +const int obj_database = obj_last_non_ddl + 1; +const int obj_relations = obj_last_non_ddl + 2; +const int obj_views = obj_last_non_ddl + 3; +const int obj_procedures = obj_last_non_ddl + 4; +const int obj_functions = obj_last_non_ddl + 5; +const int obj_packages = obj_last_non_ddl + 6; +const int obj_generators = obj_last_non_ddl + 7; +const int obj_domains = obj_last_non_ddl + 8; +const int obj_exceptions = obj_last_non_ddl + 9; +const int obj_roles = obj_last_non_ddl + 10; +const int obj_charsets = obj_last_non_ddl + 11; +const int obj_collations = obj_last_non_ddl + 12; +const int obj_filters = obj_last_non_ddl + 13; -const int obj_type_MAX = 33; // keep this last! +const int obj_type_MAX = obj_last_non_ddl + 14; // keep this last! -// used in the parser only / no relation with obj_type_MAX -const int obj_user_or_role = 34; -const int obj_schema = 35; -const int obj_parameter = 36; +// used in the parser only / no relation with obj_type_MAX (should be greater) +const int obj_user_or_role = 100; +const int obj_schema = 101; +const int obj_parameter = 102; inline const char* get_object_name(int object_type) { diff --git a/src/jrd/ods.h b/src/jrd/ods.h index e5bc65cd1d..c959dc02cb 100644 --- a/src/jrd/ods.h +++ b/src/jrd/ods.h @@ -138,6 +138,8 @@ const USHORT ODS_11_1 = ENCODE_ODS(ODS_VERSION11, 1); const USHORT ODS_11_2 = ENCODE_ODS(ODS_VERSION11, 2); const USHORT ODS_12_0 = ENCODE_ODS(ODS_VERSION12, 0); +const USHORT ODS_FB4 = ODS_12_0; // temporal const to be fixed somehow later + const USHORT ODS_FIREBIRD_FLAG = 0x8000; // Decode ODS version to Major and Minor parts. The 4 LSB's are minor and diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 70879d81e8..321ef8084f 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -451,6 +451,7 @@ RELATION(nam_roles, rel_roles, ODS_9_0, rel_persistent) 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) + FIELD(f_rol_sys_priv, nam_system_privileges, fld_system_privileges, 1, ODS_FB4) END_RELATION // Relation 32 (RDB$BACKUP_HISTORY) diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 59cde8b25f..d4f12da72f 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -44,6 +44,7 @@ #include "../jrd/cmp_proto.h" #include "../jrd/err_proto.h" #include "../jrd/exe_proto.h" +#include "../jrd/ini_proto.h" #include "../yvalve/gds_proto.h" #include "../common/isc_proto.h" #include "../jrd/met_proto.h" @@ -56,6 +57,8 @@ #include "../common/config/config.h" #include "../common/os/os_utils.h" #include "../common/classes/ClumpletWriter.h" +#include "../jrd/PreparedStatement.h" +#include "../jrd/ResultSet.h" const int UIC_BASE = 10; @@ -211,9 +214,7 @@ void SCL_check_access(thread_db* tdbb, ************************************** * * Functional description - * Check security class for desired permission. Check first that - * the desired access has been granted to the database then to the - * object in question. + * Check security class for desired permission. * **************************************/ SET_TDBB(tdbb); @@ -228,27 +229,13 @@ void SCL_check_access(thread_db* tdbb, Arg::Str(s_class->scl_name)); } - const Jrd::Attachment& attachment = *tdbb->getAttachment(); - - // Allow the database owner to back up a database even if he does not have - // read access to all the tables in the database - - if (attachment.isGbak() && (mask & SCL_select)) - return; - - // Allow the locksmith any access to database - - if (attachment.locksmith()) - return; - // 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; - if (!s_class || (mask & s_class->scl_flags)) { + if (!s_class || (mask & s_class->scl_flags)) return; - } const jrd_rel* view = NULL; @@ -373,15 +360,13 @@ void SCL_check_database(thread_db* tdbb, SecurityClass::flags_t mask) 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; + if (mask == SCL_alter && attachment->locksmith(tdbb, USE_NBACKUP_UTILITY)) + return; + const P_NAMES* names; for (names = p_names; names->p_names_priv; names++) { @@ -893,17 +878,12 @@ SecurityClass* SCL_get_class(thread_db* tdbb, const TEXT* par_string) // Name may be absent or terminated with NULL or blank. Clean up name. - if (!par_string) { + if (!par_string) return NULL; - } Firebird::MetaName string(par_string); - - //fb_utils::exact_name(string); - - if (string.isEmpty()) { + if (string.isEmpty()) return NULL; - } Jrd::Attachment* const attachment = tdbb->getAttachment(); @@ -1003,9 +983,6 @@ bool SCL_role_granted(thread_db* tdbb, const UserId& usr, const TEXT* sql_role) return true; } - Firebird::MetaName loginName(usr.usr_user_name); - const TEXT* login_name = loginName.c_str(); - bool found = false; AutoCacheRequest request(tdbb, irq_verify_role_name, IRQ_REQUESTS); @@ -1022,7 +999,7 @@ bool SCL_role_granted(thread_db* tdbb, const UserId& usr, const TEXT* sql_role) WITH RR.RDB$ROLE_NAME EQ sql_role AND RR.RDB$ROLE_NAME EQ UU.RDB$RELATION_NAME AND UU.RDB$OBJECT_TYPE EQ obj_sql_role - AND (UU.RDB$USER EQ login_name + AND (UU.RDB$USER EQ usr.getUserName().c_str() OR UU.RDB$USER EQ "PUBLIC") AND UU.RDB$USER_TYPE EQ obj_user AND UU.RDB$PRIVILEGE EQ "M" @@ -1036,74 +1013,57 @@ bool SCL_role_granted(thread_db* tdbb, const UserId& usr, const TEXT* sql_role) } -// TODO: Remove recursion. See dfw.epp/check_computed_dependencies as a model. -void SCL_find_granted_roles(thread_db* tdbb, const Firebird::MetaName& object, bool isRole, - Firebird::SortedArray& grantedRoles, bool defaultOnly) +void UserId::findGrantedRoles(thread_db* tdbb) const { SET_TDBB(tdbb); - - Jrd::Attachment* const attachment = tdbb->getAttachment(); - - const int obj_type = isRole ? obj_sql_role : obj_user; - - AutoCacheRequest request(tdbb, drq_l_granted_roles, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request) - U IN RDB$USER_PRIVILEGES WITH - U.RDB$USER EQ object.c_str() AND - U.RDB$USER_TYPE EQ obj_type AND - U.RDB$OBJECT_TYPE EQ obj_sql_role AND - U.RDB$PRIVILEGE EQ "M" - - const Firebird::MetaName roleName = U.RDB$RELATION_NAME; - const bool defaultRole = !defaultOnly || !U.RDB$FIELD_NAME.NULL && (U.RDB$FIELD_NAME[0] == 'D'); - - if (defaultRole && !grantedRoles.exist(roleName)) - { - grantedRoles.add(roleName); - SCL_find_granted_roles(tdbb, roleName, true, grantedRoles, defaultOnly); - } - - END_FOR -} - - -bool SCL_admin_role(thread_db* tdbb, const Firebird::SortedArray& roles) -{ -/************************************** - * - * S C L _ a d m i n _ r o l e - * - ************************************** - * - * Functional description - * Check if roles has an admin role. - * - **************************************/ - SET_TDBB(tdbb); Jrd::Attachment* const attachment = tdbb->getAttachment(); - bool adminRole = false; + PreparedStatement::Builder sql; + MetaName usr_get_role; + string usr_get_priv; + sql << "with recursive role_tree as ( " + << " select rdb$relation_name as nm, 0 as ur from rdb$user_privileges " + << " where rdb$privilege = 'M' and rdb$field_name = 'D' and rdb$user = " << usr_user_name << " and rdb$user_type = 8 " + << " union all " + << " select rdb$role_name as nm, 1 as ur from rdb$roles " + << " where rdb$role_name = " << usr_sql_role_name + << " union all " + << " select p.rdb$relation_name as nm, t.ur from rdb$user_privileges p " + << " join role_tree t on t.nm = p.rdb$user " + << " where p.rdb$privilege = 'M' and (p.rdb$field_name = 'D' or t.ur = 1)) " + << "select " << sql("r.rdb$role_name, ", usr_get_role) + << sql("r.rdb$system_privileges ", usr_get_priv) + << " from role_tree t join rdb$roles r on t.nm = r.rdb$role_name "; - for (int i = 0; !adminRole && (i < roles.getCount()); i++) + AutoPreparedStatement stmt(attachment->prepareStatement(tdbb, attachment->getSysTransaction(), sql)); + AutoResultSet rs(stmt->executeQuery(tdbb, attachment->getSysTransaction())); + + usr_granted_roles.clear(); + usr_privileges.clearAll(); + while (rs->fetch(tdbb)) { - AutoCacheRequest request(tdbb, irq_is_admin_role, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) R IN RDB$ROLES - WITH R.RDB$ROLE_NAME EQ roles[i].c_str() AND - R.RDB$SYSTEM_FLAG != 0 + if (!usr_granted_roles.exist(usr_get_role)) // SQL request can return duplicates { - adminRole = true; - break; + usr_granted_roles.add(usr_get_role); + Privileges p; + p.load(usr_get_priv.c_str()); + usr_privileges |= p; } - END_FOR } - return adminRole; + usr_flags &= ~USR_newrole; } -void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) +void UserId::setRoleTrusted() +{ + if (!usr_trusted_role.hasData()) + Arg::Gds(isc_miss_trusted_role).raise(); + setSqlRole(usr_trusted_role); +} + + +void UserId::sclInit(thread_db* tdbb, bool create, const UserId& tempId) { /************************************** * @@ -1122,26 +1082,22 @@ void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) Database* const dbb = tdbb->getDatabase(); Jrd::Attachment* const attachment = tdbb->getAttachment(); - const TEXT* sql_role = tempId.usr_sql_role_name.nullStr(); + const TEXT* sql_role = tempId.getSqlRole().nullStr(); // CVC: We'll verify the role and wipe it out when it doesn't exist - if (tempId.usr_user_name.hasData()) + if (tempId.getUserName().hasData() && !create) { - if (!create) + const TEXT* login_name = tempId.getUserName().c_str(); + + AutoCacheRequest request(tdbb, irq_get_role_name, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) X IN RDB$ROLES + WITH X.RDB$ROLE_NAME EQ login_name { - Firebird::MetaName loginName(tempId.usr_user_name); - const TEXT* login_name = loginName.c_str(); - - AutoCacheRequest request(tdbb, irq_get_role_name, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) X IN RDB$ROLES - WITH X.RDB$ROLE_NAME EQ login_name - { - ERR_post(Arg::Gds(isc_login_same_as_role_name) << Arg::Str(login_name)); - } - END_FOR + ERR_post(Arg::Gds(isc_login_same_as_role_name) << Arg::Str(login_name)); } + END_FOR } // CVC: If we aren't creating a db and sql_role was specified, @@ -1154,28 +1110,17 @@ void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) } if (!sql_role) - sql_role = tempId.usr_trusted_role.nullStr(); + sql_role = tempId.getTrustedRole().nullStr(); MetaName role_name(sql_role ? sql_role : NULL_ROLE); MemoryPool& pool = *attachment->att_pool; UserId* const user = FB_NEW_POOL(pool) UserId(pool, tempId); - user->usr_sql_role_name = role_name.c_str(); + user->setSqlRole(role_name); attachment->att_user = user; if (!create) { - const bool isRole = role_name != NULL_ROLE; - if (isRole) - { - // Add role itself and all roles cumulatively granted to it. Not only default. - user->usr_granted_roles.add(user->usr_sql_role_name); - SCL_find_granted_roles(tdbb, user->usr_sql_role_name, true, user->usr_granted_roles, false); - } - - // Add all default roles granted to user - SCL_find_granted_roles(tdbb, user->usr_user_name, false, user->usr_granted_roles, true); - AutoCacheRequest request(tdbb, irq_get_att_class, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) X IN RDB$DATABASE @@ -1198,17 +1143,15 @@ void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) } END_FOR } - - if (dbb->dbb_owner == user->usr_user_name) - user->usr_flags |= USR_owner; - - if (sql_role && SCL_admin_role(tdbb, user->usr_granted_roles)) - user->usr_flags |= USR_dba; } else { - dbb->dbb_owner = user->usr_user_name; - user->usr_flags |= USR_owner; + dbb->dbb_owner = user->getUserName(); + user->usr_privileges.load(INI_owner_privileges().c_str()); + user->usr_granted_roles.clear(); + user->usr_granted_roles.add("RDB$ADMIN"); + + user->usr_flags &= ~USR_newrole; } } @@ -1259,15 +1202,13 @@ SecurityClass* SCL_recompute_class(thread_db* tdbb, const TEXT* string) SET_TDBB(tdbb); SecurityClass* s_class = SCL_get_class(tdbb, string); - if (!s_class) { + if (!s_class) return NULL; - } s_class->scl_flags = compute_access(tdbb, s_class, NULL, 0, NULL); - if (s_class->scl_flags & SCL_exists) { + if (s_class->scl_flags & SCL_exists) return s_class; - } // Class no long exists - get rid of it! @@ -1337,6 +1278,31 @@ SecurityClass::flags_t SCL_get_object_mask(const int object_type) return -1 & ~SCL_corrupt; } +ULONG SCL_get_number(const UCHAR* acl) +{ +/************************************** + * + * g e t _ n u m b e r + * + ************************************** + * + * Functional description + * Get value of acl numeric string. + * + **************************************/ + ULONG n = 0; + USHORT l = *acl++; + if (l) + { + do { + n = n * UIC_BASE + *acl++ - '0'; + } while (--l); + } + + return n; +} + + static bool check_number(const UCHAR* acl, USHORT number) { /************************************** @@ -1350,16 +1316,7 @@ static bool check_number(const UCHAR* acl, USHORT number) * return true. * **************************************/ - int n = 0; - USHORT l = *acl++; - if (l) - { - do { - n = n * UIC_BASE + *acl++ - '0'; - } while (--l); - } - - return (n != number); + return (SCL_get_number(acl) != number); } @@ -1389,7 +1346,7 @@ static bool check_user_group(thread_db* tdbb, const UCHAR* acl, USHORT number) **************************************/ SET_TDBB(tdbb); - SLONG n = 0; + ULONG n = 0; USHORT l = *acl++; if (l) @@ -1482,13 +1439,26 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb, jrd_tra* sysTransaction = attachment->getSysTransaction(); SecurityClass::flags_t privileges = 0; + SecurityClass::flags_t sysPriv = SCL_exists; + + const SecurityClass::flags_t selectAnyObject = SCL_select | SCL_references; + const SecurityClass::flags_t accessAnyObject = SCL_insert | SCL_update | SCL_delete | + SCL_execute | SCL_usage | selectAnyObject; + const SecurityClass::flags_t anyDdl = SCL_create | SCL_alter | SCL_control | SCL_drop; + + if (attachment->locksmith(tdbb, ACCESS_ANY_OBJECT_IN_DATABASE)) + sysPriv |= accessAnyObject; + else if (attachment->locksmith(tdbb, SELECT_ANY_OBJECT_IN_DATABASE)) + sysPriv |= selectAnyObject; + if (attachment->locksmith(tdbb, MODIFY_ANY_OBJECT_IN_DATABASE)) + sysPriv |= anyDdl; AutoCacheRequest request(tdbb, irq_l_security, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS EQ s_class->scl_name.c_str() { - privileges |= SCL_exists; + privileges |= sysPriv; blb* blob = blb::open(tdbb, sysTransaction, &X.RDB$ACL); UCHAR* buffer = acl.getBuffer(ACL_BLOB_BUFFER_SIZE); UCHAR* end = buffer; @@ -1552,7 +1522,7 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, // privileges to the base table or (2b) the view must have // sufficient privileges on the base table. - user.usr_user_name = view->rel_owner_name.c_str(); + user.setUserName(view->rel_owner_name.c_str()); } SecurityClass::flags_t privilege = 0; @@ -1563,11 +1533,6 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, BUGCHECK(160); // msg 160 wrong ACL version } - if (user.locksmith()) - { - return -1 & ~SCL_corrupt; - } - const TEXT* p; bool hit = false; UCHAR c; @@ -1583,7 +1548,7 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, switch (c) { case id_person: - if (!(p = user.usr_user_name.nullStr()) || check_string(a, p)) + if (!(p = user.getUserName().nullStr()) || check_string(a, p)) hit = false; break; @@ -1606,7 +1571,7 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, { Firebird::MetaName role_name; get_string(a, role_name); - if (!user.usr_granted_roles.exist(role_name)) + if (!user.roleInUse(tdbb, role_name)) hit = false; break; } @@ -1640,6 +1605,11 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, hit = false; break; + case id_privilege: + if (!user.locksmith(tdbb, SCL_get_number(a))) + hit = false; + break; + case id_node: break; @@ -1739,10 +1709,71 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, return privilege; } -void Jrd::UserId::populateDpb(Firebird::ClumpletWriter& dpb) +void UserId::populateDpb(Firebird::ClumpletWriter& dpb) { if (usr_auth_block.hasData()) dpb.insertBytes(isc_dpb_auth_block, usr_auth_block.begin(), usr_auth_block.getCount()); else dpb.insertString(isc_dpb_user_name, usr_user_name); } + +void UserId::makeRoleName(Firebird::MetaName& role, const int dialect) +{ + if (role.isEmpty()) + return; + + switch (dialect) + { + case SQL_DIALECT_V5: + // Invoke utility twice: first to strip quotes, next to uppercase if needed + // For unquoted string nothing bad happens + fb_utils::dpbItemUpper(role); + fb_utils::dpbItemUpper(role); + break; + + case SQL_DIALECT_V6_TRANSITION: + case SQL_DIALECT_V6: + fb_utils::dpbItemUpper(role); + break; + + default: + break; + } +} + +// get privilege bit by name +USHORT SCL_convert_privilege(thread_db* tdbb, jrd_tra* transaction, const Firebird::string& priv) +{ + static GlobalPtr privCacheMutex; + static bool cacheFlag = false; + typedef NonPooled CachedPriv; + static GlobalPtr > privCache; + + if (!cacheFlag) + { + MutexLockGuard g(privCacheMutex, FB_FUNCTION); + + if (!cacheFlag) + { + privCache->clear(); + + AutoCacheRequest request(tdbb, irq_get_priv_bit, IRQ_REQUESTS); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + T IN RDB$TYPES + WITH T.RDB$FIELD_NAME EQ 'RDB$SYSTEM_PRIVILEGES' + { + privCache->put(T.RDB$TYPE_NAME, T.RDB$TYPE); + } + END_FOR + + cacheFlag = true; + } + } + + USHORT rc; + if (!privCache->get(priv, rc)) + (Arg::Gds(isc_wrong_prvlg) << priv).raise(); + + return rc; +} + diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 5869c66060..dd24f6e2ab 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -28,6 +28,7 @@ #include "../common/classes/tree.h" #include "../common/security.h" #include "../jrd/obj.h" +#include "../jrd/SystemPrivileges.h" namespace Firebird { class ClumpletWriter; @@ -35,6 +36,8 @@ class ClumpletWriter; namespace Jrd { +class thread_db; + const size_t ACL_BLOB_BUFFER_SIZE = MAX_USHORT; // used to read/write acl blob // Security class definition @@ -80,33 +83,149 @@ const SecurityClass::flags_t SCL_usage = 2048; // USAGE access const SecurityClass::flags_t SCL_create = 4096; +/* +scl pathcalls +SCL_check_access => SCL_get_object_mask => SCL_recompute_class => SCL_get_class => compute_access + => compute_access => compute_access + +SCL_check_database => att_security_class => SCL_get_class => compute_access + => SCL_recompute_class => SCL_get_class => compute_access + +compute_access => walk_acl + */ + // information about the user -const USHORT USR_locksmith = 1; // User has great karma -const USHORT USR_dba = 2; // User has DBA privileges -const USHORT USR_owner = 4; // User owns database -const USHORT USR_mapdown = 8; // Mapping failed when getting context +const USHORT USR_mapdown = 1; // Mapping failed when getting context +const USHORT USR_newrole = 2; // usr_granted_roles array needs refresh + +const USHORT USR_external = USR_mapdown; class UserId { public: + // Arbitrary size bitmask + template + class Bits + { + static const unsigned shift = 3; + static const unsigned bitmask = (1 << shift) - 1; + + static const unsigned L = (N >> shift) + (N & bitmask ? 1 : 0); + + public: + static const unsigned BYTES_COUNT = L; + + Bits() + { + clearAll(); + } + + Bits(const Bits& b) + { + assign(b); + } + + Bits& operator=(const Bits& b) + { + assign(b); + return *this; + } + + Bits& set(unsigned i) + { + fb_assert(i < N); + if (i < N) + data[index(i)] |= mask(i); + return *this; + } + + Bits& setAll() + { + memset(data, ~0, sizeof data); + return *this; + } + + Bits& clear(unsigned i) + { + fb_assert(i < N); + if (i < N) + data[index(i)] &= ~mask(i); + return *this; + } + + Bits& clearAll() + { + memset(data, 0, sizeof data); + return *this; + } + + bool test(unsigned int i) const + { + fb_assert(i < N); + if (i >= N) + return false; + return data[index(i)] & mask(i); + } + + void load(const void* from) + { + memcpy(data, from, sizeof data); + } + + void store(void* to) const + { + memcpy(to, data, sizeof data); + } + + Bits& operator|=(const Bits& b) + { + for (unsigned n = 0; n < L; ++n) + data[n] |= b.data[n]; + return *this; + } + + private: + UCHAR data[L]; + + void assign(const Bits& b) + { + memcpy(data, b.data, sizeof data); + } + + static unsigned index(unsigned i) + { + return i >> shift; + } + + static UCHAR mask(unsigned i) + { + return 1U << (i & bitmask); + } + }; + + typedef Bits Privileges; + +private: Firebird::MetaName usr_user_name; // User name Firebird::MetaName usr_sql_role_name; // Role name - Firebird::SortedArray usr_granted_roles; // Granted roles list + mutable Firebird::SortedArray usr_granted_roles; // Granted roles list Firebird::MetaName usr_trusted_role; // Trusted role if set + +public: Firebird::string usr_project_name; // Project name Firebird::string usr_org_name; // Organization name Firebird::string usr_auth_method; // Authentication method +private: + mutable Privileges usr_privileges; // Privileges granted to user by default +public: Auth::AuthenticationBlock usr_auth_block; // Authentication block after mapping USHORT usr_user_id; // User id USHORT usr_group_id; // Group id - USHORT usr_flags; // Misc. crud - - bool locksmith() const - { - return usr_flags & (USR_locksmith | USR_owner | USR_dba); - } +private: + mutable USHORT usr_flags; // Misc. crud +public: UserId() : usr_user_id(0), usr_group_id(0), usr_flags(0) {} @@ -119,48 +238,132 @@ public: usr_project_name(p, ui.usr_project_name), usr_org_name(p, ui.usr_org_name), usr_auth_method(p, ui.usr_auth_method), + usr_privileges(ui.usr_privileges), usr_auth_block(p), usr_user_id(ui.usr_user_id), usr_group_id(ui.usr_group_id), usr_flags(ui.usr_flags) { usr_auth_block.assign(ui.usr_auth_block); - usr_granted_roles = ui.usr_granted_roles; + if (!testFlag(USR_newrole)) + usr_granted_roles = ui.usr_granted_roles; } UserId(const UserId& ui) : usr_user_name(ui.usr_user_name), usr_sql_role_name(ui.usr_sql_role_name), - usr_granted_roles(ui.usr_granted_roles), usr_trusted_role(ui.usr_trusted_role), usr_project_name(ui.usr_project_name), usr_org_name(ui.usr_org_name), usr_auth_method(ui.usr_auth_method), + usr_privileges(ui.usr_privileges), usr_user_id(ui.usr_user_id), usr_group_id(ui.usr_group_id), usr_flags(ui.usr_flags) { usr_auth_block.assign(ui.usr_auth_block); + if (!testFlag(USR_newrole)) + usr_granted_roles = ui.usr_granted_roles; } UserId& operator=(const UserId& ui) { usr_user_name = ui.usr_user_name; usr_sql_role_name = ui.usr_sql_role_name; - usr_granted_roles = ui.usr_granted_roles; usr_trusted_role = ui.usr_trusted_role; usr_project_name = ui.usr_project_name; usr_org_name = ui.usr_org_name; + usr_privileges = ui.usr_privileges; usr_auth_method = ui.usr_auth_method; usr_user_id = ui.usr_user_id; usr_group_id = ui.usr_group_id; usr_flags = ui.usr_flags; usr_auth_block.assign(ui.usr_auth_block); + if (!testFlag(USR_newrole)) + usr_granted_roles = ui.usr_granted_roles; + return *this; } void populateDpb(Firebird::ClumpletWriter& dpb); + + bool locksmith(thread_db* tdbb, ULONG sp) const + { + if (testFlag(USR_newrole)) + findGrantedRoles(tdbb); + return usr_privileges.test(sp); + } + + static void sclInit(thread_db* tdbb, bool create, const UserId& tempId); + + void setUserName(const Firebird::MetaName& userName) + { + if (userName != usr_user_name) + { + usr_flags |= USR_newrole; + usr_user_name = userName; + } + } + + const Firebird::MetaName& getUserName() const + { + return usr_user_name; + } + + void setTrustedRole(const Firebird::MetaName& roleName) + { + usr_trusted_role = roleName; + } + + const Firebird::MetaName& getTrustedRole() const + { + return usr_trusted_role; + } + + void setSqlRole(const Firebird::MetaName& roleName) + { + if (roleName != usr_sql_role_name) + { + usr_flags |= USR_newrole; + usr_sql_role_name = roleName; + } + } + + const Firebird::MetaName& getSqlRole() const + { + return usr_sql_role_name; + } + + void setRoleTrusted(); + + bool roleInUse(thread_db* tdbb, const Firebird::MetaName& role) const + { + if (testFlag(USR_newrole)) + findGrantedRoles(tdbb); + return usr_granted_roles.exist(role); + } + + void makeRoleName(const int dialect) + { + makeRoleName(usr_sql_role_name, dialect); + makeRoleName(usr_trusted_role, dialect); + } + + bool testFlag(USHORT mask) const + { + return usr_flags & mask; + } + + void setFlag(USHORT mask) + { + usr_flags |= (mask & USR_external); + } + + static void makeRoleName(Firebird::MetaName& role, const int dialect); + +private: + void findGrantedRoles(thread_db* tdbb) const; }; // These numbers are arbitrary and only used at run-time. Can be changed if necessary at any moment. diff --git a/src/jrd/scl_proto.h b/src/jrd/scl_proto.h index 215038e397..2f8ebd2ae5 100644 --- a/src/jrd/scl_proto.h +++ b/src/jrd/scl_proto.h @@ -53,14 +53,12 @@ void SCL_check_view(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_ 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); 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); -void SCL_find_granted_roles(Jrd::thread_db* tdbb, const Firebird::MetaName& object, bool isRole, - Firebird::SortedArray& grantedRoles, bool defaultOnly); -bool SCL_admin_role(Jrd::thread_db* tdbb, const Firebird::SortedArray& roles); Jrd::SecurityClass::flags_t SCL_get_object_mask(const int object_type); +ULONG SCL_get_number(const UCHAR*); +USHORT SCL_convert_privilege(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, const Firebird::string& priv); namespace Jrd { typedef Firebird::Array Acl; diff --git a/src/jrd/shut.cpp b/src/jrd/shut.cpp index 05e822643e..c801e6fc7f 100644 --- a/src/jrd/shut.cpp +++ b/src/jrd/shut.cpp @@ -158,10 +158,10 @@ void SHUT_database(thread_db* tdbb, SSHORT flag, SSHORT delay, Sync* guard) // Only platform's user locksmith can shutdown or bring online a database - if (!attachment->locksmith()) + if (!attachment->locksmith(tdbb, CHANGE_SHUTDOWN_MODE)) { ERR_post_nothrow(Arg::Gds(isc_no_priv) << "shutdown" << "database" << dbb->dbb_filename); - if (attachment->att_user->usr_flags & USR_mapdown) + if (attachment->att_user->testFlag(USR_mapdown)) ERR_post_nothrow(Arg::Gds(isc_map_down)); ERR_punt(); } @@ -340,10 +340,10 @@ void SHUT_online(thread_db* tdbb, SSHORT flag, Sync* guard) // Only platform's user locksmith can shutdown or bring online a database - if (!attachment->att_user->locksmith()) + if (!attachment->att_user->locksmith(tdbb, CHANGE_SHUTDOWN_MODE)) { ERR_post_nothrow(Arg::Gds(isc_no_priv) << "bring online" << "database" << dbb->dbb_filename); - if (attachment->att_user->usr_flags & USR_mapdown) + if (attachment->att_user->testFlag(USR_mapdown)) ERR_post_nothrow(Arg::Gds(isc_map_down)); ERR_punt(); } diff --git a/src/jrd/status.h b/src/jrd/status.h index 63c9771b61..c78c8d1bbc 100644 --- a/src/jrd/status.h +++ b/src/jrd/status.h @@ -39,23 +39,24 @@ namespace Jrd { typedef Firebird::CheckStatusWrapper FbStatusVector; - class FbLocalStatus + template + class LocalStatusWrapper { public: - FbLocalStatus() + LocalStatusWrapper() : localStatusVector(&localStatus) { } - explicit FbLocalStatus(Firebird::MemoryPool& p) + explicit LocalStatusWrapper(Firebird::MemoryPool& p) : localStatus(p), localStatusVector(&localStatus) { } - FbStatusVector* operator->() + SW* operator->() { return &localStatusVector; } - FbStatusVector* operator&() + SW* operator&() { return &localStatusVector; } @@ -66,12 +67,12 @@ namespace Jrd return localStatusVector.getErrors()[n]; } - const FbStatusVector* operator->() const + const SW* operator->() const { return &localStatusVector; } - const FbStatusVector* operator&() const + const SW* operator&() const { return &localStatusVector; } @@ -85,7 +86,7 @@ namespace Jrd } } - void copyTo(FbStatusVector* to) const + void copyTo(SW* to) const { fb_utils::copyStatus(to, &localStatusVector); } @@ -102,9 +103,11 @@ namespace Jrd private: Firebird::LocalStatus localStatus; - FbStatusVector localStatusVector; + SW localStatusVector; }; + typedef LocalStatusWrapper FbLocalStatus; + typedef LocalStatusWrapper ThrowLocalStatus; } #endif // JRD_STATUS_H diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 8a91080fc9..fb7595071a 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -712,8 +712,8 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d expandDatabaseName(svc_expected_db, dummy, &config); string trusted_role; - mapUser(svc_username, trusted_role, NULL, &svc_auth_block, svc_auth_block, - "services manager", NULL, config->getSecurityDatabase(), svc_crypt_callback); + mapUser(true, svc_username, trusted_role, NULL, &svc_auth_block, NULL, + svc_auth_block, "services manager", NULL, config->getSecurityDatabase(), svc_crypt_callback, NULL); trusted_role.upper(); svc_trusted_role = trusted_role == ADMIN_ROLE; } @@ -722,7 +722,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d // we have embedded service connection, check OS auth if (ISC_get_user(&svc_username, NULL, NULL)) { - svc_username = SYSDBA_USER_NAME; + svc_username = DBA_USER_NAME; } } } @@ -741,7 +741,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d } // Check that the validated user has the authority to access this service - if (svc_username != SYSDBA_USER_NAME && !svc_trusted_role) { + if (svc_username != DBA_USER_NAME && !svc_trusted_role) { user_flag = SVC_user_any; } else { diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 0a3daa17c7..d455b0792b 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -385,7 +385,7 @@ void TRA_commit(thread_db* tdbb, jrd_tra* transaction, const bool retaining_flag status_exception::raise(&st); secContext->tra = NULL; - clearMap(tdbb->getDatabase()->dbb_config->getSecurityDatabase()); + clearMappingCache(tdbb->getDatabase()->dbb_config->getSecurityDatabase(), MAPPING_CACHE); transaction->eraseSecDbContext(); } @@ -3637,7 +3637,7 @@ TraceSweepEvent::TraceSweepEvent(thread_db* tdbb) gds__log("Sweep is started by %s\n" "\tDatabase \"%s\" \n" "\tOIT %" SQUADFORMAT", OAT %" SQUADFORMAT", OST %" SQUADFORMAT", Next %" SQUADFORMAT, - att->att_user->usr_user_name.c_str(), + att->att_user->getUserName().c_str(), att->att_filename.c_str(), m_sweep_info.getOIT(), m_sweep_info.getOAT(), diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 8da26b4cad..e38fc41cd8 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -497,7 +497,7 @@ enum dfw_t { dfw_arg_field_not_null, // set domain to not nullable dfw_db_crypt, // change database encryption status dfw_set_linger, // set database linger - dfw_clear_mapping // clear user mapping cache + dfw_clear_cache // clear user mapping cache }; } //namespace Jrd diff --git a/src/jrd/trace/TraceConfigStorage.cpp b/src/jrd/trace/TraceConfigStorage.cpp index 3be7d1b20f..59dd4d967b 100644 --- a/src/jrd/trace/TraceConfigStorage.cpp +++ b/src/jrd/trace/TraceConfigStorage.cpp @@ -278,7 +278,7 @@ void ConfigStorage::checkFile() gds__log("Audit configuration file \"%s\" is empty", configFileName.c_str()); } - session.ses_user = SYSDBA_USER_NAME; + session.ses_user = DBA_USER_NAME; session.ses_name = "Firebird Audit"; session.ses_flags = trs_admin | trs_system; diff --git a/src/jrd/trace/TraceManager.cpp b/src/jrd/trace/TraceManager.cpp index 244b734f19..6a29b92a57 100644 --- a/src/jrd/trace/TraceManager.cpp +++ b/src/jrd/trace/TraceManager.cpp @@ -245,30 +245,30 @@ void TraceManager::update_session(const TraceSession& session) string s_user = session.ses_user; string t_role; + UserId::Privileges priv; + ULONG mapResult; + + { // scope + AutoSetRestoreFlag autoRestore(&attachment->att_flags, ATT_mapping, true); + + Database* dbb = attachment->att_database; + fb_assert(dbb); + mapResult = mapUser(false, s_user, t_role, NULL, NULL, &priv, session.ses_auth, + attachment->att_filename.c_str(), dbb->dbb_filename.c_str(), + dbb->dbb_config->getSecurityDatabase(), dbb->dbb_provider->getCryptCallback(), + attachment->getInterface()); + } if (session.ses_auth.hasData()) { - Database* dbb = attachment->att_database; - fb_assert(dbb); - - try - { - mapUser(s_user, t_role, NULL, NULL, session.ses_auth, - attachment->att_filename.c_str(), dbb->dbb_filename.c_str(), - dbb->dbb_config->getSecurityDatabase(), - dbb->dbb_provider->getCryptCallback()); - } - catch (const Firebird::Exception&) - { - // Error in mapUser() means missing context, therefore... - return; - } + if (mapResult & MAPUSER_ERROR_NOT_THROWN) + return; // Error in mapUser() means missing context t_role.upper(); } - if (s_user != SYSDBA_USER_NAME && t_role != ADMIN_ROLE && - attachment->att_user->usr_user_name != s_user) + if (s_user != DBA_USER_NAME && t_role != ADMIN_ROLE && + attachment->att_user->getUserName() != s_user && (!priv.test(TRACE_ANY_ATTACHMENT))) { return; } @@ -284,12 +284,9 @@ void TraceManager::update_session(const TraceSession& session) RefPtr config; expandDatabaseName(service->getExpectedDb(), dummy, &config); - try - { - mapUser(s_user, t_role, NULL, NULL, session.ses_auth, "services manager", NULL, - config->getSecurityDatabase(), service->getCryptCallback()); - } - catch (const Firebird::Exception&) + if (mapUser(false, s_user, t_role, NULL, NULL, NULL, session.ses_auth, "services manager", + NULL, config->getSecurityDatabase(), service->getCryptCallback(), + NULL) & MAPUSER_ERROR_NOT_THROWN) { // Error in mapUser() means missing context, therefore... return; @@ -298,7 +295,7 @@ void TraceManager::update_session(const TraceSession& session) t_role.upper(); } - if (s_user != SYSDBA_USER_NAME && t_role != ADMIN_ROLE && service->getUserName() != s_user) + if (s_user != DBA_USER_NAME && t_role != ADMIN_ROLE && service->getUserName() != s_user) return; } else diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 7ff2f5d5ac..95033aa7e1 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -84,13 +84,13 @@ const char* TraceConnectionImpl::getDatabaseName() const char* TraceConnectionImpl::getUserName() { const UserId* user = m_att->att_user; - return user ? user->usr_user_name.c_str() : NULL; + return user ? user->getUserName().c_str() : NULL; } const char* TraceConnectionImpl::getRoleName() { const UserId* user = m_att->att_user; - return user ? user->usr_sql_role_name.c_str() : NULL; + return user ? user->getSqlRole().c_str() : NULL; } const char* TraceConnectionImpl::getCharSet() diff --git a/src/jrd/trace/TraceService.cpp b/src/jrd/trace/TraceService.cpp index 31144bf8ed..4f049b5450 100644 --- a/src/jrd/trace/TraceService.cpp +++ b/src/jrd/trace/TraceService.cpp @@ -79,7 +79,7 @@ void TraceSvcJrd::setAttachInfo(const string& /*svc_name*/, const string& user, { m_authBlock = authBlock; m_user = user; - m_admin = isAdmin || (m_user == SYSDBA_USER_NAME); + m_admin = isAdmin || (m_user == DBA_USER_NAME); } void TraceSvcJrd::startSession(TraceSession& session, bool interactive) diff --git a/src/jrd/trig.h b/src/jrd/trig.h index 5419091df7..2dd98515cb 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -258,13 +258,10 @@ static const UCHAR trigger1[] = blr_field, 6, 14, 'R', 'D', 'B', '$', 'O', 'W', 'N', 'E', 'R', '_', 'N', 'A', 'M', 'E', blr_field, 1, 8, 'R', 'D', 'B', '$', 'U', 'S', 'E', 'R', - blr_or, blr_eql, - blr_user_name, - blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', - blr_eql, - blr_current_role, - blr_literal, blr_text, 9, 0, 'R', 'D', 'B', '$', 'A', 'D', 'M', 'I', 'N', + blr_sys_function, 20, 'R','D','B','$','S','Y','S','T','E','M','_','P','R','I','V','I','L','E','G','E',1, + blr_literal, blr_text2, 2,0, 26,0, 'G','R','A','N','T','_','R','E','V','O','K','E','_','O','N','_','A','N','Y','_','O','B','J','E','C','T', + blr_literal, blr_bool, 1, blr_begin, blr_end, blr_if, @@ -382,13 +379,10 @@ static const UCHAR trigger1[] = 'A', 'M', 'E', blr_field, 6, 14, 'R', 'D', 'B', '$', 'O', 'W', 'N', 'E', 'R', '_', 'N', 'A', 'M', 'E', - blr_and, blr_neq, - blr_user_name, - blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', - blr_neq, - blr_current_role, - blr_literal, blr_text, 9, 0, 'R', 'D', 'B', '$', 'A', 'D', 'M', 'I', 'N', + blr_sys_function, 20, 'R','D','B','$','S','Y','S','T','E','M','_','P','R','I','V','I','L','E','G','E',1, + blr_literal, blr_text2, 2,0, 26,0, 'G','R','A','N','T','_','R','E','V','O','K','E','_','O','N','_','A','N','Y','_','O','B','J','E','C','T', + blr_literal, blr_bool, 1, blr_begin, blr_if, blr_not, @@ -473,13 +467,10 @@ static const UCHAR trigger1[] = 'A', 'M', 'E', blr_field, 6, 14, 'R', 'D', 'B', '$', 'O', 'W', 'N', 'E', 'R', '_', 'N', 'A', 'M', 'E', - blr_and, blr_neq, - blr_user_name, - blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', - blr_neq, - blr_current_role, - blr_literal, blr_text, 9, 0, 'R', 'D', 'B', '$', 'A', 'D', 'M', 'I', 'N', + blr_sys_function, 20, 'R','D','B','$','S','Y','S','T','E','M','_','P','R','I','V','I','L','E','G','E',1, + blr_literal, blr_text2, 2,0, 26,0, 'G','R','A','N','T','_','R','E','V','O','K','E','_','O','N','_','A','N','Y','_','O','B','J','E','C','T', + blr_literal, blr_bool, 1, blr_begin, blr_if, blr_not, @@ -632,13 +623,10 @@ static const UCHAR trigger1[] = blr_field, 18, 14, 'R', 'D', 'B', '$', 'O', 'W', 'N', 'E', 'R', '_', 'N', 'A', 'M', 'E', blr_user_name, - blr_and, blr_neq, - blr_user_name, - blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', - blr_neq, - blr_current_role, - blr_literal, blr_text, 9, 0, 'R', 'D', 'B', '$', 'A', 'D', 'M', 'I', 'N', + blr_sys_function, 20, 'R','D','B','$','S','Y','S','T','E','M','_','P','R','I','V','I','L','E','G','E',1, + blr_literal, blr_text2, 2,0, 26,0, 'G','R','A','N','T','_','R','E','V','O','K','E','_','O','N','_','A','N','Y','_','O','B','J','E','C','T', + blr_literal, blr_bool, 1, blr_if, blr_not, blr_any, @@ -743,13 +731,10 @@ static const UCHAR trigger1[] = blr_field, 26, 14, 'R', 'D', 'B', '$', 'O', 'W', 'N', 'E', 'R', '_', 'N', 'A', 'M', 'E', blr_user_name, - blr_and, blr_neq, - blr_user_name, - blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', - blr_neq, - blr_current_role, - blr_literal, blr_text, 9, 0, 'R', 'D', 'B', '$', 'A', 'D', 'M', 'I', 'N', + blr_sys_function, 20, 'R','D','B','$','S','Y','S','T','E','M','_','P','R','I','V','I','L','E','G','E',1, + blr_literal, blr_text2, 2,0, 26,0, 'G','R','A','N','T','_','R','E','V','O','K','E','_','O','N','_','A','N','Y','_','O','B','J','E','C','T', + blr_literal, blr_bool, 1, blr_if, blr_not, blr_any, @@ -850,13 +835,10 @@ static const UCHAR trigger1[] = blr_field, 22, 14, 'R', 'D', 'B', '$', 'O', 'W', 'N', 'E', 'R', '_', 'N', 'A', 'M', 'E', blr_user_name, - blr_and, blr_neq, - blr_user_name, - blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', - blr_neq, - blr_current_role, - blr_literal, blr_text, 9, 0, 'R', 'D', 'B', '$', 'A', 'D', 'M', 'I', 'N', + blr_sys_function, 20, 'R','D','B','$','S','Y','S','T','E','M','_','P','R','I','V','I','L','E','G','E',1, + blr_literal, blr_text2, 2,0, 26,0, 'G','R','A','N','T','_','R','E','V','O','K','E','_','O','N','_','A','N','Y','_','O','B','J','E','C','T', + blr_literal, blr_bool, 1, blr_if, blr_not, blr_any, @@ -2383,13 +2365,10 @@ static const UCHAR trigger31[] = blr_neq, blr_field, 1, 11, 'R', 'D', 'B', '$', 'G', 'R', 'A', 'N', 'T', 'O', 'R', blr_user_name, - blr_and, blr_neq, - blr_user_name, - blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', - blr_neq, - blr_current_role, - blr_literal, blr_text, 9, 0, 'R', 'D', 'B', '$', 'A', 'D', 'M', 'I', 'N', + blr_sys_function, 20, 'R','D','B','$','S','Y','S','T','E','M','_','P','R','I','V','I','L','E','G','E',1, + blr_literal, blr_text2, 2,0, 21,0, 'U','S','E','_','G','R','A','N','T','E','D','_','B','Y','_','C','L','A','U','S','E', + blr_literal, blr_bool, 1, blr_begin, blr_for, blr_rse, 1, diff --git a/src/jrd/types.h b/src/jrd/types.h index a9466cf7ba..e16f7c5c6d 100644 --- a/src/jrd/types.h +++ b/src/jrd/types.h @@ -187,3 +187,7 @@ TYPE("DETERMINISTIC", 1, nam_deterministic_flag) TYPE("USER", 0, nam_map_to_type) TYPE("ROLE", 1, nam_map_to_type) + +#define SYSTEM_PRIVILEGE(p) TYPE(STRINGIZE(p), int(Jrd::p), nam_system_privileges) +#include "SystemPrivileges.h" +#undef SYSTEM_PRIVILEGE diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index eb75f75891..deae879b1a 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -1464,14 +1464,14 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) break; case rel_types: - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, CREATE_USER_TYPES)) protect_system_table_delupd(tdbb, relation, "DELETE", true); if (EVL_field(0, rpb->rpb_record, f_typ_sys_flag, &desc) && MOV_get_long(&desc, 0)) protect_system_table_delupd(tdbb, relation, "DELETE", true); break; case rel_db_creators: - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, GRANT_REVOKE_ANY_DDL_RIGHT)) protect_system_table_delupd(tdbb, relation, "DELETE"); break; @@ -2486,14 +2486,14 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j break; case rel_types: - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, CREATE_USER_TYPES)) protect_system_table_delupd(tdbb, relation, "UPDATE", true); if (EVL_field(0, org_rpb->rpb_record, f_typ_sys_flag, &desc1) && MOV_get_long(&desc1, 0)) protect_system_table_delupd(tdbb, relation, "UPDATE", true); break; case rel_db_creators: - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, GRANT_REVOKE_ANY_DDL_RIGHT)) protect_system_table_delupd(tdbb, relation, "UPDATE"); break; @@ -3114,14 +3114,14 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) break; case rel_db_creators: - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, GRANT_REVOKE_ANY_DDL_RIGHT)) protect_system_table_insert(tdbb, request, relation); break; case rel_types: if (!(tdbb->getDatabase()->dbb_flags & DBB_creating)) { - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, CREATE_USER_TYPES)) protect_system_table_insert(tdbb, request, relation, true); else if (EVL_field(0, rpb->rpb_record, f_typ_sys_flag, &desc) && MOV_get_long(&desc, 0)) protect_system_table_insert(tdbb, request, relation, true); @@ -3390,7 +3390,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) break; case rel_backup_history: - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, USE_NBACKUP_UTILITY)) protect_system_table_insert(tdbb, request, relation); set_metadata_id(tdbb, rpb->rpb_record, f_backup_id, drq_g_nxt_nbakhist_id, "RDB$BACKUP_HISTORY"); @@ -3823,7 +3823,7 @@ static void check_class(thread_db* tdbb, static bool check_nullify_source(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, int field_id_1, int field_id_2) { - if (!tdbb->getAttachment()->locksmith()) + if (!tdbb->getAttachment()->locksmith(tdbb, NULL_PRIVILEGE)) // legacy right - no system privilege tuning !!! return false; bool nullify_found = false; @@ -3880,7 +3880,7 @@ static void check_owner(thread_db* tdbb, return; const Jrd::Attachment* const attachment = tdbb->getAttachment(); - const Firebird::MetaName name(attachment->att_user->usr_user_name); + const Firebird::MetaName name(attachment->att_user->getUserName()); desc2.makeText((USHORT) name.length(), CS_METADATA, (UCHAR*) name.c_str()); if (!MOV_compare(&desc1, &desc2)) return; @@ -3905,7 +3905,7 @@ static bool check_user(thread_db* tdbb, const dsc* desc) const TEXT* p = (TEXT *) desc->dsc_address; const TEXT* const end = p + desc->dsc_length; - const TEXT* q = tdbb->getAttachment()->att_user->usr_user_name.c_str(); + const TEXT* q = tdbb->getAttachment()->att_user->getUserName().c_str(); // It is OK to not internationalize this function for v4.00 as // User names are limited to 7-bit ASCII for v4.00 @@ -4302,7 +4302,7 @@ static THREAD_ENTRY_DECLARE garbage_collector(THREAD_ENTRY_PARAM arg) try { UserId user; - user.usr_user_name = "Garbage Collector"; + user.setUserName("Garbage Collector"); Jrd::Attachment* const attachment = Jrd::Attachment::create(dbb); RefPtr sAtt(FB_NEW SysStableAttachment(attachment)); @@ -5590,7 +5590,7 @@ static void set_owner_name(thread_db* tdbb, Record* record, USHORT field_id) if (!EVL_field(0, record, field_id, &desc1)) { const Jrd::Attachment* const attachment = tdbb->getAttachment(); - const Firebird::MetaName name(attachment->att_user->usr_user_name); + const Firebird::MetaName name(attachment->att_user->getUserName()); dsc desc2; desc2.makeText((USHORT) name.length(), CS_METADATA, (UCHAR*) name.c_str()); MOV_move(tdbb, &desc2, &desc1); diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 5402045532..854ce22606 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -1,19 +1,19 @@ /* 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 (?, ?, ?, ?); -- -('2016-05-15 12:46:00', 'JRD', 0, 791) +('2016-05-26 12:14:49', 'JRD', 0, 793) ('2015-03-17 18:33:00', 'QLI', 1, 533) ('2015-01-07 18:01:51', 'GFIX', 3, 134) ('1996-11-07 13:39:40', 'GPRE', 4, 1) ('2016-02-23 00:00:00', 'DSQL', 7, 40) -('2016-03-22 17:41:18', 'DYN', 8, 292) +('2016-05-30 17:56:47', 'DYN', 8, 296) ('1996-11-07 13:39:40', 'INSTALL', 10, 1) ('1996-11-07 13:38:41', 'TEST', 11, 4) ('2015-07-23 14:20:00', 'GBAK', 12, 370) ('2015-08-05 12:40:00', 'SQLERR', 13, 1045) ('1996-11-07 13:38:42', 'SQLWARN', 14, 613) ('2006-09-10 03:04:31', 'JRD_BUGCHK', 15, 307) -('2016-01-18 19:20:48', 'ISQL', 17, 195) +('2016-05-26 13:53:45', 'ISQL', 17, 196) ('2010-07-10 10:50:30', 'GSEC', 18, 105) ('2015-01-07 18:01:51', 'GSTAT', 21, 58) ('2013-12-19 17:31:31', 'FBSVCMGR', 22, 58) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index d0d7a46e7c..2af91b5549 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -898,6 +898,8 @@ Data source : @4', NULL, NULL) ('bad_crypt_key', NULL, 'CryptoManager.cpp', NULL, 0, 788, NULL, 'Invalid crypt key @1', NULL, NULL); ('encrypt_error', NULL, 'CryptoManager.cpp', NULL, 0, 789, NULL, 'Page requires encyption but crypt plugin is missing', NULL, NULL); ('max_idx_depth', NULL, 'btr.cpp', NULL, 0, 790, NULL, 'Maximum index depth (@1 levels) is reached', NULL, NULL); +('wrong_prvlg', 'SCL_convert_privilege', 'scl.epp', NULL, 0, 791, NULL, 'System privilege @1 does not exist', NULL, NULL); +('miss_prvlg', 'validateAccess', 'jrd.cpp', NULL, 0, 792, NULL, 'Unable to perform operation: system privilege @1 is missing', 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); @@ -1931,7 +1933,7 @@ COMMIT WORK; ('dyn_cannot_addrem_computed', 'DYN_modify_sql_field', 'dyn_mod.epp', NULL, 8, 249, NULL, 'Cannot add or remove COMPUTED from column @1', NULL, NULL); ('dyn_no_empty_pw', 'dyn_user', 'dyn.epp', NULL, 8, 250, NULL, 'Password should not be empty string', NULL, NULL); ('dyn_dup_index', 'DYN_define_index', 'dyn_def.epp', NULL, 8, 251, NULL, 'Index @1 already exists', NULL, NULL); -('dyn_locksmith_use_granted', 'grant/revoke', 'dyn.epp', NULL, 8, 252, NULL, 'Only @1 or database owner can use GRANTED BY clause', NULL, NULL); +('dyn_locksmith_use_granted', 'grant/revoke', 'dyn.epp', NULL, 8, 252, NULL, 'Only @1 or user with privilege USE_GRANTED_BY_CLAUSE can use GRANTED BY clause', NULL, NULL); ('dyn_dup_exception', 'DYN_define_exception', 'dyn_def.epp', NULL, 8, 253, NULL, 'Exception @1 already exists', NULL, NULL); ('dyn_dup_generator', 'DYN_define_generator', 'dyn_def.epp', NULL, 8, 254, NULL, 'Sequence @1 already exists', NULL, NULL); (NULL, 'revoke_all', 'dyn.epp', NULL, 8, 255, NULL, 'ERASE RDB$USER_PRIVILEGES failed in REVOKE ALL ON ALL', NULL, NULL); @@ -1972,6 +1974,9 @@ COMMIT WORK; ('dyn_defvaldecl_package_func', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 290, NULL, 'Default values for parameters are allowed only in declaration of packaged function @1.@2', NULL, NULL); ('dyn_create_user_no_password', 'CreateAlterUserNode', 'DdlNodes.epp', NULL, 8, 291, NULL, 'Password must be specified when creating user', NULL, NULL); ('dyn_cyclic_role', 'GrantRevokeNode::grantRevoke', 'DdlNodes.epp', NULL, 8, 292, NULL, 'role @1 can not be granted to role @2', NULL, NULL); +(NULL, 'CreateAlterRoleNode::execute', 'DdlNodes.epp', NULL, 8, 293, NULL, 'DROP SYSTEM PRIVILEGES should not be used in CREATE ROLE operator', NULL, NULL); +(NULL, 'CreateAlterRoleNode::execute', 'DdlNodes.epp', NULL, 8, 294, NULL, 'Access to SYSTEM PRIVILEGES in ROLES denied to @1', NULL, NULL); +(NULL, 'grant/revoke', 'DdlNode.epp', NULL, 8, 295, NULL, 'Only @1, DB owner @2 or user with privilege USE_GRANTED_BY_CLAUSE can use GRANTED BY clause', NULL, NULL); COMMIT WORK; -- TEST (NULL, 'main', 'test.c', NULL, 11, 0, NULL, 'This is a modified text message', NULL, NULL); @@ -3054,6 +3059,7 @@ Fetches = !', NULL, NULL); ('DATABASE_CRYPTED', 'SHOW_dbb_parameters', 'show.epp', NULL, 17, 192, NULL, 'Database encrypted', NULL, NULL); ('DATABASE_NOT_CRYPTED', 'SHOW_dbb_parameters', 'show.epp', NULL, 17, 193, NULL, 'Database not encrypted', NULL, NULL); ('DATABASE_CRYPT_PROCESS', 'SHOW_dbb_parameters', 'show.epp', NULL, 17, 194, NULL, 'crypt thread not complete', NULL, NULL); +('MSG_ROLES', 'SHOW_metadata', 'show.epp', NULL, 17, 195, NULL, 'Roles:', NULL, NULL); -- GSEC ('GsecMsg1', 'get_line', 'gsec.e', NULL, 18, 1, NULL, 'GSEC>', NULL, NULL); ('GsecMsg2', 'printhelp', 'gsec.e', 'This message is used in the Help display. It should be the same as number 1 (but in lower case).', 18, 2, NULL, 'gsec', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index 59bb1b5c11..f29969ad44 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -797,6 +797,8 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-902, '08', '006', 0, 788, 'bad_crypt_key', NULL, NULL) (-901, 'XX', '000', 0, 789, 'encrypt_error', NULL, NULL) (-904, '54', '000', 0, 790, 'max_idx_depth', NULL, NULL) +(-901, '0A', '000', 0, 791, 'wrong_prvlg', NULL, NULL) +(-902, '28', '000', 0, 792, 'miss_prvlg', NULL, NULL) -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL) diff --git a/src/utilities/ibmgr/ibmgr.cpp b/src/utilities/ibmgr/ibmgr.cpp index 2276848d73..918b30c4bd 100644 --- a/src/utilities/ibmgr/ibmgr.cpp +++ b/src/utilities/ibmgr/ibmgr.cpp @@ -131,7 +131,7 @@ int CLIB_ROUTINE main( int argc, char **argv) !strcmp(pw->pw_name, INTERBASE_USER_NAME) || !strcmp(pw->pw_name, INTERBASE_USER_SHORT)) { - strcpy(ibmgr_data.user, SYSDBA_USER_NAME); + strcpy(ibmgr_data.user, DBA_USER_NAME); } else copy_str_upper(ibmgr_data.user, pw->pw_name); @@ -882,7 +882,7 @@ static SSHORT parse_cmd_line( int argc, TEXT** argv, bool zapPasswd) switch (ibmgr_data.operation) { case OP_SHUT: - if (strcmp(ibmgr_data.user, SYSDBA_USER_NAME)) + if (strcmp(ibmgr_data.user, DBA_USER_NAME)) { SRVRMGR_msg_get(MSG_NOPERM, msg); fprintf(OUTFILE, "%s\n", msg); @@ -906,7 +906,7 @@ static SSHORT parse_cmd_line( int argc, TEXT** argv, bool zapPasswd) strcmp(ibmgr_data.real_user, FIREBIRD_USER_NAME) && strcmp(ibmgr_data.real_user, INTERBASE_USER_NAME) && strcmp(ibmgr_data.real_user, INTERBASE_USER_SHORT)) || - strcmp(ibmgr_data.user, SYSDBA_USER_NAME)) + strcmp(ibmgr_data.user, DBA_USER_NAME)) { SRVRMGR_msg_get(MSG_NOPERM, msg); fprintf(OUTFILE, "%s\n", msg); diff --git a/src/utilities/ibmgr/srvrmgr.cpp b/src/utilities/ibmgr/srvrmgr.cpp index 3d3d7aeea0..ea2c9fe976 100644 --- a/src/utilities/ibmgr/srvrmgr.cpp +++ b/src/utilities/ibmgr/srvrmgr.cpp @@ -325,12 +325,12 @@ static bool attach_service( ibmgr_data_t* data) TEXT spb[SPB_BUFLEN]; TEXT* p = spb; - if (!strcmp(data->user, SYSDBA_USER_NAME)) + if (!strcmp(data->user, DBA_USER_NAME)) { *p++ = isc_spb_version1; *p++ = isc_spb_user_name; - *p++ = strlen(SYSDBA_USER_NAME); - strcpy(p, SYSDBA_USER_NAME); + *p++ = strlen(DBA_USER_NAME); + strcpy(p, DBA_USER_NAME); p += strlen(p); *p++ = isc_spb_password; *p++ = strlen(data->password); diff --git a/src/yvalve/keywords.cpp b/src/yvalve/keywords.cpp index efcc4e0799..16de2f5f29 100644 --- a/src/yvalve/keywords.cpp +++ b/src/yvalve/keywords.cpp @@ -318,6 +318,7 @@ static const TOK tokens[] = {PRESERVE, "PRESERVE", 2, true}, {PRIMARY, "PRIMARY", 1, false}, {PRIOR, "PRIOR", 2, true}, + {PRIVILEGE, "PRIVILEGE", 1, false}, {PRIVILEGES, "PRIVILEGES", 1, false}, {PROCEDURE, "PROCEDURE", 1, false}, {PROTECTED, "PROTECTED", 1, false}, @@ -328,6 +329,7 @@ static const TOK tokens[] = {RDB_RECORD_VERSION, "RDB$RECORD_VERSION", 2, false}, {RDB_ROLE_IN_USE, "RDB$ROLE_IN_USE", 2, false}, {RDB_SET_CONTEXT, "RDB$SET_CONTEXT", 2, true}, + {RDB_SYSTEM_PRIVILEGE, "RDB$SYSTEM_PRIVILEGE", 2, false}, {READ, "READ", 1, false}, {REAL, "REAL", 1, false}, {VERSION, "RECORD_VERSION", 1, false}, @@ -408,6 +410,7 @@ static const TOK tokens[] = {SUB_TYPE, "SUB_TYPE", 1, false}, {SUM, "SUM", 1, false}, {SUSPEND, "SUSPEND", 1, false}, + {SYSTEM, "SYSTEM", 1, false}, {TABLE, "TABLE", 1, false}, {TAGS, "TAGS", 2, true}, {TAN, "TAN", 2, false},