Header And Logo

PostgreSQL
| The world's most advanced open source database.

label.c

Go to the documentation of this file.
00001 /* -------------------------------------------------------------------------
00002  *
00003  * contrib/sepgsql/label.c
00004  *
00005  * Routines to support SELinux labels (security context)
00006  *
00007  * Copyright (c) 2010-2013, PostgreSQL Global Development Group
00008  *
00009  * -------------------------------------------------------------------------
00010  */
00011 #include "postgres.h"
00012 
00013 #include "access/heapam.h"
00014 #include "access/htup_details.h"
00015 #include "access/genam.h"
00016 #include "access/xact.h"
00017 #include "catalog/catalog.h"
00018 #include "catalog/dependency.h"
00019 #include "catalog/indexing.h"
00020 #include "catalog/pg_attribute.h"
00021 #include "catalog/pg_class.h"
00022 #include "catalog/pg_database.h"
00023 #include "catalog/pg_namespace.h"
00024 #include "catalog/pg_proc.h"
00025 #include "commands/dbcommands.h"
00026 #include "commands/seclabel.h"
00027 #include "libpq/auth.h"
00028 #include "libpq/libpq-be.h"
00029 #include "miscadmin.h"
00030 #include "utils/builtins.h"
00031 #include "utils/fmgroids.h"
00032 #include "utils/guc.h"
00033 #include "utils/lsyscache.h"
00034 #include "utils/memutils.h"
00035 #include "utils/rel.h"
00036 #include "utils/tqual.h"
00037 
00038 #include "sepgsql.h"
00039 
00040 #include <selinux/label.h>
00041 
00042 /*
00043  * Saved hook entries (if stacked)
00044  */
00045 static ClientAuthentication_hook_type next_client_auth_hook = NULL;
00046 static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
00047 static fmgr_hook_type next_fmgr_hook = NULL;
00048 
00049 /*
00050  * client_label_*
00051  *
00052  * security label of the database client.  Initially the client security label
00053  * is equal to client_label_peer, and can be changed by one or more calls to
00054  * sepgsql_setcon(), and also be temporarily overridden during execution of a
00055  * trusted-procedure.
00056  *
00057  * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
00058  * rollback should also rollback the current client security label.  Therefore
00059  * we use the list client_label_pending of pending_label to keep track of which
00060  * labels were set during the (sub-)transactions.
00061  */
00062 static char *client_label_peer = NULL;  /* set by getpeercon(3) */
00063 static List *client_label_pending = NIL;        /* pending list being set by
00064                                                  * sepgsql_setcon() */
00065 static char *client_label_committed = NULL;     /* set by sepgsql_setcon(),
00066                                                  * and already committed */
00067 static char *client_label_func = NULL;  /* set by trusted procedure */
00068 
00069 typedef struct
00070 {
00071     SubTransactionId subid;
00072     char       *label;
00073 }   pending_label;
00074 
00075 /*
00076  * sepgsql_get_client_label
00077  *
00078  * Returns the current security label of the client.  All code should use this
00079  * routine to get the current label, instead of referring to the client_label_*
00080  * variables above.
00081  */
00082 char *
00083 sepgsql_get_client_label(void)
00084 {
00085     /* trusted procedure client label override */
00086     if (client_label_func)
00087         return client_label_func;
00088 
00089     /* uncommitted sepgsql_setcon() value */
00090     if (client_label_pending)
00091     {
00092         pending_label *plabel = llast(client_label_pending);
00093 
00094         if (plabel->label)
00095             return plabel->label;
00096     }
00097     else if (client_label_committed)
00098         return client_label_committed;  /* set by sepgsql_setcon() committed */
00099 
00100     /* default label */
00101     Assert(client_label_peer != NULL);
00102     return client_label_peer;
00103 }
00104 
00105 /*
00106  * sepgsql_set_client_label
00107  *
00108  * This routine tries to switch the current security label of the client, and
00109  * checks related permissions.  The supplied new label shall be added to the
00110  * client_label_pending list, then saved at transaction-commit time to ensure
00111  * transaction-awareness.
00112  */
00113 static void
00114 sepgsql_set_client_label(const char *new_label)
00115 {
00116     const char *tcontext;
00117     MemoryContext oldcxt;
00118     pending_label *plabel;
00119 
00120     /* Reset to the initial client label, if NULL */
00121     if (!new_label)
00122         tcontext = client_label_peer;
00123     else
00124     {
00125         if (security_check_context_raw((security_context_t) new_label) < 0)
00126             ereport(ERROR,
00127                     (errcode(ERRCODE_INVALID_NAME),
00128                      errmsg("SELinux: invalid security label: \"%s\"",
00129                             new_label)));
00130         tcontext = new_label;
00131     }
00132 
00133     /* Check process:{setcurrent} permission. */
00134     sepgsql_avc_check_perms_label(sepgsql_get_client_label(),
00135                                   SEPG_CLASS_PROCESS,
00136                                   SEPG_PROCESS__SETCURRENT,
00137                                   NULL,
00138                                   true);
00139     /* Check process:{dyntransition} permission. */
00140     sepgsql_avc_check_perms_label(tcontext,
00141                                   SEPG_CLASS_PROCESS,
00142                                   SEPG_PROCESS__DYNTRANSITION,
00143                                   NULL,
00144                                   true);
00145 
00146     /*
00147      * Append the supplied new_label on the pending list until the current
00148      * transaction is committed.
00149      */
00150     oldcxt = MemoryContextSwitchTo(CurTransactionContext);
00151 
00152     plabel = palloc0(sizeof(pending_label));
00153     plabel->subid = GetCurrentSubTransactionId();
00154     if (new_label)
00155         plabel->label = pstrdup(new_label);
00156     client_label_pending = lappend(client_label_pending, plabel);
00157 
00158     MemoryContextSwitchTo(oldcxt);
00159 }
00160 
00161 /*
00162  * sepgsql_xact_callback
00163  *
00164  * A callback routine of transaction commit/abort/prepare.  Commmit or abort
00165  * changes in the client_label_pending list.
00166  */
00167 static void
00168 sepgsql_xact_callback(XactEvent event, void *arg)
00169 {
00170     if (event == XACT_EVENT_COMMIT)
00171     {
00172         if (client_label_pending != NIL)
00173         {
00174             pending_label *plabel = llast(client_label_pending);
00175             char       *new_label;
00176 
00177             if (plabel->label)
00178                 new_label = MemoryContextStrdup(TopMemoryContext,
00179                                                 plabel->label);
00180             else
00181                 new_label = NULL;
00182 
00183             if (client_label_committed)
00184                 pfree(client_label_committed);
00185 
00186             client_label_committed = new_label;
00187 
00188             /*
00189              * XXX - Note that items of client_label_pending are allocated on
00190              * CurTransactionContext, thus, all acquired memory region shall
00191              * be released implicitly.
00192              */
00193             client_label_pending = NIL;
00194         }
00195     }
00196     else if (event == XACT_EVENT_ABORT)
00197         client_label_pending = NIL;
00198 }
00199 
00200 /*
00201  * sepgsql_subxact_callback
00202  *
00203  * A callback routine of sub-transaction start/abort/commit.  Releases all
00204  * security labels that are set within the sub-transaction that is aborted.
00205  */
00206 static void
00207 sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
00208                          SubTransactionId parentSubid, void *arg)
00209 {
00210     ListCell   *cell;
00211     ListCell   *prev;
00212     ListCell   *next;
00213 
00214     if (event == SUBXACT_EVENT_ABORT_SUB)
00215     {
00216         prev = NULL;
00217         for (cell = list_head(client_label_pending); cell; cell = next)
00218         {
00219             pending_label *plabel = lfirst(cell);
00220 
00221             next = lnext(cell);
00222 
00223             if (plabel->subid == mySubid)
00224                 client_label_pending
00225                     = list_delete_cell(client_label_pending, cell, prev);
00226             else
00227                 prev = cell;
00228         }
00229     }
00230 }
00231 
00232 /*
00233  * sepgsql_client_auth
00234  *
00235  * Entrypoint of the client authentication hook.
00236  * It switches the client label according to getpeercon(), and the current
00237  * performing mode according to the GUC setting.
00238  */
00239 static void
00240 sepgsql_client_auth(Port *port, int status)
00241 {
00242     if (next_client_auth_hook)
00243         (*next_client_auth_hook) (port, status);
00244 
00245     /*
00246      * In the case when authentication failed, the supplied socket shall be
00247      * closed soon, so we don't need to do anything here.
00248      */
00249     if (status != STATUS_OK)
00250         return;
00251 
00252     /*
00253      * Getting security label of the peer process using API of libselinux.
00254      */
00255     if (getpeercon_raw(port->sock, &client_label_peer) < 0)
00256         ereport(FATAL,
00257                 (errcode(ERRCODE_INTERNAL_ERROR),
00258                  errmsg("SELinux: unable to get peer label: %m")));
00259 
00260     /*
00261      * Switch the current performing mode from INTERNAL to either DEFAULT or
00262      * PERMISSIVE.
00263      */
00264     if (sepgsql_get_permissive())
00265         sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
00266     else
00267         sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
00268 }
00269 
00270 /*
00271  * sepgsql_needs_fmgr_hook
00272  *
00273  * It informs the core whether the supplied function is trusted procedure,
00274  * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
00275  * abort time of function invocation.
00276  */
00277 static bool
00278 sepgsql_needs_fmgr_hook(Oid functionId)
00279 {
00280     ObjectAddress object;
00281 
00282     if (next_needs_fmgr_hook &&
00283         (*next_needs_fmgr_hook) (functionId))
00284         return true;
00285 
00286     /*
00287      * SELinux needs the function to be called via security_definer wrapper,
00288      * if this invocation will take a domain-transition. We call these
00289      * functions as trusted-procedure, if the security policy has a rule that
00290      * switches security label of the client on execution.
00291      */
00292     if (sepgsql_avc_trusted_proc(functionId) != NULL)
00293         return true;
00294 
00295     /*
00296      * Even if not a trusted-procedure, this function should not be inlined
00297      * unless the client has db_procedure:{execute} permission. Please note
00298      * that it shall be actually failed later because of same reason with
00299      * ACL_EXECUTE.
00300      */
00301     object.classId = ProcedureRelationId;
00302     object.objectId = functionId;
00303     object.objectSubId = 0;
00304     if (!sepgsql_avc_check_perms(&object,
00305                                  SEPG_CLASS_DB_PROCEDURE,
00306                                  SEPG_DB_PROCEDURE__EXECUTE |
00307                                  SEPG_DB_PROCEDURE__ENTRYPOINT,
00308                                  SEPGSQL_AVC_NOAUDIT, false))
00309         return true;
00310 
00311     return false;
00312 }
00313 
00314 /*
00315  * sepgsql_fmgr_hook
00316  *
00317  * It switches security label of the client on execution of trusted
00318  * procedures.
00319  */
00320 static void
00321 sepgsql_fmgr_hook(FmgrHookEventType event,
00322                   FmgrInfo *flinfo, Datum *private)
00323 {
00324     struct
00325     {
00326         char       *old_label;
00327         char       *new_label;
00328         Datum       next_private;
00329     }          *stack;
00330 
00331     switch (event)
00332     {
00333         case FHET_START:
00334             stack = (void *) DatumGetPointer(*private);
00335             if (!stack)
00336             {
00337                 MemoryContext oldcxt;
00338 
00339                 oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
00340                 stack = palloc(sizeof(*stack));
00341                 stack->old_label = NULL;
00342                 stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
00343                 stack->next_private = 0;
00344 
00345                 MemoryContextSwitchTo(oldcxt);
00346 
00347                 /*
00348                  * process:transition permission between old and new label,
00349                  * when user tries to switch security label of the client on
00350                  * execution of trusted procedure.
00351                  *
00352                  * Also, db_procedure:entrypoint permission should be checked
00353                  * whether this procedure can perform as an entrypoint of the
00354                  * trusted procedure, or not.
00355                  * Note that db_procedure:execute permission shall be checked
00356                  * individually.
00357                  */
00358                 if (stack->new_label)
00359                 {
00360                     ObjectAddress object;
00361 
00362                     object.classId = ProcedureRelationId;
00363                     object.objectId = flinfo->fn_oid;
00364                     object.objectSubId = 0;
00365                     sepgsql_avc_check_perms(&object,
00366                                             SEPG_CLASS_DB_PROCEDURE,
00367                                             SEPG_DB_PROCEDURE__ENTRYPOINT,
00368                                             getObjectDescription(&object),
00369                                             true);
00370 
00371                     sepgsql_avc_check_perms_label(stack->new_label,
00372                                                   SEPG_CLASS_PROCESS,
00373                                                   SEPG_PROCESS__TRANSITION,
00374                                                   NULL, true);
00375                 }
00376                 *private = PointerGetDatum(stack);
00377             }
00378             Assert(!stack->old_label);
00379             if (stack->new_label)
00380             {
00381                 stack->old_label = client_label_func;
00382                 client_label_func = stack->new_label;
00383             }
00384             if (next_fmgr_hook)
00385                 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
00386             break;
00387 
00388         case FHET_END:
00389         case FHET_ABORT:
00390             stack = (void *) DatumGetPointer(*private);
00391 
00392             if (next_fmgr_hook)
00393                 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
00394 
00395             if (stack->new_label)
00396             {
00397                 client_label_func = stack->old_label;
00398                 stack->old_label = NULL;
00399             }
00400             break;
00401 
00402         default:
00403             elog(ERROR, "unexpected event type: %d", (int) event);
00404             break;
00405     }
00406 }
00407 
00408 /*
00409  * sepgsql_init_client_label
00410  *
00411  * Initializes the client security label and sets up related hooks for client
00412  * label management.
00413  */
00414 void
00415 sepgsql_init_client_label(void)
00416 {
00417     /*
00418      * Set up dummy client label.
00419      *
00420      * XXX - note that PostgreSQL launches background worker process like
00421      * autovacuum without authentication steps. So, we initialize sepgsql_mode
00422      * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
00423      * of server process. Later, it also launches background of user session.
00424      * In this case, the process is always hooked on post-authentication, and
00425      * we can initialize the sepgsql_mode and client_label correctly.
00426      */
00427     if (getcon_raw(&client_label_peer) < 0)
00428         ereport(ERROR,
00429                 (errcode(ERRCODE_INTERNAL_ERROR),
00430                  errmsg("SELinux: failed to get server security label: %m")));
00431 
00432     /* Client authentication hook */
00433     next_client_auth_hook = ClientAuthentication_hook;
00434     ClientAuthentication_hook = sepgsql_client_auth;
00435 
00436     /* Trusted procedure hooks */
00437     next_needs_fmgr_hook = needs_fmgr_hook;
00438     needs_fmgr_hook = sepgsql_needs_fmgr_hook;
00439 
00440     next_fmgr_hook = fmgr_hook;
00441     fmgr_hook = sepgsql_fmgr_hook;
00442 
00443     /* Transaction/Sub-transaction callbacks */
00444     RegisterXactCallback(sepgsql_xact_callback, NULL);
00445     RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
00446 }
00447 
00448 /*
00449  * sepgsql_get_label
00450  *
00451  * It returns a security context of the specified database object.
00452  * If unlabeled or incorrectly labeled, the system "unlabeled" label
00453  * shall be returned.
00454  */
00455 char *
00456 sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
00457 {
00458     ObjectAddress object;
00459     char       *label;
00460 
00461     object.classId = classId;
00462     object.objectId = objectId;
00463     object.objectSubId = subId;
00464 
00465     label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
00466     if (!label || security_check_context_raw((security_context_t) label))
00467     {
00468         security_context_t unlabeled;
00469 
00470         if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
00471             ereport(ERROR,
00472                     (errcode(ERRCODE_INTERNAL_ERROR),
00473                errmsg("SELinux: failed to get initial security label: %m")));
00474         PG_TRY();
00475         {
00476             label = pstrdup(unlabeled);
00477         }
00478         PG_CATCH();
00479         {
00480             freecon(unlabeled);
00481             PG_RE_THROW();
00482         }
00483         PG_END_TRY();
00484 
00485         freecon(unlabeled);
00486     }
00487     return label;
00488 }
00489 
00490 /*
00491  * sepgsql_object_relabel
00492  *
00493  * An entrypoint of SECURITY LABEL statement
00494  */
00495 void
00496 sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
00497 {
00498     /*
00499      * validate format of the supplied security label, if it is security
00500      * context of selinux.
00501      */
00502     if (seclabel &&
00503         security_check_context_raw((security_context_t) seclabel) < 0)
00504         ereport(ERROR,
00505                 (errcode(ERRCODE_INVALID_NAME),
00506                errmsg("SELinux: invalid security label: \"%s\"", seclabel)));
00507 
00508     /*
00509      * Do actual permission checks for each object classes
00510      */
00511     switch (object->classId)
00512     {
00513         case DatabaseRelationId:
00514             sepgsql_database_relabel(object->objectId, seclabel);
00515             break;
00516 
00517         case NamespaceRelationId:
00518             sepgsql_schema_relabel(object->objectId, seclabel);
00519             break;
00520 
00521         case RelationRelationId:
00522             if (object->objectSubId == 0)
00523                 sepgsql_relation_relabel(object->objectId,
00524                                          seclabel);
00525             else
00526                 sepgsql_attribute_relabel(object->objectId,
00527                                           object->objectSubId,
00528                                           seclabel);
00529             break;
00530 
00531         case ProcedureRelationId:
00532             sepgsql_proc_relabel(object->objectId, seclabel);
00533             break;
00534 
00535         default:
00536             elog(ERROR, "unsupported object type: %u", object->classId);
00537             break;
00538     }
00539 }
00540 
00541 /*
00542  * TEXT sepgsql_getcon(VOID)
00543  *
00544  * It returns the security label of the client.
00545  */
00546 PG_FUNCTION_INFO_V1(sepgsql_getcon);
00547 Datum
00548 sepgsql_getcon(PG_FUNCTION_ARGS)
00549 {
00550     char       *client_label;
00551 
00552     if (!sepgsql_is_enabled())
00553         PG_RETURN_NULL();
00554 
00555     client_label = sepgsql_get_client_label();
00556 
00557     PG_RETURN_TEXT_P(cstring_to_text(client_label));
00558 }
00559 
00560 /*
00561  * BOOL sepgsql_setcon(TEXT)
00562  *
00563  * It switches the security label of the client.
00564  */
00565 PG_FUNCTION_INFO_V1(sepgsql_setcon);
00566 Datum
00567 sepgsql_setcon(PG_FUNCTION_ARGS)
00568 {
00569     const char *new_label;
00570 
00571     if (PG_ARGISNULL(0))
00572         new_label = NULL;
00573     else
00574         new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
00575 
00576     sepgsql_set_client_label(new_label);
00577 
00578     PG_RETURN_BOOL(true);
00579 }
00580 
00581 /*
00582  * TEXT sepgsql_mcstrans_in(TEXT)
00583  *
00584  * It translate the given qualified MLS/MCS range into raw format
00585  * when mcstrans daemon is working.
00586  */
00587 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
00588 Datum
00589 sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
00590 {
00591     text       *label = PG_GETARG_TEXT_P(0);
00592     char       *raw_label;
00593     char       *result;
00594 
00595     if (!sepgsql_is_enabled())
00596         ereport(ERROR,
00597                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00598                  errmsg("sepgsql is not enabled")));
00599 
00600     if (selinux_trans_to_raw_context(text_to_cstring(label),
00601                                      &raw_label) < 0)
00602         ereport(ERROR,
00603                 (errcode(ERRCODE_INTERNAL_ERROR),
00604                  errmsg("SELinux: could not translate security label: %m")));
00605 
00606     PG_TRY();
00607     {
00608         result = pstrdup(raw_label);
00609     }
00610     PG_CATCH();
00611     {
00612         freecon(raw_label);
00613         PG_RE_THROW();
00614     }
00615     PG_END_TRY();
00616     freecon(raw_label);
00617 
00618     PG_RETURN_TEXT_P(cstring_to_text(result));
00619 }
00620 
00621 /*
00622  * TEXT sepgsql_mcstrans_out(TEXT)
00623  *
00624  * It translate the given raw MLS/MCS range into qualified format
00625  * when mcstrans daemon is working.
00626  */
00627 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
00628 Datum
00629 sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
00630 {
00631     text       *label = PG_GETARG_TEXT_P(0);
00632     char       *qual_label;
00633     char       *result;
00634 
00635     if (!sepgsql_is_enabled())
00636         ereport(ERROR,
00637                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00638                  errmsg("sepgsql is not currently enabled")));
00639 
00640     if (selinux_raw_to_trans_context(text_to_cstring(label),
00641                                      &qual_label) < 0)
00642         ereport(ERROR,
00643                 (errcode(ERRCODE_INTERNAL_ERROR),
00644                  errmsg("SELinux: could not translate security label: %m")));
00645 
00646     PG_TRY();
00647     {
00648         result = pstrdup(qual_label);
00649     }
00650     PG_CATCH();
00651     {
00652         freecon(qual_label);
00653         PG_RE_THROW();
00654     }
00655     PG_END_TRY();
00656     freecon(qual_label);
00657 
00658     PG_RETURN_TEXT_P(cstring_to_text(result));
00659 }
00660 
00661 /*
00662  * quote_object_names
00663  *
00664  * It tries to quote the supplied identifiers
00665  */
00666 static char *
00667 quote_object_name(const char *src1, const char *src2,
00668                   const char *src3, const char *src4)
00669 {
00670     StringInfoData result;
00671     const char *temp;
00672 
00673     initStringInfo(&result);
00674 
00675     if (src1)
00676     {
00677         temp = quote_identifier(src1);
00678         appendStringInfo(&result, "%s", temp);
00679         if (src1 != temp)
00680             pfree((void *) temp);
00681     }
00682     if (src2)
00683     {
00684         temp = quote_identifier(src2);
00685         appendStringInfo(&result, ".%s", temp);
00686         if (src2 != temp)
00687             pfree((void *) temp);
00688     }
00689     if (src3)
00690     {
00691         temp = quote_identifier(src3);
00692         appendStringInfo(&result, ".%s", temp);
00693         if (src3 != temp)
00694             pfree((void *) temp);
00695     }
00696     if (src4)
00697     {
00698         temp = quote_identifier(src4);
00699         appendStringInfo(&result, ".%s", temp);
00700         if (src4 != temp)
00701             pfree((void *) temp);
00702     }
00703     return result.data;
00704 }
00705 
00706 /*
00707  * exec_object_restorecon
00708  *
00709  * This routine is a helper called by sepgsql_restorecon; it set up
00710  * initial security labels of database objects within the supplied
00711  * catalog OID.
00712  */
00713 static void
00714 exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId)
00715 {
00716     Relation    rel;
00717     SysScanDesc sscan;
00718     HeapTuple   tuple;
00719     char       *database_name = get_database_name(MyDatabaseId);
00720     char       *namespace_name;
00721     Oid         namespace_id;
00722     char       *relation_name;
00723 
00724     /*
00725      * Open the target catalog. We don't want to allow writable accesses by
00726      * other session during initial labeling.
00727      */
00728     rel = heap_open(catalogId, AccessShareLock);
00729 
00730     sscan = systable_beginscan(rel, InvalidOid, false,
00731                                SnapshotNow, 0, NULL);
00732     while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
00733     {
00734         Form_pg_database datForm;
00735         Form_pg_namespace nspForm;
00736         Form_pg_class relForm;
00737         Form_pg_attribute attForm;
00738         Form_pg_proc proForm;
00739         char       *objname;
00740         int         objtype = 1234;
00741         ObjectAddress object;
00742         security_context_t context;
00743 
00744         /*
00745          * The way to determine object name depends on object classes. So, any
00746          * branches set up `objtype', `objname' and `object' here.
00747          */
00748         switch (catalogId)
00749         {
00750             case DatabaseRelationId:
00751                 datForm = (Form_pg_database) GETSTRUCT(tuple);
00752 
00753                 objtype = SELABEL_DB_DATABASE;
00754 
00755                 objname = quote_object_name(NameStr(datForm->datname),
00756                                             NULL, NULL, NULL);
00757 
00758                 object.classId = DatabaseRelationId;
00759                 object.objectId = HeapTupleGetOid(tuple);
00760                 object.objectSubId = 0;
00761                 break;
00762 
00763             case NamespaceRelationId:
00764                 nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
00765 
00766                 objtype = SELABEL_DB_SCHEMA;
00767 
00768                 objname = quote_object_name(database_name,
00769                                             NameStr(nspForm->nspname),
00770                                             NULL, NULL);
00771 
00772                 object.classId = NamespaceRelationId;
00773                 object.objectId = HeapTupleGetOid(tuple);
00774                 object.objectSubId = 0;
00775                 break;
00776 
00777             case RelationRelationId:
00778                 relForm = (Form_pg_class) GETSTRUCT(tuple);
00779 
00780                 if (relForm->relkind == RELKIND_RELATION)
00781                     objtype = SELABEL_DB_TABLE;
00782                 else if (relForm->relkind == RELKIND_SEQUENCE)
00783                     objtype = SELABEL_DB_SEQUENCE;
00784                 else if (relForm->relkind == RELKIND_VIEW)
00785                     objtype = SELABEL_DB_VIEW;
00786                 else
00787                     continue;   /* no need to assign security label */
00788 
00789                 namespace_name = get_namespace_name(relForm->relnamespace);
00790                 objname = quote_object_name(database_name,
00791                                             namespace_name,
00792                                             NameStr(relForm->relname),
00793                                             NULL);
00794                 pfree(namespace_name);
00795 
00796                 object.classId = RelationRelationId;
00797                 object.objectId = HeapTupleGetOid(tuple);
00798                 object.objectSubId = 0;
00799                 break;
00800 
00801             case AttributeRelationId:
00802                 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
00803 
00804                 if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
00805                     continue;   /* no need to assign security label */
00806 
00807                 objtype = SELABEL_DB_COLUMN;
00808 
00809                 namespace_id = get_rel_namespace(attForm->attrelid);
00810                 namespace_name = get_namespace_name(namespace_id);
00811                 relation_name = get_rel_name(attForm->attrelid);
00812                 objname = quote_object_name(database_name,
00813                                             namespace_name,
00814                                             relation_name,
00815                                             NameStr(attForm->attname));
00816                 pfree(namespace_name);
00817                 pfree(relation_name);
00818 
00819                 object.classId = RelationRelationId;
00820                 object.objectId = attForm->attrelid;
00821                 object.objectSubId = attForm->attnum;
00822                 break;
00823 
00824             case ProcedureRelationId:
00825                 proForm = (Form_pg_proc) GETSTRUCT(tuple);
00826 
00827                 objtype = SELABEL_DB_PROCEDURE;
00828 
00829                 namespace_name = get_namespace_name(proForm->pronamespace);
00830                 objname = quote_object_name(database_name,
00831                                             namespace_name,
00832                                             NameStr(proForm->proname),
00833                                             NULL);
00834                 pfree(namespace_name);
00835 
00836                 object.classId = ProcedureRelationId;
00837                 object.objectId = HeapTupleGetOid(tuple);
00838                 object.objectSubId = 0;
00839                 break;
00840 
00841             default:
00842                 elog(ERROR, "unexpected catalog id: %u", catalogId);
00843                 objname = NULL; /* for compiler quiet */
00844                 break;
00845         }
00846 
00847         if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
00848         {
00849             PG_TRY();
00850             {
00851                 /*
00852                  * Check SELinux permission to relabel the fetched object,
00853                  * then do the actual relabeling.
00854                  */
00855                 sepgsql_object_relabel(&object, context);
00856 
00857                 SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
00858             }
00859             PG_CATCH();
00860             {
00861                 freecon(context);
00862                 PG_RE_THROW();
00863             }
00864             PG_END_TRY();
00865             freecon(context);
00866         }
00867         else if (errno == ENOENT)
00868             ereport(WARNING,
00869                     (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
00870                             objname, objtype)));
00871         else
00872             ereport(ERROR,
00873                     (errcode(ERRCODE_INTERNAL_ERROR),
00874                      errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
00875 
00876         pfree(objname);
00877     }
00878     systable_endscan(sscan);
00879 
00880     heap_close(rel, NoLock);
00881 }
00882 
00883 /*
00884  * BOOL sepgsql_restorecon(TEXT specfile)
00885  *
00886  * This function tries to assign initial security labels on all the object
00887  * within the current database, according to the system setting.
00888  * It is typically invoked by sepgsql-install script just after initdb, to
00889  * assign initial security labels.
00890  *
00891  * If @specfile is not NULL, it uses explicitly specified specfile, instead
00892  * of the system default.
00893  */
00894 PG_FUNCTION_INFO_V1(sepgsql_restorecon);
00895 Datum
00896 sepgsql_restorecon(PG_FUNCTION_ARGS)
00897 {
00898     struct selabel_handle *sehnd;
00899     struct selinux_opt seopts;
00900 
00901     /*
00902      * SELinux has to be enabled on the running platform.
00903      */
00904     if (!sepgsql_is_enabled())
00905         ereport(ERROR,
00906                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00907                  errmsg("sepgsql is not currently enabled")));
00908 
00909     /*
00910      * Check DAC permission. Only superuser can set up initial security
00911      * labels, like root-user in filesystems
00912      */
00913     if (!superuser())
00914         ereport(ERROR,
00915                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00916           errmsg("SELinux: must be superuser to restore initial contexts")));
00917 
00918     /*
00919      * Open selabel_lookup(3) stuff. It provides a set of mapping between an
00920      * initial security label and object class/name due to the system setting.
00921      */
00922     if (PG_ARGISNULL(0))
00923     {
00924         seopts.type = SELABEL_OPT_UNUSED;
00925         seopts.value = NULL;
00926     }
00927     else
00928     {
00929         seopts.type = SELABEL_OPT_PATH;
00930         seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
00931     }
00932     sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
00933     if (!sehnd)
00934         ereport(ERROR,
00935                 (errcode(ERRCODE_INTERNAL_ERROR),
00936                errmsg("SELinux: failed to initialize labeling handle: %m")));
00937     PG_TRY();
00938     {
00939         exec_object_restorecon(sehnd, DatabaseRelationId);
00940         exec_object_restorecon(sehnd, NamespaceRelationId);
00941         exec_object_restorecon(sehnd, RelationRelationId);
00942         exec_object_restorecon(sehnd, AttributeRelationId);
00943         exec_object_restorecon(sehnd, ProcedureRelationId);
00944     }
00945     PG_CATCH();
00946     {
00947         selabel_close(sehnd);
00948         PG_RE_THROW();
00949     }
00950     PG_END_TRY();
00951 
00952     selabel_close(sehnd);
00953 
00954     PG_RETURN_BOOL(true);
00955 }