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