Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * rls.c 4 : * RLS-related utility functions. 5 : * 6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group 7 : * Portions Copyright (c) 1994, Regents of the University of California 8 : * 9 : * 10 : * IDENTIFICATION 11 : * src/backend/utils/misc/rls.c 12 : * 13 : *------------------------------------------------------------------------- 14 : */ 15 : #include "postgres.h" 16 : 17 : #include "access/htup.h" 18 : #include "access/htup_details.h" 19 : #include "access/transam.h" 20 : #include "catalog/namespace.h" 21 : #include "catalog/pg_class.h" 22 : #include "miscadmin.h" 23 : #include "utils/acl.h" 24 : #include "utils/fmgrprotos.h" 25 : #include "utils/lsyscache.h" 26 : #include "utils/rls.h" 27 : #include "utils/syscache.h" 28 : #include "utils/varlena.h" 29 : 30 : 31 : /* 32 : * check_enable_rls 33 : * 34 : * Determine, based on the relation, row_security setting, and current role, 35 : * if RLS is applicable to this query. RLS_NONE_ENV indicates that, while 36 : * RLS is not to be added for this query, a change in the environment may change 37 : * that. RLS_NONE means that RLS is not on the relation at all and therefore 38 : * we don't need to worry about it. RLS_ENABLED means RLS should be implemented 39 : * for the table and the plan cache needs to be invalidated if the environment 40 : * changes. 41 : * 42 : * Handle checking as another role via checkAsUser (for views, etc). Pass 43 : * InvalidOid to check the current user. 44 : * 45 : * If noError is set to 'true' then we just return RLS_ENABLED instead of doing 46 : * an ereport() if the user has attempted to bypass RLS and they are not 47 : * allowed to. This allows users to check if RLS is enabled without having to 48 : * deal with the actual error case (eg: error cases which are trying to decide 49 : * if the user should get data from the relation back as part of the error). 50 : */ 51 : int 52 871470 : check_enable_rls(Oid relid, Oid checkAsUser, bool noError) 53 : { 54 871470 : Oid user_id = OidIsValid(checkAsUser) ? checkAsUser : GetUserId(); 55 : HeapTuple tuple; 56 : Form_pg_class classform; 57 : bool relrowsecurity; 58 : bool relforcerowsecurity; 59 : bool amowner; 60 : 61 : /* Nothing to do for built-in relations */ 62 871470 : if (relid < (Oid) FirstNormalObjectId) 63 235436 : return RLS_NONE; 64 : 65 : /* Fetch relation's relrowsecurity and relforcerowsecurity flags */ 66 636034 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); 67 636034 : if (!HeapTupleIsValid(tuple)) 68 0 : return RLS_NONE; 69 636034 : classform = (Form_pg_class) GETSTRUCT(tuple); 70 : 71 636034 : relrowsecurity = classform->relrowsecurity; 72 636034 : relforcerowsecurity = classform->relforcerowsecurity; 73 : 74 636034 : ReleaseSysCache(tuple); 75 : 76 : /* Nothing to do if the relation does not have RLS */ 77 636034 : if (!relrowsecurity) 78 632426 : return RLS_NONE; 79 : 80 : /* 81 : * BYPASSRLS users always bypass RLS. Note that superusers are always 82 : * considered to have BYPASSRLS. 83 : * 84 : * Return RLS_NONE_ENV to indicate that this decision depends on the 85 : * environment (in this case, the user_id). 86 : */ 87 3608 : if (has_bypassrls_privilege(user_id)) 88 536 : return RLS_NONE_ENV; 89 : 90 : /* 91 : * Table owners generally bypass RLS, except if the table has been set (by 92 : * an owner) to FORCE ROW SECURITY, and this is not a referential 93 : * integrity check. 94 : * 95 : * Return RLS_NONE_ENV to indicate that this decision depends on the 96 : * environment (in this case, the user_id). 97 : */ 98 3072 : amowner = object_ownercheck(RelationRelationId, relid, user_id); 99 3072 : if (amowner) 100 : { 101 : /* 102 : * If FORCE ROW LEVEL SECURITY has been set on the relation then we 103 : * should return RLS_ENABLED to indicate that RLS should be applied. 104 : * If not, or if we are in an InNoForceRLSOperation context, we return 105 : * RLS_NONE_ENV. 106 : * 107 : * InNoForceRLSOperation indicates that we should not apply RLS even 108 : * if the table has FORCE RLS set - IF the current user is the owner. 109 : * This is specifically to ensure that referential integrity checks 110 : * are able to still run correctly. 111 : * 112 : * This is intentionally only done after we have checked that the user 113 : * is the table owner, which should always be the case for referential 114 : * integrity checks. 115 : */ 116 420 : if (!relforcerowsecurity || InNoForceRLSOperation()) 117 228 : return RLS_NONE_ENV; 118 : } 119 : 120 : /* 121 : * We should apply RLS. However, the user may turn off the row_security 122 : * GUC to get a forced error instead. 123 : */ 124 2844 : if (!row_security && !noError) 125 66 : ereport(ERROR, 126 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), 127 : errmsg("query would be affected by row-level security policy for table \"%s\"", 128 : get_rel_name(relid)), 129 : amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0)); 130 : 131 : /* RLS should be fully enabled for this relation. */ 132 2778 : return RLS_ENABLED; 133 : } 134 : 135 : /* 136 : * row_security_active 137 : * 138 : * check_enable_rls wrapped as a SQL callable function except 139 : * RLS_NONE_ENV and RLS_NONE are the same for this purpose. 140 : */ 141 : Datum 142 12 : row_security_active(PG_FUNCTION_ARGS) 143 : { 144 : /* By OID */ 145 12 : Oid tableoid = PG_GETARG_OID(0); 146 : int rls_status; 147 : 148 12 : rls_status = check_enable_rls(tableoid, InvalidOid, true); 149 12 : PG_RETURN_BOOL(rls_status == RLS_ENABLED); 150 : } 151 : 152 : Datum 153 12 : row_security_active_name(PG_FUNCTION_ARGS) 154 : { 155 : /* By qualified name */ 156 12 : text *tablename = PG_GETARG_TEXT_PP(0); 157 : RangeVar *tablerel; 158 : Oid tableoid; 159 : int rls_status; 160 : 161 : /* Look up table name. Can't lock it - we might not have privileges. */ 162 12 : tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename)); 163 12 : tableoid = RangeVarGetRelid(tablerel, NoLock, false); 164 : 165 12 : rls_status = check_enable_rls(tableoid, InvalidOid, true); 166 12 : PG_RETURN_BOOL(rls_status == RLS_ENABLED); 167 : }