1 /* ------------------------------------------------------------------------- 2 * 3 * contrib/sepgsql/hooks.c 4 * 5 * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks. 6 * 7 * Copyright (c) 2010-2018, PostgreSQL Global Development Group 8 * 9 * ------------------------------------------------------------------------- 10 */ 11 #include "postgres.h" 12 13 #include "catalog/dependency.h" 14 #include "catalog/objectaccess.h" 15 #include "catalog/pg_class.h" 16 #include "catalog/pg_database.h" 17 #include "catalog/pg_namespace.h" 18 #include "catalog/pg_proc.h" 19 #include "commands/seclabel.h" 20 #include "executor/executor.h" 21 #include "fmgr.h" 22 #include "miscadmin.h" 23 #include "tcop/utility.h" 24 #include "utils/guc.h" 25 #include "utils/queryenvironment.h" 26 27 #include "sepgsql.h" 28 29 PG_MODULE_MAGIC; 30 31 /* 32 * Declarations 33 */ 34 void _PG_init(void); 35 36 /* 37 * Saved hook entries (if stacked) 38 */ 39 static object_access_hook_type next_object_access_hook = NULL; 40 static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; 41 static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; 42 43 /* 44 * Contextual information on DDL commands 45 */ 46 typedef struct 47 { 48 NodeTag cmdtype; 49 50 /* 51 * Name of the template database given by users on CREATE DATABASE 52 * command. Elsewhere (including the case of default) NULL. 53 */ 54 const char *createdb_dtemplate; 55 } sepgsql_context_info_t; 56 57 static sepgsql_context_info_t sepgsql_context_info; 58 59 /* 60 * GUC: sepgsql.permissive = (on|off) 61 */ 62 static bool sepgsql_permissive; 63 64 bool 65 sepgsql_get_permissive(void) 66 { 67 return sepgsql_permissive; 68 } 69 70 /* 71 * GUC: sepgsql.debug_audit = (on|off) 72 */ 73 static bool sepgsql_debug_audit; 74 75 bool 76 sepgsql_get_debug_audit(void) 77 { 78 return sepgsql_debug_audit; 79 } 80 81 /* 82 * sepgsql_object_access 83 * 84 * Entrypoint of the object_access_hook. This routine performs as 85 * a dispatcher of invocation based on access type and object classes. 86 */ 87 static void 88 sepgsql_object_access(ObjectAccessType access, 89 Oid classId, 90 Oid objectId, 91 int subId, 92 void *arg) 93 { 94 if (next_object_access_hook) 95 (*next_object_access_hook) (access, classId, objectId, subId, arg); 96 97 switch (access) 98 { 99 case OAT_POST_CREATE: 100 { 101 ObjectAccessPostCreate *pc_arg = arg; 102 bool is_internal; 103 104 is_internal = pc_arg ? pc_arg->is_internal : false; 105 106 switch (classId) 107 { 108 case DatabaseRelationId: 109 Assert(!is_internal); 110 sepgsql_database_post_create(objectId, 111 sepgsql_context_info.createdb_dtemplate); 112 break; 113 114 case NamespaceRelationId: 115 Assert(!is_internal); 116 sepgsql_schema_post_create(objectId); 117 break; 118 119 case RelationRelationId: 120 if (subId == 0) 121 { 122 /* 123 * The cases in which we want to apply permission 124 * checks on creation of a new relation correspond 125 * to direct user invocation. For internal uses, 126 * that is creation of toast tables, index rebuild 127 * or ALTER TABLE commands, we need neither 128 * assignment of security labels nor permission 129 * checks. 130 */ 131 if (is_internal) 132 break; 133 134 sepgsql_relation_post_create(objectId); 135 } 136 else 137 sepgsql_attribute_post_create(objectId, subId); 138 break; 139 140 case ProcedureRelationId: 141 Assert(!is_internal); 142 sepgsql_proc_post_create(objectId); 143 break; 144 145 default: 146 /* Ignore unsupported object classes */ 147 break; 148 } 149 } 150 break; 151 152 case OAT_DROP: 153 { 154 ObjectAccessDrop *drop_arg = (ObjectAccessDrop *) arg; 155 156 /* 157 * No need to apply permission checks on object deletion due 158 * to internal cleanups; such as removal of temporary database 159 * object on session closed. 160 */ 161 if ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) != 0) 162 break; 163 164 switch (classId) 165 { 166 case DatabaseRelationId: 167 sepgsql_database_drop(objectId); 168 break; 169 170 case NamespaceRelationId: 171 sepgsql_schema_drop(objectId); 172 break; 173 174 case RelationRelationId: 175 if (subId == 0) 176 sepgsql_relation_drop(objectId); 177 else 178 sepgsql_attribute_drop(objectId, subId); 179 break; 180 181 case ProcedureRelationId: 182 sepgsql_proc_drop(objectId); 183 break; 184 185 default: 186 /* Ignore unsupported object classes */ 187 break; 188 } 189 } 190 break; 191 192 case OAT_POST_ALTER: 193 { 194 ObjectAccessPostAlter *pa_arg = arg; 195 bool is_internal = pa_arg->is_internal; 196 197 switch (classId) 198 { 199 case DatabaseRelationId: 200 Assert(!is_internal); 201 sepgsql_database_setattr(objectId); 202 break; 203 204 case NamespaceRelationId: 205 Assert(!is_internal); 206 sepgsql_schema_setattr(objectId); 207 break; 208 209 case RelationRelationId: 210 if (subId == 0) 211 { 212 /* 213 * A case when we don't want to apply permission 214 * check is that relation is internally altered 215 * without user's intention. E.g, no need to check 216 * on toast table/index to be renamed at end of 217 * the table rewrites. 218 */ 219 if (is_internal) 220 break; 221 222 sepgsql_relation_setattr(objectId); 223 } 224 else 225 sepgsql_attribute_setattr(objectId, subId); 226 break; 227 228 case ProcedureRelationId: 229 Assert(!is_internal); 230 sepgsql_proc_setattr(objectId); 231 break; 232 233 default: 234 /* Ignore unsupported object classes */ 235 break; 236 } 237 } 238 break; 239 240 case OAT_NAMESPACE_SEARCH: 241 { 242 ObjectAccessNamespaceSearch *ns_arg = arg; 243 244 /* 245 * If stacked extension already decided not to allow users to 246 * search this schema, we just stick with that decision. 247 */ 248 if (!ns_arg->result) 249 break; 250 251 Assert(classId == NamespaceRelationId); 252 Assert(ns_arg->result); 253 ns_arg->result 254 = sepgsql_schema_search(objectId, 255 ns_arg->ereport_on_violation); 256 } 257 break; 258 259 case OAT_FUNCTION_EXECUTE: 260 { 261 Assert(classId == ProcedureRelationId); 262 sepgsql_proc_execute(objectId); 263 } 264 break; 265 266 default: 267 elog(ERROR, "unexpected object access type: %d", (int) access); 268 break; 269 } 270 } 271 272 /* 273 * sepgsql_exec_check_perms 274 * 275 * Entrypoint of DML permissions 276 */ 277 static bool 278 sepgsql_exec_check_perms(List *rangeTabls, bool abort) 279 { 280 /* 281 * If security provider is stacking and one of them replied 'false' at 282 * least, we don't need to check any more. 283 */ 284 if (next_exec_check_perms_hook && 285 !(*next_exec_check_perms_hook) (rangeTabls, abort)) 286 return false; 287 288 if (!sepgsql_dml_privileges(rangeTabls, abort)) 289 return false; 290 291 return true; 292 } 293 294 /* 295 * sepgsql_utility_command 296 * 297 * It tries to rough-grained control on utility commands; some of them can 298 * break whole of the things if nefarious user would use. 299 */ 300 static void 301 sepgsql_utility_command(PlannedStmt *pstmt, 302 const char *queryString, 303 ProcessUtilityContext context, 304 ParamListInfo params, 305 QueryEnvironment *queryEnv, 306 DestReceiver *dest, 307 char *completionTag) 308 { 309 Node *parsetree = pstmt->utilityStmt; 310 sepgsql_context_info_t saved_context_info = sepgsql_context_info; 311 ListCell *cell; 312 313 PG_TRY(); 314 { 315 /* 316 * Check command tag to avoid nefarious operations, and save the 317 * current contextual information to determine whether we should apply 318 * permission checks here, or not. 319 */ 320 sepgsql_context_info.cmdtype = nodeTag(parsetree); 321 322 switch (nodeTag(parsetree)) 323 { 324 case T_CreatedbStmt: 325 326 /* 327 * We hope to reference name of the source database, but it 328 * does not appear in system catalog. So, we save it here. 329 */ 330 foreach(cell, ((CreatedbStmt *) parsetree)->options) 331 { 332 DefElem *defel = (DefElem *) lfirst(cell); 333 334 if (strcmp(defel->defname, "template") == 0) 335 { 336 sepgsql_context_info.createdb_dtemplate 337 = strVal(defel->arg); 338 break; 339 } 340 } 341 break; 342 343 case T_LoadStmt: 344 345 /* 346 * We reject LOAD command across the board on enforcing mode, 347 * because a binary module can arbitrarily override hooks. 348 */ 349 if (sepgsql_getenforce()) 350 { 351 ereport(ERROR, 352 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), 353 errmsg("SELinux: LOAD is not permitted"))); 354 } 355 break; 356 default: 357 358 /* 359 * Right now we don't check any other utility commands, 360 * because it needs more detailed information to make access 361 * control decision here, but we don't want to have two parse 362 * and analyze routines individually. 363 */ 364 break; 365 } 366 367 if (next_ProcessUtility_hook) 368 (*next_ProcessUtility_hook) (pstmt, queryString, 369 context, params, queryEnv, 370 dest, completionTag); 371 else 372 standard_ProcessUtility(pstmt, queryString, 373 context, params, queryEnv, 374 dest, completionTag); 375 } 376 PG_CATCH(); 377 { 378 sepgsql_context_info = saved_context_info; 379 PG_RE_THROW(); 380 } 381 PG_END_TRY(); 382 sepgsql_context_info = saved_context_info; 383 } 384 385 /* 386 * Module load/unload callback 387 */ 388 void 389 _PG_init(void) 390 { 391 /* 392 * We allow to load the SE-PostgreSQL module on single-user-mode or 393 * shared_preload_libraries settings only. 394 */ 395 if (IsUnderPostmaster) 396 ereport(ERROR, 397 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), 398 errmsg("sepgsql must be loaded via shared_preload_libraries"))); 399 400 /* 401 * Check availability of SELinux on the platform. If disabled, we cannot 402 * activate any SE-PostgreSQL features, and we have to skip rest of 403 * initialization. 404 */ 405 if (is_selinux_enabled() < 1) 406 { 407 sepgsql_set_mode(SEPGSQL_MODE_DISABLED); 408 return; 409 } 410 411 /* 412 * sepgsql.permissive = (on|off) 413 * 414 * This variable controls performing mode of SE-PostgreSQL on user's 415 * session. 416 */ 417 DefineCustomBoolVariable("sepgsql.permissive", 418 "Turn on/off permissive mode in SE-PostgreSQL", 419 NULL, 420 &sepgsql_permissive, 421 false, 422 PGC_SIGHUP, 423 GUC_NOT_IN_SAMPLE, 424 NULL, 425 NULL, 426 NULL); 427 428 /* 429 * sepgsql.debug_audit = (on|off) 430 * 431 * This variable allows users to turn on/off audit logs on access control 432 * decisions, independent from auditallow/auditdeny setting in the 433 * security policy. We intend to use this option for debugging purpose. 434 */ 435 DefineCustomBoolVariable("sepgsql.debug_audit", 436 "Turn on/off debug audit messages", 437 NULL, 438 &sepgsql_debug_audit, 439 false, 440 PGC_USERSET, 441 GUC_NOT_IN_SAMPLE, 442 NULL, 443 NULL, 444 NULL); 445 446 /* Initialize userspace access vector cache */ 447 sepgsql_avc_init(); 448 449 /* Initialize security label of the client and related stuff */ 450 sepgsql_init_client_label(); 451 452 /* Security label provider hook */ 453 register_label_provider(SEPGSQL_LABEL_TAG, 454 sepgsql_object_relabel); 455 456 /* Object access hook */ 457 next_object_access_hook = object_access_hook; 458 object_access_hook = sepgsql_object_access; 459 460 /* DML permission check */ 461 next_exec_check_perms_hook = ExecutorCheckPerms_hook; 462 ExecutorCheckPerms_hook = sepgsql_exec_check_perms; 463 464 /* ProcessUtility hook */ 465 next_ProcessUtility_hook = ProcessUtility_hook; 466 ProcessUtility_hook = sepgsql_utility_command; 467 468 /* init contextual info */ 469 memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info)); 470 } 471