diff --git a/doc/sql.extensions/README.context_variables2 b/doc/sql.extensions/README.context_variables2 index 5f6994e631..bac0d8ec92 100644 --- a/doc/sql.extensions/README.context_variables2 +++ b/doc/sql.extensions/README.context_variables2 @@ -98,6 +98,9 @@ Usage: CURRENT_USER | Current user for the connection. Returned value is the | same as of CURRENT_USER pseudo-variable | + EFFECTIVE_USER | Effective user for now. It indicates privileges of + | which user is currently used to execute function, procedure, trigger. + | CURRENT_ROLE | Current role for the connection. Returned value is the | same as of CURRENT_ROLE pseudo-variable | diff --git a/doc/sql.extensions/README.sql_security.txt b/doc/sql.extensions/README.sql_security.txt index b0f767196b..830f20c247 100644 --- a/doc/sql.extensions/README.sql_security.txt +++ b/doc/sql.extensions/README.sql_security.txt @@ -24,7 +24,8 @@ By default INVOKER is used to keep backward compatibility. You can change this b with SQL STANDARD by using ALTER DATABASE SET DEFAULT SQL SECURITY statement. If INVOKER is specified a current set of privileges of the current user will be used. -If DEFINER - a set of privileges of object owner will be used to check an access to database objects used by this object. +If DEFINER - a set of privileges of object owner will be used to check an access to database objects +used by this object. Trigger inherits SQL SECURITY option from TABLE but can overwrite it by explicit specifying. If SQL SECURITY option will be changed for table, existing triggers without explicitly specified option will not use new value immediately @@ -32,6 +33,19 @@ it will take effect next time trigger will be loaded into metadata cache. For procedures and functions defined in package explicit SQL SECURITY clause is prohibit. +In stored procedures, functions or triggers you may check which user if really effective and which privileges +are applying to accessed objects by using the system context variable EFFECTIVE_USER from SYSTEM namespace. +select RDB$GET_CONTEXT('SYSTEM', 'EFFECTIVE_USER') from RDB$DATABASE; + +Note: now the same object may be called in different security contexts and requires different privileges. +For example we have: +- a stored procedure INV with SQL SECURITY INVOKER which insert records in a table T +- a stored procedure DEF with SQL SECURITY DEFINER defined by SYSDBA + +If a user U calls INV an access to T will require an INSERT privile to be granted to U (and EXECUTE on INV of course). +In this case U is EFFECTIVE_USER due INV running. +If user U calls DEF an access to T will require an INSERT privilege to be granted to SYSDBA (end EXECUTE on DEF). +In this case SYSDBA is EFFECTIVE_USER due INV running as well as due DEF running. Example 1. It's enough to grant only SELECT privilege to user US for table T. In case of INVOKER it will require also EXECUTE for function F. diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index 5775357950..e3889ab188 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1918,6 +1918,8 @@ C -- PARAMETER (GDS__tra_snapshot_does_not_exist = 335545252) INTEGER*4 GDS__eds_input_prm_not_used PARAMETER (GDS__eds_input_prm_not_used = 335545253) + INTEGER*4 GDS__effective_user + PARAMETER (GDS__effective_user = 335545254) 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 40856827e7..fe8d576bf0 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1913,6 +1913,8 @@ const gds_tra_snapshot_does_not_exist = 335545252; isc_eds_input_prm_not_used = 335545253; gds_eds_input_prm_not_used = 335545253; + isc_effective_user = 335545254; + gds_effective_user = 335545254; isc_gfix_db_name = 335740929; gds_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 28a2ccba5c..28fc273dba 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -3149,6 +3149,9 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) cons Arg::Str(procedure->getName().identifier) << Arg::Str(procedure->getName().package)); } + UserId* invoker = procedure->invoker ? procedure->invoker : tdbb->getAttachment()->att_ss_user; + AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); + ULONG inMsgLength = 0; UCHAR* inMsg = NULL; diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index d047919c1b..00470cc478 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -955,6 +955,7 @@ static const struct { {"bad_repl_handle", 335545251}, {"tra_snapshot_does_not_exist", 335545252}, {"eds_input_prm_not_used", 335545253}, + {"effective_user", 335545254}, {"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 e21dce6711..966af087d8 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -989,6 +989,7 @@ const ISC_STATUS isc_tom_chacha_key = 335545250L; const ISC_STATUS isc_bad_repl_handle = 335545251L; const ISC_STATUS isc_tra_snapshot_does_not_exist = 335545252L; const ISC_STATUS isc_eds_input_prm_not_used = 335545253L; +const ISC_STATUS isc_effective_user = 335545254L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1463,7 +1464,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 = 1407; +const ISC_STATUS isc_err_max = 1408; #else /* c definitions */ @@ -2422,6 +2423,7 @@ const ISC_STATUS isc_err_max = 1407; #define isc_bad_repl_handle 335545251L #define isc_tra_snapshot_does_not_exist 335545252L #define isc_eds_input_prm_not_used 335545253L +#define isc_effective_user 335545254L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2896,7 +2898,7 @@ const ISC_STATUS isc_err_max = 1407; #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 1407 +#define isc_err_max 1408 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 1a32c7964c..2163f6221b 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -958,6 +958,7 @@ Data source : @4"}, /* eds_statement */ {335545251, "invalid replicator handle"}, /* bad_repl_handle */ {335545252, "Transaction's base snapshot number does not exist"}, /* tra_snapshot_does_not_exist */ {335545253, "Input parameter '@1' is not used in SQL query text"}, /* eds_input_prm_not_used */ + {335545254, "Effective user is @1"}, /* effective_user */ {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 11b6f31b8c..b4bba26cf9 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -954,6 +954,7 @@ static const struct { {335545251, -901}, /* 931 bad_repl_handle */ {335545252, -901}, /* 932 tra_snapshot_does_not_exist */ {335545253, -901}, /* 933 eds_input_prm_not_used */ + {335545254, -551}, /* 934 effective_user */ {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 e6b5b292a6..70469c42f4 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -954,6 +954,7 @@ static const struct { {335545251, "08003"}, // 931 bad_repl_handle {335545252, "0B000"}, // 932 tra_snapshot_does_not_exist {335545253, "42000"}, // 933 eds_input_prm_not_used + {335545254, "28000"}, // 934 effective_user {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index c692f418d0..f2cb702f87 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -207,6 +207,8 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb) : att_pool(pool), att_memory_stats(&dbb->dbb_memory_stats), att_database(dbb), + att_ss_user(NULL), + att_user_ids(*pool), att_active_snapshots(*pool), att_requests(*pool), att_lock_owner_id(Database::getLockOwnerId()), @@ -986,6 +988,18 @@ bool Attachment::getIdleTimerTimestamp(ISC_TIMESTAMP_TZ& ts) const return true; } +UserId* Attachment::getUserId(const MetaName& userName) +{ + UserId* result = NULL; + if (!att_user_ids.get(userName, result)) + { + result = FB_NEW_POOL(*att_pool) UserId(*att_pool); + result->setUserName(userName); + att_user_ids.put(userName, result); + } + return result; +} + /// Attachment::IdleTimer void Attachment::IdleTimer::handler() diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 6a792a4b12..4f90227382 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -359,6 +359,9 @@ public: Database* att_database; // Parent database block Attachment* att_next; // Next attachment to database UserId* att_user; // User identification + UserId* att_ss_user; // User identification for SQL SECURITY actual user + Firebird::GenericMap > > att_user_ids; // set of used UserIds jrd_tra* att_transactions; // Transactions belonging to attachment jrd_tra* att_dbkey_trans; // transaction to control db-key scope TraNumber att_oldest_snapshot; // GTT's record versions older than this can be garbage-collected @@ -566,6 +569,8 @@ public: att_batches.findAndRemove(b); } + UserId* getUserId(const Firebird::MetaName &userName); + private: Attachment(MemoryPool* pool, Database* dbb); ~Attachment(); @@ -608,7 +613,8 @@ private: inline bool Attachment::locksmith(thread_db* tdbb, SystemPrivilege sp) const { - return att_user && att_user->locksmith(tdbb, sp); + return att_user && att_user->locksmith(tdbb, sp) || + att_ss_user && att_ss_user->locksmith(tdbb, sp); } inline jrd_tra* Attachment::getSysTransaction() diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 4057ea5c41..86bace596c 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -732,7 +732,7 @@ ExtEngineManager::Function::~Function() void ExtEngineManager::Function::execute(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const { EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine); - const MetaName& userName = udf->ssDefiner.specified && udf->ssDefiner.value ? udf->owner : ""; + const MetaName& userName = udf->invoker ? udf->invoker->getUserName() : ""; ContextManager ctxManager(tdbb, attInfo, function, (udf->getName().package.isEmpty() ? CallerName(obj_udf, udf->getName().identifier, userName) : @@ -786,8 +786,7 @@ ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, UCHAR* inMsg, UCHAR* out firstFetch(true) { attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine); - const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? - procedure->prc->owner : ""; + const MetaName& userName = procedure->prc->invoker ? procedure->prc->invoker->getUserName() : ""; ContextManager ctxManager(tdbb, attInfo, procedure->procedure, (procedure->prc->getName().package.isEmpty() ? CallerName(obj_procedure, procedure->prc->getName().identifier, userName) : @@ -821,8 +820,7 @@ bool ExtEngineManager::ResultSet::fetch(thread_db* tdbb) if (!resultSet) return wasFirstFetch; - const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? - procedure->prc->owner : ""; + const MetaName& userName = procedure->prc->invoker ? procedure->prc->invoker->getUserName() : ""; ContextManager ctxManager(tdbb, attInfo, charSet, (procedure->prc->getName().package.isEmpty() ? CallerName(obj_procedure, procedure->prc->getName().identifier, userName) : @@ -1286,7 +1284,7 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: entryPointTrimmed.trim(); EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine); - const MetaName& userName = udf->ssDefiner.specified && udf->ssDefiner.value ? udf->owner : ""; + const MetaName& userName = udf->invoker ? udf->invoker->getUserName() : ""; ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet, (udf->getName().package.isEmpty() ? CallerName(obj_udf, udf->getName().identifier, userName) : @@ -1410,7 +1408,7 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ entryPointTrimmed.trim(); EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine); - const MetaName& userName = prc->ssDefiner.specified && prc->ssDefiner.value ? prc->owner : ""; + const MetaName& userName = prc->invoker ? prc->invoker->getUserName() : ""; ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet, (prc->getName().package.isEmpty() ? CallerName(obj_procedure, prc->getName().identifier, userName) : diff --git a/src/jrd/Function.epp b/src/jrd/Function.epp index 2a815171c8..b2c943ce1e 100644 --- a/src/jrd/Function.epp +++ b/src/jrd/Function.epp @@ -221,6 +221,7 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT (X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME))); function->owner = X.RDB$OWNER_NAME; + Nullable ssDefiner; if (!X.RDB$SECURITY_CLASS.NULL) { @@ -241,19 +242,22 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT // SQL SECURITY of function must be the same if it's defined in package if (!PKG.RDB$SQL_SECURITY.NULL) - function->ssDefiner = (bool) PKG.RDB$SQL_SECURITY; + ssDefiner = (bool) PKG.RDB$SQL_SECURITY; END_FOR } - if (!function->ssDefiner.specified) + if (!ssDefiner.specified) { if (!X.RDB$SQL_SECURITY.NULL) - function->ssDefiner = (bool) X.RDB$SQL_SECURITY; + ssDefiner = (bool) X.RDB$SQL_SECURITY; else - function->ssDefiner = MET_get_ss_definer(tdbb); + ssDefiner = MET_get_ss_definer(tdbb); } + if (ssDefiner.orElse(false)) + function->invoker = attachment->getUserId(function->owner); + size_t count = 0; ULONG length = 0; diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/JrdStatement.cpp index 9850695efb..312932b39e 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/JrdStatement.cpp @@ -54,7 +54,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) accessList(*p), resources(*p), triggerName(*p), - triggerOwner(*p), + triggerInvoker(NULL), parentStatement(NULL), subStatements(*p), fors(*p), @@ -402,7 +402,8 @@ void JrdStatement::verifyAccess(thread_db* tdbb) SET_TDBB(tdbb); ExternalAccessList external; - buildExternalAccess(tdbb, external); + const MetaName defaultUser; + buildExternalAccess(tdbb, external, defaultUser); for (ExternalAccess* item = external.begin(); item != external.end(); ++item) { @@ -440,7 +441,7 @@ void JrdStatement::verifyAccess(thread_db* tdbb) if (!relation) continue; - MetaName userName; + MetaName userName = item->user; if (item->exa_view_id) { jrd_rel* view = MET_lookup_relation_id(tdbb, item->exa_view_id, false); @@ -479,7 +480,7 @@ void JrdStatement::verifyAccess(thread_db* tdbb) { const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str()); - MetaName userName; + MetaName userName = item->user; if (access->acc_ss_rel_id) { @@ -488,9 +489,6 @@ void JrdStatement::verifyAccess(thread_db* tdbb) userName = view->rel_owner_name; } - if (userName.isEmpty() && routine->ssDefiner.specified && routine->ssDefiner.value && routine->owner.hasData()) - userName = routine->owner; - if (routine->getName().package.isEmpty()) { SCL_check_access(tdbb, sec_class, userName, aclType, @@ -695,7 +693,7 @@ void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, // Invoke buildExternalAccess for triggers in vector inline void JrdStatement::triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, - TrigVector* tvec) + TrigVector* tvec, const MetaName& user) { if (!tvec) return; @@ -706,34 +704,39 @@ inline void JrdStatement::triggersExternalAccess(thread_db* tdbb, ExternalAccess t.compile(tdbb); if (t.statement) - t.statement->buildExternalAccess(tdbb, list); + { + const MetaName& userName = (t.ssDefiner.specified && t.ssDefiner.value) ? t.owner : user; + t.statement->buildExternalAccess(tdbb, list, userName); + } } } // Recursively walk external dependencies (procedures, triggers) for request to assemble full // list of requests it depends on. -void JrdStatement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list) +void JrdStatement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, const MetaName &user) { for (ExternalAccess* item = externalList.begin(); item != externalList.end(); ++item) { FB_SIZE_T i; - if (list.find(*item, i)) - continue; - - list.insert(i, *item); // Add externals recursively if (item->exa_action == ExternalAccess::exa_procedure) { jrd_prc* const procedure = MET_lookup_procedure_id(tdbb, item->exa_prc_id, false, false, 0); if (procedure && procedure->getStatement()) - procedure->getStatement()->buildExternalAccess(tdbb, list); + { + item->user = procedure->invoker ? procedure->invoker->getUserName() : user; + procedure->getStatement()->buildExternalAccess(tdbb, list, item->user); + } } else if (item->exa_action == ExternalAccess::exa_function) { Function* const function = Function::lookup(tdbb, item->exa_fun_id, false, false, 0); if (function && function->getStatement()) - function->getStatement()->buildExternalAccess(tdbb, list); + { + item->user = function->invoker ? function->invoker->getUserName() : user; + function->getStatement()->buildExternalAccess(tdbb, list, item->user); + } } else { @@ -762,9 +765,13 @@ void JrdStatement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list continue; // should never happen, silence the compiler } - triggersExternalAccess(tdbb, list, vec1); - triggersExternalAccess(tdbb, list, vec2); + item->user = relation->rel_ss_definer.orElse(false) ? relation->rel_owner_name : user; + triggersExternalAccess(tdbb, list, vec1, item->user); + triggersExternalAccess(tdbb, list, vec2, item->user); } + + if (!list.find(*item, i)) + list.insert(i, *item); } } diff --git a/src/jrd/JrdStatement.h b/src/jrd/JrdStatement.h index 7f252b5db9..7c10c59d34 100644 --- a/src/jrd/JrdStatement.h +++ b/src/jrd/JrdStatement.h @@ -59,9 +59,9 @@ public: private: static void verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers, Firebird::MetaName userName); - static void triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, TrigVector* tvec); + static void triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, TrigVector* tvec, const Firebird::MetaName &user); - void buildExternalAccess(thread_db* tdbb, ExternalAccessList& list); + void buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, const Firebird::MetaName& user); public: MemoryPool* pool; @@ -76,7 +76,7 @@ public: const jrd_prc* procedure; // procedure, if any const Function* function; // function, if any Firebird::MetaName triggerName; // name of request (trigger), if any - Firebird::MetaName triggerOwner; // user name if trigger run with SQL SECURITY DEFINER + Jrd::UserId* triggerInvoker; // user name if trigger run with SQL SECURITY DEFINER JrdStatement* parentStatement; // Sub routine's parent statement Firebird::Array subStatements; // Array of subroutines' statements const StmtNode* topNode; // top of execution tree diff --git a/src/jrd/Routine.h b/src/jrd/Routine.h index 73f3c41ec7..5157647ae0 100644 --- a/src/jrd/Routine.h +++ b/src/jrd/Routine.h @@ -38,6 +38,7 @@ namespace Jrd class Lock; class Format; class Parameter; + class UserId; class Routine : public Firebird::PermanentStorage { @@ -60,7 +61,8 @@ namespace Jrd useCount(0), intUseCount(0), alterCount(0), - existenceLock(NULL) + existenceLock(NULL), + invoker(NULL) { } @@ -180,8 +182,8 @@ namespace Jrd USHORT alterCount; // No. of times the routine was altered Lock* existenceLock; // existence lock, if any - Nullable ssDefiner; // true ? SQL DEFINER : SQL INVOKER Firebird::MetaName owner; + Jrd::UserId* invoker; // Invoker ID }; } diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index c57d9fd6db..31925e6bcf 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -335,6 +335,7 @@ const char CURRENT_ROLE_NAME[] = "CURRENT_ROLE", SESSION_IDLE_TIMEOUT[] = "SESSION_IDLE_TIMEOUT", STATEMENT_TIMEOUT[] = "STATEMENT_TIMEOUT", + EFFECTIVE_USER_NAME[] = "EFFECTIVE_USER", // SYSTEM namespace: transaction wise items TRANSACTION_ID_NAME[] = "TRANSACTION_ID", ISOLATION_LEVEL_NAME[] = "ISOLATION_LEVEL", @@ -3976,6 +3977,18 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar resultStr.printf("%d", EDS::Manager::getConnPool()->getLifeTime()); else if (nameStr == REPLICATION_SEQ_NAME) resultStr.printf("%" UQUADFORMAT, dbb->getReplSequence(tdbb)); + else if (nameStr == EFFECTIVE_USER_NAME) + { + MetaName user; + if (attachment->att_ss_user) + user = attachment->att_ss_user->getUserName(); + else if (attachment->att_user) + user = attachment->att_user->getUserName(); + + if (user.isEmpty()) + return NULL; + resultStr = user.c_str(); + } else { // "Context variable %s is not found in namespace %s" diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 4628533423..787b183b8b 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -1158,7 +1158,13 @@ void EXE_execute_triggers(thread_db* tdbb, TraceTrigExecute trace(tdbb, trigger, which_trig); - EXE_start(tdbb, trigger, transaction); + { // Scope to replace att_ss_user + const JrdStatement* s = trigger->getStatement(); + UserId* invoker = s->triggerInvoker ? s->triggerInvoker : tdbb->getAttachment()->att_ss_user; + AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); + + EXE_start(tdbb, trigger, transaction); + } const bool ok = (trigger->req_operation != jrd_req::req_unwind); trace.finish(ok ? ITracePlugin::RESULT_SUCCESS : ITracePlugin::RESULT_FAILED); diff --git a/src/jrd/exe.h b/src/jrd/exe.h index bba74d004d..d901e30c22 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -252,6 +252,7 @@ struct ExternalAccess USHORT exa_fun_id; USHORT exa_rel_id; USHORT exa_view_id; + Firebird::MetaName user; // User which touch the recources. // Procedure ExternalAccess(exa_act action, USHORT id) : @@ -279,6 +280,8 @@ struct ExternalAccess return i1.exa_rel_id > i2.exa_rel_id; if (i1.exa_view_id != i2.exa_view_id) return i1.exa_view_id > i2.exa_view_id; + if (i1.user != i2.user) + return i1.user > i2.user; return false; // Equal } }; diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 5dc2c2689f..8c1cab5886 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -461,12 +461,14 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (statement && statement->parentStatement) statement = statement->parentStatement; - if (statement && statement->triggerName.hasData()) - tran->getHandle()->tra_caller_name = CallerName(obj_trigger, statement->triggerName, statement->triggerOwner); + if (statement && statement->triggerInvoker) + tran->getHandle()->tra_caller_name = CallerName(obj_trigger, + statement->triggerName, + statement->triggerInvoker->getUserName()); else if (statement && (routine = statement->getRoutine()) && routine->getName().identifier.hasData()) { - const MetaName& userName = routine->ssDefiner.specified && routine->ssDefiner.value ? routine->owner : ""; + const MetaName& userName = routine->invoker ? routine->invoker->getUserName() : ""; if (routine->getName().package.isEmpty()) { tran->getHandle()->tra_caller_name = CallerName(routine->getObjectType(), diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 38da5bf3c9..512f4d4d42 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -915,8 +915,8 @@ void Trigger::compile(thread_db* tdbb) } statement->triggerName = name; - if (ssDefiner.specified && ssDefiner.value) - statement->triggerOwner = owner; + if (ssDefiner.orElse(false)) + statement->triggerInvoker = att->getUserId(owner); if (sys_trigger) statement->flags |= JrdStatement::FLAG_SYS_TRIGGER; diff --git a/src/jrd/met.epp b/src/jrd/met.epp index bb153fc134..a48b83455e 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -3325,6 +3325,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) } procedure->setId(P.RDB$PROCEDURE_ID); + Nullable ssDefiner; if (!P.RDB$SECURITY_CLASS.NULL) procedure->setSecurityName(P.RDB$SECURITY_CLASS); @@ -3340,20 +3341,24 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) procedure->setSecurityName(PKG.RDB$SECURITY_CLASS); if (!PKG.RDB$SQL_SECURITY.NULL) - procedure->ssDefiner = (bool) PKG.RDB$SQL_SECURITY; + ssDefiner = (bool) PKG.RDB$SQL_SECURITY; } END_FOR } - if (!procedure->ssDefiner.specified) + if (!ssDefiner.specified) { if (!P.RDB$SQL_SECURITY.NULL) - procedure->ssDefiner = (bool) P.RDB$SQL_SECURITY; + ssDefiner = (bool) P.RDB$SQL_SECURITY; else - procedure->ssDefiner = MET_get_ss_definer(tdbb); + ssDefiner = MET_get_ss_definer(tdbb); } procedure->owner = P.RDB$OWNER_NAME; + + if (ssDefiner.orElse(false)) + procedure->invoker = attachment->getUserId(procedure->owner); + procedure->setImplemented(true); procedure->getInputFields().resize(P.RDB$PROCEDURE_INPUTS); procedure->getOutputFields().resize(P.RDB$PROCEDURE_OUTPUTS); diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index 1eab651903..659b7a1028 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -158,6 +158,9 @@ bool ProcedureScan::getRecord(thread_db* tdbb) const if (--tdbb->tdbb_quantum < 0) JRD_reschedule(tdbb, 0, true); + UserId* invoker = m_procedure->invoker ? m_procedure->invoker : tdbb->getAttachment()->att_ss_user; + AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); + jrd_req* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index aae3a26140..3c277efbea 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -76,8 +76,8 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb, const SecurityClas static SecurityClass::flags_t get_sys_privileges(thread_db* tdbb); static SecurityClass::flags_t walk_acl(thread_db* tdbb, const Acl&, const MetaName&, SLONG, const Firebird::MetaName&); -static void raiseError(SecurityClass::flags_t mask, SLONG type, const Firebird::MetaName& name, - const Firebird::MetaName& r_name); +static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, const Firebird::MetaName& name, + const Firebird::MetaName& r_name, const MetaName &invoker); namespace @@ -176,8 +176,8 @@ namespace } // anonymous namespace -static void raiseError(SecurityClass::flags_t mask, SLONG type, const Firebird::MetaName& name, - const Firebird::MetaName& r_name) +static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, const Firebird::MetaName& name, + const Firebird::MetaName& r_name, const Firebird::MetaName& invoker) { const P_NAMES* names; for (names = p_names; names->p_names_priv; names++) @@ -190,9 +190,16 @@ static void raiseError(SecurityClass::flags_t mask, SLONG type, const Firebird:: const Firebird::string fullName = r_name.hasData() ? r_name.c_str() + Firebird::string(".") + name.c_str() : name.c_str(); - ERR_post(Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << - Arg::Str(typeAsStr) << - Arg::Str(fullName)); + Arg::StatusVector status; + status << Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << + Arg::Str(typeAsStr) << + Arg::Str(fullName); + if (invoker.hasData()) + { + status << Arg::Gds(isc_effective_user) << Arg::Str(invoker); + } + ERR_post(status); + } @@ -231,9 +238,15 @@ void SCL_check_access(thread_db* tdbb, if (s_class && (s_class->scl_flags & SCL_corrupt)) { - ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("(ACL unrecognized)") << + Arg::StatusVector status; + status << Arg::Gds(isc_no_priv) << Arg::Str("(ACL unrecognized)") << Arg::Str("security_class") << - Arg::Str(s_class->scl_name)); + Arg::Str(s_class->scl_name); + if (userName.hasData()) + { + status << Arg::Gds(isc_effective_user) << Arg::Str(userName); + } + ERR_post(status); } // Make fast check first @@ -263,7 +276,7 @@ void SCL_check_access(thread_db* tdbb, return; } - raiseError(mask, type, name, r_name); + raiseError(tdbb, mask, type, name, r_name, userName); } @@ -568,7 +581,7 @@ void SCL_check_index(thread_db* tdbb, const Firebird::MetaName& index_name, UCHA { // Someone is going to reference system table in FK // Usually it's not good idea - raiseError(mask, SCL_object_table, reln_name, ""); + raiseError(tdbb, mask, SCL_object_table, reln_name, "", NULL); } // Check if the relation exists. It may not have been created yet. @@ -791,7 +804,7 @@ void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla { // Someone is going to modify system table layout // Usually it's not good idea - raiseError(mask, SCL_object_table, name, ""); + raiseError(tdbb, mask, SCL_object_table, name, "", NULL); } if (!REL.RDB$SECURITY_CLASS.NULL) diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 9869e720c6..d242702e8f 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -261,6 +261,19 @@ public: usr_granted_roles = ui.usr_granted_roles; } + UserId(Firebird::MemoryPool& p) + : usr_user_name(p), + usr_sql_role_name(p), + usr_granted_roles(p), + usr_trusted_role(p), + usr_init_role(p), + usr_project_name(p), + usr_org_name(p), + usr_auth_method(p), + usr_auth_block(p) + { + } + UserId(const UserId& ui) : usr_user_name(ui.usr_user_name), usr_sql_role_name(ui.usr_sql_role_name), diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index ab23a30c9f..14eee1d523 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -1,7 +1,7 @@ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); -- -('2019-03-04 19:40:00', 'JRD', 0, 934) +('2019-03-04 19:40:00', 'JRD', 0, 935) ('2015-03-17 18:33:00', 'QLI', 1, 533) ('2018-03-17 12:00:00', 'GFIX', 3, 136) ('1996-11-07 13:39:40', 'GPRE', 4, 1) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 471a6d6fad..3b9d7cd713 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -1040,7 +1040,8 @@ Data source : @4', NULL, NULL) ('tom_chacha_key', NULL, 'SysFunction.cpp', NULL, 0, 930, NULL, 'Invalid key length @1, need 16 or 32', NULL, NULL); ('bad_repl_handle', NULL, 'jrd.cpp', NULL, 0, 931, NULL, 'invalid replicator handle', NULL, NULL); ('tra_snapshot_does_not_exist', NULL, 'tpc.cpp', NULL, 0, 932, NULL, 'Transaction''s base snapshot number does not exist', NULL, NULL); -('eds_input_prm_not_used', NULL, 'ExtDS.cpp', NULL, 0, 933, NULL, 'Input parameter ''@1'' is not used in SQL query text', NULL, NULL) +('eds_input_prm_not_used', NULL, 'ExtDS.cpp', NULL, 0, 933, NULL, 'Input parameter ''@1'' is not used in SQL query text', NULL, NULL); +('effective_user', NULL, NULL, NULL, 0, 934, NULL, 'Effective user is @1', NULL, NULL); -- QLI (NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL); (NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index 7b750130e4..5f4626aa2e 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -940,6 +940,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, '08', '003', 0, 931, 'bad_repl_handle', NULL, NULL) (-901, '0B', '000', 0, 932, 'tra_snapshot_does_not_exist', NULL, NULL) (-901, '42', '000', 0, 933, 'eds_input_prm_not_used', NULL, NULL) +(-551, '28', '000', 0, 934, 'effective_user', NULL, 'ERROR') -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)