PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
uavc.c
Go to the documentation of this file.
1/* -------------------------------------------------------------------------
2 *
3 * contrib/sepgsql/uavc.c
4 *
5 * Implementation of userspace access vector cache; that enables to cache
6 * access control decisions recently used, and reduce number of kernel
7 * invocations to avoid unnecessary performance hit.
8 *
9 * Copyright (c) 2011-2024, PostgreSQL Global Development Group
10 *
11 * -------------------------------------------------------------------------
12 */
13#include "postgres.h"
14
15#include "catalog/pg_proc.h"
16#include "commands/seclabel.h"
17#include "common/hashfn.h"
18#include "sepgsql.h"
19#include "storage/ipc.h"
20#include "utils/guc.h"
21#include "utils/memutils.h"
22
23/*
24 * avc_cache
25 *
26 * It enables to cache access control decision (and behavior on execution of
27 * trusted procedure, db_procedure class only) for a particular pair of
28 * security labels and object class in userspace.
29 */
30typedef struct
31{
32 uint32 hash; /* hash value of this cache entry */
33 char *scontext; /* security context of the subject */
34 char *tcontext; /* security context of the target */
35 uint16 tclass; /* object class of the target */
36
37 uint32 allowed; /* permissions to be allowed */
38 uint32 auditallow; /* permissions to be audited on allowed */
39 uint32 auditdeny; /* permissions to be audited on denied */
40
41 bool permissive; /* true, if permissive rule */
42 bool hot_cache; /* true, if recently referenced */
44 /* true, if tcontext is valid */
45 char *ncontext; /* temporary scontext on execution of trusted
46 * procedure, or NULL elsewhere */
47} avc_cache;
48
49/*
50 * Declaration of static variables
51 */
52#define AVC_NUM_SLOTS 512
53#define AVC_NUM_RECLAIM 16
54#define AVC_DEF_THRESHOLD 384
55
57static List *avc_slots[AVC_NUM_SLOTS]; /* avc's hash buckets */
58static int avc_num_caches; /* number of caches currently used */
59static int avc_lru_hint; /* index of the buckets to be reclaimed next */
60static int avc_threshold; /* threshold to launch cache-reclaiming */
61static char *avc_unlabeled; /* system 'unlabeled' label */
62
63/*
64 * Hash function
65 */
66static uint32
67sepgsql_avc_hash(const char *scontext, const char *tcontext, uint16 tclass)
68{
69 return hash_any((const unsigned char *) scontext, strlen(scontext))
70 ^ hash_any((const unsigned char *) tcontext, strlen(tcontext))
71 ^ tclass;
72}
73
74/*
75 * Reset all the avc caches
76 */
77static void
79{
81
82 memset(avc_slots, 0, sizeof(List *) * AVC_NUM_SLOTS);
84 avc_lru_hint = 0;
85 avc_unlabeled = NULL;
86}
87
88/*
89 * Reclaim caches recently unreferenced
90 */
91static void
93{
94 ListCell *cell;
95 int index;
96
98 {
100
101 foreach(cell, avc_slots[index])
102 {
103 avc_cache *cache = lfirst(cell);
104
105 if (!cache->hot_cache)
106 {
109
110 pfree(cache->scontext);
111 pfree(cache->tcontext);
112 if (cache->ncontext)
113 pfree(cache->ncontext);
114 pfree(cache);
115
117 }
118 else
119 {
120 cache->hot_cache = false;
121 }
122 }
124 }
125}
126
127/* -------------------------------------------------------------------------
128 *
129 * sepgsql_avc_check_valid
130 *
131 * This function checks whether the cached entries are still valid. If
132 * the security policy has been reloaded (or any other events that requires
133 * resetting userspace caches has occurred) since the last reference to
134 * the access vector cache, we must flush the cache.
135 *
136 * Access control decisions must be atomic, but multiple system calls may
137 * be required to make a decision; thus, when referencing the access vector
138 * cache, we must loop until we complete without an intervening cache flush
139 * event. In practice, looping even once should be very rare. Callers should
140 * do something like this:
141 *
142 * sepgsql_avc_check_valid();
143 * do {
144 * :
145 * <reference to uavc>
146 * :
147 * } while (!sepgsql_avc_check_valid())
148 *
149 * -------------------------------------------------------------------------
150 */
151static bool
153{
154 if (selinux_status_updated() > 0)
155 {
157
158 return false;
159 }
160 return true;
161}
162
163/*
164 * sepgsql_avc_unlabeled
165 *
166 * Returns an alternative label to be applied when no label or an invalid
167 * label would otherwise be assigned.
168 */
169static char *
171{
172 if (!avc_unlabeled)
173 {
174 char *unlabeled;
175
176 if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
178 (errcode(ERRCODE_INTERNAL_ERROR),
179 errmsg("SELinux: failed to get initial security label: %m")));
180 PG_TRY();
181 {
183 }
184 PG_FINALLY();
185 {
186 freecon(unlabeled);
187 }
188 PG_END_TRY();
189 }
190 return avc_unlabeled;
191}
192
193/*
194 * sepgsql_avc_compute
195 *
196 * A fallback path, when cache mishit. It asks SELinux its access control
197 * decision for the supplied pair of security context and object class.
198 */
199static avc_cache *
200sepgsql_avc_compute(const char *scontext, const char *tcontext, uint16 tclass)
201{
202 char *ucontext = NULL;
203 char *ncontext = NULL;
204 MemoryContext oldctx;
205 avc_cache *cache;
206 uint32 hash;
207 int index;
208 struct av_decision avd;
209
210 hash = sepgsql_avc_hash(scontext, tcontext, tclass);
212
213 /*
214 * Validation check of the supplied security context. Because it always
215 * invoke system-call, frequent check should be avoided. Unless security
216 * policy is reloaded, validation status shall be kept, so we also cache
217 * whether the supplied security context was valid, or not.
218 */
219 if (security_check_context_raw(tcontext) != 0)
220 ucontext = sepgsql_avc_unlabeled();
221
222 /*
223 * Ask SELinux its access control decision
224 */
225 if (!ucontext)
226 sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
227 else
228 sepgsql_compute_avd(scontext, ucontext, tclass, &avd);
229
230 /*
231 * It also caches a security label to be switched when a client labeled as
232 * 'scontext' executes a procedure labeled as 'tcontext', not only access
233 * control decision on the procedure. The security label to be switched
234 * shall be computed uniquely on a pair of 'scontext' and 'tcontext',
235 * thus, it is reasonable to cache the new label on avc, and enables to
236 * reduce unnecessary system calls. It shall be referenced at
237 * sepgsql_needs_fmgr_hook to check whether the supplied function is a
238 * trusted procedure, or not.
239 */
240 if (tclass == SEPG_CLASS_DB_PROCEDURE)
241 {
242 if (!ucontext)
243 ncontext = sepgsql_compute_create(scontext, tcontext,
244 SEPG_CLASS_PROCESS, NULL);
245 else
246 ncontext = sepgsql_compute_create(scontext, ucontext,
247 SEPG_CLASS_PROCESS, NULL);
248 if (strcmp(scontext, ncontext) == 0)
249 {
250 pfree(ncontext);
251 ncontext = NULL;
252 }
253 }
254
255 /*
256 * Set up an avc_cache object
257 */
259
260 cache = palloc0(sizeof(avc_cache));
261
262 cache->hash = hash;
263 cache->scontext = pstrdup(scontext);
264 cache->tcontext = pstrdup(tcontext);
265 cache->tclass = tclass;
266
267 cache->allowed = avd.allowed;
268 cache->auditallow = avd.auditallow;
269 cache->auditdeny = avd.auditdeny;
270 cache->hot_cache = true;
271 if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE)
272 cache->permissive = true;
273 if (!ucontext)
274 cache->tcontext_is_valid = true;
275 if (ncontext)
276 cache->ncontext = pstrdup(ncontext);
277
279
282
283 avc_slots[index] = lcons(cache, avc_slots[index]);
284
285 MemoryContextSwitchTo(oldctx);
286
287 return cache;
288}
289
290/*
291 * sepgsql_avc_lookup
292 *
293 * Look up a cache entry that matches the supplied security contexts and
294 * object class. If not found, create a new cache entry.
295 */
296static avc_cache *
297sepgsql_avc_lookup(const char *scontext, const char *tcontext, uint16 tclass)
298{
299 avc_cache *cache;
300 ListCell *cell;
301 uint32 hash;
302 int index;
303
304 hash = sepgsql_avc_hash(scontext, tcontext, tclass);
306
307 foreach(cell, avc_slots[index])
308 {
309 cache = lfirst(cell);
310
311 if (cache->hash == hash &&
312 cache->tclass == tclass &&
313 strcmp(cache->tcontext, tcontext) == 0 &&
314 strcmp(cache->scontext, scontext) == 0)
315 {
316 cache->hot_cache = true;
317 return cache;
318 }
319 }
320 /* not found, so insert a new cache */
321 return sepgsql_avc_compute(scontext, tcontext, tclass);
322}
323
324/*
325 * sepgsql_avc_check_perms(_label)
326 *
327 * It returns 'true', if the security policy suggested to allow the required
328 * permissions. Otherwise, it returns 'false' or raises an error according
329 * to the 'abort_on_violation' argument.
330 * The 'tobject' and 'tclass' identify the target object being referenced,
331 * and 'required' is a bitmask of permissions (SEPG_*__*) defined for each
332 * object classes.
333 * The 'audit_name' is the object name (optional). If SEPGSQL_AVC_NOAUDIT
334 * was supplied, it means to skip all the audit messages.
335 */
336bool
337sepgsql_avc_check_perms_label(const char *tcontext,
338 uint16 tclass, uint32 required,
339 const char *audit_name,
340 bool abort_on_violation)
341{
342 char *scontext = sepgsql_get_client_label();
343 avc_cache *cache;
344 uint32 denied;
345 uint32 audited;
346 bool result;
347
349 do
350 {
351 result = true;
352
353 /*
354 * If the target object is unlabeled, we perform the check using the
355 * label supplied by sepgsql_avc_unlabeled().
356 */
357 if (tcontext)
358 cache = sepgsql_avc_lookup(scontext, tcontext, tclass);
359 else
360 cache = sepgsql_avc_lookup(scontext,
361 sepgsql_avc_unlabeled(), tclass);
362
363 denied = required & ~cache->allowed;
364
365 /*
366 * Compute permissions to be audited
367 */
369 audited = (denied ? (denied & ~0) : (required & ~0));
370 else
371 audited = denied ? (denied & cache->auditdeny)
372 : (required & cache->auditallow);
373
374 if (denied)
375 {
376 /*
377 * In permissive mode or permissive domain, violated permissions
378 * shall be audited to the log files at once, and then implicitly
379 * allowed to avoid a flood of access denied logs, because the
380 * purpose of permissive mode/domain is to collect a violation log
381 * that will make it possible to fix up the security policy.
382 */
383 if (!sepgsql_getenforce() || cache->permissive)
384 cache->allowed |= required;
385 else
386 result = false;
387 }
388 } while (!sepgsql_avc_check_valid());
389
390 /*
391 * In the case when we have something auditable actions here,
392 * sepgsql_audit_log shall be called with text representation of security
393 * labels for both of subject and object. It records this access
394 * violation, so DBA will be able to find out unexpected security problems
395 * later.
396 */
397 if (audited != 0 &&
398 audit_name != SEPGSQL_AVC_NOAUDIT &&
400 {
401 sepgsql_audit_log(denied != 0,
402 (sepgsql_getenforce() && !cache->permissive),
403 cache->scontext,
404 cache->tcontext_is_valid ?
406 cache->tclass,
407 audited,
408 audit_name);
409 }
410
411 if (abort_on_violation && !result)
413 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
414 errmsg("SELinux: security policy violation")));
415
416 return result;
417}
418
419bool
421 uint16 tclass, uint32 required,
422 const char *audit_name,
423 bool abort_on_violation)
424{
425 char *tcontext = GetSecurityLabel(tobject, SEPGSQL_LABEL_TAG);
426 bool rc;
427
428 rc = sepgsql_avc_check_perms_label(tcontext,
429 tclass, required,
430 audit_name, abort_on_violation);
431 if (tcontext)
432 pfree(tcontext);
433
434 return rc;
435}
436
437/*
438 * sepgsql_avc_trusted_proc
439 *
440 * If the supplied function OID is configured as a trusted procedure, this
441 * function will return a security label to be used during the execution of
442 * that function. Otherwise, it returns NULL.
443 */
444char *
446{
447 char *scontext = sepgsql_get_client_label();
448 char *tcontext;
449 ObjectAddress tobject;
450 avc_cache *cache;
451
452 tobject.classId = ProcedureRelationId;
453 tobject.objectId = functionId;
454 tobject.objectSubId = 0;
455 tcontext = GetSecurityLabel(&tobject, SEPGSQL_LABEL_TAG);
456
458 do
459 {
460 if (tcontext)
461 cache = sepgsql_avc_lookup(scontext, tcontext,
463 else
464 cache = sepgsql_avc_lookup(scontext, sepgsql_avc_unlabeled(),
466 } while (!sepgsql_avc_check_valid());
467
468 return cache->ncontext;
469}
470
471/*
472 * sepgsql_avc_exit
473 *
474 * Clean up userspace AVC on process exit.
475 */
476static void
478{
479 selinux_status_close();
480}
481
482/*
483 * sepgsql_avc_init
484 *
485 * Initialize the userspace AVC. This should be called from _PG_init.
486 */
487void
489{
490 int rc;
491
492 /*
493 * All the avc stuff shall be allocated in avc_mem_cxt
494 */
496 "userspace access vector cache",
498 memset(avc_slots, 0, sizeof(avc_slots));
499 avc_num_caches = 0;
500 avc_lru_hint = 0;
502
503 /*
504 * SELinux allows to mmap(2) its kernel status page in read-only mode to
505 * inform userspace applications its status updating (such as policy
506 * reloading) without system-call invocations. This feature is only
507 * supported in Linux-2.6.38 or later, however, libselinux provides a
508 * fallback mode to know its status using netlink sockets.
509 */
510 rc = selinux_status_open(1);
511 if (rc < 0)
513 (errcode(ERRCODE_INTERNAL_ERROR),
514 errmsg("SELinux: could not open selinux status : %m")));
515 else if (rc > 0)
516 ereport(LOG,
517 (errmsg("SELinux: kernel status page uses fallback mode")));
518
519 /* Arrange to close selinux status page on process exit. */
521}
uint16_t uint16
Definition: c.h:484
uint32_t uint32
Definition: c.h:485
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define LOG
Definition: elog.h:31
#define PG_TRY(...)
Definition: elog.h:371
#define PG_END_TRY(...)
Definition: elog.h:396
#define ERROR
Definition: elog.h:39
#define PG_FINALLY(...)
Definition: elog.h:388
#define ereport(elevel,...)
Definition: elog.h:149
static Datum hash_any(const unsigned char *k, int keylen)
Definition: hashfn.h:31
bool sepgsql_get_debug_audit(void)
Definition: hooks.c:74
void on_proc_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:309
char * sepgsql_get_client_label(void)
Definition: label.c:80
List * lcons(void *datum, List *list)
Definition: list.c:495
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1683
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
char * pstrdup(const char *in)
Definition: mcxt.c:1696
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc0(Size size)
Definition: mcxt.c:1347
MemoryContext TopMemoryContext
Definition: mcxt.c:149
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
void * arg
#define lfirst(lc)
Definition: pg_list.h:172
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
uintptr_t Datum
Definition: postgres.h:64
unsigned int Oid
Definition: postgres_ext.h:31
MemoryContextSwitchTo(old_ctx)
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:715
char * GetSecurityLabel(const ObjectAddress *object, const char *provider)
Definition: seclabel.c:272
char * sepgsql_compute_create(const char *scontext, const char *tcontext, uint16 tclass, const char *objname)
Definition: selinux.c:842
int sepgsql_get_mode(void)
Definition: selinux.c:625
void sepgsql_compute_avd(const char *scontext, const char *tcontext, uint16 tclass, struct av_decision *avd)
Definition: selinux.c:739
void sepgsql_audit_log(bool denied, bool enforcing, const char *scontext, const char *tcontext, uint16 tclass, uint32 audited, const char *audit_name)
Definition: selinux.c:678
bool sepgsql_getenforce(void)
Definition: selinux.c:651
#define SEPGSQL_MODE_INTERNAL
Definition: sepgsql.h:30
#define SEPG_CLASS_DB_PROCEDURE
Definition: sepgsql.h:48
#define SEPGSQL_AVC_NOAUDIT
Definition: sepgsql.h:250
#define SEPGSQL_LABEL_TAG
Definition: sepgsql.h:23
#define SEPG_CLASS_PROCESS
Definition: sepgsql.h:36
Definition: pg_list.h:54
Definition: uavc.c:31
uint16 tclass
Definition: uavc.c:35
uint32 allowed
Definition: uavc.c:37
uint32 auditdeny
Definition: uavc.c:39
char * scontext
Definition: uavc.c:33
char * tcontext
Definition: uavc.c:34
bool tcontext_is_valid
Definition: uavc.c:43
char * ncontext
Definition: uavc.c:45
uint32 hash
Definition: uavc.c:32
bool permissive
Definition: uavc.c:41
uint32 auditallow
Definition: uavc.c:38
bool hot_cache
Definition: uavc.c:42
Definition: type.h:96
static uint32 sepgsql_avc_hash(const char *scontext, const char *tcontext, uint16 tclass)
Definition: uavc.c:67
static void sepgsql_avc_reclaim(void)
Definition: uavc.c:92
char * sepgsql_avc_trusted_proc(Oid functionId)
Definition: uavc.c:445
bool sepgsql_avc_check_perms_label(const char *tcontext, uint16 tclass, uint32 required, const char *audit_name, bool abort_on_violation)
Definition: uavc.c:337
static int avc_lru_hint
Definition: uavc.c:59
static int avc_threshold
Definition: uavc.c:60
#define AVC_NUM_SLOTS
Definition: uavc.c:52
static MemoryContext avc_mem_cxt
Definition: uavc.c:56
static bool sepgsql_avc_check_valid(void)
Definition: uavc.c:152
static char * sepgsql_avc_unlabeled(void)
Definition: uavc.c:170
static void sepgsql_avc_reset(void)
Definition: uavc.c:78
static void sepgsql_avc_exit(int code, Datum arg)
Definition: uavc.c:477
static List * avc_slots[AVC_NUM_SLOTS]
Definition: uavc.c:57
static int avc_num_caches
Definition: uavc.c:58
static char * avc_unlabeled
Definition: uavc.c:61
#define AVC_DEF_THRESHOLD
Definition: uavc.c:54
#define AVC_NUM_RECLAIM
Definition: uavc.c:53
static avc_cache * sepgsql_avc_lookup(const char *scontext, const char *tcontext, uint16 tclass)
Definition: uavc.c:297
static avc_cache * sepgsql_avc_compute(const char *scontext, const char *tcontext, uint16 tclass)
Definition: uavc.c:200
void sepgsql_avc_init(void)
Definition: uavc.c:488
bool sepgsql_avc_check_perms(const ObjectAddress *tobject, uint16 tclass, uint32 required, const char *audit_name, bool abort_on_violation)
Definition: uavc.c:420