/* * PROGRAM: JRD Access Method * MODULE: grant.epp * 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.29 Sean Leyne - Removed obsolete "Netware" port * */ #include "firebird.h" #include #include #include "../jrd/common.h" #include "../jrd/ibase.h" #include "../jrd/jrd.h" #include "../jrd/scl.h" #include "../jrd/acl.h" #include "../jrd/irq.h" #include "../jrd/blb.h" #include "../jrd/btr.h" #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/dyn_proto.h" #include "../jrd/err_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/grant_proto.h" #include "../jrd/jrd_proto.h" #include "../jrd/met_proto.h" #include "../jrd/scl_proto.h" #include "../common/utils_proto.h" #include "../common/classes/array.h" #include "../jrd/constants.h" using namespace Jrd; /* privileges given to the owner of a relation */ const SecurityClass::flags_t OWNER_PRIVS = SCL_control | SCL_read | SCL_write | SCL_delete | SCL_protect; //const SecurityClass::flags_t VIEW_PRIVS = SCL_read | SCL_write | SCL_delete; //const ULONG ACL_BUFFER_SIZE = 4096; inline void CHECK_AND_MOVE(Acl& to, UCHAR from) { to.add(from); } DATABASE DB = STATIC "yachts.lnk"; static void define_default_class(thread_db*, const TEXT*, Firebird::MetaName&, const Acl&, jrd_tra*); #ifdef NOT_USED_OR_REPLACED static void delete_security_class(thread_db*, const TEXT*); static void grant_views(TEXT**, SecurityClass::flags_t); static void purge_default_class(TEXT*, SSHORT); #endif 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); 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&); void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, jrd_tra* transaction) { /************************************** * * G R A N T _ p r i v i l e g e s * ************************************** * * Functional description * Compute access control list from SQL privileges. * This calculation is tricky and involves interaction between * the relation-level and field-level privileges. Do not change * the order of operations lightly. * **************************************/ SET_TDBB(tdbb); bool restrct = false; //Database* dbb = tdbb->getDatabase(); 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); if (s_class.length() == 0) { return; } /* start the acl off by giving the owner all privileges */ Acl acl, default_acl; CHECK_AND_MOVE(acl, ACL_version); grant_user(acl, owner, obj_user, (id == obj_procedure || id == obj_package_header ? SCL_execute | OWNER_PRIVS : OWNER_PRIVS)); /* Pick up any relation-level privileges */ 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); if (id == obj_relation) { /* 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. */ default_acl.assign(acl); const SecurityClass::flags_t aggregate_public = save_field_privileges(tdbb, acl, name.c_str(), owner, public_priv, transaction); /* SQL tables don't need the 'all priviliges to all views' acl anymore. This special acl was only generated for SQL. */ #ifdef NOT_USED_OR_REPLACED /* grant_views (&acl, VIEW_PRIVS); */ #endif /* finish off and store the security class for the relation */ finish_security_class(acl, aggregate_public); save_security_class(tdbb, s_class, acl, transaction); if (acl.getCount() != default_acl.getCount()) /* relation privs were added? */ restrct = true; /* 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 */ if (restrct) { finish_security_class(default_acl, public_priv); define_default_class(tdbb, name.c_str(), default_class, default_acl, transaction); } } else { finish_security_class(acl, public_priv); save_security_class(tdbb, s_class, acl, transaction); } } static void define_default_class(thread_db* tdbb, const TEXT* relation_name, Firebird::MetaName& default_class, const Acl& acl, jrd_tra* transaction) { /************************************** * * 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); Database* dbb = tdbb->getDatabase(); if (default_class.length() == 0) { default_class.printf("%s%" SQUADFORMAT, DEFAULT_CLASS, DPM_gen_id(tdbb, MET_lookup_generator(tdbb, DEFAULT_CLASS), false, 1)); jrd_req* request = CMP_find_request(tdbb, irq_grant7, IRQ_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ relation_name if (!REQUEST(irq_grant7)) REQUEST(irq_grant7) = request; MODIFY REL USING REL.RDB$DEFAULT_CLASS.NULL = FALSE; jrd_vtof(default_class.c_str(), REL.RDB$DEFAULT_CLASS, sizeof(REL.RDB$DEFAULT_CLASS)); END_MODIFY; END_FOR; if (!REQUEST(irq_grant7)) REQUEST(irq_grant7) = request; } save_security_class(tdbb, default_class, acl, transaction); dsc desc; desc.dsc_dtype = dtype_text; desc.dsc_sub_type = 0; desc.dsc_scale = 0; desc.dsc_ttype() = ttype_metadata; desc.dsc_address = (UCHAR *) relation_name; desc.dsc_length = strlen(relation_name); DFW_post_work(transaction, dfw_scan_relation, &desc, 0); } static void finish_security_class(Acl& acl, SecurityClass::flags_t public_priv) { /************************************** * * 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. * **************************************/ if (public_priv) { CHECK_AND_MOVE(acl, ACL_id_list); SCL_move_priv(public_priv, acl); } CHECK_AND_MOVE(acl, ACL_end); } static SecurityClass::flags_t get_public_privs(thread_db* tdbb, const TEXT* object_name, SSHORT obj_type) { /************************************** * * 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); Database* dbb = tdbb->getDatabase(); SecurityClass::flags_t public_priv = 0; jrd_req* request = CMP_find_request(tdbb, irq_grant5, IRQ_REQUESTS); 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 PRV.RDB$FIELD_NAME MISSING if (!REQUEST(irq_grant5)) REQUEST(irq_grant5) = request; public_priv |= trans_sql_priv(PRV.RDB$PRIVILEGE); END_FOR; if (!REQUEST(irq_grant5)) REQUEST(irq_grant5) = request; return public_priv; } static void get_object_info(thread_db* tdbb, const TEXT* object_name, SSHORT obj_type, Firebird::MetaName& owner, Firebird::MetaName& s_class, Firebird::MetaName& default_class, bool& view) { /************************************** * * 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); Database* dbb = tdbb->getDatabase(); owner = s_class = default_class = ""; view = false; if (obj_type == obj_relation) { jrd_req* request = CMP_find_request(tdbb, irq_grant1, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ object_name if (!REQUEST(irq_grant1)) REQUEST(irq_grant1) = request; 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; if (!REQUEST(irq_grant1)) REQUEST(irq_grant1) = request; } else if (obj_type == obj_package_header) { jrd_req* request = CMP_find_request(tdbb, irq_grant10, IRQ_REQUESTS); FOR (REQUEST_HANDLE request) PKG IN RDB$PACKAGES WITH PKG.RDB$PACKAGE_NAME EQ object_name if (!REQUEST(irq_grant10)) REQUEST(irq_grant10) = request; s_class = PKG.RDB$SECURITY_CLASS; default_class = ""; owner = PKG.RDB$OWNER_NAME; view = false; END_FOR; if (!REQUEST(irq_grant10)) REQUEST(irq_grant10) = request; } else { jrd_req* request = CMP_find_request(tdbb, irq_grant9, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) REL IN RDB$PROCEDURES WITH REL.RDB$PROCEDURE_NAME EQ object_name AND REL.RDB$PACKAGE_NAME MISSING if (!REQUEST(irq_grant9)) REQUEST(irq_grant9) = request; s_class = REL.RDB$SECURITY_CLASS; default_class = ""; owner = REL.RDB$OWNER_NAME; view = false; END_FOR; if (!REQUEST(irq_grant9)) REQUEST(irq_grant9) = request; } } 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) { /************************************** * * g e t _ u s e r _ p r i v s * ************************************** * * Functional description * Get privileges for a particular object. * **************************************/ SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); Firebird::MetaName user; SSHORT user_type = -2; SecurityClass::flags_t priv = 0; jrd_req* request = CMP_find_request(tdbb, irq_grant2, IRQ_REQUESTS); 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$FIELD_NAME MISSING SORTED BY PRV.RDB$USER, PRV.RDB$USER_TYPE if (!REQUEST(irq_grant2)) REQUEST(irq_grant2) = request; fb_utils::exact_name_limit(PRV.RDB$USER, sizeof(PRV.RDB$USER)); if (user != PRV.RDB$USER || user_type != PRV.RDB$USER_TYPE) { if (user.length()) { grant_user(acl, user, user_type, priv); } user_type = PRV.RDB$USER_TYPE; if (user_type == obj_user) { priv = public_priv; } else { priv = 0; } user = PRV.RDB$USER; } priv |= trans_sql_priv(PRV.RDB$PRIVILEGE); END_FOR; if (!REQUEST(irq_grant2)) REQUEST(irq_grant2) = request; if (user.length()) grant_user(acl, user, user_type, priv); } static void grant_user(Acl& acl, const Firebird::MetaName& user, SSHORT user_type, SecurityClass::flags_t privs) { /************************************** * * g r a n t _ u s e r * ************************************** * * Functional description * Grant privileges to a particular user. * **************************************/ CHECK_AND_MOVE(acl, ACL_id_list); switch (user_type) { case obj_user_group: CHECK_AND_MOVE(acl, id_group); break; case obj_sql_role: CHECK_AND_MOVE(acl, id_sql_role); break; case obj_user: CHECK_AND_MOVE(acl, id_person); break; case obj_package_header: CHECK_AND_MOVE(acl, id_package); break; case obj_procedure: CHECK_AND_MOVE(acl, id_procedure); break; case obj_trigger: CHECK_AND_MOVE(acl, id_trigger); break; case obj_view: CHECK_AND_MOVE(acl, id_view); break; default: BUGCHECK(292); /* Illegal user_type */ } const UCHAR length = user.length(); CHECK_AND_MOVE(acl, length); if (length) { acl.add(reinterpret_cast(user.c_str()), length); } SCL_move_priv(privs, acl); } #ifdef NOT_USED_OR_REPLACED static void delete_security_class( thread_db* tdbb, const TEXT* s_class) { /************************************** * * d e l e t e _ s e c u r i t y _ c l a s s * ************************************** * * Functional description * Delete a security class. * **************************************/ SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); jrd_req* handle = NULL; FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) CLS IN RDB$SECURITY_CLASSES WITH CLS.RDB$SECURITY_CLASS EQ s_class ERASE CLS; END_FOR; CMP_release(tdbb, handle); } static void grant_views(Acl& acl, SecurityClass::flags_t privs) { /************************************** * * g r a n t _ v i e w s * ************************************** * * Functional description * Grant privileges to all views. * **************************************/ CHECK_AND_MOVE(acl, ACL_id_list); CHECK_AND_MOVE(acl, id_views); CHECK_AND_MOVE(acl, 0); SCL_move_priv(privs, acl); } static void purge_default_class( TEXT* object_name, SSHORT obj_type) { /************************************** * * p u r g e _ d e f a u l t _ c l a s s * ************************************** * * Functional description * Get rid of any previously defined * default security class for this relation. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); jrd_req* request = CMP_find_request(tdbb, irq_grant8, IRQ_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ object_name AND REL.RDB$DEFAULT_CLASS NOT MISSING if (!REQUEST(irq_grant8)) REQUEST(irq_grant8) = request; fb_utils::exact_name_limit(REL.RDB$DEFAULT_CLASS, sizeof(REL.RDB$DEFAULT_CLASS)); delete_security_class(tdbb, REL.RDB$DEFAULT_CLASS); MODIFY REL USING REL.RDB$DEFAULT_CLASS.NULL = TRUE; END_MODIFY; END_FOR; if (!REQUEST(irq_grant8)) REQUEST(irq_grant8) = request; dsc desc; desc.dsc_dtype = dtype_text; desc.dsc_sub_type = 0; desc.dsc_scale = 0; desc.dsc_ttype() = ttype_metadata; desc.dsc_address = (UCHAR *) object_name; desc.dsc_length = strlen(object_name); DFW_post_work(transaction, dfw_scan_relation, &desc, 0); } #endif // NOT_USED_OR_REPLACED static SecurityClass::flags_t save_field_privileges(thread_db* tdbb, Acl& relation_acl, const TEXT* relation_name, const Firebird::MetaName& owner, SecurityClass::flags_t public_priv, jrd_tra* transaction) { /************************************** * * 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. * Conversely, field-level privileges must be added to the relation * security class to be effective. * **************************************/ SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); Acl field_acl(relation_acl); const Acl acl_start(relation_acl); Firebird::MetaName field_name, user, s_class; SecurityClass::flags_t aggregate_public = public_priv; SecurityClass::flags_t priv = 0; SecurityClass::flags_t field_public = 0; SSHORT user_type = -1; jrd_req* request = CMP_find_request(tdbb, irq_grant6, IRQ_REQUESTS); jrd_req* request2 = NULL; jrd_req* request3 = NULL; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) 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) SORTED BY PRV.RDB$FIELD_NAME, PRV.RDB$USER if (!REQUEST(irq_grant6)) REQUEST(irq_grant6) = request; 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)); /* create a control break on field_name,user */ if (user != PRV.RDB$USER || field_name != PRV.RDB$FIELD_NAME) { /* flush out information for old user */ if (user.length()) { if (user != "PUBLIC") { 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); 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); } else { field_public = field_public | public_priv | priv; } } /* initialize for new user */ priv = 0; user = PRV.RDB$USER; user_type = PRV.RDB$USER_TYPE; } /* create a control break on field_name */ if (field_name != PRV.RDB$FIELD_NAME) { /* 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 */ if (field_name.length()) { aggregate_public |= field_public; finish_security_class(field_acl, (field_public | public_priv)); save_security_class(tdbb, s_class, field_acl, transaction); } /* initialize for new field */ field_name = PRV.RDB$FIELD_NAME; s_class = FLD.RDB$SECURITY_CLASS; if (FLD.RDB$SECURITY_CLASS.NULL || s_class.length() == 0) { bool unique = false; FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) 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 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; FOR (REQUEST_HANDLE request3) RFR2 IN RDB$RELATION_FIELDS WITH RFR2.RDB$SECURITY_CLASS = RFR.RDB$SECURITY_CLASS unique = false; END_FOR; } RFR.RDB$SECURITY_CLASS.NULL = FALSE; s_class = RFR.RDB$SECURITY_CLASS; END_MODIFY; END_FOR; } field_public = 0; /* restart a security class at the end of the relation-level privs */ field_acl.assign(acl_start); } priv |= trans_sql_priv(PRV.RDB$PRIVILEGE); END_FOR; if (!REQUEST(irq_grant6)) REQUEST(irq_grant6) = request; if (request2) CMP_release(tdbb, request2); if (request3) CMP_release(tdbb, request3); /* flush out the last user's info */ if (user.length()) { if (user != "PUBLIC") { 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); 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); } else { field_public = field_public | public_priv | priv; } } /* flush out the last field's info, and schedule a format update */ if (field_name.length()) { aggregate_public |= field_public; finish_security_class(field_acl, (field_public | public_priv)); save_security_class(tdbb, s_class, field_acl, transaction); dsc desc; desc.dsc_dtype = dtype_text; desc.dsc_sub_type = 0; desc.dsc_scale = 0; desc.dsc_ttype() = ttype_metadata; desc.dsc_address = (UCHAR *) relation_name; desc.dsc_length = strlen(relation_name); DFW_post_work(transaction, dfw_update_format, &desc, 0); } return aggregate_public; } static void save_security_class(thread_db* tdbb, const Firebird::MetaName& s_class, const Acl& acl, jrd_tra* transaction) { /************************************** * * 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); Database* dbb = tdbb->getDatabase(); bid blob_id; blb* blob = BLB_create(tdbb, transaction, &blob_id); size_t length = acl.getCount(); const UCHAR* buffer = acl.begin(); while (length) { const size_t step = length > ACL_BLOB_BUFFER_SIZE ? ACL_BLOB_BUFFER_SIZE : length; BLB_put_segment(tdbb, blob, buffer, static_cast(step)); length -= step; buffer += step; } BLB_close(tdbb, blob); jrd_req* request = CMP_find_request(tdbb, irq_grant3, IRQ_REQUESTS); bool found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) CLS IN RDB$SECURITY_CLASSES WITH CLS.RDB$SECURITY_CLASS EQ s_class.c_str() if (!REQUEST(irq_grant3)) REQUEST(irq_grant3) = request; found = true; MODIFY CLS CLS.RDB$ACL = blob_id; END_MODIFY; END_FOR; if (!REQUEST(irq_grant3)) REQUEST(irq_grant3) = request; if (!found) { request = CMP_find_request(tdbb, irq_grant4, IRQ_REQUESTS); STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) CLS IN RDB$SECURITY_CLASSES jrd_vtof(s_class.c_str(), CLS.RDB$SECURITY_CLASS, sizeof(CLS.RDB$SECURITY_CLASS)); CLS.RDB$ACL = blob_id; END_STORE; if (!REQUEST(irq_grant4)) REQUEST(irq_grant4) = request; } } static SecurityClass::flags_t trans_sql_priv(const TEXT* privileges) { /************************************** * * 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; switch (UPPER7(privileges[0])) { case 'S': priv |= SCL_read; break; case 'I': priv |= SCL_sql_insert; break; case 'U': priv |= SCL_sql_update; break; case 'D': priv |= SCL_sql_delete; break; case 'R': priv |= SCL_sql_references; break; case 'X': priv |= SCL_execute; break; } return priv; } static SecurityClass::flags_t squeeze_acl(Acl& acl, const Firebird::MetaName& user, SSHORT user_type) { /************************************** * * 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. * **************************************/ UCHAR* dup_acl = NULL; SecurityClass::flags_t privilege = 0; UCHAR c; /* Make sure that this half-finished acl looks good enough to process. */ acl.push(0); UCHAR* a = acl.begin(); if (*a++ != ACL_version) BUGCHECK(160); /* msg 160 wrong ACL version */ bool hit = false; while ( (c = *a++) ) switch (c) { case ACL_id_list: dup_acl = a - 1; hit = true; while ( (c = *a++) ) { switch (c) { case id_person: if (user_type != obj_user) hit = false; if (check_string(a, user)) hit = false; break; case id_sql_role: if (user_type != obj_sql_role) hit = false; if (check_string(a, user)) hit = false; break; case id_view: if (user_type != obj_view) hit = false; if (check_string(a, user)) hit = false; break; case id_procedure: if (user_type != obj_procedure) hit = false; if (check_string(a, user)) hit = false; break; case id_trigger: if (user_type != obj_trigger) hit = false; if (check_string(a, user)) hit = false; 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); break; case id_views: hit = false; break; case id_node: case id_user: { hit = false; // Seems strange with the same increment just after the switch. a += *a + 1; } break; case id_group: if (user_type != obj_user_group) hit = false; if (check_string(a, user)) hit = false; break; default: BUGCHECK(293); /* bad ACL */ } a += *a + 1; } break; case ACL_priv_list: if (hit) { while ( (c = *a++) ) { switch (c) { case priv_control: privilege |= SCL_control; break; case priv_read: privilege |= SCL_read; break; case priv_write: privilege |= SCL_write; break; case priv_sql_insert: privilege |= SCL_sql_insert; break; case priv_sql_delete: privilege |= SCL_sql_delete; break; case priv_sql_references: privilege |= SCL_sql_references; break; case priv_sql_update: privilege |= SCL_sql_update; break; case priv_delete: privilege |= SCL_delete; break; case priv_grant: privilege |= SCL_grant; break; case priv_protect: privilege |= SCL_protect; break; case priv_execute: privilege |= SCL_execute; break; default: BUGCHECK(293); /* bad ACL */ } } /* Squeeze out duplicate acl element. */ fb_assert(dup_acl); acl.remove(dup_acl, a); a = dup_acl; } else while (*a++); break; default: BUGCHECK(293); /* bad ACL */ } // remove added extra '\0' byte acl.pop(); return privilege; } static bool check_string(const UCHAR* acl, const Firebird::MetaName& name) { /************************************** * * 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. * **************************************/ /* JPN: Since Kanji User names are not allowed, No need to fix this UPPER loop. */ USHORT l = *acl++; const TEXT* string = name.c_str(); if (l) { do { const UCHAR c1 = *acl++; const TEXT c2 = *string++; if (UPPER7(c1) != UPPER7(c2)) { return true; } } while (--l); } return (*string && *string != ' ') ? true : false; }