⭐ openGauss database source code analysis series articles -- object permission management ⭐

❤️ Hello, I'm Gauss squirrel club. Welcome to study~ ❤️ ‍

Described in the previous article "9.3 role management" In this chapter, we introduce the wonderful contents related to "9.4 object permission management" in Chapter 9 security management source code analysis.

9.4 object permission management

Permission management is an important part of security management. openGauss permission management is based on access control list (ACL).

9.4.1 authority management

1. Access control list

Access control list is the basis of realizing database object permission management. Each object has ACL to store all authorization information of the object. When a user accesses an object, only the user who is in the ACL of the object and has the required permissions can access the object.
Each ACL is a linked list composed of one or more aclitems. Each AclItem is composed of three parts: authorizer, authorizee and permission bit, which records the users who can operate on the object and their permissions.
The code of the data structure AclItem is as follows:

typedef struct AclItem {
    Oid ai_grantee;     /* OID of authorized person */
    Oid ai_grantor;     /* OID of authorizer */
    AclMode ai_privs;  /* Permission bit: 32 bits */
} AclItem;

Where AI_ The PRIVS field is of type AclMode. AclMode is a 32-bit bit. The upper 16 bits are permission option bits. When the bit value is 1, it indicates AI in AclItem_ The user corresponding to grant has the authorization permission for the corresponding operation of this object. Otherwise, it means that the user has no authorization permission; The lower 16 bits are the operation permission bits. When the bit value is 1, it indicates AI in AclItem_ The user corresponding to grant has the corresponding operation permission of this object. Otherwise, it means that the user does not have the corresponding permission. In the structure bitmap 9-18 of AclMode, Grant Option records the permission grant or delegation of each permission bit. The lower 16 bits record the authorization of each permission. When the authorization statement uses ALL, it indicates ALL permissions of the object.

Figure 9-18 structure diagram of opengauss aclmode

openGauss records the permissions to execute DML class operations and DDL class operations in two AclMode structures respectively, and distinguishes them with the value of bit 15, so as to realize that for each database object, the same authorizer and authorized person correspond to two different aclmodes, representing recording DML class operation permissions and DDL class operation permissions respectively. The implementation method is shown in Figure 9-19 and figure 9-20.

Figure 9-19 AclMode structure of opengauss recording DML class operation permissions

Figure 9-20 AclMode structure of opengauss recording DDL class operation permissions

The permissions represented by each permission parameter are shown in table 9-4.
Table 9-4 permission parameters

parameter

Object permissions

parameter

Object permissions

a

INSERT

T

TEMPORARY

r

SELECT

c

CONNECT

w

UPDATE

p

COMPUTE

d

DELETE

R

READ

D

TRUNCATE

W

WRITE

x

REFERENCES

A

ALTER

t

TRIGGER

P

DROP

X

EXECUTE

m

COMMENT

U

USAGE

i

INDEX

C

CREATE

v

VACUUM

2. Object permission management

Database object permission management mainly grants or reclaims the permissions of one or more roles on objects by using the SQL command "GRANT/REVOKE". The "GRANT/REVOKE" command is implemented by the function ExecuteGrantStmt, which has only one parameter of GrantStmt type. The basic execution process is shown in Figure 9-21.

Figure 9-21 execution flow of function ExecuteGrantStmt

The data structure GrantStmt definition code is as follows:

typedef struct GrantStmt {
    NodeTag type;
    bool is_grant;            /* true = Authorization, false = recycle */
    GrantTargetType targtype;  /*  Type of operation target  */
    GrantObjectType objtype;  /*  Types of operated objects: tables, databases, schemas, functions, etc  */
    List* objects;            /*  Collection of manipulated objects  */
    List* privileges;          /*  List of permissions to operate  */
    List* grantees;           /*  Collection of authorized persons  */
    bool grant_option;       /*  true = Re grant permissions  */
    DropBehavior behavior;   /*  Behavior of reclaiming permissions  */
} GrantStmt;

The function ExecuteGrantStmt first converts the GrantStmt structure to the InternalGrant structure, and converts the permission list to the internal AclMode representation. When the value of privileges is NIL, it means that all permissions are granted or recycled. At this time, set all of InternalGrant_ The PRIVS field is true and the privileges field is ACL_NO_RIGHTS.
The code of the data structure InternalGrant is as follows:

typedef struct InternalGrant {
    bool is_grant;            /*  true=Authorization, false = recycle  */
    GrantObjectType objtype;  /*  Types of operated objects: tables, databases, schemas, functions, etc  */
    List* objects;            /*  Collection of manipulated objects  */
    bool all_privs;           /*  Grant or recycle all permissions  */
AclMode privileges;      /*  AclMode The permissions corresponding to DML class operations expressed in the form  */
AclMode ddl_privileges;  /*  AclMode The permission corresponding to the DDL class operation expressed in form  */
List* col_privs;          /*  Permissions corresponding to DML class operations performed on columns  */
List* col_ddl_privs;      /*  The permission corresponding to the DDL class operation performed on the column  */
    List* grantees;          /*  Collection of authorized persons  */
    bool grant_option;      /*  true=Re grant permissions  */
    DropBehavior behavior; /*  Behavior of reclaiming permissions  */
} InternalGrant;

