8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 07:23:04 +01:00
firebird-mirror/src/jrd/dyn.epp
2009-01-20 08:33:59 +00:00

2406 lines
58 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* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->getDatabase();
const UCHAR* ptr = ddl;
if (*ptr++ != isc_dyn_version_1) {
ERR_post(Arg::Gds(isc_wrodynver));
}
ISC_STATUS* status = tdbb->tdbb_status_vector;
fb_utils::init_status(status);
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);
}
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* 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* 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)
{
bpb.resize(15);
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;
// 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* 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* 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* 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* 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* 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* 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$GRANT",
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* 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* dbb = tdbb->getDatabase();
jrd_tra* tra = tdbb->getTransaction();
Database::Checkout dcoHolder(dbb);
ISC_STATUS_ARRAY status;
try
{
DeferredWork* work = DFW_post_work(tra, dfw_user_management, NULL, 0);
UserManagement* sec = reinterpret_cast<UserManagement*>(work->dfw_args);
if (!sec)
{
sec = FB_NEW(*tra->tra_pool) UserManagement(tdbb);
work->dfw_args = reinterpret_cast<DeferredWork*>(sec);
}
internal_user_data userData;
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())
{
// 249: Password should not be empty string
status_exception::raise(Arg::Gds(ENCODE_ISC_MSG(249, 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;
}
}
int errcode = sec->execute(status, &userData);
switch (errcode)
{
case 0: // nothing
break;
case GsecMsg22:
status_exception::raise(Arg::Gds(ENCODE_ISC_MSG(errcode, GSEC_MSG_FAC)) <<
Arg::Str(userData.user_name));
default:
status_exception::raise(Arg::Gds(ENCODE_ISC_MSG(errcode, GSEC_MSG_FAC)));
}
}
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);
}
}