📄 acl.c
字号:
*/Acl *acldefault(GrantObjectType objtype, Oid ownerId){ AclMode world_default; AclMode owner_default; Acl *acl; AclItem *aip; switch (objtype) { case ACL_OBJECT_RELATION: world_default = ACL_NO_RIGHTS; owner_default = ACL_ALL_RIGHTS_RELATION; break; case ACL_OBJECT_DATABASE: world_default = ACL_CREATE_TEMP; /* not NO_RIGHTS! */ owner_default = ACL_ALL_RIGHTS_DATABASE; break; case ACL_OBJECT_FUNCTION: /* Grant EXECUTE by default, for now */ world_default = ACL_EXECUTE; owner_default = ACL_ALL_RIGHTS_FUNCTION; break; case ACL_OBJECT_LANGUAGE: /* Grant USAGE by default, for now */ world_default = ACL_USAGE; owner_default = ACL_ALL_RIGHTS_LANGUAGE; break; case ACL_OBJECT_NAMESPACE: world_default = ACL_NO_RIGHTS; owner_default = ACL_ALL_RIGHTS_NAMESPACE; break; case ACL_OBJECT_TABLESPACE: world_default = ACL_NO_RIGHTS; owner_default = ACL_ALL_RIGHTS_TABLESPACE; break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); world_default = ACL_NO_RIGHTS; /* keep compiler quiet */ owner_default = ACL_NO_RIGHTS; break; } acl = allocacl((world_default != ACL_NO_RIGHTS) ? 2 : 1); aip = ACL_DAT(acl); if (world_default != ACL_NO_RIGHTS) { aip->ai_grantee = ACL_ID_PUBLIC; aip->ai_grantor = ownerId; ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS); aip++; } /* * Note that the owner's entry shows all ordinary privileges but no grant * options. This is because his grant options come "from the system" and * not from his own efforts. (The SQL spec says that the owner's rights * come from a "_SYSTEM" authid.) However, we do consider that the * owner's ordinary privileges are self-granted; this lets him revoke * them. We implement the owner's grant options without any explicit * "_SYSTEM"-like ACL entry, by internally special-casing the owner * whereever we are testing grant options. */ aip->ai_grantee = ownerId; aip->ai_grantor = ownerId; ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS); return acl;}/* * Update an ACL array to add or remove specified privileges. * * old_acl: the input ACL array * mod_aip: defines the privileges to be added, removed, or substituted * modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL * ownerId: Oid of object owner * behavior: RESTRICT or CASCADE behavior for recursive removal * * ownerid and behavior are only relevant when the update operation specifies * deletion of grant options. * * The result is a modified copy; the input object is not changed. * * NB: caller is responsible for having detoasted the input ACL, if needed. */Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip, int modechg, Oid ownerId, DropBehavior behavior){ Acl *new_acl = NULL; AclItem *old_aip, *new_aip = NULL; AclMode old_rights, old_goptions, new_rights, new_goptions; int dst, num; /* These checks for null input are probably dead code, but... */ if (!old_acl || ACL_NUM(old_acl) < 0) old_acl = allocacl(0); if (!mod_aip) { new_acl = allocacl(ACL_NUM(old_acl)); memcpy(new_acl, old_acl, ACL_SIZE(old_acl)); return new_acl; } /* If granting grant options, check for circularity */ if (modechg != ACL_MODECHG_DEL && ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS) check_circularity(old_acl, mod_aip, ownerId); num = ACL_NUM(old_acl); old_aip = ACL_DAT(old_acl); /* * Search the ACL for an existing entry for this grantee and grantor. If * one exists, just modify the entry in-place (well, in the same position, * since we actually return a copy); otherwise, insert the new entry at * the end. */ for (dst = 0; dst < num; ++dst) { if (aclitem_match(mod_aip, old_aip + dst)) { /* found a match, so modify existing item */ new_acl = allocacl(num); new_aip = ACL_DAT(new_acl); memcpy(new_acl, old_acl, ACL_SIZE(old_acl)); break; } } if (dst == num) { /* need to append a new item */ new_acl = allocacl(num + 1); new_aip = ACL_DAT(new_acl); memcpy(new_aip, old_aip, num * sizeof(AclItem)); /* initialize the new entry with no permissions */ new_aip[dst].ai_grantee = mod_aip->ai_grantee; new_aip[dst].ai_grantor = mod_aip->ai_grantor; ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst], ACL_NO_RIGHTS, ACL_NO_RIGHTS); num++; /* set num to the size of new_acl */ } old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]); old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]); /* apply the specified permissions change */ switch (modechg) { case ACL_MODECHG_ADD: ACLITEM_SET_RIGHTS(new_aip[dst], old_rights | ACLITEM_GET_RIGHTS(*mod_aip)); break; case ACL_MODECHG_DEL: ACLITEM_SET_RIGHTS(new_aip[dst], old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip)); break; case ACL_MODECHG_EQL: ACLITEM_SET_RIGHTS(new_aip[dst], ACLITEM_GET_RIGHTS(*mod_aip)); break; } new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]); new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]); /* * If the adjusted entry has no permissions, delete it from the list. */ if (new_rights == ACL_NO_RIGHTS) { memmove(new_aip + dst, new_aip + dst + 1, (num - dst - 1) * sizeof(AclItem)); ARR_DIMS(new_acl)[0] = num - 1; ARR_SIZE(new_acl) -= sizeof(AclItem); } /* * Remove abandoned privileges (cascading revoke). Currently we can only * handle this when the grantee is not PUBLIC. */ if ((old_goptions & ~new_goptions) != 0) { Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC); new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee, (old_goptions & ~new_goptions), ownerId, behavior); } return new_acl;}/* * Update an ACL array to reflect a change of owner to the parent object * * old_acl: the input ACL array (must not be NULL) * oldOwnerId: Oid of the old object owner * newOwnerId: Oid of the new object owner * * The result is a modified copy; the input object is not changed. * * NB: caller is responsible for having detoasted the input ACL, if needed. */Acl *aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId){ Acl *new_acl; AclItem *new_aip; AclItem *old_aip; AclItem *dst_aip; AclItem *src_aip; AclItem *targ_aip; bool newpresent = false; int dst, src, targ, num; /* * Make a copy of the given ACL, substituting new owner ID for old * wherever it appears as either grantor or grantee. Also note if the new * owner ID is already present. */ num = ACL_NUM(old_acl); old_aip = ACL_DAT(old_acl); new_acl = allocacl(num); new_aip = ACL_DAT(new_acl); memcpy(new_aip, old_aip, num * sizeof(AclItem)); for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++) { if (dst_aip->ai_grantor == oldOwnerId) dst_aip->ai_grantor = newOwnerId; else if (dst_aip->ai_grantor == newOwnerId) newpresent = true; if (dst_aip->ai_grantee == oldOwnerId) dst_aip->ai_grantee = newOwnerId; else if (dst_aip->ai_grantee == newOwnerId) newpresent = true; } /* * If the old ACL contained any references to the new owner, then we may * now have generated an ACL containing duplicate entries. Find them and * merge them so that there are not duplicates. (This is relatively * expensive since we use a stupid O(N^2) algorithm, but it's unlikely to * be the normal case.) * * To simplify deletion of duplicate entries, we temporarily leave them in * the array but set their privilege masks to zero; when we reach such an * entry it's just skipped. (Thus, a side effect of this code will be to * remove privilege-free entries, should there be any in the input.) dst * is the next output slot, targ is the currently considered input slot * (always >= dst), and src scans entries to the right of targ looking for * duplicates. Once an entry has been emitted to dst it is known * duplicate-free and need not be considered anymore. */ if (newpresent) { dst = 0; for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++) { /* ignore if deleted in an earlier pass */ if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS) continue; /* find and merge any duplicates */ for (src = targ + 1, src_aip = targ_aip + 1; src < num; src++, src_aip++) { if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS) continue; if (aclitem_match(targ_aip, src_aip)) { ACLITEM_SET_RIGHTS(*targ_aip, ACLITEM_GET_RIGHTS(*targ_aip) | ACLITEM_GET_RIGHTS(*src_aip)); /* mark the duplicate deleted */ ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS); } } /* and emit to output */ new_aip[dst] = *targ_aip; dst++; } /* Adjust array size to be 'dst' items */ ARR_DIMS(new_acl)[0] = dst; ARR_SIZE(new_acl) = ACL_N_SIZE(dst); } return new_acl;}/* * When granting grant options, we must disallow attempts to set up circular * chains of grant options. Suppose A (the object owner) grants B some * privileges with grant option, and B re-grants them to C. If C could * grant the privileges to B as well, then A would be unable to effectively * revoke the privileges from B, since recursive_revoke would consider that * B still has 'em from C. * * We check for this by recursively deleting all grant options belonging to * the target grantee, and then seeing if the would-be grantor still has the * grant option or not. */static voidcheck_circularity(const Acl *old_acl, const AclItem *mod_aip, Oid ownerId){ Acl *acl; AclItem *aip; int i, num; AclMode own_privs; /* * For now, grant options can only be granted to roles, not PUBLIC. * Otherwise we'd have to work a bit harder here. */ Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC); /* The owner always has grant options, no need to check */ if (mod_aip->ai_grantor == ownerId) return; /* Make a working copy */ acl = allocacl(ACL_NUM(old_acl)); memcpy(acl, old_acl, ACL_SIZE(old_acl)); /* Zap all grant options of target grantee, plus what depends on 'em */cc_restart: num = ACL_NUM(acl); aip = ACL_DAT(acl); for (i = 0; i < num; i++) { if (aip[i].ai_grantee == mod_aip->ai_grantee && ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS) { Acl *new_acl; /* We'll actually zap ordinary privs too, but no matter */ new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL, ownerId, DROP_CASCADE); pfree(acl); acl = new_acl; goto cc_restart; } } /* Now we can compute grantor's independently-derived privileges */ own_privs = aclmask(acl, mod_aip->ai_grantor, ownerId, ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)), ACLMASK_ALL); own_privs = ACL_OPTION_TO_PRIVS(own_privs); if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_GRANT_OPERATION), errmsg("grant options cannot be granted back to your own grantor"))); pfree(acl);}/* * Ensure that no privilege is "abandoned". A privilege is abandoned * if the user that granted the privilege loses the grant option. (So * the chain through which it was granted is broken.) Either the * abandoned privileges are revoked as well, or an error message is * printed, depending on the drop behavior option. * * acl: the input ACL list * grantee: the user from whom some grant options have been revoked * revoke_privs: the grant options being revoked * ownerId: Oid of object owner * behavior: RESTRICT or CASCADE behavior for recursive removal * * The input Acl object is pfree'd if replaced. */static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs, Oid ownerId, DropBehavior behavior){ AclMode still_has; AclItem *aip; int i, num; /* The owner can never truly lose grant options, so short-circuit */ if (grantee == ownerId) return acl; /* The grantee might still have the privileges via another grantor */ still_has = aclmask(acl, grantee, ownerId, ACL_GRANT_OPTION_FOR(revoke_privs), ACLMASK_ALL); revoke_privs &= ~still_has; if (revoke_privs == ACL_NO_RIGHTS) return acl;restart: num = ACL_NUM(acl); aip = ACL_DAT(acl); for (i = 0; i < num; i++) { if (aip[i].ai_grantor == grantee && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0) { AclItem mod_acl; Acl *new_acl; if (behavior == DROP_RESTRICT) ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), errmsg("dependent privileges exist"), errhint("Use CASCADE to revoke them too."))); mod_acl.ai_grantor = grantee; mod_acl.ai_grantee = aip[i].ai_grantee; ACLITEM_SET_PRIVS_GOPTIONS(mod_acl, revoke_privs, revoke_privs); new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL, ownerId, behavior); pfree(acl); acl = new_acl; goto restart; } } return acl;}/* * aclmask --- compute bitmask of all privileges held by roleid. * * When 'how' = ACLMASK_ALL, this simply returns the privilege bits * held by the given roleid according to the given ACL list, ANDed * with 'mask'. (The point of passing 'mask' is to let the routine * exit early if all privileges of interest have been found.) * * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask * is known true. (This lets us exit soonest in cases where the * caller is only going to test for zero or nonzero result.) * * Usage patterns: * * To see if any of a set of privileges are held: * if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0) * * To see if all of a set of privileges are held: * if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs) * * To determine exactly which of a set of privileges are held: * heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL); */AclModeaclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how){ AclMode result; AclMode remaining; AclItem *aidat; int i, num; /* * Null ACL should not happen, since caller should have inserted * appropriate default */ if (acl == NULL) elog(ERROR, "null ACL"); /* Quick exit for mask == 0 */ if (mask == 0) return 0; result = 0; /* Owner always implicitly has all grant options */ if ((mask & ACLITEM_ALL_GOPTION_BITS) && has_privs_of_role(roleid, ownerId)) { result = mask & ACLITEM_ALL_GOPTION_BITS; if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0)) return result; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -