mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-22 09:03:24 +02:00
The new indent version includes numerous fixes thanks to Piotr Stefaniak. The main changes visible in this commit are: * Nicer formatting of function-pointer declarations. * No longer unexpectedly removes spaces in expressions using casts, sizeof, or offsetof. * No longer wants to add a space in "struct structname *varname", as well as some similar cases for const- or volatile-qualified pointers. * Declarations using PG_USED_FOR_ASSERTS_ONLY are formatted more nicely. * Fixes bug where comments following declarations were sometimes placed with no space separating them from the code. * Fixes some odd decisions for comments following case labels. * Fixes some cases where comments following code were indented to less than the expected column 33. On the less good side, it now tends to put more whitespace around typedef names that are not listed in typedefs.list. This might encourage us to put more effort into typedef name collection; it's not really a bug in indent itself. There are more changes coming after this round, having to do with comment indentation and alignment of lines appearing within parentheses. I wanted to limit the size of the diffs to something that could be reviewed without one's eyes completely glazing over, so it seemed better to split up the changes as much as practical. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
949 lines
18 KiB
C
949 lines
18 KiB
C
/* -------------------------------------------------------------------------
|
|
*
|
|
* contrib/sepgsql/selinux.c
|
|
*
|
|
* Interactions between userspace and selinux in kernelspace,
|
|
* using libselinux api.
|
|
*
|
|
* Copyright (c) 2010-2017, 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
|
|
},
|
|
{
|
|
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 ? true : false);
|
|
}
|
|
|
|
/*
|
|
* 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. In the default, it writes out
|
|
* audit records into standard PG's logfile. It also allows to set up
|
|
* external audit log receiver, such as auditd in Linux, using the
|
|
* sepgsql_audit_hook.
|
|
*
|
|
* 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,
|
|
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);
|
|
}
|
|
}
|
|
appendStringInfo(&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);
|
|
|
|
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((security_context_t) scontext,
|
|
(security_context_t) 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;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
security_context_t 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((security_context_t) scontext,
|
|
(security_context_t) 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_CATCH();
|
|
{
|
|
freecon(ncontext);
|
|
PG_RE_THROW();
|
|
}
|
|
PG_END_TRY();
|
|
freecon(ncontext);
|
|
|
|
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;
|
|
|
|
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));
|
|
|
|
if (denied &&
|
|
sepgsql_getenforce() > 0 &&
|
|
(avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) == 0)
|
|
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,
|
|
scontext,
|
|
tcontext,
|
|
tclass,
|
|
audited,
|
|
audit_name);
|
|
}
|
|
|
|
if (!result && abort_on_violation)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("SELinux: security policy violation")));
|
|
return result;
|
|
}
|