From: <ale...@us...> - 2014-09-02 16:55:25
|
Revision: 60025 http://sourceforge.net/p/firebird/code/60025 Author: alexpeshkoff Date: 2014-09-02 16:55:14 +0000 (Tue, 02 Sep 2014) Log Message: ----------- Implemented CORE-4538: Access rights for CREATE DATABASE operator Modified Paths: -------------- firebird/trunk/doc/sql.extensions/README.ddl_access.txt firebird/trunk/lang_helpers/gds_codes.ftn firebird/trunk/lang_helpers/gds_codes.pas firebird/trunk/src/dsql/DdlNodes.epp firebird/trunk/src/dsql/DdlNodes.h firebird/trunk/src/dsql/parse.y firebird/trunk/src/include/gen/codetext.h firebird/trunk/src/include/gen/iberror.h firebird/trunk/src/include/gen/ids.h firebird/trunk/src/include/gen/msgs.h firebird/trunk/src/include/gen/sql_code.h firebird/trunk/src/include/gen/sql_state.h firebird/trunk/src/isql/show.epp firebird/trunk/src/jrd/jrd.cpp firebird/trunk/src/jrd/names.h firebird/trunk/src/jrd/opt.cpp firebird/trunk/src/jrd/relations.h firebird/trunk/src/jrd/tra.cpp firebird/trunk/src/jrd/tra.h firebird/trunk/src/msgs/facilities2.sql firebird/trunk/src/msgs/messages2.sql firebird/trunk/src/msgs/system_errors2.sql Added Paths: ----------- firebird/trunk/src/jrd/DbCreators.cpp firebird/trunk/src/jrd/DbCreators.h Modified: firebird/trunk/doc/sql.extensions/README.ddl_access.txt =================================================================== --- firebird/trunk/doc/sql.extensions/README.ddl_access.txt 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/doc/sql.extensions/README.ddl_access.txt 2014-09-02 16:55:14 UTC (rev 60025) @@ -16,12 +16,12 @@ REVOKE [GRANT OPTION FOR] DROP ANY <OBJECT> FROM [USER | ROLE] <user/role name>; Where <OBJECT> could be: -TABLE, VIEW, PROCEDURE, FUNCTION, PACKAGE, GENERATOR, SEQUENCE, DOMAIN, -EXCEPTION, ROLE, DATABASE, CHARACTER SET, COLLATION, FILTER +TABLE, VIEW, PROCEDURE, FUNCTION, PACKAGE, GENERATOR, SEQUENCE, DOMAIN, +EXCEPTION, ROLE, CHARACTER SET, COLLATION, FILTER Description: -Makes it possible to grant and revoke privileges on DDL operations. +Makes it possible to grant and revoke privileges on DDL operations. DDL operations for managing triggers and indices re-use table privileges. @@ -31,5 +31,15 @@ Sample: GRANT CREATE TABLE TO Joe; -GRANT ALTER ANY TABLE TO Joe; +GRANT ALTER ANY TABLE TO Joe; REVOKE CREATE TABLE FROM Joe; + +For database access special form is supported: + +GRANT CREATE DATABASE TO [USER | ROLE] <user/role name>; +GRANT ALTER DATABASE TO [USER | ROLE] <user/role name> [WITH GRANT OPTION]; +GRANT DROP DATABASE TO [USER | ROLE] <user/role name> [WITH GRANT OPTION]; + +REVOKE CREATE DATABASE FROM [USER | ROLE] <user/role name>; +REVOKE [GRANT OPTION FOR] ALTER DATABASE FROM [USER | ROLE] <user/role name>; +REVOKE [GRANT OPTION FOR] DROP DATABASE FROM [USER | ROLE] <user/role name>; Modified: firebird/trunk/lang_helpers/gds_codes.ftn =================================================================== --- firebird/trunk/lang_helpers/gds_codes.ftn 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/lang_helpers/gds_codes.ftn 2014-09-02 16:55:14 UTC (rev 60025) @@ -1604,6 +1604,12 @@ PARAMETER (GDS__dsql_cant_grant_option = 335545095) INTEGER*4 GDS__read_conflict PARAMETER (GDS__read_conflict = 335545096) + INTEGER*4 GDS__crdb_load + PARAMETER (GDS__crdb_load = 335545097) + INTEGER*4 GDS__crdb_nodb + PARAMETER (GDS__crdb_nodb = 335545098) + INTEGER*4 GDS__crdb_notable + PARAMETER (GDS__crdb_notable = 335545099) INTEGER*4 GDS__gfix_db_name PARAMETER (GDS__gfix_db_name = 335740929) INTEGER*4 GDS__gfix_invalid_sw Modified: firebird/trunk/lang_helpers/gds_codes.pas =================================================================== --- firebird/trunk/lang_helpers/gds_codes.pas 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/lang_helpers/gds_codes.pas 2014-09-02 16:55:14 UTC (rev 60025) @@ -809,6 +809,9 @@ gds_dyn_no_priv = 335545094; gds_dsql_cant_grant_option = 335545095; gds_read_conflict = 335545096; + gds_crdb_load = 335545097; + gds_crdb_nodb = 335545098; + gds_crdb_notable = 335545099; gds_gfix_db_name = 335740929; gds_gfix_invalid_sw = 335740930; gds_gfix_incmp_sw = 335740932; Modified: firebird/trunk/src/dsql/DdlNodes.epp =================================================================== --- firebird/trunk/src/dsql/DdlNodes.epp 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/dsql/DdlNodes.epp 2014-09-02 16:55:14 UTC (rev 60025) @@ -115,7 +115,61 @@ //---------------------- +void ExecInSecurityDb::executeInSecurityDb(jrd_tra* localTransaction) +{ + LocalStatus st; + SecDbContext* secDbContext = localTransaction->getSecDbContext(); + if (!secDbContext) + { + Attachment* lAtt = localTransaction->getAttachment(); + const char* secDb = lAtt->att_database->dbb_config->getSecurityDatabase(); + ClumpletWriter dpb(ClumpletWriter::WideTagged, MAX_DPB_SIZE, isc_dpb_version2); + if (lAtt->att_user) + lAtt->att_user->populateDpb(dpb); + IAttachment* att = DispatcherPtr()->attachDatabase(&st, secDb, + dpb.getBufferLength(), dpb.getBuffer()); + check(&st); + + ITransaction* tra = att->startTransaction(&st, 0, NULL); + check(&st); + + secDbContext = localTransaction->setSecDbContext(att, tra); + } + + // run all statements under savepoint control + string savePoint; + savePoint.printf("ExecInSecurityDb%d", secDbContext->savePoint++); + secDbContext->att->execute(&st, secDbContext->tra, 0, ("SAVEPOINT " + savePoint).c_str(), + SQL_DIALECT_V6, NULL, NULL, NULL, NULL); + check(&st); + + try + { + runInSecurityDb(secDbContext); + + secDbContext->att->execute(&st, secDbContext->tra, 0, ("RELEASE SAVEPOINT " + savePoint).c_str(), + SQL_DIALECT_V6, NULL, NULL, NULL, NULL); + savePoint.erase(); + check(&st); + } + catch (const Exception&) + { + if (savePoint.hasData()) + { + LocalStatus tmp; + secDbContext->att->execute(&tmp, secDbContext->tra, 0, ("ROLLBACK TO SAVEPOINT " + savePoint).c_str(), + SQL_DIALECT_V6, NULL, NULL, NULL, NULL); + } + + throw; + } +} + + +//---------------------- + + // Check temporary table reference rules between given child relation and master // relation (owner of given PK/UK index). static void checkForeignKeyTempScope(thread_db* tdbb, jrd_tra* transaction, @@ -9662,243 +9716,213 @@ return false; } -// It's purpose is to add/drop mapping from any security name to DB security object. -void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +void MappingNode::runInSecurityDb(SecDbContext* secDbContext) { - if (!(tdbb->getAttachment() && tdbb->getAttachment()->locksmith())) - status_exception::raise(Arg::Gds(isc_adm_task_denied)); + Firebird::LocalStatus st; + IStatus* s = &st; - if (global) + // first of all try to use regenerated DDL statement + // that's the best way if security database is FB3 or higher fb version + string ddl; + + switch(op) { - LocalStatus st; - LocalStatus s2; // we will use it in DDL case and remember - IStatus* s = &st; + case MAP_ADD: + ddl = "CREATE MAPPING "; + break; + case MAP_MOD: + ddl = "ALTER MAPPING "; + break; + case MAP_DROP: + ddl = "DROP MAPPING "; + break; + case MAP_RPL: + ddl = "CREATE OR ALTER MAPPING "; + break; + } - SecDbContext* secDbContext = transaction->getSecDbContext(); - if (!secDbContext) + addItem(ddl, name.c_str()); + if (op != MAP_DROP) + { + ddl += " USING "; + switch (mode) { - const char* secDb = tdbb->getDatabase()->dbb_config->getSecurityDatabase(); - ClumpletWriter dpb(ClumpletWriter::WideTagged, MAX_DPB_SIZE, isc_dpb_version2); - if (tdbb->getAttachment()->att_user) - tdbb->getAttachment()->att_user->populateDpb(dpb); - IAttachment* att = DispatcherPtr()->attachDatabase(s, secDb, - dpb.getBufferLength(), dpb.getBuffer()); - check(s); + case 'P': + if (!plugin) + ddl += "ANY PLUGIN "; + else + { + ddl += "PLUGIN "; + addItem(ddl, plugin->c_str()); + ddl += ' '; + } + break; + case 'S': + ddl += "ANY PLUGIN SERVERWIDE "; + break; + case '*': + ddl += "* "; + break; + case 'M': + ddl += "MAPPING "; + break; + } - ITransaction* tra = att->startTransaction(s, 0, NULL); - check(s); + if (db) + { + ddl += "IN "; + addItem(ddl, db->c_str()); + ddl += ' '; + } - secDbContext = transaction->setSecDbContext(att, tra); + if (fromType) + { + ddl += "FROM "; + if (!from) + ddl += "ANY "; + addItem(ddl, fromType->c_str()); + ddl += ' '; + if (from) + { + addItem(ddl, from->getString().c_str()); + ddl += ' '; + } } - // run all statements under savepoint control - string savePoint; - savePoint.printf("GLOBALMAP%d", secDbContext->savePoint++); - secDbContext->att->execute(s, secDbContext->tra, 0, ("SAVEPOINT " + savePoint).c_str(), - SQL_DIALECT_V6, NULL, NULL, NULL, NULL); - check(s); + ddl += "TO "; + ddl += (role ? "ROLE" : "USER"); + if (to) + { + ddl += ' '; + addItem(ddl, to->c_str()); + } + } + // Now try to run DDL + secDbContext->att->execute(s, secDbContext->tra, 0, ddl.c_str(), SQL_DIALECT_V6, + NULL, NULL, NULL, NULL); + + if (s->getStatus() & IStatus::FB_HAS_ERRORS) + { try { - // first of all try to use regenerated DDL statement - // that's the best way if security database is FB3 or higher fb version - string ddl; + // try direct access to rdb$auth_mapping table in secure db + LocalStatus s2; + s = &s2; + // check presence of such record in the table + Message msgCheck; + Field<Varying> nm(msgCheck, 1); + nm = name.c_str(); + + Message result; + Field<ISC_INT64> cnt(result); + + const char* checkSql = "select count(*) from RDB$AUTH_MAPPING where RDB$MAP_NAME = ?"; + + secDbContext->att->execute(s, secDbContext->tra, 0, checkSql, SQL_DIALECT_V6, + msgCheck.getMetadata(), msgCheck.getBuffer(), result.getMetadata(), result.getBuffer()); + check(s); + + if (cnt > 1 && op != MAP_DROP) + ERRD_bugcheck("Database mapping misconfigured"); + + bool hasLine = cnt > 0; switch(op) { case MAP_ADD: - ddl = "CREATE MAPPING "; + if (hasLine) + (Arg::Gds(isc_map_already_exists) << name).raise(); break; + case MAP_MOD: - ddl = "ALTER MAPPING "; - break; case MAP_DROP: - ddl = "DROP MAPPING "; + if (!hasLine) + (Arg::Gds(isc_map_not_exists) << name).raise(); break; + case MAP_RPL: - ddl = "CREATE OR ALTER MAPPING "; + op = hasLine ? MAP_MOD : MAP_DROP; break; } - addItem(ddl, name.c_str()); - if (op != MAP_DROP) - { - ddl += " USING "; - switch (mode) - { - case 'P': - if (!plugin) - ddl += "ANY PLUGIN "; - else - { - ddl += "PLUGIN "; - addItem(ddl, plugin->c_str()); - ddl += ' '; - } - break; - case 'S': - ddl += "ANY PLUGIN SERVERWIDE "; - break; - case '*': - ddl += "* "; - break; - case 'M': - ddl += "MAPPING "; - break; - } + // Get ready to modify table + Message full; + Field<ISC_SHORT> toType(full); + Field<Varying> t(full, MAX_SQL_IDENTIFIER_LEN); + Field<Varying> usng2(full, 1); + Field<Varying> plug2(full, MAX_SQL_IDENTIFIER_LEN); + Field<Varying> d2(full, MAX_SQL_IDENTIFIER_LEN); + Field<Varying> type2(full, MAX_SQL_IDENTIFIER_LEN); + Field<Varying> f2(full, 255); + Field<Varying> nm2(full, MAX_SQL_IDENTIFIER_LEN); - if (db) - { - ddl += "IN "; - addItem(ddl, db->c_str()); - ddl += ' '; - } + toType = role ? 1 : 0; + if (to) + t = to->c_str(); + usng2.set(1, &mode); + if (plugin) + plug2 = plugin->c_str(); + if (db) + d2 = db->c_str(); + if (fromType) + type2 = fromType->c_str(); + if (from) + f2 = from->getString().c_str(); + nm2 = name.c_str(); - if (fromType) - { - ddl += "FROM "; - if (!from) - ddl += "ANY "; - addItem(ddl, fromType->c_str()); - ddl += ' '; - if (from) - { - addItem(ddl, from->getString().c_str()); - ddl += ' '; - } - } - - ddl += "TO "; - ddl += (role ? "ROLE" : "USER"); - if (to) - { - ddl += ' '; - addItem(ddl, to->c_str()); - } + Message* msg = NULL; + const char* sql = NULL; + switch(op) + { + case MAP_ADD: + sql = "insert into RDB$AUTH_MAPPING(RDB$MAP_TO_TYPE, RDB$MAP_TO, RDB$MAP_USING, " + "RDB$MAP_PLUGIN, RDB$MAP_DB, RDB$MAP_FROM_TYPE, RDB$MAP_FROM, RDB$MAP_NAME) " + "values (?, ?, ?, ?, ?, ?, ?, ?)"; + msg = &full; + break; + case MAP_MOD: + sql = "update RDB$AUTH_MAPPING set RDB$MAP_TO_TYPE = ?, RDB$MAP_TO = ?, " + "RDB$MAP_USING = ?, RDB$MAP_PLUGIN = ?, RDB$MAP_DB = ?, " + "RDB$MAP_FROM_TYPE = ?, RDB$MAP_FROM = ? " + "where RDB$MAP_NAME = ?"; + msg = &full; + break; + case MAP_DROP: + sql = "delete from RDBAUTH_MAPPING where RDB$MAP_NAME = ?"; + msg = &msgCheck; + break; } - // Now try to run DDL - secDbContext->att->execute(&s2, secDbContext->tra, 0, ddl.c_str(), SQL_DIALECT_V6, - NULL, NULL, NULL, NULL); - - if (s2.getStatus() & IStatus::FB_HAS_ERRORS) - { - // try direct access to rdb$auth_mapping table in secure db - - // check presence of such record in the table - Message msgCheck; - Field<Varying> nm(msgCheck, 1); - nm = name.c_str(); - - Message result; - Field<ISC_INT64> cnt(result); - - const char* checkSql = "select count(*) from RDB$AUTH_MAPPING where RDB$MAP_NAME = ?"; - - secDbContext->att->execute(s, secDbContext->tra, 0, checkSql, SQL_DIALECT_V6, - msgCheck.getMetadata(), msgCheck.getBuffer(), result.getMetadata(), result.getBuffer()); - check(s); - - if (cnt > 1 && op != MAP_DROP) - ERRD_bugcheck("Database mapping misconfigured"); - - bool hasLine = cnt > 0; - switch(op) - { - case MAP_ADD: - if (hasLine) - (Arg::Gds(isc_map_already_exists) << name).raise(); - break; - - case MAP_MOD: - case MAP_DROP: - if (!hasLine) - (Arg::Gds(isc_map_not_exists) << name).raise(); - break; - - case MAP_RPL: - op = hasLine ? MAP_MOD : MAP_DROP; - break; - } - - // Get ready to modify table - Message full; - Field<ISC_SHORT> toType(full); - Field<Varying> t(full, MAX_SQL_IDENTIFIER_LEN); - Field<Varying> usng2(full, 1); - Field<Varying> plug2(full, MAX_SQL_IDENTIFIER_LEN); - Field<Varying> d2(full, MAX_SQL_IDENTIFIER_LEN); - Field<Varying> type2(full, MAX_SQL_IDENTIFIER_LEN); - Field<Varying> f2(full, 255); - Field<Varying> nm2(full, MAX_SQL_IDENTIFIER_LEN); - - toType = role ? 1 : 0; - if (to) - t = to->c_str(); - usng2.set(1, &mode); - if (plugin) - plug2 = plugin->c_str(); - if (db) - d2 = db->c_str(); - if (fromType) - type2 = fromType->c_str(); - if (from) - f2 = from->getString().c_str(); - nm2 = name.c_str(); - - Message* msg = NULL; - const char* sql = NULL; - switch(op) - { - case MAP_ADD: - sql = "insert into RDB$AUTH_MAPPING(RDB$MAP_TO_TYPE, RDB$MAP_TO, RDB$MAP_USING, " - "RDB$MAP_PLUGIN, RDB$MAP_DB, RDB$MAP_FROM_TYPE, RDB$MAP_FROM, RDB$MAP_NAME) " - "values (?, ?, ?, ?, ?, ?, ?, ?)"; - msg = &full; - break; - case MAP_MOD: - sql = "update RDB$AUTH_MAPPING set RDB$MAP_TO_TYPE = ?, RDB$MAP_TO = ?, " - "RDB$MAP_USING = ?, RDB$MAP_PLUGIN = ?, RDB$MAP_DB = ?, " - "RDB$MAP_FROM_TYPE = ?, RDB$MAP_FROM = ? " - "where RDB$MAP_NAME = ?"; - msg = &full; - break; - case MAP_DROP: - sql = "delete from RDBAUTH_MAPPING where RDB$MAP_NAME = ?"; - msg = &msgCheck; - break; - } - - // Actual modification - fb_assert(sql && msg); - secDbContext->att->execute(s, secDbContext->tra, 0, sql, SQL_DIALECT_V6, - msg->getMetadata(), msg->getBuffer(), NULL, NULL); - check(s); - - secDbContext->att->execute(s, secDbContext->tra, 0, ("RELEASE SAVEPOINT " + savePoint).c_str(), - SQL_DIALECT_V6, NULL, NULL, NULL, NULL); - savePoint.erase(); - check(s); - } + // Actual modification + fb_assert(sql && msg); + secDbContext->att->execute(s, secDbContext->tra, 0, sql, SQL_DIALECT_V6, + msg->getMetadata(), msg->getBuffer(), NULL, NULL); + check(s); } - catch (const Exception&) + catch(const Exception& ex) { - if (savePoint.hasData()) + if (st.getStatus() & IStatus::FB_HAS_ERRORS) { - secDbContext->att->execute(s, secDbContext->tra, 0, ("ROLLBACK TO SAVEPOINT " + savePoint).c_str(), - SQL_DIALECT_V6, NULL, NULL, NULL, NULL); - } - - if (s2.getStatus() & IStatus::FB_HAS_ERRORS) - { - const ISC_STATUS* stat2 = s2.getErrors(); + const ISC_STATUS* stat2 = st.getErrors(); if (stat2[1] != isc_dsql_token_unk_err) - status_exception::raise(&s2); + status_exception::raise(&st); } throw; } + } +} +// 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 (global) + { + executeInSecurityDb(transaction); return; } @@ -10319,6 +10343,8 @@ // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); + createDbJobs.clear(); + const GranteeClause* usersPtr; const GranteeClause* usersEnd; @@ -10356,9 +10382,83 @@ } } + if (createDbJobs.hasData()) + executeInSecurityDb(transaction); + savePoint.release(); // everything is ok } +void GrantRevokeNode::runInSecurityDb(SecDbContext* secDbContext) +{ + for (unsigned n = 0; n < createDbJobs.getCount(); ++n) + { + CreateDbJob& j = createDbJobs[n]; + LocalStatus st; + IStatus* s = &st; + + Message gr; + Field<ISC_SHORT> uType(gr); + Field<Varying> u(gr, MAX_SQL_IDENTIFIER_LEN); + uType = j.userType; + u = j.user.c_str(); + + Message result; + Field<ISC_INT64> cnt(result); + + const char* checkSql = "select count(*) from RDB$DB_CREATORS where RDB$USER_TYPE = ? and RDB$USER = ?"; + secDbContext->att->execute(s, secDbContext->tra, 0, checkSql, SQL_DIALECT_V6, + gr.getMetadata(), gr.getBuffer(), result.getMetadata(), result.getBuffer()); + check(s); + + if (isGrant) + { + if (!cnt) + { + const char* insertSql = "insert into RDB$DB_CREATORS(RDB$USER_TYPE, RDB$USER) values(?, ?)"; + secDbContext->att->execute(s, secDbContext->tra, 0, insertSql, SQL_DIALECT_V6, + gr.getMetadata(), gr.getBuffer(), NULL, NULL); + check(s); + } + } + else + { + if (cnt) + { + const char* deleteSql = "delete from RDB$DB_CREATORS where RDB$USER_TYPE = ? and RDB$USER = ?"; + secDbContext->att->execute(s, secDbContext->tra, 0, deleteSql, SQL_DIALECT_V6, + gr.getMetadata(), gr.getBuffer(), NULL, NULL); + + j.grantErased = true; + } + + if (!j.grantErased) + { + if (j.allOnAll) + { + const char* all = "ALL"; + + if (j.badGrantor) + { + // msg 246: @1 is not grantor of @2 on @3 to @4. + (Arg::PrivateDyn(246) << j.revoker.c_str() << all << all << j.user).raise(); + } + + // msg 247: Warning: @1 on @2 is not granted to @3. + ERR_post_warning( + Arg::Warning(isc_dyn_miss_priv_warning) << + all << all << j.user); + } + else + { + // msg 247: Warning: @1 on @2 is not granted to @3. + ERR_post_warning(Arg::Warning(isc_dyn_miss_priv_warning) << + privilegeName('C') << "DATABASE" << j.user); + } + } + } + } +} + void GrantRevokeNode::modifyPrivileges(thread_db* tdbb, jrd_tra* transaction, SSHORT option, const GranteeClause* user) { @@ -10467,8 +10567,9 @@ if (!isGrant && !privs) // REVOKE ALL ON ALL { AutoCacheRequest request(tdbb, drq_e_grant3, DYN_REQUESTS); - bool grantErased = false; - bool badGrantor = false; + CreateDbJob all(userType, user); + all.allOnAll = true; + all.revoker = grantorRevoker; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) PRIV IN RDB$USER_PRIVILEGES @@ -10478,30 +10579,15 @@ if (tdbb->getAttachment()->att_user->locksmith() || grantorRevoker == PRIV.RDB$GRANTOR) { ERASE PRIV; - grantErased = true; + all.grantErased = true; } else - badGrantor = true; + all.badGrantor = true; } END_FOR - const char* all = "ALL"; + createDbJobs.push(all); - if (badGrantor && !grantErased) - { - // msg 246: @1 is not grantor of @2 on @3 to @4. - status_exception::raise(Arg::PrivateDyn(246) << - grantorRevoker.c_str() << all << all << user.c_str()); - } - - if (!grantErased) - { - // msg 247: Warning: @1 on @2 is not granted to @3. - ERR_post_warning( - Arg::Warning(isc_dyn_miss_priv_warning) << - all << all << Arg::Str(user)); - } - return; } @@ -10527,6 +10613,30 @@ } } + if (objType == obj_database && strchr(privileges, 'C')) + { + if (options || grantor) + { + (Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) << + "GRANT/ADMIN OPTION and GRANTED BY not supported for CREATE DATABASE grants").raise(); + } + + if (userType != obj_sql_role && userType != obj_user) + { + (Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) << + "Only grants to USER or ROLE are supported for CREATE DATABASE").raise(); + } + + CreateDbJob job(userType, user); + createDbJobs.push(job); + + char* cPtr = strchr(privileges, 'C'); + size_t len = strlen(cPtr); + memmove(cPtr, cPtr + 1, len); + if (!privileges[0]) + return; + } + char priv[2]; priv[1] = '\0'; Modified: firebird/trunk/src/dsql/DdlNodes.h =================================================================== --- firebird/trunk/src/dsql/DdlNodes.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/dsql/DdlNodes.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -41,8 +41,8 @@ class CompoundStmtNode; class RelationSourceNode; class ValueListNode; +class SecDbContext; - struct BoolSourceClause { explicit BoolSourceClause(MemoryPool& p) @@ -135,6 +135,18 @@ }; +class ExecInSecurityDb +{ +public: + virtual ~ExecInSecurityDb() { } + + void executeInSecurityDb(jrd_tra* tra); + +protected: + virtual void runInSecurityDb(SecDbContext* secDbContext) = 0; +}; + + template <typename CreateNode, typename DropNode, ISC_STATUS ERROR_CODE> class RecreateNode : public DdlNode { @@ -1802,7 +1814,7 @@ }; -class MappingNode : public DdlNode +class MappingNode : public DdlNode, private ExecInSecurityDb { public: enum OP {MAP_ADD, MAP_MOD, MAP_RPL, MAP_DROP}; @@ -1836,6 +1848,7 @@ (op == MAP_ADD ? "CREATE" : op == MAP_MOD ? "ALTER" : op == MAP_RPL ? "CREATE OR ALTER" : "DROP"); } + void runInSecurityDb(SecDbContext* secDbContext); private: void addItem(Firebird::string& ddl, const char* text); @@ -1974,11 +1987,12 @@ typedef Firebird::Pair<Firebird::NonPooled<char, ValueListNode*> > PrivilegeClause; typedef Firebird::Pair<Firebird::NonPooled<SSHORT, Firebird::MetaName> > GranteeClause; -class GrantRevokeNode : public DdlNode +class GrantRevokeNode : public DdlNode, private ExecInSecurityDb { public: GrantRevokeNode(MemoryPool& p, bool aIsGrant) : DdlNode(p), + createDbJobs(p), isGrant(aIsGrant), privileges(p), roles(p), @@ -2001,6 +2015,7 @@ statusVector << Firebird::Arg::Gds(isGrant ? isc_dsql_grant_failed : isc_dsql_revoke_failed); } + void runInSecurityDb(SecDbContext* secDbContext); private: void modifyPrivileges(thread_db* tdbb, jrd_tra* transaction, SSHORT option, const GranteeClause* user); @@ -2043,6 +2058,19 @@ return "<Unknown>"; } + struct CreateDbJob + { + CreateDbJob(SSHORT a_userType, const Firebird::MetaName& a_user) + : allOnAll(false), grantErased(false), badGrantor(false), + userType(a_userType), user(a_user) + { } + + bool allOnAll, grantErased, badGrantor; + SSHORT userType; + Firebird::MetaName user, revoker; + }; + Firebird::Array<CreateDbJob> createDbJobs; + public: bool isGrant; Firebird::Array<PrivilegeClause> privileges; Modified: firebird/trunk/src/dsql/parse.y =================================================================== --- firebird/trunk/src/dsql/parse.y 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/dsql/parse.y 2014-09-02 16:55:14 UTC (rev 60025) @@ -976,6 +976,7 @@ %type db_ddl_privilege(<privilegeArray>) db_ddl_privilege($privilegeArray) + | CREATE { $privilegeArray->add(PrivilegeClause('C', NULL)); } | ALTER { $privilegeArray->add(PrivilegeClause('L', NULL)); } | DROP { $privilegeArray->add(PrivilegeClause('O', NULL)); } ; Modified: firebird/trunk/src/include/gen/codetext.h =================================================================== --- firebird/trunk/src/include/gen/codetext.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/include/gen/codetext.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -798,6 +798,9 @@ {"dyn_no_priv", 335545094}, {"dsql_cant_grant_option", 335545095}, {"read_conflict", 335545096}, + {"crdb_load", 335545097}, + {"crdb_nodb", 335545098}, + {"crdb_notable", 335545099}, {"gfix_db_name", 335740929}, {"gfix_invalid_sw", 335740930}, {"gfix_incmp_sw", 335740932}, Modified: firebird/trunk/src/include/gen/iberror.h =================================================================== --- firebird/trunk/src/include/gen/iberror.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/include/gen/iberror.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -832,6 +832,9 @@ const ISC_STATUS isc_dyn_no_priv = 335545094L; const ISC_STATUS isc_dsql_cant_grant_option = 335545095L; const ISC_STATUS isc_read_conflict = 335545096L; +const ISC_STATUS isc_crdb_load = 335545097L; +const ISC_STATUS isc_crdb_nodb = 335545098L; +const ISC_STATUS isc_crdb_notable = 335545099L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1290,7 +1293,7 @@ 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 = 1234; +const ISC_STATUS isc_err_max = 1237; #else /* c definitions */ @@ -2092,6 +2095,9 @@ #define isc_dyn_no_priv 335545094L #define isc_dsql_cant_grant_option 335545095L #define isc_read_conflict 335545096L +#define isc_crdb_load 335545097L +#define isc_crdb_nodb 335545098L +#define isc_crdb_notable 335545099L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2550,7 +2556,7 @@ #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 1234 +#define isc_err_max 1237 #endif Modified: firebird/trunk/src/include/gen/ids.h =================================================================== --- firebird/trunk/src/include/gen/ids.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/include/gen/ids.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -660,3 +660,15 @@ const USHORT f_sec_map_to = 7; +// Relation 47 (RDB$DB_CREATORS) + + const USHORT f_crt_user = 0; + const USHORT f_crt_u_type = 1; + + +// Relation 48 (SEC$DB_CREATORS) + + const USHORT f_sec_crt_user = 0; + const USHORT f_sec_crt_u_type = 1; + + Modified: firebird/trunk/src/include/gen/msgs.h =================================================================== --- firebird/trunk/src/include/gen/msgs.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/include/gen/msgs.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -801,6 +801,9 @@ {335545094, "There is no privilege for this operation"}, /* dyn_no_priv */ {335545095, "Using GRANT OPTION on @1 not allowed"}, /* dsql_cant_grant_option */ {335545096, "read conflicts with concurrent update"}, /* read_conflict */ + {335545097, "@1 failed when working with CREATE DATABASE grants"}, /* crdb_load */ + {335545098, "CREATE DATABASE grants check is not possible when database @1 is not present"}, /* crdb_nodb */ + {335545099, "CREATE DATABASE grants check is not possible when table RDB$DB_CREATORS is not present in database @1"}, /* crdb_notable */ {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 */ Modified: firebird/trunk/src/include/gen/sql_code.h =================================================================== --- firebird/trunk/src/include/gen/sql_code.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/include/gen/sql_code.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -797,6 +797,9 @@ {335545094, -901}, /* 774 dyn_no_priv */ {335545095, -901}, /* 775 dsql_cant_grant_option */ {335545096, -904}, /* 776 read_conflict */ + {335545097, -901}, /* 777 crdb_load */ + {335545098, -901}, /* 778 crdb_nodb */ + {335545099, -901}, /* 779 crdb_notable */ {335740929, -901}, /* 1 gfix_db_name */ {335740930, -901}, /* 2 gfix_invalid_sw */ {335740932, -901}, /* 4 gfix_incmp_sw */ Modified: firebird/trunk/src/include/gen/sql_state.h =================================================================== --- firebird/trunk/src/include/gen/sql_state.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/include/gen/sql_state.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -797,6 +797,9 @@ {335545094, "42000"}, // 774 dyn_no_priv {335545095, "42000"}, // 775 dsql_cant_grant_option {335545096, "40001"}, // 776 read_conflict + {335545097, "08004"}, // 777 crdb_load + {335545098, "0A000"}, // 778 crdb_nodb + {335545099, "0A000"}, // 779 crdb_notable {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw Modified: firebird/trunk/src/isql/show.epp =================================================================== --- firebird/trunk/src/isql/show.epp 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/isql/show.epp 2014-09-02 16:55:14 UTC (rev 60025) @@ -1452,6 +1452,44 @@ return ps_ERR; END_ERROR + if (obj_type == obj_database || obj_type == 255) + { + FOR PRV IN SEC$DB_CREATORS + SORTED BY PRV.SEC$USER_TYPE, PRV.SEC$USER + + if (first && optional_msg) + isqlGlob.prints(optional_msg); + + first = false; + fb_utils::exact_name(PRV.SEC$USER); + + switch (PRV.SEC$USER_TYPE) + { + case obj_sql_role: + case obj_user: + if (mangle && isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION) + IUTILS_copy_SQL_id(PRV.SEC$USER, SQL_identifier, DBL_QUOTE); + else + strcpy(SQL_identifier, PRV.SEC$USER); + break; + default: + fb_assert(false); + strcpy(SQL_identifier, PRV.SEC$USER); + break; + } + + set_grantee(PRV.SEC$USER_TYPE, SQL_identifier, user_string); + + isqlGlob.printf("GRANT CREATE DATABASE TO %s%s%s", + user_string, terminator, NEWLINE); + + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR + } + if (!first) return (SKIP); } Added: firebird/trunk/src/jrd/DbCreators.cpp =================================================================== --- firebird/trunk/src/jrd/DbCreators.cpp (rev 0) +++ firebird/trunk/src/jrd/DbCreators.cpp 2014-09-02 16:55:14 UTC (rev 60025) @@ -0,0 +1,249 @@ +/* + * PROGRAM: JRD access method + * MODULE: DbCreators.cpp + * DESCRIPTION: Checks CREATE DATABASE right (in security.db) + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alex Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2014 Alex Peshkov <peshkoff at mail.ru> + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#include "firebird.h" +#include "firebird/Provider.h" +#include "../auth/SecureRemotePassword/Message.h" +#include "gen/iberror.h" + +#include "../jrd/constants.h" +#include "../common/classes/ClumpletWriter.h" +#include "../common/classes/init.h" +#include "../common/classes/Hash.h" +#include "../common/classes/GenericMap.h" +#include "../common/classes/RefMutex.h" +#include "../common/classes/SyncObject.h" +#include "../common/classes/MetaName.h" +#include "../common/isc_s_proto.h" +#include "../common/isc_proto.h" +#include "../common/ThreadStart.h" +#include "../common/db_alias.h" + +#include "../jrd/DbCreators.h" +#include "../jrd/tra.h" +#include "../jrd/ini.h" +#include "gen/ids.h" + +#define DBC_DEBUG(A) + +using namespace Firebird; + +namespace { + +void check(const char* s, IStatus* st) +{ + if (!(st->getStatus() & IStatus::FB_HAS_ERRORS)) + return; + + Arg::StatusVector newStatus(st); + newStatus << Arg::Gds(isc_crdb_load) << s; + newStatus.raise(); +} + +bool openDb(const char* securityDb, RefPtr<IAttachment>& att, RefPtr<ITransaction>& 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); + + LocalStatus st; + att = prov->attachDatabase(&st, securityDb, + embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer()); + if (st.getStatus() & IStatus::FB_HAS_ERRORS) + { + if (!fb_utils::containsErrorCode(st.getErrors(), isc_io_error)) + check("IProvider::attachDatabase", &st); + + // missing security DB - checking granted rights not possible + return false; + } + + ClumpletWriter readOnly(ClumpletWriter::Tpb, MAX_DPB_SIZE, isc_tpb_version1); + readOnly.insertTag(isc_tpb_read); + readOnly.insertTag(isc_tpb_wait); + tra = att->startTransaction(&st, readOnly.getBufferLength(), readOnly.getBuffer()); + check("IAttachment::startTransaction", &st); + + return true; +} + +} // anonymous namespace + + +namespace Jrd { + +bool checkCreateDatabaseGrant(Firebird::string& userName, Firebird::string& trustedRole, + const char* securityDb) +{ + if (userName == SYSDBA_USER_NAME || trustedRole == ADMIN_ROLE) + return true; + + RefPtr<IAttachment> att; + RefPtr<ITransaction> tra; + if (!openDb(securityDb, att, tra)) + return false; + + Message gr; + Field<ISC_SHORT> uType(gr); + Field<Varying> u(gr, MAX_SQL_IDENTIFIER_LEN); + Field<ISC_SHORT> rType(gr); + Field<Varying> r(gr, MAX_SQL_IDENTIFIER_LEN); + uType = obj_user; + u = userName.c_str(); + rType = trustedRole.hasData() ? obj_sql_role : 255; + r = trustedRole.c_str(); + + Message result; + Field<ISC_INT64> cnt(result); + + LocalStatus st; + att->execute(&st, tra, 0, + "select count(*) from RDB$DB_CREATORS" + " where (RDB$USER_TYPE = ? and RDB$USER = ?) or (RDB$USER_TYPE = ? and RDB$USER = ?)", + SQL_DIALECT_V6, gr.getMetadata(), gr.getBuffer(), result.getMetadata(), result.getBuffer()); + if (st.getStatus() & IStatus::FB_HAS_ERRORS) + { + if (fb_utils::containsErrorCode(st.getErrors(), isc_dsql_relation_err)) + { + // isc_dsql_relation_err when exec SQL - i.e. table RDB$DB_CREATORS + // is missing due to non-FB3 security DB + return false; + } + check("IAttachment::execute", &st); + } + + return cnt > 0; +} + + +const Format* DbCreatorsScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +{ + jrd_tra* const transaction = tdbb->getTransaction(); + return transaction->getDbCreatorsList()->getList(tdbb, relation)->getFormat(); +} + +bool DbCreatorsScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, + FB_UINT64 position, Record* record) const +{ + jrd_tra* const transaction = tdbb->getTransaction(); + return transaction->getDbCreatorsList()->getList(tdbb, relation)->fetch(position, record); +} + +DbCreatorsList::DbCreatorsList(jrd_tra* tra) + : SnapshotData(*tra->tra_pool) +{ } + +RecordBuffer* DbCreatorsList::makeBuffer(thread_db* tdbb) +{ + MemoryPool* const pool = tdbb->getTransaction()->tra_pool; + allocBuffer(tdbb, *pool, rel_sec_db_creators); + return getData(rel_sec_db_creators); +} + +RecordBuffer* DbCreatorsList::getList(thread_db* tdbb, jrd_rel* relation) +{ + fb_assert(relation); + fb_assert(relation->rel_id == rel_sec_db_creators); + + RecordBuffer* buffer = getData(relation); + if (buffer) + { + return buffer; + } + + RefPtr<IAttachment> att; + RefPtr<ITransaction> tra; + const char* dbName = tdbb->getDatabase()->dbb_config->getSecurityDatabase(); + if (!openDb(dbName, att, tra)) + { + // In embedded mode we are not raising any errors - silent return + if (MasterInterfacePtr()->serverMode(-1) < 0) + return makeBuffer(tdbb); + + (Arg::Gds(isc_crdb_nodb) << dbName).raise(); + } + + Message gr; + Field<ISC_SHORT> uType(gr); + Field<Varying> u(gr, MAX_SQL_IDENTIFIER_LEN); + + LocalStatus st; + RefPtr<IResultSet> curs(att->openCursor(&st, tra, 0, + "select RDB$USER_TYPE, RDB$USER from RDB$DB_CREATORS", + SQL_DIALECT_V6, NULL, NULL, gr.getMetadata(), NULL)); + + if (st.getStatus() & IStatus::FB_HAS_ERRORS) + { + if (!fb_utils::containsErrorCode(st.getErrors(), isc_dsql_relation_err)) + check("IAttachment::openCursor", &st); + + // isc_dsql_relation_err when opening cursor - i.e. table + // is missing due to non-FB3 security DB + + // In embedded mode we are not raising any errors - silent return + if (MasterInterfacePtr()->serverMode(-1) < 0) + return makeBuffer(tdbb); + + (Arg::Gds(isc_crdb_notable) << dbName).raise(); + } + + try + { + buffer = makeBuffer(tdbb); + while (curs->fetchNext(&st, gr.getBuffer()) == IStatus::FB_OK) + { + int charset = CS_METADATA; + Record* record = buffer->getTempRecord(); + record->nullify(); + + putField(tdbb, record, + DumpField(f_sec_crt_user, VALUE_STRING, u->len, u->data), + charset); + + SINT64 v = uType; + putField(tdbb, record, + DumpField(f_sec_crt_u_type, VALUE_INTEGER, sizeof(v), &v), + charset); + + buffer->store(record); + } + check("IResultSet::fetchNext", &st); + } + catch (const Exception&) + { + clearSnapshot(); + throw; + } + + return getData(relation); +} + +} // namespace Jrd Property changes on: firebird/trunk/src/jrd/DbCreators.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/x-c++src \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: firebird/trunk/src/jrd/DbCreators.h =================================================================== --- firebird/trunk/src/jrd/DbCreators.h (rev 0) +++ firebird/trunk/src/jrd/DbCreators.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -0,0 +1,69 @@ +/* + * PROGRAM: JRD access method + * MODULE: DbCreators.h + * DESCRIPTION: GRANT CREATE DATABASE right (in security.db) + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alex Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2014 Alex Peshkov <peshkoff at mail.ru> + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef JRD_DB_CREATORS +#define JRD_DB_CREATORS + +#include "../common/classes/alloc.h" +#include "../common/classes/fb_string.h" +#include "../jrd/recsrc/RecordSource.h" +#include "../jrd/Monitoring.h" + +namespace Jrd { + +bool checkCreateDatabaseGrant(Firebird::string& userName, Firebird::string& trustedRole, + const char* securityDb); + +class DbCreatorsScan: public VirtualTableScan +{ +public: + DbCreatorsScan(CompilerScratch* csb, const Firebird::string& alias, + StreamType stream, jrd_rel* relation) + : VirtualTableScan(csb, alias, stream, relation) + {} + +protected: + const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const; + bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const; +}; + +class DbCreatorsList : public SnapshotData +{ +public: + explicit DbCreatorsList(jrd_tra* tra); + + RecordBuffer* getList(thread_db* tdbb, jrd_rel* relation); + +private: + RecordBuffer* makeBuffer(thread_db* tdbb); +}; + +} // namespace Jrd + + +#endif // JRD_DB_CREATORS Property changes on: firebird/trunk/src/jrd/DbCreators.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/x-chdr \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Modified: firebird/trunk/src/jrd/jrd.cpp =================================================================== --- firebird/trunk/src/jrd/jrd.cpp 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/jrd/jrd.cpp 2014-09-02 16:55:14 UTC (rev 60025) @@ -126,6 +126,7 @@ #include "../jrd/DebugInterface.h" #include "../jrd/EngineInterface.h" #include "../jrd/CryptoManager.h" +#include "../jrd/DbCreators.h" #include "../dsql/dsql.h" #include "../dsql/dsql_proto.h" @@ -995,7 +996,7 @@ static void rollback(thread_db*, jrd_tra*, const bool); static void strip_quotes(string&); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); -static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*, const RefPtr<Config>*); +static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*, const RefPtr<Config>*, bool); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -1007,7 +1008,7 @@ m_filename(filename), m_options(options) { - getUserInfo(m_id, *m_options, m_filename, NULL, NULL); + getUserInfo(m_id, *m_options, m_filename, NULL, NULL, false); } @@ -1397,7 +1398,7 @@ } // Check for correct credentials supplied - getUserInfo(userId, options, org_filename.c_str(), expanded_name.c_str(), &config); + getUserInfo(userId, options, org_filename.c_str(), expanded_name.c_str(), &config, false); #ifdef WIN_NT guardDbInit.enter(); // Required to correctly expand name of just created database @@ -2409,7 +2410,7 @@ } // Check for correct credentials supplied - getUserInfo(userId, options, org_filename.c_str(), NULL, &config); + getUserInfo(userId, options, org_filename.c_str(), NULL, &config, true); #ifdef WIN_NT guardDbInit.enter(); // Required to correctly expand name of just created database @@ -7055,9 +7056,8 @@ getUserInfo - @brief Almost stub-like now. - Planned to take into an account mapping of users and groups. - Fills UserId structure with resulting values. + @brief Fills UserId structure with resulting values. + Takes into an account mapping of users and groups. @param user @param options @@ -7065,7 +7065,7 @@ **/ static void getUserInfo(UserId& user, const DatabaseOptions& options, - const char* aliasName, const char* dbName, const RefPtr<Config>* config) + const char* aliasName, const char* dbName, const RefPtr<Config>* config, bool creating) { bool wheel = false; int id = -1, group = -1; // CVC: This var contained trash @@ -7092,6 +7092,12 @@ aliasName, dbName, (config ? (*config)->getSecurityDatabase() : NULL)); ISC_systemToUtf8(name); ISC_systemToUtf8(trusted_role); + + if (creating && config) // when config is NULL we are in error handler + { + if (!checkCreateDatabaseGrant(name, trusted_role, (*config)->getSecurityDatabase())) + (Arg::Gds(isc_no_priv) << "CREATE" << "DATABASE" << aliasName).raise(); + } } else { Modified: firebird/trunk/src/jrd/names.h =================================================================== --- firebird/trunk/src/jrd/names.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/jrd/names.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -394,3 +394,8 @@ NAME("SEC$MAP_FROM", nam_sec_map_from) NAME("SEC$MAP_TO_TYPE", nam_sec_map_to_type) NAME("SEC$MAP_TO", nam_sec_map_to) + +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) Modified: firebird/trunk/src/jrd/opt.cpp =================================================================== --- firebird/trunk/src/jrd/opt.cpp 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/jrd/opt.cpp 2014-09-02 16:55:14 UTC (rev 60025) @@ -85,6 +85,7 @@ #include "../jrd/recsrc/RecordSource.h" #include "../jrd/recsrc/Cursor.h" #include "../jrd/Mapping.h" +#include "../jrd/DbCreators.h" #include "../jrd/Optimizer.h" #include "../dsql/BoolNodes.h" @@ -2264,17 +2265,24 @@ else if (relation->isVirtual()) { // Virtual table: monitoring or security - if (relation->rel_id == rel_global_auth_mapping) + switch(relation->rel_id) { + case rel_global_auth_mapping: rsb = FB_NEW(*tdbb->getDefaultPool()) GlobalMappingScan(csb, alias, stream, relation); - } - else if (relation->rel_id == rel_sec_users || relation->rel_id == rel_sec_user_attributes) - { + break; + + case rel_sec_users: + case rel_sec_user_attributes: rsb = FB_NEW(*tdbb->getDefaultPool()) UsersTableScan(csb, alias, stream, relation); - } - else - { + break; + + case rel_sec_db_creators: + rsb = FB_NEW(*tdbb->getDefaultPool()) DbCreatorsScan(csb, alias, stream, relation); + break; + + default: rsb = FB_NEW(*tdbb->getDefaultPool()) MonitoringTableScan(csb, alias, stream, relation); + break; } } else Modified: firebird/trunk/src/jrd/relations.h =================================================================== --- firebird/trunk/src/jrd/relations.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/jrd/relations.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -649,7 +649,7 @@ // Relation 46 (SEC$GLOBAL_AUTH_MAPPING) RELATION(nam_sec_global_auth_mapping, rel_global_auth_mapping, ODS_12_0, rel_virtual) - FIELD(f_sec_map_name, nam_sec_map_name, fld_map_name, 1, ODS_12_0) + FIELD(f_sec_map_name, nam_sec_map_name, fld_map_name, 0, ODS_12_0) FIELD(f_sec_map_using, nam_sec_map_using, fld_map_using, 0, ODS_12_0) FIELD(f_sec_map_plugin, nam_sec_map_plugin, fld_map_plugin, 0, ODS_12_0) FIELD(f_sec_map_db, nam_sec_map_db, fld_map_db, 0, ODS_12_0) @@ -658,3 +658,15 @@ FIELD(f_sec_map_to_type, nam_sec_map_to_type, fld_obj_type, 0, ODS_12_0) FIELD(f_sec_map_to, nam_sec_map_to, fld_map_to, 0, ODS_12_0) END_RELATION + +// Relation 47 (RDB$DB_CREATORS) +RELATION(nam_db_creators, rel_db_creators, ODS_12_0, rel_persistent) + FIELD(f_crt_user, nam_user, fld_user, 1, ODS_12_0) + FIELD(f_crt_u_type, nam_user_type, fld_obj_type, 1, ODS_12_0) +END_RELATION + +// Relation 48 (SEC$DB_CREATORS) +RELATION(nam_sec_db_creators, rel_sec_db_creators, ODS_12_0, rel_virtual) + FIELD(f_sec_crt_user, nam_sec_user, fld_user, 0, ODS_12_0) + FIELD(f_sec_crt_u_type, nam_sec_user_type, fld_obj_type, 0, ODS_12_0) +END_RELATION Modified: firebird/trunk/src/jrd/tra.cpp =================================================================== --- firebird/trunk/src/jrd/tra.cpp 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/jrd/tra.cpp 2014-09-02 16:55:14 UTC (rev 60025) @@ -73,6 +73,7 @@ #include "../jrd/Function.h" #include "../jrd/Collation.h" #include "../jrd/Mapping.h" +#include "../jrd/DbCreators.h" const int DYN_MSG_FAC = 8; @@ -3401,7 +3402,16 @@ return tra_mapping_list; } +DbCreatorsList* jrd_tra::getDbCreatorsList() +{ + if (!tra_dbcreators_list) + { + tra_dbcreators_list = FB_NEW(*tra_pool) DbCreatorsList(this); + } + return tra_dbcreators_list; +} + jrd_tra* jrd_tra::getOuter() { jrd_tra* tra = this; Modified: firebird/trunk/src/jrd/tra.h =================================================================== --- firebird/trunk/src/jrd/tra.h 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/jrd/tra.h 2014-09-02 16:55:14 UTC (rev 60025) @@ -71,6 +71,7 @@ class dsql_opn; class UserManagement; class MappingList; +class DbCreatorsList; class thread_db; class SecDbContext @@ -303,6 +304,7 @@ UserManagement* tra_user_management; SecDbContext* tra_sec_db_context; MappingList* tra_mapping_list; + DbCreatorsList* tra_dbcreators_list; MemoryPool* tra_autonomous_pool; USHORT tra_autonomous_cnt; static const USHORT TRA_AUTONOMOUS_PER_POOL = 64; @@ -359,6 +361,7 @@ SecDbContext* setSecDbContext(Firebird::IAttachment* att, Firebird::ITransaction* tra); void eraseSecDbContext(); MappingList* getMappingList(); + DbCreatorsList* getDbCreatorsList(); GenIdCache* getGenIdCache() { Modified: firebird/trunk/src/msgs/facilities2.sql =================================================================== --- firebird/trunk/src/msgs/facilities2.sql 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/msgs/facilities2.sql 2014-09-02 16:55:14 UTC (rev 60025) @@ -1,7 +1,7 @@ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); -- -('2014-08-27 16:38:00', 'JRD', 0, 777) +('2014-09-02 19:54:57', 'JRD', 0, 780) ('2012-01-23 20:10:30', 'QLI', 1, 532) ('2013-11-13 15:59:10', 'GFIX', 3, 122) ('1996-11-07 13:39:40', 'GPRE', 4, 1) Modified: firebird/trunk/src/msgs/messages2.sql =================================================================== --- firebird/trunk/src/msgs/messages2.sql 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/msgs/messages2.sql 2014-09-02 16:55:14 UTC (rev 60025) @@ -884,6 +884,9 @@ ('dyn_no_priv', NULL, 'scl.epp', NULL, 0, 774, NULL, 'There is no privilege for this operation', NULL, NULL); ('dsql_cant_grant_option', NULL, 'DdlNodes.epp', NULL, 0, 775, NULL, 'Using GRANT OPTION on @1 not allowed', NULL, NULL); ('read_conflict', NULL, NULL, 'vio.cpp', 0, 776, NULL, 'read conflicts with concurrent update', NULL, NULL); +('crdb_load', 'check', 'DbCreators.cpp', NULL, 0, 777, NULL, '@1 failed when working with CREATE DATABASE grants', NULL, NULL); +('crdb_nodb', 'DbCreatorsList::getList', 'DbCreators.cpp', NULL, 0, 778, NULL, 'CREATE DATABASE grants check is not possible when database @1 is not present', NULL, NULL); +('crdb_notable', 'DbCreatorsList::getList', 'DbCreators.cpp', NULL, 0, 779, NULL, 'CREATE DATABASE grants check is not possible when table RDB$DB_CREATORS is not present in database @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); Modified: firebird/trunk/src/msgs/system_errors2.sql =================================================================== --- firebird/trunk/src/msgs/system_errors2.sql 2014-09-02 06:29:44 UTC (rev 60024) +++ firebird/trunk/src/msgs/system_errors2.sql 2014-09-02 16:55:14 UTC (rev 60025) @@ -783,6 +783,9 @@ (-901, '42', '000', 0, 774, 'dyn_no_priv', NULL, NULL) (-901, '42', '000', 0, 775, 'dsql_cant_grant_option', NULL, NULL); (-904, '40', '001', 0, 776, 'read_conflict', NULL, NULL); +(-901, '08', '004', 0, 777, 'crdb_load', NULL, NULL); +(-901, '0A', '000', 0, 778, 'crdb_nodb', NULL, NULL); +(-901, '0A', '000', 0, 779, 'crdb_notable', NULL, NULL); -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |