mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-01 21:31:19 +03:00 
			
		
		
		
	SELinux itself does this (at least in modern releases), and it seems like a good idea to reduce confusion. Dave Page Discussion: https://postgr.es/m/CA+OCxowsQoLEYc=jN7OtNvOdX0Jg5L7nMYt++=k0X78HGq-sXg@mail.gmail.com
		
			
				
	
	
		
			955 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			955 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* -------------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * contrib/sepgsql/selinux.c
 | 
						|
 *
 | 
						|
 * Interactions between userspace and selinux in kernelspace,
 | 
						|
 * using libselinux api.
 | 
						|
 *
 | 
						|
 * Copyright (c) 2010-2022, PostgreSQL Global Development Group
 | 
						|
 *
 | 
						|
 * -------------------------------------------------------------------------
 | 
						|
 */
 | 
						|
#include "postgres.h"
 | 
						|
 | 
						|
#include "lib/stringinfo.h"
 | 
						|
 | 
						|
#include "sepgsql.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * selinux_catalog
 | 
						|
 *
 | 
						|
 * This mapping table enables to translate the name of object classes and
 | 
						|
 * access vectors to/from their own codes.
 | 
						|
 * When we ask SELinux whether the required privileges are allowed or not,
 | 
						|
 * we use security_compute_av(3). It needs us to represent object classes
 | 
						|
 * and access vectors using 'external' codes defined in the security policy.
 | 
						|
 * It is determined in the runtime, not build time. So, it needs an internal
 | 
						|
 * service to translate object class/access vectors which we want to check
 | 
						|
 * into the code which kernel want to be given.
 | 
						|
 */
 | 
						|
