Age Owner Branch data TLA Line data Source code
1 : : /* -------------------------------------------------------------------------
2 : : *
3 : : * contrib/sepgsql/label.c
4 : : *
5 : : * Routines to support SELinux labels (security context)
6 : : *
7 : : * Copyright (c) 2010-2025, PostgreSQL Global Development Group
8 : : *
9 : : * -------------------------------------------------------------------------
10 : : */
11 : : #include "postgres.h"
12 : :
13 : : #include <selinux/label.h>
14 : :
15 : : #include "access/genam.h"
16 : : #include "access/htup_details.h"
17 : : #include "access/table.h"
18 : : #include "access/xact.h"
19 : : #include "catalog/catalog.h"
20 : : #include "catalog/dependency.h"
21 : : #include "catalog/pg_attribute.h"
22 : : #include "catalog/pg_class.h"
23 : : #include "catalog/pg_database.h"
24 : : #include "catalog/pg_namespace.h"
25 : : #include "catalog/pg_proc.h"
26 : : #include "commands/seclabel.h"
27 : : #include "libpq/auth.h"
28 : : #include "libpq/libpq-be.h"
29 : : #include "miscadmin.h"
30 : : #include "sepgsql.h"
31 : : #include "utils/builtins.h"
32 : : #include "utils/fmgroids.h"
33 : : #include "utils/guc.h"
34 : : #include "utils/lsyscache.h"
35 : : #include "utils/memutils.h"
36 : : #include "utils/rel.h"
37 : :
38 : : /*
39 : : * Saved hook entries (if stacked)
40 : : */
41 : : static ClientAuthentication_hook_type next_client_auth_hook = NULL;
42 : : static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
43 : : static fmgr_hook_type next_fmgr_hook = NULL;
44 : :
45 : : /*
46 : : * client_label_*
47 : : *
48 : : * security label of the database client. Initially the client security label
49 : : * is equal to client_label_peer, and can be changed by one or more calls to
50 : : * sepgsql_setcon(), and also be temporarily overridden during execution of a
51 : : * trusted-procedure.
52 : : *
53 : : * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
54 : : * rollback should also rollback the current client security label. Therefore
55 : : * we use the list client_label_pending of pending_label to keep track of which
56 : : * labels were set during the (sub-)transactions.
57 : : */
58 : : static char *client_label_peer = NULL; /* set by getpeercon(3) */
59 : : static List *client_label_pending = NIL; /* pending list being set by
60 : : * sepgsql_setcon() */
61 : : static char *client_label_committed = NULL; /* set by sepgsql_setcon(), and
62 : : * already committed */
63 : : static char *client_label_func = NULL; /* set by trusted procedure */
64 : :
65 : : typedef struct
66 : : {
67 : : SubTransactionId subid;
68 : : char *label;
69 : : } pending_label;
70 : :
71 : : /*
72 : : * sepgsql_get_client_label
73 : : *
74 : : * Returns the current security label of the client. All code should use this
75 : : * routine to get the current label, instead of referring to the client_label_*
76 : : * variables above.
77 : : */
78 : : char *
5340 rhaas@postgresql.org 79 :UBC 0 : sepgsql_get_client_label(void)
80 : : {
81 : : /* trusted procedure client label override */
4923 82 [ # # ]: 0 : if (client_label_func)
83 : 0 : return client_label_func;
84 : :
85 : : /* uncommitted sepgsql_setcon() value */
86 [ # # ]: 0 : if (client_label_pending)
87 : : {
4836 bruce@momjian.us 88 : 0 : pending_label *plabel = llast(client_label_pending);
89 : :
4923 rhaas@postgresql.org 90 [ # # ]: 0 : if (plabel->label)
91 : 0 : return plabel->label;
92 : : }
93 [ # # ]: 0 : else if (client_label_committed)
94 : 0 : return client_label_committed; /* set by sepgsql_setcon() committed */
95 : :
96 : : /* default label */
97 [ # # ]: 0 : Assert(client_label_peer != NULL);
98 : 0 : return client_label_peer;
99 : : }
100 : :
101 : : /*
102 : : * sepgsql_set_client_label
103 : : *
104 : : * This routine tries to switch the current security label of the client, and
105 : : * checks related permissions. The supplied new label shall be added to the
106 : : * client_label_pending list, then saved at transaction-commit time to ensure
107 : : * transaction-awareness.
108 : : */
109 : : static void
110 : 0 : sepgsql_set_client_label(const char *new_label)
111 : : {
112 : : const char *tcontext;
113 : : MemoryContext oldcxt;
114 : : pending_label *plabel;
115 : :
116 : : /* Reset to the initial client label, if NULL */
117 [ # # ]: 0 : if (!new_label)
118 : 0 : tcontext = client_label_peer;
119 : : else
120 : : {
1849 michael@paquier.xyz 121 [ # # ]: 0 : if (security_check_context_raw(new_label) < 0)
4923 rhaas@postgresql.org 122 [ # # ]: 0 : ereport(ERROR,
123 : : (errcode(ERRCODE_INVALID_NAME),
124 : : errmsg("SELinux: invalid security label: \"%s\"",
125 : : new_label)));
126 : 0 : tcontext = new_label;
127 : : }
128 : :
129 : : /* Check process:{setcurrent} permission. */
130 : 0 : sepgsql_avc_check_perms_label(sepgsql_get_client_label(),
131 : : SEPG_CLASS_PROCESS,
132 : : SEPG_PROCESS__SETCURRENT,
133 : : NULL,
134 : : true);
135 : : /* Check process:{dyntransition} permission. */
136 : 0 : sepgsql_avc_check_perms_label(tcontext,
137 : : SEPG_CLASS_PROCESS,
138 : : SEPG_PROCESS__DYNTRANSITION,
139 : : NULL,
140 : : true);
141 : :
142 : : /*
143 : : * Append the supplied new_label on the pending list until the current
144 : : * transaction is committed.
145 : : */
146 : 0 : oldcxt = MemoryContextSwitchTo(CurTransactionContext);
147 : :
148 : 0 : plabel = palloc0(sizeof(pending_label));
149 : 0 : plabel->subid = GetCurrentSubTransactionId();
150 [ # # ]: 0 : if (new_label)
151 : 0 : plabel->label = pstrdup(new_label);
152 : 0 : client_label_pending = lappend(client_label_pending, plabel);
153 : :
154 : 0 : MemoryContextSwitchTo(oldcxt);
155 : 0 : }
156 : :
157 : : /*
158 : : * sepgsql_xact_callback
159 : : *
160 : : * A callback routine of transaction commit/abort/prepare. Commit or abort
161 : : * changes in the client_label_pending list.
162 : : */
163 : : static void
164 : 0 : sepgsql_xact_callback(XactEvent event, void *arg)
165 : : {
166 [ # # ]: 0 : if (event == XACT_EVENT_COMMIT)
167 : : {
168 [ # # ]: 0 : if (client_label_pending != NIL)
169 : : {
4836 bruce@momjian.us 170 : 0 : pending_label *plabel = llast(client_label_pending);
171 : : char *new_label;
172 : :
4923 rhaas@postgresql.org 173 [ # # ]: 0 : if (plabel->label)
174 : 0 : new_label = MemoryContextStrdup(TopMemoryContext,
175 : 0 : plabel->label);
176 : : else
177 : 0 : new_label = NULL;
178 : :
179 [ # # ]: 0 : if (client_label_committed)
180 : 0 : pfree(client_label_committed);
181 : :
182 : 0 : client_label_committed = new_label;
183 : :
184 : : /*
185 : : * XXX - Note that items of client_label_pending are allocated on
186 : : * CurTransactionContext, thus, all acquired memory region shall
187 : : * be released implicitly.
188 : : */
189 : 0 : client_label_pending = NIL;
190 : : }
191 : : }
192 [ # # ]: 0 : else if (event == XACT_EVENT_ABORT)
193 : 0 : client_label_pending = NIL;
194 : 0 : }
195 : :
196 : : /*
197 : : * sepgsql_subxact_callback
198 : : *
199 : : * A callback routine of sub-transaction start/abort/commit. Releases all
200 : : * security labels that are set within the sub-transaction that is aborted.
201 : : */
202 : : static void
203 : 0 : sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
204 : : SubTransactionId parentSubid, void *arg)
205 : : {
206 : : ListCell *cell;
207 : :
208 [ # # ]: 0 : if (event == SUBXACT_EVENT_ABORT_SUB)
209 : : {
2245 tgl@sss.pgh.pa.us 210 [ # # # # : 0 : foreach(cell, client_label_pending)
# # ]
211 : : {
4836 bruce@momjian.us 212 : 0 : pending_label *plabel = lfirst(cell);
213 : :
4923 rhaas@postgresql.org 214 [ # # ]: 0 : if (plabel->subid == mySubid)
215 : : client_label_pending
2245 tgl@sss.pgh.pa.us 216 : 0 : = foreach_delete_current(client_label_pending, cell);
217 : : }
218 : : }
5340 rhaas@postgresql.org 219 : 0 : }
220 : :
221 : : /*
222 : : * sepgsql_client_auth
223 : : *
224 : : * Entrypoint of the client authentication hook.
225 : : * It switches the client label according to getpeercon(), and the current
226 : : * performing mode according to the GUC setting.
227 : : */
228 : : static void
4952 229 : 0 : sepgsql_client_auth(Port *port, int status)
230 : : {
231 [ # # ]: 0 : if (next_client_auth_hook)
232 : 0 : (*next_client_auth_hook) (port, status);
233 : :
234 : : /*
235 : : * In the case when authentication failed, the supplied socket shall be
236 : : * closed soon, so we don't need to do anything here.
237 : : */
238 [ # # ]: 0 : if (status != STATUS_OK)
239 : 0 : return;
240 : :
241 : : /*
242 : : * Getting security label of the peer process using API of libselinux.
243 : : */
4923 244 [ # # ]: 0 : if (getpeercon_raw(port->sock, &client_label_peer) < 0)
4952 245 [ # # ]: 0 : ereport(FATAL,
246 : : (errcode(ERRCODE_INTERNAL_ERROR),
247 : : errmsg("SELinux: unable to get peer label: %m")));
248 : :
249 : : /*
250 : : * Switch the current performing mode from INTERNAL to either DEFAULT or
251 : : * PERMISSIVE.
252 : : */
253 [ # # ]: 0 : if (sepgsql_get_permissive())
254 : 0 : sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
255 : : else
256 : 0 : sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
257 : : }
258 : :
259 : : /*
260 : : * sepgsql_needs_fmgr_hook
261 : : *
262 : : * It informs the core whether the supplied function is trusted procedure,
263 : : * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
264 : : * abort time of function invocation.
265 : : */
266 : : static bool
267 : 0 : sepgsql_needs_fmgr_hook(Oid functionId)
268 : : {
269 : : ObjectAddress object;
270 : :
271 [ # # # # ]: 0 : if (next_needs_fmgr_hook &&
272 : 0 : (*next_needs_fmgr_hook) (functionId))
273 : 0 : return true;
274 : :
275 : : /*
276 : : * SELinux needs the function to be called via security_definer wrapper,
277 : : * if this invocation will take a domain-transition. We call these
278 : : * functions as trusted-procedure, if the security policy has a rule that
279 : : * switches security label of the client on execution.
280 : : */
281 [ # # ]: 0 : if (sepgsql_avc_trusted_proc(functionId) != NULL)
282 : 0 : return true;
283 : :
284 : : /*
285 : : * Even if not a trusted-procedure, this function should not be inlined
286 : : * unless the client has db_procedure:{execute} permission. Please note
287 : : * that it shall be actually failed later because of same reason with
288 : : * ACL_EXECUTE.
289 : : */
290 : 0 : object.classId = ProcedureRelationId;
291 : 0 : object.objectId = functionId;
292 : 0 : object.objectSubId = 0;
293 [ # # ]: 0 : if (!sepgsql_avc_check_perms(&object,
294 : : SEPG_CLASS_DB_PROCEDURE,
295 : : SEPG_DB_PROCEDURE__EXECUTE |
296 : : SEPG_DB_PROCEDURE__ENTRYPOINT,
297 : : SEPGSQL_AVC_NOAUDIT, false))
298 : 0 : return true;
299 : :
300 : 0 : return false;
301 : : }
302 : :
303 : : /*
304 : : * sepgsql_fmgr_hook
305 : : *
306 : : * It switches security label of the client on execution of trusted
307 : : * procedures.
308 : : */
309 : : static void
310 : 0 : sepgsql_fmgr_hook(FmgrHookEventType event,
311 : : FmgrInfo *flinfo, Datum *private)
312 : : {
313 : : struct
314 : : {
315 : : char *old_label;
316 : : char *new_label;
317 : : Datum next_private;
318 : : } *stack;
319 : :
320 [ # # # ]: 0 : switch (event)
321 : : {
322 : 0 : case FHET_START:
323 : 0 : stack = (void *) DatumGetPointer(*private);
324 [ # # ]: 0 : if (!stack)
325 : : {
326 : : MemoryContext oldcxt;
327 : :
328 : 0 : oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
329 : 0 : stack = palloc(sizeof(*stack));
330 : 0 : stack->old_label = NULL;
331 : 0 : stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
332 : 0 : stack->next_private = 0;
333 : :
334 : 0 : MemoryContextSwitchTo(oldcxt);
335 : :
336 : : /*
337 : : * process:transition permission between old and new label,
338 : : * when user tries to switch security label of the client on
339 : : * execution of trusted procedure.
340 : : *
341 : : * Also, db_procedure:entrypoint permission should be checked
342 : : * whether this procedure can perform as an entrypoint of the
343 : : * trusted procedure, or not. Note that db_procedure:execute
344 : : * permission shall be checked individually.
345 : : */
346 [ # # ]: 0 : if (stack->new_label)
347 : : {
348 : : ObjectAddress object;
349 : :
4530 350 : 0 : object.classId = ProcedureRelationId;
351 : 0 : object.objectId = flinfo->fn_oid;
352 : 0 : object.objectSubId = 0;
353 : 0 : sepgsql_avc_check_perms(&object,
354 : : SEPG_CLASS_DB_PROCEDURE,
355 : : SEPG_DB_PROCEDURE__ENTRYPOINT,
1879 michael@paquier.xyz 356 : 0 : getObjectDescription(&object, false),
357 : : true);
358 : :
4952 rhaas@postgresql.org 359 : 0 : sepgsql_avc_check_perms_label(stack->new_label,
360 : : SEPG_CLASS_PROCESS,
361 : : SEPG_PROCESS__TRANSITION,
362 : : NULL, true);
363 : : }
364 : 0 : *private = PointerGetDatum(stack);
365 : : }
366 [ # # ]: 0 : Assert(!stack->old_label);
367 [ # # ]: 0 : if (stack->new_label)
368 : : {
4923 369 : 0 : stack->old_label = client_label_func;
370 : 0 : client_label_func = stack->new_label;
371 : : }
4952 372 [ # # ]: 0 : if (next_fmgr_hook)
373 : 0 : (*next_fmgr_hook) (event, flinfo, &stack->next_private);
374 : 0 : break;
375 : :
376 : 0 : case FHET_END:
377 : : case FHET_ABORT:
378 : 0 : stack = (void *) DatumGetPointer(*private);
379 : :
380 [ # # ]: 0 : if (next_fmgr_hook)
381 : 0 : (*next_fmgr_hook) (event, flinfo, &stack->next_private);
382 : :
383 [ # # ]: 0 : if (stack->new_label)
384 : : {
4923 385 : 0 : client_label_func = stack->old_label;
4952 386 : 0 : stack->old_label = NULL;
387 : : }
388 : 0 : break;
389 : :
390 : 0 : default:
391 [ # # ]: 0 : elog(ERROR, "unexpected event type: %d", (int) event);
392 : : break;
393 : : }
394 : 0 : }
395 : :
396 : : /*
397 : : * sepgsql_init_client_label
398 : : *
399 : : * Initializes the client security label and sets up related hooks for client
400 : : * label management.
401 : : */
402 : : void
403 : 0 : sepgsql_init_client_label(void)
404 : : {
405 : : /*
406 : : * Set up dummy client label.
407 : : *
408 : : * XXX - note that PostgreSQL launches background worker process like
409 : : * autovacuum without authentication steps. So, we initialize sepgsql_mode
410 : : * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
411 : : * of server process. Later, it also launches background of user session.
412 : : * In this case, the process is always hooked on post-authentication, and
413 : : * we can initialize the sepgsql_mode and client_label correctly.
414 : : */
4923 415 [ # # ]: 0 : if (getcon_raw(&client_label_peer) < 0)
4952 416 [ # # ]: 0 : ereport(ERROR,
417 : : (errcode(ERRCODE_INTERNAL_ERROR),
418 : : errmsg("SELinux: failed to get server security label: %m")));
419 : :
420 : : /* Client authentication hook */
421 : 0 : next_client_auth_hook = ClientAuthentication_hook;
422 : 0 : ClientAuthentication_hook = sepgsql_client_auth;
423 : :
424 : : /* Trusted procedure hooks */
425 : 0 : next_needs_fmgr_hook = needs_fmgr_hook;
426 : 0 : needs_fmgr_hook = sepgsql_needs_fmgr_hook;
427 : :
428 : 0 : next_fmgr_hook = fmgr_hook;
429 : 0 : fmgr_hook = sepgsql_fmgr_hook;
430 : :
431 : : /* Transaction/Sub-transaction callbacks */
4923 432 : 0 : RegisterXactCallback(sepgsql_xact_callback, NULL);
433 : 0 : RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
5340 434 : 0 : }
435 : :
436 : : /*
437 : : * sepgsql_get_label
438 : : *
439 : : * It returns a security context of the specified database object.
440 : : * If unlabeled or incorrectly labeled, the system "unlabeled" label
441 : : * shall be returned.
442 : : */
443 : : char *
444 : 0 : sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
445 : : {
446 : : ObjectAddress object;
447 : : char *label;
448 : :
5263 bruce@momjian.us 449 : 0 : object.classId = classId;
450 : 0 : object.objectId = objectId;
451 : 0 : object.objectSubId = subId;
452 : :
5340 rhaas@postgresql.org 453 : 0 : label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
1849 michael@paquier.xyz 454 [ # # # # ]: 0 : if (!label || security_check_context_raw(label))
455 : : {
456 : : char *unlabeled;
457 : :
5340 rhaas@postgresql.org 458 [ # # ]: 0 : if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
459 [ # # ]: 0 : ereport(ERROR,
460 : : (errcode(ERRCODE_INTERNAL_ERROR),
461 : : errmsg("SELinux: failed to get initial security label: %m")));
462 [ # # ]: 0 : PG_TRY();
463 : : {
464 : 0 : label = pstrdup(unlabeled);
465 : : }
2136 peter@eisentraut.org 466 : 0 : PG_FINALLY();
467 : : {
5340 rhaas@postgresql.org 468 : 0 : freecon(unlabeled);
469 : : }
470 [ # # ]: 0 : PG_END_TRY();
471 : : }
472 : 0 : return label;
473 : : }
474 : :
475 : : /*
476 : : * sepgsql_object_relabel
477 : : *
478 : : * An entrypoint of SECURITY LABEL statement
479 : : */
480 : : void
481 : 0 : sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
482 : : {
483 : : /*
484 : : * validate format of the supplied security label, if it is security
485 : : * context of selinux.
486 : : */
487 [ # # # # ]: 0 : if (seclabel &&
1849 michael@paquier.xyz 488 : 0 : security_check_context_raw(seclabel) < 0)
5340 rhaas@postgresql.org 489 [ # # ]: 0 : ereport(ERROR,
490 : : (errcode(ERRCODE_INVALID_NAME),
491 : : errmsg("SELinux: invalid security label: \"%s\"", seclabel)));
492 : :
493 : : /*
494 : : * Do actual permission checks for each object classes
495 : : */
496 [ # # # # : 0 : switch (object->classId)
# ]
497 : : {
5097 498 : 0 : case DatabaseRelationId:
499 : 0 : sepgsql_database_relabel(object->objectId, seclabel);
500 : 0 : break;
501 : :
5340 502 : 0 : case NamespaceRelationId:
5263 bruce@momjian.us 503 : 0 : sepgsql_schema_relabel(object->objectId, seclabel);
5340 rhaas@postgresql.org 504 : 0 : break;
505 : :
506 : 0 : case RelationRelationId:
507 [ # # ]: 0 : if (object->objectSubId == 0)
508 : 0 : sepgsql_relation_relabel(object->objectId,
509 : : seclabel);
510 : : else
511 : 0 : sepgsql_attribute_relabel(object->objectId,
512 : 0 : object->objectSubId,
513 : : seclabel);
514 : 0 : break;
515 : :
516 : 0 : case ProcedureRelationId:
517 : 0 : sepgsql_proc_relabel(object->objectId, seclabel);
518 : 0 : break;
519 : :
520 : 0 : default:
3832 521 [ # # ]: 0 : ereport(ERROR,
522 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
523 : : errmsg("sepgsql provider does not support labels on %s",
524 : : getObjectTypeDescription(object, false))));
525 : : break;
526 : : }
5340 527 : 0 : }
528 : :
529 : : /*
530 : : * TEXT sepgsql_getcon(VOID)
531 : : *
532 : : * It returns the security label of the client.
533 : : */
534 : 0 : PG_FUNCTION_INFO_V1(sepgsql_getcon);
535 : : Datum
536 : 0 : sepgsql_getcon(PG_FUNCTION_ARGS)
537 : : {
538 : : char *client_label;
539 : :
540 [ # # ]: 0 : if (!sepgsql_is_enabled())
541 : 0 : PG_RETURN_NULL();
542 : :
543 : 0 : client_label = sepgsql_get_client_label();
544 : :
545 : 0 : PG_RETURN_TEXT_P(cstring_to_text(client_label));
546 : : }
547 : :
548 : : /*
549 : : * BOOL sepgsql_setcon(TEXT)
550 : : *
551 : : * It switches the security label of the client.
552 : : */
4923 553 : 0 : PG_FUNCTION_INFO_V1(sepgsql_setcon);
554 : : Datum
555 : 0 : sepgsql_setcon(PG_FUNCTION_ARGS)
556 : : {
557 : : const char *new_label;
558 : :
559 [ # # ]: 0 : if (PG_ARGISNULL(0))
560 : 0 : new_label = NULL;
561 : : else
562 : 0 : new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
563 : :
564 : 0 : sepgsql_set_client_label(new_label);
565 : :
566 : 0 : PG_RETURN_BOOL(true);
567 : : }
568 : :
569 : : /*
570 : : * TEXT sepgsql_mcstrans_in(TEXT)
571 : : *
572 : : * It translate the given qualified MLS/MCS range into raw format
573 : : * when mcstrans daemon is working.
574 : : */
5340 575 : 0 : PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
576 : : Datum
577 : 0 : sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
578 : : {
3100 noah@leadboat.com 579 : 0 : text *label = PG_GETARG_TEXT_PP(0);
580 : : char *raw_label;
581 : : char *result;
582 : :
5340 rhaas@postgresql.org 583 [ # # ]: 0 : if (!sepgsql_is_enabled())
584 [ # # ]: 0 : ereport(ERROR,
585 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
586 : : errmsg("sepgsql is not enabled")));
587 : :
588 [ # # ]: 0 : if (selinux_trans_to_raw_context(text_to_cstring(label),
589 : : &raw_label) < 0)
590 [ # # ]: 0 : ereport(ERROR,
591 : : (errcode(ERRCODE_INTERNAL_ERROR),
592 : : errmsg("SELinux: could not translate security label: %m")));
593 : :
594 [ # # ]: 0 : PG_TRY();
595 : : {
596 : 0 : result = pstrdup(raw_label);
597 : : }
2136 peter@eisentraut.org 598 : 0 : PG_FINALLY();
599 : : {
5340 rhaas@postgresql.org 600 : 0 : freecon(raw_label);
601 : : }
602 [ # # ]: 0 : PG_END_TRY();
603 : :
604 : 0 : PG_RETURN_TEXT_P(cstring_to_text(result));
605 : : }
606 : :
607 : : /*
608 : : * TEXT sepgsql_mcstrans_out(TEXT)
609 : : *
610 : : * It translate the given raw MLS/MCS range into qualified format
611 : : * when mcstrans daemon is working.
612 : : */
613 : 0 : PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
614 : : Datum
615 : 0 : sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
616 : : {
3100 noah@leadboat.com 617 : 0 : text *label = PG_GETARG_TEXT_PP(0);
618 : : char *qual_label;
619 : : char *result;
620 : :
5340 rhaas@postgresql.org 621 [ # # ]: 0 : if (!sepgsql_is_enabled())
622 [ # # ]: 0 : ereport(ERROR,
623 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
624 : : errmsg("sepgsql is not currently enabled")));
625 : :
626 [ # # ]: 0 : if (selinux_raw_to_trans_context(text_to_cstring(label),
627 : : &qual_label) < 0)
628 [ # # ]: 0 : ereport(ERROR,
629 : : (errcode(ERRCODE_INTERNAL_ERROR),
630 : : errmsg("SELinux: could not translate security label: %m")));
631 : :
632 [ # # ]: 0 : PG_TRY();
633 : : {
634 : 0 : result = pstrdup(qual_label);
635 : : }
2136 peter@eisentraut.org 636 : 0 : PG_FINALLY();
637 : : {
5340 rhaas@postgresql.org 638 : 0 : freecon(qual_label);
639 : : }
640 [ # # ]: 0 : PG_END_TRY();
641 : :
642 : 0 : PG_RETURN_TEXT_P(cstring_to_text(result));
643 : : }
644 : :
645 : : /*
646 : : * quote_object_name
647 : : *
648 : : * Concatenate as many of the given strings as aren't NULL, with dots between.
649 : : * Quote any of the strings that wouldn't be valid identifiers otherwise.
650 : : */
651 : : static char *
5330 652 : 0 : quote_object_name(const char *src1, const char *src2,
653 : : const char *src3, const char *src4)
654 : : {
655 : : StringInfoData result;
656 : :
657 : 0 : initStringInfo(&result);
658 [ # # ]: 0 : if (src1)
1106 tgl@sss.pgh.pa.us 659 : 0 : appendStringInfoString(&result, quote_identifier(src1));
5330 rhaas@postgresql.org 660 [ # # ]: 0 : if (src2)
1106 tgl@sss.pgh.pa.us 661 : 0 : appendStringInfo(&result, ".%s", quote_identifier(src2));
5330 rhaas@postgresql.org 662 [ # # ]: 0 : if (src3)
1106 tgl@sss.pgh.pa.us 663 : 0 : appendStringInfo(&result, ".%s", quote_identifier(src3));
5330 rhaas@postgresql.org 664 [ # # ]: 0 : if (src4)
1106 tgl@sss.pgh.pa.us 665 : 0 : appendStringInfo(&result, ".%s", quote_identifier(src4));
5330 rhaas@postgresql.org 666 : 0 : return result.data;
667 : : }
668 : :
669 : : /*
670 : : * exec_object_restorecon
671 : : *
672 : : * This routine is a helper called by sepgsql_restorecon; it set up
673 : : * initial security labels of database objects within the supplied
674 : : * catalog OID.
675 : : */
676 : : static void
2999 tgl@sss.pgh.pa.us 677 : 0 : exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
678 : : {
679 : : Relation rel;
680 : : SysScanDesc sscan;
681 : : HeapTuple tuple;
5263 bruce@momjian.us 682 : 0 : char *database_name = get_database_name(MyDatabaseId);
683 : : char *namespace_name;
684 : : Oid namespace_id;
685 : : char *relation_name;
686 : :
687 : : /*
688 : : * Open the target catalog. We don't want to allow writable accesses by
689 : : * other session during initial labeling.
690 : : */
2420 andres@anarazel.de 691 : 0 : rel = table_open(catalogId, AccessShareLock);
692 : :
5340 rhaas@postgresql.org 693 : 0 : sscan = systable_beginscan(rel, InvalidOid, false,
694 : : NULL, 0, NULL);
695 [ # # ]: 0 : while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
696 : : {
697 : : Form_pg_database datForm;
698 : : Form_pg_namespace nspForm;
699 : : Form_pg_class relForm;
700 : : Form_pg_attribute attForm;
701 : : Form_pg_proc proForm;
702 : : char *objname;
5263 bruce@momjian.us 703 : 0 : int objtype = 1234;
704 : : ObjectAddress object;
705 : : char *context;
706 : :
707 : : /*
708 : : * The way to determine object name depends on object classes. So, any
709 : : * branches set up `objtype', `objname' and `object' here.
710 : : */
5340 rhaas@postgresql.org 711 [ # # # # : 0 : switch (catalogId)
# # ]
712 : : {
5097 713 : 0 : case DatabaseRelationId:
714 : 0 : datForm = (Form_pg_database) GETSTRUCT(tuple);
715 : :
716 : 0 : objtype = SELABEL_DB_DATABASE;
717 : :
718 : 0 : objname = quote_object_name(NameStr(datForm->datname),
719 : : NULL, NULL, NULL);
720 : :
721 : 0 : object.classId = DatabaseRelationId;
2482 andres@anarazel.de 722 : 0 : object.objectId = datForm->oid;
5097 rhaas@postgresql.org 723 : 0 : object.objectSubId = 0;
724 : 0 : break;
725 : :
5340 726 : 0 : case NamespaceRelationId:
727 : 0 : nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
728 : :
729 : 0 : objtype = SELABEL_DB_SCHEMA;
730 : :
5330 731 : 0 : objname = quote_object_name(database_name,
732 : 0 : NameStr(nspForm->nspname),
733 : : NULL, NULL);
734 : :
5340 735 : 0 : object.classId = NamespaceRelationId;
2482 andres@anarazel.de 736 : 0 : object.objectId = nspForm->oid;
5340 rhaas@postgresql.org 737 : 0 : object.objectSubId = 0;
738 : 0 : break;
739 : :
740 : 0 : case RelationRelationId:
741 : 0 : relForm = (Form_pg_class) GETSTRUCT(tuple);
742 : :
3072 mail@joeconway.com 743 [ # # ]: 0 : if (relForm->relkind == RELKIND_RELATION ||
744 [ # # ]: 0 : relForm->relkind == RELKIND_PARTITIONED_TABLE)
5340 rhaas@postgresql.org 745 : 0 : objtype = SELABEL_DB_TABLE;
746 [ # # ]: 0 : else if (relForm->relkind == RELKIND_SEQUENCE)
747 : 0 : objtype = SELABEL_DB_SEQUENCE;
748 [ # # ]: 0 : else if (relForm->relkind == RELKIND_VIEW)
749 : 0 : objtype = SELABEL_DB_VIEW;
750 : : else
751 : 0 : continue; /* no need to assign security label */
752 : :
753 : 0 : namespace_name = get_namespace_name(relForm->relnamespace);
5330 754 : 0 : objname = quote_object_name(database_name,
755 : : namespace_name,
756 : 0 : NameStr(relForm->relname),
757 : : NULL);
5340 758 : 0 : pfree(namespace_name);
759 : :
760 : 0 : object.classId = RelationRelationId;
2482 andres@anarazel.de 761 : 0 : object.objectId = relForm->oid;
5340 rhaas@postgresql.org 762 : 0 : object.objectSubId = 0;
763 : 0 : break;
764 : :
765 : 0 : case AttributeRelationId:
766 : 0 : attForm = (Form_pg_attribute) GETSTRUCT(tuple);
767 : :
3072 mail@joeconway.com 768 [ # # ]: 0 : if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION &&
769 [ # # ]: 0 : get_rel_relkind(attForm->attrelid) != RELKIND_PARTITIONED_TABLE)
5340 rhaas@postgresql.org 770 : 0 : continue; /* no need to assign security label */
771 : :
772 : 0 : objtype = SELABEL_DB_COLUMN;
773 : :
774 : 0 : namespace_id = get_rel_namespace(attForm->attrelid);
775 : 0 : namespace_name = get_namespace_name(namespace_id);
776 : 0 : relation_name = get_rel_name(attForm->attrelid);
5330 777 : 0 : objname = quote_object_name(database_name,
778 : : namespace_name,
779 : : relation_name,
780 : 0 : NameStr(attForm->attname));
5340 781 : 0 : pfree(namespace_name);
5330 782 : 0 : pfree(relation_name);
783 : :
5340 784 : 0 : object.classId = RelationRelationId;
785 : 0 : object.objectId = attForm->attrelid;
786 : 0 : object.objectSubId = attForm->attnum;
787 : 0 : break;
788 : :
789 : 0 : case ProcedureRelationId:
790 : 0 : proForm = (Form_pg_proc) GETSTRUCT(tuple);
791 : :
792 : 0 : objtype = SELABEL_DB_PROCEDURE;
793 : :
794 : 0 : namespace_name = get_namespace_name(proForm->pronamespace);
5330 795 : 0 : objname = quote_object_name(database_name,
796 : : namespace_name,
797 : 0 : NameStr(proForm->proname),
798 : : NULL);
5340 799 : 0 : pfree(namespace_name);
800 : :
801 : 0 : object.classId = ProcedureRelationId;
2482 andres@anarazel.de 802 : 0 : object.objectId = proForm->oid;
5340 rhaas@postgresql.org 803 : 0 : object.objectSubId = 0;
804 : 0 : break;
805 : :
806 : 0 : default:
807 [ # # ]: 0 : elog(ERROR, "unexpected catalog id: %u", catalogId);
808 : : objname = NULL; /* for compiler quiet */
809 : : break;
810 : : }
811 : :
812 [ # # ]: 0 : if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
813 : : {
814 [ # # ]: 0 : PG_TRY();
815 : : {
816 : : /*
817 : : * Check SELinux permission to relabel the fetched object,
818 : : * then do the actual relabeling.
819 : : */
820 : 0 : sepgsql_object_relabel(&object, context);
821 : :
822 : 0 : SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
823 : : }
2136 peter@eisentraut.org 824 : 0 : PG_FINALLY();
825 : : {
5340 rhaas@postgresql.org 826 : 0 : freecon(context);
827 : : }
828 [ # # ]: 0 : PG_END_TRY();
829 : : }
830 [ # # ]: 0 : else if (errno == ENOENT)
831 [ # # ]: 0 : ereport(WARNING,
832 : : (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
833 : : objname, objtype)));
834 : : else
835 [ # # ]: 0 : ereport(ERROR,
836 : : (errcode(ERRCODE_INTERNAL_ERROR),
837 : : errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
838 : :
5330 839 : 0 : pfree(objname);
840 : : }
5340 841 : 0 : systable_endscan(sscan);
842 : :
2420 andres@anarazel.de 843 : 0 : table_close(rel, NoLock);
5340 rhaas@postgresql.org 844 : 0 : }
845 : :
846 : : /*
847 : : * BOOL sepgsql_restorecon(TEXT specfile)
848 : : *
849 : : * This function tries to assign initial security labels on all the object
850 : : * within the current database, according to the system setting.
851 : : * It is typically invoked by sepgsql-install script just after initdb, to
852 : : * assign initial security labels.
853 : : *
854 : : * If @specfile is not NULL, it uses explicitly specified specfile, instead
855 : : * of the system default.
856 : : */
857 : 0 : PG_FUNCTION_INFO_V1(sepgsql_restorecon);
858 : : Datum
859 : 0 : sepgsql_restorecon(PG_FUNCTION_ARGS)
860 : : {
861 : : struct selabel_handle *sehnd;
862 : : struct selinux_opt seopts;
863 : :
864 : : /*
865 : : * SELinux has to be enabled on the running platform.
866 : : */
867 [ # # ]: 0 : if (!sepgsql_is_enabled())
868 [ # # ]: 0 : ereport(ERROR,
869 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
870 : : errmsg("sepgsql is not currently enabled")));
871 : :
872 : : /*
873 : : * Check DAC permission. Only superuser can set up initial security
874 : : * labels, like root-user in filesystems
875 : : */
876 [ # # ]: 0 : if (!superuser())
877 [ # # ]: 0 : ereport(ERROR,
878 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
879 : : errmsg("SELinux: must be superuser to restore initial contexts")));
880 : :
881 : : /*
882 : : * Open selabel_lookup(3) stuff. It provides a set of mapping between an
883 : : * initial security label and object class/name due to the system setting.
884 : : */
885 [ # # ]: 0 : if (PG_ARGISNULL(0))
886 : : {
887 : 0 : seopts.type = SELABEL_OPT_UNUSED;
888 : 0 : seopts.value = NULL;
889 : : }
890 : : else
891 : : {
892 : 0 : seopts.type = SELABEL_OPT_PATH;
893 : 0 : seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
894 : : }
895 : 0 : sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
896 [ # # ]: 0 : if (!sehnd)
897 [ # # ]: 0 : ereport(ERROR,
898 : : (errcode(ERRCODE_INTERNAL_ERROR),
899 : : errmsg("SELinux: failed to initialize labeling handle: %m")));
900 [ # # ]: 0 : PG_TRY();
901 : : {
5097 902 : 0 : exec_object_restorecon(sehnd, DatabaseRelationId);
5340 903 : 0 : exec_object_restorecon(sehnd, NamespaceRelationId);
904 : 0 : exec_object_restorecon(sehnd, RelationRelationId);
905 : 0 : exec_object_restorecon(sehnd, AttributeRelationId);
906 : 0 : exec_object_restorecon(sehnd, ProcedureRelationId);
907 : : }
2136 peter@eisentraut.org 908 : 0 : PG_FINALLY();
909 : : {
5340 rhaas@postgresql.org 910 : 0 : selabel_close(sehnd);
911 : : }
5263 bruce@momjian.us 912 [ # # ]: 0 : PG_END_TRY();
913 : :
5340 rhaas@postgresql.org 914 : 0 : PG_RETURN_BOOL(true);
915 : : }
|