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;
|
||
|
}
|