~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to contrib/sepgsql/hooks.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -------------------------------------------------------------------------
 
2
 *
 
3
 * contrib/sepgsql/hooks.c
 
4
 *
 
5
 * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
 
6
 *
 
7
 * Copyright (c) 2010-2011, PostgreSQL Global Development Group
 
8
 *
 
9
 * -------------------------------------------------------------------------
 
10
 */
 
11
#include "postgres.h"
 
12
 
 
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"
 
19
#include "fmgr.h"
 
20
#include "libpq/auth.h"
 
21
#include "miscadmin.h"
 
22
#include "tcop/utility.h"
 
23
#include "utils/guc.h"
 
24
 
 
25
#include "sepgsql.h"
 
26
 
 
27
PG_MODULE_MAGIC;
 
28
 
 
29
/*
 
30
 * Declarations
 
31
 */
 
32
void            _PG_init(void);
 
33
 
 
34
/*
 
35
 * Saved hook entries (if stacked)
 
36
 */
 
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;
 
43
 
 
44
/*
 
45
 * GUC: sepgsql.permissive = (on|off)
 
46
 */
 
47
static bool sepgsql_permissive;
 
48
 
 
49
bool
 
50
sepgsql_get_permissive(void)
 
51
{
 
52
        return sepgsql_permissive;
 
53
}
 
54
 
 
55
/*
 
56
 * GUC: sepgsql.debug_audit = (on|off)
 
57
 */
 
58
static bool sepgsql_debug_audit;
 
59
 
 
60
bool
 
61
sepgsql_get_debug_audit(void)
 
62
{
 
63
        return sepgsql_debug_audit;
 
64
}
 
65
 
 
66
/*
 
67
 * sepgsql_client_auth
 
68
 *
 
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.
 
72
 */
 
73
static void
 
74
sepgsql_client_auth(Port *port, int status)
 
75
{
 
76
        char       *context;
 
77
 
 
78
        if (next_client_auth_hook)
 
79
                (*next_client_auth_hook) (port, status);
 
80
 
 
81
        /*
 
82
         * In the case when authentication failed, the supplied socket shall be
 
83
         * closed soon, so we don't need to do anything here.
 
84
         */
 
85
        if (status != STATUS_OK)
 
86
                return;
 
87
 
 
88
        /*
 
89
         * Getting security label of the peer process using API of libselinux.
 
90
         */
 
91
        if (getpeercon_raw(port->sock, &context) < 0)
 
92
                ereport(FATAL,
 
93
                                (errcode(ERRCODE_INTERNAL_ERROR),
 
94
                                 errmsg("SELinux: unable to get peer label: %m")));
 
95
 
 
96
        sepgsql_set_client_label(context);
 
97
 
 
98
        /*
 
99
         * Switch the current performing mode from INTERNAL to either DEFAULT or
 
100
         * PERMISSIVE.
 
101
         */
 
102
        if (sepgsql_permissive)
 
103
                sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
 
104
        else
 
105
                sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
 
106
}
 
107
 
 
108
/*
 
109
 * sepgsql_object_access
 
110
 *
 
111
 * Entrypoint of the object_access_hook. This routine performs as
 
112
 * a dispatcher of invocation based on access type and object classes.
 
113
 */
 
114
static void
 
115
sepgsql_object_access(ObjectAccessType access,
 
116
                                          Oid classId,
 
117
                                          Oid objectId,
 
118
                                          int subId)
 
119
{
 
120
        if (next_object_access_hook)
 
121
                (*next_object_access_hook) (access, classId, objectId, subId);
 
122
 
 
123
        switch (access)
 
124
        {
 
125
                case OAT_POST_CREATE:
 
126
                        switch (classId)
 
127
                        {
 
128
                                case NamespaceRelationId:
 
129
                                        sepgsql_schema_post_create(objectId);
 
130
                                        break;
 
131
 
 
132
                                case RelationRelationId:
 
133
                                        if (subId == 0)
 
134
                                                sepgsql_relation_post_create(objectId);
 
135
                                        else
 
136
                                                sepgsql_attribute_post_create(objectId, subId);
 
137
                                        break;
 
138
 
 
139
                                case ProcedureRelationId:
 
140
                                        sepgsql_proc_post_create(objectId);
 
141
                                        break;
 
142
 
 
143
                                default:
 
144
                                        /* Ignore unsupported object classes */
 
145
                                        break;
 
146
                        }
 
147
                        break;
 
148
 
 
149
                default:
 
150
                        elog(ERROR, "unexpected object access type: %d", (int) access);
 
151
                        break;
 
152
        }
 
153
}
 
154
 
 
155
/*
 
156
 * sepgsql_exec_check_perms
 
157
 *
 
158
 * Entrypoint of DML permissions
 
159
 */
 
160
static bool
 
161
sepgsql_exec_check_perms(List *rangeTabls, bool abort)
 
162
{
 
163
        /*
 
164
         * If security provider is stacking and one of them replied 'false' at
 
165
         * least, we don't need to check any more.
 
166
         */
 
167
        if (next_exec_check_perms_hook &&
 
168
                !(*next_exec_check_perms_hook) (rangeTabls, abort))
 
169
                return false;
 
170
 
 
171
        if (!sepgsql_dml_privileges(rangeTabls, abort))
 
172
                return false;
 
173
 
 
174
        return true;
 
175
}
 
176
 
 
177
/*
 
178
 * sepgsql_needs_fmgr_hook
 
179
 *
 
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.
 
183
 */
 
184
static bool
 
185
sepgsql_needs_fmgr_hook(Oid functionId)
 
186
{
 
187
        char       *old_label;
 
188
        char       *new_label;
 
189
        char       *function_label;
 
190
 
 
191
        if (next_needs_fmgr_hook &&
 
192
                (*next_needs_fmgr_hook) (functionId))
 
193
                return true;
 
194
 
 
195
        /*
 
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.
 
200
         */
 
201
        old_label = sepgsql_get_client_label();
 
202
        new_label = sepgsql_proc_get_domtrans(functionId);
 
203
        if (strcmp(old_label, new_label) != 0)
 
204
        {
 
205
                pfree(new_label);
 
206
                return true;
 
207
        }
 
208
        pfree(new_label);
 
209
 
 
210
        /*
 
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
 
214
         * ACL_EXECUTE.
 
215
         */
 
216
        function_label = sepgsql_get_label(ProcedureRelationId, functionId, 0);
 
217
        if (sepgsql_check_perms(sepgsql_get_client_label(),
 
218
                                                        function_label,
 
219
                                                        SEPG_CLASS_DB_PROCEDURE,
 
220
                                                        SEPG_DB_PROCEDURE__EXECUTE,
 
221
                                                        NULL, false) != true)
 
222
        {
 
223
                pfree(function_label);
 
224
                return true;
 
225
        }
 
226
        pfree(function_label);
 
227
        return false;
 
228
}
 
229
 
 
230
/*
 
231
 * sepgsql_fmgr_hook
 
232
 *
 
233
 * It switches security label of the client on execution of trusted
 
234
 * procedures.
 
235
 */
 
236
static void
 
237
sepgsql_fmgr_hook(FmgrHookEventType event,
 
238
                                  FmgrInfo *flinfo, Datum *private)
 
239
{
 
240
        struct
 
241
        {
 
242
                char       *old_label;
 
243
                char       *new_label;
 
244
                Datum           next_private;
 
245
        }                  *stack;
 
246
 
 
247
        switch (event)
 
248
        {
 
249
                case FHET_START:
 
250
                        stack = (void *) DatumGetPointer(*private);
 
251
                        if (!stack)
 
252
                        {
 
253
                                MemoryContext oldcxt;
 
254
                                const char *cur_label = sepgsql_get_client_label();
 
255
 
 
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;
 
261
 
 
262
                                MemoryContextSwitchTo(oldcxt);
 
263
 
 
264
                                if (strcmp(cur_label, stack->new_label) != 0)
 
265
                                {
 
266
                                        /*
 
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.
 
270
                                         */
 
271
                                        sepgsql_check_perms(cur_label, stack->new_label,
 
272
                                                                                SEPG_CLASS_PROCESS,
 
273
                                                                                SEPG_PROCESS__TRANSITION,
 
274
                                                                                NULL, true);
 
275
                                }
 
276
 
 
277
                                *private = PointerGetDatum(stack);
 
278
                        }
 
279
                        Assert(!stack->old_label);
 
280
                        stack->old_label = sepgsql_set_client_label(stack->new_label);
 
281
 
 
282
                        if (next_fmgr_hook)
 
283
                                (*next_fmgr_hook) (event, flinfo, &stack->next_private);
 
284
                        break;
 
285
 
 
286
                case FHET_END:
 
287
                case FHET_ABORT:
 
288
                        stack = (void *) DatumGetPointer(*private);
 
289
 
 
290
                        if (next_fmgr_hook)
 
291
                                (*next_fmgr_hook) (event, flinfo, &stack->next_private);
 
292
 
 
293
                        sepgsql_set_client_label(stack->old_label);
 
294
                        stack->old_label = NULL;
 
295
                        break;
 
296
 
 
297
                default:
 
298
                        elog(ERROR, "unexpected event type: %d", (int) event);
 
299
                        break;
 
300
        }
 
301
}
 
302
 
 
303
/*
 
304
 * sepgsql_utility_command
 
305
 *
 
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.
 
308
 */
 
309
static void
 
310
sepgsql_utility_command(Node *parsetree,
 
311
                                                const char *queryString,
 
312
                                                ParamListInfo params,
 
313
                                                bool isTopLevel,
 
314
                                                DestReceiver *dest,
 
315
                                                char *completionTag)
 
316
{
 
317
        if (next_ProcessUtility_hook)
 
318
                (*next_ProcessUtility_hook) (parsetree, queryString, params,
 
319
                                                                         isTopLevel, dest, completionTag);
 
320
 
 
321
        /*
 
322
         * Check command tag to avoid nefarious operations
 
323
         */
 
324
        switch (nodeTag(parsetree))
 
325
        {
 
326
                case T_LoadStmt:
 
327
 
 
328
                        /*
 
329
                         * We reject LOAD command across the board on enforcing mode,
 
330
                         * because a binary module can arbitrarily override hooks.
 
331
                         */
 
332
                        if (sepgsql_getenforce())
 
333
                        {
 
334
                                ereport(ERROR,
 
335
                                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
336
                                                 errmsg("SELinux: LOAD is not permitted")));
 
337
                        }
 
338
                        break;
 
339
                default:
 
340
 
 
341
                        /*
 
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
 
345
                         * individually.
 
346
                         */
 
347
                        break;
 
348
        }
 
349
 
 
350
        /*
 
351
         * Original implementation
 
352
         */
 
353
        standard_ProcessUtility(parsetree, queryString, params,
 
354
                                                        isTopLevel, dest, completionTag);
 
355
}
 
356
 
 
357
/*
 
358
 * Module load/unload callback
 
359
 */
 
360
void
 
361
_PG_init(void)
 
362
{
 
363
        char       *context;
 
364
 
 
365
        /*
 
366
         * We allow to load the SE-PostgreSQL module on single-user-mode or
 
367
         * shared_preload_libraries settings only.
 
368
         */
 
369
        if (IsUnderPostmaster)
 
370
                ereport(ERROR,
 
371
                                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 
372
                         errmsg("sepgsql must be loaded via shared_preload_libraries")));
 
373
 
 
374
        /*
 
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
 
377
         * initialization.
 
378
         */
 
379
        if (is_selinux_enabled() < 1)
 
380
        {
 
381
                sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
 
382
                return;
 
383
        }
 
384
 
 
385
        /*
 
386
         * sepgsql.permissive = (on|off)
 
387
         *
 
388
         * This variable controls performing mode of SE-PostgreSQL on user's
 
389
         * session.
 
390
         */
 
391
        DefineCustomBoolVariable("sepgsql.permissive",
 
392
                                                         "Turn on/off permissive mode in SE-PostgreSQL",
 
393
                                                         NULL,
 
394
                                                         &sepgsql_permissive,
 
395
                                                         false,
 
396
                                                         PGC_SIGHUP,
 
397
                                                         GUC_NOT_IN_SAMPLE,
 
398
                                                         NULL,
 
399
                                                         NULL,
 
400
                                                         NULL);
 
401
 
 
402
        /*
 
403
         * sepgsql.debug_audit = (on|off)
 
404
         *
 
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.
 
408
         */
 
409
        DefineCustomBoolVariable("sepgsql.debug_audit",
 
410
                                                         "Turn on/off debug audit messages",
 
411
                                                         NULL,
 
412
                                                         &sepgsql_debug_audit,
 
413
                                                         false,
 
414
                                                         PGC_USERSET,
 
415
                                                         GUC_NOT_IN_SAMPLE,
 
416
                                                         NULL,
 
417
                                                         NULL,
 
418
                                                         NULL);
 
419
 
 
420
        /*
 
421
         * Set up dummy client label.
 
422
         *
 
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.
 
429
         */
 
430
        if (getcon_raw(&context) < 0)
 
431
                ereport(ERROR,
 
432
                                (errcode(ERRCODE_INTERNAL_ERROR),
 
433
                                 errmsg("SELinux: failed to get server security label: %m")));
 
434
        sepgsql_set_client_label(context);
 
435
 
 
436
        /* Security label provider hook */
 
437
        register_label_provider(SEPGSQL_LABEL_TAG,
 
438
                                                        sepgsql_object_relabel);
 
439
 
 
440
        /* Client authentication hook */
 
441
        next_client_auth_hook = ClientAuthentication_hook;
 
442
        ClientAuthentication_hook = sepgsql_client_auth;
 
443
 
 
444
        /* Object access hook */
 
445
        next_object_access_hook = object_access_hook;
 
446
        object_access_hook = sepgsql_object_access;
 
447
 
 
448
        /* DML permission check */
 
449
        next_exec_check_perms_hook = ExecutorCheckPerms_hook;
 
450
        ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
 
451
 
 
452
        /* Trusted procedure hooks */
 
453
        next_needs_fmgr_hook = needs_fmgr_hook;
 
454
        needs_fmgr_hook = sepgsql_needs_fmgr_hook;
 
455
 
 
456
        next_fmgr_hook = fmgr_hook;
 
457
        fmgr_hook = sepgsql_fmgr_hook;
 
458
 
 
459
        /* ProcessUtility hook */
 
460
        next_ProcessUtility_hook = ProcessUtility_hook;
 
461
        ProcessUtility_hook = sepgsql_utility_command;
 
462
}