]> granicus.if.org Git - postgresql/blob - contrib/sepgsql/label.c
Allow sepgsql labels to depend on object name.
[postgresql] / contrib / sepgsql / label.c
1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/label.c
4  *
5  * Routines to support SELinux labels (security context)
6  *
7  * Copyright (c) 2010-2013, PostgreSQL Global Development Group
8  *
9  * -------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12
13 #include "access/heapam.h"
14 #include "access/htup_details.h"
15 #include "access/genam.h"
16 #include "access/xact.h"
17 #include "catalog/catalog.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_attribute.h"
21 #include "catalog/pg_class.h"
22 #include "catalog/pg_database.h"
23 #include "catalog/pg_namespace.h"
24 #include "catalog/pg_proc.h"
25 #include "commands/dbcommands.h"
26 #include "commands/seclabel.h"
27 #include "libpq/auth.h"
28 #include "libpq/libpq-be.h"
29 #include "miscadmin.h"
30 #include "utils/builtins.h"
31 #include "utils/fmgroids.h"
32 #include "utils/guc.h"
33 #include "utils/lsyscache.h"
34 #include "utils/memutils.h"
35 #include "utils/rel.h"
36 #include "utils/tqual.h"
37
38 #include "sepgsql.h"
39
40 #include <selinux/label.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(),
66                                                                                                  * and 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 *
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
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.      Commmit or abort
165  * changes in the client_label_pending list.
166  */
167 static void
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
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
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
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                                                                  SEPGSQL_AVC_NOAUDIT, false))
308                 return true;
309
310         return false;
311 }
312
313 /*
314  * sepgsql_fmgr_hook
315  *
316  * It switches security label of the client on execution of trusted
317  * procedures.
318  */
319 static void
320 sepgsql_fmgr_hook(FmgrHookEventType event,
321                                   FmgrInfo *flinfo, Datum *private)
322 {
323         struct
324         {
325                 char       *old_label;
326                 char       *new_label;
327                 Datum           next_private;
328         }                  *stack;
329
330         switch (event)
331         {
332                 case FHET_START:
333                         stack = (void *) DatumGetPointer(*private);
334                         if (!stack)
335                         {
336                                 MemoryContext oldcxt;
337
338                                 oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
339                                 stack = palloc(sizeof(*stack));
340                                 stack->old_label = NULL;
341                                 stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
342                                 stack->next_private = 0;
343
344                                 MemoryContextSwitchTo(oldcxt);
345
346                                 /*
347                                  * process:transition permission between old and new label,
348                                  * when user tries to switch security label of the client on
349                                  * execution of trusted procedure.
350                                  */
351                                 if (stack->new_label)
352                                         sepgsql_avc_check_perms_label(stack->new_label,
353                                                                                                   SEPG_CLASS_PROCESS,
354                                                                                                   SEPG_PROCESS__TRANSITION,
355                                                                                                   NULL, true);
356
357                                 *private = PointerGetDatum(stack);
358                         }
359                         Assert(!stack->old_label);
360                         if (stack->new_label)
361                         {
362                                 stack->old_label = client_label_func;
363                                 client_label_func = stack->new_label;
364                         }
365                         if (next_fmgr_hook)
366                                 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
367                         break;
368
369                 case FHET_END:
370                 case FHET_ABORT:
371                         stack = (void *) DatumGetPointer(*private);
372
373                         if (next_fmgr_hook)
374                                 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
375
376                         if (stack->new_label)
377                         {
378                                 client_label_func = stack->old_label;
379                                 stack->old_label = NULL;
380                         }
381                         break;
382
383                 default:
384                         elog(ERROR, "unexpected event type: %d", (int) event);
385                         break;
386         }
387 }
388
389 /*
390  * sepgsql_init_client_label
391  *
392  * Initializes the client security label and sets up related hooks for client
393  * label management.
394  */
395 void
396 sepgsql_init_client_label(void)
397 {
398         /*
399          * Set up dummy client label.
400          *
401          * XXX - note that PostgreSQL launches background worker process like
402          * autovacuum without authentication steps. So, we initialize sepgsql_mode
403          * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
404          * of server process. Later, it also launches background of user session.
405          * In this case, the process is always hooked on post-authentication, and
406          * we can initialize the sepgsql_mode and client_label correctly.
407          */
408         if (getcon_raw(&client_label_peer) < 0)
409                 ereport(ERROR,
410                                 (errcode(ERRCODE_INTERNAL_ERROR),
411                                  errmsg("SELinux: failed to get server security label: %m")));
412
413         /* Client authentication hook */
414         next_client_auth_hook = ClientAuthentication_hook;
415         ClientAuthentication_hook = sepgsql_client_auth;
416
417         /* Trusted procedure hooks */
418         next_needs_fmgr_hook = needs_fmgr_hook;
419         needs_fmgr_hook = sepgsql_needs_fmgr_hook;
420
421         next_fmgr_hook = fmgr_hook;
422         fmgr_hook = sepgsql_fmgr_hook;
423
424         /* Transaction/Sub-transaction callbacks */
425         RegisterXactCallback(sepgsql_xact_callback, NULL);
426         RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
427 }
428
429 /*
430  * sepgsql_get_label
431  *
432  * It returns a security context of the specified database object.
433  * If unlabeled or incorrectly labeled, the system "unlabeled" label
434  * shall be returned.
435  */
436 char *
437 sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
438 {
439         ObjectAddress object;
440         char       *label;
441
442         object.classId = classId;
443         object.objectId = objectId;
444         object.objectSubId = subId;
445
446         label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
447         if (!label || security_check_context_raw((security_context_t) label))
448         {
449                 security_context_t unlabeled;
450
451                 if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
452                         ereport(ERROR,
453                                         (errcode(ERRCODE_INTERNAL_ERROR),
454                            errmsg("SELinux: failed to get initial security label: %m")));
455                 PG_TRY();
456                 {
457                         label = pstrdup(unlabeled);
458                 }
459                 PG_CATCH();
460                 {
461                         freecon(unlabeled);
462                         PG_RE_THROW();
463                 }
464                 PG_END_TRY();
465
466                 freecon(unlabeled);
467         }
468         return label;
469 }
470
471 /*
472  * sepgsql_object_relabel
473  *
474  * An entrypoint of SECURITY LABEL statement
475  */
476 void
477 sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
478 {
479         /*
480          * validate format of the supplied security label, if it is security
481          * context of selinux.
482          */
483         if (seclabel &&
484                 security_check_context_raw((security_context_t) seclabel) < 0)
485                 ereport(ERROR,
486                                 (errcode(ERRCODE_INVALID_NAME),
487                            errmsg("SELinux: invalid security label: \"%s\"", seclabel)));
488
489         /*
490          * Do actual permission checks for each object classes
491          */
492         switch (object->classId)
493         {
494                 case DatabaseRelationId:
495                         sepgsql_database_relabel(object->objectId, seclabel);
496                         break;
497
498                 case NamespaceRelationId:
499                         sepgsql_schema_relabel(object->objectId, seclabel);
500                         break;
501
502                 case RelationRelationId:
503                         if (object->objectSubId == 0)
504                                 sepgsql_relation_relabel(object->objectId,
505                                                                                  seclabel);
506                         else
507                                 sepgsql_attribute_relabel(object->objectId,
508                                                                                   object->objectSubId,
509                                                                                   seclabel);
510                         break;
511
512                 case ProcedureRelationId:
513                         sepgsql_proc_relabel(object->objectId, seclabel);
514                         break;
515
516                 default:
517                         elog(ERROR, "unsupported object type: %u", object->classId);
518                         break;
519         }
520 }
521
522 /*
523  * TEXT sepgsql_getcon(VOID)
524  *
525  * It returns the security label of the client.
526  */
527 PG_FUNCTION_INFO_V1(sepgsql_getcon);
528 Datum
529 sepgsql_getcon(PG_FUNCTION_ARGS)
530 {
531         char       *client_label;
532
533         if (!sepgsql_is_enabled())
534                 PG_RETURN_NULL();
535
536         client_label = sepgsql_get_client_label();
537
538         PG_RETURN_TEXT_P(cstring_to_text(client_label));
539 }
540
541 /*
542  * BOOL sepgsql_setcon(TEXT)
543  *
544  * It switches the security label of the client.
545  */
546 PG_FUNCTION_INFO_V1(sepgsql_setcon);
547 Datum
548 sepgsql_setcon(PG_FUNCTION_ARGS)
549 {
550         const char *new_label;
551
552         if (PG_ARGISNULL(0))
553                 new_label = NULL;
554         else
555                 new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
556
557         sepgsql_set_client_label(new_label);
558
559         PG_RETURN_BOOL(true);
560 }
561
562 /*
563  * TEXT sepgsql_mcstrans_in(TEXT)
564  *
565  * It translate the given qualified MLS/MCS range into raw format
566  * when mcstrans daemon is working.
567  */
568 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
569 Datum
570 sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
571 {
572         text       *label = PG_GETARG_TEXT_P(0);
573         char       *raw_label;
574         char       *result;
575
576         if (!sepgsql_is_enabled())
577                 ereport(ERROR,
578                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
579                                  errmsg("sepgsql is not enabled")));
580
581         if (selinux_trans_to_raw_context(text_to_cstring(label),
582                                                                          &raw_label) < 0)
583                 ereport(ERROR,
584                                 (errcode(ERRCODE_INTERNAL_ERROR),
585                                  errmsg("SELinux: could not translate security label: %m")));
586
587         PG_TRY();
588         {
589                 result = pstrdup(raw_label);
590         }
591         PG_CATCH();
592         {
593                 freecon(raw_label);
594                 PG_RE_THROW();
595         }
596         PG_END_TRY();
597         freecon(raw_label);
598
599         PG_RETURN_TEXT_P(cstring_to_text(result));
600 }
601
602 /*
603  * TEXT sepgsql_mcstrans_out(TEXT)
604  *
605  * It translate the given raw MLS/MCS range into qualified format
606  * when mcstrans daemon is working.
607  */
608 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
609 Datum
610 sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
611 {
612         text       *label = PG_GETARG_TEXT_P(0);
613         char       *qual_label;
614         char       *result;
615
616         if (!sepgsql_is_enabled())
617                 ereport(ERROR,
618                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
619                                  errmsg("sepgsql is not currently enabled")));
620
621         if (selinux_raw_to_trans_context(text_to_cstring(label),
622                                                                          &qual_label) < 0)
623                 ereport(ERROR,
624                                 (errcode(ERRCODE_INTERNAL_ERROR),
625                                  errmsg("SELinux: could not translate security label: %m")));
626
627         PG_TRY();
628         {
629                 result = pstrdup(qual_label);
630         }
631         PG_CATCH();
632         {
633                 freecon(qual_label);
634                 PG_RE_THROW();
635         }
636         PG_END_TRY();
637         freecon(qual_label);
638
639         PG_RETURN_TEXT_P(cstring_to_text(result));
640 }
641
642 /*
643  * quote_object_names
644  *
645  * It tries to quote the supplied identifiers
646  */
647 static char *
648 quote_object_name(const char *src1, const char *src2,
649                                   const char *src3, const char *src4)
650 {
651         StringInfoData result;
652         const char *temp;
653
654         initStringInfo(&result);
655
656         if (src1)
657         {
658                 temp = quote_identifier(src1);
659                 appendStringInfo(&result, "%s", temp);
660                 if (src1 != temp)
661                         pfree((void *) temp);
662         }
663         if (src2)
664         {
665                 temp = quote_identifier(src2);
666                 appendStringInfo(&result, ".%s", temp);
667                 if (src2 != temp)
668                         pfree((void *) temp);
669         }
670         if (src3)
671         {
672                 temp = quote_identifier(src3);
673                 appendStringInfo(&result, ".%s", temp);
674                 if (src3 != temp)
675                         pfree((void *) temp);
676         }
677         if (src4)
678         {
679                 temp = quote_identifier(src4);
680                 appendStringInfo(&result, ".%s", temp);
681                 if (src4 != temp)
682                         pfree((void *) temp);
683         }
684         return result.data;
685 }
686
687 /*
688  * exec_object_restorecon
689  *
690  * This routine is a helper called by sepgsql_restorecon; it set up
691  * initial security labels of database objects within the supplied
692  * catalog OID.
693  */
694 static void
695 exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId)
696 {
697         Relation        rel;
698         SysScanDesc sscan;
699         HeapTuple       tuple;
700         char       *database_name = get_database_name(MyDatabaseId);
701         char       *namespace_name;
702         Oid                     namespace_id;
703         char       *relation_name;
704
705         /*
706          * Open the target catalog. We don't want to allow writable accesses by
707          * other session during initial labeling.
708          */
709         rel = heap_open(catalogId, AccessShareLock);
710
711         sscan = systable_beginscan(rel, InvalidOid, false,
712                                                            SnapshotNow, 0, NULL);
713         while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
714         {
715                 Form_pg_database datForm;
716                 Form_pg_namespace nspForm;
717                 Form_pg_class relForm;
718                 Form_pg_attribute attForm;
719                 Form_pg_proc proForm;
720                 char       *objname;
721                 int                     objtype = 1234;
722                 ObjectAddress object;
723                 security_context_t context;
724
725                 /*
726                  * The way to determine object name depends on object classes. So, any
727                  * branches set up `objtype', `objname' and `object' here.
728                  */
729                 switch (catalogId)
730                 {
731                         case DatabaseRelationId:
732                                 datForm = (Form_pg_database) GETSTRUCT(tuple);
733
734                                 objtype = SELABEL_DB_DATABASE;
735
736                                 objname = quote_object_name(NameStr(datForm->datname),
737                                                                                         NULL, NULL, NULL);
738
739                                 object.classId = DatabaseRelationId;
740                                 object.objectId = HeapTupleGetOid(tuple);
741                                 object.objectSubId = 0;
742                                 break;
743
744                         case NamespaceRelationId:
745                                 nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
746
747                                 objtype = SELABEL_DB_SCHEMA;
748
749                                 objname = quote_object_name(database_name,
750                                                                                         NameStr(nspForm->nspname),
751                                                                                         NULL, NULL);
752
753                                 object.classId = NamespaceRelationId;
754                                 object.objectId = HeapTupleGetOid(tuple);
755                                 object.objectSubId = 0;
756                                 break;
757
758                         case RelationRelationId:
759                                 relForm = (Form_pg_class) GETSTRUCT(tuple);
760
761                                 if (relForm->relkind == RELKIND_RELATION)
762                                         objtype = SELABEL_DB_TABLE;
763                                 else if (relForm->relkind == RELKIND_SEQUENCE)
764                                         objtype = SELABEL_DB_SEQUENCE;
765                                 else if (relForm->relkind == RELKIND_VIEW)
766                                         objtype = SELABEL_DB_VIEW;
767                                 else
768                                         continue;       /* no need to assign security label */
769
770                                 namespace_name = get_namespace_name(relForm->relnamespace);
771                                 objname = quote_object_name(database_name,
772                                                                                         namespace_name,
773                                                                                         NameStr(relForm->relname),
774                                                                                         NULL);
775                                 pfree(namespace_name);
776
777                                 object.classId = RelationRelationId;
778                                 object.objectId = HeapTupleGetOid(tuple);
779                                 object.objectSubId = 0;
780                                 break;
781
782                         case AttributeRelationId:
783                                 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
784
785                                 if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
786                                         continue;       /* no need to assign security label */
787
788                                 objtype = SELABEL_DB_COLUMN;
789
790                                 namespace_id = get_rel_namespace(attForm->attrelid);
791                                 namespace_name = get_namespace_name(namespace_id);
792                                 relation_name = get_rel_name(attForm->attrelid);
793                                 objname = quote_object_name(database_name,
794                                                                                         namespace_name,
795                                                                                         relation_name,
796                                                                                         NameStr(attForm->attname));
797                                 pfree(namespace_name);
798                                 pfree(relation_name);
799
800                                 object.classId = RelationRelationId;
801                                 object.objectId = attForm->attrelid;
802                                 object.objectSubId = attForm->attnum;
803                                 break;
804
805                         case ProcedureRelationId:
806                                 proForm = (Form_pg_proc) GETSTRUCT(tuple);
807
808                                 objtype = SELABEL_DB_PROCEDURE;
809
810                                 namespace_name = get_namespace_name(proForm->pronamespace);
811                                 objname = quote_object_name(database_name,
812                                                                                         namespace_name,
813                                                                                         NameStr(proForm->proname),
814                                                                                         NULL);
815                                 pfree(namespace_name);
816
817                                 object.classId = ProcedureRelationId;
818                                 object.objectId = HeapTupleGetOid(tuple);
819                                 object.objectSubId = 0;
820                                 break;
821
822                         default:
823                                 elog(ERROR, "unexpected catalog id: %u", catalogId);
824                                 objname = NULL; /* for compiler quiet */
825                                 break;
826                 }
827
828                 if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
829                 {
830                         PG_TRY();
831                         {
832                                 /*
833                                  * Check SELinux permission to relabel the fetched object,
834                                  * then do the actual relabeling.
835                                  */
836                                 sepgsql_object_relabel(&object, context);
837
838                                 SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
839                         }
840                         PG_CATCH();
841                         {
842                                 freecon(context);
843                                 PG_RE_THROW();
844                         }
845                         PG_END_TRY();
846                         freecon(context);
847                 }
848                 else if (errno == ENOENT)
849                         ereport(WARNING,
850                                         (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
851                                                         objname, objtype)));
852                 else
853                         ereport(ERROR,
854                                         (errcode(ERRCODE_INTERNAL_ERROR),
855                                          errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
856
857                 pfree(objname);
858         }
859         systable_endscan(sscan);
860
861         heap_close(rel, NoLock);
862 }
863
864 /*
865  * BOOL sepgsql_restorecon(TEXT specfile)
866  *
867  * This function tries to assign initial security labels on all the object
868  * within the current database, according to the system setting.
869  * It is typically invoked by sepgsql-install script just after initdb, to
870  * assign initial security labels.
871  *
872  * If @specfile is not NULL, it uses explicitly specified specfile, instead
873  * of the system default.
874  */
875 PG_FUNCTION_INFO_V1(sepgsql_restorecon);
876 Datum
877 sepgsql_restorecon(PG_FUNCTION_ARGS)
878 {
879         struct selabel_handle *sehnd;
880         struct selinux_opt seopts;
881
882         /*
883          * SELinux has to be enabled on the running platform.
884          */
885         if (!sepgsql_is_enabled())
886                 ereport(ERROR,
887                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
888                                  errmsg("sepgsql is not currently enabled")));
889
890         /*
891          * Check DAC permission. Only superuser can set up initial security
892          * labels, like root-user in filesystems
893          */
894         if (!superuser())
895                 ereport(ERROR,
896                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
897                   errmsg("SELinux: must be superuser to restore initial contexts")));
898
899         /*
900          * Open selabel_lookup(3) stuff. It provides a set of mapping between an
901          * initial security label and object class/name due to the system setting.
902          */
903         if (PG_ARGISNULL(0))
904         {
905                 seopts.type = SELABEL_OPT_UNUSED;
906                 seopts.value = NULL;
907         }
908         else
909         {
910                 seopts.type = SELABEL_OPT_PATH;
911                 seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
912         }
913         sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
914         if (!sehnd)
915                 ereport(ERROR,
916                                 (errcode(ERRCODE_INTERNAL_ERROR),
917                            errmsg("SELinux: failed to initialize labeling handle: %m")));
918         PG_TRY();
919         {
920                 exec_object_restorecon(sehnd, DatabaseRelationId);
921                 exec_object_restorecon(sehnd, NamespaceRelationId);
922                 exec_object_restorecon(sehnd, RelationRelationId);
923                 exec_object_restorecon(sehnd, AttributeRelationId);
924                 exec_object_restorecon(sehnd, ProcedureRelationId);
925         }
926         PG_CATCH();
927         {
928                 selabel_close(sehnd);
929                 PG_RE_THROW();
930         }
931         PG_END_TRY();
932
933         selabel_close(sehnd);
934
935         PG_RETURN_BOOL(true);
936 }