Header And Logo

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

hooks.c

Go to the documentation of this file.
00001 /* -------------------------------------------------------------------------
00002  *
00003  * contrib/sepgsql/hooks.c
00004  *
00005  * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
00006  *
00007  * Copyright (c) 2010-2013, PostgreSQL Global Development Group
00008  *
00009  * -------------------------------------------------------------------------
00010  */
00011 #include "postgres.h"
00012 
00013 #include "catalog/dependency.h"
00014 #include "catalog/objectaccess.h"
00015 #include "catalog/pg_class.h"
00016 #include "catalog/pg_database.h"
00017 #include "catalog/pg_namespace.h"
00018 #include "catalog/pg_proc.h"
00019 #include "commands/seclabel.h"
00020 #include "executor/executor.h"
00021 #include "fmgr.h"
00022 #include "miscadmin.h"
00023 #include "tcop/utility.h"
00024 #include "utils/guc.h"
00025 
00026 #include "sepgsql.h"
00027 
00028 PG_MODULE_MAGIC;
00029 
00030 /*
00031  * Declarations
00032  */
00033 void        _PG_init(void);
00034 
00035 /*
00036  * Saved hook entries (if stacked)
00037  */
00038 static object_access_hook_type next_object_access_hook = NULL;
00039 static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
00040 static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
00041 
00042 /*
00043  * Contextual information on DDL commands
00044  */
00045 typedef struct
00046 {
00047     NodeTag     cmdtype;
00048 
00049     /*
00050      * Name of the template database given by users on CREATE DATABASE
00051      * command. Elsewhere (including the case of default) NULL.
00052      */
00053     const char *createdb_dtemplate;
00054 }   sepgsql_context_info_t;
00055 
00056 static sepgsql_context_info_t sepgsql_context_info;
00057 
00058 /*
00059  * GUC: sepgsql.permissive = (on|off)
00060  */
00061 static bool sepgsql_permissive;
00062 
00063 bool
00064 sepgsql_get_permissive(void)
00065 {
00066     return sepgsql_permissive;
00067 }
00068 
00069 /*
00070  * GUC: sepgsql.debug_audit = (on|off)
00071  */
00072 static bool sepgsql_debug_audit;
00073 
00074 bool
00075 sepgsql_get_debug_audit(void)
00076 {
00077     return sepgsql_debug_audit;
00078 }
00079 
00080 /*
00081  * sepgsql_object_access
00082  *
00083  * Entrypoint of the object_access_hook. This routine performs as
00084  * a dispatcher of invocation based on access type and object classes.
00085  */
00086 static void
00087 sepgsql_object_access(ObjectAccessType access,
00088                       Oid classId,
00089                       Oid objectId,
00090                       int subId,
00091                       void *arg)
00092 {
00093     if (next_object_access_hook)
00094         (*next_object_access_hook) (access, classId, objectId, subId, arg);
00095 
00096     switch (access)
00097     {
00098         case OAT_POST_CREATE:
00099             {
00100                 ObjectAccessPostCreate *pc_arg = arg;
00101                 bool    is_internal;
00102 
00103                 is_internal = pc_arg ? pc_arg->is_internal : false;
00104 
00105                 switch (classId)
00106                 {
00107                     case DatabaseRelationId:
00108                         Assert(!is_internal);
00109                         sepgsql_database_post_create(objectId,
00110                                                      sepgsql_context_info.createdb_dtemplate);
00111                         break;
00112 
00113                     case NamespaceRelationId:
00114                         Assert(!is_internal);
00115                         sepgsql_schema_post_create(objectId);
00116                         break;
00117 
00118                     case RelationRelationId:
00119                         if (subId == 0)
00120                         {
00121                             /*
00122                              * The cases in which we want to apply permission
00123                              * checks on creation of a new relation correspond
00124                              * to direct user invocation.  For internal uses,
00125                              * that is creation of toast tables, index rebuild
00126                              * or ALTER TABLE commands, we need neither
00127                              * assignment of security labels nor permission
00128                              * checks.
00129                              */
00130                             if (is_internal)
00131                                 break;
00132 
00133                             sepgsql_relation_post_create(objectId);
00134                         }
00135                         else
00136                             sepgsql_attribute_post_create(objectId, subId);
00137                         break;
00138 
00139                     case ProcedureRelationId:
00140                         Assert(!is_internal);
00141                         sepgsql_proc_post_create(objectId);
00142                         break;
00143 
00144                     default:
00145                         /* Ignore unsupported object classes */
00146                         break;
00147                 }
00148             }
00149             break;
00150 
00151         case OAT_DROP:
00152             {
00153                 ObjectAccessDrop *drop_arg = (ObjectAccessDrop *) arg;
00154 
00155                 /*
00156                  * No need to apply permission checks on object deletion due
00157                  * to internal cleanups; such as removal of temporary database
00158                  * object on session closed.
00159                  */
00160                 if ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) != 0)
00161                     break;
00162 
00163                 switch (classId)
00164                 {
00165                     case DatabaseRelationId:
00166                         sepgsql_database_drop(objectId);
00167                         break;
00168 
00169                     case NamespaceRelationId:
00170                         sepgsql_schema_drop(objectId);
00171                         break;
00172 
00173                     case RelationRelationId:
00174                         if (subId == 0)
00175                             sepgsql_relation_drop(objectId);
00176                         else
00177                             sepgsql_attribute_drop(objectId, subId);
00178                         break;
00179 
00180                     case ProcedureRelationId:
00181                         sepgsql_proc_drop(objectId);
00182                         break;
00183 
00184                     default:
00185                         /* Ignore unsupported object classes */
00186                         break;
00187                 }
00188             }
00189             break;
00190 
00191         case OAT_POST_ALTER:
00192             {
00193                 ObjectAccessPostAlter  *pa_arg = arg;
00194                 bool    is_internal = pa_arg->is_internal;
00195 
00196                 switch (classId)
00197                 {
00198                     case DatabaseRelationId:
00199                         Assert(!is_internal);
00200                         sepgsql_database_setattr(objectId);
00201                         break;
00202 
00203                     case NamespaceRelationId:
00204                         Assert(!is_internal);
00205                         sepgsql_schema_setattr(objectId);
00206                         break;
00207 
00208                     case RelationRelationId:
00209                         if (subId == 0)
00210                         {
00211                             /*
00212                              * A case when we don't want to apply permission
00213                              * check is that relation is internally altered
00214                              * without user's intention. E.g, no need to
00215                              * check on toast table/index to be renamed at
00216                              * end of the table rewrites.
00217                              */
00218                             if (is_internal)
00219                                 break;
00220 
00221                             sepgsql_relation_setattr(objectId);
00222                         }
00223                         else
00224                             sepgsql_attribute_setattr(objectId, subId);
00225                         break;
00226 
00227                     case ProcedureRelationId:
00228                         Assert(!is_internal);
00229                         sepgsql_proc_setattr(objectId);
00230                         break;
00231 
00232                     default:
00233                         /* Ignore unsupported object classes */
00234                         break;
00235                 }
00236             }
00237             break;
00238 
00239         case OAT_NAMESPACE_SEARCH:
00240             {
00241                 ObjectAccessNamespaceSearch   *ns_arg = arg;
00242 
00243                 /*
00244                  * If stacked extension already decided not to allow users
00245                  * to search this schema, we just stick with that decision.
00246                  */
00247                 if (!ns_arg->result)
00248                     break;
00249 
00250                 Assert(classId == NamespaceRelationId);
00251                 Assert(ns_arg->result);
00252                 ns_arg->result
00253                     = sepgsql_schema_search(objectId,
00254                                             ns_arg->ereport_on_violation);
00255             }
00256             break;
00257 
00258         case OAT_FUNCTION_EXECUTE:
00259             {
00260                 Assert(classId == ProcedureRelationId);
00261                 sepgsql_proc_execute(objectId);
00262             }
00263             break;
00264 
00265         default:
00266             elog(ERROR, "unexpected object access type: %d", (int) access);
00267             break;
00268     }
00269 }
00270 
00271 /*
00272  * sepgsql_exec_check_perms
00273  *
00274  * Entrypoint of DML permissions
00275  */
00276 static bool
00277 sepgsql_exec_check_perms(List *rangeTabls, bool abort)
00278 {
00279     /*
00280      * If security provider is stacking and one of them replied 'false' at
00281      * least, we don't need to check any more.
00282      */
00283     if (next_exec_check_perms_hook &&
00284         !(*next_exec_check_perms_hook) (rangeTabls, abort))
00285         return false;
00286 
00287     if (!sepgsql_dml_privileges(rangeTabls, abort))
00288         return false;
00289 
00290     return true;
00291 }
00292 
00293 /*
00294  * sepgsql_utility_command
00295  *
00296  * It tries to rough-grained control on utility commands; some of them can
00297  * break whole of the things if nefarious user would use.
00298  */
00299 static void
00300 sepgsql_utility_command(Node *parsetree,
00301                         const char *queryString,
00302                         ProcessUtilityContext context,
00303                         ParamListInfo params,
00304                         DestReceiver *dest,
00305                         char *completionTag)
00306 {
00307     sepgsql_context_info_t saved_context_info = sepgsql_context_info;
00308     ListCell   *cell;
00309 
00310     PG_TRY();
00311     {
00312         /*
00313          * Check command tag to avoid nefarious operations, and save the
00314          * current contextual information to determine whether we should apply
00315          * permission checks here, or not.
00316          */
00317         sepgsql_context_info.cmdtype = nodeTag(parsetree);
00318 
00319         switch (nodeTag(parsetree))
00320         {
00321             case T_CreatedbStmt:
00322 
00323                 /*
00324                  * We hope to reference name of the source database, but it
00325                  * does not appear in system catalog. So, we save it here.
00326                  */
00327                 foreach(cell, ((CreatedbStmt *) parsetree)->options)
00328                 {
00329                     DefElem    *defel = (DefElem *) lfirst(cell);
00330 
00331                     if (strcmp(defel->defname, "template") == 0)
00332                     {
00333                         sepgsql_context_info.createdb_dtemplate
00334                             = strVal(defel->arg);
00335                         break;
00336                     }
00337                 }
00338                 break;
00339 
00340             case T_LoadStmt:
00341 
00342                 /*
00343                  * We reject LOAD command across the board on enforcing mode,
00344                  * because a binary module can arbitrarily override hooks.
00345                  */
00346                 if (sepgsql_getenforce())
00347                 {
00348                     ereport(ERROR,
00349                             (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00350                              errmsg("SELinux: LOAD is not permitted")));
00351                 }
00352                 break;
00353             default:
00354 
00355                 /*
00356                  * Right now we don't check any other utility commands,
00357                  * because it needs more detailed information to make access
00358                  * control decision here, but we don't want to have two parse
00359                  * and analyze routines individually.
00360                  */
00361                 break;
00362         }
00363 
00364         if (next_ProcessUtility_hook)
00365             (*next_ProcessUtility_hook) (parsetree, queryString,
00366                                          context, params,
00367                                          dest, completionTag);
00368         else
00369             standard_ProcessUtility(parsetree, queryString,
00370                                     context, params,
00371                                     dest, completionTag);
00372     }
00373     PG_CATCH();
00374     {
00375         sepgsql_context_info = saved_context_info;
00376         PG_RE_THROW();
00377     }
00378     PG_END_TRY();
00379     sepgsql_context_info = saved_context_info;
00380 }
00381 
00382 /*
00383  * Module load/unload callback
00384  */
00385 void
00386 _PG_init(void)
00387 {
00388     /*
00389      * We allow to load the SE-PostgreSQL module on single-user-mode or
00390      * shared_preload_libraries settings only.
00391      */
00392     if (IsUnderPostmaster)
00393         ereport(ERROR,
00394                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00395              errmsg("sepgsql must be loaded via shared_preload_libraries")));
00396 
00397     /*
00398      * Check availability of SELinux on the platform. If disabled, we cannot
00399      * activate any SE-PostgreSQL features, and we have to skip rest of
00400      * initialization.
00401      */
00402     if (is_selinux_enabled() < 1)
00403     {
00404         sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
00405         return;
00406     }
00407 
00408     /*
00409      * sepgsql.permissive = (on|off)
00410      *
00411      * This variable controls performing mode of SE-PostgreSQL on user's
00412      * session.
00413      */
00414     DefineCustomBoolVariable("sepgsql.permissive",
00415                              "Turn on/off permissive mode in SE-PostgreSQL",
00416                              NULL,
00417                              &sepgsql_permissive,
00418                              false,
00419                              PGC_SIGHUP,
00420                              GUC_NOT_IN_SAMPLE,
00421                              NULL,
00422                              NULL,
00423                              NULL);
00424 
00425     /*
00426      * sepgsql.debug_audit = (on|off)
00427      *
00428      * This variable allows users to turn on/off audit logs on access control
00429      * decisions, independent from auditallow/auditdeny setting in the
00430      * security policy. We intend to use this option for debugging purpose.
00431      */
00432     DefineCustomBoolVariable("sepgsql.debug_audit",
00433                              "Turn on/off debug audit messages",
00434                              NULL,
00435                              &sepgsql_debug_audit,
00436                              false,
00437                              PGC_USERSET,
00438                              GUC_NOT_IN_SAMPLE,
00439                              NULL,
00440                              NULL,
00441                              NULL);
00442 
00443     /* Initialize userspace access vector cache */
00444     sepgsql_avc_init();
00445 
00446     /* Initialize security label of the client and related stuff */
00447     sepgsql_init_client_label();
00448 
00449     /* Security label provider hook */
00450     register_label_provider(SEPGSQL_LABEL_TAG,
00451                             sepgsql_object_relabel);
00452 
00453     /* Object access hook */
00454     next_object_access_hook = object_access_hook;
00455     object_access_hook = sepgsql_object_access;
00456 
00457     /* DML permission check */
00458     next_exec_check_perms_hook = ExecutorCheckPerms_hook;
00459     ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
00460 
00461     /* ProcessUtility hook */
00462     next_ProcessUtility_hook = ProcessUtility_hook;
00463     ProcessUtility_hook = sepgsql_utility_command;
00464 
00465     /* init contextual info */
00466     memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
00467 }