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