static struct
 | 
						|
{
 | 
						|
	const char *class_name;
 | 
						|
	uint16		class_code;
 | 
						|
	struct
 | 
						|
	{
 | 
						|
		const char *av_name;
 | 
						|
		uint32		av_code;
 | 
						|
	}			av[32];
 | 
						|
}			selinux_catalog[] =
 | 
						|
 | 
						|
{
 | 
						|
	{
 | 
						|
		"process", SEPG_CLASS_PROCESS,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"transition", SEPG_PROCESS__TRANSITION
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"dyntransition", SEPG_PROCESS__DYNTRANSITION
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setcurrent", SEPG_PROCESS__SETCURRENT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"file", SEPG_CLASS_FILE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"read", SEPG_FILE__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_FILE__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"create", SEPG_FILE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_FILE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"unlink", SEPG_FILE__UNLINK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rename", SEPG_FILE__RENAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"append", SEPG_FILE__APPEND
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"dir", SEPG_CLASS_DIR,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"read", SEPG_DIR__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_DIR__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"create", SEPG_DIR__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DIR__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"unlink", SEPG_DIR__UNLINK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rename", SEPG_DIR__RENAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"search", SEPG_DIR__SEARCH
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"add_name", SEPG_DIR__ADD_NAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"remove_name", SEPG_DIR__REMOVE_NAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rmdir", SEPG_DIR__RMDIR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"reparent", SEPG_DIR__REPARENT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"lnk_file", SEPG_CLASS_LNK_FILE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"read", SEPG_LNK_FILE__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_LNK_FILE__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"create", SEPG_LNK_FILE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_LNK_FILE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"unlink", SEPG_LNK_FILE__UNLINK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rename", SEPG_LNK_FILE__RENAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"chr_file", SEPG_CLASS_CHR_FILE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"read", SEPG_CHR_FILE__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_CHR_FILE__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"create", SEPG_CHR_FILE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_CHR_FILE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"unlink", SEPG_CHR_FILE__UNLINK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rename", SEPG_CHR_FILE__RENAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"blk_file", SEPG_CLASS_BLK_FILE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"read", SEPG_BLK_FILE__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_BLK_FILE__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"create", SEPG_BLK_FILE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_BLK_FILE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"unlink", SEPG_BLK_FILE__UNLINK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rename", SEPG_BLK_FILE__RENAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"sock_file", SEPG_CLASS_SOCK_FILE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"read", SEPG_SOCK_FILE__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_SOCK_FILE__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"create", SEPG_SOCK_FILE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_SOCK_FILE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"unlink", SEPG_SOCK_FILE__UNLINK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rename", SEPG_SOCK_FILE__RENAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"fifo_file", SEPG_CLASS_FIFO_FILE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"read", SEPG_FIFO_FILE__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_FIFO_FILE__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"create", SEPG_FIFO_FILE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_FIFO_FILE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"unlink", SEPG_FIFO_FILE__UNLINK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"rename", SEPG_FIFO_FILE__RENAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_database", SEPG_CLASS_DB_DATABASE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_DATABASE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_DATABASE__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_DATABASE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_DATABASE__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_DATABASE__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_DATABASE__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"access", SEPG_DB_DATABASE__ACCESS
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"load_module", SEPG_DB_DATABASE__LOAD_MODULE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_schema", SEPG_CLASS_DB_SCHEMA,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_SCHEMA__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_SCHEMA__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_SCHEMA__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_SCHEMA__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_SCHEMA__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_SCHEMA__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"search", SEPG_DB_SCHEMA__SEARCH
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"add_name", SEPG_DB_SCHEMA__ADD_NAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"remove_name", SEPG_DB_SCHEMA__REMOVE_NAME
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_table", SEPG_CLASS_DB_TABLE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_TABLE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_TABLE__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_TABLE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_TABLE__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_TABLE__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_TABLE__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"select", SEPG_DB_TABLE__SELECT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"update", SEPG_DB_TABLE__UPDATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"insert", SEPG_DB_TABLE__INSERT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"delete", SEPG_DB_TABLE__DELETE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"lock", SEPG_DB_TABLE__LOCK
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"truncate", SEPG_DB_TABLE__TRUNCATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_sequence", SEPG_CLASS_DB_SEQUENCE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_SEQUENCE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_SEQUENCE__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_SEQUENCE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_SEQUENCE__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_SEQUENCE__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"get_value", SEPG_DB_SEQUENCE__GET_VALUE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"next_value", SEPG_DB_SEQUENCE__NEXT_VALUE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"set_value", SEPG_DB_SEQUENCE__SET_VALUE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_procedure", SEPG_CLASS_DB_PROCEDURE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_PROCEDURE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_PROCEDURE__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_PROCEDURE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_PROCEDURE__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_PROCEDURE__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"execute", SEPG_DB_PROCEDURE__EXECUTE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"install", SEPG_DB_PROCEDURE__INSTALL
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_column", SEPG_CLASS_DB_COLUMN,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_COLUMN__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_COLUMN__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_COLUMN__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_COLUMN__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_COLUMN__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_COLUMN__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"select", SEPG_DB_COLUMN__SELECT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"update", SEPG_DB_COLUMN__UPDATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"insert", SEPG_DB_COLUMN__INSERT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_tuple", SEPG_CLASS_DB_TUPLE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_TUPLE__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_TUPLE__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"select", SEPG_DB_TUPLE__SELECT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"update", SEPG_DB_TUPLE__UPDATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"insert", SEPG_DB_TUPLE__INSERT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"delete", SEPG_DB_TUPLE__DELETE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_blob", SEPG_CLASS_DB_BLOB,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_BLOB__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_BLOB__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_BLOB__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_BLOB__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_BLOB__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_BLOB__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"read", SEPG_DB_BLOB__READ
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"write", SEPG_DB_BLOB__WRITE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"import", SEPG_DB_BLOB__IMPORT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"export", SEPG_DB_BLOB__EXPORT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_language", SEPG_CLASS_DB_LANGUAGE,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_LANGUAGE__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_LANGUAGE__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_LANGUAGE__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_LANGUAGE__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_LANGUAGE__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"implement", SEPG_DB_LANGUAGE__IMPLEMENT
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"execute", SEPG_DB_LANGUAGE__EXECUTE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		"db_view", SEPG_CLASS_DB_VIEW,
 | 
						|
		{
 | 
						|
			{
 | 
						|
				"create", SEPG_DB_VIEW__CREATE
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"drop", SEPG_DB_VIEW__DROP
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"getattr", SEPG_DB_VIEW__GETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"setattr", SEPG_DB_VIEW__SETATTR
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelfrom", SEPG_DB_VIEW__RELABELFROM
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"relabelto", SEPG_DB_VIEW__RELABELTO
 | 
						|
			},
 | 
						|
			{
 | 
						|
				"expand", SEPG_DB_VIEW__EXPAND
 | 
						|
			},
 | 
						|
			{
 | 
						|
				NULL, 0UL
 | 
						|
			},
 | 
						|
		}
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_mode
 | 
						|
 *
 | 
						|
 * SEPGSQL_MODE_DISABLED: Disabled on runtime
 | 
						|
 * SEPGSQL_MODE_DEFAULT: Same as system settings
 | 
						|
 * SEPGSQL_MODE_PERMISSIVE: Always permissive mode
 | 
						|
 * SEPGSQL_MODE_INTERNAL: Same as permissive, except for no audit logs
 | 
						|
 */
 | 
						|
static int	sepgsql_mode = SEPGSQL_MODE_INTERNAL;
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_is_enabled
 | 
						|
 */
 | 
						|
bool
 | 
						|
sepgsql_is_enabled(void)
 | 
						|
{
 | 
						|
	return (sepgsql_mode != SEPGSQL_MODE_DISABLED);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_get_mode
 | 
						|
 */
 | 
						|
int
 | 
						|
sepgsql_get_mode(void)
 | 
						|
{
 | 
						|
	return sepgsql_mode;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_set_mode
 | 
						|
 */
 | 
						|
int
 | 
						|
sepgsql_set_mode(int new_mode)
 | 
						|
{
 | 
						|
	int			old_mode = sepgsql_mode;
 | 
						|
 | 
						|
	sepgsql_mode = new_mode;
 | 
						|
 | 
						|
	return old_mode;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_getenforce
 | 
						|
 *
 | 
						|
 * It returns whether the current working mode tries to enforce access
 | 
						|
 * control decision, or not. It shall be enforced when sepgsql_mode is
 | 
						|
 * SEPGSQL_MODE_DEFAULT and system is running in enforcing mode.
 | 
						|
 */
 | 
						|
bool
 | 
						|
sepgsql_getenforce(void)
 | 
						|
{
 | 
						|
	if (sepgsql_mode == SEPGSQL_MODE_DEFAULT &&
 | 
						|
		selinux_status_getenforce() > 0)
 | 
						|
		return true;
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_audit_log
 | 
						|
 *
 | 
						|
 * It generates a security audit record. It writes out audit records
 | 
						|
 * into standard PG's logfile.
 | 
						|
 *
 | 
						|
 * SELinux can control what should be audited and should not using
 | 
						|
 * "auditdeny" and "auditallow" rules in the security policy. In the
 | 
						|
 * default, all the access violations are audited, and all the access
 | 
						|
 * allowed are not audited. But we can set up the security policy, so
 | 
						|
 * we can have exceptions. So, it is necessary to follow the suggestion
 | 
						|
 * come from the security policy. (av_decision.auditallow and auditdeny)
 | 
						|
 *
 | 
						|
 * Security audit is an important feature, because it enables us to check
 | 
						|
 * what was happen if we have a security incident. In fact, ISO/IEC15408
 | 
						|
 * defines several security functionalities for audit features.
 | 
						|
 */
 | 
						|
void
 | 
						|
sepgsql_audit_log(bool denied,
 | 
						|
				  bool enforcing,
 | 
						|
				  const char *scontext,
 | 
						|
				  const char *tcontext,
 | 
						|
				  uint16 tclass,
 | 
						|
				  uint32 audited,
 | 
						|
				  const char *audit_name)
 | 
						|
{
 | 
						|
	StringInfoData buf;
 | 
						|
	const char *class_name;
 | 
						|
	const char *av_name;
 | 
						|
	int			i;
 | 
						|
 | 
						|
	/* lookup name of the object class */
 | 
						|
	Assert(tclass < SEPG_CLASS_MAX);
 | 
						|
	class_name = selinux_catalog[tclass].class_name;
 | 
						|
 | 
						|
	/* lookup name of the permissions */
 | 
						|
	initStringInfo(&buf);
 | 
						|
	appendStringInfo(&buf, "%s {",
 | 
						|
					 (denied ? "denied" : "allowed"));
 | 
						|
	for (i = 0; selinux_catalog[tclass].av[i].av_name; i++)
 | 
						|
	{
 | 
						|
		if (audited & (1UL << i))
 | 
						|
		{
 | 
						|
			av_name = selinux_catalog[tclass].av[i].av_name;
 | 
						|
			appendStringInfo(&buf, " %s", av_name);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	appendStringInfoString(&buf, " }");
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Call external audit module, if loaded
 | 
						|
	 */
 | 
						|
	appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
 | 
						|
					 scontext, tcontext, class_name);
 | 
						|
	if (audit_name)
 | 
						|
		appendStringInfo(&buf, " name=\"%s\"", audit_name);
 | 
						|
 | 
						|
	if (enforcing)
 | 
						|
		appendStringInfoString(&buf, " permissive=0");
 | 
						|
	else
 | 
						|
		appendStringInfoString(&buf, " permissive=1");
 | 
						|
 | 
						|
	ereport(LOG, (errmsg("SELinux: %s", buf.data)));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_compute_avd
 | 
						|
 *
 | 
						|
 * It actually asks SELinux what permissions are allowed on a pair of
 | 
						|
 * the security contexts and object class. It also returns what permissions
 | 
						|
 * should be audited on access violation or allowed.
 | 
						|
 * In most cases, subject's security context (scontext) is a client, and
 | 
						|
 * target security context (tcontext) is a database object.
 | 
						|
 *
 | 
						|
 * The access control decision shall be set on the given av_decision.
 | 
						|
 * The av_decision.allowed has a bitmask of SEPG_<class>__<perms>
 | 
						|
 * to suggest a set of allowed actions in this object class.
 | 
						|
 */
 | 
						|
void
 | 
						|
sepgsql_compute_avd(const char *scontext,
 | 
						|
					const char *tcontext,
 | 
						|
					uint16 tclass,
 | 
						|
					struct av_decision *avd)
 | 
						|
{
 | 
						|
	const char *tclass_name;
 | 
						|
	security_class_t tclass_ex;
 | 
						|
	struct av_decision avd_ex;
 | 
						|
	int			i,
 | 
						|
				deny_unknown = security_deny_unknown();
 | 
						|
 | 
						|
	/* Get external code of the object class */
 | 
						|
	Assert(tclass < SEPG_CLASS_MAX);
 | 
						|
	Assert(tclass == selinux_catalog[tclass].class_code);
 | 
						|
 | 
						|
	tclass_name = selinux_catalog[tclass].class_name;
 | 
						|
	tclass_ex = string_to_security_class(tclass_name);
 | 
						|
 | 
						|
	if (tclass_ex == 0)
 | 
						|
	{
 | 
						|
		/*
 | 
						|
		 * If the current security policy does not support permissions
 | 
						|
		 * corresponding to database objects, we fill up them with dummy data.
 | 
						|
		 * If security_deny_unknown() returns positive value, undefined
 | 
						|
		 * permissions should be denied. Otherwise, allowed
 | 
						|
		 */
 | 
						|
		avd->allowed = (security_deny_unknown() > 0 ? 0 : ~0);
 | 
						|
		avd->auditallow = 0U;
 | 
						|
		avd->auditdeny = ~0U;
 | 
						|
		avd->flags = 0;
 | 
						|
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Ask SELinux what is allowed set of permissions on a pair of the
 | 
						|
	 * security contexts and the given object class.
 | 
						|
	 */
 | 
						|
	if (security_compute_av_flags_raw(scontext,
 | 
						|
									  tcontext,
 | 
						|
									  tclass_ex, 0, &avd_ex) < 0)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INTERNAL_ERROR),
 | 
						|
				 errmsg("SELinux could not compute av_decision: "
 | 
						|
						"scontext=%s tcontext=%s tclass=%s: %m",
 | 
						|
						scontext, tcontext, tclass_name)));
 | 
						|
 | 
						|
	/*
 | 
						|
	 * SELinux returns its access control decision as a set of permissions
 | 
						|
	 * represented in external code which depends on run-time environment. So,
 | 
						|
	 * we need to translate it to the internal representation before returning
 | 
						|
	 * results for the caller.
 | 
						|
	 */
 | 
						|
	memset(avd, 0, sizeof(struct av_decision));
 | 
						|
 | 
						|
	for (i = 0; selinux_catalog[tclass].av[i].av_name; i++)
 | 
						|
	{
 | 
						|
		access_vector_t av_code_ex;
 | 
						|
		const char *av_name = selinux_catalog[tclass].av[i].av_name;
 | 
						|
		uint32		av_code = selinux_catalog[tclass].av[i].av_code;
 | 
						|
 | 
						|
		av_code_ex = string_to_av_perm(tclass_ex, av_name);
 | 
						|
		if (av_code_ex == 0)
 | 
						|
		{
 | 
						|
			/* fill up undefined permissions */
 | 
						|
			if (!deny_unknown)
 | 
						|
				avd->allowed |= av_code;
 | 
						|
			avd->auditdeny |= av_code;
 | 
						|
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (avd_ex.allowed & av_code_ex)
 | 
						|
			avd->allowed |= av_code;
 | 
						|
		if (avd_ex.auditallow & av_code_ex)
 | 
						|
			avd->auditallow |= av_code;
 | 
						|
		if (avd_ex.auditdeny & av_code_ex)
 | 
						|
			avd->auditdeny |= av_code;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_compute_create
 | 
						|
 *
 | 
						|
 * It returns a default security context to be assigned on a new database
 | 
						|
 * object. SELinux compute it based on a combination of client, upper object
 | 
						|
 * which owns the new object and object class.
 | 
						|
 *
 | 
						|
 * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create
 | 
						|
 * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0),
 | 
						|
 * SELinux looks-up its security policy. If it has a special rule on the
 | 
						|
 * combination of these security contexts and object class (db_table),
 | 
						|
 * it returns the security context suggested by the special rule.
 | 
						|
 * Otherwise, it returns the security context of schema, as is.
 | 
						|
 *
 | 
						|
 * We expect the caller already applies sanity/validation checks on the
 | 
						|
 * given security context.
 | 
						|
 *
 | 
						|
 * scontext: security context of the subject (mostly, peer process).
 | 
						|
 * tcontext: security context of the upper database object.
 | 
						|
 * tclass: class code (SEPG_CLASS_*) of the new object in creation
 | 
						|
 */
 | 
						|
char *
 | 
						|
sepgsql_compute_create(const char *scontext,
 | 
						|
					   const char *tcontext,
 | 
						|
					   uint16 tclass,
 | 
						|
					   const char *objname)
 | 
						|
{
 | 
						|
	char	   *ncontext;
 | 
						|
	security_class_t tclass_ex;
 | 
						|
	const char *tclass_name;
 | 
						|
	char	   *result;
 | 
						|
 | 
						|
	/* Get external code of the object class */
 | 
						|
	Assert(tclass < SEPG_CLASS_MAX);
 | 
						|
 | 
						|
	tclass_name = selinux_catalog[tclass].class_name;
 | 
						|
	tclass_ex = string_to_security_class(tclass_name);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Ask SELinux what is the default context for the given object class on a
 | 
						|
	 * pair of security contexts
 | 
						|
	 */
 | 
						|
	if (security_compute_create_name_raw(scontext,
 | 
						|
										 tcontext,
 | 
						|
										 tclass_ex,
 | 
						|
										 objname,
 | 
						|
										 &ncontext) < 0)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INTERNAL_ERROR),
 | 
						|
				 errmsg("SELinux could not compute a new context: "
 | 
						|
						"scontext=%s tcontext=%s tclass=%s: %m",
 | 
						|
						scontext, tcontext, tclass_name)));
 | 
						|
 | 
						|
	/*
 | 
						|
	 * libselinux returns malloc()'ed string, so we need to copy it on the
 | 
						|
	 * palloc()'ed region.
 | 
						|
	 */
 | 
						|
	PG_TRY();
 | 
						|
	{
 | 
						|
		result = pstrdup(ncontext);
 | 
						|
	}
 | 
						|
	PG_FINALLY();
 | 
						|
	{
 | 
						|
		freecon(ncontext);
 | 
						|
	}
 | 
						|
	PG_END_TRY();
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * sepgsql_check_perms
 | 
						|
 *
 | 
						|
 * It makes access control decision without userspace caching mechanism.
 | 
						|
 * If SELinux denied the required accesses on the pair of security labels,
 | 
						|
 * it raises an error or returns false.
 | 
						|
 *
 | 
						|
 * scontext: security label of the subject (mostly, peer process)
 | 
						|
 * tcontext: security label of the object being referenced
 | 
						|
 * tclass: class code (SEPG_CLASS_*) of the object being referenced
 | 
						|
 * required: a mask of required permissions (SEPG_<class>__<perm>)
 | 
						|
 * audit_name: a human-readable object name for audit logs, or NULL.
 | 
						|
 * abort_on_violation: true, if error shall be raised on access violation
 | 
						|
 */
 | 
						|
bool
 | 
						|
sepgsql_check_perms(const char *scontext,
 | 
						|
					const char *tcontext,
 | 
						|
					uint16 tclass,
 | 
						|
					uint32 required,
 | 
						|
					const char *audit_name,
 | 
						|
					bool abort_on_violation)
 | 
						|
{
 | 
						|
	struct av_decision avd;
 | 
						|
	uint32		denied;
 | 
						|
	uint32		audited;
 | 
						|
	bool		result = true;
 | 
						|
	bool		enforcing;
 | 
						|
 | 
						|
	sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
 | 
						|
 | 
						|
	denied = required & ~avd.allowed;
 | 
						|
 | 
						|
	if (sepgsql_get_debug_audit())
 | 
						|
		audited = (denied ? denied : required);
 | 
						|
	else
 | 
						|
		audited = (denied ? (denied & avd.auditdeny)
 | 
						|
				   : (required & avd.auditallow));
 | 
						|
 | 
						|
	enforcing = sepgsql_getenforce() > 0 &&
 | 
						|
		(avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) == 0;
 | 
						|
 | 
						|
	if (denied && enforcing)
 | 
						|
		result = false;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * It records a security audit for the request, if needed. But, when
 | 
						|
	 * SE-PgSQL performs 'internal' mode, it needs to keep silent.
 | 
						|
	 */
 | 
						|
	if (audited && sepgsql_mode != SEPGSQL_MODE_INTERNAL)
 | 
						|
	{
 | 
						|
		sepgsql_audit_log(denied,
 | 
						|
						  enforcing,
 | 
						|
						  scontext,
 | 
						|
						  tcontext,
 | 
						|
						  tclass,
 | 
						|
						  audited,
 | 
						|
						  audit_name);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!result && abort_on_violation)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 | 
						|
				 errmsg("SELinux: security policy violation")));
 | 
						|
	return result;
 | 
						|
}
 |