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