Function ExecuteGrantStmt after the structural transfer is completed, the function ExecGrantStmt_ is called. OIDs, call the permission management function of the corresponding object according to the object type. Next, taking the permission management process of table object as an example, the algorithm of permission management is introduced. Function ExecGrant_Relation is used to process the granting or recycling of table object permissions. The input parameter is a variable of InternalGrant type, which stores the operation object information, authorized person information and permission information of the granting or recycling operation. Function execgrant_ The processing flow of relation is shown in Figure 9-22.

Figure 9-22 function execgrant_ Processing flow of relation

The processing flow of this function is:
(1) From system table pg_class. If there is no old ACL, create a new ACL and call the function acldefault to assign the default permission information to the ACL. According to different objects, the initial default permissions contain some permissions that can be given to PUBLIC. If an old ACL exists, the old ACL is stored as a copy.
(2) Call select_best_grantor function to obtain the authorization permission available of the authorizer on the operation object_ goptions; Set the parameter available_ Gooptions pass in function restrict_and_check_grant, combined with the operation permissions given in the SQL command, calculate the actual permissions to be granted or recycled.
(3) Call merge_ acl_ with_ The grant function generates a new ACL. If the permission is granted, add the permission to be granted to the old ACL; If the permission is recycled, the permission to be recycled is deleted from the old ACL.
(4) Update the new ACL to the system table pg_class corresponds to the ACL field of the tuple to complete the authorization or recycling process.
The relevant codes of this function are as follows:

static void ExecGrant_Relation(InternalGrant* istmt)
{
    . . .
/*  Loop through each table object   */
    foreach (cell, istmt->objects) {
        . . .
/*  Judge whether the table object to be operated exists. If it does not exist, an error will be prompted   */
        tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
        if (!HeapTupleIsValid(tuple))
            ereport(
                ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", relOid)));
        pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
. . .
        /*  System table pg_class. If there is no old ACL, create a new ACL. If there is an old ACL, store the old ACL as a copy   */
        ownerId = pg_class_tuple->relowner;
        aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, &isNull);
        if (isNull) {
            switch (pg_class_tuple->relkind) {
                case RELKIND_SEQUENCE:
                    old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
                    break;
                default:
                    old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
                    break;
            }
            noldmembers = 0;
            oldmembers = NULL;
        } else {
            old_acl = DatumGetAclPCopy(aclDatum);
            noldmembers = aclmembers(old_acl, &oldmembers);
        }
        old_rel_acl = aclcopy(old_acl);

        /*  Processing table level permissions   */
        if (this_privileges != ACL_NO_RIGHTS) {
            AclMode avail_goptions;
            Acl* new_acl = NULL;
            Oid grantorId;
            HeapTuple newtuple = NULL;
            Datum values[Natts_pg_class];
            bool nulls[Natts_pg_class] = {false};
            bool replaces[Natts_pg_class] = {false};
            int nnewmembers;
            Oid* newmembers = NULL;
            AclObjectKind aclkind;

            /*  Obtain the grantorId of the authorizer and the authorization permission available of the authorizer on the operation object_ goptions   */
            select_best_grantor(GetUserId(), this_privileges, old_acl, ownerId, &grantorId, &avail_goptions);

            switch (pg_class_tuple->relkind) {
                case RELKIND_SEQUENCE:
                    aclkind = ACL_KIND_SEQUENCE;
                    break;
                default:
                    aclkind = ACL_KIND_CLASS;
                    break;
            }

            /*  Combined parameter avail_ According to the operation permissions given in gooptions and SQL commands, calculate the actual permissions to be granted or recycled   */
            this_privileges = restrict_and_check_grant(istmt->is_grant,
                avail_goptions,
                istmt->all_privs,
                this_privileges,
                relOid,
                grantorId,
                aclkind,
                NameStr(pg_class_tuple->relname),
                0,
                NULL);

            /*  Generate a new ACL and update it to the system table pg_class corresponds to the ACL field of the tuple   */
            new_acl = merge_acl_with_grant(old_acl,
                istmt->is_grant,
                istmt->grant_option,
                istmt->behavior,
                istmt->grantees,
                this_privileges,
                grantorId,
                ownerId);
. . .
            replaces[Anum_pg_class_relacl - 1] = true;
            values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);

            newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);

            simple_heap_update(relation, &newtuple->t_self, newtuple);
. . .
        }

        /*  Execgrant is called if column level authorization or recycling exists_ Attribute function processing  */
