Thread: RLS policy issue

RLS policy issue

From
Ted Toth
Date:
I'm work on understanding and implementing RLS. Since I work on
systems using SELinux (MLS policy) I'm using the sepgsql module that
I've modified slightly i.e. I've added a function named
sepgsql_check_row_perm that I'm using in the policy for example I have
a 'reports' table that looks like:

                                                     Table "public.reports"
     Column     |      Type       |                      Modifiers
                  | Storage  | Stats target | Description

----------------+-----------------+------------------------------------------------------+----------+--------------+-------------
 id             | integer         | not null default
nextval('reports_id_seq'::regclass) | plain    |              |
 report         | json            |
                  | extended |              |
 message_id     | integer         | not null
                  | plain    |              |
 location       | geometry(Point) |
                  | main     |              |
 security_label | text            | default sepgsql_getcon()
                  | extended |              |
Policies:
    POLICY "check_report_delete_selinux" FOR DELETE
      USING sepgsql_check_row_perm(security_label, sepgsql_getcon(),
'delete'::text)
    POLICY "check_report_insert_selinux" FOR INSERT
      WITH CHECK sepgsql_check_row_perm(security_label,
sepgsql_getcon(), 'insert'::text)
    POLICY "check_report_select_selinux" FOR SELECT
      USING sepgsql_check_row_perm(sepgsql_getcon(), security_label,
'select'::text)
    POLICY "check_report_update_selinux" FOR UPDATE
      USING sepgsql_check_row_perm(security_label, sepgsql_getcon(),
'update'::text)
      WITH CHECK sepgsql_check_row_perm(security_label,
sepgsql_getcon(), 'update'::text)

When I do a select I expect sepgsql_check_row_perm to be called and at
least output the elog message I added here's part of the patch I apply
to add the sepgsql_check_row_perm funstion to the module:

 /*
+ * BOOL sepgsql_check_row_perm(TEXT, TEXT, TEXT)
+ *
+ * Check if perm allowed for tuple.
+ * This is a variant of sepgsql_avc_check_perms_label which allows the
+ * specifying of both the source and target contexts. For MLS
+ * (write up read down) dominance purposes in the case of
+ * INSERT/UPDATE/DELETE (write) the source is the tuples context
+ * and it must dominate the peers context however in the case of
+ * SELECT (read) the source is the peers context and it must dominate
+ * the tuples context.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_check_row_perm);
+Datum
+sepgsql_check_row_perm(PG_FUNCTION_ARGS)
+{
+       const char *scontext;
+       const char *tcontext;
+       const char *perm_name;
+       access_vector_t av_perm;
+
+       elog(DEBUG1, "sepgsql_check_row_perm");

I'd also expect that the "rewrite" would have added the POLICY SELECT
USING clause to the query but I don't see any indication of that in
the details that follow:

< 2015-05-21 16:59:39.030 CDT >STATEMENT:  select * from reports
< 2015-05-21 16:59:39.030 CDT >LOG:  rewritten parse tree:
< 2015-05-21 16:59:39.030 CDT >DETAIL:  (
       {QUERY
       :commandType 1
       :querySource 0
       :canSetTag true
       :utilityStmt <>
       :resultRelation 0
       :hasAggs false
       :hasWindowFuncs false
       :hasSubLinks false
       :hasDistinctOn false
       :hasRecursive false
       :hasModifyingCTE false
       :hasForUpdate false
       :hasRowSecurity true
       :cteList <>
       :rtable (
          {RTE
          :alias <>
          :eref
             {ALIAS
             :aliasname reports
             :colnames ("id" "report" "message_id" "location" "security_label")
             }
          :rtekind 0
          :relid 19116
          :relkind r
          :lateral false
          :inh true
          :inFromCl true
          :requiredPerms 2
          :checkAsUser 0
          :selectedCols (b 9 10 11 12 13)
          :modifiedCols (b)
          :securityQuals <>
          }
       )
       :jointree
          {FROMEXPR
          :fromlist (
             {RANGETBLREF
             :rtindex 1
             }
          )
          :quals <>
          }
       :targetList (
          {TARGETENTRY
          :expr
             {VAR
             :varno 1
             :varattno 1
             :vartype 23
             :vartypmod -1
             :varcollid 0
             :varlevelsup 0
             :varnoold 1
             :varoattno 1
             :location 7
             }
          :resno 1
          :resname id
          :ressortgroupref 0
          :resorigtbl 19116
          :resorigcol 1
          :resjunk false
          }
          {TARGETENTRY
          :expr
             {VAR
             :varno 1
             :varattno 2
             :vartype 114
             :vartypmod -1
             :varcollid 0
             :varlevelsup 0
             :varnoold 1
             :varoattno 2
             :location 7
             }
          :resno 2
          :resname report
          :ressortgroupref 0
          :resorigtbl 19116
          :resorigcol 2
          :resjunk false
          }
          {TARGETENTRY
          :expr
             {VAR
             :varno 1
             :varattno 3
             :vartype 23
             :vartypmod -1
             :varcollid 0
             :varlevelsup 0
             :varnoold 1
             :varoattno 3
             :location 7
             }
          :resno 3
          :resname message_id
          :ressortgroupref 0
          :resorigtbl 19116
          :resorigcol 3
          :resjunk false
          }
          {TARGETENTRY
          :expr
             {VAR
             :varno 1
             :varattno 4
             :vartype 17780
             :vartypmod 4
             :varcollid 0
             :varlevelsup 0
             :varnoold 1
             :varoattno 4
             :location 7
             }
          :resno 4
          :resname location
          :ressortgroupref 0
          :resorigtbl 19116
          :resorigcol 4
          :resjunk false
          }
          {TARGETENTRY
          :expr
             {VAR
             :varno 1
             :varattno 5
             :vartype 25
             :vartypmod -1
             :varcollid 100
             :varlevelsup 0
             :varnoold 1
             :varoattno 5
             :location 7
             }
          :resno 5
          :resname security_label
          :ressortgroupref 0
          :resorigtbl 19116
          :resorigcol 5
          :resjunk false
          }
       )
       :withCheckOptions <>
       :returningList <>
       :groupClause <>
       :havingQual <>
       :windowClause <>
       :distinctClause <>
       :sortClause <>
       :limitOffset <>
       :limitCount <>
       :rowMarks <>
       :setOperations <>
       :constraintDeps <>
       }
    )

Should I see something in the rewrite details related to the policy?
As I've shown the the table has policy defined and the details show
hasRowSecurity true why might my policy not be getting applied to the
query?


Re: RLS policy issue

From
Stephen Frost
Date:
Ted,

* Ted Toth (txtoth@gmail.com) wrote:
> I'd also expect that the "rewrite" would have added the POLICY SELECT
> USING clause to the query but I don't see any indication of that in
> the details that follow:

Just running 'explain' should show the policy.

Are you running this as the owner of the table or as a superuser?  As
noted in the documentation, the owner (who controls the policies
on the table anyway) and the superuser (who can bypass all
authorization) do not have the RLS policies applied unless the
'row_security' GUC is set to 'force', like so:

SET row_security = force;

By the way, you might be interested in the test_rls_hooks module which I
wrote and committed recently under src/test/modules.  That's the
approach which I was thinking about using with sepgsql to provide policy
enforcement, but using regular policies should also work.

    Thanks!

        Stephen

Attachment