Re: Some notes about the index-functions security vulnerability

From: "Trevor Talbot" <quension(at)gmail(dot)com>
To: "Tom Lane" <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: Some notes about the index-functions security vulnerability
Date: 2008-01-14 03:59:08
Message-ID: 90bce5730801131959t594ea210y361af31e036745ca@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 1/8/08, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us> wrote:

> The other issue that ought to be on the TODO radar is that we've only
> plugged the hole for the very limited case of maintenance operations that
> are likely to be executed by superusers. If user A modifies user B's
> table (via INSERT/UPDATE/DELETE), there are a lot of bits of code that are
> controlled by B but will be executed with A's permissions; so A must trust
> B a whole lot. This general issue has been understood for quite some
> time, I think, but maybe it's time to make a serious push to solve it.

High-level brain dump, does not cover all the use cases I'm sure...

Given Invoker executing code created by Definer, there are basically 3
situations:

1) Definer does not trust Invoker, but Invoker trusts Definer.
-> use Invoker's permission set

This is probably the case for most system/library generic functions,
such as the various trigger templates They are typically owned by a
superuser

2) Invoker does not trust Definer, but Definer trusts Invoker.
-> use Definer's permission set

This case covers most triggers, since they are there to maintain
Definer's data, and Invoker's input is inherently controlled.

3) Neither trusts the other.
-> use the intersection of Invoker's and Definer's permission sets

This is essentially the case for any arbitrary functions floating
around, where Invoker's input is not inherently controlled, and
Definer is an unknown entity.

Situation 1 is covered by SECURITY INVOKER, and 2 is covered by
SECURITY DEFINER. Suppose another function option is added for
situation 3, "SECURITY INTERSECTION". Also suppose there is a new role
option, "TRUSTED" (needs a better name).

* A function is created with SECURITY INTERSECTION by default.
* A function's owner can choose SECURITY DEFINER.
* Only a role with TRUSTED can choose SECURITY INVOKER.
* Only the superuser has TRUSTED by default.

The idea here is that by default, neither Invoker nor Definer need to
be terribly concerned. If Definer is creating the function
specifically to operate on its own data, and is checking input
appropriately, SECURITY DEFINER will allow it to work. If Definer is
creating the function for generic use purposes, Invoker will want to
apply it to its own data, and SECURITY INVOKER is appropriate for
that. A Definer's trustworthiness for all Invokers is determined by
the superuser via the TRUSTED role option.

> Offhand I can cite the following ways in which B could exploit A's
> privileges:
> * triggers

Ideally Invoker's permission set would be replaced by the trigger
owner's for the duration of the call. However it doesn't look like
there actually is an owner concept for triggers, despite there being a
TRIGGER permission for the associated table.

The next appropriate option is to assign the table owner's permission
set to Invoker. In the case of functions marked SECURITY INVOKER, this
leaves a hole: a role that has TRIGGER permission on the table can
elevate its permissions to that of the table owner's when calling that
function.

If the role with TRIGGER permission is not TRUSTED, it can only create
new functions with SECURITY INTERSECTION, which will result in
executing with its own permissions at best. This seems reasonable.

> * functions in indexes
> * functions in CHECK constraints
> * functions in DEFAULT expressions
> * functions in rules (including VIEW definitions)

Replace the Invoker's permission set with the table owner's for the
duration of the call. These all require you to be the owner of the
associated object, so there is no potential hole as with triggers.

> The first three of these are probably not too difficult to solve: we could
> switch privilege state to the table owner before executing such functions,
> because the backend knows perfectly well when it's doing each of those
> things. But default expressions and rules get intertwined freely with
> query fragments supplied by the calling user, and it's not so easy to see
> how to know what to execute as which user.

I'll just wave my hands wildly here and say functions in expressions
supplied by the Invoker magically avoid being called with the object
owner's permission set instead. I don't know how, they just do :)

What this doesn't allow is actually executing things like VIEW
expressions using the calling user's permission set. I don't have an
actual use case for that, but I feel it's a problem somehow.

I've also completely avoided things like CURRENT_USER by talking about
permission sets only.

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Webb Sprague 2008-01-14 04:09:10 Re: Postgresql Materialized views
Previous Message Andrew Chernow 2008-01-14 03:57:48 Re: Postgresql Materialized views