. . .
        if (have_col_privileges) {
            AttrNumber i;

            for (i = 0; i < num_col_privileges; i++) {
                if (col_privileges[i] == ACL_NO_RIGHTS)
                    continue;
                ExecGrant_Attribute(istmt,
                    relOid,
                    NameStr(pg_class_tuple->relname),
                    i + FirstLowInvalidHeapAttributeNumber,
                    ownerId,
                    col_privileges[i],
                    attRelation,
                    old_rel_acl);
            }
        }
    . . .
    }

    heap_close(attRelation, RowExclusiveLock);
    heap_close(relation, RowExclusiveLock);
}

9.4.2 authority check

When a user accesses a database object, the database will check whether the user has the operation permission of the object. Usually, the owner and superuser of the database object have all the operation permissions of the object, and other ordinary users need to be granted permissions to perform the corresponding operations. The database checks the user's access rights to the database object by querying the access control list of the database object. The ACL of the database object is saved in the corresponding system table. When the object permission is granted or recycled, the ACL permission bit saved in the system table will be updated. The common database object permission check function, ACL check function, ACL system table and object owner check function are shown in table 9-5.

Table 9-5 database object function correspondence table.

object

Permission check

ACL check

Owner check

System table

table

pg_class_aclcheck

pg_class_aclmask

pg_class_ownercheck

pg_class

column

pg_attribute_aclcheck

pg_attribute_aclmask

NA

pg_attribute

database

pg_database_aclcheck

pg_database_aclmask

pg_database_ownercheck

pg_database

function

pg_proc_aclcheck

pg_proc_aclmask

pg_proc_ownercheck

pg_proc

language

pg_language_aclcheck

pg_language_aclmask

pg_language_ownercheck

pg_language

largeobject

pg_largeobject_aclcheck_snapshot

pg_largeobject_aclmask_snapshot

pg_largeobject_ownercheck

pg_largeobject_metadata

namespace

pg_namespace_aclcheck

pg_namespace_aclmask

pg_namespace_ownercheck

pg_namespace

tablespace

pg_tablespace_aclcheck

pg_tablespace_aclmask

pg_tablespace_ownercheck

pg_tablespace

foreign data wrapper

pg_foreign_data_wrapper_aclcheck

pg_foreign_data_wrapper_aclmask

pg_foreign_data_wrapper_ownercheck

pg_foreign_data_wrapper

foreign server

pg_foreign_server_aclcheck

pg_foreign_server_aclmask

pg_foreign_server_ownercheck

pg_foreign_server

type

pg_type_aclcheck

pg_type_aclmask

pg_type_ownercheck

pg_type

The following describes the permission check process by taking the permission check of the table as an example. Table permission check function PG_ class_ The definition code of aclcheck is as follows:
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode, bool check_nodegroup)
{
    if (pg_class_aclmask(table_oid, roleid, mode, ACLMASK_ANY, check_nodegroup) != 0)
        return ACLCHECK_OK;
    else
        return ACLCHECK_NO_PRIV;
}

pg_ class_ The aclcheck function has four input parameters, including table_oid indicates the table to be checked, roleid indicates the user or role to be checked, and mode indicates the permission to be checked. This permission can be one permission or a combination of multiple permissions. The fourth parameter is check_nodegroup is used to indicate whether to check nodegroup logical cluster permissions. If this parameter is not assigned during call, it defaults to true. The return value of the function is the enumeration type AclResult. If the check result has permission, aclcheck is returned_ OK, aclcheck will be returned if there is no permission_ NO_ PRIV.

pg_class_ The aclcheck function calls pg_class_ The aclmask function implements object permission checking. pg_class_ The aclmask function has five parameters, of which the fourth parameter how is the AclMaskHow enumeration type, including ACLMASK_ALL and ACLMASK_ANY has two values; ACLMASK_ALL indicates that all permissions in the permission mode to be checked must be met, ACLMASK_ANY means that only one permission in the permission mode to be checked needs to be met. pg_class_ The remaining four parameters of the aclmask function are table_oid, roleid, mode, and check_nodegroup, directly by pg_class_ The aclcheck function is passed in. pg_class_aclmask function from PG_ Get the ACL permission information from the ACL system table, call the aclmask function to complete the permission bit verification, and return the permission check result through the AclMode data type.

Thank you for learning the wonderful content of "9.4 object permission management" in Chapter 9 security management source code analysis. Next, we will start the introduction of "9.5 audit and tracking".
Coming soon.

💜 After passing by, I saw here. Please praise, collect and comment. Thousands of words. Thank you very much 💜

Tags: Database Big Data DBA

Posted on Sun, 17 Oct 2021 22:16:47 -0400 by Warz