| /* ------------------------------------------------------------------------- |
| * |
| * contrib/sepgsql/selinux.c |
| * |
| * Interactions between userspace and selinux in kernelspace, |
| * using libselinux api. |
| * |
| * Copyright (c) 2010-2021, 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 ? 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. 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, |
| 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); |
| |
| 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; |
| |
| 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; |
| } |