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