1
/* -------------------------------------------------------------------------
3
* contrib/sepgsql/hooks.c
5
* Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
7
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
9
* -------------------------------------------------------------------------
13
#include "catalog/objectaccess.h"
14
#include "catalog/pg_class.h"
15
#include "catalog/pg_namespace.h"
16
#include "catalog/pg_proc.h"
17
#include "commands/seclabel.h"
18
#include "executor/executor.h"
20
#include "libpq/auth.h"
21
#include "miscadmin.h"
22
#include "tcop/utility.h"
23
#include "utils/guc.h"
35
* Saved hook entries (if stacked)
37
static object_access_hook_type next_object_access_hook = NULL;
38
static ClientAuthentication_hook_type next_client_auth_hook = NULL;
39
static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
40
static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
41
static fmgr_hook_type next_fmgr_hook = NULL;
42
static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
45
* GUC: sepgsql.permissive = (on|off)
47
static bool sepgsql_permissive;
50
sepgsql_get_permissive(void)
52
return sepgsql_permissive;
56
* GUC: sepgsql.debug_audit = (on|off)
58
static bool sepgsql_debug_audit;
61
sepgsql_get_debug_audit(void)
63
return sepgsql_debug_audit;
69
* Entrypoint of the client authentication hook.
70
* It switches the client label according to getpeercon(), and the current
71
* performing mode according to the GUC setting.
74
sepgsql_client_auth(Port *port, int status)
78
if (next_client_auth_hook)
79
(*next_client_auth_hook) (port, status);
82
* In the case when authentication failed, the supplied socket shall be
83
* closed soon, so we don't need to do anything here.
85
if (status != STATUS_OK)
89
* Getting security label of the peer process using API of libselinux.
91
if (getpeercon_raw(port->sock, &context) < 0)
93
(errcode(ERRCODE_INTERNAL_ERROR),
94
errmsg("SELinux: unable to get peer label: %m")));
96
sepgsql_set_client_label(context);
99
* Switch the current performing mode from INTERNAL to either DEFAULT or
102
if (sepgsql_permissive)
103
sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
105
sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
109
* sepgsql_object_access
111
* Entrypoint of the object_access_hook. This routine performs as
112
* a dispatcher of invocation based on access type and object classes.
115
sepgsql_object_access(ObjectAccessType access,
120
if (next_object_access_hook)
121
(*next_object_access_hook) (access, classId, objectId, subId);
125
case OAT_POST_CREATE:
128
case NamespaceRelationId:
129
sepgsql_schema_post_create(objectId);
132
case RelationRelationId:
134
sepgsql_relation_post_create(objectId);
136
sepgsql_attribute_post_create(objectId, subId);
139
case ProcedureRelationId:
140
sepgsql_proc_post_create(objectId);
144
/* Ignore unsupported object classes */
150
elog(ERROR, "unexpected object access type: %d", (int) access);
156
* sepgsql_exec_check_perms
158
* Entrypoint of DML permissions
161
sepgsql_exec_check_perms(List *rangeTabls, bool abort)
164
* If security provider is stacking and one of them replied 'false' at
165
* least, we don't need to check any more.
167
if (next_exec_check_perms_hook &&
168
!(*next_exec_check_perms_hook) (rangeTabls, abort))
171
if (!sepgsql_dml_privileges(rangeTabls, abort))
178
* sepgsql_needs_fmgr_hook
180
* It informs the core whether the supplied function is trusted procedure,
181
* or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
182
* abort time of function invocation.
185
sepgsql_needs_fmgr_hook(Oid functionId)
189
char *function_label;
191
if (next_needs_fmgr_hook &&
192
(*next_needs_fmgr_hook) (functionId))
196
* SELinux needs the function to be called via security_definer wrapper,
197
* if this invocation will take a domain-transition. We call these
198
* functions as trusted-procedure, if the security policy has a rule that
199
* switches security label of the client on execution.
201
old_label = sepgsql_get_client_label();
202
new_label = sepgsql_proc_get_domtrans(functionId);
203
if (strcmp(old_label, new_label) != 0)
211
* Even if not a trusted-procedure, this function should not be inlined
212
* unless the client has db_procedure:{execute} permission. Please note
213
* that it shall be actually failed later because of same reason with
216
function_label = sepgsql_get_label(ProcedureRelationId, functionId, 0);
217
if (sepgsql_check_perms(sepgsql_get_client_label(),
219
SEPG_CLASS_DB_PROCEDURE,
220
SEPG_DB_PROCEDURE__EXECUTE,
221
NULL, false) != true)
223
pfree(function_label);
226
pfree(function_label);
233
* It switches security label of the client on execution of trusted
237
sepgsql_fmgr_hook(FmgrHookEventType event,
238
FmgrInfo *flinfo, Datum *private)
250
stack = (void *) DatumGetPointer(*private);
253
MemoryContext oldcxt;
254
const char *cur_label = sepgsql_get_client_label();
256
oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
257
stack = palloc(sizeof(*stack));
258
stack->old_label = NULL;
259
stack->new_label = sepgsql_proc_get_domtrans(flinfo->fn_oid);
260
stack->next_private = 0;
262
MemoryContextSwitchTo(oldcxt);
264
if (strcmp(cur_label, stack->new_label) != 0)
267
* process:transition permission between old and new
268
* label, when user tries to switch security label of the
269
* client on execution of trusted procedure.
271
sepgsql_check_perms(cur_label, stack->new_label,
273
SEPG_PROCESS__TRANSITION,
277
*private = PointerGetDatum(stack);
279
Assert(!stack->old_label);
280
stack->old_label = sepgsql_set_client_label(stack->new_label);
283
(*next_fmgr_hook) (event, flinfo, &stack->next_private);
288
stack = (void *) DatumGetPointer(*private);
291
(*next_fmgr_hook) (event, flinfo, &stack->next_private);
293
sepgsql_set_client_label(stack->old_label);
294
stack->old_label = NULL;
298
elog(ERROR, "unexpected event type: %d", (int) event);
304
* sepgsql_utility_command
306
* It tries to rough-grained control on utility commands; some of them can
307
* break whole of the things if nefarious user would use.
310
sepgsql_utility_command(Node *parsetree,
311
const char *queryString,
312
ParamListInfo params,
317
if (next_ProcessUtility_hook)
318
(*next_ProcessUtility_hook) (parsetree, queryString, params,
319
isTopLevel, dest, completionTag);
322
* Check command tag to avoid nefarious operations
324
switch (nodeTag(parsetree))
329
* We reject LOAD command across the board on enforcing mode,
330
* because a binary module can arbitrarily override hooks.
332
if (sepgsql_getenforce())
335
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
336
errmsg("SELinux: LOAD is not permitted")));
342
* Right now we don't check any other utility commands, because it
343
* needs more detailed information to make access control decision
344
* here, but we don't want to have two parse and analyze routines
351
* Original implementation
353
standard_ProcessUtility(parsetree, queryString, params,
354
isTopLevel, dest, completionTag);
358
* Module load/unload callback
366
* We allow to load the SE-PostgreSQL module on single-user-mode or
367
* shared_preload_libraries settings only.
369
if (IsUnderPostmaster)
371
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
372
errmsg("sepgsql must be loaded via shared_preload_libraries")));
375
* Check availability of SELinux on the platform. If disabled, we cannot
376
* activate any SE-PostgreSQL features, and we have to skip rest of
379
if (is_selinux_enabled() < 1)
381
sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
386
* sepgsql.permissive = (on|off)
388
* This variable controls performing mode of SE-PostgreSQL on user's
391
DefineCustomBoolVariable("sepgsql.permissive",
392
"Turn on/off permissive mode in SE-PostgreSQL",
403
* sepgsql.debug_audit = (on|off)
405
* This variable allows users to turn on/off audit logs on access control
406
* decisions, independent from auditallow/auditdeny setting in the
407
* security policy. We intend to use this option for debugging purpose.
409
DefineCustomBoolVariable("sepgsql.debug_audit",
410
"Turn on/off debug audit messages",
412
&sepgsql_debug_audit,
421
* Set up dummy client label.
423
* XXX - note that PostgreSQL launches background worker process like
424
* autovacuum without authentication steps. So, we initialize sepgsql_mode
425
* with SEPGSQL_MODE_INTERNAL, and client_label with the security context
426
* of server process. Later, it also launches background of user session.
427
* In this case, the process is always hooked on post-authentication, and
428
* we can initialize the sepgsql_mode and client_label correctly.
430
if (getcon_raw(&context) < 0)
432
(errcode(ERRCODE_INTERNAL_ERROR),
433
errmsg("SELinux: failed to get server security label: %m")));
434
sepgsql_set_client_label(context);
436
/* Security label provider hook */
437
register_label_provider(SEPGSQL_LABEL_TAG,
438
sepgsql_object_relabel);
440
/* Client authentication hook */
441
next_client_auth_hook = ClientAuthentication_hook;
442
ClientAuthentication_hook = sepgsql_client_auth;
444
/* Object access hook */
445
next_object_access_hook = object_access_hook;
446
object_access_hook = sepgsql_object_access;
448
/* DML permission check */
449
next_exec_check_perms_hook = ExecutorCheckPerms_hook;
450
ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
452
/* Trusted procedure hooks */
453
next_needs_fmgr_hook = needs_fmgr_hook;
454
needs_fmgr_hook = sepgsql_needs_fmgr_hook;
456
next_fmgr_hook = fmgr_hook;
457
fmgr_hook = sepgsql_fmgr_hook;
459
/* ProcessUtility hook */
460
next_ProcessUtility_hook = ProcessUtility_hook;
461
ProcessUtility_hook = sepgsql_utility_command;