|
From: <ale...@us...> - 2014-12-26 14:10:41
|
Revision: 60432
http://sourceforge.net/p/firebird/code/60432
Author: alexpeshkoff
Date: 2014-12-26 14:10:39 +0000 (Fri, 26 Dec 2014)
Log Message:
-----------
Fixed CORE-4651: CREATE DATABASE fails for the user having RDB$ADMIN rights in security database
Modified Paths:
--------------
firebird/trunk/doc/sql.extensions/README.ddl.txt
firebird/trunk/src/dsql/parse.y
firebird/trunk/src/isql/isql.epp
firebird/trunk/src/jrd/DbCreators.cpp
firebird/trunk/src/jrd/DbCreators.h
firebird/trunk/src/jrd/jrd.cpp
firebird/trunk/src/yvalve/preparse.cpp
Modified: firebird/trunk/doc/sql.extensions/README.ddl.txt
===================================================================
--- firebird/trunk/doc/sql.extensions/README.ddl.txt 2014-12-26 12:27:40 UTC (rev 60431)
+++ firebird/trunk/doc/sql.extensions/README.ddl.txt 2014-12-26 14:10:39 UTC (rev 60432)
@@ -462,3 +462,16 @@
Also, the name RDB$GENERATORS does not refer anymore to an invisible system
generator. The name can be applied to an user generator.
+
+
+18) Added ROLE clause to CREATE DATABASE statement.
+(Alex Peshkov)
+
+For 3.0, setting role when creating database may affect user rights to create databases.
+This can happen if user is granted (in appropriate security database) system role RDB$ADMIN
+or some ordinary role which in turn is granted CREATE DATABASE right. When using API call
+IProvider::createDatabase (or isc_create_database) there are no problems with placing
+isc_dpb_sql_role_name tag with required value into DPB, but CREATE DATABASE statement
+missed ROLE clause before 3.0.
+
+ISQL now also takes into an account global role setting when creating databases.
Modified: firebird/trunk/src/dsql/parse.y
===================================================================
--- firebird/trunk/src/dsql/parse.y 2014-12-26 12:27:40 UTC (rev 60431)
+++ firebird/trunk/src/dsql/parse.y 2014-12-26 14:10:39 UTC (rev 60432)
@@ -1841,6 +1841,7 @@
db_initial_option($alterDatabaseNode)
: KW_PAGE_SIZE equals pos_short_integer
| USER utf_string
+ | ROLE utf_string
| PASSWORD utf_string
| SET NAMES utf_string
| LENGTH equals long_integer page_noise
Modified: firebird/trunk/src/isql/isql.epp
===================================================================
--- firebird/trunk/src/isql/isql.epp 2014-12-26 12:27:40 UTC (rev 60431)
+++ firebird/trunk/src/isql/isql.epp 2014-12-26 14:10:39 UTC (rev 60432)
@@ -342,7 +342,7 @@
static bool check_timestamp(const tm& times, const int msec);
static size_t chop_at(char target[], const size_t size);
static void col_check(const TEXT*, unsigned*);
-static void copy_str(TEXT**, const TEXT**, bool*, const TEXT* const,
+static void copy_str(Firebird::string&, const TEXT**, bool*, const TEXT* const,
const TEXT* const, literal_string_type);
static processing_state copy_table(TEXT*, TEXT*, TEXT*);
static processing_state create_db(const TEXT*, TEXT*);
@@ -3458,7 +3458,7 @@
}
-static void copy_str(TEXT** output_str,
+static void copy_str(Firebird::string& output_str,
const TEXT** input_str,
bool* done,
const TEXT* const str_begin,
@@ -3510,47 +3510,33 @@
int slen = 0;
const TEXT* b1;
- TEXT* temp_str = *output_str;
const TEXT* temp_local_stmt_str = *input_str;
switch (str_flag)
{
case SINGLE_QUOTED_STRING:
slen = str_end - temp_local_stmt_str;
- if (temp_str != temp_local_stmt_str)
- {
- strncpy(temp_str, temp_local_stmt_str, slen);
- }
- *output_str = temp_str + slen;
+ output_str.append(temp_local_stmt_str, slen);
*input_str = str_end;
break;
case DOUBLE_QUOTED_STRING:
slen = str_begin - temp_local_stmt_str;
- if (temp_str != temp_local_stmt_str)
- {
- strncpy(temp_str, temp_local_stmt_str, slen);
- }
- temp_str += slen;
- *temp_str = SINGLE_QUOTE;
- temp_str++;
+ output_str.append(temp_local_stmt_str, slen);
+ output_str += SINGLE_QUOTE;
b1 = str_begin + 1;
while (b1 < str_end)
{
- *temp_str = *b1;
- temp_str++;
+ output_str += *b1;
switch (*b1)
{
case SINGLE_QUOTE:
- *temp_str = SINGLE_QUOTE;
- temp_str++;
+ output_str += SINGLE_QUOTE;
break;
case DBL_QUOTE:
b1++;
if (*b1 != DBL_QUOTE)
{
- temp_str--;
- *temp_str = SINGLE_QUOTE;
- temp_str++;
+ output_str[output_str.length() - 1] = SINGLE_QUOTE;
b1--;
}
break;
@@ -3559,20 +3545,12 @@
}
b1++;
}
- *output_str = temp_str;
*input_str = b1;
break;
case NO_MORE_STRING:
- slen = static_cast<int>(strlen(temp_local_stmt_str));
- if (temp_str != temp_local_stmt_str)
- {
- strncpy(temp_str, temp_local_stmt_str, slen);
- }
- temp_str += slen;
- *temp_str = '\0';
+ output_str += temp_local_stmt_str;
*done = true;
- *output_str = temp_str;
- *input_str = temp_local_stmt_str + slen;
+ *input_str = temp_local_stmt_str + strlen(temp_local_stmt_str);
break;
default:
break;
@@ -3690,6 +3668,16 @@
}
+static void appendClause(Firebird::string& to, const char* label, const TEXT* value)
+{
+ to += ' ';
+ to += label;
+ to += " \'";
+ to += value;
+ to += "\' ";
+}
+
+
static processing_state create_db(const TEXT* statement, TEXT* d_name)
{
/**************************************
@@ -3704,8 +3692,9 @@
*
* Parameters: statement == the entire statement for processing.
*
- * Note: SQL ROLE settings are ignored - the newly created database
- * will not have any roles defined in it.
+ * Note: SQL ROLE setting must be taken into an account no matter
+ * that the newly created database will not have any roles defined
+ * in it. Role may affect right to create new database.
*
**************************************/
processing_state ret = SKIP;
@@ -3713,146 +3702,81 @@
// Disconnect from the database and cleanup
ISQL_disconnect_database(false);
- SLONG arglength = static_cast<SLONG>(strlen(statement) + strlen(isqlGlob.User) + strlen(Password) + 24);
- if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET))
- arglength += static_cast<SLONG>(strlen(setValues.ISQL_charset) + strlen(" SET NAMES \'\' "));
- TEXT* local_statement = (TEXT*) ISQL_ALLOC(arglength + 1);
- if (!local_statement)
- return (FAIL);
- TEXT usr[USER_LENGTH];
- TEXT psw[PASSWORD_LENGTH];
+ for (int createWithoutRole = 0; createWithoutRole < 2; ++createWithoutRole)
+ {
+ ret = SKIP;
+ TEXT usr[USER_LENGTH];
+ TEXT psw[PASSWORD_LENGTH];
+ TEXT role[ROLE_LENGTH];
- strcpy(local_statement, statement);
+ Firebird::string local_statement(statement);
- TEXT quote = DBL_QUOTE;
- const TEXT* p = NULL;
+ TEXT quote = DBL_QUOTE;
+ const TEXT* p = NULL;
- // If there is a user parameter, we will set it into the create stmt.
- if (global_usr || global_psw ||
- (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET)))
- {
- strip_quotes(isqlGlob.User, usr);
- strip_quotes(Password, psw);
- // Look for the quotes on the database name and find the close quotes.
- // Use '"' first, if not successful try '''.
- // CVC: Again, this is wrong with embedded quotes.
- // Maybe IUTILS_remove_and_unescape_quotes coupled with IUTILS_copy_SQL_id could work.
- const TEXT* q = strchr(statement, quote);
- if (!q)
+ // If there is a user parameter, we will set it into the create stmt.
+ if (global_usr || global_psw ||
+ (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET)))
{
- quote = SINGLE_QUOTE;
- q = strchr(statement, quote);
- }
+ strip_quotes(isqlGlob.User, usr);
+ strip_quotes(Password, psw);
+ strip_quotes(isqlGlob.Role, role);
+ // Look for the quotes on the database name and find the close quotes.
+ // Use '"' first, if not successful try '''.
+ // CVC: Again, this is wrong with embedded quotes.
+ // Maybe IUTILS_remove_and_unescape_quotes coupled with IUTILS_copy_SQL_id could work.
+ const TEXT* q = strchr(statement, quote);
+ if (!q)
+ {
+ quote = SINGLE_QUOTE;
+ q = strchr(statement, quote);
+ }
- if (q)
- {
- // Set quote to match open quote
- quote = *q;
- q++;
- p = strchr(q, quote);
- if (p)
+ if (q)
{
- p++;
- const ptrdiff_t slen = p - statement;
- local_statement[slen] = '\0';
- if (isqlGlob.SQL_dialect == 1)
+ // Set quote to match open quote
+ quote = *q;
+ q++;
+ p = strchr(q, quote);
+ if (p)
{
- if (global_usr)
- sprintf(local_statement + strlen(local_statement),
- " USER \'%s\' ", usr);
- if (global_psw)
- sprintf(local_statement + strlen(local_statement),
- " PASSWORD \'%s\' ", psw);
- if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET))
- sprintf(local_statement + strlen(local_statement),
- " SET NAMES \'%s\' ", setValues.ISQL_charset);
- sprintf(local_statement + strlen(local_statement), "%s", p);
+ p++;
+ const ptrdiff_t slen = p - statement;
+ local_statement.resize(slen);
+ if (isqlGlob.SQL_dialect == 1)
+ {
+ if (global_usr)
+ appendClause(local_statement, "USER", usr);
+ if (global_psw)
+ appendClause(local_statement, "PASSWORD", psw);
+ if (global_role && createWithoutRole == 0)
+ appendClause(local_statement, "ROLE", role);
+ if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET))
+ appendClause(local_statement, "SET NAMES", setValues.ISQL_charset);
+ local_statement += p;
+ }
}
}
}
- }
- SLONG cnt = 0;
- if ((isqlGlob.SQL_dialect == 0) || (isqlGlob.SQL_dialect > 1))
- {
- const TEXT* q = strchr(statement, SINGLE_QUOTE);
- while (q)
+ SLONG cnt = 0;
+ if ((isqlGlob.SQL_dialect == 0) || (isqlGlob.SQL_dialect > 1))
{
- cnt++;
- const TEXT* l = q + 1;
- q = strchr(l, SINGLE_QUOTE);
- }
-
- TEXT* new_local_statement = NULL;
- if (cnt > 0)
- {
- arglength = static_cast<SLONG>(strlen(statement) +
- strlen(isqlGlob.User) + strlen(Password) + 24 + 2 * cnt);
-
- if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET))
- arglength += static_cast<SLONG>(strlen(setValues.ISQL_charset) + strlen(" SET NAMES \'\' "));
- new_local_statement = (TEXT*) ISQL_ALLOC(arglength + 1);
-
- if (!new_local_statement)
+ const TEXT* q = strchr(statement, SINGLE_QUOTE);
+ while (q)
{
- ISQL_FREE(local_statement);
- return (FAIL);
+ cnt++;
+ const TEXT* l = q + 1;
+ q = strchr(l, SINGLE_QUOTE);
}
- }
- TEXT errbuf[MSG_LENGTH];
+ Firebird::string new_local_statement;
+ TEXT errbuf[MSG_LENGTH];
- TEXT* temp_str;
- if (new_local_statement)
- temp_str = new_local_statement;
- else
- temp_str = local_statement;
-
- const TEXT* temp_local_stmt_str = local_statement;
- bool done = false;
- while (!done)
- {
- const TEXT* str_begin = NULL;
- const TEXT* str_end = NULL;
- literal_string_type str_flag = INIT_STR_FLAG;
- get_str(temp_local_stmt_str, &str_begin, &str_end, &str_flag);
- if (str_flag == INCOMPLETE_STRING)
+ const TEXT* temp_local_stmt_str = local_statement.c_str();
+ bool done = false;
+ while (!done)
{
- IUTILS_msg_get(INCOMPLETE_STR, errbuf, SafeArg() << "create database statement");
- STDERROUT(errbuf);
- ISQL_FREE(local_statement);
- if (new_local_statement)
- ISQL_FREE(new_local_statement);
- return (FAIL);
- }
- copy_str(&temp_str, &temp_local_stmt_str, &done, str_begin, str_end, str_flag);
- }
-
- if (new_local_statement)
- temp_str = new_local_statement;
- else
- temp_str = local_statement;
-
- if (global_usr)
- sprintf(temp_str + strlen(temp_str), " USER \'%s\' ", usr);
-
- if (global_psw)
- sprintf(temp_str + strlen(temp_str), " PASSWORD \'%s\' ", psw);
-
- if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET))
- sprintf(temp_str + strlen(temp_str), " SET NAMES \'%s\' ", setValues.ISQL_charset);
-
- if (new_local_statement)
- temp_str = new_local_statement + strlen(new_local_statement);
- else
- temp_str = local_statement + strlen(local_statement);
-
- if (p && strlen(p) > 0)
- {
- temp_local_stmt_str = p;
- bool done2 = false;
- while (!done2)
- {
const TEXT* str_begin = NULL;
const TEXT* str_end = NULL;
literal_string_type str_flag = INIT_STR_FLAG;
@@ -3861,33 +3785,63 @@
{
IUTILS_msg_get(INCOMPLETE_STR, errbuf, SafeArg() << "create database statement");
STDERROUT(errbuf);
- ISQL_FREE(local_statement);
- if (new_local_statement)
- ISQL_FREE(new_local_statement);
return (FAIL);
}
- copy_str(&temp_str, &temp_local_stmt_str, &done2,
- str_begin, str_end, str_flag);
+ copy_str(new_local_statement, &temp_local_stmt_str, &done, str_begin, str_end, str_flag);
}
+
+ if (global_usr)
+ appendClause(new_local_statement, "USER", usr);
+ if (global_psw)
+ appendClause(new_local_statement, "PASSWORD", psw);
+ if (global_role && createWithoutRole == 0)
+ appendClause(new_local_statement, "ROLE", role);
+ if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET))
+ appendClause(new_local_statement, "SET NAMES", setValues.ISQL_charset);
+
+ if (p && strlen(p) > 0)
+ {
+ temp_local_stmt_str = p;
+ bool done2 = false;
+ while (!done2)
+ {
+ const TEXT* str_begin = NULL;
+ const TEXT* str_end = NULL;
+ literal_string_type str_flag = INIT_STR_FLAG;
+ get_str(temp_local_stmt_str, &str_begin, &str_end, &str_flag);
+ if (str_flag == INCOMPLETE_STRING)
+ {
+ IUTILS_msg_get(INCOMPLETE_STR, errbuf, SafeArg() << "create database statement");
+ STDERROUT(errbuf);
+ return (FAIL);
+ }
+ copy_str(new_local_statement, &temp_local_stmt_str, &done2, str_begin, str_end, str_flag);
+ }
+ }
+
+ local_statement = new_local_statement;
}
- if (new_local_statement)
+ // execute the create statement
+ // If the isqlGlob.SQL_dialect is not set or set to 2, create the database
+ // as a dialect 3 database.
+ unsigned dialect =
+ (isqlGlob.SQL_dialect == 0 || isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION) ?
+ requested_SQL_dialect : isqlGlob.SQL_dialect;
+ DB = Firebird::UtlInterfacePtr()->executeCreateDatabase(fbStatus, local_statement.length(),
+ local_statement.c_str(), dialect, NULL);
+
+ if ((!DB) && (createWithoutRole == 0) && (fbStatus->getErrors()[1] == isc_dsql_error))
{
- ISQL_FREE(local_statement);
- local_statement = new_local_statement;
+ // OLd server failed to parse ROLE clause
+ continue;
}
- }
- // execute the create statement
- // If the isqlGlob.SQL_dialect is not set or set to 2, create the database
- // as a dialect 3 database.
- unsigned dialect =
- (isqlGlob.SQL_dialect == 0 || isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION) ?
- requested_SQL_dialect : isqlGlob.SQL_dialect;
- DB = Firebird::UtlInterfacePtr()->executeCreateDatabase(fbStatus, 0, local_statement, dialect, NULL);
- if (ISQL_errmsg(fbStatus))
- {
- ret = FAIL;
+ if (ISQL_errmsg(fbStatus))
+ {
+ ret = FAIL;
+ }
+ break;
}
if (DB)
@@ -4022,8 +3976,6 @@
}
- if (local_statement)
- ISQL_FREE(local_statement);
return (ret);
}
Modified: firebird/trunk/src/jrd/DbCreators.cpp
===================================================================
--- firebird/trunk/src/jrd/DbCreators.cpp 2014-12-26 12:27:40 UTC (rev 60431)
+++ firebird/trunk/src/jrd/DbCreators.cpp 2014-12-26 14:10:39 UTC (rev 60432)
@@ -100,10 +100,10 @@
namespace Jrd {
-bool checkCreateDatabaseGrant(Firebird::string& userName, Firebird::string& trustedRole,
- const char* securityDb)
+bool checkCreateDatabaseGrant(const string& userName, const string& trustedRole,
+ const string& sqlRole, const char* securityDb)
{
- if (userName == SYSDBA_USER_NAME || trustedRole == ADMIN_ROLE)
+ if (userName == SYSDBA_USER_NAME)
return true;
RefPtr<IAttachment> att;
@@ -111,6 +111,46 @@
if (!openDb(securityDb, att, tra))
return false;
+ string role(sqlRole);
+ if (role.hasData())
+ {
+ role.upper();
+
+ // We need to check is admin 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'";
+
+ Message prm;
+ Field<Varying> u(prm, MAX_SQL_IDENTIFIER_LEN);
+ Field<Varying> r(prm, MAX_SQL_IDENTIFIER_LEN);
+ u = userName.c_str();
+ r = role.c_str();
+
+ Message result;
+ Field<ISC_INT64> cnt(result);
+
+ LocalStatus st;
+ att->execute(&st, tra, 0, sql, SQL_DIALECT_V6, prm.getMetadata(), prm.getBuffer(),
+ result.getMetadata(), result.getBuffer());
+
+ if (st.getStatus() & IStatus::FB_HAS_ERRORS)
+ {
+ // isc_dsql_relation_err when exec SQL - i.e. table RDB$USER_PRIVILEGES
+ // is missing due to non-FB security DB
+ if (!fb_utils::containsErrorCode(st.getErrors(), isc_dsql_relation_err))
+ check("IAttachment::execute", &st);
+
+ role = "";
+ }
+ else if (cnt == 0)
+ role = "";
+ }
+ else
+ role = trustedRole;
+
+ if (role == ADMIN_ROLE)
+ return true;
+
Message gr;
Field<ISC_SHORT> uType(gr);
Field<Varying> u(gr, MAX_SQL_IDENTIFIER_LEN);
@@ -118,8 +158,8 @@
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();
+ rType = role.hasData() ? obj_sql_role : 255;
+ r = role.c_str();
Message result;
Field<ISC_INT64> cnt(result);
Modified: firebird/trunk/src/jrd/DbCreators.h
===================================================================
--- firebird/trunk/src/jrd/DbCreators.h 2014-12-26 12:27:40 UTC (rev 60431)
+++ firebird/trunk/src/jrd/DbCreators.h 2014-12-26 14:10:39 UTC (rev 60432)
@@ -36,8 +36,8 @@
namespace Jrd {
-bool checkCreateDatabaseGrant(Firebird::string& userName, Firebird::string& trustedRole,
- const char* securityDb);
+bool checkCreateDatabaseGrant(const Firebird::string& userName, const Firebird::string& trustedRole,
+ const Firebird::string& sqlRole, const char* securityDb);
class DbCreatorsScan: public VirtualTableScan
{
Modified: firebird/trunk/src/jrd/jrd.cpp
===================================================================
--- firebird/trunk/src/jrd/jrd.cpp 2014-12-26 12:27:40 UTC (rev 60431)
+++ firebird/trunk/src/jrd/jrd.cpp 2014-12-26 14:10:39 UTC (rev 60432)
@@ -7128,7 +7128,7 @@
if (creating && config) // when config is NULL we are in error handler
{
- if (!checkCreateDatabaseGrant(name, trusted_role, (*config)->getSecurityDatabase()))
+ if (!checkCreateDatabaseGrant(name, trusted_role, options.dpb_role_name, (*config)->getSecurityDatabase()))
(Arg::Gds(isc_no_priv) << "CREATE" << "DATABASE" << aliasName).raise();
}
}
Modified: firebird/trunk/src/yvalve/preparse.cpp
===================================================================
--- firebird/trunk/src/yvalve/preparse.cpp 2014-12-26 12:27:40 UTC (rev 60431)
+++ firebird/trunk/src/yvalve/preparse.cpp 2014-12-26 14:10:39 UTC (rev 60432)
@@ -46,7 +46,8 @@
PP_PAGES = 8,
PP_PAGE = 9,
PP_SET = 10,
- PP_NAMES = 11
+ PP_NAMES = 11,
+ PP_ROLE = 12
};
@@ -79,6 +80,7 @@
{"PAGE", 4, PP_PAGE},
{"SET", 3, PP_SET},
{"NAMES", 5, PP_NAMES},
+ {"ROLE", 4, PP_ROLE},
{"", 0, 0}
};
@@ -214,6 +216,17 @@
matched = true;
break;
+ case PP_ROLE:
+ if (get_token(status, STRING, false, &stmt, stmt_end, token))
+ {
+ get_out = true;
+ break;
+ }
+
+ dpb.insertString(isc_dpb_sql_role_name, token);
+ matched = true;
+ break;
+
case PP_SET:
if (get_token(status, SYMBOL, false, &stmt, stmt_end, token) ||
token.length() != pp_symbols[PP_NAMES].length ||
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|