1 /* -------------------------------------------------------------------------
2 *
3 * contrib/sepgsql/label.c
4 *
5 * Routines to support SELinux labels (security context)
6 *
7 * Copyright (c) 2010-2021, 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 *
sepgsql_get_client_label(void)80 sepgsql_get_client_label(void)
81 {
82 /* trusted procedure client label override */
83 if (client_label_func)
84 return client_label_func;
85
86 /* uncommitted sepgsql_setcon() value */
87 if (client_label_pending)
88 {
89 pending_label *plabel = llast(client_label_pending);
90
91 if (plabel->label)
92 return plabel->label;
93 }
94 else if (client_label_committed)
95 return client_label_committed; /* set by sepgsql_setcon() committed */
96
97 /* default label */
98 Assert(client_label_peer != NULL);
99 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
sepgsql_set_client_label(const char * new_label)111 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 if (!new_label)
119 tcontext = client_label_peer;
120 else
121 {
122 if (security_check_context_raw(new_label) < 0)
123 ereport(ERROR,
124 (errcode(ERRCODE_INVALID_NAME),
125 errmsg("SELinux: invalid security label: \"%s\"",
126 new_label)));
127 tcontext = new_label;
128 }
129
130 /* Check process:{setcurrent} permission. */
131 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 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 oldcxt = MemoryContextSwitchTo(CurTransactionContext);
148
149 plabel = palloc0(sizeof(pending_label));
150 plabel->subid = GetCurrentSubTransactionId();
151 if (new_label)
152 plabel->label = pstrdup(new_label);
153 client_label_pending = lappend(client_label_pending, plabel);
154
155 MemoryContextSwitchTo(oldcxt);
156 }
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
sepgsql_xact_callback(XactEvent event,void * arg)165 sepgsql_xact_callback(XactEvent event, void *arg)
166 {
167 if (event == XACT_EVENT_COMMIT)
168 {
169 if (client_label_pending != NIL)
170 {
171 pending_label *plabel = llast(client_label_pending);
172 char *new_label;
173
174 if (plabel->label)
175 new_label = MemoryContextStrdup(TopMemoryContext,
176 plabel->label);
177 else
178 new_label = NULL;
179
180 if (client_label_committed)
181 pfree(client_label_committed);
182
183 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 client_label_pending = NIL;
191 }
192 }
193 else if (event == XACT_EVENT_ABORT)
194 client_label_pending = NIL;
195 }
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
sepgsql_subxact_callback(SubXactEvent event,SubTransactionId mySubid,SubTransactionId parentSubid,void * arg)204 sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
205 SubTransactionId parentSubid, void *arg)
206 {
207 ListCell *cell;
208
209 if (event == SUBXACT_EVENT_ABORT_SUB)
210 {
211 foreach(cell, client_label_pending)
212 {
213 pending_label *plabel = lfirst(cell);
214
215 if (plabel->subid == mySubid)
216 client_label_pending
217 = foreach_delete_current(client_label_pending, cell);
218 }
219 }
220 }
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
sepgsql_client_auth(Port * port,int status)230 sepgsql_client_auth(Port *port, int status)
231 {
232 if (next_client_auth_hook)
233 (*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 if (status != STATUS_OK)
240 return;
241
242 /*
243 * Getting security label of the peer process using API of libselinux.
244 */
245 if (getpeercon_raw(port->sock, &client_label_peer) < 0)
246 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 if (sepgsql_get_permissive())
255 sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
256 else
257 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
sepgsql_needs_fmgr_hook(Oid functionId)268 sepgsql_needs_fmgr_hook(Oid functionId)
269 {
270 ObjectAddress object;
271
272 if (next_needs_fmgr_hook &&
273 (*next_needs_fmgr_hook) (functionId))
274 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 if (sepgsql_avc_trusted_proc(functionId) != NULL)
283 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 object.classId = ProcedureRelationId;
292 object.objectId = functionId;
293 object.objectSubId = 0;
294 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 return true;
300
301 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
sepgsql_fmgr_hook(FmgrHookEventType event,FmgrInfo * flinfo,Datum * private)311 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 switch (event)
322 {
323 case FHET_START:
324 stack = (void *) DatumGetPointer(*private);
325 if (!stack)
326 {
327 MemoryContext oldcxt;
328
329 oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
330 stack = palloc(sizeof(*stack));
331 stack->old_label = NULL;
332 stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
333 stack->next_private = 0;
334
335 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 if (stack->new_label)
348 {
349 ObjectAddress object;
350
351 object.classId = ProcedureRelationId;
352 object.objectId = flinfo->fn_oid;
353 object.objectSubId = 0;
354 sepgsql_avc_check_perms(&object,
355 SEPG_CLASS_DB_PROCEDURE,
356 SEPG_DB_PROCEDURE__ENTRYPOINT,
357 getObjectDescription(&object, false),
358 true);
359
360 sepgsql_avc_check_perms_label(stack->new_label,
361 SEPG_CLASS_PROCESS,
362 SEPG_PROCESS__TRANSITION,
363 NULL, true);
364 }
365 *private = PointerGetDatum(stack);
366 }
367 Assert(!stack->old_label);
368 if (stack->new_label)
369 {
370 stack->old_label = client_label_func;
371 client_label_func = stack->new_label;
372 }
373 if (next_fmgr_hook)
374 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
375 break;
376
377 case FHET_END:
378 case FHET_ABORT:
379 stack = (void *) DatumGetPointer(*private);
380
381 if (next_fmgr_hook)
382 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
383
384 if (stack->new_label)
385 {
386 client_label_func = stack->old_label;
387 stack->old_label = NULL;
388 }
389 break;
390
391 default:
392 elog(ERROR, "unexpected event type: %d", (int) event);
393 break;
394 }
395 }
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
sepgsql_init_client_label(void)404 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 */
416 if (getcon_raw(&client_label_peer) < 0)
417 ereport(ERROR,
418 (errcode(ERRCODE_INTERNAL_ERROR),
419 errmsg("SELinux: failed to get server security label: %m")));
420
421 /* Client authentication hook */
422 next_client_auth_hook = ClientAuthentication_hook;
423 ClientAuthentication_hook = sepgsql_client_auth;
424
425 /* Trusted procedure hooks */
426 next_needs_fmgr_hook = needs_fmgr_hook;
427 needs_fmgr_hook = sepgsql_needs_fmgr_hook;
428
429 next_fmgr_hook = fmgr_hook;
430 fmgr_hook = sepgsql_fmgr_hook;
431
432 /* Transaction/Sub-transaction callbacks */
433 RegisterXactCallback(sepgsql_xact_callback, NULL);
434 RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
435 }
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 *
sepgsql_get_label(Oid classId,Oid objectId,int32 subId)445 sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
446 {
447 ObjectAddress object;
448 char *label;
449
450 object.classId = classId;
451 object.objectId = objectId;
452 object.objectSubId = subId;
453
454 label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
455 if (!label || security_check_context_raw(label))
456 {
457 char *unlabeled;
458
459 if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
460 ereport(ERROR,
461 (errcode(ERRCODE_INTERNAL_ERROR),
462 errmsg("SELinux: failed to get initial security label: %m")));
463 PG_TRY();
464 {
465 label = pstrdup(unlabeled);
466 }
467 PG_FINALLY();
468 {
469 freecon(unlabeled);
470 }
471 PG_END_TRY();
472 }
473 return label;
474 }
475
476 /*
477 * sepgsql_object_relabel
478 *
479 * An entrypoint of SECURITY LABEL statement
480 */
481 void
sepgsql_object_relabel(const ObjectAddress * object,const char * seclabel)482 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 if (seclabel &&
489 security_check_context_raw(seclabel) < 0)
490 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 switch (object->classId)
498 {
499 case DatabaseRelationId:
500 sepgsql_database_relabel(object->objectId, seclabel);
501 break;
502
503 case NamespaceRelationId:
504 sepgsql_schema_relabel(object->objectId, seclabel);
505 break;
506
507 case RelationRelationId:
508 if (object->objectSubId == 0)
509 sepgsql_relation_relabel(object->objectId,
510 seclabel);
511 else
512 sepgsql_attribute_relabel(object->objectId,
513 object->objectSubId,
514 seclabel);
515 break;
516
517 case ProcedureRelationId:
518 sepgsql_proc_relabel(object->objectId, seclabel);
519 break;
520
521 default:
522 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 }
528 }
529
530 /*
531 * TEXT sepgsql_getcon(VOID)
532 *
533 * It returns the security label of the client.
534 */
535 PG_FUNCTION_INFO_V1(sepgsql_getcon);
536 Datum
sepgsql_getcon(PG_FUNCTION_ARGS)537 sepgsql_getcon(PG_FUNCTION_ARGS)
538 {
539 char *client_label;
540
541 if (!sepgsql_is_enabled())
542 PG_RETURN_NULL();
543
544 client_label = sepgsql_get_client_label();
545
546 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 */
554 PG_FUNCTION_INFO_V1(sepgsql_setcon);
555 Datum
sepgsql_setcon(PG_FUNCTION_ARGS)556 sepgsql_setcon(PG_FUNCTION_ARGS)
557 {
558 const char *new_label;
559
560 if (PG_ARGISNULL(0))
561 new_label = NULL;
562 else
563 new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
564
565 sepgsql_set_client_label(new_label);
566
567 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 */
576 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
577 Datum
sepgsql_mcstrans_in(PG_FUNCTION_ARGS)578 sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
579 {
580 text *label = PG_GETARG_TEXT_PP(0);
581 char *raw_label;
582 char *result;
583
584 if (!sepgsql_is_enabled())
585 ereport(ERROR,
586 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
587 errmsg("sepgsql is not enabled")));
588
589 if (selinux_trans_to_raw_context(text_to_cstring(label),
590 &raw_label) < 0)
591 ereport(ERROR,
592 (errcode(ERRCODE_INTERNAL_ERROR),
593 errmsg("SELinux: could not translate security label: %m")));
594
595 PG_TRY();
596 {
597 result = pstrdup(raw_label);
598 }
599 PG_FINALLY();
600 {
601 freecon(raw_label);
602 }
603 PG_END_TRY();
604
605 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 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
615 Datum
sepgsql_mcstrans_out(PG_FUNCTION_ARGS)616 sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
617 {
618 text *label = PG_GETARG_TEXT_PP(0);
619 char *qual_label;
620 char *result;
621
622 if (!sepgsql_is_enabled())
623 ereport(ERROR,
624 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
625 errmsg("sepgsql is not currently enabled")));
626
627 if (selinux_raw_to_trans_context(text_to_cstring(label),
628 &qual_label) < 0)
629 ereport(ERROR,
630 (errcode(ERRCODE_INTERNAL_ERROR),
631 errmsg("SELinux: could not translate security label: %m")));
632
633 PG_TRY();
634 {
635 result = pstrdup(qual_label);
636 }
637 PG_FINALLY();
638 {
639 freecon(qual_label);
640 }
641 PG_END_TRY();
642
643 PG_RETURN_TEXT_P(cstring_to_text(result));
644 }
645
646 /*
647 * quote_object_name
648 *
649 * It tries to quote the supplied identifiers
650 */
651 static char *
quote_object_name(const char * src1,const char * src2,const char * src3,const char * src4)652 quote_object_name(const char *src1, const char *src2,
653 const char *src3, const char *src4)
654 {
655 StringInfoData result;
656 const char *temp;
657
658 initStringInfo(&result);
659
660 if (src1)
661 {
662 temp = quote_identifier(src1);
663 appendStringInfoString(&result, temp);
664 if (src1 != temp)
665 pfree((void *) temp);
666 }
667 if (src2)
668 {
669 temp = quote_identifier(src2);
670 appendStringInfo(&result, ".%s", temp);
671 if (src2 != temp)
672 pfree((void *) temp);
673 }
674 if (src3)
675 {
676 temp = quote_identifier(src3);
677 appendStringInfo(&result, ".%s", temp);
678 if (src3 != temp)
679 pfree((void *) temp);
680 }
681 if (src4)
682 {
683 temp = quote_identifier(src4);
684 appendStringInfo(&result, ".%s", temp);
685 if (src4 != temp)
686 pfree((void *) temp);
687 }
688 return result.data;
689 }
690
691 /*
692 * exec_object_restorecon
693 *
694 * This routine is a helper called by sepgsql_restorecon; it set up
695 * initial security labels of database objects within the supplied
696 * catalog OID.
697 */
698 static void
exec_object_restorecon(struct selabel_handle * sehnd,Oid catalogId)699 exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
700 {
701 Relation rel;
702 SysScanDesc sscan;
703 HeapTuple tuple;
704 char *database_name = get_database_name(MyDatabaseId);
705 char *namespace_name;
706 Oid namespace_id;
707 char *relation_name;
708
709 /*
710 * Open the target catalog. We don't want to allow writable accesses by
711 * other session during initial labeling.
712 */
713 rel = table_open(catalogId, AccessShareLock);
714
715 sscan = systable_beginscan(rel, InvalidOid, false,
716 NULL, 0, NULL);
717 while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
718 {
719 Form_pg_database datForm;
720 Form_pg_namespace nspForm;
721 Form_pg_class relForm;
722 Form_pg_attribute attForm;
723 Form_pg_proc proForm;
724 char *objname;
725 int objtype = 1234;
726 ObjectAddress object;
727 char *context;
728
729 /*
730 * The way to determine object name depends on object classes. So, any
731 * branches set up `objtype', `objname' and `object' here.
732 */
733 switch (catalogId)
734 {
735 case DatabaseRelationId:
736 datForm = (Form_pg_database) GETSTRUCT(tuple);
737
738 objtype = SELABEL_DB_DATABASE;
739
740 objname = quote_object_name(NameStr(datForm->datname),
741 NULL, NULL, NULL);
742
743 object.classId = DatabaseRelationId;
744 object.objectId = datForm->oid;
745 object.objectSubId = 0;
746 break;
747
748 case NamespaceRelationId:
749 nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
750
751 objtype = SELABEL_DB_SCHEMA;
752
753 objname = quote_object_name(database_name,
754 NameStr(nspForm->nspname),
755 NULL, NULL);
756
757 object.classId = NamespaceRelationId;
758 object.objectId = nspForm->oid;
759 object.objectSubId = 0;
760 break;
761
762 case RelationRelationId:
763 relForm = (Form_pg_class) GETSTRUCT(tuple);
764
765 if (relForm->relkind == RELKIND_RELATION ||
766 relForm->relkind == RELKIND_PARTITIONED_TABLE)
767 objtype = SELABEL_DB_TABLE;
768 else if (relForm->relkind == RELKIND_SEQUENCE)
769 objtype = SELABEL_DB_SEQUENCE;
770 else if (relForm->relkind == RELKIND_VIEW)
771 objtype = SELABEL_DB_VIEW;
772 else
773 continue; /* no need to assign security label */
774
775 namespace_name = get_namespace_name(relForm->relnamespace);
776 objname = quote_object_name(database_name,
777 namespace_name,
778 NameStr(relForm->relname),
779 NULL);
780 pfree(namespace_name);
781
782 object.classId = RelationRelationId;
783 object.objectId = relForm->oid;
784 object.objectSubId = 0;
785 break;
786
787 case AttributeRelationId:
788 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
789
790 if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION &&
791 get_rel_relkind(attForm->attrelid) != RELKIND_PARTITIONED_TABLE)
792 continue; /* no need to assign security label */
793
794 objtype = SELABEL_DB_COLUMN;
795
796 namespace_id = get_rel_namespace(attForm->attrelid);
797 namespace_name = get_namespace_name(namespace_id);
798 relation_name = get_rel_name(attForm->attrelid);
799 objname = quote_object_name(database_name,
800 namespace_name,
801 relation_name,
802 NameStr(attForm->attname));
803 pfree(namespace_name);
804 pfree(relation_name);
805
806 object.classId = RelationRelationId;
807 object.objectId = attForm->attrelid;
808 object.objectSubId = attForm->attnum;
809 break;
810
811 case ProcedureRelationId:
812 proForm = (Form_pg_proc) GETSTRUCT(tuple);
813
814 objtype = SELABEL_DB_PROCEDURE;
815
816 namespace_name = get_namespace_name(proForm->pronamespace);
817 objname = quote_object_name(database_name,
818 namespace_name,
819 NameStr(proForm->proname),
820 NULL);
821 pfree(namespace_name);
822
823 object.classId = ProcedureRelationId;
824 object.objectId = proForm->oid;
825 object.objectSubId = 0;
826 break;
827
828 default:
829 elog(ERROR, "unexpected catalog id: %u", catalogId);
830 objname = NULL; /* for compiler quiet */
831 break;
832 }
833
834 if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
835 {
836 PG_TRY();
837 {
838 /*
839 * Check SELinux permission to relabel the fetched object,
840 * then do the actual relabeling.
841 */
842 sepgsql_object_relabel(&object, context);
843
844 SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
845 }
846 PG_FINALLY();
847 {
848 freecon(context);
849 }
850 PG_END_TRY();
851 }
852 else if (errno == ENOENT)
853 ereport(WARNING,
854 (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
855 objname, objtype)));
856 else
857 ereport(ERROR,
858 (errcode(ERRCODE_INTERNAL_ERROR),
859 errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
860
861 pfree(objname);
862 }
863 systable_endscan(sscan);
864
865 table_close(rel, NoLock);
866 }
867
868 /*
869 * BOOL sepgsql_restorecon(TEXT specfile)
870 *
871 * This function tries to assign initial security labels on all the object
872 * within the current database, according to the system setting.
873 * It is typically invoked by sepgsql-install script just after initdb, to
874 * assign initial security labels.
875 *
876 * If @specfile is not NULL, it uses explicitly specified specfile, instead
877 * of the system default.
878 */
879 PG_FUNCTION_INFO_V1(sepgsql_restorecon);
880 Datum
sepgsql_restorecon(PG_FUNCTION_ARGS)881 sepgsql_restorecon(PG_FUNCTION_ARGS)
882 {
883 struct selabel_handle *sehnd;
884 struct selinux_opt seopts;
885
886 /*
887 * SELinux has to be enabled on the running platform.
888 */
889 if (!sepgsql_is_enabled())
890 ereport(ERROR,
891 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
892 errmsg("sepgsql is not currently enabled")));
893
894 /*
895 * Check DAC permission. Only superuser can set up initial security
896 * labels, like root-user in filesystems
897 */
898 if (!superuser())
899 ereport(ERROR,
900 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
901 errmsg("SELinux: must be superuser to restore initial contexts")));
902
903 /*
904 * Open selabel_lookup(3) stuff. It provides a set of mapping between an
905 * initial security label and object class/name due to the system setting.
906 */
907 if (PG_ARGISNULL(0))
908 {
909 seopts.type = SELABEL_OPT_UNUSED;
910 seopts.value = NULL;
911 }
912 else
913 {
914 seopts.type = SELABEL_OPT_PATH;
915 seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
916 }
917 sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
918 if (!sehnd)
919 ereport(ERROR,
920 (errcode(ERRCODE_INTERNAL_ERROR),
921 errmsg("SELinux: failed to initialize labeling handle: %m")));
922 PG_TRY();
923 {
924 exec_object_restorecon(sehnd, DatabaseRelationId);
925 exec_object_restorecon(sehnd, NamespaceRelationId);
926 exec_object_restorecon(sehnd, RelationRelationId);
927 exec_object_restorecon(sehnd, AttributeRelationId);
928 exec_object_restorecon(sehnd, ProcedureRelationId);
929 }
930 PG_FINALLY();
931 {
932 selabel_close(sehnd);
933 }
934 PG_END_TRY();
935
936 PG_RETURN_BOOL(true);
937 }
938