| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * rls.c |
| 4 | * RLS-related utility functions. |
| 5 | * |
| 6 | * Portions Copyright (c) 1996-2019, 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/builtins.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 | check_enable_rls(Oid relid, Oid checkAsUser, bool noError) |
| 53 | { |
| 54 | Oid user_id = 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 | if (relid < (Oid) FirstNormalObjectId) |
| 63 | return RLS_NONE; |
| 64 | |
| 65 | /* Fetch relation's relrowsecurity and relforcerowsecurity flags */ |
| 66 | tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); |
| 67 | if (!HeapTupleIsValid(tuple)) |
| 68 | return RLS_NONE; |
| 69 | classform = (Form_pg_class) GETSTRUCT(tuple); |
| 70 | |
| 71 | relrowsecurity = classform->relrowsecurity; |
| 72 | relforcerowsecurity = classform->relforcerowsecurity; |
| 73 | |
| 74 | ReleaseSysCache(tuple); |
| 75 | |
| 76 | /* Nothing to do if the relation does not have RLS */ |
| 77 | if (!relrowsecurity) |
| 78 | 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 | if (has_bypassrls_privilege(user_id)) |
| 88 | 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 | amowner = pg_class_ownercheck(relid, user_id); |
| 99 | 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 | if (!relforcerowsecurity || InNoForceRLSOperation()) |
| 117 | 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 | if (!row_security && !noError) |
| 125 | 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 | 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 | row_security_active(PG_FUNCTION_ARGS) |
| 143 | { |
| 144 | /* By OID */ |
| 145 | Oid tableoid = PG_GETARG_OID(0); |
| 146 | int rls_status; |
| 147 | |
| 148 | rls_status = check_enable_rls(tableoid, InvalidOid, true); |
| 149 | PG_RETURN_BOOL(rls_status == RLS_ENABLED); |
| 150 | } |
| 151 | |
| 152 | Datum |
| 153 | row_security_active_name(PG_FUNCTION_ARGS) |
| 154 | { |
| 155 | /* By qualified name */ |
| 156 | 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 | tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename)); |
| 163 | tableoid = RangeVarGetRelid(tablerel, NoLock, false); |
| 164 | |
| 165 | rls_status = check_enable_rls(tableoid, InvalidOid, true); |
| 166 | PG_RETURN_BOOL(rls_status == RLS_ENABLED); |
| 167 | } |
| 168 | |