A PostgreSQL flaw
An announcement of possibly insecure practices in user-defined PostgreSQL functions seems at first blush to be a fairly straightforward advisory; a deeper look reveals some serious implications. It is a problem that echoes a textbook security hole in UNIX setuid programs; it would appear that the developers did not consider that history when adding a setuid-like capability to PostgreSQL. Unfortunately, it also appears that the fix that the advisory recommends is not up to the task of resolving the issue. Anyone using SECURITY DEFINER functions in PostgreSQL probably has quite a large job ahead of them to clear up this particular mess.
PostgreSQL functions can be be declared as "SECURITY DEFINER" functions, which causes them to run with the privileges of the owner rather than those of the invoker. PostgreSQL binds the operators and functions called at runtime and searches each element in the schema path to find them. Unfortunately, the user invoking the function can control the schema search path and, by defining operators or other functions that are used by the SECURITY DEFINER function, the invoker can run any code with the permissions of the owner.
The once common, now hopefully largely eradicated, UNIX parallel was a vulnerability in setuid programs that invoked other programs via exec(). If the program did not either sanitize its PATH environment variable or fully specify the path to the executable, it was vulnerable to attackers who would put their own code in the path, with the same name as the executable, ahead of the standard program. When the setuid program executed, it would grab the wrong binary and the attacker could run arbitrary code with the permissions of the owner of the setuid program. Another important requirement is that all elements of the sanitized PATH and the directory of the binary are not writable by non-privileged users.
So, much like the solution to the UNIX issue, the advisory suggests that SECURITY DEFINER functions specify a sanitized schema path. The equivalent to a fully specified path is not recommended as it is "likely to induce mistakes and will furthermore make the source code harder to read and maintain." Unfortunately, it turns out that because of the way PostgreSQL processes the function definitions, the only solution is to schema-qualify each and every function and operator reference in the function. In addition, setting a schema search path in a function is not local to the function, it changes the global search path for the whole program; functions that do this should restore the original search path on exit.
It turns out that the references in a function are resolved as PostgreSQL creates an execution plan for the function. This is prior to actually executing the "set search path" operation in the function and so it will bind to functions and operators in the user controlled schema path as described here. The only alternative is the laborious and error-prone task of schema-qualifying function and operator references in SECURITY DEFINER functions.
This is a very unfortunate outcome for a feature that was meant to promote more secure database usage. The idea is to separate the database privileges into different users but to still allow users with few privileges to perform a restricted set of privileged operations. It is surprising that the UNIX setuid issues from the dawn of time_t were not more closely studied when this feature was implemented. It would also seem that the PostgreSQL developers will need to rework how the execution plan and search path interact to fix this design flaw.
Index entries for this article | |
---|---|
GuestArticles | Edge, Jake |
Posted Feb 22, 2007 3:29 UTC (Thu)
by dw (guest, #12017)
[Link] (3 responses)
Posted Feb 22, 2007 13:46 UTC (Thu)
by nix (subscriber, #2304)
[Link]
That's a rather wide constraint.
(FWIW, Oracle has the equivalent of SECURITY DEFINER be the *default* but avoids this bug by searching the object owner's schema instead for such functions. Perhaps simply prepending the object owner's schema to the search path when within such functions would largely fix the problem...)
Posted Feb 23, 2007 23:32 UTC (Fri)
by intgr (subscriber, #39733)
[Link] (1 responses)
No -- Mallory needs access to existing SECURITY DEFINER functions defined by a higher-privilege user. Mallory can only create his own functions, but that way he'll only be able to impersonate himself.
Posted Feb 26, 2007 21:15 UTC (Mon)
by schabi (guest, #14079)
[Link]
In most cases, it even suffices that he creates the function or operator in his own schema, as - by default - that schema precedes the ones that define the built-in operators and functions.
If my memory of PostgreSQL serves me well (come on, it's only been a year!), then then Mallory (the 'untrusted' session) would first need the permission to create new functions before an attack would be successful, no?A PostgreSQL flaw
Well, you need to have been able to create new functions in some schema (which you can later put in the search path) at some earlier point in time.A PostgreSQL flaw
A PostgreSQL flaw
That's not correct. He needs to define his own function or operator that shadows a function or operator used by the privileged function (by having the same name and signature), then arrange the search path so that his function is found first, and then call the privileged function.A PostgreSQL flaw