mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 17:23:03 +01:00
2412 lines
57 KiB
Plaintext
2412 lines
57 KiB
Plaintext
/*
|
|
* PROGRAM: JRD Data Definition Utility
|
|
* MODULE: dyn.epp
|
|
* DESCRIPTION: Dynamic data definition
|
|
*
|
|
* The contents of this file are subject to the Interbase 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.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, 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 Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
* 2001.04.20 Claudio Valderrama: Fix bug in grant/revoke by making user
|
|
* case insensitive.
|
|
* 2001.05.24 Claudio Valderrama: Move DYN_delete_role to dyn_del.e.
|
|
* 2001.06.05 John Bellardo: Renamed the revoke static function to
|
|
* revoke_permission, because there is already
|
|
* a revoke(2) function in *nix.
|
|
* 2001.06.20 Claudio Valderrama: Enable calls to DYN_delete_generator.
|
|
* 2001.10.01 Claudio Valderrama: Enable explicit GRANT...to ROLE role_name.
|
|
* and complain if the grantee ROLE doesn't exist.
|
|
* 2001.10.06 Claudio Valderrama: Forbid "NONE" from role-related operations.
|
|
* Honor explicit USER keyword in GRANTs and REVOKEs. *
|
|
* 2002.08.10 Dmitry Yemanov: ALTER VIEW
|
|
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
|
|
*
|
|
* Alex Peshkov
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "../jrd/common.h"
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/ods.h"
|
|
#include "../jrd/tra.h"
|
|
#include "../jrd/dfw_proto.h"
|
|
#include "../jrd/UserManagement.h"
|
|
#include "../jrd/scl.h"
|
|
#include "../jrd/drq.h"
|
|
#include "../jrd/flags.h"
|
|
#include "../jrd/ibase.h"
|
|
#include "../jrd/lls.h"
|
|
#include "../jrd/met.h"
|
|
#include "../jrd/btr.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../jrd/dyn.h"
|
|
//#include "../jrd/license.h"
|
|
#include "../jrd/blb_proto.h"
|
|
#include "../jrd/cmp_proto.h"
|
|
#include "../jrd/dpm_proto.h"
|
|
#include "../jrd/dyn_proto.h"
|
|
#include "../jrd/dyn_df_proto.h"
|
|
#include "../jrd/dyn_dl_proto.h"
|
|
#include "../jrd/dyn_md_proto.h"
|
|
#include "../jrd/err_proto.h"
|
|
#include "../jrd/exe_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#include "../jrd/inf_proto.h"
|
|
#include "../jrd/intl_proto.h"
|
|
#include "../jrd/isc_f_proto.h"
|
|
#include "../jrd/met_proto.h"
|
|
#include "../jrd/vio_proto.h"
|
|
#include "../jrd/thread_proto.h"
|
|
#include "../common/utils_proto.h"
|
|
#include "../jrd/jrd_pwd.h"
|
|
#include "../utilities/gsec/gsec.h"
|
|
#include "../jrd/msg_encode.h"
|
|
|
|
using MsgFormat::SafeArg;
|
|
|
|
|
|
DATABASE DB = STATIC "ODS.RDB";
|
|
|
|
using namespace Jrd;
|
|
using namespace Firebird;
|
|
|
|
|
|
static void grant(Global*, const UCHAR**);
|
|
static bool grantor_can_grant(Global*, const TEXT*, const TEXT*, const MetaName&,
|
|
const MetaName&, bool);
|
|
static bool grantor_can_grant_role(thread_db*, Global*, const MetaName&, const MetaName&);
|
|
static const char* privilege_name(char symbol);
|
|
static void revoke_permission(Global*, const UCHAR**);
|
|
static void revoke_all(Global*, const UCHAR**);
|
|
static void store_privilege(Global*, const MetaName&, const MetaName&, const MetaName&,
|
|
const TEXT*, SSHORT, SSHORT, int, const MetaName&);
|
|
static void set_field_class_name(Global*, const MetaName&, const MetaName&);
|
|
static void dyn_user(Global*, const UCHAR**);
|
|
|
|
|
|
void DYN_ddl(/*Attachment* attachment,*/ jrd_tra* transaction, USHORT length, const UCHAR* ddl)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ d d l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Do meta-data.
|
|
*
|
|
**************************************/
|
|
|
|
thread_db* const tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
const UCHAR* ptr = ddl;
|
|
|
|
if (*ptr++ != isc_dyn_version_1) {
|
|
ERR_post(Arg::Gds(isc_wrodynver));
|
|
}
|
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
|
|
|
Global gbl(transaction);
|
|
|
|
// Create a pool for DYN to operate in. It will be released when
|
|
// the routine exits.
|
|
MemoryPool* const tempPool = dbb->createPool();
|
|
Jrd::ContextPoolHolder context(tdbb, tempPool);
|
|
|
|
try {
|
|
Database::CheckoutLockGuard guard(dbb, dbb->dbb_dyn_mutex);
|
|
|
|
VIO_start_save_point(tdbb, transaction);
|
|
transaction->tra_save_point->sav_verb_count++;
|
|
|
|
DYN_execute(&gbl, &ptr, NULL, NULL, NULL, NULL, NULL);
|
|
transaction->tra_save_point->sav_verb_count--;
|
|
VIO_verb_cleanup(tdbb, transaction);
|
|
fb_assert(ptr <= ddl + length); // disaster check after it happened
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
const Savepoint* savepoint = transaction->tra_save_point;
|
|
if (savepoint && savepoint->sav_verb_count)
|
|
{
|
|
// An error during undo is very bad and has to be dealt with
|
|
// by aborting the transaction. The easiest way is to kill
|
|
// the application by calling bugcheck.
|
|
|
|
try {
|
|
VIO_verb_cleanup(tdbb, transaction);
|
|
}
|
|
catch (const Exception&) {
|
|
BUGCHECK(290); // msg 290 error during savepoint backout
|
|
}
|
|
}
|
|
|
|
dbb->deletePool(tempPool);
|
|
|
|
ERR_punt();
|
|
}
|
|
|
|
dbb->deletePool(tempPool);
|
|
}
|
|
|
|
|
|
void DYN_error(bool status_flag, USHORT number, const SafeArg& sarg)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* DDL failed.
|
|
*
|
|
**************************************/
|
|
thread_db* const tdbb = JRD_get_thread_data();
|
|
|
|
if (tdbb->tdbb_status_vector[1] == isc_no_meta_update)
|
|
return;
|
|
|
|
TEXT error_buffer[BUFFER_MEDIUM];
|
|
Arg::Gds local_status(isc_no_meta_update);
|
|
|
|
if (number)
|
|
{
|
|
fb_msg_format(NULL, DYN_MSG_FAC, number, sizeof(error_buffer), error_buffer, sarg);
|
|
|
|
ISC_STATUS_ARRAY temp_status;
|
|
fb_utils::init_status(temp_status);
|
|
temp_status[1] = ENCODE_ISC_MSG(number, DYN_MSG_FAC);
|
|
FB_SQLSTATE_STRING sqlstate;
|
|
fb_sqlstate(sqlstate, temp_status);
|
|
if (!strcmp(sqlstate, "HY000"))
|
|
strcpy(sqlstate, "42000"); // default SQLSTATE for DYN
|
|
|
|
local_status << Arg::Gds(isc_random) << Arg::Str(error_buffer) << Arg::SqlState(sqlstate);
|
|
}
|
|
ERR_make_permanent(local_status);
|
|
if (status_flag) {
|
|
local_status.append(Arg::StatusVector(tdbb->tdbb_status_vector));
|
|
}
|
|
|
|
local_status.copyTo(tdbb->tdbb_status_vector);
|
|
}
|
|
|
|
|
|
void DYN_error_punt(bool status_flag, USHORT number, const SafeArg& arg)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e r r o r _ p u n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* DDL failed.
|
|
*
|
|
**************************************/
|
|
|
|
DYN_error(status_flag, number, arg);
|
|
ERR_punt();
|
|
}
|
|
|
|
|
|
void DYN_error_punt(bool status_flag, USHORT number, const char* str)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e r r o r _ p u n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* DDL failed.
|
|
*
|
|
**************************************/
|
|
|
|
DYN_error(status_flag, number, SafeArg() << str);
|
|
ERR_punt();
|
|
}
|
|
|
|
|
|
void DYN_error_punt(bool status_flag, USHORT number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e r r o r _ p u n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* DDL failed.
|
|
*
|
|
**************************************/
|
|
|
|
static const SafeArg dummy;
|
|
DYN_error(status_flag, number, dummy);
|
|
ERR_punt();
|
|
}
|
|
|
|
|
|
bool DYN_is_it_sql_role(Global* gbl,
|
|
const MetaName& input_name,
|
|
MetaName& output_name,
|
|
thread_db* tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ i s _ i t _ s q l _ r o l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
* If input_name is found in RDB$ROLES, then returns true. Otherwise
|
|
* returns false.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
const USHORT major_version = dbb->dbb_ods_version;
|
|
const USHORT minor_original = dbb->dbb_minor_original;
|
|
bool found = false;
|
|
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0)
|
|
return found;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_get_role_nm, DYN_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$ROLES WITH
|
|
X.RDB$ROLE_NAME EQ input_name.c_str()
|
|
if (!DYN_REQUEST(drq_get_role_nm))
|
|
DYN_REQUEST(drq_get_role_nm) = request;
|
|
|
|
found = true;
|
|
output_name = X.RDB$OWNER_NAME;
|
|
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_get_role_nm))
|
|
DYN_REQUEST(drq_get_role_nm) = request;
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
void DYN_unsupported_verb()
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ u n s u p p o r t e d _ v e r b
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* We encountered an unsupported dyn verb.
|
|
*
|
|
**************************************/
|
|
|
|
static const SafeArg dummy;
|
|
DYN_error_punt(false, 2, dummy); // msg 2: "unsupported DYN verb"
|
|
}
|
|
|
|
|
|
void DYN_execute(Global* gbl,
|
|
const UCHAR** ptr,
|
|
const MetaName* relation_name,
|
|
MetaName* field_name,
|
|
MetaName* trigger_name,
|
|
MetaName* function_name,
|
|
MetaName* procedure_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e x e c u t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a dynamic ddl statement.
|
|
*
|
|
**************************************/
|
|
UCHAR verb;
|
|
|
|
switch (verb = *(*ptr)++)
|
|
{
|
|
case isc_dyn_begin:
|
|
while (**ptr != isc_dyn_end)
|
|
{
|
|
DYN_execute(gbl, ptr, relation_name, field_name,
|
|
trigger_name, function_name, procedure_name);
|
|
}
|
|
++(*ptr);
|
|
break;
|
|
|
|
// Runtime security-related dynamic DDL should not require licensing.
|
|
// A placeholder case statement for SQL 3 Roles is reserved below.
|
|
|
|
case isc_dyn_grant:
|
|
grant(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_revoke:
|
|
revoke_permission(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_revoke_all:
|
|
revoke_all(gbl, ptr);
|
|
break;
|
|
|
|
/***
|
|
case isc_dyn_def_role:
|
|
create_role (gbl, ptr);
|
|
break;
|
|
***/
|
|
default:
|
|
// make sure that the license allows metadata operations
|
|
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_mod_database:
|
|
DYN_modify_database(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_collation:
|
|
DYN_define_collation(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_del_collation:
|
|
DYN_delete_collation(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_rel:
|
|
case isc_dyn_def_view:
|
|
DYN_define_relation(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_rel:
|
|
DYN_modify_relation(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_view:
|
|
DYN_modify_view(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_rel:
|
|
DYN_delete_relation(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_def_security_class:
|
|
DYN_define_security_class(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_security_class:
|
|
DYN_delete_security_class(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_exception:
|
|
DYN_define_exception(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_exception:
|
|
DYN_modify_exception(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_del_exception:
|
|
DYN_delete_exception(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_filter:
|
|
DYN_define_filter(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_filter:
|
|
DYN_modify_filter(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_filter:
|
|
DYN_delete_filter(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_function:
|
|
DYN_define_function(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_function_arg:
|
|
DYN_define_function_arg(gbl, ptr, function_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_function:
|
|
DYN_modify_function(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_function:
|
|
DYN_delete_function(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_generator:
|
|
DYN_define_generator(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_generator:
|
|
DYN_modify_generator(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_generator:
|
|
DYN_delete_generator(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_sql_role:
|
|
DYN_define_role(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_sql_role:
|
|
DYN_modify_role(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_del_sql_role:
|
|
DYN_delete_role(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_procedure:
|
|
DYN_define_procedure(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_procedure:
|
|
DYN_modify_procedure(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_procedure:
|
|
DYN_delete_procedure(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_parameter:
|
|
DYN_define_parameter(gbl, ptr, procedure_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_prc_parameter:
|
|
DYN_modify_parameter(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_parameter:
|
|
DYN_delete_parameter(gbl, ptr, procedure_name);
|
|
break;
|
|
|
|
case isc_dyn_def_shadow:
|
|
DYN_define_shadow(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_shadow:
|
|
DYN_delete_shadow(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_trigger:
|
|
DYN_define_trigger(gbl, ptr, relation_name, NULL, false);
|
|
break;
|
|
|
|
case isc_dyn_mod_trigger:
|
|
DYN_modify_trigger(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_trigger:
|
|
DYN_delete_trigger(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_trigger_msg:
|
|
DYN_define_trigger_msg(gbl, ptr, trigger_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_trigger_msg:
|
|
DYN_modify_trigger_msg(gbl, ptr, trigger_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_trigger_msg:
|
|
DYN_delete_trigger_msg(gbl, ptr, trigger_name);
|
|
break;
|
|
|
|
case isc_dyn_def_global_fld:
|
|
DYN_define_global_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_global_fld:
|
|
DYN_modify_global_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_global_fld:
|
|
DYN_delete_global_field(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_local_fld:
|
|
DYN_define_local_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_local_fld:
|
|
DYN_modify_local_field(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_local_fld:
|
|
DYN_delete_local_field(gbl, ptr, relation_name); //, field_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_sql_fld:
|
|
DYN_modify_sql_field(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_def_sql_fld:
|
|
DYN_define_sql_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_rel_constraint:
|
|
DYN_define_constraint(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_rel_constraint:
|
|
DYN_delete_constraint(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_def_idx:
|
|
DYN_define_index(gbl, ptr, relation_name, verb, NULL, NULL, NULL, NULL);
|
|
break;
|
|
|
|
case isc_dyn_mod_idx:
|
|
DYN_modify_index(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_idx:
|
|
DYN_delete_index(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_view_relation:
|
|
DYN_define_view_relation(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_def_dimension:
|
|
DYN_define_dimension(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_dimensions:
|
|
DYN_delete_dimensions(gbl, ptr); // relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_charset:
|
|
DYN_modify_charset(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_collation:
|
|
DYN_modify_collation(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mapping:
|
|
DYN_modify_mapping(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_user:
|
|
dyn_user(gbl, ptr);
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SLONG DYN_get_number(const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ n u m b e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up a number and possible clear a null flag.
|
|
*
|
|
**************************************/
|
|
const UCHAR* p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
*ptr = p + length;
|
|
|
|
return gds__vax_integer(p, length);
|
|
}
|
|
|
|
|
|
USHORT DYN_get_string(const TEXT** ptr, TEXT* field, size_t size, bool transliterate)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up a string, move to a target, and, if requested,
|
|
* set a null flag. Return length of string.
|
|
* If destination field size is too small, punt.
|
|
* Strings need enough space for null pad.
|
|
*
|
|
**************************************/
|
|
const TEXT* p = *ptr;
|
|
USHORT length = (UCHAR) *p++;
|
|
length |= ((USHORT) ((UCHAR) (*p++))) << 8;
|
|
|
|
HalfStaticArray<TEXT, MAX_SQL_IDENTIFIER_LEN> temp;
|
|
USHORT l = length;
|
|
|
|
if (l)
|
|
{
|
|
if (length >= size) {
|
|
DYN_error_punt(false, 159); // msg 159: Name longer than database field size
|
|
}
|
|
|
|
TEXT* p2 = (transliterate ? temp.getBuffer(length) : field);
|
|
|
|
do {
|
|
*p2++ = *p++;
|
|
} while (--l);
|
|
}
|
|
|
|
*ptr = p;
|
|
|
|
if (transliterate)
|
|
{
|
|
length = INTL_convert_bytes(JRD_get_thread_data(),
|
|
ttype_metadata, (BYTE*) field, size - 1,
|
|
ttype_dynamic, (const BYTE*) temp.begin(), length, ERR_post);
|
|
}
|
|
|
|
field[length] = 0;
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
USHORT DYN_get_string(const TEXT** ptr, MetaName& field, size_t, bool transliterate)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up an object name, move to a target. Return length of string.
|
|
* If destination field size is too small, punt.
|
|
*
|
|
**************************************/
|
|
const TEXT* p = *ptr;
|
|
USHORT length = (UCHAR) *p++;
|
|
length |= ((USHORT) ((UCHAR) (*p++))) << 8;
|
|
|
|
if (length > MAX_SQL_IDENTIFIER_LEN)
|
|
{
|
|
DYN_error_punt(false, 159);
|
|
// msg 159: Name longer than database field size
|
|
}
|
|
field.assign(p, length);
|
|
p += length;
|
|
|
|
*ptr = p;
|
|
|
|
if (transliterate)
|
|
{
|
|
TEXT temp[MAX_SQL_IDENTIFIER_LEN];
|
|
|
|
length = INTL_convert_bytes(JRD_get_thread_data(),
|
|
ttype_metadata, (BYTE*) temp, sizeof(temp),
|
|
ttype_dynamic, (const BYTE*) field.c_str(), field.length(), ERR_post);
|
|
field.assign(temp, length);
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
USHORT DYN_get_string(const TEXT** ptr, string& field, size_t, bool transliterate)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up an object name, move to a target. Return length of string.
|
|
* If destination field size is too small, punt.
|
|
*
|
|
**************************************/
|
|
const TEXT* p = *ptr;
|
|
USHORT length = (UCHAR) *p++;
|
|
length |= ((USHORT) ((UCHAR) (*p++))) << 8;
|
|
|
|
if (length > MAX_SQL_IDENTIFIER_LEN)
|
|
{
|
|
DYN_error_punt(false, 159);
|
|
// msg 159: Name longer than database field size
|
|
}
|
|
field.assign(p, length);
|
|
p += length;
|
|
|
|
*ptr = p;
|
|
|
|
if (transliterate)
|
|
{
|
|
TEXT temp[MAX_SQL_IDENTIFIER_LEN];
|
|
|
|
length = INTL_convert_bytes(JRD_get_thread_data(),
|
|
ttype_metadata, (BYTE*) temp, sizeof(temp),
|
|
ttype_dynamic, (const BYTE*) field.c_str(), field.length(), ERR_post);
|
|
field.assign(temp, length);
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
USHORT DYN_get_string(const TEXT** ptr, PathName& field, size_t, bool transliterate)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up a file name, move to a target. Return length
|
|
* of string.
|
|
*
|
|
**************************************/
|
|
const TEXT* p = *ptr;
|
|
USHORT length = (UCHAR) *p++;
|
|
length |= ((USHORT) ((UCHAR) (*p++))) << 8;
|
|
|
|
field.assign(p, length);
|
|
p += length;
|
|
|
|
*ptr = p;
|
|
|
|
if (transliterate)
|
|
{
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
PathName temp;
|
|
|
|
temp.reserve(
|
|
INTL_convert_bytes(tdbb,
|
|
ttype_metadata, NULL, 0,
|
|
ttype_dynamic, (const BYTE*) field.begin(), field.length(), ERR_post));
|
|
|
|
length = INTL_convert_bytes(tdbb,
|
|
ttype_metadata, (BYTE*) temp.begin(), temp.capacity(),
|
|
ttype_dynamic, (const BYTE*) field.begin(), field.length(), ERR_post);
|
|
field.assign(temp.c_str(), length);
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
USHORT DYN_get_string(const TEXT** ptr, UCharBuffer& array, size_t, bool transliterate)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up a string, move to a target. Return length
|
|
* of string.
|
|
*
|
|
**************************************/
|
|
const TEXT* p = *ptr;
|
|
USHORT length = (UCHAR) *p++;
|
|
length |= ((USHORT) ((UCHAR) (*p++))) << 8;
|
|
|
|
if (transliterate)
|
|
{
|
|
UCharBuffer temp;
|
|
memcpy(temp.getBuffer(length), p, length);
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
array.resize(
|
|
INTL_convert_bytes(tdbb,
|
|
ttype_metadata, NULL, 0,
|
|
ttype_dynamic, temp.begin(), length, ERR_post));
|
|
|
|
length = INTL_convert_bytes(tdbb,
|
|
ttype_metadata, array.begin(), array.getCapacity(),
|
|
ttype_dynamic, temp.begin(), length, ERR_post);
|
|
array.resize(length);
|
|
}
|
|
else
|
|
memcpy(array.getBuffer(length), p, length);
|
|
|
|
p += length;
|
|
*ptr = p;
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
USHORT DYN_put_blr_blob(Global* gbl, const UCHAR** ptr, bid* blob_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ p u t _ b l r _ b l o b
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Write out a blr blob.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
const UCHAR *p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
|
|
if (!length)
|
|
{
|
|
*ptr = p;
|
|
return length;
|
|
}
|
|
|
|
try {
|
|
blb* blob = BLB_create(tdbb, gbl->gbl_transaction, blob_id);
|
|
BLB_put_segment(tdbb, blob, p, length);
|
|
BLB_close(tdbb, blob);
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_error_punt(true, 106);
|
|
// msg 106: "Create metadata blob failed"
|
|
}
|
|
|
|
*ptr = p + length;
|
|
|
|
return length;
|
|
}
|
|
|
|
USHORT DYN_put_text_blob(Global* gbl, const UCHAR** ptr, bid* blob_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ p u t _ t e x t _ b l o b
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Write out a text blob.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
const UCHAR* p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
|
|
if (!length)
|
|
{
|
|
*ptr = p;
|
|
return length;
|
|
}
|
|
|
|
// make the code die at some place if DYN_error_punt doesn't jump far away.
|
|
const UCHAR* end = NULL;
|
|
|
|
try {
|
|
UCharBuffer bpb;
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
|
|
{
|
|
const size_t convSize = 15;
|
|
bpb.resize(convSize);
|
|
|
|
UCHAR* p = bpb.begin();
|
|
*p++ = isc_bpb_version1;
|
|
|
|
*p++ = isc_bpb_source_type;
|
|
*p++ = 2;
|
|
put_vax_short(p, isc_blob_text);
|
|
p += 2;
|
|
*p++ = isc_bpb_source_interp;
|
|
*p++ = 1;
|
|
*p++ = tdbb->getAttachment()->att_charset;
|
|
|
|
*p++ = isc_bpb_target_type;
|
|
*p++ = 2;
|
|
put_vax_short(p, isc_blob_text);
|
|
p += 2;
|
|
*p++ = isc_bpb_target_interp;
|
|
*p++ = 1;
|
|
*p++ = CS_METADATA;
|
|
fb_assert(size_t(p - bpb.begin()) <= convSize);
|
|
|
|
// set the array count to the number of bytes we used
|
|
bpb.shrink(p - bpb.begin());
|
|
}
|
|
|
|
blb* blob = BLB_create2(tdbb, gbl->gbl_transaction, blob_id, bpb.getCount(), bpb.begin());
|
|
|
|
for (end = p + length; p < end; p += TEXT_BLOB_LENGTH)
|
|
{
|
|
length = (p + TEXT_BLOB_LENGTH <= end) ? TEXT_BLOB_LENGTH : end - p;
|
|
BLB_put_segment(tdbb, blob, p, length);
|
|
}
|
|
BLB_close(tdbb, blob);
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_error_punt(true, 106);
|
|
// msg 106: "Create metadata blob failed"
|
|
}
|
|
|
|
*ptr = end;
|
|
|
|
return length;
|
|
}
|
|
|
|
void DYN_rundown_request(jrd_req* handle, SSHORT id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ r u n d o w n _ r e q u e s t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Unwind a request and save it
|
|
* in the dyn internal request list.
|
|
*
|
|
**************************************/
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
if (!handle) {
|
|
return;
|
|
}
|
|
|
|
EXE_unwind(tdbb, handle);
|
|
if (id >= 0 && !DYN_REQUEST(id)) {
|
|
DYN_REQUEST(id) = handle;
|
|
}
|
|
}
|
|
|
|
|
|
USHORT DYN_skip_attribute(const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ s k i p _ a t t r i b u t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Skip over attribute returning length (excluding
|
|
* size of count bytes).
|
|
*
|
|
**************************************/
|
|
const UCHAR* p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
*ptr = p + length;
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
static void grant( Global* gbl, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g r a n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute SQL grant operation.
|
|
*
|
|
**************************************/
|
|
|
|
SSHORT id;
|
|
char privileges[16];
|
|
MetaName object;
|
|
MetaName field;
|
|
MetaName user;
|
|
TEXT priv[2];
|
|
bool grant_role_stmt = false;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
MetaName grantor = tdbb->getAttachment()->att_user->usr_user_name;
|
|
|
|
const USHORT major_version = dbb->dbb_ods_version;
|
|
const USHORT minor_original = dbb->dbb_minor_original;
|
|
|
|
GET_STRING(ptr, privileges);
|
|
if (!strcmp(privileges, "A")) {
|
|
strcpy(privileges, ALL_PRIVILEGES);
|
|
}
|
|
|
|
int options = 0;
|
|
SSHORT user_type = -1;
|
|
SSHORT obj_type = -1;
|
|
MetaName dummy_name;
|
|
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_rel_name:
|
|
obj_type = obj_relation;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_prc_name:
|
|
obj_type = obj_procedure;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_fld_name:
|
|
GET_STRING(ptr, field);
|
|
break;
|
|
|
|
case isc_dyn_grant_user_group:
|
|
user_type = obj_user_group;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_user:
|
|
{
|
|
GET_STRING(ptr, user);
|
|
// This test may become obsolete as we now allow explicit ROLE keyword.
|
|
if (DYN_is_it_sql_role(gbl, user, dummy_name, tdbb))
|
|
{
|
|
user_type = obj_sql_role;
|
|
if (user == NULL_ROLE)
|
|
{
|
|
DYN_error_punt(false, 195, user.c_str());
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
user_type = obj_user;
|
|
user.upper7();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_user_explicit:
|
|
GET_STRING(ptr, user);
|
|
user_type = obj_user;
|
|
user.upper7();
|
|
break;
|
|
|
|
case isc_dyn_grant_role:
|
|
user_type = obj_sql_role;
|
|
GET_STRING(ptr, user);
|
|
if (!DYN_is_it_sql_role(gbl, user, dummy_name, tdbb))
|
|
{
|
|
DYN_error_punt(false, 188, user.c_str());
|
|
// msg 188: Role doesn't exist.
|
|
}
|
|
if (user == NULL_ROLE)
|
|
{
|
|
DYN_error_punt(false, 195, user.c_str());
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_sql_role_name: // role name in role_name_list
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0) {
|
|
DYN_error_punt(false, 196);
|
|
}
|
|
else
|
|
{
|
|
obj_type = obj_sql_role;
|
|
GET_STRING(ptr, object);
|
|
grant_role_stmt = true;
|
|
if (object == NULL_ROLE)
|
|
{
|
|
DYN_error_punt(false, 195, object.c_str());
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_proc:
|
|
user_type = obj_procedure;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_trig:
|
|
user_type = obj_trigger;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_view:
|
|
user_type = obj_view;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_options:
|
|
case isc_dyn_grant_admin_options:
|
|
options = DYN_get_number(ptr);
|
|
break;
|
|
|
|
case isc_dyn_grant_grantor:
|
|
if (! tdbb->getAttachment()->locksmith())
|
|
{
|
|
DYN_error_punt(false, 252, SYSDBA_USER_NAME);
|
|
}
|
|
GET_STRING(ptr, grantor);
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
grantor.upper7();
|
|
|
|
jrd_req* request = NULL;
|
|
|
|
try {
|
|
|
|
request = CMP_find_request(tdbb,
|
|
(USHORT) (field.length() > 0 ? drq_l_grant1 : drq_l_grant2), DYN_REQUESTS);
|
|
for (const TEXT* pr = privileges; *pr; pr++)
|
|
{
|
|
bool duplicate = false;
|
|
priv[0] = *pr;
|
|
priv[1] = 0;
|
|
if (field.length() > 0)
|
|
{
|
|
id = drq_l_grant1;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$RELATION_NAME EQ object.c_str() AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$PRIVILEGE EQ priv AND
|
|
PRIV.RDB$USER = user.c_str() AND
|
|
PRIV.RDB$USER_TYPE = user_type AND
|
|
PRIV.RDB$GRANTOR EQ grantor.c_str() AND
|
|
PRIV.RDB$FIELD_NAME EQ field.c_str()
|
|
if (!DYN_REQUEST(drq_l_grant1)) {
|
|
DYN_REQUEST(drq_l_grant1) = request;
|
|
}
|
|
|
|
if ((PRIV.RDB$GRANT_OPTION.NULL) ||
|
|
(PRIV.RDB$GRANT_OPTION) ||
|
|
(PRIV.RDB$GRANT_OPTION == options))
|
|
{
|
|
duplicate = true;
|
|
}
|
|
else
|
|
{
|
|
ERASE PRIV; // has to be 0 and options == 1
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_l_grant1))
|
|
{
|
|
DYN_REQUEST(drq_l_grant1) = request;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
id = drq_l_grant2;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$RELATION_NAME EQ object.c_str() AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$PRIVILEGE EQ priv AND
|
|
PRIV.RDB$USER = user.c_str() AND
|
|
PRIV.RDB$USER_TYPE = user_type AND
|
|
PRIV.RDB$GRANTOR EQ grantor.c_str() AND
|
|
PRIV.RDB$FIELD_NAME MISSING
|
|
if (!DYN_REQUEST(drq_l_grant2))
|
|
DYN_REQUEST(drq_l_grant2) = request;
|
|
|
|
if ((PRIV.RDB$GRANT_OPTION.NULL) ||
|
|
(PRIV.RDB$GRANT_OPTION) ||
|
|
(PRIV.RDB$GRANT_OPTION == options))
|
|
{
|
|
duplicate = true;
|
|
}
|
|
else
|
|
{
|
|
ERASE PRIV; // has to be 0 and options == 1
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_l_grant2))
|
|
{
|
|
DYN_REQUEST(drq_l_grant2) = request;
|
|
}
|
|
}
|
|
|
|
if (duplicate) {
|
|
continue;
|
|
}
|
|
|
|
if (grant_role_stmt)
|
|
{
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0)
|
|
{
|
|
DYN_error_punt(false, 196);
|
|
}
|
|
id = drq_get_role_nm;
|
|
|
|
if (!grantor_can_grant_role(tdbb, gbl, grantor, object))
|
|
{
|
|
// ERR_punt();
|
|
goto do_punt;
|
|
}
|
|
|
|
if (user_type == obj_sql_role)
|
|
{
|
|
/****************************************************
|
|
**
|
|
** temporary restriction. This should be removed once
|
|
** GRANT role1 TO rolex is supported and this message
|
|
** could be reused for blocking cycles of role grants
|
|
**
|
|
*****************************************************/
|
|
DYN_error_punt(false, 192, user.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* In the case where the object is a view, then the grantor must have
|
|
some kind of grant privileges on the base table(s)/view(s). If the
|
|
grantor is the owner of the view, then we have to explicitely check
|
|
this because the owner of a view by default has grant privileges on
|
|
his own view. If the grantor is not the owner of the view, then the
|
|
base table/view grant privilege checks were made when the grantor
|
|
got its grant privilege on the view and no further checks are
|
|
necessary.
|
|
As long as only locksmith can use GRANTED BY, no need specially checking
|
|
for privileges of current user. AP-2008 */
|
|
|
|
if (!obj_type)
|
|
{
|
|
// relation or view because we cannot distinguish at this point.
|
|
id = drq_gcg1;
|
|
if (!grantor_can_grant(gbl,
|
|
tdbb->getAttachment()->att_user->usr_user_name.c_str(),
|
|
priv,
|
|
object,
|
|
field,
|
|
true))
|
|
{
|
|
// grantor_can_grant has moved the error string already.
|
|
// just punt back to the setjump
|
|
// ERR_punt();
|
|
goto do_punt;
|
|
}
|
|
}
|
|
}
|
|
|
|
id = drq_s_grant;
|
|
store_privilege(gbl, object, user, field, pr, user_type, obj_type, options, grantor);
|
|
} // for (...)
|
|
|
|
} // try
|
|
catch (const Exception& ex)
|
|
{
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
// we need to rundown as we have to set the env.
|
|
// But in case the error is from store_priveledge we have already
|
|
// unwound the request so passing that as null
|
|
jrd_req* req1 = (id == drq_s_grant || id == drq_gcg1) ? NULL : request;
|
|
DYN_rundown_request(req1, -1);
|
|
|
|
switch (id)
|
|
{
|
|
case drq_l_grant1:
|
|
DYN_error_punt(true, 77);
|
|
// msg 77: "SELECT RDB$USER_PRIVILEGES failed in grant"
|
|
break;
|
|
case drq_l_grant2:
|
|
DYN_error_punt(true, 78);
|
|
// msg 78: "SELECT RDB$USER_PRIVILEGES failed in grant"
|
|
break;
|
|
case drq_s_grant:
|
|
case drq_gcg1:
|
|
ERR_punt();
|
|
// store_priviledge || grantor_can_grant error already handled,
|
|
// just bail out
|
|
break;
|
|
case drq_get_role_nm:
|
|
ERR_punt();
|
|
break;
|
|
default:
|
|
fb_assert(id == drq_gcg1);
|
|
DYN_error_punt(true, 78);
|
|
// msg 78: "SELECT RDB$USER_PRIVILEGES failed in grant"
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
do_punt: // ugly, rethink logic of this function
|
|
ERR_punt();
|
|
}
|
|
|
|
|
|
static bool grantor_can_grant(Global* gbl,
|
|
const TEXT* grantor,
|
|
const TEXT* privilege,
|
|
const MetaName& relation_name,
|
|
const MetaName& field_name,
|
|
bool top_level)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g r a n t o r _ c a n _ g r a n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* return: true is the grantor has grant privilege on the relation/field.
|
|
* false otherwise.
|
|
*
|
|
**************************************/
|
|
USHORT err_num;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
// Verify that the input relation exists.
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_gcg4, DYN_REQUESTS);
|
|
|
|
|
|
try {
|
|
|
|
err_num = 182; // for the longjump
|
|
bool sql_relation = false;
|
|
bool relation_exists = false;
|
|
// SELECT RDB$RELATIONS failed in grant
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
REL IN RDB$RELATIONS WITH
|
|
REL.RDB$RELATION_NAME = relation_name.c_str()
|
|
relation_exists = true;
|
|
if ((!REL.RDB$FLAGS.NULL) && (REL.RDB$FLAGS & REL_sql))
|
|
sql_relation = true;
|
|
if (!DYN_REQUEST(drq_gcg4))
|
|
DYN_REQUEST(drq_gcg4) = request;
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg4))
|
|
DYN_REQUEST(drq_gcg4) = request;
|
|
if (!relation_exists)
|
|
{
|
|
DYN_error(false, 175, SafeArg() << relation_name.c_str());
|
|
// table/view .. does not exist
|
|
return false;
|
|
}
|
|
|
|
// Verify the the input field exists.
|
|
|
|
if (field_name.length() > 0)
|
|
{
|
|
err_num = 183;
|
|
bool field_exists = false;
|
|
|
|
// SELECT RDB$RELATION_FIELDS failed in grant
|
|
request = CMP_find_request(tdbb, drq_gcg5, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
G_FLD IN RDB$RELATION_FIELDS WITH
|
|
G_FLD.RDB$RELATION_NAME = relation_name.c_str() AND
|
|
G_FLD.RDB$FIELD_NAME = field_name.c_str()
|
|
if (!DYN_REQUEST(drq_gcg5))
|
|
DYN_REQUEST(drq_gcg5) = request;
|
|
field_exists = true;
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg5))
|
|
DYN_REQUEST(drq_gcg5) = request;
|
|
if (!field_exists)
|
|
{
|
|
DYN_error(false, 176, SafeArg() << field_name.c_str() << relation_name.c_str());
|
|
// column .. does not exist in table/view ..
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If the current user is locksmith - allow all grants to occur
|
|
|
|
if (tdbb->getAttachment()->locksmith()) {
|
|
return true;
|
|
}
|
|
|
|
// If this is a non-sql table, then the owner will probably not have any
|
|
// entries in the rdb$user_privileges table. Give the owner of a GDML
|
|
// table all privileges.
|
|
err_num = 184;
|
|
bool grantor_is_owner = false;
|
|
// SELECT RDB$RELATIONS/RDB$OWNER_NAME failed in grant
|
|
|
|
request = CMP_find_request(tdbb, drq_gcg2, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
REL IN RDB$RELATIONS WITH
|
|
REL.RDB$RELATION_NAME = relation_name.c_str() AND
|
|
REL.RDB$OWNER_NAME = UPPERCASE(grantor)
|
|
if (!DYN_REQUEST(drq_gcg2))
|
|
DYN_REQUEST(drq_gcg2) = request;
|
|
grantor_is_owner = true;
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg2))
|
|
DYN_REQUEST(drq_gcg2) = request;
|
|
if (!sql_relation && grantor_is_owner) {
|
|
return true;
|
|
}
|
|
|
|
// Remember the grant option for non field-specific user-privileges, and
|
|
// the grant option for the user-privileges for the input field.
|
|
// -1 = no privilege found (yet)
|
|
// 0 = privilege without grant option found
|
|
// 1 = privilege with grant option found
|
|
SSHORT go_rel = -1;
|
|
SSHORT go_fld = -1;
|
|
|
|
|
|
// Verify that the grantor has the grant option for this relation/field
|
|
// in the rdb$user_privileges. If not, then we don't need to look further.
|
|
|
|
err_num = 185;
|
|
// SELECT RDB$USER_PRIVILEGES failed in grant
|
|
|
|
request = CMP_find_request(tdbb, drq_gcg1, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRV IN RDB$USER_PRIVILEGES WITH
|
|
PRV.RDB$USER = UPPERCASE(grantor) AND
|
|
PRV.RDB$USER_TYPE = obj_user AND
|
|
PRV.RDB$RELATION_NAME = relation_name.c_str() AND
|
|
PRV.RDB$OBJECT_TYPE = obj_relation AND
|
|
PRV.RDB$PRIVILEGE = privilege
|
|
if (!DYN_REQUEST(drq_gcg1))
|
|
DYN_REQUEST(drq_gcg1) = request;
|
|
|
|
if (PRV.RDB$FIELD_NAME.NULL)
|
|
{
|
|
if (PRV.RDB$GRANT_OPTION.NULL || !PRV.RDB$GRANT_OPTION)
|
|
go_rel = 0;
|
|
else if (go_rel)
|
|
go_rel = 1;
|
|
}
|
|
else
|
|
{
|
|
if (PRV.RDB$GRANT_OPTION.NULL || !PRV.RDB$GRANT_OPTION)
|
|
{
|
|
if (field_name.length() && field_name == PRV.RDB$FIELD_NAME)
|
|
go_fld = 0;
|
|
}
|
|
else
|
|
{
|
|
if (field_name.length() && field_name == PRV.RDB$FIELD_NAME)
|
|
go_fld = 1;
|
|
}
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg1))
|
|
{
|
|
DYN_REQUEST(drq_gcg1) = request;
|
|
}
|
|
|
|
if (field_name.length())
|
|
{
|
|
if (go_fld == 0)
|
|
{
|
|
DYN_error(false,
|
|
(USHORT)(top_level ? 167 : 168),
|
|
SafeArg() << privilege << field_name.c_str() << relation_name.c_str());
|
|
// no grant option for privilege .. on column .. of [base] table/view ..
|
|
return false;
|
|
}
|
|
|
|
if (go_fld == -1)
|
|
{
|
|
if (go_rel == 0)
|
|
{
|
|
DYN_error(false,
|
|
(USHORT)(top_level ? 169 : 170),
|
|
SafeArg() << privilege << relation_name.c_str() << field_name.c_str());
|
|
// no grant option for privilege .. on [base] table/view .. (for column ..)
|
|
return false;
|
|
}
|
|
|
|
if (go_rel == -1)
|
|
{
|
|
DYN_error(false,
|
|
(USHORT)(top_level ? 171 : 172),
|
|
SafeArg() << privilege << relation_name.c_str() << field_name.c_str());
|
|
// no .. privilege with grant option on [base] table/view .. (for column ..)
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (go_rel == 0)
|
|
{
|
|
DYN_error(false, 173, SafeArg() << privilege << relation_name.c_str());
|
|
// no grant option for privilege .. on table/view ..
|
|
return false;
|
|
}
|
|
|
|
if (go_rel == -1)
|
|
{
|
|
DYN_error(false, 174, SafeArg() << privilege << relation_name.c_str());
|
|
// no .. privilege with grant option on table/view ..
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If the grantor is not the owner of the relation, then we don't need to
|
|
// check the base table(s)/view(s) because that check was performed when
|
|
// the grantor was given its privileges.
|
|
|
|
if (!grantor_is_owner) {
|
|
return true;
|
|
}
|
|
|
|
// Find all the base fields/relations and check for the correct grant privileges on them.
|
|
|
|
err_num = 186;
|
|
// SELECT RDB$VIEW_RELATIONS/RDB$RELATION_FIELDS/... failed in grant
|
|
|
|
request = CMP_find_request(tdbb, drq_gcg3, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
G_FLD IN RDB$RELATION_FIELDS CROSS
|
|
G_VIEW IN RDB$VIEW_RELATIONS WITH
|
|
G_FLD.RDB$RELATION_NAME = relation_name.c_str() AND
|
|
G_FLD.RDB$BASE_FIELD NOT MISSING AND
|
|
G_VIEW.RDB$VIEW_NAME EQ G_FLD.RDB$RELATION_NAME AND
|
|
G_VIEW.RDB$VIEW_CONTEXT EQ G_FLD.RDB$VIEW_CONTEXT
|
|
if (!DYN_REQUEST(drq_gcg3)) {
|
|
DYN_REQUEST(drq_gcg3) = request;
|
|
}
|
|
|
|
if (field_name.length())
|
|
{
|
|
if (field_name == G_FLD.RDB$FIELD_NAME)
|
|
if (!grantor_can_grant(gbl, grantor, privilege, G_VIEW.RDB$RELATION_NAME,
|
|
G_FLD.RDB$BASE_FIELD, false))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!grantor_can_grant(gbl, grantor, privilege, G_VIEW.RDB$RELATION_NAME,
|
|
G_FLD.RDB$BASE_FIELD, false))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg3))
|
|
DYN_REQUEST(drq_gcg3) = request;
|
|
|
|
// all done.
|
|
|
|
} // try
|
|
catch (const Exception& ex)
|
|
{
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, err_num);
|
|
// msg 77: "SELECT RDB$USER_PRIVILEGES failed in grant"
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool grantor_can_grant_role(thread_db* tdbb,
|
|
Global* gbl,
|
|
const MetaName& grantor,
|
|
const MetaName& role_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g r a n t o r _ c a n _ g r a n t _ r o l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
* return: true if the grantor has admin privilege on the role.
|
|
* false otherwise.
|
|
*
|
|
**************************************/
|
|
|
|
bool grantable = false;
|
|
bool no_admin = false;
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
// Fetch the name of the owner of the ROLE
|
|
MetaName owner;
|
|
if (DYN_is_it_sql_role(gbl, role_name, owner, tdbb))
|
|
{
|
|
// Both SYSDBA and the owner of this ROLE can grant membership
|
|
if (tdbb->getAttachment()->locksmith() || owner == grantor)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// role name not exist.
|
|
DYN_error(false, 188, SafeArg() << role_name.c_str());
|
|
return false;
|
|
}
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_get_role_au, DYN_REQUESTS);
|
|
|
|
// The 'grantor' is not the owner of the ROLE, see if they
|
|
// have admin privilege on the role
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRV IN RDB$USER_PRIVILEGES WITH
|
|
PRV.RDB$USER = UPPERCASE(grantor.c_str()) AND
|
|
PRV.RDB$USER_TYPE = obj_user AND
|
|
PRV.RDB$RELATION_NAME EQ role_name.c_str() AND
|
|
PRV.RDB$OBJECT_TYPE = obj_sql_role AND
|
|
PRV.RDB$PRIVILEGE EQ "M"
|
|
if (!DYN_REQUEST(drq_get_role_au)) {
|
|
DYN_REQUEST(drq_get_role_au) = request;
|
|
}
|
|
|
|
if (PRV.RDB$GRANT_OPTION == 2)
|
|
grantable = true;
|
|
else
|
|
no_admin = true;
|
|
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_get_role_au))
|
|
{
|
|
DYN_REQUEST(drq_get_role_au) = request;
|
|
}
|
|
|
|
if (!grantable)
|
|
{
|
|
// 189: user have no admin option.
|
|
// 190: user is not a member of the role.
|
|
DYN_error(false, no_admin ? 189 : 190, SafeArg() << grantor.c_str() << role_name.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static const char* privilege_name(char symbol)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p r i v i l e g e _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Diagnostics print helper.
|
|
*
|
|
**************************************/
|
|
switch (UPPER7(symbol))
|
|
{
|
|
case 'A': return "All";
|
|
case 'I': return "Insert";
|
|
case 'U': return "Update";
|
|
case 'D': return "Delete";
|
|
case 'S': return "Select";
|
|
case 'X': return "Execute";
|
|
case 'M': return "Role";
|
|
case 'R': return "Reference";
|
|
}
|
|
return "<Unknown>";
|
|
}
|
|
|
|
|
|
static void revoke_permission(Global* gbl, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e v o k e _ p e r m i s s i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Revoke some privileges.
|
|
*
|
|
* Note: According to SQL II, section 11.37, pg 280,
|
|
* general rules 8 & 9,
|
|
* if the specified priviledge for the specified user
|
|
* does not exist, it is not an exception error condition
|
|
* but a completion condition. Since V4.0 does not support
|
|
* completion conditions (warnings) we do not return
|
|
* any indication that the revoke operation didn't do
|
|
* anything.
|
|
* 1994-August-2 Rich Damon & David Schnepper
|
|
*
|
|
**************************************/
|
|
UCHAR verb;
|
|
char privileges[16];
|
|
MetaName object, field, user;
|
|
MetaName dummy_name;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
const USHORT major_version = dbb->dbb_ods_version;
|
|
const USHORT minor_original = dbb->dbb_minor_original;
|
|
|
|
// Stash away a copy of the revoker's name, in uppercase form
|
|
|
|
const UserId* revoking_user = tdbb->getAttachment()->att_user;
|
|
MetaName revoking_as_user_name(revoking_user->usr_user_name);
|
|
revoking_as_user_name.upper7();
|
|
|
|
GET_STRING(ptr, privileges);
|
|
if (!strcmp(privileges, "A")) {
|
|
strcpy(privileges, ALL_PRIVILEGES);
|
|
}
|
|
|
|
int options = 0;
|
|
SSHORT user_type = -1;
|
|
SSHORT obj_type = -1;
|
|
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_rel_name:
|
|
obj_type = obj_relation;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_prc_name:
|
|
obj_type = obj_procedure;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_fld_name:
|
|
GET_STRING(ptr, field);
|
|
break;
|
|
|
|
case isc_dyn_grant_user_group:
|
|
user_type = obj_user_group;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_user:
|
|
GET_STRING(ptr, user);
|
|
// This test may become obsolete as we now allow explicit ROLE keyword.
|
|
if (DYN_is_it_sql_role(gbl, user, dummy_name, tdbb))
|
|
{
|
|
user_type = obj_sql_role;
|
|
if (user == NULL_ROLE)
|
|
{
|
|
DYN_error_punt(false, 195, user.c_str());
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
user_type = obj_user;
|
|
user.upper7();
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_user_explicit:
|
|
GET_STRING(ptr, user);
|
|
user_type = obj_user;
|
|
user.upper7();
|
|
break;
|
|
|
|
case isc_dyn_grant_role:
|
|
user_type = obj_sql_role;
|
|
GET_STRING(ptr, user);
|
|
if (!DYN_is_it_sql_role(gbl, user, dummy_name, tdbb))
|
|
{
|
|
DYN_error_punt(false, 188, user.c_str());
|
|
// msg 188: Role doesn't exist.
|
|
}
|
|
if (user == NULL_ROLE)
|
|
{
|
|
DYN_error_punt(false, 195, user.c_str());
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_sql_role_name: // role name in role_name_list
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0) {
|
|
DYN_error_punt(false, 196);
|
|
}
|
|
else
|
|
{
|
|
obj_type = obj_sql_role;
|
|
GET_STRING(ptr, object);
|
|
/*
|
|
CVC: Make this a warning in the future.
|
|
if (object == NULL_ROLE)
|
|
DYN_error_punt(false, 195, object.c_str());
|
|
*/
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_proc:
|
|
user_type = obj_procedure;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_trig:
|
|
user_type = obj_trigger;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_view:
|
|
user_type = obj_view;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_options:
|
|
case isc_dyn_grant_admin_options:
|
|
options = DYN_get_number(ptr);
|
|
break;
|
|
|
|
case isc_dyn_grant_grantor:
|
|
if (! tdbb->getAttachment()->locksmith())
|
|
{
|
|
DYN_error_punt(false, 252, SYSDBA_USER_NAME);
|
|
}
|
|
GET_STRING(ptr, revoking_as_user_name);
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
revoking_as_user_name.upper7();
|
|
|
|
|
|
USHORT id = field.length() ? drq_e_grant1 : drq_e_grant2;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, id, DYN_REQUESTS);
|
|
|
|
try {
|
|
|
|
TEXT temp[2];
|
|
temp[1] = 0;
|
|
for (const TEXT* pr = privileges; (temp[0] = *pr); pr++)
|
|
{
|
|
bool grant_erased = false;
|
|
bool bad_grantor = false;
|
|
|
|
if (field.length())
|
|
{
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$PRIVILEGE EQ temp AND
|
|
PRIV.RDB$RELATION_NAME EQ object.c_str() AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$USER = user.c_str() AND
|
|
PRIV.RDB$USER_TYPE = user_type AND
|
|
PRIV.RDB$FIELD_NAME EQ field.c_str()
|
|
if (!DYN_REQUEST(drq_e_grant1))
|
|
DYN_REQUEST(drq_e_grant1) = request;
|
|
|
|
if (revoking_as_user_name == PRIV.RDB$GRANTOR)
|
|
{
|
|
ERASE PRIV;
|
|
grant_erased = true;
|
|
}
|
|
else
|
|
{
|
|
bad_grantor = true;
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_e_grant1)) {
|
|
DYN_REQUEST(drq_e_grant1) = request;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$PRIVILEGE EQ temp AND
|
|
PRIV.RDB$RELATION_NAME EQ object.c_str() AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$USER EQ user.c_str() AND
|
|
PRIV.RDB$USER_TYPE = user_type
|
|
if (!DYN_REQUEST(drq_e_grant2))
|
|
DYN_REQUEST(drq_e_grant2) = request;
|
|
|
|
// revoking a permission at the table level implies
|
|
// revoking the perm. on all columns. So for all fields
|
|
// in this table which have been granted the privilege, we
|
|
// erase the entries from RDB$USER_PRIVILEGES.
|
|
|
|
if (revoking_as_user_name == PRIV.RDB$GRANTOR)
|
|
{
|
|
ERASE PRIV;
|
|
grant_erased = true;
|
|
}
|
|
else
|
|
{
|
|
bad_grantor = true;
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_e_grant2))
|
|
DYN_REQUEST(drq_e_grant2) = request;
|
|
}
|
|
|
|
if (options && grant_erased)
|
|
{
|
|
// Add the privilege without the grant option
|
|
// There is a modify trigger on the rdb$user_privileges
|
|
// which disallows the table from being updated. It would
|
|
// have to be changed such that only the grant_option
|
|
// field can be updated.
|
|
|
|
const USHORT old_id = id;
|
|
id = drq_s_grant;
|
|
|
|
store_privilege(gbl, object, user, field, pr, user_type, obj_type,
|
|
0, revoking_as_user_name);
|
|
|
|
id = old_id;
|
|
}
|
|
|
|
if (bad_grantor && !grant_erased)
|
|
{
|
|
DYN_error_punt(false, 246, SafeArg() << revoking_as_user_name.c_str() <<
|
|
privilege_name(temp[0]) << object.c_str() << user.c_str());
|
|
// msg 246: @1 is not grantor of @2 on @3 to @4.
|
|
}
|
|
|
|
if (!grant_erased)
|
|
{
|
|
ERR_post_warning(Arg::Warning(isc_dyn_miss_priv_warning) <<
|
|
Arg::Str(privilege_name(temp[0])) << Arg::Str(object) << Arg::Str(user));
|
|
// msg 247: Warning: @1 on @2 is not granted to @3.
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
// we need to rundown as we have to set the env.
|
|
// But in case the error is from store_priveledge we have already
|
|
// unwound the request so passing that as null
|
|
DYN_rundown_request(((id == drq_s_grant) ? NULL : request), -1);
|
|
if (id == drq_e_grant1)
|
|
{
|
|
DYN_error_punt(true, 111);
|
|
// msg 111: "ERASE RDB$USER_PRIVILEGES failed in revoke(1)"
|
|
}
|
|
else if (id == drq_e_grant2)
|
|
{
|
|
DYN_error_punt(true, 113);
|
|
// msg 113: "ERASE RDB$USER_PRIVILEGES failed in revoke (3)"
|
|
}
|
|
else
|
|
{
|
|
ERR_punt();
|
|
// store_priviledge error already handled, just bail out
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void revoke_all(Global* gbl, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e v o k e _ a l l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Revoke all privileges on all objects from user/role.
|
|
*
|
|
**************************************/
|
|
UCHAR verb;
|
|
MetaName user, dummy_name;
|
|
SSHORT user_type = -1;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
|
|
case isc_dyn_grant_user:
|
|
GET_STRING(ptr, user);
|
|
// This test may become obsolete as we now allow explicit ROLE keyword.
|
|
if (DYN_is_it_sql_role(gbl, user, dummy_name, tdbb))
|
|
{
|
|
user_type = obj_sql_role;
|
|
if (user == NULL_ROLE)
|
|
{
|
|
DYN_error_punt(false, 195, user.c_str());
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
user_type = obj_user;
|
|
user.upper7();
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_user_explicit:
|
|
GET_STRING(ptr, user);
|
|
user_type = obj_user;
|
|
user.upper7();
|
|
break;
|
|
|
|
case isc_dyn_grant_role:
|
|
GET_STRING(ptr, user);
|
|
user_type = obj_sql_role;
|
|
if (!DYN_is_it_sql_role(gbl, user, dummy_name, tdbb)) {
|
|
DYN_error_punt(false, 188, user.c_str()); // msg 188: Role doesn't exist.
|
|
}
|
|
if (user == NULL_ROLE)
|
|
{
|
|
DYN_error_punt(false, 195, user.c_str());
|
|
// msg 195: keyword NONE could not be used as SQL role name.
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
|
|
const UserId* revoking_user = tdbb->getAttachment()->att_user;
|
|
MetaName revoking_as_user_name(revoking_user->usr_user_name);
|
|
revoking_as_user_name.upper7();
|
|
|
|
bool grant_erased = false;
|
|
bool bad_grantor = false;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_e_grant3, DYN_REQUESTS);
|
|
|
|
try {
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$USER = user.c_str() AND
|
|
PRIV.RDB$USER_TYPE = user_type
|
|
|
|
if (!DYN_REQUEST(drq_e_grant1))
|
|
DYN_REQUEST(drq_e_grant1) = request;
|
|
|
|
if (revoking_user->locksmith() || revoking_as_user_name == PRIV.RDB$GRANTOR)
|
|
{
|
|
ERASE PRIV;
|
|
grant_erased = true;
|
|
}
|
|
else
|
|
{
|
|
bad_grantor = true;
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_e_grant3)) {
|
|
DYN_REQUEST(drq_e_grant3) = request;
|
|
}
|
|
|
|
if (!grant_erased)
|
|
{
|
|
const char* all = "ALL";
|
|
if (bad_grantor)
|
|
{
|
|
DYN_error_punt(false, 246, SafeArg() << revoking_as_user_name.c_str() <<
|
|
all << all << user.c_str());
|
|
// msg 246: @1 is not grantor of @2 on @3 to @4.
|
|
}
|
|
|
|
ERR_post_warning(Arg::Warning(isc_dyn_miss_priv_warning) << all << all << user);
|
|
// msg 247: Warning: @1 on @2 is not granted to @3.
|
|
}
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
ex.stuff_exception(tdbb->tdbb_status_vector);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 255);
|
|
// msg 255: "ERASE RDB$USER_PRIVILEGES failed in REVOKE ALL ON ALL"
|
|
}
|
|
}
|
|
|
|
|
|
static void set_field_class_name(Global* gbl, const MetaName& relation, const MetaName& field)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e t _ f i e l d _ c l a s s _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* For field level grants, be sure the
|
|
* field has a unique class name.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_s_f_class, DYN_REQUESTS);
|
|
jrd_req* request2 = NULL;
|
|
|
|
bool unique = false;
|
|
|
|
FOR (REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
RFR IN RDB$RELATION_FIELDS
|
|
WITH RFR.RDB$FIELD_NAME = field.c_str() AND
|
|
RFR.RDB$RELATION_NAME = relation.c_str() AND
|
|
RFR.RDB$SECURITY_CLASS MISSING
|
|
|
|
MODIFY RFR
|
|
while (!unique)
|
|
{
|
|
sprintf(RFR.RDB$SECURITY_CLASS, "%s%" SQUADFORMAT, SQL_FLD_SECCLASS_PREFIX,
|
|
DPM_gen_id(tdbb, MET_lookup_generator(tdbb, "RDB$SECURITY_CLASS"), false, 1));
|
|
|
|
unique = true;
|
|
request2 = CMP_find_request(tdbb, drq_s_u_class, DYN_REQUESTS);
|
|
FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
RFR1 IN RDB$RELATION_FIELDS
|
|
WITH RFR1.RDB$SECURITY_CLASS = RFR.RDB$SECURITY_CLASS
|
|
unique = false;
|
|
END_FOR;
|
|
}
|
|
|
|
RFR.RDB$SECURITY_CLASS.NULL = FALSE;
|
|
END_MODIFY;
|
|
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_s_f_class))
|
|
DYN_REQUEST(drq_s_f_class) = request;
|
|
|
|
|
|
if (request2 && !DYN_REQUEST(drq_s_u_class))
|
|
DYN_REQUEST(drq_s_u_class) = request2;
|
|
}
|
|
|
|
|
|
static void store_privilege(Global* gbl,
|
|
const MetaName& object,
|
|
const MetaName& user,
|
|
const MetaName& field,
|
|
const TEXT* privilege,
|
|
SSHORT user_type,
|
|
SSHORT obj_type,
|
|
int option,
|
|
const MetaName& grantor)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t o r e _ p r i v i l e g e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Does its own cleanup in case of error, so calling
|
|
* routine should not.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_s_grant, DYN_REQUESTS);
|
|
|
|
// need to unwind our own request here!! SM 27-Sep-96
|
|
|
|
try {
|
|
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES
|
|
PRIV.RDB$FIELD_NAME.NULL = TRUE;
|
|
strcpy(PRIV.RDB$RELATION_NAME, object.c_str());
|
|
strcpy(PRIV.RDB$USER, user.c_str());
|
|
strcpy(PRIV.RDB$GRANTOR, grantor.c_str());
|
|
PRIV.RDB$USER_TYPE = user_type;
|
|
PRIV.RDB$OBJECT_TYPE = obj_type;
|
|
if (field.length())
|
|
{
|
|
strcpy(PRIV.RDB$FIELD_NAME, field.c_str());
|
|
PRIV.RDB$FIELD_NAME.NULL = FALSE;
|
|
set_field_class_name(gbl, object, field);
|
|
}
|
|
PRIV.RDB$PRIVILEGE[0] = privilege[0];
|
|
PRIV.RDB$PRIVILEGE[1] = 0;
|
|
PRIV.RDB$GRANT_OPTION = option;
|
|
|
|
END_STORE;
|
|
|
|
if (!DYN_REQUEST(drq_s_grant)) {
|
|
DYN_REQUEST(drq_s_grant) = request;
|
|
}
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 79);
|
|
// msg 79: "STORE RDB$USER_PRIVILEGES failed in grant"
|
|
}
|
|
}
|
|
|
|
|
|
static void dyn_user(Global* /*gbl*/, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* d y n _ u s e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Implements CREATE/ALTER/DROP USER
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* const dbb = tdbb->getDatabase();
|
|
jrd_tra* const tra = tdbb->getTransaction();
|
|
|
|
Database::Checkout dcoHolder(dbb);
|
|
|
|
ISC_STATUS_ARRAY status;
|
|
try
|
|
{
|
|
internal_user_data* userData = FB_NEW(*tra->tra_pool) internal_user_data;
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_user_end)
|
|
{
|
|
string text;
|
|
GET_STRING(ptr, text);
|
|
|
|
switch(verb)
|
|
{
|
|
case isc_dyn_user_add:
|
|
text.upper();
|
|
userData->operation = ADD_OPER;
|
|
text.copyTo(userData->user_name, sizeof(userData->user_name));
|
|
userData->user_name_entered = true;
|
|
break;
|
|
|
|
case isc_dyn_user_mod:
|
|
text.upper();
|
|
userData->operation = MOD_OPER;
|
|
text.copyTo(userData->user_name, sizeof(userData->user_name));
|
|
userData->user_name_entered = true;
|
|
break;
|
|
|
|
case isc_dyn_user_del:
|
|
text.upper();
|
|
userData->operation = DEL_OPER;
|
|
text.copyTo(userData->user_name, sizeof(userData->user_name));
|
|
userData->user_name_entered = true;
|
|
break;
|
|
|
|
case isc_dyn_user_passwd:
|
|
if (text.isEmpty())
|
|
{
|
|
// 250: Password should not be empty string
|
|
status_exception::raise(Arg::Gds(ENCODE_ISC_MSG(250, DYN_MSG_FAC)));
|
|
}
|
|
text.copyTo(userData->password, sizeof(userData->password));
|
|
userData->password_entered = true;
|
|
break;
|
|
|
|
case isc_dyn_user_first:
|
|
if (text.hasData())
|
|
{
|
|
text.copyTo(userData->first_name, sizeof(userData->first_name));
|
|
userData->first_name_entered = true;
|
|
}
|
|
else
|
|
{
|
|
userData->first_name_entered = false;
|
|
userData->first_name_specified = true;
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_user_middle:
|
|
if (text.hasData())
|
|
{
|
|
text.copyTo(userData->middle_name, sizeof(userData->middle_name));
|
|
userData->middle_name_entered = true;
|
|
}
|
|
else
|
|
{
|
|
userData->middle_name_entered = false;
|
|
userData->middle_name_specified = true;
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_user_last:
|
|
if (text.hasData())
|
|
{
|
|
text.copyTo(userData->last_name, sizeof(userData->last_name));
|
|
userData->last_name_entered = true;
|
|
}
|
|
else
|
|
{
|
|
userData->last_name_entered = false;
|
|
userData->last_name_specified = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
USHORT id = tra->getUserManagement()->put(userData);
|
|
DFW_post_work(tra, dfw_user_management, NULL, id);
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
e.stuff_exception(status);
|
|
memmove(&status[2], &status[0], sizeof(status) - 2 * sizeof(status[0]));
|
|
status[0] = isc_arg_gds;
|
|
status[1] = isc_no_meta_update;
|
|
status_exception::raise(status);
|
|
}
|
|
}
|