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