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