8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 21:23:04 +01:00
firebird-mirror/src/jrd/grant.epp

1201 lines
28 KiB
Plaintext
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: grant.epp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: SQL Grant/Revoke Handler
*
* 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): ______________________________________.
2002-10-30 07:40:58 +01:00
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <stdio.h>
#include <string.h>
2016-05-31 19:07:08 +02:00
#include <ctype.h>
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
#include "../jrd/scl.h"
#include "../jrd/acl.h"
#include "../jrd/irq.h"
#include "../jrd/blb.h"
2003-11-30 22:04:18 +01:00
#include "../jrd/btr.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/req.h"
#include "../jrd/tra.h"
#include "../jrd/val.h"
#include "../jrd/met.h"
#include "../jrd/intl.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dfw_proto.h"
#include "../jrd/dpm_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
2010-10-12 10:02:57 +02:00
#include "../yvalve/gds_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/grant_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/scl_proto.h"
2003-12-31 06:36:12 +01:00
#include "../common/utils_proto.h"
#include "../common/classes/array.h"
#include "../jrd/constants.h"
2001-05-23 15:26:42 +02:00
using namespace Jrd;
2009-11-25 04:58:43 +01:00
// privileges given to the owner of a relation
2001-05-23 15:26:42 +02:00
const SecurityClass::flags_t OWNER_PRIVS = SCL_control | SCL_drop | SCL_alter;
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
inline void CHECK_AND_MOVE(Acl& to, UCHAR from)
{
to.add(from);
}
2001-05-23 15:26:42 +02:00
DATABASE DB = STATIC "yachts.lnk";
2009-04-04 18:39:31 +02:00
static void define_default_class(thread_db*, const TEXT*, Firebird::MetaName&, const Acl&,
jrd_tra*);
static void finish_security_class(Acl&, SecurityClass::flags_t);
static void get_object_info(thread_db*, const TEXT*, SSHORT,
Firebird::MetaName&, Firebird::MetaName&, Firebird::MetaName&, bool&);
static SecurityClass::flags_t get_public_privs(thread_db*, const TEXT*, SSHORT);
static void get_user_privs(thread_db*, Acl&, const TEXT*, SSHORT, const Firebird::MetaName&,
SecurityClass::flags_t);
static void grant_user(Acl&, const Firebird::MetaName&, SSHORT, SecurityClass::flags_t);
2009-04-04 18:39:31 +02:00
static SecurityClass::flags_t save_field_privileges(thread_db*, Acl&, const TEXT*,
const Firebird::MetaName&, SecurityClass::flags_t, jrd_tra*);
static void save_security_class(thread_db*, const Firebird::MetaName&, const Acl&, jrd_tra*);
static SecurityClass::flags_t trans_sql_priv(const TEXT*);
static SecurityClass::flags_t squeeze_acl(Acl&, const Firebird::MetaName&, SSHORT);
static bool check_string(const UCHAR*, const Firebird::MetaName&);
2001-05-23 15:26:42 +02:00
void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* G R A N T _ p r i v i l e g e s
*
**************************************
*
* Functional description
* Compute access control list from SQL privileges.
2008-12-18 11:57:12 +01:00
* This calculation is tricky and involves interaction between
* the relation-level and field-level privileges. Do not change
2001-05-23 15:26:42 +02:00
* the order of operations lightly.
*
**************************************/
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
bool restrct = false;
2001-05-23 15:26:42 +02:00
Firebird::MetaName s_class, owner, default_class;
bool view; // unused after being retrieved.
get_object_info(tdbb, name.c_str(), id, owner, s_class, default_class, view);
2001-05-23 15:26:42 +02:00
if (s_class.length() == 0) {
return;
}
2001-05-23 15:26:42 +02:00
2009-11-25 04:58:43 +01:00
// start the acl off by giving the owner all privileges
Acl acl, default_acl;
2001-05-23 15:26:42 +02:00
CHECK_AND_MOVE(acl, ACL_version);
2009-12-25 20:29:58 +01:00
2009-12-25 13:58:06 +01:00
SecurityClass::flags_t priv = OWNER_PRIVS;
switch (id)
2009-12-25 13:58:06 +01:00
{
case obj_relation:
priv |= SCL_references;
case obj_view:
priv |= SCL_select | SCL_insert | SCL_update | SCL_delete;
break;
case obj_procedure:
case obj_udf:
case obj_package_header:
priv |= SCL_execute;
break;
case obj_field:
case obj_exception:
case obj_generator:
case obj_charset:
case obj_collation:
priv |= SCL_usage;
break;
2013-03-08 03:37:40 +01:00
default:
2014-07-10 04:21:18 +02:00
if (id >= obj_database && id < obj_type_MAX)
priv = OWNER_PRIVS;
2014-07-10 04:21:18 +02:00
break;
2009-12-25 13:58:06 +01:00
}
grant_user(acl, owner, obj_user, priv);
2001-05-23 15:26:42 +02:00
// Pick up core privileges
2001-05-23 15:26:42 +02:00
const SecurityClass::flags_t public_priv = get_public_privs(tdbb, name.c_str(), id);
get_user_privs(tdbb, acl, name.c_str(), id, owner, public_priv);
2001-05-23 15:26:42 +02:00
if (id == obj_relation)
{
2009-11-25 04:58:43 +01:00
// Now handle field-level privileges. This might require adding
// UPDATE privilege to the relation-level acl, Therefore, save
// off the relation acl because we need to add a default field
// acl in that case.
2001-05-23 15:26:42 +02:00
default_acl.assign(acl);
2001-05-23 15:26:42 +02:00
const SecurityClass::flags_t aggregate_public =
save_field_privileges(tdbb, acl, name.c_str(), owner, public_priv,
transaction);
2001-05-23 15:26:42 +02:00
2009-11-25 04:58:43 +01:00
// finish off and store the security class for the relation
2001-05-23 15:26:42 +02:00
finish_security_class(acl, aggregate_public);
2001-05-23 15:26:42 +02:00
save_security_class(tdbb, s_class, acl, transaction);
2001-05-23 15:26:42 +02:00
2009-11-25 04:58:43 +01:00
if (acl.getCount() != default_acl.getCount()) // relation privs were added?
restrct = true;
2001-05-23 15:26:42 +02:00
2009-11-25 04:58:43 +01:00
// if there have been privileges added at the relation level which
// need to be restricted from other fields in the relation,
// update the acl for them
2001-05-23 15:26:42 +02:00
if (restrct)
{
finish_security_class(default_acl, public_priv);
define_default_class(tdbb, name.c_str(), default_class, default_acl,
transaction);
2001-05-23 15:26:42 +02:00
}
}
2009-09-30 03:10:11 +02:00
else
{
finish_security_class(acl, public_priv);
save_security_class(tdbb, s_class, acl, transaction);
}
2001-05-23 15:26:42 +02:00
}
2008-07-16 03:39:12 +02:00
static void define_default_class(thread_db* tdbb,
2003-12-31 06:36:12 +01:00
const TEXT* relation_name,
Firebird::MetaName& default_class,
const Acl& acl,
jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d e f i n e _ d e f a u l t _ c l a s s
*
**************************************
*
* Functional description
* Update the default security class for fields
* which have not been specifically granted
* any privileges. We must grant them all
* privileges which were specifically granted
* at the relation level, but none of the
* privileges we added at the relation level
* for the purpose of accessing other fields.
*
**************************************/
SET_TDBB(tdbb);
2009-11-25 09:38:52 +01:00
if (default_class.length() == 0)
{
2006-05-04 02:02:58 +02:00
default_class.printf("%s%" SQUADFORMAT, DEFAULT_CLASS,
2008-12-18 11:57:12 +01:00
DPM_gen_id(tdbb, MET_lookup_generator(tdbb, DEFAULT_CLASS), false, 1));
2001-05-23 15:26:42 +02:00
AutoCacheRequest request(tdbb, irq_grant7, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
2001-05-23 15:26:42 +02:00
REL IN RDB$RELATIONS
WITH REL.RDB$RELATION_NAME EQ relation_name
{
2001-05-23 15:26:42 +02:00
MODIFY REL USING
REL.RDB$DEFAULT_CLASS.NULL = FALSE;
jrd_vtof(default_class.c_str(), REL.RDB$DEFAULT_CLASS,
2001-05-23 15:26:42 +02:00
sizeof(REL.RDB$DEFAULT_CLASS));
END_MODIFY
}
END_FOR
2001-05-23 15:26:42 +02:00
}
save_security_class(tdbb, default_class, acl, transaction);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
dsc desc;
2001-05-23 15:26:42 +02:00
desc.dsc_dtype = dtype_text;
desc.dsc_sub_type = 0;
desc.dsc_scale = 0;
desc.dsc_ttype() = ttype_metadata;
2001-05-23 15:26:42 +02:00
desc.dsc_address = (UCHAR *) relation_name;
2014-07-17 20:48:46 +02:00
desc.dsc_length = static_cast<USHORT>(strlen(relation_name));
DFW_post_work(transaction, dfw_scan_relation, &desc, 0);
2001-05-23 15:26:42 +02:00
}
2004-08-26 13:07:57 +02:00
static void finish_security_class(Acl& acl, SecurityClass::flags_t public_priv)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f i n i s h _ s e c u r i t y _ c l a s s
*
**************************************
*
* Functional description
* Finish off a security class, putting
* in a wildcard for any public privileges.
*
**************************************/
2009-11-25 09:38:52 +01:00
if (public_priv)
{
CHECK_AND_MOVE(acl, ACL_id_list);
SCL_move_priv(public_priv, acl);
2001-05-23 15:26:42 +02:00
}
CHECK_AND_MOVE(acl, ACL_end);
2001-05-23 15:26:42 +02:00
}
2008-12-20 09:12:19 +01:00
static SecurityClass::flags_t get_public_privs(thread_db* tdbb,
const TEXT* object_name,
SSHORT obj_type)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ p u b l i c _ p r i v s
*
**************************************
*
* Functional description
* Get public privileges for a particular object.
*
**************************************/
SET_TDBB(tdbb);
Jrd::Attachment* attachment = tdbb->getAttachment();
2001-05-23 15:26:42 +02:00
SecurityClass::flags_t public_priv = 0;
2001-05-23 15:26:42 +02:00
AutoCacheRequest request(tdbb, irq_grant5, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
PRV IN RDB$USER_PRIVILEGES
WITH PRV.RDB$RELATION_NAME EQ object_name AND
PRV.RDB$OBJECT_TYPE EQ obj_type AND
PRV.RDB$USER EQ "PUBLIC" AND
PRV.RDB$USER_TYPE EQ obj_user AND
2008-12-18 11:57:12 +01:00
PRV.RDB$FIELD_NAME MISSING
{
public_priv |= trans_sql_priv(PRV.RDB$PRIVILEGE);
}
END_FOR
2001-05-23 15:26:42 +02:00
return public_priv;
2001-05-23 15:26:42 +02:00
}
static void get_object_info(thread_db* tdbb,
2003-12-31 06:36:12 +01:00
const TEXT* object_name,
2001-05-23 15:26:42 +02:00
SSHORT obj_type,
Firebird::MetaName& owner,
2008-12-18 11:57:12 +01:00
Firebird::MetaName& s_class,
Firebird::MetaName& default_class,
bool& view)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ o b j e c t _ i n f o
*
**************************************
*
* Functional description
* This could be done in MET_scan_relation () or MET_lookup_procedure,
* but presumably we wish to make sure the information we have is
* up-to-the-minute.
*
**************************************/
SET_TDBB(tdbb);
Jrd::Attachment* attachment = tdbb->getAttachment();
2001-05-23 15:26:42 +02:00
owner = s_class = default_class = "";
view = false;
2001-05-23 15:26:42 +02:00
if (obj_type == obj_relation)
{
AutoCacheRequest request(tdbb, irq_grant1, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
REL IN RDB$RELATIONS WITH
2008-12-18 11:57:12 +01:00
REL.RDB$RELATION_NAME EQ object_name
{
s_class = REL.RDB$SECURITY_CLASS;
default_class = REL.RDB$DEFAULT_CLASS;
owner = REL.RDB$OWNER_NAME;
view = !REL.RDB$VIEW_BLR.isEmpty();
}
END_FOR
2001-05-23 15:26:42 +02:00
}
else if (obj_type == obj_package_header)
{
AutoCacheRequest request(tdbb, irq_grant10, IRQ_REQUESTS);
FOR (REQUEST_HANDLE request)
PKG IN RDB$PACKAGES
WITH PKG.RDB$PACKAGE_NAME EQ object_name
{
s_class = PKG.RDB$SECURITY_CLASS;
default_class = "";
owner = PKG.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
2009-12-25 13:58:06 +01:00
else if (obj_type == obj_procedure)
{
AutoCacheRequest request(tdbb, irq_grant9, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
2009-12-25 13:58:06 +01:00
PRC IN RDB$PROCEDURES WITH
PRC.RDB$PROCEDURE_NAME EQ object_name AND
PRC.RDB$PACKAGE_NAME MISSING
{
2009-12-25 13:58:06 +01:00
s_class = PRC.RDB$SECURITY_CLASS;
default_class = "";
2009-12-25 13:58:06 +01:00
owner = PRC.RDB$OWNER_NAME;
view = false;
}
END_FOR
2001-05-23 15:26:42 +02:00
}
2009-12-25 13:58:06 +01:00
else if (obj_type == obj_udf)
{
AutoCacheRequest request(tdbb, irq_grant11, IRQ_REQUESTS);
2009-12-25 13:58:06 +01:00
FOR(REQUEST_HANDLE request)
FUN IN RDB$FUNCTIONS WITH
FUN.RDB$FUNCTION_NAME EQ object_name AND
FUN.RDB$PACKAGE_NAME MISSING
{
2009-12-25 13:58:06 +01:00
s_class = FUN.RDB$SECURITY_CLASS;
default_class = "";
owner = FUN.RDB$OWNER_NAME;
view = false;
}
END_FOR
2009-12-25 13:58:06 +01:00
}
else if (obj_type == obj_charset)
{
AutoCacheRequest request(tdbb, irq_grant12, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
CS IN RDB$CHARACTER_SETS WITH
CS.RDB$CHARACTER_SET_NAME EQ object_name
{
s_class = CS.RDB$SECURITY_CLASS;
default_class = "";
owner = CS.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
else if (obj_type == obj_collation)
{
AutoCacheRequest request(tdbb, irq_grant13, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
COLL IN RDB$COLLATIONS WITH
COLL.RDB$COLLATION_NAME EQ object_name
{
s_class = COLL.RDB$SECURITY_CLASS;
default_class = "";
owner = COLL.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
else if (obj_type == obj_exception)
{
AutoCacheRequest request(tdbb, irq_grant14, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
XCP IN RDB$EXCEPTIONS WITH
XCP.RDB$EXCEPTION_NAME EQ object_name
{
s_class = XCP.RDB$SECURITY_CLASS;
default_class = "";
owner = XCP.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
else if (obj_type == obj_generator)
{
AutoCacheRequest request(tdbb, irq_grant15, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
GEN IN RDB$GENERATORS WITH
GEN.RDB$GENERATOR_NAME EQ object_name
{
s_class = GEN.RDB$SECURITY_CLASS;
default_class = "";
owner = GEN.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
else if (obj_type == obj_field)
{
AutoCacheRequest request(tdbb, irq_grant16, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
FLD IN RDB$FIELDS WITH
FLD.RDB$FIELD_NAME EQ object_name
{
s_class = FLD.RDB$SECURITY_CLASS;
default_class = "";
owner = FLD.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
else if (obj_type == obj_database)
{
AutoCacheRequest request(tdbb, irq_grant17, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
X IN RDB$DATABASE
{
s_class = X.RDB$SECURITY_CLASS;
default_class = "";
owner = tdbb->getDatabase()->dbb_owner;
view = false;
}
END_FOR
}
else if (obj_type == obj_blob_filter)
{
AutoCacheRequest request(tdbb, irq_grant18, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
FLT IN RDB$FILTERS WITH
FLT.RDB$FUNCTION_NAME EQ object_name
{
s_class = FLT.RDB$SECURITY_CLASS;
default_class = "";
owner = FLT.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
else if (obj_type == obj_sql_role)
{
AutoCacheRequest request(tdbb, irq_grant19, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
ROL IN RDB$ROLES WITH
ROL.RDB$ROLE_NAME EQ object_name
{
s_class = ROL.RDB$SECURITY_CLASS;
default_class = "";
owner = ROL.RDB$OWNER_NAME;
view = false;
}
END_FOR
}
else
{
s_class = get_object_name(obj_type);
default_class = "";
owner = tdbb->getDatabase()->dbb_owner;
view = false;
}
2001-05-23 15:26:42 +02:00
}
static void get_user_privs(thread_db* tdbb,
Acl& acl,
const TEXT* object_name,
SSHORT obj_type,
const Firebird::MetaName& owner,
SecurityClass::flags_t public_priv)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ u s e r _ p r i v s
*
**************************************
*
* Functional description
* Get privileges for a particular object.
*
**************************************/
SET_TDBB(tdbb);
Jrd::Attachment* attachment = tdbb->getAttachment();
2001-05-23 15:26:42 +02:00
Firebird::MetaName user;
SSHORT user_type = -2;
SecurityClass::flags_t priv = 0;
2001-05-23 15:26:42 +02:00
AutoCacheRequest request(tdbb, irq_grant2, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
PRV IN RDB$USER_PRIVILEGES
WITH PRV.RDB$RELATION_NAME EQ object_name AND
PRV.RDB$OBJECT_TYPE EQ obj_type AND
(PRV.RDB$USER NE "PUBLIC" OR PRV.RDB$USER_TYPE NE obj_user) AND
(PRV.RDB$USER NE owner.c_str() OR PRV.RDB$USER_TYPE NE obj_user) AND
((PRV.RDB$OBJECT_TYPE NE obj_sql_role AND
PRV.RDB$FIELD_NAME MISSING) OR
(PRV.RDB$OBJECT_TYPE EQ obj_sql_role AND
PRV.RDB$GRANT_OPTION EQ WITH_ADMIN_OPTION))
2001-05-23 15:26:42 +02:00
SORTED BY PRV.RDB$USER, PRV.RDB$USER_TYPE
{
fb_utils::exact_name_limit(PRV.RDB$USER, sizeof(PRV.RDB$USER));
if (user != PRV.RDB$USER || user_type != PRV.RDB$USER_TYPE)
2001-05-23 15:26:42 +02:00
{
if (user.length())
2001-05-23 15:26:42 +02:00
{
grant_user(acl, user, user_type, priv);
2001-05-23 15:26:42 +02:00
}
user_type = PRV.RDB$USER_TYPE;
if (user_type == obj_user)
{
priv = public_priv;
2001-05-23 15:26:42 +02:00
}
else
{
priv = 0;
}
user = PRV.RDB$USER;
2001-05-23 15:26:42 +02:00
}
priv |= trans_sql_priv(obj_type == obj_sql_role ? "O" : PRV.RDB$PRIVILEGE);
}
END_FOR
2001-05-23 15:26:42 +02:00
if (user.length())
grant_user(acl, user, user_type, priv);
2001-05-23 15:26:42 +02:00
}
2008-07-16 03:39:12 +02:00
static void grant_user(Acl& acl,
const Firebird::MetaName& user,
SSHORT user_type,
SecurityClass::flags_t privs)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g r a n t _ u s e r
*
**************************************
*
* Functional description
* Grant privileges to a particular user.
*
**************************************/
Acl::size_type back = acl.getCount();
CHECK_AND_MOVE(acl, ACL_id_list);
2009-01-20 09:33:59 +01:00
switch (user_type)
{
2001-05-23 15:26:42 +02:00
case obj_user_group:
CHECK_AND_MOVE(acl, id_group);
2001-05-23 15:26:42 +02:00
break;
case obj_sql_role:
CHECK_AND_MOVE(acl, id_sql_role);
2001-05-23 15:26:42 +02:00
break;
case obj_user:
CHECK_AND_MOVE(acl, id_person);
2001-05-23 15:26:42 +02:00
break;
case obj_package_header:
CHECK_AND_MOVE(acl, id_package);
break;
2001-05-23 15:26:42 +02:00
case obj_procedure:
CHECK_AND_MOVE(acl, id_procedure);
2001-05-23 15:26:42 +02:00
break;
2009-12-25 13:58:06 +01:00
case obj_udf:
CHECK_AND_MOVE(acl, id_function);
break;
2001-05-23 15:26:42 +02:00
case obj_trigger:
CHECK_AND_MOVE(acl, id_trigger);
2001-05-23 15:26:42 +02:00
break;
case obj_view:
CHECK_AND_MOVE(acl, id_view);
2001-05-23 15:26:42 +02:00
break;
2016-05-31 19:07:08 +02:00
case obj_privilege:
CHECK_AND_MOVE(acl, id_privilege);
fb_assert(isdigit(user[0]));
break;
2001-05-23 15:26:42 +02:00
default:
2009-11-25 04:58:43 +01:00
BUGCHECK(292); // Illegal user_type
2001-05-23 15:26:42 +02:00
}
const UCHAR length = user.length();
CHECK_AND_MOVE(acl, length);
2001-05-23 15:26:42 +02:00
if (length) {
acl.add(reinterpret_cast<const UCHAR*>(user.c_str()), length);
2001-05-23 15:26:42 +02:00
}
if (!SCL_move_priv(privs, acl))
acl.shrink(back);
2001-05-23 15:26:42 +02:00
}
2008-07-16 03:39:12 +02:00
static SecurityClass::flags_t save_field_privileges(thread_db* tdbb,
2008-12-20 09:12:19 +01:00
Acl& relation_acl,
const TEXT* relation_name,
const Firebird::MetaName& owner,
SecurityClass::flags_t public_priv,
jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s a v e _ f i e l d _ p r i v i l e g e s
*
**************************************
*
* Functional description
* Compute the privileges for all fields within a relation.
* All fields must be given the initial relation-level privileges.
2008-12-18 11:57:12 +01:00
* Conversely, field-level privileges must be added to the relation
2001-05-23 15:26:42 +02:00
* security class to be effective.
*
**************************************/
SET_TDBB(tdbb);
Jrd::Attachment* attachment = tdbb->getAttachment();
2001-05-23 15:26:42 +02:00
2008-07-15 08:24:04 +02:00
Acl field_acl(relation_acl);
const Acl acl_start(relation_acl);
2001-05-23 15:26:42 +02:00
Firebird::MetaName field_name, user, s_class;
SecurityClass::flags_t aggregate_public = public_priv;
2009-02-21 23:50:48 +01:00
SecurityClass::flags_t priv = 0;
SecurityClass::flags_t field_public = 0;
SSHORT user_type = -1;
2001-05-23 15:26:42 +02:00
AutoCacheRequest request(tdbb, irq_grant6, IRQ_REQUESTS);
AutoRequest request2, request3;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
2001-05-23 15:26:42 +02:00
FLD IN RDB$RELATION_FIELDS CROSS
PRV IN RDB$USER_PRIVILEGES
OVER RDB$RELATION_NAME, RDB$FIELD_NAME
WITH PRV.RDB$OBJECT_TYPE EQ obj_relation AND
PRV.RDB$RELATION_NAME EQ relation_name AND
PRV.RDB$FIELD_NAME NOT MISSING AND
(PRV.RDB$USER NE owner.c_str() OR PRV.RDB$USER_TYPE NE obj_user)
2008-12-18 11:57:12 +01:00
SORTED BY PRV.RDB$FIELD_NAME, PRV.RDB$USER
{
2005-10-24 13:56:00 +02:00
fb_utils::exact_name_limit(PRV.RDB$USER, sizeof(PRV.RDB$USER));
fb_utils::exact_name_limit(PRV.RDB$FIELD_NAME, sizeof(PRV.RDB$FIELD_NAME));
2001-05-23 15:26:42 +02:00
2009-11-25 04:58:43 +01:00
// create a control break on field_name,user
2001-05-23 15:26:42 +02:00
if (user != PRV.RDB$USER || field_name != PRV.RDB$FIELD_NAME)
2001-05-23 15:26:42 +02:00
{
2009-11-25 04:58:43 +01:00
// flush out information for old user
2001-05-23 15:26:42 +02:00
if (user.length())
2001-05-23 15:26:42 +02:00
{
if (user != "PUBLIC")
2001-05-23 15:26:42 +02:00
{
const SecurityClass::flags_t field_priv =
public_priv | priv | squeeze_acl(field_acl, user, user_type);
grant_user(field_acl, user, user_type, field_priv);
2008-07-16 03:39:12 +02:00
const SecurityClass::flags_t relation_priv =
public_priv | priv | squeeze_acl(relation_acl, user, user_type);
grant_user(relation_acl, user, user_type, relation_priv);
2001-05-23 15:26:42 +02:00
}
else
{
field_public = field_public | public_priv | priv;
2001-05-23 15:26:42 +02:00
}
}
2009-11-25 04:58:43 +01:00
// initialize for new user
2001-05-23 15:26:42 +02:00
priv = 0;
user = PRV.RDB$USER;
2001-05-23 15:26:42 +02:00
user_type = PRV.RDB$USER_TYPE;
}
2009-11-25 04:58:43 +01:00
// create a control break on field_name
2001-05-23 15:26:42 +02:00
2009-11-25 09:38:52 +01:00
if (field_name != PRV.RDB$FIELD_NAME)
{
2009-11-25 04:58:43 +01:00
// finish off the last field, adding a wildcard at end, giving PUBLIC
// all privileges available at the table level as well as those
// granted at the field level
2001-05-23 15:26:42 +02:00
2009-11-25 09:38:52 +01:00
if (field_name.length())
{
2001-05-23 15:26:42 +02:00
aggregate_public |= field_public;
finish_security_class(field_acl, (field_public | public_priv));
save_security_class(tdbb, s_class, field_acl, transaction);
2001-05-23 15:26:42 +02:00
}
2009-11-25 04:58:43 +01:00
// initialize for new field
2001-05-23 15:26:42 +02:00
field_name = PRV.RDB$FIELD_NAME;
s_class = FLD.RDB$SECURITY_CLASS;
2005-10-28 08:18:17 +02:00
if (FLD.RDB$SECURITY_CLASS.NULL || s_class.length() == 0)
2005-10-24 13:56:00 +02:00
{
bool unique = false;
2002-07-01 17:46:07 +02:00
FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
2005-10-24 13:56:00 +02:00
RFR IN RDB$RELATION_FIELDS WITH
RFR.RDB$RELATION_NAME EQ FLD.RDB$RELATION_NAME
AND RFR.RDB$FIELD_NAME EQ FLD.RDB$FIELD_NAME
{
2005-10-24 13:56:00 +02:00
MODIFY RFR
while (!unique)
{
2009-06-04 08:17:04 +02:00
sprintf(RFR.RDB$SECURITY_CLASS, "%s%" SQUADFORMAT, SQL_FLD_SECCLASS_PREFIX,
2014-04-02 04:22:13 +02:00
DPM_gen_id(tdbb, MET_lookup_generator(tdbb, SQL_SECCLASS_GENERATOR),
2008-12-18 11:57:12 +01:00
false, 1));
2005-10-24 13:56:00 +02:00
unique = true;
FOR (REQUEST_HANDLE request3)
2008-12-18 11:57:12 +01:00
RFR2 IN RDB$RELATION_FIELDS
2005-10-24 13:56:00 +02:00
WITH RFR2.RDB$SECURITY_CLASS = RFR.RDB$SECURITY_CLASS
{
2005-10-24 13:56:00 +02:00
unique = false;
}
END_FOR
2005-10-24 13:56:00 +02:00
}
RFR.RDB$SECURITY_CLASS.NULL = FALSE;
s_class = RFR.RDB$SECURITY_CLASS;
END_MODIFY
}
END_FOR
2001-05-23 15:26:42 +02:00
}
2001-05-23 15:26:42 +02:00
field_public = 0;
2009-11-25 04:58:43 +01:00
// restart a security class at the end of the relation-level privs
field_acl.assign(acl_start);
2001-05-23 15:26:42 +02:00
}
priv |= trans_sql_priv(PRV.RDB$PRIVILEGE);
}
END_FOR
2001-05-23 15:26:42 +02:00
2009-11-25 04:58:43 +01:00
// flush out the last user's info
2001-05-23 15:26:42 +02:00
if (user.length())
2001-05-23 15:26:42 +02:00
{
if (user != "PUBLIC")
2001-05-23 15:26:42 +02:00
{
const SecurityClass::flags_t field_priv =
public_priv | priv | squeeze_acl(field_acl, user, user_type);
grant_user(field_acl, user, user_type, field_priv);
2008-07-16 03:39:12 +02:00
const SecurityClass::flags_t relation_priv =
public_priv | priv | squeeze_acl(relation_acl, user, user_type);
grant_user(relation_acl, user, user_type, relation_priv);
2001-05-23 15:26:42 +02:00
}
else
{
field_public = field_public | public_priv | priv;
2001-05-23 15:26:42 +02:00
}
}
2009-11-25 04:58:43 +01:00
// flush out the last field's info, and schedule a format update
2001-05-23 15:26:42 +02:00
if (field_name.length())
2001-05-23 15:26:42 +02:00
{
aggregate_public |= field_public;
finish_security_class(field_acl, (field_public | public_priv));
save_security_class(tdbb, s_class, field_acl, transaction);
2008-12-18 11:57:12 +01:00
dsc desc;
2001-05-23 15:26:42 +02:00
desc.dsc_dtype = dtype_text;
desc.dsc_sub_type = 0;
desc.dsc_scale = 0;
desc.dsc_ttype() = ttype_metadata;
2001-05-23 15:26:42 +02:00
desc.dsc_address = (UCHAR *) relation_name;
2014-07-17 20:48:46 +02:00
desc.dsc_length = static_cast<USHORT>(strlen(relation_name));
DFW_post_work(transaction, dfw_update_format, &desc, 0);
2001-05-23 15:26:42 +02:00
}
return aggregate_public;
}
2008-07-16 03:39:12 +02:00
static void save_security_class(thread_db* tdbb,
const Firebird::MetaName& s_class,
const Acl& acl,
jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s a v e _ s e c u r i t y _ c l a s s
*
**************************************
*
* Functional description
* Store or update the named security class.
*
**************************************/
SET_TDBB(tdbb);
bid blob_id;
blb* blob = blb::create(tdbb, transaction, &blob_id);
size_t length = acl.getCount();
2008-07-15 08:24:04 +02:00
const UCHAR* buffer = acl.begin();
while (length)
{
2008-07-15 08:24:04 +02:00
const size_t step = length > ACL_BLOB_BUFFER_SIZE ? ACL_BLOB_BUFFER_SIZE : length;
blob->BLB_put_segment(tdbb, buffer, static_cast<USHORT>(step));
length -= step;
buffer += step;
}
blob->BLB_close(tdbb);
2001-05-23 15:26:42 +02:00
AutoCacheRequest request(tdbb, irq_grant3, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
bool found = false;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
2001-05-23 15:26:42 +02:00
CLS IN RDB$SECURITY_CLASSES
WITH CLS.RDB$SECURITY_CLASS EQ s_class.c_str()
{
2003-12-31 06:36:12 +01:00
found = true;
2001-05-23 15:26:42 +02:00
MODIFY CLS
CLS.RDB$ACL = blob_id;
END_MODIFY
}
END_FOR
2001-05-23 15:26:42 +02:00
2009-11-25 09:38:52 +01:00
if (!found)
{
request.reset(tdbb, irq_grant4, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
2001-05-23 15:26:42 +02:00
CLS IN RDB$SECURITY_CLASSES
{
2008-12-18 11:57:12 +01:00
jrd_vtof(s_class.c_str(), CLS.RDB$SECURITY_CLASS, sizeof(CLS.RDB$SECURITY_CLASS));
2001-05-23 15:26:42 +02:00
CLS.RDB$ACL = blob_id;
}
END_STORE
2001-05-23 15:26:42 +02:00
}
}
static SecurityClass::flags_t trans_sql_priv(const TEXT* privileges)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* t r a n s _ s q l _ p r i v
*
**************************************
*
* Functional description
* Map a SQL privilege letter into an internal privilege bit.
*
**************************************/
SecurityClass::flags_t priv = 0;
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
switch (UPPER7(privileges[0]))
{
2001-05-23 15:26:42 +02:00
case 'S':
priv |= SCL_select;
2001-05-23 15:26:42 +02:00
break;
case 'I':
priv |= SCL_insert;
2001-05-23 15:26:42 +02:00
break;
case 'U':
priv |= SCL_update;
2001-05-23 15:26:42 +02:00
break;
case 'D':
priv |= SCL_delete;
2001-05-23 15:26:42 +02:00
break;
case 'R':
priv |= SCL_references;
2001-05-23 15:26:42 +02:00
break;
case 'X':
priv |= SCL_execute;
break;
case 'G':
priv |= SCL_usage;
break;
case 'C':
priv |= SCL_create;
break;
case 'L':
priv |= SCL_alter;
break;
case 'O':
priv |= SCL_drop;
break;
2001-05-23 15:26:42 +02:00
}
return priv;
}
2008-12-20 09:12:19 +01:00
static SecurityClass::flags_t squeeze_acl(Acl& acl, const Firebird::MetaName& user, SSHORT user_type)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s q u e e z e _ a c l
*
**************************************
*
* Functional description
* Walk an access control list looking for a hit. If a hit
* is found, return privileges and squeeze out that acl-element.
* The caller will use the returned privilege to insert a new
* privilege for the input user.
*
**************************************/
2009-02-21 23:50:48 +01:00
UCHAR* dup_acl = NULL;
SecurityClass::flags_t privilege = 0;
UCHAR c;
2001-05-23 15:26:42 +02:00
2009-11-25 04:58:43 +01:00
// Make sure that this half-finished acl looks good enough to process.
acl.push(0);
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
UCHAR* a = acl.begin();
if (*a++ != ACL_version)
2009-11-25 04:58:43 +01:00
BUGCHECK(160); // msg 160 wrong ACL version
2001-05-23 15:26:42 +02:00
bool hit = false;
2008-12-18 11:57:12 +01:00
while ( (c = *a++) )
2009-01-20 09:33:59 +01:00
switch (c)
{
2001-05-23 15:26:42 +02:00
case ACL_id_list:
dup_acl = a - 1;
hit = true;
2009-01-20 09:33:59 +01:00
while ( (c = *a++) )
{
switch (c)
{
2001-05-23 15:26:42 +02:00
case id_person:
if (user_type != obj_user)
hit = false;
if (check_string(a, user))
hit = false;
2001-05-23 15:26:42 +02:00
break;
case id_sql_role:
if (user_type != obj_sql_role)
hit = false;
if (check_string(a, user))
hit = false;
2001-05-23 15:26:42 +02:00
break;
case id_view:
if (user_type != obj_view)
hit = false;
if (check_string(a, user))
hit = false;
2001-05-23 15:26:42 +02:00
break;
case id_procedure:
if (user_type != obj_procedure)
hit = false;
if (check_string(a, user))
hit = false;
2001-05-23 15:26:42 +02:00
break;
2009-12-25 13:58:06 +01:00
case id_function:
if (user_type != obj_udf)
hit = false;
if (check_string(a, user))
hit = false;
break;
2001-05-23 15:26:42 +02:00
case id_trigger:
if (user_type != obj_trigger)
hit = false;
if (check_string(a, user))
hit = false;
2001-05-23 15:26:42 +02:00
break;
case id_project:
case id_organization:
hit = false;
// CVC: What's the idea of calling a function whose only
// result is boolean without checking it?
check_string(a, user);
2001-05-23 15:26:42 +02:00
break;
case id_views:
hit = false;
2001-05-23 15:26:42 +02:00
break;
case id_node:
case id_user:
{
2008-01-16 10:07:24 +01:00
hit = false;
// Seems strange with the same increment just after the switch.
a += *a + 1;
}
2008-01-16 10:07:24 +01:00
break;
2001-05-23 15:26:42 +02:00
case id_group:
if (user_type != obj_user_group)
hit = false;
if (check_string(a, user))
hit = false;
2001-05-23 15:26:42 +02:00
break;
2016-05-31 19:07:08 +02:00
case id_privilege:
if (user_type != obj_privilege)
hit = false;
if (check_string(a, user))
hit = false;
break;
2001-05-23 15:26:42 +02:00
default:
2009-11-25 04:58:43 +01:00
BUGCHECK(293); // bad ACL
2001-05-23 15:26:42 +02:00
}
a += *a + 1;
2001-05-23 15:26:42 +02:00
}
break;
case ACL_priv_list:
2009-01-20 09:33:59 +01:00
if (hit)
{
while ( (c = *a++) )
2009-02-21 23:50:48 +01:00
{
2009-01-20 09:33:59 +01:00
switch (c)
{
2001-05-23 15:26:42 +02:00
case priv_control:
privilege |= SCL_control;
break;
case priv_select:
privilege |= SCL_select;
2001-05-23 15:26:42 +02:00
break;
case priv_insert:
privilege |= SCL_insert;
break;
case priv_delete:
privilege |= SCL_delete;
2001-05-23 15:26:42 +02:00
break;
case priv_references:
privilege |= SCL_references;
2001-05-23 15:26:42 +02:00
break;
case priv_update:
privilege |= SCL_update;
2001-05-23 15:26:42 +02:00
break;
case priv_drop:
privilege |= SCL_drop;
2001-05-23 15:26:42 +02:00
break;
case priv_alter:
privilege |= SCL_alter;
2001-05-23 15:26:42 +02:00
break;
case priv_execute:
privilege |= SCL_execute;
2001-05-23 15:26:42 +02:00
break;
case priv_usage:
privilege |= SCL_usage;
2001-05-23 15:26:42 +02:00
break;
case priv_write:
// unused, but supported for backward compatibility
privilege |= SCL_insert | SCL_update | SCL_delete;
2001-05-23 15:26:42 +02:00
break;
case priv_grant:
// unused
2001-05-23 15:26:42 +02:00
break;
default:
2009-11-25 04:58:43 +01:00
BUGCHECK(293); // bad ACL
2001-05-23 15:26:42 +02:00
}
2009-02-21 23:50:48 +01:00
}
2009-11-25 04:58:43 +01:00
// Squeeze out duplicate acl element.
2009-02-21 23:50:48 +01:00
fb_assert(dup_acl);
acl.remove(dup_acl, a);
a = dup_acl;
2001-05-23 15:26:42 +02:00
}
else
while (*a++);
2001-05-23 15:26:42 +02:00
break;
default:
2009-11-25 04:58:43 +01:00
BUGCHECK(293); // bad ACL
2001-05-23 15:26:42 +02:00
}
// remove added extra '\0' byte
acl.pop();
2001-05-23 15:26:42 +02:00
return privilege;
}
static bool check_string(const UCHAR* acl, const Firebird::MetaName& name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c h e c k _ s t r i n g
*
**************************************
*
* Functional description
* Check a string against an acl string. If they don't match,
* return true.
2001-05-23 15:26:42 +02:00
*
**************************************/
2009-11-25 04:58:43 +01:00
// JPN: Since Kanji User names are not allowed, No need to fix this UPPER loop.
2001-05-23 15:26:42 +02:00
USHORT l = *acl++;
const TEXT* string = name.c_str();
if (l)
{
2008-12-18 11:57:12 +01:00
do
{
const UCHAR c1 = *acl++;
const TEXT c2 = *string++;
2001-05-23 15:26:42 +02:00
if (UPPER7(c1) != UPPER7(c2))
{
return true;
}
2001-05-23 15:26:42 +02:00
} while (--l);
}
2001-05-23 15:26:42 +02:00
return (*string && *string != ' ') ? true : false;
2001-05-23 15:26:42 +02:00
}