diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index 5900630..0000000 --- a/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -postgresql-8.3.1.tar.bz2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e18d446 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +postgresql-8.3.7.tar.bz2 diff --git a/Makefile b/Makefile deleted file mode 100644 index dad7c56..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Makefile for source rpm: sepostgresql -# $Id$ -NAME := sepostgresql -SPECFILE = $(firstword $(wildcard *.spec)) - -define find-makefile-common -for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done -endef - -MAKEFILE_COMMON := $(shell $(find-makefile-common)) - -ifeq ($(MAKEFILE_COMMON),) -# attept a checkout -define checkout-makefile-common -test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2 -endef - -MAKEFILE_COMMON := $(shell $(checkout-makefile-common)) -endif - -include $(MAKEFILE_COMMON) diff --git a/sepostgresql-pg_dump-8.3.1-2.patch b/sepostgresql-pg_dump-8.3.1-2.patch deleted file mode 100644 index 79f39c1..0000000 --- a/sepostgresql-pg_dump-8.3.1-2.patch +++ /dev/null @@ -1,447 +0,0 @@ -diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c ---- pgace/src/bin/pg_dump/pg_dump.c 2008-02-03 01:18:48.000000000 +0900 -+++ sepgsql/src/bin/pg_dump/pg_dump.c 2008-02-03 01:26:35.000000000 +0900 -@@ -118,6 +118,9 @@ static int g_numNamespaces; - /* flag to turn on/off dollar quoting */ - static int disable_dollar_quoting = 0; - -+/* flag to tuen on/off SE-PostgreSQL support */ -+#define SELINUX_SYSATTR_NAME "security_context" -+static int enable_selinux = 0; - - static void help(const char *progname); - static void expand_schema_name_patterns(SimpleStringList *patterns, -@@ -267,6 +270,7 @@ main(int argc, char **argv) - {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, - {"disable-triggers", no_argument, &disable_triggers, 1}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"enable-selinux", no_argument, &enable_selinux, 1}, - - {NULL, 0, NULL, 0} - }; -@@ -419,6 +423,8 @@ main(int argc, char **argv) - disable_triggers = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "enable-selinux") == 0) -+ enable_selinux = 1; - else - { - fprintf(stderr, -@@ -549,6 +555,24 @@ main(int argc, char **argv) - std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); - g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); - -+ if (enable_selinux) { -+ /* confirm whther server support SELinux features */ -+ const char *tmp = PQparameterStatus(g_conn, "security_sysattr_name"); -+ -+ if (!tmp) { -+ write_msg(NULL, "could not get security_sysattr_name from libpq\n"); -+ exit(1); -+ } -+ if (!!strcmp(SELINUX_SYSATTR_NAME, tmp) != 0) { -+ write_msg(NULL, "server does not have SELinux feature\n"); -+ exit(1); -+ } -+ if (g_fout->remoteVersion < 80204) { -+ write_msg(NULL, "server version is too old (%u)\n", g_fout->remoteVersion); -+ exit(1); -+ } -+ } -+ - /* Set the datestyle to ISO to ensure the dump's portability */ - do_sql_command(g_conn, "SET DATESTYLE = ISO"); - -@@ -771,6 +795,7 @@ help(const char *progname) - printf(_(" --use-set-session-authorization\n" - " use SESSION AUTHORIZATION commands instead of\n" - " ALTER OWNER commands to set ownership\n")); -+ printf(_(" --enable-selinux enable to dump security context in SE-PostgreSQL\n")); - - printf(_("\nConnection options:\n")); - printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -@@ -1160,7 +1185,8 @@ dumpTableData_insert(Archive *fout, void - if (fout->remoteVersion >= 70100) - { - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " -- "SELECT * FROM ONLY %s", -+ "SELECT * %s FROM ONLY %s", -+ (!enable_selinux ? "" : "," SELINUX_SYSATTR_NAME), - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname)); - } -@@ -1774,11 +1800,32 @@ dumpBlobComments(Archive *AH, void *arg) - Oid blobOid; - char *comment; - -+ blobOid = atooid(PQgetvalue(res, i, 0)); -+ -+ /* dump security context of binary large object */ -+ if (enable_selinux) { -+ PGresult *__res; -+ char query[512]; -+ -+ snprintf(query, sizeof(query), -+ "SELECT lo_get_security(%u)", blobOid); -+ __res = PQexec(g_conn, query); -+ check_sql_result(__res, g_conn, query, PGRES_TUPLES_OK); -+ -+ if (PQntuples(__res) != 1) { -+ write_msg(NULL, "lo_get_security(%u) returns %d tuples\n", -+ blobOid, PQntuples(__res)); -+ exit_nicely(); -+ } -+ archprintf(AH, "SELECT lo_set_security(%u, '%s');\n", -+ blobOid, PQgetvalue(__res, 0, 0)); -+ PQclear(__res); -+ } -+ - /* ignore blobs without comments */ - if (PQgetisnull(res, i, 1)) - continue; - -- blobOid = atooid(PQgetvalue(res, i, 0)); - comment = PQgetvalue(res, i, 1); - - printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", -@@ -2886,6 +2933,7 @@ getTables(int *numTables) - int i_owning_col; - int i_reltablespace; - int i_reloptions; -+ int i_selinux; - - /* Make sure we are in proper schema */ - selectSourceSchema("pg_catalog"); -@@ -2926,6 +2974,7 @@ getTables(int *numTables) - "d.refobjsubid as owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') as reloptions " -+ "%s " - "from pg_class c " - "left join pg_depend d on " - "(c.relkind = '%c' and " -@@ -2935,6 +2984,7 @@ getTables(int *numTables) - "where relkind in ('%c', '%c', '%c', '%c') " - "order by c.oid", - username_subquery, -+ (!enable_selinux ? "" : ",c." SELINUX_SYSATTR_NAME), - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); -@@ -3101,6 +3151,7 @@ getTables(int *numTables) - i_owning_col = PQfnumber(res, "owning_col"); - i_reltablespace = PQfnumber(res, "reltablespace"); - i_reloptions = PQfnumber(res, "reloptions"); -+ i_selinux = PQfnumber(res, SELINUX_SYSATTR_NAME); - - for (i = 0; i < ntups; i++) - { -@@ -3131,6 +3182,9 @@ getTables(int *numTables) - } - tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); - tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); -+ tblinfo[i].relsecurity = NULL; -+ if (i_selinux >= 0) -+ tblinfo[i].relsecurity = strdup(PQgetvalue(res, i, i_selinux)); - - /* other fields were zeroed above */ - -@@ -4319,6 +4373,7 @@ getTableAttrs(TableInfo *tblinfo, int nu - int i_atthasdef; - int i_attisdropped; - int i_attislocal; -+ int i_attselinux; - PGresult *res; - int ntups; - bool hasdefaults; -@@ -4362,11 +4417,13 @@ getTableAttrs(TableInfo *tblinfo, int nu - appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, " - "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname " -+ "%s " /* security context, if required */ - "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t " - "on a.atttypid = t.oid " - "where a.attrelid = '%u'::pg_catalog.oid " - "and a.attnum > 0::pg_catalog.int2 " - "order by a.attrelid, a.attnum", -+ (!enable_selinux ? "" : ",a." SELINUX_SYSATTR_NAME), - tbinfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 70100) -@@ -4415,6 +4472,7 @@ getTableAttrs(TableInfo *tblinfo, int nu - i_atthasdef = PQfnumber(res, "atthasdef"); - i_attisdropped = PQfnumber(res, "attisdropped"); - i_attislocal = PQfnumber(res, "attislocal"); -+ i_attselinux = PQfnumber(res, SELINUX_SYSATTR_NAME); - - tbinfo->numatts = ntups; - tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); -@@ -4425,6 +4483,7 @@ getTableAttrs(TableInfo *tblinfo, int nu - tbinfo->typstorage = (char *) malloc(ntups * sizeof(char)); - tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool)); - tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); -+ tbinfo->attsecurity = (char **) malloc(ntups * sizeof(char *)); - tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); - tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); - tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); -@@ -4456,6 +4515,11 @@ getTableAttrs(TableInfo *tblinfo, int nu - tbinfo->inhAttrs[j] = false; - tbinfo->inhAttrDef[j] = false; - tbinfo->inhNotNull[j] = false; -+ -+ /* security attribute, if defined */ -+ tbinfo->attsecurity[j] = NULL; -+ if (i_attselinux >= 0 && !PQgetisnull(res, j, i_attselinux)) -+ tbinfo->attsecurity[j] = strdup(PQgetvalue(res, j, i_attselinux)); - } - - PQclear(res); -@@ -6428,6 +6492,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - char *proconfig; - char *procost; - char *prorows; -+ char *proselinux = NULL; - char *lanname; - char *rettypename; - int nallargs; -@@ -6459,8 +6524,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " - "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname " -+ "%s " /* security context, if required */ - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", -+ (!enable_selinux ? "" : "," SELINUX_SYSATTR_NAME), - finfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 80100) -@@ -6562,6 +6629,13 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); - lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); - -+ if (enable_selinux) { -+ int i_selinux = PQfnumber(res, "security_context"); -+ -+ if (i_selinux >= 0 && !PQgetisnull(res, 0, i_selinux)) -+ proselinux = PQgetvalue(res, 0, i_selinux); -+ } -+ - /* - * See backend/commands/define.c for details of how the 'AS' clause is - * used. -@@ -6698,6 +6772,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - if (prosecdef[0] == 't') - appendPQExpBuffer(q, " SECURITY DEFINER"); - -+ if (proselinux) -+ appendPQExpBuffer(q, " CONTEXT = '%s'", proselinux); -+ - /* - * COST and ROWS are emitted only if present and not default, so as not to - * break backwards-compatibility of the dump without need. Keep this code -@@ -8779,6 +8856,9 @@ dumpTableSchema(Archive *fout, TableInfo - if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j]) - appendPQExpBuffer(q, " NOT NULL"); - -+ if (enable_selinux && tbinfo->attsecurity[j]) -+ appendPQExpBuffer(q, " CONTEXT = '%s'", tbinfo->attsecurity[j]); -+ - actual_atts++; - } - } -@@ -8826,6 +8906,9 @@ dumpTableSchema(Archive *fout, TableInfo - if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) - appendPQExpBuffer(q, "\nWITH (%s)", tbinfo->reloptions); - -+ if (enable_selinux && tbinfo->relsecurity) -+ appendPQExpBuffer(q, " CONTEXT = '%s'", tbinfo->relsecurity); -+ - appendPQExpBuffer(q, ";\n"); - - /* Loop dumping statistics and storage statements */ -@@ -10243,6 +10326,12 @@ fmtCopyColumnList(const TableInfo *ti) - - appendPQExpBuffer(q, "("); - needComma = false; -+ -+ if (enable_selinux) { -+ appendPQExpBuffer(q, SELINUX_SYSATTR_NAME); -+ needComma = true; -+ } -+ - for (i = 0; i < numatts; i++) - { - if (attisdropped[i]) -diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h ---- pgace/src/bin/pg_dump/pg_dump.h 2008-01-08 01:39:49.000000000 +0900 -+++ sepgsql/src/bin/pg_dump/pg_dump.h 2008-01-10 18:25:12.000000000 +0900 -@@ -238,6 +238,7 @@ typedef struct _tableInfo - char relkind; - char *reltablespace; /* relation tablespace */ - char *reloptions; /* options specified by WITH (...) */ -+ char *relsecurity; /* security attribute of the relation */ - bool hasindex; /* does it have any indexes? */ - bool hasrules; /* does it have any rules? */ - bool hasoids; /* does it have OIDs? */ -@@ -262,6 +263,7 @@ typedef struct _tableInfo - char *typstorage; /* type storage scheme */ - bool *attisdropped; /* true if attr is dropped; don't dump it */ - bool *attislocal; /* true if attr has local definition */ -+ char **attsecurity; /* security attribute of attribute (column) */ - - /* - * Note: we need to store per-attribute notnull, default, and constraint -diff -rpNU3 pgace/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpall.c ---- pgace/src/bin/pg_dump/pg_dumpall.c 2008-01-08 01:39:49.000000000 +0900 -+++ sepgsql/src/bin/pg_dump/pg_dumpall.c 2008-01-10 18:25:12.000000000 +0900 -@@ -67,6 +67,10 @@ static int disable_triggers = 0; - static int use_setsessauth = 0; - static int server_version; - -+/* flag to tuen on/off SE-PostgreSQL support */ -+#define SELINUX_SYSATTR_NAME "security_context" -+static int enable_selinux = 0; -+ - static FILE *OPF; - static char *filename = NULL; - -@@ -119,6 +123,7 @@ main(int argc, char *argv[]) - {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, - {"disable-triggers", no_argument, &disable_triggers, 1}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"enable-selinux", no_argument, NULL, 1001}, - - {NULL, 0, NULL, 0} - }; -@@ -290,6 +295,10 @@ main(int argc, char *argv[]) - appendPQExpBuffer(pgdumpopts, " --disable-triggers"); - else if (strcmp(optarg, "use-set-session-authorization") == 0) - /* no-op, still allowed for compatibility */ ; -+ else if (strcmp(optarg, "enable-selinux") == 0) { -+ appendPQExpBuffer(pgdumpopts, " --enable-selinux"); -+ enable_selinux = 1; -+ } - else - { - fprintf(stderr, -@@ -300,6 +309,11 @@ main(int argc, char *argv[]) - } - break; - -+ case 1001: -+ appendPQExpBuffer(pgdumpopts, " --enable-selinux"); -+ enable_selinux = 1; -+ break; -+ - case 0: - break; - -@@ -391,6 +405,24 @@ main(int argc, char *argv[]) - } - } - -+ if (enable_selinux) { -+ /* confirm whther server support SELinux features */ -+ const char *tmp = PQparameterStatus(conn, "security_sysattr_name"); -+ -+ if (!tmp) { -+ fprintf(stderr, "could not get security_sysattr_name from libpq\n"); -+ exit(1); -+ } -+ if (!!strcmp(SELINUX_SYSATTR_NAME, tmp) != 0) { -+ fprintf(stderr, "server does not have SELinux feature\n"); -+ exit(1); -+ } -+ if (server_version < 80204) { -+ fprintf(stderr, "server version is too old (%u)\n", server_version); -+ exit(1); -+ } -+ } -+ - /* - * Open the output file if required, otherwise use stdout - */ -@@ -505,6 +537,7 @@ help(void) - printf(_(" --use-set-session-authorization\n" - " use SESSION AUTHORIZATION commands instead of\n" - " OWNER TO commands\n")); -+ printf(_(" --enable-selinux enable to dump security attribute\n")); - - printf(_("\nConnection options:\n")); - printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -@@ -915,16 +948,18 @@ dumpCreateDB(PGconn *conn) - fprintf(OPF, "--\n-- Database creation\n--\n\n"); - - if (server_version >= 80100) -- res = executeQuery(conn, -+ appendPQExpBuffer(buf, - "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "datistemplate, datacl, datconnlimit, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " -+ "%s " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " -- "WHERE datallowconn ORDER BY 1"); -+ "WHERE datallowconn ORDER BY 1", -+ (!enable_selinux ? "" : "d." SELINUX_SYSATTR_NAME)); - else if (server_version >= 80000) -- res = executeQuery(conn, -+ appendPQExpBuffer(buf, - "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " -@@ -933,7 +968,7 @@ dumpCreateDB(PGconn *conn) - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70300) -- res = executeQuery(conn, -+ appendPQExpBuffer(buf, - "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " -@@ -942,7 +977,7 @@ dumpCreateDB(PGconn *conn) - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70100) -- res = executeQuery(conn, -+ appendPQExpBuffer(buf, - "SELECT datname, " - "coalesce(" - "(select usename from pg_shadow where usesysid=datdba), " -@@ -958,7 +993,7 @@ dumpCreateDB(PGconn *conn) - * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal - * with getting a NULL by not printing any OWNER clause. - */ -- res = executeQuery(conn, -+ appendPQExpBuffer(buf, - "SELECT datname, " - "(select usename from pg_shadow where usesysid=datdba), " - "pg_encoding_to_char(d.encoding), " -@@ -968,6 +1003,7 @@ dumpCreateDB(PGconn *conn) - "FROM pg_database d " - "ORDER BY 1"); - } -+ res = executeQuery(conn, buf->data); - - for (i = 0; i < PQntuples(res); i++) - { -@@ -978,6 +1014,7 @@ dumpCreateDB(PGconn *conn) - char *dbacl = PQgetvalue(res, i, 4); - char *dbconnlimit = PQgetvalue(res, i, 5); - char *dbtablespace = PQgetvalue(res, i, 6); -+ char *dbsecurity = PQgetvalue(res, i, 7); - char *fdbname; - - fdbname = strdup(fmtId(dbname)); -@@ -1021,6 +1058,9 @@ dumpCreateDB(PGconn *conn) - appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", - dbconnlimit); - -+ if (enable_selinux && dbsecurity) -+ appendPQExpBuffer(buf, " CONTEXT = '%s'", dbsecurity); -+ - appendPQExpBuffer(buf, ";\n"); - - if (strcmp(dbistemplate, "t") == 0) diff --git a/sepostgresql-pg_dump-8.3.7-2.patch b/sepostgresql-pg_dump-8.3.7-2.patch new file mode 100644 index 0000000..adea1a2 --- /dev/null +++ b/sepostgresql-pg_dump-8.3.7-2.patch @@ -0,0 +1,670 @@ +diff -rpNU3 base/src/bin/initdb/initdb.c sepgsql/src/bin/initdb/initdb.c +--- base/src/bin/initdb/initdb.c 2008-11-05 09:57:00.000000000 +0900 ++++ sepgsql/src/bin/initdb/initdb.c 2008-12-28 01:19:14.000000000 +0900 +@@ -94,6 +94,7 @@ static bool debug = false; + static bool noclean = false; + static bool show_setting = false; + static char *xlog_dir = ""; ++static char *pgace_feature = "none"; + + + /* internal vars */ +@@ -1212,6 +1213,11 @@ setup_config(void) + "#default_text_search_config = 'pg_catalog.simple'", + repltok); + ++ snprintf(repltok, sizeof(repltok), ++ "pgace_feature = '%s'", pgace_feature); ++ conflines = replace_token(conflines, ++ "#pgace_feature = 'none'", repltok); ++ + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); + + writefile(path, conflines); +@@ -2383,6 +2389,7 @@ usage(const char *progname) + printf(_(" -U, --username=NAME database superuser name\n")); + printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); + printf(_(" --pwfile=FILE read password for the new superuser from file\n")); ++ printf(_(" --pgace-feature=FEATURE specify an enhanced security feature\n")); + printf(_(" -?, --help show this help, then exit\n")); + printf(_(" -V, --version output version information, then exit\n")); + printf(_("\nLess commonly used options:\n")); +@@ -2417,6 +2424,7 @@ main(int argc, char *argv[]) + {"auth", required_argument, NULL, 'A'}, + {"pwprompt", no_argument, NULL, 'W'}, + {"pwfile", required_argument, NULL, 9}, ++ {"pgace-feature", required_argument, NULL, 10}, + {"username", required_argument, NULL, 'U'}, + {"help", no_argument, NULL, '?'}, + {"version", no_argument, NULL, 'V'}, +@@ -2531,6 +2539,9 @@ main(int argc, char *argv[]) + case 9: + pwfilename = xstrdup(optarg); + break; ++ case 10: ++ pgace_feature = xstrdup(optarg); ++ break; + case 's': + show_setting = true; + break; +diff -rpNU3 base/src/bin/pg_dump/pg_ace_dump.h sepgsql/src/bin/pg_dump/pg_ace_dump.h +--- base/src/bin/pg_dump/pg_ace_dump.h 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_ace_dump.h 2008-10-14 15:38:18.000000000 +0900 +@@ -0,0 +1,284 @@ ++#ifndef PG_ACE_DUMP_H ++#define PG_ACE_DUMP_H ++ ++#include "pg_backup.h" ++#include "pg_dump.h" ++ ++#define PG_ACE_FEATURE_NOTHING 0 ++#define PG_ACE_FEATURE_SELINUX 1 ++ ++#define SELINUX_SYSATTR_NAME "security_context" ++ ++/* ++ * pg_ace_dumpCheckServerFeature ++ * ++ * This hook checks whether the server has required feature, or not. ++ */ ++static inline void ++pg_ace_dumpCheckServerFeature(int feature, PGconn *conn) ++{ ++ const char *serv_feature; ++ ++ if (feature == PG_ACE_FEATURE_NOTHING) ++ return; ++ ++ serv_feature = PQparameterStatus(conn, "pgace_security_feature"); ++ if (!serv_feature) ++ { ++ fprintf(stderr, "could not get pgace_feature parameter.\n"); ++ exit(1); ++ } ++ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ if (strcmp(serv_feature, "selinux") != 0) ++ { ++ fprintf(stderr, "server does not have SELinux feature\n"); ++ exit(1); ++ } ++ } ++} ++ ++/* ++ * pg_ace_dumpDatabaseXXXX ++ * ++ * These hooks gives a chance to inject a security system column ++ * on dumping pg_database system catalog. ++ * A modified part must have ",d." style, and ++ * its result should be printed to buf. ++ */ ++static inline const char * ++pg_ace_dumpDatabaseQuery(int feature) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ return (",d." SELINUX_SYSATTR_NAME); ++ ++ return ""; ++} ++ ++static inline void ++pg_ace_dumpDatabasePrint(int feature, PQExpBuffer buf, ++ PGresult *res, int index) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ int i_security = PQfnumber(res, SELINUX_SYSATTR_NAME); ++ char *dbsecurity = PQgetvalue(res, index, i_security); ++ ++ if (dbsecurity && dbsecurity[0] != '\0') ++ appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", dbsecurity); ++ } ++} ++ ++/* ++ * pg_ace_dumpClassXXXX ++ * ++ * These hooks give a chance to inject a security system column ++ * on dumping pg_class system catalog. The modified part has to ++ * be formalized to ",c." style. The result ++ * should be preserved at TableInfo->relsecurity to print later, ++ * if exist. ++ */ ++static inline const char * ++pg_ace_dumpClassQuery(int feature) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ return (",c." SELINUX_SYSATTR_NAME); ++ ++ return ""; ++} ++ ++static inline char * ++pg_ace_dumpClassPreserve(int feature, PGresult *res, int index) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ int attno = PQfnumber(res, SELINUX_SYSATTR_NAME); ++ char *relcontext; ++ ++ if (attno < 0) ++ return NULL; ++ ++ relcontext = PQgetvalue(res, index, attno); ++ ++ if (!relcontext || relcontext[0] == '\0') ++ return NULL; ++ ++ return strdup(relcontext); ++ } ++ ++ return NULL; ++} ++ ++static inline void ++pg_ace_dumpClassPrint(int feature, PQExpBuffer buf, TableInfo *tbinfo) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ char *relcontext = tbinfo->relsecurity; ++ ++ if (relcontext) ++ appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", relcontext); ++ ++ return; ++ } ++} ++ ++/* ++ * pg_ace_dumpAttributeXXXX ++ * ++ * These hooks give a chance to inject a security system column ++ * on dumping pg_attribute system catalog. The modified part has ++ * to be formalized to ",a." style. The result ++ * should be preserved at TableInfo->attsecurity[index] to print ++ * later, if exist. ++ */ ++static inline const char * ++pg_ace_dumpAttributeQuery(int feature) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ return (",a." SELINUX_SYSATTR_NAME); ++ ++ return ""; ++} ++ ++static inline char * ++pg_ace_dumpAttributePreserve(int feature, PGresult *res, int index) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ int attno = PQfnumber(res, SELINUX_SYSATTR_NAME); ++ char *attcontext; ++ ++ if (attno < 0) ++ return NULL; ++ ++ attcontext = PQgetvalue(res, index, attno); ++ if (!attcontext || attcontext[0] == '\0') ++ return NULL; ++ ++ return strdup(attcontext); ++ } ++ ++ return NULL; ++} ++ ++static inline void ++pg_ace_dumpAttributePrint(int feature, PQExpBuffer buf, ++ TableInfo *tbinfo, int index) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ char *relcontext = tbinfo->relsecurity; ++ char *attcontext = tbinfo->attsecurity[index]; ++ ++ if (attcontext) ++ { ++ if (relcontext && strcmp(relcontext, attcontext) == 0) ++ return; ++ ++ appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", attcontext); ++ } ++ return; ++ } ++} ++ ++/* ++ * pg_ace_dumpProcXXXX ++ * ++ * These hooks give a chance to inject a security system column ++ * on dumping pg_proc system catalog. The modified part has to be ++ * formalized to "" style. The result should be ++ * printed later, if exist. ++ */ ++static inline const char * ++pg_ace_dumpProcQuery(int feature) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ return ("," SELINUX_SYSATTR_NAME); ++ ++ return ""; ++} ++ ++static inline void ++pg_ace_dumpProcPrint(int feature, PQExpBuffer buf, ++ PGresult *res, int index) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ int i_selinux = PQfnumber(res, SELINUX_SYSATTR_NAME); ++ char *prosecurity; ++ ++ if (i_selinux < 0) ++ return; ++ ++ prosecurity = PQgetvalue(res, index, i_selinux); ++ if (prosecurity && prosecurity[0] != '\0') ++ appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", prosecurity); ++ } ++} ++ ++/* ++ * pg_ace_dumpTableDataQuery ++ * ++ * This hook gives a chance to inject a security attribute system column ++ * on dumping of user's table. ++ * It must have "," style. ++ */ ++static inline const char * ++pg_ace_dumpTableDataQuery(int feature) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ return ("," SELINUX_SYSATTR_NAME); ++ ++ return ""; ++} ++ ++/* ++ * pg_ace_dumpCopyColumnList ++ * ++ * This hook gives a chance to inject a security attribute column within ++ * COPY statement. When a column is added, you have to return true. It ++ * enables to set needComma 'true', otherwise 'false'. ++ */ ++static inline bool ++pg_ace_dumpCopyColumnList(int feature, PQExpBuffer buf) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ appendPQExpBuffer(buf, SELINUX_SYSATTR_NAME); ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * pg_ace_dumpBlobComments ++ * ++ * This hook gives a chance to inject a query to restore a security ++ * attribute of binary large object. ++ */ ++static inline void ++pg_ace_dumpBlobComments(int feature, Archive *AH, PGconn *conn, Oid blobOid) ++{ ++ if (feature == PG_ACE_FEATURE_SELINUX) ++ { ++ PGresult *res; ++ char query[256]; ++ ++ snprintf(query, sizeof(query), ++ "SELECT lo_get_security(%u)", blobOid); ++ res = PQexec(conn, query); ++ if (!res) ++ return; ++ ++ if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) ++ archprintf(AH, "SELECT lo_set_security(%u, '%s');\n", ++ blobOid, PQgetvalue(res, 0, 0)); ++ ++ PQclear(res); ++ } ++} ++ ++#endif +diff -rpNU3 base/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c +--- base/src/bin/pg_dump/pg_dump.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dump.c 2009-02-02 11:58:34.000000000 +0900 +@@ -50,6 +50,7 @@ int optreset; + + #include "pg_backup_archiver.h" + #include "dumputils.h" ++#include "pg_ace_dump.h" + + extern char *optarg; + extern int optind, +@@ -118,6 +119,8 @@ static int g_numNamespaces; + /* flag to turn on/off dollar quoting */ + static int disable_dollar_quoting = 0; + ++/* flag to turn on/off security attribute support */ ++static int pg_ace_feature = PG_ACE_FEATURE_NOTHING; + + static void help(const char *progname); + static void expand_schema_name_patterns(SimpleStringList *patterns, +@@ -267,6 +270,7 @@ main(int argc, char **argv) + {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, + {"disable-triggers", no_argument, &disable_triggers, 1}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-context", no_argument, &pg_ace_feature, PG_ACE_FEATURE_SELINUX}, + + {NULL, 0, NULL, 0} + }; +@@ -419,6 +423,8 @@ main(int argc, char **argv) + disable_triggers = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-context") == 0) ++ pg_ace_feature = PG_ACE_FEATURE_SELINUX; + else + { + fprintf(stderr, +@@ -549,6 +555,8 @@ main(int argc, char **argv) + std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); + g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); + ++ pg_ace_dumpCheckServerFeature(pg_ace_feature, g_conn); ++ + /* Set the datestyle to ISO to ensure the dump's portability */ + do_sql_command(g_conn, "SET DATESTYLE = ISO"); + +@@ -771,6 +779,7 @@ help(const char *progname) + printf(_(" --use-set-session-authorization\n" + " use SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --security-context enable to dump security context of SE-PostgreSQL\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -1171,7 +1180,8 @@ dumpTableData_insert(Archive *fout, void + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +- "SELECT * FROM ONLY %s", ++ "SELECT * %s FROM ONLY %s", ++ pg_ace_dumpTableDataQuery(pg_ace_feature), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } +@@ -1785,11 +1795,14 @@ dumpBlobComments(Archive *AH, void *arg) + Oid blobOid; + char *comment; + ++ blobOid = atooid(PQgetvalue(res, i, 0)); ++ ++ pg_ace_dumpBlobComments(pg_ace_feature, AH, g_conn, blobOid); ++ + /* ignore blobs without comments */ + if (PQgetisnull(res, i, 1)) + continue; + +- blobOid = atooid(PQgetvalue(res, i, 0)); + comment = PQgetvalue(res, i, 1); + + printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", +@@ -2927,6 +2940,7 @@ getTables(int *numTables) + "d.refobjsubid as owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') as reloptions " ++ "%s " + "from pg_class c " + "left join pg_depend d on " + "(c.relkind = '%c' and " +@@ -2936,6 +2950,7 @@ getTables(int *numTables) + "where relkind in ('%c', '%c', '%c', '%c') " + "order by c.oid", + username_subquery, ++ pg_ace_dumpClassQuery(pg_ace_feature), + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); +@@ -3132,6 +3147,7 @@ getTables(int *numTables) + } + tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); + tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); ++ tblinfo[i].relsecurity = pg_ace_dumpClassPreserve(pg_ace_feature, res, i); + + /* other fields were zeroed above */ + +@@ -4363,11 +4379,13 @@ getTableAttrs(TableInfo *tblinfo, int nu + appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, " + "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, " + "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname " ++ "%s " /* security context, if required */ + "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t " + "on a.atttypid = t.oid " + "where a.attrelid = '%u'::pg_catalog.oid " + "and a.attnum > 0::pg_catalog.int2 " + "order by a.attrelid, a.attnum", ++ pg_ace_dumpAttributeQuery(pg_ace_feature), + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70100) +@@ -4426,6 +4444,7 @@ getTableAttrs(TableInfo *tblinfo, int nu + tbinfo->typstorage = (char *) malloc(ntups * sizeof(char)); + tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); ++ tbinfo->attsecurity = (char **) malloc(ntups * sizeof(char *)); + tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); + tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); +@@ -4457,6 +4476,8 @@ getTableAttrs(TableInfo *tblinfo, int nu + tbinfo->inhAttrs[j] = false; + tbinfo->inhAttrDef[j] = false; + tbinfo->inhNotNull[j] = false; ++ ++ tbinfo->attsecurity[j] = pg_ace_dumpAttributePreserve(pg_ace_feature, res, j); + } + + PQclear(res); +@@ -6460,8 +6481,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname " ++ "%s " /* security context, if required */ + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", ++ pg_ace_dumpProcQuery(pg_ace_feature), + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80100) +@@ -6699,6 +6722,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + if (prosecdef[0] == 't') + appendPQExpBuffer(q, " SECURITY DEFINER"); + ++ pg_ace_dumpProcPrint(pg_ace_feature, q, res, 0); ++ + /* + * COST and ROWS are emitted only if present and not default, so as not to + * break backwards-compatibility of the dump without need. Keep this code +@@ -8780,6 +8805,8 @@ dumpTableSchema(Archive *fout, TableInfo + if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j]) + appendPQExpBuffer(q, " NOT NULL"); + ++ pg_ace_dumpAttributePrint(pg_ace_feature, q, tbinfo, j); ++ + actual_atts++; + } + } +@@ -8827,6 +8854,8 @@ dumpTableSchema(Archive *fout, TableInfo + if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) + appendPQExpBuffer(q, "\nWITH (%s)", tbinfo->reloptions); + ++ pg_ace_dumpClassPrint(pg_ace_feature, q, tbinfo); ++ + appendPQExpBuffer(q, ";\n"); + + /* Loop dumping statistics and storage statements */ +@@ -10244,6 +10273,10 @@ fmtCopyColumnList(const TableInfo *ti) + + appendPQExpBuffer(q, "("); + needComma = false; ++ ++ if (pg_ace_dumpCopyColumnList(pg_ace_feature, q)) ++ needComma = true; ++ + for (i = 0; i < numatts; i++) + { + if (attisdropped[i]) +diff -rpNU3 base/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h +--- base/src/bin/pg_dump/pg_dump.h 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dump.h 2009-02-02 11:58:34.000000000 +0900 +@@ -238,6 +238,7 @@ typedef struct _tableInfo + char relkind; + char *reltablespace; /* relation tablespace */ + char *reloptions; /* options specified by WITH (...) */ ++ char *relsecurity; /* security attribute of the relation */ + bool hasindex; /* does it have any indexes? */ + bool hasrules; /* does it have any rules? */ + bool hasoids; /* does it have OIDs? */ +@@ -262,6 +263,7 @@ typedef struct _tableInfo + char *typstorage; /* type storage scheme */ + bool *attisdropped; /* true if attr is dropped; don't dump it */ + bool *attislocal; /* true if attr has local definition */ ++ char **attsecurity; /* security attribute of attribute (column) */ + + /* + * Note: we need to store per-attribute notnull, default, and constraint +diff -rpNU3 base/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpall.c +--- base/src/bin/pg_dump/pg_dumpall.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dumpall.c 2008-07-11 14:10:51.000000000 +0900 +@@ -27,6 +27,7 @@ int optreset; + #endif + + #include "dumputils.h" ++#include "pg_ace_dump.h" + + + /* version string we expect back from pg_dump */ +@@ -67,6 +68,9 @@ static int disable_triggers = 0; + static int use_setsessauth = 0; + static int server_version; + ++/* flag to turn on/off security attribute support */ ++static int pg_ace_feature = PG_ACE_FEATURE_NOTHING; ++ + static FILE *OPF; + static char *filename = NULL; + +@@ -119,6 +123,7 @@ main(int argc, char *argv[]) + {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, + {"disable-triggers", no_argument, &disable_triggers, 1}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-context", no_argument, &pg_ace_feature, PG_ACE_FEATURE_SELINUX}, + + {NULL, 0, NULL, 0} + }; +@@ -290,6 +295,8 @@ main(int argc, char *argv[]) + appendPQExpBuffer(pgdumpopts, " --disable-triggers"); + else if (strcmp(optarg, "use-set-session-authorization") == 0) + /* no-op, still allowed for compatibility */ ; ++ else if (strcmp(optarg, "security-context") == 0) ++ pg_ace_feature = PG_ACE_FEATURE_SELINUX; + else + { + fprintf(stderr, +@@ -316,6 +323,8 @@ main(int argc, char *argv[]) + appendPQExpBuffer(pgdumpopts, " --disable-triggers"); + if (use_setsessauth) + appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); ++ if (pg_ace_feature == PG_ACE_FEATURE_SELINUX) ++ appendPQExpBuffer(pgdumpopts, " --security-context"); + + if (optind < argc) + { +@@ -391,6 +400,8 @@ main(int argc, char *argv[]) + } + } + ++ pg_ace_dumpCheckServerFeature(pg_ace_feature, conn); ++ + /* + * Open the output file if required, otherwise use stdout + */ +@@ -505,6 +516,7 @@ help(void) + printf(_(" --use-set-session-authorization\n" + " use SESSION AUTHORIZATION commands instead of\n" + " OWNER TO commands\n")); ++ printf(_(" --security-context enables to dump security context of SE-PostgreSQL\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -915,16 +927,18 @@ dumpCreateDB(PGconn *conn) + fprintf(OPF, "--\n-- Database creation\n--\n\n"); + + if (server_version >= 80100) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ "%s " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " +- "WHERE datallowconn ORDER BY 1"); ++ "WHERE datallowconn ORDER BY 1", ++ pg_ace_dumpDatabaseQuery(pg_ace_feature)); + else if (server_version >= 80000) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " +@@ -933,7 +947,7 @@ dumpCreateDB(PGconn *conn) + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70300) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " +@@ -942,7 +956,7 @@ dumpCreateDB(PGconn *conn) + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " +@@ -958,7 +972,7 @@ dumpCreateDB(PGconn *conn) + * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal + * with getting a NULL by not printing any OWNER clause. + */ +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " +@@ -968,6 +982,7 @@ dumpCreateDB(PGconn *conn) + "FROM pg_database d " + "ORDER BY 1"); + } ++ res = executeQuery(conn, buf->data); + + for (i = 0; i < PQntuples(res); i++) + { +@@ -1021,6 +1036,8 @@ dumpCreateDB(PGconn *conn) + appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", + dbconnlimit); + ++ pg_ace_dumpDatabasePrint(pg_ace_feature, buf, res, i); ++ + appendPQExpBuffer(buf, ";\n"); + + if (strcmp(dbistemplate, "t") == 0) diff --git a/sepostgresql-pgace-8.3.1-2.patch b/sepostgresql-pgace-8.3.1-2.patch deleted file mode 100644 index 9c1782d..0000000 --- a/sepostgresql-pgace-8.3.1-2.patch +++ /dev/null @@ -1,4084 +0,0 @@ -diff -rpNU3 base/src/backend/Makefile pgace/src/backend/Makefile ---- base/src/backend/Makefile 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/Makefile 2008-01-08 01:39:49.000000000 +0900 -@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.glo - - DIRS = access bootstrap catalog parser commands executor lib libpq \ - main nodes optimizer port postmaster regex rewrite \ -- storage tcop tsearch utils $(top_builddir)/src/timezone -+ security storage tcop tsearch utils $(top_builddir)/src/timezone - - SUBSYSOBJS = $(DIRS:%=%/SUBSYS.o) - -diff -rpNU3 base/src/backend/access/common/heaptuple.c pgace/src/backend/access/common/heaptuple.c ---- base/src/backend/access/common/heaptuple.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/access/common/heaptuple.c 2008-01-10 12:42:25.000000000 +0900 -@@ -67,6 +67,7 @@ - #include "access/heapam.h" - #include "access/tuptoaster.h" - #include "executor/tuptable.h" -+#include "security/pgace.h" - - - /* Does att's datatype allow packing into the 1-byte-header varlena format? */ -@@ -473,6 +474,9 @@ heap_attisnull(HeapTuple tup, int attnum - case MinCommandIdAttributeNumber: - case MaxTransactionIdAttributeNumber: - case MaxCommandIdAttributeNumber: -+#ifdef SECURITY_SYSATTR_NAME -+ case SecurityAttributeNumber: -+#endif - /* these are never null */ - break; - -@@ -785,6 +789,11 @@ heap_getsysattr(HeapTuple tup, int attnu - case TableOidAttributeNumber: - result = ObjectIdGetDatum(tup->t_tableOid); - break; -+#ifdef SECURITY_SYSATTR_NAME -+ case SecurityAttributeNumber: -+ result = ObjectIdGetDatum(HeapTupleGetSecurity(tup)); -+ break; -+#endif - default: - elog(ERROR, "invalid attnum: %d", attnum); - result = 0; /* keep compiler quiet */ -@@ -816,6 +825,7 @@ heap_copytuple(HeapTuple tuple) - newTuple->t_tableOid = tuple->t_tableOid; - newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE); - memcpy((char *) newTuple->t_data, (char *) tuple->t_data, tuple->t_len); -+ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); - return newTuple; - } - -@@ -909,6 +919,10 @@ heap_form_tuple(TupleDesc tupleDescripto - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); - -+#ifdef SECURITY_SYSATTR_NAME -+ len += sizeof(Oid); -+#endif -+ - hoff = len = MAXALIGN(len); /* align user data safely */ - - data_len = heap_compute_data_size(tupleDescriptor, values, isnull); -@@ -940,6 +954,10 @@ heap_form_tuple(TupleDesc tupleDescripto - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - td->t_infomask = HEAP_HASOID; - -+#ifdef SECURITY_SYSATTR_NAME -+ td->t_infomask |= HEAP_HASSECURITY; -+#endif -+ - heap_fill_tuple(tupleDescriptor, - values, - isnull, -@@ -1020,6 +1038,10 @@ heap_formtuple(TupleDesc tupleDescriptor - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); - -+#ifdef SECURITY_SYSATTR_NAME -+ len += sizeof(Oid); -+#endif -+ - hoff = len = MAXALIGN(len); /* align user data safely */ - - data_len = ComputeDataSize(tupleDescriptor, values, nulls); -@@ -1051,6 +1073,10 @@ heap_formtuple(TupleDesc tupleDescriptor - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - td->t_infomask = HEAP_HASOID; - -+#ifdef SECURITY_SYSATTR_NAME -+ td->t_infomask |= HEAP_HASSECURITY; -+#endif -+ - DataFill(tupleDescriptor, - values, - nulls, -@@ -1129,6 +1155,7 @@ heap_modify_tuple(HeapTuple tuple, - newTuple->t_tableOid = tuple->t_tableOid; - if (tupleDesc->tdhasoid) - HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); -+ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); - - return newTuple; - } -@@ -1201,6 +1228,7 @@ heap_modifytuple(HeapTuple tuple, - newTuple->t_tableOid = tuple->t_tableOid; - if (tupleDesc->tdhasoid) - HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); -+ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); - - return newTuple; - } -@@ -1847,6 +1875,10 @@ heap_form_minimal_tuple(TupleDesc tupleD - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); - -+#ifdef SECURITY_SYSATTR_NAME -+ len += sizeof(Oid); -+#endif -+ - hoff = len = MAXALIGN(len); /* align user data safely */ - - data_len = heap_compute_data_size(tupleDescriptor, values, isnull); -@@ -1868,6 +1900,10 @@ heap_form_minimal_tuple(TupleDesc tupleD - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - tuple->t_infomask = HEAP_HASOID; - -+#ifdef SECURITY_SYSATTR_NAME -+ tuple->t_infomask |= HEAP_HASSECURITY; -+#endif -+ - heap_fill_tuple(tupleDescriptor, - values, - isnull, -@@ -1979,6 +2015,11 @@ heap_addheader(int natts, /* max domain - hoff = offsetof(HeapTupleHeaderData, t_bits); - if (withoid) - hoff += sizeof(Oid); -+ -+#ifdef SECURITY_SYSATTR_NAME -+ hoff += sizeof(Oid); -+#endif -+ - hoff = MAXALIGN(hoff); - len = hoff + structlen; - -@@ -1997,6 +2038,10 @@ heap_addheader(int natts, /* max domain - if (withoid) /* else leave infomask = 0 */ - td->t_infomask = HEAP_HASOID; - -+#ifdef SECURITY_SYSATTR_NAME -+ td->t_infomask |= HEAP_HASSECURITY; -+#endif -+ - memcpy((char *) td + hoff, structure, structlen); - - return tuple; -diff -rpNU3 base/src/backend/access/heap/heapam.c pgace/src/backend/access/heap/heapam.c ---- base/src/backend/access/heap/heapam.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/access/heap/heapam.c 2008-03-19 10:08:35.000000000 +0900 -@@ -50,6 +50,7 @@ - #include "catalog/namespace.h" - #include "miscadmin.h" - #include "pgstat.h" -+#include "security/pgace.h" - #include "storage/procarray.h" - #include "storage/smgr.h" - #include "utils/datum.h" -@@ -1946,6 +1947,9 @@ heap_insert(Relation relation, HeapTuple - Oid - simple_heap_insert(Relation relation, HeapTuple tup) - { -+ if (!pgaceHeapTupleInsert(relation, tup, true, false)) -+ elog(ERROR, "simple_heap_insert on %s failed due to security reason", -+ RelationGetRelationName(relation)); - return heap_insert(relation, tup, GetCurrentCommandId(true), true, true); - } - -@@ -2227,6 +2231,9 @@ simple_heap_delete(Relation relation, It - ItemPointerData update_ctid; - TransactionId update_xmax; - -+ if (!pgaceHeapTupleDelete(relation, tid, true, false)) -+ elog(ERROR, "simple_heap_delete on %s failed due to security reason", -+ RelationGetRelationName(relation)); - result = heap_delete(relation, tid, - &update_ctid, &update_xmax, - GetCurrentCommandId(true), InvalidSnapshot, -@@ -2870,6 +2877,9 @@ simple_heap_update(Relation relation, It - ItemPointerData update_ctid; - TransactionId update_xmax; - -+ if (!pgaceHeapTupleUpdate(relation, otid, tup, true, false)) -+ elog(ERROR, "simple_heap_update on %s failed due to security reason", -+ RelationGetRelationName(relation)); - result = heap_update(relation, otid, tup, - &update_ctid, &update_xmax, - GetCurrentCommandId(true), InvalidSnapshot, -diff -rpNU3 base/src/backend/access/heap/tuptoaster.c pgace/src/backend/access/heap/tuptoaster.c ---- base/src/backend/access/heap/tuptoaster.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/access/heap/tuptoaster.c 2008-03-19 10:08:35.000000000 +0900 -@@ -35,6 +35,7 @@ - #include "access/tuptoaster.h" - #include "access/xact.h" - #include "catalog/catalog.h" -+#include "security/pgace.h" - #include "utils/fmgroids.h" - #include "utils/pg_lzcompress.h" - #include "utils/typcache.h" -@@ -589,6 +590,9 @@ toast_insert_or_update(Relation rel, Hea - hoff += BITMAPLEN(numAttrs); - if (newtup->t_data->t_infomask & HEAP_HASOID) - hoff += sizeof(Oid); -+#ifdef SECURITY_SYSATTR_NAME -+ hoff += sizeof(Oid); -+#endif - hoff = MAXALIGN(hoff); - Assert(hoff == newtup->t_data->t_hoff); - /* now convert to a limit on the tuple data size */ -@@ -838,6 +842,9 @@ toast_insert_or_update(Relation rel, Hea - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+#ifdef SECURITY_SYSATTR_NAME -+ new_len += sizeof(Oid); -+#endif - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -@@ -989,6 +996,9 @@ toast_flatten_tuple_attribute(Datum valu - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+#ifdef SECURITY_SYSATTR_NAME -+ new_len += sizeof(Oid); -+#endif - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -@@ -1175,6 +1185,8 @@ toast_save_datum(Relation rel, Datum val - if (!HeapTupleIsValid(toasttup)) - elog(ERROR, "failed to build TOAST tuple"); - -+ if (!pgaceHeapTupleInsert(toastrel, toasttup, true, false)) -+ elog(ERROR, "failed to insert TOAST tuple due to security reason"); - heap_insert(toastrel, toasttup, mycid, use_wal, use_fsm); - - /* -diff -rpNU3 base/src/backend/bootstrap/bootparse.y pgace/src/backend/bootstrap/bootparse.y ---- base/src/backend/bootstrap/bootparse.y 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/bootstrap/bootparse.y 2008-01-08 01:39:49.000000000 +0900 -@@ -212,7 +212,8 @@ Boot_CreateStmt: - 0, - ONCOMMIT_NOOP, - (Datum) 0, -- true); -+ true, -+ NIL); - elog(DEBUG4, "relation created with oid %u", id); - } - do_end(); -diff -rpNU3 base/src/backend/catalog/Makefile pgace/src/backend/catalog/Makefile ---- base/src/backend/catalog/Makefile 2007-09-11 10:53:53.000000000 +0900 -+++ pgace/src/backend/catalog/Makefile 2007-09-21 01:08:03.000000000 +0900 -@@ -35,6 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_sr - pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_tablespace.h pg_pltemplate.h \ - pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ -+ pg_security.h \ - pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ - pg_ts_parser.h pg_ts_template.h \ - toasting.h indexing.h \ -diff -rpNU3 base/src/backend/catalog/catalog.c pgace/src/backend/catalog/catalog.c ---- base/src/backend/catalog/catalog.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/catalog/catalog.c 2008-03-19 10:08:35.000000000 +0900 -@@ -30,6 +30,7 @@ - #include "catalog/pg_database.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_pltemplate.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_shdepend.h" - #include "catalog/pg_shdescription.h" - #include "catalog/pg_tablespace.h" -@@ -257,6 +258,7 @@ IsSharedRelation(Oid relationId) - relationId == AuthMemRelationId || - relationId == DatabaseRelationId || - relationId == PLTemplateRelationId || -+ relationId == SecurityRelationId || - relationId == SharedDescriptionRelationId || - relationId == SharedDependRelationId || - relationId == TableSpaceRelationId) -@@ -269,6 +271,8 @@ IsSharedRelation(Oid relationId) - relationId == DatabaseNameIndexId || - relationId == DatabaseOidIndexId || - relationId == PLTemplateNameIndexId || -+ relationId == SecurityOidIndexId || -+ relationId == SecuritySeclabelIndexId || - relationId == SharedDescriptionObjIndexId || - relationId == SharedDependDependerIndexId || - relationId == SharedDependReferenceIndexId || -diff -rpNU3 base/src/backend/catalog/genbki.sh pgace/src/backend/catalog/genbki.sh ---- base/src/backend/catalog/genbki.sh 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/catalog/genbki.sh 2008-01-08 01:39:49.000000000 +0900 -@@ -130,6 +130,22 @@ for dir in $INCLUDE_DIRS; do - fi - done - -+# Get SECURITY_SYSATTR_NAME from security/pgace.h -+for dir in $INCLUDE_DIRS; do -+ if [ -f "$dir/pg_config.h" ]; then -+ SECURITY_SYSATTR_NAME=`grep '#define[ ]*SECURITY_SYSATTR_NAME' $dir/pg_config.h | $AWK '{ print $3 }' | sed 's/\"//g'` -+ break -+ fi -+done -+ -+function SECURITY_SYSATTR_NAME_filter() { -+ if [ -z "$SECURITY_SYSATTR_NAME" ]; then -+ grep -v SECURITY_SYSATTR_NAME; -+ else -+ cat -+ fi -+} -+ - touch ${OUTPUT_PREFIX}.description.$$ - touch ${OUTPUT_PREFIX}.shdescription.$$ - -@@ -143,7 +159,7 @@ touch ${OUTPUT_PREFIX}.shdescription.$$ - # Substitute values of configuration constants - # ---------------- - # --cat $INFILES | \ -+cat $INFILES | SECURITY_SYSATTR_NAME_filter | \ - sed -e 's;/\*.*\*/;;g' \ - -e 's;/\*;\ - /*\ -@@ -165,6 +181,7 @@ sed -e "s/;[ ]*$//g" \ - -e "s/PGUID/$BOOTSTRAP_SUPERUSERID/g" \ - -e "s/NAMEDATALEN/$NAMEDATALEN/g" \ - -e "s/PGNSP/$PG_CATALOG_NAMESPACE/g" \ -+ -e "s/SECURITY_SYSATTR_NAME/$SECURITY_SYSATTR_NAME/g" \ - | $AWK ' - # ---------------- - # now use awk to process remaining .h file.. -diff -rpNU3 base/src/backend/catalog/heap.c pgace/src/backend/catalog/heap.c ---- base/src/backend/catalog/heap.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/catalog/heap.c 2008-01-08 01:39:49.000000000 +0900 -@@ -53,6 +53,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_expr.h" - #include "parser/parse_relation.h" -+#include "security/pgace.h" - #include "storage/smgr.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -67,7 +68,8 @@ static void AddNewRelationTuple(Relation - Oid new_rel_oid, Oid new_type_oid, - Oid relowner, - char relkind, -- Datum reloptions); -+ Datum reloptions, -+ List *pgace_attr_list); - static Oid AddNewRelationType(const char *typeName, - Oid typeNamespace, - Oid new_rel_oid, -@@ -144,7 +146,21 @@ static FormData_pg_attribute a7 = { - true, 'p', 'i', true, false, false, true, 0 - }; - -+#ifdef SECURITY_SYSATTR_NAME -+/* -+ * SECURITY_SYSATTR_NAME is defined at PGACE header file. -+ * If SELinux is enabled, it is defined as "security_context" -+ */ -+static FormData_pg_attribute a8 = { -+ 0, {SECURITY_SYSATTR_NAME}, SECLABELOID, 0, sizeof(Oid), -+ SecurityAttributeNumber, 0, -1, -1, -+ true, 'p', 'i', true, false, false, true, 0 -+}; -+ -+static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; -+#else - static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; -+#endif - - /* - * This function returns a Form_pg_attribute pointer for a system attribute. -@@ -467,7 +483,8 @@ AddNewAttributeTuples(Oid new_rel_oid, - TupleDesc tupdesc, - char relkind, - bool oidislocal, -- int oidinhcount) -+ int oidinhcount, -+ List *pgace_attr_list) - { - const Form_pg_attribute *dpp; - int i; -@@ -502,6 +519,7 @@ AddNewAttributeTuples(Oid new_rel_oid, - false, - ATTRIBUTE_TUPLE_SIZE, - (void *) *dpp); -+ pgaceCreateAttributeCommon(rel, tup, pgace_attr_list); - - simple_heap_insert(rel, tup); - -@@ -592,7 +610,8 @@ void - InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -- Datum reloptions) -+ Datum reloptions, -+ List *pgace_attr_list) - { - Form_pg_class rd_rel = new_rel_desc->rd_rel; - Datum values[Natts_pg_class]; -@@ -642,6 +661,7 @@ InsertPgClassTuple(Relation pg_class_des - * be embarrassing to do this sort of thing in polite company. - */ - HeapTupleSetOid(tup, new_rel_oid); -+ pgaceCreateRelationCommon(pg_class_desc, tup, pgace_attr_list); - - /* finally insert the new tuple, update the indexes, and clean up */ - simple_heap_insert(pg_class_desc, tup); -@@ -665,7 +685,8 @@ AddNewRelationTuple(Relation pg_class_de - Oid new_type_oid, - Oid relowner, - char relkind, -- Datum reloptions) -+ Datum reloptions, -+ List *pgace_attr_list) - { - Form_pg_class new_rel_reltup; - -@@ -725,7 +746,7 @@ AddNewRelationTuple(Relation pg_class_de - new_rel_desc->rd_att->tdtypeid = new_type_oid; - - /* Now build and insert the tuple */ -- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); -+ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions, pgace_attr_list); - } - - -@@ -791,7 +812,8 @@ heap_create_with_catalog(const char *rel - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -- bool allow_system_table_mods) -+ bool allow_system_table_mods, -+ List *pgace_attr_list) - { - Relation pg_class_desc; - Relation new_rel_desc; -@@ -963,13 +985,14 @@ heap_create_with_catalog(const char *rel - new_type_oid, - ownerid, - relkind, -- reloptions); -+ reloptions, -+ pgace_attr_list); - - /* - * now add tuples to pg_attribute for the attributes in our new relation. - */ - AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, -- oidislocal, oidinhcount); -+ oidislocal, oidinhcount, pgace_attr_list); - - /* - * Make a dependency link to force the relation to be deleted if its -diff -rpNU3 base/src/backend/catalog/index.c pgace/src/backend/catalog/index.c ---- base/src/backend/catalog/index.c 2008-02-03 01:11:28.000000000 +0900 -+++ pgace/src/backend/catalog/index.c 2008-02-03 01:18:48.000000000 +0900 -@@ -624,7 +624,7 @@ index_create(Oid heapRelationId, - */ - InsertPgClassTuple(pg_class, indexRelation, - RelationGetRelid(indexRelation), -- reloptions); -+ reloptions, NIL); - - /* done with pg_class */ - heap_close(pg_class, RowExclusiveLock); -diff -rpNU3 base/src/backend/catalog/pg_aggregate.c pgace/src/backend/catalog/pg_aggregate.c ---- base/src/backend/catalog/pg_aggregate.c 2008-01-14 22:59:48.000000000 +0900 -+++ pgace/src/backend/catalog/pg_aggregate.c 2008-01-14 23:08:31.000000000 +0900 -@@ -213,8 +213,9 @@ AggregateCreate(const char *aggName, - PointerGetDatum(NULL), /* parameterModes */ - PointerGetDatum(NULL), /* parameterNames */ - PointerGetDatum(NULL), /* proconfig */ -- 1, /* procost */ -- 0); /* prorows */ -+ 1, /* procost */ -+ 0, /* prorows */ -+ NULL); /* PGACE opaque */ - - /* - * Okay to create the pg_aggregate entry. -diff -rpNU3 base/src/backend/catalog/pg_largeobject.c pgace/src/backend/catalog/pg_largeobject.c ---- base/src/backend/catalog/pg_largeobject.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/catalog/pg_largeobject.c 2008-01-08 01:39:49.000000000 +0900 -@@ -18,6 +18,8 @@ - #include "access/heapam.h" - #include "catalog/indexing.h" - #include "catalog/pg_largeobject.h" -+#include "catalog/pg_security.h" -+#include "security/pgace.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" - -@@ -57,6 +59,8 @@ LargeObjectCreate(Oid loid) - - ntup = heap_formtuple(pg_largeobject->rd_att, values, nulls); - -+ pgaceLargeObjectCreate(pg_largeobject, ntup); -+ - /* - * Insert it - */ -@@ -91,6 +95,8 @@ LargeObjectDrop(Oid loid) - - while ((tuple = systable_getnext(sd)) != NULL) - { -+ if (!found) -+ pgaceLargeObjectDrop(pg_largeobject, tuple); - simple_heap_delete(pg_largeobject, &tuple->t_self); - found = true; - } -diff -rpNU3 base/src/backend/catalog/pg_proc.c pgace/src/backend/catalog/pg_proc.c ---- base/src/backend/catalog/pg_proc.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/catalog/pg_proc.c 2008-01-08 01:39:49.000000000 +0900 -@@ -27,6 +27,7 @@ - #include "mb/pg_wchar.h" - #include "miscadmin.h" - #include "parser/parse_type.h" -+#include "security/pgace.h" - #include "tcop/pquery.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -74,7 +75,8 @@ ProcedureCreate(const char *procedureNam - Datum parameterNames, - Datum proconfig, - float4 procost, -- float4 prorows) -+ float4 prorows, -+ void *pgaceItem) - { - Oid retval; - int parameterCount; -@@ -339,6 +341,7 @@ ProcedureCreate(const char *procedureNam - - /* Okay, do it... */ - tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces); -+ pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem); - simple_heap_update(rel, &tup->t_self, tup); - - ReleaseSysCache(oldtup); -@@ -348,6 +351,7 @@ ProcedureCreate(const char *procedureNam - { - /* Creating a new procedure */ - tup = heap_formtuple(tupDesc, values, nulls); -+ pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem); - simple_heap_insert(rel, tup); - is_update = false; - } -diff -rpNU3 base/src/backend/catalog/toasting.c pgace/src/backend/catalog/toasting.c ---- base/src/backend/catalog/toasting.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/catalog/toasting.c 2008-01-08 01:39:49.000000000 +0900 -@@ -199,7 +199,8 @@ create_toast_table(Relation rel, Oid toa - 0, - ONCOMMIT_NOOP, - (Datum) 0, -- true); -+ true, -+ NIL); - - /* make the toast relation visible, else index creation will fail */ - CommandCounterIncrement(); -diff -rpNU3 base/src/backend/commands/cluster.c pgace/src/backend/commands/cluster.c ---- base/src/backend/commands/cluster.c 2008-02-03 01:11:28.000000000 +0900 -+++ pgace/src/backend/commands/cluster.c 2008-02-03 01:18:48.000000000 +0900 -@@ -666,7 +666,8 @@ make_new_heap(Oid OIDOldHeap, const char - 0, - ONCOMMIT_NOOP, - reloptions, -- allowSystemTableMods); -+ allowSystemTableMods, -+ NIL); - - ReleaseSysCache(tuple); - -diff -rpNU3 base/src/backend/commands/copy.c pgace/src/backend/commands/copy.c ---- base/src/backend/commands/copy.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/commands/copy.c 2008-02-01 11:24:01.000000000 +0900 -@@ -22,6 +22,7 @@ - - #include "access/heapam.h" - #include "access/xact.h" -+#include "catalog/heap.h" - #include "catalog/namespace.h" - #include "catalog/pg_type.h" - #include "commands/copy.h" -@@ -34,6 +35,7 @@ - #include "optimizer/planner.h" - #include "parser/parse_relation.h" - #include "rewrite/rewriteHandler.h" -+#include "security/pgace.h" - #include "storage/fd.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -159,6 +161,11 @@ typedef struct CopyStateData - char *raw_buf; - int raw_buf_index; /* next byte to process */ - int raw_buf_len; /* total # of bytes stored */ -+ -+ /* dumpable/restorable system column support */ -+ FmgrInfo security_out_function; -+ bool security_force_quot; -+ bool security_force_notnull; - } CopyStateData; - - typedef CopyStateData *CopyState; -@@ -242,7 +249,7 @@ static const char BinarySignature[11] = - /* non-export function prototypes */ - static void DoCopyTo(CopyState cstate); - static void CopyTo(CopyState cstate); --static void CopyOneRowTo(CopyState cstate, Oid tupleOid, -+static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid securityOid, - Datum *values, bool *nulls); - static void CopyFrom(CopyState cstate); - static bool CopyReadLine(CopyState cstate); -@@ -1073,6 +1080,8 @@ DoCopy(const CopyStmt *stmt, const char - /* Generate or convert list of attributes to process */ - cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); - -+ pgaceCopyTable(cstate->rel, cstate->attnumlist, is_from); -+ - num_phys_attrs = tupDesc->natts; - - /* Convert FORCE QUOTE name list to per-column flags, check validity */ -@@ -1093,6 +1102,10 @@ DoCopy(const CopyStmt *stmt, const char - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", - NameStr(tupDesc->attrs[attnum - 1]->attname)))); -+ if (pgaceIsSecuritySystemColumn(attnum)) { -+ cstate->security_force_quot = true; -+ continue; -+ } - cstate->force_quote_flags[attnum - 1] = true; - } - } -@@ -1115,6 +1128,9 @@ DoCopy(const CopyStmt *stmt, const char - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", - NameStr(tupDesc->attrs[attnum - 1]->attname)))); -+ if (pgaceIsSecuritySystemColumn(attnum)) -+ continue; /* ignore, if specified */ -+ - cstate->force_notnull_flags[attnum - 1] = true; - } - } -@@ -1305,16 +1321,27 @@ CopyTo(CopyState cstate) - int attnum = lfirst_int(cur); - Oid out_func_oid; - bool isvarlena; -+ FmgrInfo *out_fmgr; -+ Form_pg_attribute pg_attribute; -+ -+ if (pgaceIsSecuritySystemColumn(attnum)) { -+ /* PGACE: dumpable system column */ -+ pg_attribute = SystemAttributeDefinition(attnum, false); -+ out_fmgr = &cstate->security_out_function; -+ } else { -+ pg_attribute = attr[attnum - 1]; -+ out_fmgr = &cstate->out_functions[attnum - 1]; -+ } - - if (cstate->binary) -- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, -+ getTypeBinaryOutputInfo(pg_attribute->atttypid, - &out_func_oid, - &isvarlena); - else -- getTypeOutputInfo(attr[attnum - 1]->atttypid, -+ getTypeOutputInfo(pg_attribute->atttypid, - &out_func_oid, - &isvarlena); -- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); -+ fmgr_info(out_func_oid, out_fmgr); - } - - /* -@@ -1370,6 +1397,12 @@ CopyTo(CopyState cstate) - hdr_delim = true; - - colname = NameStr(attr[attnum - 1]->attname); -+ if (pgaceIsSecuritySystemColumn(attnum)) { -+ Form_pg_attribute sysatt = SystemAttributeDefinition(attnum, false); -+ colname = NameStr(sysatt->attname); -+ } else { -+ colname = NameStr(attr[attnum - 1]->attname); -+ } - - CopyAttributeOutCSV(cstate, colname, false, - list_length(cstate->attnumlist) == 1); -@@ -1395,11 +1428,14 @@ CopyTo(CopyState cstate) - { - CHECK_FOR_INTERRUPTS(); - -+ if (!pgaceCopyToTuple(cstate->rel, cstate->attnumlist, tuple)) -+ continue; -+ - /* Deconstruct the tuple ... faster than repeated heap_getattr */ - heap_deform_tuple(tuple, tupDesc, values, nulls); - - /* Format and send the data */ -- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); -+ CopyOneRowTo(cstate, HeapTupleGetOid(tuple), HeapTupleGetSecurity(tuple), values, nulls); - } - - heap_endscan(scandesc); -@@ -1425,7 +1461,7 @@ CopyTo(CopyState cstate) - * Emit one row during CopyTo(). - */ - static void --CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) -+CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid tupleSecurity, Datum *values, bool *nulls) - { - bool need_delim = false; - FmgrInfo *out_functions = cstate->out_functions; -@@ -1464,8 +1500,10 @@ CopyOneRowTo(CopyState cstate, Oid tuple - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); -- Datum value = values[attnum - 1]; -- bool isnull = nulls[attnum - 1]; -+ Datum value; -+ bool isnull; -+ bool force_quot; -+ FmgrInfo *out_fmgr; - - if (!cstate->binary) - { -@@ -1474,6 +1512,19 @@ CopyOneRowTo(CopyState cstate, Oid tuple - need_delim = true; - } - -+ /* PGACE: dumpable system column support */ -+ if (pgaceIsSecuritySystemColumn(attnum)) { -+ value = tupleSecurity; -+ isnull = false; -+ force_quot = cstate->security_force_quot; -+ out_fmgr = &cstate->security_out_function; -+ } else { -+ value = values[attnum - 1]; -+ isnull = nulls[attnum - 1]; -+ force_quot = cstate->force_quote_flags[attnum - 1]; -+ out_fmgr = &out_functions[attnum - 1]; -+ } -+ - if (isnull) - { - if (!cstate->binary) -@@ -1485,11 +1536,9 @@ CopyOneRowTo(CopyState cstate, Oid tuple - { - if (!cstate->binary) - { -- string = OutputFunctionCall(&out_functions[attnum - 1], -- value); -+ string = OutputFunctionCall(out_fmgr, value); - if (cstate->csv_mode) -- CopyAttributeOutCSV(cstate, string, -- cstate->force_quote_flags[attnum - 1], -+ CopyAttributeOutCSV(cstate, string, force_quot, - list_length(cstate->attnumlist) == 1); - else - CopyAttributeOutText(cstate, string); -@@ -1498,8 +1547,7 @@ CopyOneRowTo(CopyState cstate, Oid tuple - { - bytea *outputbytes; - -- outputbytes = SendFunctionCall(&out_functions[attnum - 1], -- value); -+ outputbytes = SendFunctionCall(out_fmgr, value); - CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); - CopySendData(cstate, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); -@@ -1633,8 +1681,11 @@ CopyFrom(CopyState cstate) - num_defaults; - FmgrInfo *in_functions; - FmgrInfo oid_in_function; -+ FmgrInfo security_in_function; -+ bool security_in_function_prepared = false; - Oid *typioparams; - Oid oid_typioparam; -+ Oid security_typioparam; - int attnum; - int i; - Oid in_func_oid; -@@ -1904,6 +1955,7 @@ CopyFrom(CopyState cstate) - { - bool skip_tuple; - Oid loaded_oid = InvalidOid; -+ Oid loaded_security = InvalidOid; - - CHECK_FOR_INTERRUPTS(); - -@@ -1978,6 +2030,35 @@ CopyFrom(CopyState cstate) - int attnum = lfirst_int(cur); - int m = attnum - 1; - -+ if (pgaceIsSecuritySystemColumn(attnum)) { -+ Form_pg_attribute sysatt = SystemAttributeDefinition(attnum, false); -+ if (fieldno >= fldct) -+ ereport(ERROR, -+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), -+ errmsg("missing data for column \"%s\"", -+ NameStr(sysatt->attname)))); -+ string = field_strings[fieldno++]; -+ cstate->cur_attname = NameStr(attr[m]->attname); -+ cstate->cur_attval = string; -+ -+ if (!security_in_function_prepared) { -+ getTypeInputInfo(sysatt->atttypid, &in_func_oid, -+ &security_typioparam); -+ fmgr_info(in_func_oid, &security_in_function); -+ security_in_function_prepared = true; -+ } -+ if (string) { -+ Datum d = InputFunctionCall(&security_in_function, -+ string, -+ security_typioparam, -+ sysatt->atttypmod); -+ loaded_security = ObjectIdGetDatum(d); -+ } -+ cstate->cur_attname = NULL; -+ cstate->cur_attval = NULL; -+ continue; -+ } -+ - if (fieldno >= fldct) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), -@@ -2048,6 +2129,33 @@ CopyFrom(CopyState cstate) - int attnum = lfirst_int(cur); - int m = attnum - 1; - -+ if (pgaceIsSecuritySystemColumn(attnum)) { -+ Form_pg_attribute sysatt = SystemAttributeDefinition(attnum, false); -+ Datum d; -+ -+ cstate->cur_attname = NameStr(sysatt->attname); -+ i++; -+ -+ if (!security_in_function_prepared) { -+ Oid security_in_func_oid; -+ getTypeBinaryInputInfo(sysatt->atttypid, -+ &security_in_func_oid, -+ &security_typioparam); -+ fmgr_info(security_in_func_oid, &security_in_function); -+ security_in_function_prepared = true; -+ } -+ d = CopyReadBinaryAttribute(cstate, -+ i, -+ &security_in_function, -+ security_typioparam, -+ sysatt->atttypmod, -+ &isnull); -+ if (!isnull) -+ loaded_security = ObjectIdGetDatum(d); -+ cstate->cur_attname = NULL; -+ continue; -+ } -+ - cstate->cur_attname = NameStr(attr[m]->attname); - i++; - values[m] = CopyReadBinaryAttribute(cstate, -@@ -2079,6 +2187,7 @@ CopyFrom(CopyState cstate) - - if (cstate->oids && file_has_oids) - HeapTupleSetOid(tuple, loaded_oid); -+ HeapTupleSetSecurity(tuple, loaded_security); - - /* Triggers and stuff need to be invoked in query context. */ - MemoryContextSwitchTo(oldcontext); -@@ -2102,6 +2211,9 @@ CopyFrom(CopyState cstate) - } - } - -+ if (!skip_tuple && !pgaceHeapTupleInsert(cstate->rel, tuple, false, false)) -+ skip_tuple = true; -+ - if (!skip_tuple) - { - /* Place tuple in tuple slot */ -@@ -3364,6 +3476,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relati - break; - } - } -+ -+ /* PGACE: writable system column support */ -+ if (attnum == InvalidAttrNumber) -+ { -+ Form_pg_attribute sysatt = SystemAttributeByName(name, true); -+ if (sysatt) { -+ if (pgaceIsSecuritySystemColumn(sysatt->attnum)) -+ attnum = sysatt->attnum; -+ } -+ } -+ - if (attnum == InvalidAttrNumber) - { - if (rel != NULL) -@@ -3413,7 +3536,7 @@ copy_dest_receive(TupleTableSlot *slot, - slot_getallattrs(slot); - - /* And send the data */ -- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); -+ CopyOneRowTo(cstate, InvalidOid, InvalidOid, slot->tts_values, slot->tts_isnull); - } - - /* -diff -rpNU3 base/src/backend/commands/dbcommands.c pgace/src/backend/commands/dbcommands.c ---- base/src/backend/commands/dbcommands.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/commands/dbcommands.c 2008-01-08 01:39:49.000000000 +0900 -@@ -40,6 +40,7 @@ - #include "miscadmin.h" - #include "pgstat.h" - #include "postmaster/bgwriter.h" -+#include "security/pgace.h" - #include "storage/freespace.h" - #include "storage/procarray.h" - #include "storage/smgr.h" -@@ -92,6 +93,7 @@ createdb(const CreatedbStmt *stmt) - DefElem *dtemplate = NULL; - DefElem *dencoding = NULL; - DefElem *dconnlimit = NULL; -+ DefElem *dpgace_item = NULL; - char *dbname = stmt->dbname; - char *dbowner = NULL; - const char *dbtemplate = NULL; -@@ -151,6 +153,13 @@ createdb(const CreatedbStmt *stmt) - errmsg("LOCATION is not supported anymore"), - errhint("Consider using tablespaces instead."))); - } -+ else if (pgaceIsGramSecurityItem(defel)) { -+ if (dpgace_item) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ dpgace_item = defel; -+ } - else - elog(ERROR, "option \"%s\" not recognized", - defel->defname); -@@ -424,6 +433,7 @@ createdb(const CreatedbStmt *stmt) - new_record, new_record_nulls); - - HeapTupleSetOid(tuple, dboid); -+ pgaceGramCreateDatabase(pg_database_rel, tuple, dpgace_item); - - simple_heap_insert(pg_database_rel, tuple); - -@@ -832,6 +842,7 @@ AlterDatabase(AlterDatabaseStmt *stmt) - ListCell *option; - int connlimit = -1; - DefElem *dconnlimit = NULL; -+ DefElem *dpgace_item = NULL; - Datum new_record[Natts_pg_database]; - char new_record_nulls[Natts_pg_database]; - char new_record_repl[Natts_pg_database]; -@@ -849,6 +860,13 @@ AlterDatabase(AlterDatabaseStmt *stmt) - errmsg("conflicting or redundant options"))); - dconnlimit = defel; - } -+ else if (pgaceIsGramSecurityItem(defel)) { -+ if (dpgace_item) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ dpgace_item = defel; -+ } - else - elog(ERROR, "option \"%s\" not recognized", - defel->defname); -@@ -894,6 +912,7 @@ AlterDatabase(AlterDatabaseStmt *stmt) - - newtuple = heap_modifytuple(tuple, RelationGetDescr(rel), new_record, - new_record_nulls, new_record_repl); -+ pgaceGramAlterDatabase(rel, newtuple, dpgace_item); - simple_heap_update(rel, &tuple->t_self, newtuple); - - /* Update indexes */ -diff -rpNU3 base/src/backend/commands/functioncmds.c pgace/src/backend/commands/functioncmds.c ---- base/src/backend/commands/functioncmds.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/commands/functioncmds.c 2008-01-08 01:39:49.000000000 +0900 -@@ -47,6 +47,7 @@ - #include "miscadmin.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+#include "security/pgace.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -412,7 +413,8 @@ compute_attributes_sql_style(List *optio - bool *security_definer, - ArrayType **proconfig, - float4 *procost, -- float4 *prorows) -+ float4 *prorows, -+ DefElem **pgaceItem) - { - ListCell *option; - DefElem *as_item = NULL; -@@ -444,6 +446,14 @@ compute_attributes_sql_style(List *optio - errmsg("conflicting or redundant options"))); - language_item = defel; - } -+ else if (pgaceIsGramSecurityItem(defel)) -+ { -+ if (*pgaceItem) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ *pgaceItem = defel; -+ } - else if (compute_common_attribute(defel, - &volatility_item, - &strict_item, -@@ -624,6 +634,7 @@ CreateFunction(CreateFunctionStmt *stmt) - HeapTuple languageTuple; - Form_pg_language languageStruct; - List *as_clause; -+ DefElem *pgaceItem = NULL; - - /* Convert list of names to a name and namespace */ - namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, -@@ -647,7 +658,7 @@ CreateFunction(CreateFunctionStmt *stmt) - compute_attributes_sql_style(stmt->options, - &as_clause, &language, - &volatility, &isStrict, &security, -- &proconfig, &procost, &prorows); -+ &proconfig, &procost, &prorows, &pgaceItem); - - /* Convert language name to canonical case */ - languageName = case_translate_language_name(language); -@@ -801,7 +812,8 @@ CreateFunction(CreateFunctionStmt *stmt) - PointerGetDatum(parameterNames), - PointerGetDatum(proconfig), - procost, -- prorows); -+ prorows, -+ pgaceItem); - } - - -@@ -1151,6 +1163,7 @@ AlterFunction(AlterFunctionStmt *stmt) - List *set_items = NIL; - DefElem *cost_item = NULL; - DefElem *rows_item = NULL; -+ DefElem *pgaceItem = NULL; - - rel = heap_open(ProcedureRelationId, RowExclusiveLock); - -@@ -1182,6 +1195,15 @@ AlterFunction(AlterFunctionStmt *stmt) - { - DefElem *defel = (DefElem *) lfirst(l); - -+ if (pgaceIsGramSecurityItem(defel)) { -+ if (pgaceItem) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ pgaceItem = defel; -+ continue; -+ } -+ - if (compute_common_attribute(defel, - &volatility_item, - &strict_item, -@@ -1252,6 +1274,7 @@ AlterFunction(AlterFunctionStmt *stmt) - tup = heap_modifytuple(tup, RelationGetDescr(rel), - repl_val, repl_null, repl_repl); - } -+ pgaceGramAlterFunction(rel, tup, pgaceItem); - - /* Do the update */ - simple_heap_update(rel, &tup->t_self, tup); -diff -rpNU3 base/src/backend/commands/lockcmds.c pgace/src/backend/commands/lockcmds.c ---- base/src/backend/commands/lockcmds.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/commands/lockcmds.c 2008-01-08 01:39:49.000000000 +0900 -@@ -18,6 +18,7 @@ - #include "catalog/namespace.h" - #include "commands/lockcmds.h" - #include "miscadmin.h" -+#include "security/pgace.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" - -@@ -59,6 +60,8 @@ LockTableCommand(LockStmt *lockstmt) - aclcheck_error(aclresult, ACL_KIND_CLASS, - get_rel_name(reloid)); - -+ pgaceLockTable(reloid); -+ - if (lockstmt->nowait) - rel = relation_open_nowait(reloid, lockstmt->mode); - else -diff -rpNU3 base/src/backend/commands/proclang.c pgace/src/backend/commands/proclang.c ---- base/src/backend/commands/proclang.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/commands/proclang.c 2008-01-08 01:39:49.000000000 +0900 -@@ -144,7 +144,8 @@ CreateProceduralLanguage(CreatePLangStmt - PointerGetDatum(NULL), - PointerGetDatum(NULL), - 1, -- 0); -+ 0, -+ NULL); - } - - /* -@@ -177,7 +178,8 @@ CreateProceduralLanguage(CreatePLangStmt - PointerGetDatum(NULL), - PointerGetDatum(NULL), - 1, -- 0); -+ 0, -+ NULL); - } - } - else -diff -rpNU3 base/src/backend/commands/tablecmds.c pgace/src/backend/commands/tablecmds.c ---- base/src/backend/commands/tablecmds.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/commands/tablecmds.c 2008-03-19 10:08:35.000000000 +0900 -@@ -57,6 +57,7 @@ - #include "parser/parser.h" - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteHandler.h" -+#include "security/pgace.h" - #include "storage/smgr.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -434,7 +435,8 @@ DefineRelation(CreateStmt *stmt, char re - parentOidCount, - stmt->oncommit, - reloptions, -- allowSystemTableMods); -+ allowSystemTableMods, -+ pgaceRelationAttrList(stmt)); - - StoreCatalogInheritance(relationId, inheritOids); - -@@ -2031,6 +2033,7 @@ ATPrepCmd(List **wqueue, Relation rel, A - case AT_DisableRule: - case AT_AddInherit: /* INHERIT / NO INHERIT */ - case AT_DropInherit: -+ case AT_SetSecurityLabel: - ATSimplePermissions(rel, false); - /* These commands never recurse */ - /* No command-specific prep needed */ -@@ -2253,6 +2256,9 @@ ATExecCmd(AlteredTableInfo *tab, Relatio - case AT_DropInherit: - ATExecDropInherit(rel, (RangeVar *) cmd->def); - break; -+ case AT_SetSecurityLabel: -+ pgaceAlterRelationCommon(rel, cmd); -+ break; - default: /* oops */ - elog(ERROR, "unrecognized alter table type: %d", - (int) cmd->subtype); -diff -rpNU3 base/src/backend/commands/trigger.c pgace/src/backend/commands/trigger.c ---- base/src/backend/commands/trigger.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/commands/trigger.c 2008-01-08 01:39:49.000000000 +0900 -@@ -31,6 +31,7 @@ - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -+#include "security/pgace.h" - #include "tcop/utility.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -1574,6 +1575,12 @@ ExecCallTriggerFunc(TriggerData *trigdat - */ - InitFunctionCallInfoData(fcinfo, finfo, 0, (Node *) trigdata, NULL); - -+ /* PGACE: permission check for trigegr function */ -+ if (!pgaceCallFunctionTrigger(finfo, trigdata)) { -+ MemoryContextSwitchTo(oldContext); -+ return (HeapTuple) DatumGetPointer(NULL); -+ } -+ - result = FunctionCallInvoke(&fcinfo); - - MemoryContextSwitchTo(oldContext); -diff -rpNU3 base/src/backend/executor/execMain.c pgace/src/backend/executor/execMain.c ---- base/src/backend/executor/execMain.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/executor/execMain.c 2008-03-19 10:08:35.000000000 +0900 -@@ -48,6 +48,7 @@ - #include "optimizer/clauses.h" - #include "parser/parse_clause.h" - #include "parser/parsetree.h" -+#include "security/pgace.h" - #include "storage/smgr.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" -@@ -136,6 +137,8 @@ ExecutorStart(QueryDesc *queryDesc, int - Assert(queryDesc != NULL); - Assert(queryDesc->estate == NULL); - -+ pgaceExecutorStart(queryDesc, eflags); -+ - /* - * If the transaction is read-only, we need to check if any writes are - * planned to non-temporary tables. EXPLAIN is considered read-only. -@@ -1216,6 +1219,8 @@ ExecutePlan(EState *estate, - - for (;;) - { -+ Oid __tts_security = InvalidOid; /* PGACE: explicit security labaling */ -+ - /* Reset the per-output-tuple exprcontext */ - ResetPerTupleExprContext(estate); - -@@ -1357,6 +1362,13 @@ lnext: ; - } - - /* -+ * PGACE: security attribute system columnt support. -+ * If client specified a explicit security label, -+ * pgaceFetchSecurityLabel() fetch it via junk attribute. -+ */ -+ pgaceFetchSecurityAttribute(junkfilter, slot, &__tts_security); -+ -+ /* - * Create a new "clean" tuple with all junk attributes removed. We - * don't need to do this for DELETE, however (there will in fact - * be no non-junk attributes in a DELETE!) -@@ -1364,6 +1376,7 @@ lnext: ; - if (operation != CMD_DELETE) - slot = ExecFilterJunk(junkfilter, slot); - } -+ slot->tts_security = __tts_security; - - /* - * now that we have a tuple, do the appropriate thing with it.. either -@@ -1484,6 +1497,9 @@ ExecInsert(TupleTableSlot *slot, - resultRelInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelInfo->ri_RelationDesc; - -+ /* PGACE: put an explicit security labeling */ -+ HeapTupleStoreSecurityFromSlot(tuple, slot); -+ - /* BEFORE ROW INSERT Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) -@@ -1520,6 +1536,13 @@ ExecInsert(TupleTableSlot *slot, - ExecConstraints(resultRelInfo, slot, estate); - - /* -+ * PGACE: check HeapTuple Insertion permission -+ */ -+ if (!pgaceHeapTupleInsert(resultRelationDesc, tuple, -+ false, !!resultRelInfo->ri_projectReturning)) -+ return; -+ -+ /* - * insert the tuple - * - * Note: heap_insert returns the tid (location) of the new tuple in the -@@ -1586,6 +1609,10 @@ ExecDelete(ItemPointer tupleid, - return; - } - -+ if (!pgaceHeapTupleDelete(resultRelationDesc, tupleid, -+ false, !!resultRelInfo->ri_projectReturning)) -+ return; -+ - /* - * delete the tuple - * -@@ -1722,6 +1749,9 @@ ExecUpdate(TupleTableSlot *slot, - resultRelInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelInfo->ri_RelationDesc; - -+ /* PGACE: put an explicit security attribute */ -+ HeapTupleStoreSecurityFromSlot(tuple, slot); -+ - /* BEFORE ROW UPDATE Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) -@@ -1765,6 +1795,11 @@ lreplace:; - if (resultRelationDesc->rd_att->constr) - ExecConstraints(resultRelInfo, slot, estate); - -+ /* PGACE: check HeapTuple update permission */ -+ if (!pgaceHeapTupleUpdate(resultRelationDesc, tupleid, tuple, -+ false, !!resultRelInfo->ri_projectReturning)) -+ return; -+ - /* - * replace the heap tuple - * -@@ -2629,7 +2664,8 @@ OpenIntoRel(QueryDesc *queryDesc) - 0, - into->onCommit, - reloptions, -- allowSystemTableMods); -+ allowSystemTableMods, -+ NIL); - - FreeTupleDesc(tupdesc); - -@@ -2735,6 +2771,13 @@ intorel_receive(TupleTableSlot *slot, De - - tuple = ExecCopySlotTuple(slot); - -+ /* PGACE: store explicit security labeling and check HeapTuple insertion permission */ -+ HeapTupleStoreSecurityFromSlot(tuple, slot); -+ if (!pgaceHeapTupleInsert(estate->es_into_relation_descriptor, tuple, false, false)) { -+ heap_freetuple(tuple); -+ return; -+ } -+ - heap_insert(estate->es_into_relation_descriptor, - tuple, - estate->es_output_cid, -diff -rpNU3 base/src/backend/executor/execQual.c pgace/src/backend/executor/execQual.c ---- base/src/backend/executor/execQual.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/executor/execQual.c 2008-01-08 01:39:49.000000000 +0900 -@@ -47,6 +47,7 @@ - #include "nodes/makefuncs.h" - #include "optimizer/planmain.h" - #include "parser/parse_expr.h" -+#include "security/pgace.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -1750,6 +1751,8 @@ ExecEvalFunc(FuncExprState *fcache, - /* Go directly to ExecMakeFunctionResult on subsequent uses */ - fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult; - -+ pgaceCallFunction(&fcache->func); -+ - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } - -diff -rpNU3 base/src/backend/libpq/be-fsstubs.c pgace/src/backend/libpq/be-fsstubs.c ---- base/src/backend/libpq/be-fsstubs.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/libpq/be-fsstubs.c 2008-01-08 01:39:49.000000000 +0900 -@@ -45,6 +45,7 @@ - #include "libpq/be-fsstubs.h" - #include "libpq/libpq-fs.h" - #include "miscadmin.h" -+#include "security/pgace.h" - #include "storage/fd.h" - #include "storage/large_object.h" - #include "utils/memutils.h" -@@ -353,6 +354,8 @@ lo_import(PG_FUNCTION_ARGS) - errmsg("could not open server file \"%s\": %m", - fnamebuf))); - -+ pgaceLargeObjectImport(fd); -+ - /* - * create an inversion object - */ -@@ -434,6 +437,8 @@ lo_export(PG_FUNCTION_ARGS) - errmsg("could not create server file \"%s\": %m", - fnamebuf))); - -+ pgaceLargeObjectExport(fd, lobjId); -+ - /* - * read in from the inversion file and write to the filesystem - */ -diff -rpNU3 base/src/backend/nodes/copyfuncs.c pgace/src/backend/nodes/copyfuncs.c ---- base/src/backend/nodes/copyfuncs.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/nodes/copyfuncs.c 2008-03-19 10:08:35.000000000 +0900 -@@ -24,6 +24,7 @@ - - #include "nodes/plannodes.h" - #include "nodes/relation.h" -+#include "security/pgace.h" - #include "utils/datum.h" - - -@@ -85,6 +86,7 @@ _copyPlannedStmt(PlannedStmt *from) - COPY_NODE_FIELD(rowMarks); - COPY_NODE_FIELD(relationOids); - COPY_SCALAR_FIELD(nParamExec); -+ COPY_NODE_FIELD(pgaceItem); - - return newnode; - } -@@ -1789,6 +1791,7 @@ _copyColumnDef(ColumnDef *from) - COPY_NODE_FIELD(raw_default); - COPY_STRING_FIELD(cooked_default); - COPY_NODE_FIELD(constraints); -+ COPY_NODE_FIELD(pgaceItem); - - return newnode; - } -@@ -1869,6 +1872,7 @@ _copyQuery(Query *from) - COPY_NODE_FIELD(limitCount); - COPY_NODE_FIELD(rowMarks); - COPY_NODE_FIELD(setOperations); -+ COPY_NODE_FIELD(pgaceItem); - - return newnode; - } -@@ -2105,6 +2109,7 @@ _copyCreateStmt(CreateStmt *from) - COPY_NODE_FIELD(options); - COPY_SCALAR_FIELD(oncommit); - COPY_STRING_FIELD(tablespacename); -+ COPY_NODE_FIELD(pgaceItem); - - return newnode; - } -@@ -3602,6 +3607,10 @@ copyObject(void *from) - break; - - default: -+ retval = pgaceCopyObject(from); -+ if (retval) -+ break; -+ - elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from)); - retval = from; /* keep compiler quiet */ - break; -diff -rpNU3 base/src/backend/nodes/outfuncs.c pgace/src/backend/nodes/outfuncs.c ---- base/src/backend/nodes/outfuncs.c 2008-01-14 22:59:48.000000000 +0900 -+++ pgace/src/backend/nodes/outfuncs.c 2008-01-14 23:08:31.000000000 +0900 -@@ -26,6 +26,7 @@ - #include "lib/stringinfo.h" - #include "nodes/plannodes.h" - #include "nodes/relation.h" -+#include "security/pgace.h" - #include "utils/datum.h" - - -@@ -252,6 +253,7 @@ _outPlannedStmt(StringInfo str, PlannedS - WRITE_NODE_FIELD(rowMarks); - WRITE_NODE_FIELD(relationOids); - WRITE_INT_FIELD(nParamExec); -+ WRITE_NODE_FIELD(pgaceItem); - } - - /* -@@ -1748,6 +1750,7 @@ _outQuery(StringInfo str, Query *node) - WRITE_NODE_FIELD(limitCount); - WRITE_NODE_FIELD(rowMarks); - WRITE_NODE_FIELD(setOperations); -+ WRITE_NODE_FIELD(pgaceItem); - } - - static void -@@ -2440,6 +2443,8 @@ _outNode(StringInfo str, void *obj) - break; - - default: -+ if (pgaceOutObject(str, obj)) -+ break; - - /* - * This should be an ERROR, but it's too useful to be able to -diff -rpNU3 base/src/backend/nodes/readfuncs.c pgace/src/backend/nodes/readfuncs.c ---- base/src/backend/nodes/readfuncs.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/nodes/readfuncs.c 2008-03-12 20:03:44.000000000 +0900 -@@ -24,6 +24,7 @@ - - #include "nodes/parsenodes.h" - #include "nodes/readfuncs.h" -+#include "security/pgace.h" - - - /* -@@ -154,6 +155,7 @@ _readQuery(void) - READ_NODE_FIELD(limitCount); - READ_NODE_FIELD(rowMarks); - READ_NODE_FIELD(setOperations); -+ READ_NODE_FIELD(pgaceItem); - - READ_DONE(); - } -@@ -1126,8 +1128,9 @@ parseNodeString(void) - return_value = _readDeclareCursorStmt(); - else - { -- elog(ERROR, "badly formatted node string \"%.32s\"...", token); -- return_value = NULL; /* keep compiler quiet */ -+ return_value = pgaceReadObject(token); -+ if (!return_value) -+ elog(ERROR, "badly formatted node string \"%.32s\"...", token); - } - - return (Node *) return_value; -diff -rpNU3 base/src/backend/optimizer/plan/planner.c pgace/src/backend/optimizer/plan/planner.c ---- base/src/backend/optimizer/plan/planner.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/optimizer/plan/planner.c 2008-01-08 01:39:49.000000000 +0900 -@@ -101,6 +101,10 @@ planner(Query *parse, int cursorOptions, - result = (*planner_hook) (parse, cursorOptions, boundParams); - else - result = standard_planner(parse, cursorOptions, boundParams); -+ -+ /* PGACE: pgaceItem is passed to PlannedStmt */ -+ result->pgaceItem = parse->pgaceItem; -+ - return result; - } - -diff -rpNU3 base/src/backend/parser/analyze.c pgace/src/backend/parser/analyze.c ---- base/src/backend/parser/analyze.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/parser/analyze.c 2008-01-11 16:13:01.000000000 +0900 -@@ -36,6 +36,7 @@ - #include "parser/parse_relation.h" - #include "parser/parse_target.h" - #include "parser/parsetree.h" -+#include "security/pgace.h" - - - typedef struct -@@ -404,6 +405,9 @@ transformInsertStmt(ParseState *pstate, - Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); - pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); - -+ /* security attribute system column support */ -+ pgaceTransformInsertStmt(&icolumns, &attrnos, selectQuery->targetList); -+ - /*---------- - * Generate an expression list for the INSERT that selects all the - * non-resjunk columns from the subquery. (INSERT's tlist must be -@@ -563,14 +567,15 @@ transformInsertStmt(ParseState *pstate, - Expr *expr = (Expr *) lfirst(lc); - ResTarget *col; - TargetEntry *tle; -+ AttrNumber anum = (AttrNumber) lfirst_int(attnos); - - col = (ResTarget *) lfirst(icols); - Assert(IsA(col, ResTarget)); - - tle = makeTargetEntry(expr, -- (AttrNumber) lfirst_int(attnos), -+ anum, - col->name, -- false); -+ anum < 0 ? true : false); - qry->targetList = lappend(qry->targetList, tle); - - icols = lnext(icols); -@@ -733,6 +738,7 @@ transformSelectStmt(ParseState *pstate, - /* handle any SELECT INTO/CREATE TABLE AS spec */ - if (stmt->intoClause) - { -+ pgaceTransformSelectStmt(qry->targetList); - qry->intoClause = stmt->intoClause; - if (stmt->intoClause->colNames) - applyColumnNames(qry->targetList, stmt->intoClause->colNames); -diff -rpNU3 base/src/backend/parser/gram.y pgace/src/backend/parser/gram.y ---- base/src/backend/parser/gram.y 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/parser/gram.y 2008-03-19 10:08:35.000000000 +0900 -@@ -56,6 +56,7 @@ - #include "commands/defrem.h" - #include "nodes/makefuncs.h" - #include "parser/gramparse.h" -+#include "security/pgace.h" - #include "storage/lmgr.h" - #include "utils/date.h" - #include "utils/datetime.h" -@@ -351,6 +352,8 @@ static Node *makeXmlExpr(XmlExprOp op, c - %type OptTableSpace OptConsTableSpace OptTableSpaceOwner - %type opt_check_option - -+%type OptSecurityItem SecurityItem -+ - %type xml_attribute_el - %type xml_attribute_list xml_attributes - %type xml_root_version opt_xml_root_standalone -@@ -1637,6 +1640,24 @@ alter_table_cmd: - n->def = (Node *) $3; - $$ = (Node *)n; - } -+ /* ALTER TABLE CONTEXT = '...' */ -+ | SecurityItem -+ { -+ AlterTableCmd *n = makeNode(AlterTableCmd); -+ n->subtype = AT_SetSecurityLabel; -+ n->name = NULL; -+ n->def = (Node *) $1; -+ $$ = (Node *) n; -+ } -+ /* ALTER TABLE ALTER [COLUMN] CONTEXT = '...' */ -+ | ALTER opt_column ColId SecurityItem -+ { -+ AlterTableCmd *n = makeNode(AlterTableCmd); -+ n->subtype = AT_SetSecurityLabel; -+ n->name = $3; -+ n->def = (Node *) $4; -+ $$ = (Node *) n; -+ } - | alter_rel_cmd - { - $$ = $1; -@@ -1883,7 +1904,7 @@ opt_using: - *****************************************************************************/ - - CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' -- OptInherit OptWith OnCommitOption OptTableSpace -+ OptInherit OptWith OnCommitOption OptTableSpace OptSecurityItem - { - CreateStmt *n = makeNode(CreateStmt); - $4->istemp = $2; -@@ -1894,10 +1915,11 @@ CreateStmt: CREATE OptTemp TABLE qualifi - n->options = $9; - n->oncommit = $10; - n->tablespacename = $11; -+ n->pgaceItem = (Node *) $12; - $$ = (Node *)n; - } - | CREATE OptTemp TABLE qualified_name OF qualified_name -- '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace -+ '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecurityItem - { - /* SQL99 CREATE TABLE OF (cols) seems to be satisfied - * by our inheritance capabilities. Let's try it... -@@ -1911,6 +1933,7 @@ CreateStmt: CREATE OptTemp TABLE qualifi - n->options = $10; - n->oncommit = $11; - n->tablespacename = $12; -+ n->pgaceItem = (Node *) $13; - $$ = (Node *)n; - } - ; -@@ -1953,13 +1976,14 @@ TableElement: - | TableConstraint { $$ = $1; } - ; - --columnDef: ColId Typename ColQualList -+columnDef: ColId Typename ColQualList OptSecurityItem - { - ColumnDef *n = makeNode(ColumnDef); - n->colname = $1; - n->typename = $2; - n->constraints = $3; - n->is_local = true; -+ n->pgaceItem = (Node *) $4; - $$ = (Node *)n; - } - ; -@@ -4278,6 +4302,10 @@ common_func_opt_item: - /* we abuse the normal content of a DefElem here */ - $$ = makeDefElem("set", (Node *)$1); - } -+ | SecurityItem -+ { -+ $$ = $1; -+ } - ; - - createfunc_opt_item: -@@ -5361,6 +5389,10 @@ createdb_opt_item: - { - $$ = makeDefElem("owner", NULL); - } -+ | SecurityItem -+ { -+ $$ = $1; -+ } - ; - - /* -@@ -5409,6 +5441,10 @@ alterdb_opt_item: - { - $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4)); - } -+ | SecurityItem -+ { -+ $$ = $1; -+ } - ; - - -@@ -8736,6 +8772,26 @@ target_el: a_expr AS ColLabel - } - ; - -+/***************************************************************************** -+ * -+ * PGACE Security Items -+ * -+ *****************************************************************************/ -+ -+OptSecurityItem: -+ SecurityItem { $$ = $1; } -+ | /* EMPTY */ { $$ = NULL; } -+ ; -+ -+SecurityItem: -+ IDENT '=' Sconst -+ { -+ DefElem *n = pgaceGramSecurityItem($1, $3); -+ if (n == NULL) -+ yyerror("syntax error"); -+ $$ = n; -+ } -+ ; - - /***************************************************************************** - * -diff -rpNU3 base/src/backend/parser/parse_target.c pgace/src/backend/parser/parse_target.c ---- base/src/backend/parser/parse_target.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/parser/parse_target.c 2008-01-08 01:39:49.000000000 +0900 -@@ -26,6 +26,7 @@ - #include "parser/parse_relation.h" - #include "parser/parse_target.h" - #include "parser/parse_type.h" -+#include "security/pgace.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" - #include "utils/typcache.h" -@@ -335,14 +336,19 @@ transformAssignedExpr(ParseState *pstate - Relation rd = pstate->p_target_relation; - - Assert(rd != NULL); -- if (attrno <= 0) -+ if (attrno > 0) { -+ attrtype = attnumTypeId(rd, attrno); -+ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; -+ } else if (pgaceIsSecuritySystemColumn(attrno)) { -+ attrtype = SECLABELOID; -+ attrtypmod = -1; -+ } else { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot assign to system column \"%s\"", - colname), - parser_errposition(pstate, location))); -- attrtype = attnumTypeId(rd, attrno); -- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; -+ } - - /* - * If the expression is a DEFAULT placeholder, insert the attribute's -@@ -483,6 +489,9 @@ updateTargetListEntry(ParseState *pstate - */ - tle->resno = (AttrNumber) attrno; - tle->resname = colname; -+ -+ if (pgaceIsSecuritySystemColumn(attrno)) -+ tle->resjunk = true; - } - - -@@ -749,6 +758,7 @@ checkInsertTargets(ParseState *pstate, L - Bitmapset *wholecols = NULL; - Bitmapset *partialcols = NULL; - ListCell *tl; -+ bool security_attr = false; - - foreach(tl, cols) - { -@@ -757,14 +767,31 @@ checkInsertTargets(ParseState *pstate, L - int attrno; - - /* Lookup column name, ereport on failure */ -- attrno = attnameAttNum(pstate->p_target_relation, name, false); -- if (attrno == InvalidAttrNumber) -+ attrno = attnameAttNum(pstate->p_target_relation, name, true); -+ if (attrno == InvalidAttrNumber) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - name, - RelationGetRelationName(pstate->p_target_relation)), - parser_errposition(pstate, col->location))); -+ } else if (attrno <= 0) { -+ if (pgaceIsSecuritySystemColumn(attrno)) { -+ if (security_attr) -+ ereport(ERROR, -+ (errcode(ERRCODE_DUPLICATE_COLUMN), -+ errmsg("column \"%s\" specified more than once", name), -+ parser_errposition(pstate, col->location))); -+ security_attr = true; -+ *attrnos = lappend_int(*attrnos, attrno); -+ continue; -+ } -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), -+ errmsg("column \"%s\" of relation \"%s\" is system column", -+ name, RelationGetRelationName(pstate->p_target_relation)), -+ parser_errposition(pstate, col->location))); -+ } - - /* - * Check for duplicates, but only of whole columns --- we allow -diff -rpNU3 base/src/backend/postmaster/postmaster.c pgace/src/backend/postmaster/postmaster.c ---- base/src/backend/postmaster/postmaster.c 2008-01-14 22:59:48.000000000 +0900 -+++ pgace/src/backend/postmaster/postmaster.c 2008-01-25 19:04:56.000000000 +0900 -@@ -107,6 +107,7 @@ - #include "postmaster/pgarch.h" - #include "postmaster/postmaster.h" - #include "postmaster/syslogger.h" -+#include "security/pgace.h" - #include "storage/fd.h" - #include "storage/ipc.h" - #include "storage/pg_shmem.h" -@@ -1026,6 +1027,9 @@ PostmasterMain(int argc, char *argv[]) - Assert(StartupPID != 0); - pmState = PM_STARTUP; - -+ if (!pgaceInitializePostmaster()) -+ ExitPostmaster(1); -+ - status = ServerLoop(); - - /* -@@ -2039,9 +2043,11 @@ pmdie(SIGNAL_ARGS) - signal_child(PgArchPID, SIGQUIT); - if (PgStatPID != 0) - signal_child(PgStatPID, SIGQUIT); -+ pgaceFinalizePostmaster(); - ExitPostmaster(0); - break; - } -+ pgaceFinalizePostmaster(); - - PG_SETMASK(&UnBlockSig); - -diff -rpNU3 base/src/backend/rewrite/rewriteHandler.c pgace/src/backend/rewrite/rewriteHandler.c ---- base/src/backend/rewrite/rewriteHandler.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/rewrite/rewriteHandler.c 2008-01-08 01:39:49.000000000 +0900 -@@ -24,6 +24,7 @@ - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteHandler.h" - #include "rewrite/rewriteManip.h" -+#include "security/pgace.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" - #include "commands/trigger.h" -@@ -1880,5 +1881,8 @@ QueryRewrite(Query *parsetree) - if (!foundOriginalQuery && lastInstead != NULL) - lastInstead->canSetTag = true; - -+ /* PGACE: general queries proxy */ -+ results = pgaceProxyQuery(results); -+ - return results; - } -diff -rpNU3 base/src/backend/security/Makefile pgace/src/backend/security/Makefile ---- base/src/backend/security/Makefile 1970-01-01 09:00:00.000000000 +0900 -+++ pgace/src/backend/security/Makefile 2008-03-13 23:25:01.000000000 +0900 -@@ -0,0 +1,26 @@ -+# -+# src/backend/security/Makefile -+# Makefile for Security Purpose Extensions -+# -+# Copyright (c) 2006 - 2007 KaiGai Kohei -+# -+ubdir = src/backend/security -+top_builddir = ../../.. -+include $(top_builddir)/src/Makefile.global -+ -+OBJS := pgaceCommon.o pgaceHooks.o -+ -+all: SUBSYS.o -+ -+SUBSYS.o: $(OBJS) -+ $(LD) $(LDREL) $(LDOUT) $@ $^ -+ -+depend dep: -+ $(CC) -MM $(CFLAGS) *.c >depend -+ -+clean: -+ rm -f SUBSYS.o $(OBJS) -+ -+ifeq (depend,$(wildcard depend)) -+include depend -+endif -diff -rpNU3 base/src/backend/security/pgaceCommon.c pgace/src/backend/security/pgaceCommon.c ---- base/src/backend/security/pgaceCommon.c 1970-01-01 09:00:00.000000000 +0900 -+++ pgace/src/backend/security/pgaceCommon.c 2008-02-01 17:24:11.000000000 +0900 -@@ -0,0 +1,714 @@ -+/* -+ * src/backend/security/pgaceCommon.c -+ * Common part of PostgreSQL Access Control Extension -+ * Copyright 2007 KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/genam.h" -+#include "access/heapam.h" -+#include "access/xact.h" -+#include "catalog/catalog.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_security.h" -+#include "catalog/pg_type.h" -+#include "executor/executor.h" -+#include "miscadmin.h" -+#include "nodes/makefuncs.h" -+#include "nodes/parsenodes.h" -+#include "parser/parse_expr.h" -+#include "security/pgace.h" -+#include "utils/builtins.h" -+#include "utils/fmgroids.h" -+#include "utils/syscache.h" -+#include -+#include -+ -+/***************************************************************************** -+ * Security attribute system column support -+ *****************************************************************************/ -+#ifdef SECURITY_SYSATTR_NAME -+ -+bool pgaceIsSecuritySystemColumn(int attrno) -+{ -+ return ((attrno == SecurityAttributeNumber) ? true : false); -+} -+ -+void pgaceTransformSelectStmt(List *targetList) { -+ ListCell *l; -+ -+ foreach (l, targetList) { -+ TargetEntry *tle = lfirst(l); -+ -+ if (tle->resjunk) -+ continue; -+ if (!strcmp(tle->resname, SECURITY_SYSATTR_NAME)) { -+ if (exprType((Node *) tle->expr) != SECLABELOID) -+ elog(ERROR, "type mismatch in explicit labeling"); -+ tle->resjunk = true; -+ break; -+ } -+ } -+} -+ -+void pgaceTransformInsertStmt(List **p_icolumns, List **p_attrnos, List *targetList) { -+ AttrNumber security_attrno = 0; -+ ListCell *lc; -+ -+ foreach (lc, targetList) { -+ TargetEntry *tle = (TargetEntry *) lfirst(lc); -+ -+ security_attrno++; -+ if (strcmp(tle->resname, SECURITY_SYSATTR_NAME)) -+ continue; -+ -+ if (list_length(*p_icolumns) < list_length(targetList)) { -+ List *__icolumns = NIL; -+ List *__attrnos = NIL; -+ ListCell *l1, *l2; -+ int index = 0; -+ -+ forboth(l1, *p_icolumns, l2, *p_attrnos) { -+ if (++index == security_attrno) { -+ ResTarget *col = makeNode(ResTarget); -+ col->name = pstrdup(SECURITY_SYSATTR_NAME); -+ col->indirection = NIL; -+ col->val = NULL; -+ col->location = -1; -+ -+ __icolumns = lappend(__icolumns, col); -+ __attrnos = lappend_int(__attrnos, SecurityAttributeNumber); -+ } -+ if (lfirst_int(l2) == SecurityAttributeNumber) -+ return; -+ __icolumns = lappend(__icolumns, lfirst(l1)); -+ __attrnos = lappend_int(__attrnos, lfirst_int(l2)); -+ } -+ *p_icolumns = __icolumns; -+ *p_attrnos = __attrnos; -+ } -+ break; -+ } -+} -+ -+void pgaceFetchSecurityAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, Oid *tts_security) -+{ -+ AttrNumber attno; -+ Datum datum; -+ bool isnull; -+ -+ attno = ExecFindJunkAttribute(junkfilter, SECURITY_SYSATTR_NAME); -+ if (attno != InvalidAttrNumber) { -+ datum = ExecGetJunkAttribute(slot, attno, &isnull); -+ if (!isnull) -+ *tts_security = DatumGetObjectId(datum); -+ } -+} -+#else /* SECURITY_SYSATTR_NAME */ -+ -+bool pgaceIsSecuritySystemColumn(int attrno) { -+ return false; -+} -+ -+void pgaceTransformSelectStmt(List *targetList) { -+ /* do nothing */ -+} -+ -+void pgaceTransformInsertStmt(List **p_icolumns, -+ List **p_attrnos, -+ List *targetList) { -+ /* do nothing */ -+} -+ -+void pgaceFetchSecurityAttribute(JunkFilter *junkfilter, -+ TupleTableSlot *slot, -+ Oid *tts_security) { -+ /* do nothing */ -+} -+#endif /* SECURITY_SYSATTR_NAME */ -+ -+/***************************************************************************** -+ * Extended SQL statements support -+ *****************************************************************************/ -+ -+/* CREATE TABLE with explicit CONTEXT */ -+List *pgaceRelationAttrList(CreateStmt *stmt) -+{ -+ List *result = NIL; -+ ListCell *l; -+ DefElem *defel, *newel; -+ -+ if (stmt->pgaceItem) { -+ defel = (DefElem *) stmt->pgaceItem; -+ -+ Assert(IsA(defel, DefElem)); -+ if (!pgaceIsGramSecurityItem(defel)) -+ elog(ERROR, "node is not a pgace security item"); -+ newel = makeDefElem(NULL, (Node *) copyObject(defel)); -+ result = lappend(result, newel); -+ } -+ -+ foreach (l, stmt->tableElts) { -+ ColumnDef *cdef = (ColumnDef *) lfirst(l); -+ defel = (DefElem *) cdef->pgaceItem; -+ -+ if (defel) { -+ Assert(IsA(defel, DefElem)); -+ if (!pgaceIsGramSecurityItem(defel)) -+ elog(ERROR, "node is not a pgace security item"); -+ newel = makeDefElem(pstrdup(cdef->colname), -+ (Node *) copyObject(defel)); -+ result = lappend(result, newel); -+ } -+ } -+ return result; -+} -+ -+void pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, List *pgace_attr_list) { -+ ListCell *l; -+ -+ foreach (l, pgace_attr_list) { -+ DefElem *defel = (DefElem *) lfirst(l); -+ -+ if (!defel->defname) { -+ Assert(pgaceIsGramSecurityItem((DefElem *)defel->arg)); -+ pgaceGramCreateRelation(rel, tuple, (DefElem *)defel->arg); -+ break; -+ } -+ } -+} -+ -+void pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple, List *pgace_attr_list) { -+ Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple); -+ ListCell *l; -+ -+ foreach (l, pgace_attr_list) { -+ DefElem *defel = lfirst(l); -+ -+ if (!defel->defname) -+ continue; /* for table */ -+ if (!strcmp(defel->defname, NameStr(attr->attname))) { -+ Assert(pgaceIsGramSecurityItem((DefElem *)defel->arg)); -+ pgaceGramCreateAttribute(rel, tuple, (DefElem *)defel->arg); -+ break; -+ } -+ } -+} -+ -+/* ALTER [ALTER ] CONTEXT = 'xxx' statement */ -+static void alterRelationCommon(Relation rel, DefElem *defel) { -+ Relation pg_class; -+ HeapTuple tuple; -+ -+ pg_class = heap_open(RelationRelationId, RowExclusiveLock); -+ -+ tuple = SearchSysCacheCopy(RELOID, -+ ObjectIdGetDatum(RelationGetRelid(rel)), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation '%s'", RelationGetRelationName(rel)); -+ pgaceGramAlterRelation(rel, tuple, defel); -+ -+ simple_heap_update(pg_class, &tuple->t_self, tuple); -+ CatalogUpdateIndexes(pg_class, tuple); -+ -+ heap_freetuple(tuple); -+ heap_close(pg_class, RowExclusiveLock); -+} -+ -+static void alterAttributeCommon(Relation rel, char *colName, DefElem *defel) { -+ Relation pg_attr; -+ HeapTuple tuple; -+ -+ pg_attr = heap_open(AttributeRelationId, RowExclusiveLock); -+ -+ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for attribute '%s' of relation '%s'", -+ colName, RelationGetRelationName(rel)); -+ pgaceGramAlterAttribute(rel, tuple, defel); -+ -+ simple_heap_update(pg_attr, &tuple->t_self, tuple); -+ CatalogUpdateIndexes(pg_attr, tuple); -+ -+ heap_freetuple(tuple); -+ heap_close(pg_attr, RowExclusiveLock); -+} -+ -+void pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd) { -+ DefElem *defel = (DefElem *) cmd->def; -+ -+ Assert(IsA(defel, DefElem)); -+ -+ if (!pgaceIsGramSecurityItem(defel)) -+ elog(ERROR, "unsupported pgace security item"); -+ -+ if (!cmd->name) { -+ alterRelationCommon(rel, defel); -+ } else { -+ alterAttributeCommon(rel, cmd->name, defel); -+ } -+} -+ -+/***************************************************************************** -+ * security_label type input/output handler -+ *****************************************************************************/ -+static Oid early_security_label_to_sid(char *seclabel); -+static char *early_sid_to_security_label(Oid sid); -+#define EARLY_PG_SECURITY "global/pg_security.bootstrap" -+ -+static bool pg_security_is_available() { -+ /* -1 : early mode, 0: now in transfer, 1: available */ -+ static int pg_security_state = -1; -+ char fname[MAXPGPATH]; -+ FILE *filp; -+ -+ if (pg_security_state > 0) -+ return true; -+ if (IsBootstrapProcessingMode() || pg_security_state==0) -+ return false; -+ /* -+ * if initial setting up was not done, the cache file is remaining. -+ * so we have to insert its contains into pg_selinux. -+ * we can make decision of whether it already done, or not, by looking -+ * the existance of 'EARLY_PG_SECURITY'. -+ */ -+ snprintf(fname, sizeof(fname), "%s/%s", DataDir, EARLY_PG_SECURITY); -+ filp = fopen(fname, "rb"); -+ if (filp) { -+ Relation rel; -+ CatalogIndexState ind; -+ HeapTuple tuple; -+ char buffer[1024]; -+ Oid secoid, metaoid; -+ Datum value; -+ char isnull; -+ -+ pg_security_state = 0; -+ -+ PG_TRY(); -+ { -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ ind = CatalogOpenIndexes(rel); -+ while (fscanf(filp, "%u %s", &secoid, buffer) == 2) { -+ metaoid = early_security_label_to_sid(pgaceSecurityLabelOfLabel(buffer)); -+ -+ value = DirectFunctionCall1(textin, CStringGetDatum(buffer)); -+ isnull = ' '; -+ tuple = heap_formtuple(RelationGetDescr(rel), &value, &isnull); -+ -+ HeapTupleSetOid(tuple, secoid); -+ HeapTupleSetSecurity(tuple, metaoid); -+ -+ simple_heap_insert(rel, tuple); -+ CatalogIndexInsert(ind, tuple); -+ -+ heap_freetuple(tuple); -+ } -+ CatalogCloseIndexes(ind); -+ heap_close(rel, RowExclusiveLock); -+ -+ CommandCounterIncrement(); -+ } -+ PG_CATCH(); -+ { -+ fclose(filp); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ fclose(filp); -+ if (unlink(fname) != 0) -+ elog(ERROR, "PGACE: could not unlink '%s'", fname); -+ } -+ pg_security_state = 1; -+ -+ return true; -+} -+ -+static Oid early_security_label_to_sid(char *seclabel) -+{ -+ char fname[MAXPGPATH], buffer[1024]; -+ Oid sid, minsid = SecurityRelationId; -+ FILE *filp; -+ -+ snprintf(fname, sizeof(fname), "%s/%s", DataDir, EARLY_PG_SECURITY); -+ filp = fopen(fname, "a+b"); -+ if (!filp) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("could not open '%s'", fname))); -+ flock(fileno(filp), LOCK_EX); -+ while (fscanf(filp, "%u %s", &sid, buffer) == 2) { -+ if (!strcmp(seclabel, buffer)) { -+ fclose(filp); -+ return sid; -+ } -+ if (sid < minsid) -+ minsid = sid; -+ } -+ sid = minsid - 1; -+ fprintf(filp, "%u %s\n", sid, seclabel); -+ fclose(filp); -+ -+ return sid; -+} -+ -+static char *early_sid_to_security_label(Oid sid) -+{ -+ char fname[MAXPGPATH], buffer[1024], *seclabel; -+ FILE *filp; -+ Oid __sid; -+ -+ snprintf(fname, sizeof(fname), "%s/%s", DataDir, EARLY_PG_SECURITY); -+ filp = fopen(fname, "rb"); -+ if (!filp) -+ goto not_found; -+ -+ flock(fileno(filp), LOCK_SH); -+ while (fscanf(filp, "%u %s", &__sid, buffer) == 2) { -+ if (sid == __sid) { -+ fclose(filp); -+ return pstrdup(buffer); -+ } -+ } -+ fclose(filp); -+ -+not_found: -+ seclabel = pgaceSecurityLabelCheckValid(NULL); -+ elog(seclabel ? NOTICE : ERROR, -+ "PGACE: No text representation for sid = %u", sid); -+ return seclabel; -+} -+ -+static Oid get_security_label_oid(Relation rel, CatalogIndexState ind, char *new_label) -+{ -+ /* rel has to be opened with RowExclusiveLock */ -+ char *mlabel_str, *__mlabel_str; -+ Datum mlabel_text; -+ HeapTuple tuple; -+ Oid label_oid; -+ -+ mlabel_str = pgaceSecurityLabelOfLabel(new_label); -+ __mlabel_str = pgaceSecurityLabelCheckValid(mlabel_str); -+ if (mlabel_str != __mlabel_str) -+ elog(NOTICE, "PGACE: '%s' is not a valid security label," -+ " '%s' is applied instead.", mlabel_str, __mlabel_str); -+ -+ /* 1. lookup syscache */ -+ mlabel_text = DirectFunctionCall1(textin, CStringGetDatum(mlabel_str)); -+ tuple = SearchSysCache(SECURITYLABEL, -+ mlabel_text, -+ 0, 0, 0); -+ if (HeapTupleIsValid(tuple)) { -+ label_oid = HeapTupleGetSecurity(tuple); -+ ReleaseSysCache(tuple); -+ } else { -+ /* 2. lookup table on SnapshotSelf */ -+ SysScanDesc scan; -+ ScanKeyData skey; -+ -+ ScanKeyInit(&skey, -+ Anum_pg_security_seclabel, -+ BTEqualStrategyNumber, F_TEXTEQ, -+ PointerGetDatum(mlabel_text)); -+ scan = systable_beginscan(rel, SecuritySeclabelIndexId, -+ true, SnapshotSelf, 1, &skey); -+ tuple = systable_getnext(scan); -+ if (HeapTupleIsValid(tuple)) { -+ label_oid = HeapTupleGetSecurity(tuple); -+ } else { -+ /* 3. insert a new tuple into pg_security */ -+ Datum value = PointerGetDatum(mlabel_text); -+ char isnull = ' '; -+ Oid meta_oid; -+ -+ tuple = heap_formtuple(RelationGetDescr(rel), -+ &value, &isnull); -+ meta_oid = GetNewOid(rel); -+ HeapTupleSetOid(tuple, meta_oid); -+ HeapTupleSetSecurity(tuple, meta_oid); -+ -+ label_oid = simple_heap_insert(rel, tuple); -+ Assert(label_oid == meta_oid); -+ -+ CatalogIndexInsert(ind, tuple); -+ } -+ systable_endscan(scan); -+ } -+ return label_oid; -+} -+ -+static Oid security_label_to_sid(char *label_str) -+{ -+ Datum label_text; -+ Oid label_oid; -+ HeapTuple tuple; -+ -+ if (!pg_security_is_available()) -+ return early_security_label_to_sid(label_str); -+ -+ /* 1. lookup system cache first */ -+ label_text = DirectFunctionCall1(textin, CStringGetDatum(label_str)); -+ tuple = SearchSysCache(SECURITYLABEL, -+ label_text, -+ 0, 0, 0); -+ if (HeapTupleIsValid(tuple)) { -+ label_oid = HeapTupleGetOid(tuple); -+ ReleaseSysCache(tuple); -+ } else { -+ /* 2. lookup within the current command ID */ -+ Relation rel; -+ SysScanDesc scan; -+ ScanKeyData skey; -+ Oid meta_oid; -+ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ ScanKeyInit(&skey, -+ Anum_pg_security_seclabel, -+ BTEqualStrategyNumber, F_TEXTEQ, -+ PointerGetDatum(label_text)); -+ scan = systable_beginscan(rel, SecuritySeclabelIndexId, -+ true, SnapshotSelf, 1, &skey); -+ tuple = systable_getnext(scan); -+ if (HeapTupleIsValid(tuple)) { -+ label_oid = HeapTupleGetOid(tuple); -+ } else { -+ CatalogIndexState ind; -+ Datum value = PointerGetDatum(label_text); -+ char isnull = ' '; -+ -+ ind = CatalogOpenIndexes(rel); -+ -+ tuple = heap_formtuple(RelationGetDescr(rel), -+ &value, &isnull); -+ meta_oid = get_security_label_oid(rel, ind, label_str); -+ HeapTupleSetSecurity(tuple, meta_oid); -+ -+ label_oid = simple_heap_insert(rel, tuple); -+ -+ CatalogIndexInsert(ind, tuple); -+ CatalogCloseIndexes(ind); -+ } -+ systable_endscan(scan); -+ heap_close(rel, RowExclusiveLock); -+ } -+ return label_oid; -+} -+ -+static char *sid_to_security_label(Oid sid) -+{ -+ HeapTuple tuple; -+ Datum tcon; -+ char *seclabel; -+ bool isnull, syscache = true; -+ -+ if (!pg_security_is_available()) -+ return early_sid_to_security_label(sid); -+ -+ tuple = SearchSysCache(SECURITYOID, -+ ObjectIdGetDatum(sid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) { -+ Relation rel; -+ SysScanDesc scan; -+ ScanKeyData skey; -+ -+ syscache = false; -+ rel = heap_open(SecurityRelationId, AccessShareLock); -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(sid)); -+ scan = systable_beginscan(rel, SecurityOidIndexId, -+ true, SnapshotSelf, 1, &skey); -+ tuple = systable_getnext(scan); -+ if (HeapTupleIsValid(tuple)) -+ tuple = heap_copytuple(tuple); -+ systable_endscan(scan); -+ heap_close(rel, AccessShareLock); -+ -+ if (!HeapTupleIsValid(tuple)) { -+ seclabel = pgaceSecurityLabelCheckValid(NULL); -+ elog(seclabel ? NOTICE : ERROR, -+ "PGACE: No text representation for sid = %u", sid); -+ return seclabel; -+ } -+ } -+ tcon = SysCacheGetAttr(SECURITYOID, -+ tuple, -+ Anum_pg_security_seclabel, -+ &isnull); -+ seclabel = DatumGetCString(DirectFunctionCall1(textout, -+ PointerGetDatum(tcon))); -+ if (syscache) -+ ReleaseSysCache(tuple); -+ -+ return seclabel; -+} -+ -+/* security_label_in -- security_label input function */ -+Datum -+security_label_in(PG_FUNCTION_ARGS) -+{ -+ char *label = PG_GETARG_CSTRING(0); -+ char *__label; -+ -+ label = pgaceSecurityLabelIn(label); -+ __label = pgaceSecurityLabelCheckValid(label); -+ if (label != __label) -+ elog(ERROR, "PGACE: '%s' is not a valid security label", label); -+ -+ PG_RETURN_OID(security_label_to_sid(label)); -+} -+ -+/* security_label_out -- security_label output function */ -+Datum -+security_label_out(PG_FUNCTION_ARGS) -+{ -+ Oid sid = PG_GETARG_OID(0); -+ char *label = sid_to_security_label(sid); -+ char *__label = pgaceSecurityLabelCheckValid(label); -+ if (label != __label) -+ elog(NOTICE, "PGACE: '%s' is not a valid security label," -+ " '%s' is applied instead.", label, __label); -+ PG_RETURN_CSTRING(pgaceSecurityLabelOut(__label)); -+} -+ -+/* security_label_raw_in -- security_label input function in raw format */ -+Datum -+security_label_raw_in(PG_FUNCTION_ARGS) -+{ -+ char *label = PG_GETARG_CSTRING(0); -+ char *__label; -+ -+ __label = pgaceSecurityLabelCheckValid(label); -+ if (label != __label) -+ elog(ERROR, "PGACE: '%s' is not a valid security label", label); -+ -+ PG_RETURN_OID(security_label_to_sid(label)); -+} -+ -+/* security_label_raw_out -- security_label output function in raw format */ -+Datum -+security_label_raw_out(PG_FUNCTION_ARGS) -+{ -+ Oid sid = PG_GETARG_OID(0); -+ char *label = sid_to_security_label(sid); -+ char *__label = pgaceSecurityLabelCheckValid(label); -+ -+ if (label != __label) -+ elog(NOTICE, "PGACE: '%s' is not a valid security label," -+ " '%s' is applied instead.", label, __label); -+ PG_RETURN_CSTRING(__label); -+} -+ -+/* text_to_security_label -- security_label cast function */ -+Datum -+text_to_security_label(PG_FUNCTION_ARGS) -+{ -+ text *t = PG_GETARG_TEXT_P(0); -+ Datum seclabel; -+ -+ seclabel = DirectFunctionCall1(textout, -+ PointerGetDatum(t)); -+ return DirectFunctionCall1(security_label_in, seclabel); -+} -+ -+/* security_label_to_text -- security_label cast function */ -+Datum -+security_label_to_text(PG_FUNCTION_ARGS) -+{ -+ Oid sid = PG_GETARG_OID(0); -+ Datum seclabel; -+ -+ seclabel = DirectFunctionCall1(security_label_out, -+ ObjectIdGetDatum(sid)); -+ return DirectFunctionCall1(textin, seclabel); -+} -+ -+/***************************************************************************** -+ * Set/Get security attribute of Large Object -+ *****************************************************************************/ -+Datum -+lo_get_security(PG_FUNCTION_ARGS) -+{ -+ Oid loid = PG_GETARG_OID(0); -+ Oid lo_security = InvalidOid; -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc sd; -+ HeapTuple tuple; -+ bool found = false; -+ -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ -+ rel = heap_open(LargeObjectRelationId, AccessShareLock); -+ -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotNow, 1, &skey); -+ -+ while ((tuple = systable_getnext(sd)) != NULL) { -+ lo_security = HeapTupleGetSecurity(tuple); -+ pgaceLargeObjectGetSecurity(tuple); -+ found = true; -+ break; -+ } -+ systable_endscan(sd); -+ -+ heap_close(rel, AccessShareLock); -+ -+ if (!found) -+ elog(ERROR, "large object %u does not exist", loid); -+ -+ PG_RETURN_OID(lo_security); -+} -+ -+Datum -+lo_set_security(PG_FUNCTION_ARGS) -+{ -+ Oid loid = PG_GETARG_OID(0); -+ Oid lo_security = PG_GETARG_OID(1); -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc sd; -+ HeapTuple tuple, newtup; -+ CatalogIndexState indstate; -+ bool found = false; -+ -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ -+ rel = heap_open(LargeObjectRelationId, RowExclusiveLock); -+ -+ indstate = CatalogOpenIndexes(rel); -+ -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotNow, 1, &skey); -+ -+ while ((tuple = systable_getnext(sd)) != NULL) { -+ newtup = heap_copytuple(tuple); -+ if (!found) -+ pgaceLargeObjectSetSecurity(newtup, lo_security); -+ HeapTupleSetSecurity(newtup, lo_security); -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ CatalogUpdateIndexes(rel, newtup); -+ found = true; -+ } -+ systable_endscan(sd); -+ CatalogCloseIndexes(indstate); -+ heap_close(rel, RowExclusiveLock); -+ -+ CommandCounterIncrement(); -+ -+ if (!found) -+ elog(ERROR, "large object %u does not exist.", loid); -+ -+ PG_RETURN_BOOL(true); -+} -diff -rpNU3 base/src/backend/security/pgaceHooks.c pgace/src/backend/security/pgaceHooks.c ---- base/src/backend/security/pgaceHooks.c 1970-01-01 09:00:00.000000000 +0900 -+++ pgace/src/backend/security/pgaceHooks.c 2008-03-13 23:25:01.000000000 +0900 -@@ -0,0 +1,628 @@ -+/* -+ * src/backend/security/pgaceHooks.c -+ * Dummy functions of PostgreSQL Access Control Extension -+ * when no users enables the framework. -+ * Copyright 2007 KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "security/pgace.h" -+ -+/****************************************************************** -+ * Initialize / Finalize related hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceShmemSize() have to return the size of shared memory segment -+ * required by PGACE implementation. If no shared memory segment needed, -+ * it should return 0. -+ */ -+Size pgaceShmemSize(void) -+{ -+ return (Size) 0; -+} -+ -+/* -+ * pgaceInitialize() is called when a new PostgreSQL instance is generated. -+ * A PGACE implementation can initialize itself. -+ * -+ * @is_bootstrap : true, if bootstraping mode. -+ */ -+void pgaceInitialize(bool is_bootstrap) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceInitializePostmaster() is called when a postmaster server process -+ * is started up. If it returns false, the server starting up process -+ * will be aborted. -+ */ -+bool pgaceInitializePostmaster(void) -+{ -+ return true; -+} -+ -+/* -+ * pgaceFinalizePostmaster() is called when a postmaster server process -+ * is just ending up. -+ */ -+void pgaceFinalizePostmaster(void) -+{ -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * SQL proxy hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceProxyQuery() is called just after query rewrite phase. -+ * PGACE implementation can modify the query trees in this hook, -+ * if necessary. -+ * -+ * @queryList : a list of Query typed objects. -+ */ -+List *pgaceProxyQuery(List *queryList) -+{ -+ return queryList; -+} -+ -+/* -+ * pgacePortalStart() is called on the top of PortalStart(). -+ * -+ * @portal : a Portal object currently executed. -+ */ -+void pgacePortalStart(Portal portal) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceExecutorStart() is called on the top of ExecutorStart(). -+ * -+ * @queryDesc : a QueryDesc object given to ExecutorStart(). -+ * @eflags : eflags valus given to ExecutorStart(). -+ * if EXEC_FLAG_EXPLAIN_ONLY is set, no real access will run. -+ */ -+void pgaceExecutorStart(QueryDesc *queryDesc, int eflags) -+{ -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * HeapTuple modification hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceHeapTupleInsert() is called when a new tuple attempt to be inserted. -+ * If it returns false, this insertion of a new tuple will be cancelled. -+ * However, it does not generate any error. -+ * -+ * @rel : the target relation -+ * @tuple : the tuple attmpt to be inserted -+ * @is_internal : true, if this operation is invoked by system internal processes. -+ * @with_returning : true, if INSERT statement has RETURNING clause. -+ */ -+bool pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, -+ bool is_internal, bool with_returning) -+{ -+ return true; -+} -+ -+/* -+ * pgaceHeapTupleUpdate() is called when a tuple attempt to be updated. -+ * If it returns false, this update will be cancelled. -+ * However, it does not generate any error. -+ * -+ * @rel : the target relation -+ * @otid : ItemPointer of the tuple to be updated -+ * @newtup : the new contains of the updated tuple -+ * @is_internal : true, if this operation is invoked by system internal processes. -+ * @with_returning : true, if INSERT statement has RETURNING clause. -+ */ -+bool pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, -+ bool is_internal, bool with_returning) -+{ -+ return true; -+} -+ -+/* -+ * pgaceHeapTupleDelete() is called when a tuple attempt to be deleted. -+ * If it returns false, this deletion will be cancelled. -+ * However, it does not generate any error. -+ * -+ * @rel : the target relation -+ * @otid : ItemPointer of the tuple to be deleted -+ * @is_internal : true, if this operation is invoked by system internal processes. -+ * @with_returning : true, if INSERT statement has RETURNING clause. -+ */ -+bool pgaceHeapTupleDelete(Relation rel, ItemPointer otid, -+ bool is_internal, bool with_returning) -+{ -+ return true; -+} -+ -+/****************************************************************** -+ * Extended SQL statement hooks -+ ******************************************************************/ -+/* -+ * PGACE implementation can use pgaceGramSecurityItem() hook to extend -+ * SQL statement for security purpose. This hook is deployed on parser/gram.y -+ * as a part of the SQL grammer. If no SQL extension is necessary, it has to -+ * return NULL to cause yyerror(). -+ * -+ * @defname : given string -+ * @value : given string -+ */ -+DefElem *pgaceGramSecurityItem(char *defname, char *value) -+{ -+ return NULL; -+} -+ -+/* -+ * PGACE implementation has to return true, if the given DefElem holds -+ * security item generated in pgaceGramSecurityItem(). false, if any other. -+ * -+ * @defel : given DefElem object -+ */ -+bool pgaceIsGramSecurityItem(DefElem *defel) -+{ -+ return false; -+} -+ -+/* -+ * pgaceGramCreateRelation() is called to modify a tuple just before inserting -+ * a new relation with CREATE TABLE, if extended statement is used. -+ * -+ * @rel : pg_class relation -+ * @tuple : a tuple of new relation -+ * @defel : extended statement -+ */ -+void pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGramCreateAttribute() is called to modify a tuple just before inserting -+ * a new attribute with CREATE TABLE, if extended statement is used. -+ * -+ * @rel : pg_attribute relation -+ * @tuple : a tuple of new attribute -+ * @defel : extended statement -+ */ -+void pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGramAlterRelation() is called to modify a tuple just before updating -+ * a relation with ALTER TABLE, if extended statement is used. -+ * -+ * @rel : target relation -+ * @tuple : a tuple of new relation -+ * @defel : extended statement -+ */ -+void pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGramAlterAttribute() is called to modify a tuple just before updating -+ * an attribute with ALTER TABLE, if extended statement is specified. -+ * -+ * @rel : target relation -+ * @tuple : a tuple of new attribute -+ * @defel : extended statement -+ */ -+void pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGramCreateDatabase() is called to modify a tuple just before inserting -+ * a new database with CREATE DATABASE, if extended statement is used. -+ * -+ * @rel : pg_database relation -+ * @tuple : a tuple of the new database -+ * @defel : extended statement -+ */ -+void pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGramAlterDatabase() is called to modify a tuple just before updating -+ * a database with ALTER DATABASE, if extended statement is used. -+ * -+ * @rel : pg_database relation -+ * @tuple : a tuple of the updated database -+ * @defel : extended statement -+ */ -+void pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGramCreateFunction() is called to modify a tuple just before inserting -+ * a new function into pg_proc, if extended statement is used. -+ * -+ * @rel : pg_proc relation -+ * @tuple : a tuple of the new function -+ * @defel : extended statement -+ */ -+void pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGramAlterFunction() is called to modify a tuple just before updating -+ * a function with ALTER FUNCTION, if extended statement is used. -+ * -+ * @rel : pg_proc relation -+ * @tuple : a tuple of the function -+ * @defel : extended statement -+ */ -+void pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * DATABASE related hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceSetDatabaseParam() is called when clients tries to set GUC variables -+ * -+ * @name : The name of GUC variable -+ * @argstr : The new valus of GUC variable. If argstr is NULL, it means -+ * clients tries to reset the variable. -+ */ -+void pgaceSetDatabaseParam(const char *name, char *argstring) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceGetDatabaseParam() is called when clients tries to refer GUC variables -+ * -+ * @name : The name of GUC variable -+ */ -+void pgaceGetDatabaseParam(const char *name) -+{ -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * FUNCTION related hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceCallFunction() is called just before executing SQL function -+ * as a part of query. -+ * -+ * @finfo : FmgrInfo object for the target function -+ */ -+void pgaceCallFunction(FmgrInfo *finfo) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgaceCallFunctionTrigger() is called just before executing -+ * trigger function. -+ * If it returns false, the trigger function will not be called and caller -+ * receives NULL tuple as a result. In the case when Before-Row triggers, -+ * it means the current operations on the tuple should be skipped. -+ * -+ * @finfo : FmgrInfo object for the target function -+ * @tgdata : TriggerData object for the current trigger invokation -+ */ -+bool pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata) -+{ -+ return true; -+} -+ -+/* -+ * pgaceCallFunctionFastPath() is called just before executing -+ * SQL function in the fast path. -+ * -+ * @finfo : FmgrInfo object for the target function -+ */ -+void pgaceCallFunctionFastPath(FmgrInfo *finfo) -+{ -+ /* do nothing */ -+} -+ -+/* -+ * pgacePreparePlanCheck() is called before foreign key/primary key constraint checks, -+ * at ri_PlanCheck(). PGACE implementation can return its opaque data for any purpose. -+ * -+ * @rel : the target relation in which a constraint is configured -+ */ -+Datum pgacePreparePlanCheck(Relation rel) -+{ -+ return (Datum) 0; -+} -+ -+/* -+ * pgaceRestorePlanCheck() is called after foreign key/primary key constraint checks, -+ * at ri_PlanCheck(). PGACE implementation can use an opaque data generated in the above -+ * pgacePreparePlanCheck(). -+ * -+ * @rel : the target relation in which a constraint is configured -+ * @pgace_saved : an opaque data returned from pgacePreparePlanCheck() -+ */ -+void pgaceRestorePlanCheck(Relation rel, Datum pgace_saved) -+{ -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * TABLE related hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceLockTable() is called when explicit LOCK statement used. -+ * -+ * @relid : the target relation id -+ */ -+void pgaceLockTable(Oid relid) -+{ -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * COPY TO/COPY FROM statement hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceCopyTable() is called when COPY TO/COPY FROM statement is processed -+ * -+ * @rel : the target relation -+ * @attNumList : the list of attribute numbers -+ * @isFrom : true, if the given statement is 'COPY FROM' -+ */ -+void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) { -+ /* do nothing */ -+} -+ -+/* -+ * pgaceCopyToTuple() is called to check whether the given tuple should be -+ * filtered, or not in the process of COPY TO statement. -+ * If it returns false, the given tuple will be filtered from the result set -+ * -+ * @rel : the target relation -+ * @attNumList : the list of attribute numbers -+ * @tuple : the target tuple -+ */ -+bool pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) { -+ return true; -+} -+ -+/****************************************************************** -+ * Loadable shared library module hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceLoadSharedModule() is called just before load a shared library -+ * module. -+ * -+ * @filename : full path name of the shared library module -+ */ -+void pgaceLoadSharedModule(const char *filename) { -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * Binary Large Object (BLOB) hooks -+ ******************************************************************/ -+ -+/* -+ * pgaceLargeObjectGetSecurity() is called when lo_get_security() is executed -+ * It returns its security attribute. -+ * -+ * @tuple : a tuple which is a part of the target largeobject. -+ */ -+void pgaceLargeObjectGetSecurity(HeapTuple tuple) { -+ elog(ERROR, "PGACE: There is no guest module."); -+} -+ -+/* -+ * pgaceLargeObjectSetSecurity() is called when lo_set_security() is executed -+ * -+ * @tuple : a tuple which is a part of the target largeobject. -+ * @lo_security : new security attribute specified -+ */ -+void pgaceLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security) { -+ elog(ERROR, "PGACE: There is no guest module."); -+} -+ -+/* -+ * pgaceLargeObjectCreate() is called when a new large object is created -+ * -+ * @rel : pg_largeobject relation opened with RowExclusiveLock -+ * @tuple : a new tuple for the new large object -+ */ -+void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) { -+ /* do nothing */ -+} -+ -+/* -+ * pgaceLargeObjectDrop() is called when a large object is dropped once for -+ * a large object -+ * -+ * @rel : pg_largeobject relation opened with RowExclusiveLock -+ * @tuple : one of the tuples within the target large object -+ */ -+void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple) { -+ /* do nothing */ -+} -+ -+/* -+ * pgaceLargeObjectRead is called when they read from a large object -+ * -+ * @rel : pg_largeobject relation opened with AccessShareLock -+ * @tuple : the head tuple within the given large object -+ */ -+void pgaceLargeObjectRead(Relation rel, HeapTuple tuple) { -+ /* do nothing */ -+} -+ -+/* -+ * pgaceLargeObjectWrite() is called when they write to a large object -+ * -+ * @rel : pg_largeobject relation opened with RowExclusiveLock -+ * @newtup : the head tuple within the given large object -+ * @oldtup : the head tuple in older version, if exist -+ */ -+void pgaceLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup) { -+ /* do nothing */ -+} -+ -+/* -+ * pgaceLargeObjectTruncate() is called when they truncate a large object. -+ * -+ * @rel : pg_largeobject relation opened with RowExclusiveLock -+ * @loid : large object identifier -+ * @headtup : the head tuple to be truncated. NULL means this BLOB will be expanded. -+ */ -+void pgaceLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup) { -+ /* do nothing */ -+} -+ -+/* -+ * pgaceLargeObjectImport() is called when lo_import() is processed -+ * -+ * @fd : file descriptor to be inported -+ */ -+void pgaceLargeObjectImport(int fd) { -+ /* do nothing */ -+} -+ -+/* -+ * pgaceLargeObjectExport() is called when lo_import() is processed -+ * -+ * @fd : file descriptor to be exported -+ * @loid : large object to be exported -+ */ -+void pgaceLargeObjectExport(int fd, Oid loid) { -+ /* do nothing */ -+} -+ -+/****************************************************************** -+ * Security Label hooks -+ ******************************************************************/ -+ -+/* -+ * PGACE implementation can use pgaceSecurityLabelIn() hook to translate -+ * a input security label from external representation into internal one. -+ * If no translation is necessary, it has to return @seclabel as is. -+ * -+ * @seclabel : security label being input -+ */ -+char *pgaceSecurityLabelIn(char *seclabel) -+{ -+ return seclabel; -+} -+ -+/* -+ * PGACE implementation can use pgaceSecurityLabelOut() hook to translate -+ * a security label in internal representation into external one. -+ * If no translation is necessary, it has to return @seclabel as is. -+ * -+ * @seclabel : security label being output -+ */ -+char *pgaceSecurityLabelOut(char *seclabel) -+{ -+ return seclabel; -+} -+ -+/* -+ * pgaceSecurityLabelCheckValid() checks whether the @seclabel is valid or not. -+ * In addition, it can returns an alternative security label, if possible. -+ * -+ * It has to return @seclabel as is, if @seclabel is a valid security label. -+ * It can return an alternative label, if @seclabel is NOT a valid one and -+ * there is an alternative. In any other case, it returns NULL. -+ * @seclabel may be NULL. In this case, @seclabel is always invalid. -+ * -+ * @seclabel : security label to be checked -+ */ -+char *pgaceSecurityLabelCheckValid(char *seclabel) -+{ -+ return seclabel; -+} -+ -+/* -+ * pgaceSecurityLabelOfLabel() returns the security attribute of a newly -+ * generated tuple within pg_security -+ * -+ * @new_label : a text representation of security context which will be newly -+ * inserted into pg_security. -+ */ -+char *pgaceSecurityLabelOfLabel(char *new_label) -+{ -+ return pstrdup("unlabeled"); -+} -+ -+/****************************************************************** -+ * Extended node type hooks -+ ******************************************************************/ -+ -+/* -+ * If PGACE implementation requires new node type, a method to copy object. -+ * pgaceCopyObject() provides a hook to copy new node typed object. -+ * If a given object (@orig) has a tag extended by PGACE implementation, -+ * it have to copy and return it. -+ * If it returns NULL, @orig is not available for the PGACE implementation. -+ * -+ * @orig : a object which to copy -+ */ -+Node *pgaceCopyObject(Node *orig) -+{ -+ return NULL; -+} -+ -+/* -+ * pgaceOutObject() provides a hook to translate a object to text representation. -+ * If a given object (@node) has a tag extended by PGACE implementation, it have -+ * to put a text representation into StringInfo. -+ * If it returns false, @node is not available for the PGACE implementation. -+ * -+ * @str : StringInfo which to put the text representation -+ * @node : a object that text representation is required -+ */ -+bool pgaceOutObject(StringInfo str, Node *node) -+{ -+ return false; -+} -+ -+/* -+ * pgaceReadObject() provides a hook to read a text representation of an object. -+ * If a given token is a tag extended by PGACE implementation, it have to create -+ * an object same as original one. -+ * -+ * @token : a tag for the object -+ */ -+void *pgaceReadObject(char *token) -+{ -+ return NULL; -+} -+ -+/****************************************************************** -+ * Extended functions stub -+ ******************************************************************/ -+ -+/* -+ * In this section, you can put function stubs when your security -+ * module is not activated. -+ */ -diff -rpNU3 base/src/backend/storage/ipc/ipci.c pgace/src/backend/storage/ipc/ipci.c ---- base/src/backend/storage/ipc/ipci.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/storage/ipc/ipci.c 2008-01-08 01:39:49.000000000 +0900 -@@ -25,6 +25,7 @@ - #include "postmaster/autovacuum.h" - #include "postmaster/bgwriter.h" - #include "postmaster/postmaster.h" -+#include "security/pgace.h" - #include "storage/freespace.h" - #include "storage/ipc.h" - #include "storage/pg_shmem.h" -@@ -117,6 +118,7 @@ CreateSharedMemoryAndSemaphores(bool mak - #ifdef EXEC_BACKEND - size = add_size(size, ShmemBackendArraySize()); - #endif -+ size = add_size(size, pgaceShmemSize()); - - /* freeze the addin request size and include it */ - addin_request_allowed = false; -diff -rpNU3 base/src/backend/storage/large_object/inv_api.c pgace/src/backend/storage/large_object/inv_api.c ---- base/src/backend/storage/large_object/inv_api.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/storage/large_object/inv_api.c 2008-03-19 10:08:35.000000000 +0900 -@@ -39,6 +39,7 @@ - #include "catalog/pg_largeobject.h" - #include "commands/comment.h" - #include "libpq/libpq-fs.h" -+#include "security/pgace.h" - #include "storage/large_object.h" - #include "utils/fmgroids.h" - #include "utils/resowner.h" -@@ -445,6 +446,10 @@ inv_read(LargeObjectDesc *obj_desc, char - - if (HeapTupleHasNulls(tuple)) /* paranoia */ - elog(ERROR, "null field found in pg_largeobject"); -+ -+ if (!nread) -+ pgaceLargeObjectRead(lo_heap_r, tuple); -+ - data = (Form_pg_largeobject) GETSTRUCT(tuple); - - /* -@@ -633,6 +638,8 @@ inv_write(LargeObjectDesc *obj_desc, con - replace[Anum_pg_largeobject_data - 1] = 'r'; - newtup = heap_modifytuple(oldtuple, RelationGetDescr(lo_heap_r), - values, nulls, replace); -+ if (nwritten - n == 0) -+ pgaceLargeObjectWrite(lo_heap_r, newtup, oldtuple); - simple_heap_update(lo_heap_r, &newtup->t_self, newtup); - CatalogIndexInsert(indstate, newtup); - heap_freetuple(newtup); -@@ -676,6 +683,8 @@ inv_write(LargeObjectDesc *obj_desc, con - values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); - values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); - newtup = heap_formtuple(lo_heap_r->rd_att, values, nulls); -+ if (nwritten - n == 0) -+ pgaceLargeObjectWrite(lo_heap_r, newtup, NULL); - simple_heap_insert(lo_heap_r, newtup); - CatalogIndexInsert(indstate, newtup); - heap_freetuple(newtup); -@@ -756,6 +765,7 @@ inv_truncate(LargeObjectDesc *obj_desc, - olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple); - Assert(olddata->pageno >= pageno); - } -+ pgaceLargeObjectTruncate(lo_heap_r, obj_desc->id, oldtuple); - - /* - * If we found the page of the truncation point we need to truncate the -diff -rpNU3 base/src/backend/tcop/fastpath.c pgace/src/backend/tcop/fastpath.c ---- base/src/backend/tcop/fastpath.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/tcop/fastpath.c 2008-01-08 01:39:49.000000000 +0900 -@@ -26,6 +26,7 @@ - #include "libpq/pqformat.h" - #include "mb/pg_wchar.h" - #include "miscadmin.h" -+#include "security/pgace.h" - #include "tcop/fastpath.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -353,6 +354,8 @@ HandleFunctionRequest(StringInfo msgBuf) - */ - InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, NULL, NULL); - -+ pgaceCallFunctionFastPath(fcinfo.flinfo); -+ - if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) - rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo); - else -diff -rpNU3 base/src/backend/tcop/postgres.c pgace/src/backend/tcop/postgres.c ---- base/src/backend/tcop/postgres.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/tcop/postgres.c 2008-03-19 10:08:35.000000000 +0900 -@@ -53,6 +53,7 @@ - #include "parser/parser.h" - #include "postmaster/autovacuum.h" - #include "rewrite/rewriteHandler.h" -+#include "security/pgace.h" - #include "storage/freespace.h" - #include "storage/ipc.h" - #include "storage/proc.h" -@@ -629,6 +630,9 @@ pg_rewrite_query(Query *query) - { - /* don't rewrite utilities, just dump 'em into result list */ - querytree_list = list_make1(query); -+ -+ /* PGACE rewrite utility query, if necessary */ -+ querytree_list = pgaceProxyQuery(querytree_list); - } - else - { -diff -rpNU3 base/src/backend/tcop/pquery.c pgace/src/backend/tcop/pquery.c ---- base/src/backend/tcop/pquery.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/tcop/pquery.c 2008-01-08 01:39:49.000000000 +0900 -@@ -19,6 +19,7 @@ - #include "commands/prepare.h" - #include "commands/trigger.h" - #include "miscadmin.h" -+#include "security/pgace.h" - #include "tcop/pquery.h" - #include "tcop/tcopprot.h" - #include "tcop/utility.h" -@@ -455,6 +456,9 @@ PortalStart(Portal portal, ParamListInfo - AssertArg(PortalIsValid(portal)); - AssertState(portal->status == PORTAL_DEFINED); - -+ /* PGACE: PosrtalStart hook */ -+ pgacePortalStart(portal); -+ - /* - * Set up global portal context pointers. - */ -diff -rpNU3 base/src/backend/utils/adt/ri_triggers.c pgace/src/backend/utils/adt/ri_triggers.c ---- base/src/backend/utils/adt/ri_triggers.c 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/backend/utils/adt/ri_triggers.c 2008-03-19 10:08:35.000000000 +0900 -@@ -37,6 +37,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_relation.h" - #include "miscadmin.h" -+#include "security/pgace.h" - #include "utils/acl.h" - #include "utils/fmgroids.h" - #include "utils/lsyscache.h" -@@ -3202,6 +3203,7 @@ ri_PlanCheck(const char *querystr, int n - Relation query_rel; - Oid save_userid; - bool save_secdefcxt; -+ Datum save_pgace; - - /* - * The query is always run against the FK table except when this is an -@@ -3219,7 +3221,18 @@ ri_PlanCheck(const char *querystr, int n - SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); - - /* Create the plan */ -- qplan = SPI_prepare(querystr, nargs, argtypes); -+ save_pgace = pgacePreparePlanCheck(query_rel); -+ PG_TRY(); -+ { -+ qplan = SPI_prepare(querystr, nargs, argtypes); -+ } -+ PG_CATCH(); -+ { -+ pgaceRestorePlanCheck(query_rel, save_pgace); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ pgaceRestorePlanCheck(query_rel, save_pgace); - - if (qplan == NULL) - elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr); -diff -rpNU3 base/src/backend/utils/cache/syscache.c pgace/src/backend/utils/cache/syscache.c ---- base/src/backend/utils/cache/syscache.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/utils/cache/syscache.c 2008-01-08 01:39:49.000000000 +0900 -@@ -39,6 +39,7 @@ - #include "catalog/pg_opfamily.h" - #include "catalog/pg_proc.h" - #include "catalog/pg_rewrite.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_statistic.h" - #include "catalog/pg_ts_config.h" - #include "catalog/pg_ts_config_map.h" -@@ -676,7 +677,31 @@ static const struct cachedesc cacheinfo[ - 0 - }, - 1024 -- } -+ }, -+ {SecurityRelationId, /*SECURITYOID */ -+ SecurityOidIndexId, -+ 0, -+ 1, -+ { -+ ObjectIdAttributeNumber, -+ 0, -+ 0, -+ 0 -+ }, -+ 128 -+ }, -+ {SecurityRelationId, /* SECURITYLABEL */ -+ SecuritySeclabelIndexId, -+ 0, -+ 1, -+ { -+ Anum_pg_security_seclabel, -+ 0, -+ 0, -+ 0 -+ }, -+ 128 -+ }, - }; - - static CatCache *SysCache[ -diff -rpNU3 base/src/backend/utils/fmgr/dfmgr.c pgace/src/backend/utils/fmgr/dfmgr.c ---- base/src/backend/utils/fmgr/dfmgr.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/utils/fmgr/dfmgr.c 2008-01-08 01:39:49.000000000 +0900 -@@ -22,6 +22,7 @@ - #include "port/dynloader/win32.h" - #endif - #include "miscadmin.h" -+#include "security/pgace.h" - #include "utils/dynamic_loader.h" - #include "utils/hsearch.h" - -@@ -73,7 +74,7 @@ char *Dynamic_library_path; - static void *internal_load_library(const char *libname); - static void internal_unload_library(const char *libname); - static bool file_exists(const char *name); --static char *expand_dynamic_library_name(const char *name); -+//static char *expand_dynamic_library_name(const char *name); - static void check_restricted_library_name(const char *name); - static char *substitute_libpath_macro(const char *name); - static char *find_in_dynamic_libpath(const char *basename); -@@ -106,6 +107,9 @@ load_external_function(char *filename, c - /* Expand the possibly-abbreviated filename to an exact path name */ - fullname = expand_dynamic_library_name(filename); - -+ /* PGACE: check whether the module can be loaded, or not */ -+ pgaceLoadSharedModule(fullname); -+ - /* Load the shared library, unless we already did */ - lib_handle = internal_load_library(fullname); - -@@ -146,6 +150,9 @@ load_file(const char *filename, bool res - /* Expand the possibly-abbreviated filename to an exact path name */ - fullname = expand_dynamic_library_name(filename); - -+ /* PGACE: check whether the module can be loaded, or not */ -+ pgaceLoadSharedModule(fullname); -+ - /* Unload the library if currently loaded */ - internal_unload_library(fullname); - -@@ -395,7 +402,7 @@ file_exists(const char *name) - * - * The result will always be freshly palloc'd. - */ --static char * -+char * - expand_dynamic_library_name(const char *name) - { - bool have_slash; -diff -rpNU3 base/src/backend/utils/init/postinit.c pgace/src/backend/utils/init/postinit.c ---- base/src/backend/utils/init/postinit.c 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/backend/utils/init/postinit.c 2008-01-08 01:39:49.000000000 +0900 -@@ -31,6 +31,7 @@ - #include "pgstat.h" - #include "postmaster/autovacuum.h" - #include "postmaster/postmaster.h" -+#include "security/pgace.h" - #include "storage/backendid.h" - #include "storage/fd.h" - #include "storage/ipc.h" -@@ -601,6 +602,9 @@ InitPostgres(const char *in_dbname, Oid - if (!bootstrap) - pgstat_bestart(); - -+ /* PGACE: initialize access control extension facility */ -+ pgaceInitialize(bootstrap); -+ - /* close the transaction we started above */ - if (!bootstrap) - CommitTransactionCommand(); -diff -rpNU3 base/src/backend/utils/misc/guc.c pgace/src/backend/utils/misc/guc.c ---- base/src/backend/utils/misc/guc.c 2008-02-03 01:11:28.000000000 +0900 -+++ pgace/src/backend/utils/misc/guc.c 2008-02-03 01:18:48.000000000 +0900 -@@ -54,6 +54,7 @@ - #include "postmaster/postmaster.h" - #include "postmaster/syslogger.h" - #include "postmaster/walwriter.h" -+#include "security/pgace.h" - #include "storage/fd.h" - #include "storage/freespace.h" - #include "tcop/tcopprot.h" -@@ -268,6 +269,7 @@ static int max_index_keys; - static int max_identifier_length; - static int block_size; - static bool integer_datetimes; -+static char *security_sysattr_name; - - /* should be static, but commands/variable.c needs to get at these */ - char *role_string; -@@ -2460,6 +2462,20 @@ static struct config_string ConfigureNam - }, - #endif /* USE_SSL */ - -+ { -+ {"security_sysattr_name", PGC_INTERNAL, PRESET_OPTIONS, -+ gettext_noop("Shows the name of security attribute system column"), -+ NULL, -+ GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE -+ }, -+ &security_sysattr_name, -+#ifdef SECURITY_SYSATTR_NAME -+ SECURITY_SYSATTR_NAME, NULL, NULL, -+#else -+ "undefined", NULL, NULL, -+#endif -+ }, -+ - /* End-of-list marker */ - { - {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL -@@ -3300,6 +3316,8 @@ ResetAllOptions(void) - { - int i; - -+ pgaceSetDatabaseParam("all", NULL); -+ - for (i = 0; i < num_guc_variables; i++) - { - struct config_generic *gconf = guc_variables[i]; -@@ -4982,6 +5000,7 @@ ExecSetVariableStmt(VariableSetStmt *stm - { - case VAR_SET_VALUE: - case VAR_SET_CURRENT: -+ pgaceSetDatabaseParam(stmt->name, ExtractSetVariableArgs(stmt)); - set_config_option(stmt->name, - ExtractSetVariableArgs(stmt), - (superuser() ? PGC_SUSET : PGC_USERSET), -@@ -5039,6 +5058,7 @@ ExecSetVariableStmt(VariableSetStmt *stm - break; - case VAR_SET_DEFAULT: - case VAR_RESET: -+ pgaceSetDatabaseParam(stmt->name, NULL); - set_config_option(stmt->name, - NULL, - (superuser() ? PGC_SUSET : PGC_USERSET), -@@ -5367,6 +5387,9 @@ EmitWarningsOnPlaceholders(const char *c - void - GetPGVariable(const char *name, DestReceiver *dest) - { -+ /* PGACE: check get param permission */ -+ pgaceGetDatabaseParam(name); -+ - if (guc_name_compare(name, "all") == 0) - ShowAllGUCConfig(dest); - else -diff -rpNU3 base/src/include/access/htup.h pgace/src/include/access/htup.h ---- base/src/include/access/htup.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/access/htup.h 2008-01-10 12:42:25.000000000 +0900 -@@ -161,7 +161,7 @@ typedef HeapTupleHeaderData *HeapTupleHe - #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ - #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ - #define HEAP_HASOID 0x0008 /* has an object-id field */ --/* bit 0x0010 is available */ -+#define HEAP_HASSECURITY 0x0010 /* has an security attribute field */ - #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ - #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ - #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ -@@ -347,6 +347,28 @@ do { \ - (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ - ) - -+#define HeapTupleHeaderGetSecurity(tup) \ -+ ( \ -+ ((tup)->t_infomask & HEAP_HASSECURITY) \ -+ ? (*((Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (((tup)->t_infomask & HEAP_HASOID) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid)))) \ -+ : InvalidOid \ -+ ) -+ -+#define HeapTupleHeaderSetSecurity(tup, security) \ -+ do { \ -+ Assert((tup)->t_infomask & HEAP_HASSECURITY); \ -+ *((Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (((tup)->t_infomask & HEAP_HASOID) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid))) = (security); \ -+ } while(0) -+ -+#define HeapTupleGetSecurity(tuple) \ -+ HeapTupleHeaderGetSecurity((tuple)->t_data) -+ -+#define HeapTupleSetSecurity(tuple, security) \ -+ HeapTupleHeaderSetSecurity((tuple)->t_data, (security)) - - /* - * BITMAPLEN(NATTS) - -@@ -402,7 +424,12 @@ do { \ - #define MaxTransactionIdAttributeNumber (-5) - #define MaxCommandIdAttributeNumber (-6) - #define TableOidAttributeNumber (-7) -+#ifdef SECURITY_SYSATTR_NAME -+#define SecurityAttributeNumber (-8) -+#define FirstLowInvalidHeapAttributeNumber (-9) -+#else - #define FirstLowInvalidHeapAttributeNumber (-8) -+#endif - - - /* -diff -rpNU3 base/src/include/catalog/heap.h pgace/src/include/catalog/heap.h ---- base/src/include/catalog/heap.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/catalog/heap.h 2008-01-08 01:39:49.000000000 +0900 -@@ -52,7 +52,8 @@ extern Oid heap_create_with_catalog(cons - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -- bool allow_system_table_mods); -+ bool allow_system_table_mods, -+ List *pgace_attr_list); - - extern void heap_drop_with_catalog(Oid relid); - -@@ -65,7 +66,8 @@ extern List *heap_truncate_find_FKs(List - extern void InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -- Datum reloptions); -+ Datum reloptions, -+ List *pgace_attr_list); - - extern List *AddRelationRawConstraints(Relation rel, - List *rawColDefaults, -diff -rpNU3 base/src/include/catalog/indexing.h pgace/src/include/catalog/indexing.h ---- base/src/include/catalog/indexing.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/catalog/indexing.h 2008-01-08 01:39:49.000000000 +0900 -@@ -252,6 +252,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, - DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); - #define TypeNameNspIndexId 2704 - -+DECLARE_UNIQUE_INDEX(pg_security_oid_index, 3401, on pg_security using btree(oid oid_ops)); -+#define SecurityOidIndexId 3401 -+DECLARE_UNIQUE_INDEX(pg_security_seclabel_index, 3402, on pg_security using btree(seclabel text_ops)); -+#define SecuritySeclabelIndexId 3402 -+ - /* last step of initialization script: build the indexes declared above */ - BUILD_INDICES - -diff -rpNU3 base/src/include/catalog/pg_attribute.h pgace/src/include/catalog/pg_attribute.h ---- base/src/include/catalog/pg_attribute.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/catalog/pg_attribute.h 2008-01-08 01:39:49.000000000 +0900 -@@ -282,6 +282,7 @@ DATA(insert ( 1247 cmin 29 0 4 -4 0 - DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); -+DATA(insert ( 1247 SECURITY_SYSATTR_NAME 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); - - /* ---------------- - * pg_proc -@@ -338,6 +339,7 @@ DATA(insert ( 1255 cmin 29 0 4 -4 0 - DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); -+DATA(insert ( 1255 SECURITY_SYSATTR_NAME 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); - - /* ---------------- - * pg_attribute -@@ -386,6 +388,7 @@ DATA(insert ( 1249 cmin 29 0 4 -4 0 - DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); -+DATA(insert ( 1249 SECURITY_SYSATTR_NAME 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); - - /* ---------------- - * pg_class -@@ -454,6 +457,7 @@ DATA(insert ( 1259 cmin 29 0 4 -4 0 - DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); - DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); -+DATA(insert ( 1259 SECURITY_SYSATTR_NAME 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); - - /* ---------------- - * pg_index -diff -rpNU3 base/src/include/catalog/pg_cast.h pgace/src/include/catalog/pg_cast.h ---- base/src/include/catalog/pg_cast.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/catalog/pg_cast.h 2008-01-08 01:39:49.000000000 +0900 -@@ -331,4 +331,10 @@ DATA(insert ( 1560 1560 1685 i )); - DATA(insert ( 1562 1562 1687 i )); - DATA(insert ( 1700 1700 1703 i )); - -+/* -+ * Security Label to/from text representation -+ */ -+DATA(insert (25 3403 3408 i)); -+DATA(insert (3403 25 3409 i)); -+ - #endif /* PG_CAST_H */ -diff -rpNU3 base/src/include/catalog/pg_proc.h pgace/src/include/catalog/pg_proc.h ---- base/src/include/catalog/pg_proc.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/catalog/pg_proc.h 2008-01-08 01:39:49.000000000 +0900 -@@ -4113,6 +4113,16 @@ DESCR("I/O"); - DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 f f t f i 1 23 "2950" _null_ _null_ _null_ uuid_hash - _null_ _null_ )); - DESCR("hash"); - -+/* PostgreSQL Access Control Extension related functions */ -+DATA(insert OID = 3404 ( security_label_in PGNSP PGUID 12 1 0 f f t f i 1 3403 "2275" _null_ _null_ _null_ security_label_in - _null_ _null_ )); -+DATA(insert OID = 3405 ( security_label_out PGNSP PGUID 12 1 0 f f t f i 1 2275 "3403" _null_ _null_ _null_ security_label_out - _null_ _null_ )); -+DATA(insert OID = 3406 ( security_label_raw_in PGNSP PGUID 12 1 0 f f t f i 1 3403 "2275" _null_ _null_ _null_ security_label_raw_in - _null_ _null_ )); -+DATA(insert OID = 3407 ( security_label_raw_out PGNSP PGUID 12 1 0 f f t f i 1 2275 "3403" _null_ _null_ _null_ security_label_raw_out - _null_ _null_ )); -+DATA(insert OID = 3408 ( text_to_security_label PGNSP PGUID 12 1 0 f f t f i 1 3403 "25" _null_ _null_ _null_ text_to_security_label - _null_ _null_ )); -+DATA(insert OID = 3409 ( security_label_to_text PGNSP PGUID 12 1 0 f f t f i 1 25 "3403" _null_ _null_ _null_ security_label_to_text - _null_ _null_ )); -+DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 1 0 f f t f v 1 3403 "26" _null_ _null_ _null_ lo_get_security - _null_ _null_ )); -+DATA(insert OID = 3411 ( lo_set_security PGNSP PGUID 12 1 0 f f t f v 2 16 "26 3403" _null_ _null_ _null_ lo_set_security - _null_ _null_ )); -+ - /* enum related procs */ - DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 f f t f i 1 3500 "2275" _null_ _null_ _null_ anyenum_in - _null_ _null_ )); - DESCR("I/O"); -@@ -4460,7 +4470,8 @@ extern Oid ProcedureCreate(const char *p - Datum parameterNames, - Datum proconfig, - float4 procost, -- float4 prorows); -+ float4 prorows, -+ void *pgace_item); - - extern bool function_parse_error_transpose(const char *prosrc); - -diff -rpNU3 base/src/include/catalog/pg_security.h pgace/src/include/catalog/pg_security.h ---- base/src/include/catalog/pg_security.h 1970-01-01 09:00:00.000000000 +0900 -+++ pgace/src/include/catalog/pg_security.h 2007-09-21 01:08:03.000000000 +0900 -@@ -0,0 +1,31 @@ -+/* -+ * src/include/catalog/pg_security.h -+ * Definition of the security label relation (pg_security) -+ * -+ * Copyright (c) 2006 - 2007 KaiGai Kohei -+ */ -+#ifndef PG_SECURITY_H -+#define PG_SECURITY_H -+ -+#define SecurityRelationId 3400 -+ -+CATALOG(pg_security,3400) BKI_SHARED_RELATION -+{ -+ text seclabel; /* text representation of security label */ -+} FormData_pg_security; -+ -+/* ---------------- -+ * Form_pg_security corresponds to a pointer to a tuple with -+ * the format of pg_security relation. -+ * ---------------- -+ */ -+typedef FormData_pg_security *Form_pg_security; -+ -+/* ---------------- -+ * compiler constants for pg_selinux -+ * ---------------- -+ */ -+#define Natts_pg_security 1 -+#define Anum_pg_security_seclabel 1 -+ -+#endif /* PG_SELINUX_H */ -diff -rpNU3 base/src/include/catalog/pg_type.h pgace/src/include/catalog/pg_type.h ---- base/src/include/catalog/pg_type.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/catalog/pg_type.h 2008-01-08 01:39:49.000000000 +0900 -@@ -611,6 +611,9 @@ DATA(insert OID = 2283 ( anyelement PGN - #define ANYELEMENTOID 2283 - DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ )); - #define ANYNONARRAYOID 2776 -+DATA(insert OID = 3403 ( security_label PGNSP PGUID 4 t b t \054 0 0 0 security_label_in security_label_out - - - - - i p f 0 -1 0 _null_ _null_ )); -+DESCR("Security Label Identifier for PGACE"); -+#define SECLABELOID 3403 - DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ )); - #define ANYENUMOID 3500 - -diff -rpNU3 base/src/include/executor/tuptable.h pgace/src/include/executor/tuptable.h ---- base/src/include/executor/tuptable.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/executor/tuptable.h 2008-01-08 01:39:49.000000000 +0900 -@@ -118,6 +118,7 @@ typedef struct TupleTableSlot - MinimalTuple tts_mintuple; /* set if it's a minimal tuple, else NULL */ - HeapTupleData tts_minhdr; /* workspace if it's a minimal tuple */ - long tts_off; /* saved state for slot_deform_tuple */ -+ Oid tts_security; /* pgace security system attribute */ - } TupleTableSlot; - - /* -@@ -139,6 +140,12 @@ typedef TupleTableData *TupleTable; - #define TupIsNull(slot) \ - ((slot) == NULL || (slot)->tts_isempty) - -+/* -+ * PGACE: HeapTupleStoreSecurityFromSlot -+ */ -+#define HeapTupleStoreSecurityFromSlot(tuple, slot) \ -+ HeapTupleSetSecurity((tuple), (slot)->tts_security) -+ - /* in executor/execTuples.c */ - extern TupleTable ExecCreateTupleTable(int tableSize); - extern void ExecDropTupleTable(TupleTable table, bool shouldFree); -diff -rpNU3 base/src/include/fmgr.h pgace/src/include/fmgr.h ---- base/src/include/fmgr.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/fmgr.h 2008-01-08 01:39:49.000000000 +0900 -@@ -52,6 +52,9 @@ typedef struct FmgrInfo - void *fn_extra; /* extra space for use by handler */ - MemoryContext fn_mcxt; /* memory context to store fn_extra in */ - fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ -+ -+ PGFunction fn_pgace_addr; /* PGACE opaque addr field */ -+ Datum fn_pgace_data; /* PGACE opaque data field */ - } FmgrInfo; - - /* -@@ -511,6 +514,7 @@ extern Oid get_call_expr_argtype(fmNodeP - */ - extern char *Dynamic_library_path; - -+extern char *expand_dynamic_library_name(const char *name); - extern PGFunction load_external_function(char *filename, char *funcname, - bool signalNotFound, void **filehandle); - extern PGFunction lookup_external_function(void *filehandle, char *funcname); -diff -rpNU3 base/src/include/libpq/be-fsstubs.h pgace/src/include/libpq/be-fsstubs.h ---- base/src/include/libpq/be-fsstubs.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/libpq/be-fsstubs.h 2008-01-08 01:39:49.000000000 +0900 -@@ -36,6 +36,9 @@ extern Datum lo_tell(PG_FUNCTION_ARGS); - extern Datum lo_unlink(PG_FUNCTION_ARGS); - extern Datum lo_truncate(PG_FUNCTION_ARGS); - -+extern Datum lo_get_security(PG_FUNCTION_ARGS); -+extern Datum lo_set_security(PG_FUNCTION_ARGS); -+ - /* - * These are not fmgr-callable, but are available to C code. - * Probably these should have had the underscore-free names, -diff -rpNU3 base/src/include/nodes/parsenodes.h pgace/src/include/nodes/parsenodes.h ---- base/src/include/nodes/parsenodes.h 2008-03-19 09:48:23.000000000 +0900 -+++ pgace/src/include/nodes/parsenodes.h 2008-03-19 10:08:35.000000000 +0900 -@@ -131,6 +131,7 @@ typedef struct Query - - Node *setOperations; /* set-operation tree if this is top level of - * a UNION/INTERSECT/EXCEPT query */ -+ Node *pgaceItem; /* PGACE: an opaque item for security purpose */ - } Query; - - -@@ -391,6 +392,7 @@ typedef struct ColumnDef - Node *raw_default; /* default value (untransformed parse tree) */ - char *cooked_default; /* nodeToString representation */ - List *constraints; /* other constraints on column */ -+ Node *pgaceItem; /* PGACE: security attribute */ - } ColumnDef; - - /* -@@ -917,7 +919,8 @@ typedef enum AlterTableType - AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */ - AT_DisableRule, /* DISABLE RULE name */ - AT_AddInherit, /* INHERIT parent */ -- AT_DropInherit /* NO INHERIT parent */ -+ AT_DropInherit, /* NO INHERIT parent */ -+ AT_SetSecurityLabel, /* PGACE: set security label */ - } AlterTableType; - - typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ -@@ -1108,6 +1111,7 @@ typedef struct CreateStmt - List *options; /* options from WITH clause */ - OnCommitAction oncommit; /* what do we do at COMMIT? */ - char *tablespacename; /* table space to use, or NULL */ -+ Node *pgaceItem; /* PGACE: security attribute */ - } CreateStmt; - - /* ---------- -diff -rpNU3 base/src/include/nodes/plannodes.h pgace/src/include/nodes/plannodes.h ---- base/src/include/nodes/plannodes.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/nodes/plannodes.h 2008-01-08 01:39:49.000000000 +0900 -@@ -73,6 +73,8 @@ typedef struct PlannedStmt - List *relationOids; /* OIDs of relations the plan depends on */ - - int nParamExec; /* number of PARAM_EXEC Params used */ -+ -+ Node *pgaceItem; /* PGACE: an opaque item for security purpose */ - } PlannedStmt; - - /* macro for fetching the Plan associated with a SubPlan node */ -diff -rpNU3 base/src/include/pg_config.h.in pgace/src/include/pg_config.h.in ---- base/src/include/pg_config.h.in 2008-01-28 16:06:37.000000000 +0900 -+++ pgace/src/include/pg_config.h.in 2008-01-28 16:14:33.000000000 +0900 -@@ -637,6 +637,9 @@ - your system. */ - #undef PTHREAD_CREATE_JOINABLE - -+/* The name of security attribute. */ -+#undef SECURITY_SYSATTR_NAME -+ - /* The size of a `size_t', as computed by sizeof. */ - #undef SIZEOF_SIZE_T - -diff -rpNU3 base/src/include/security/pgace.h pgace/src/include/security/pgace.h ---- base/src/include/security/pgace.h 1970-01-01 09:00:00.000000000 +0900 -+++ pgace/src/include/security/pgace.h 2008-02-01 20:22:14.000000000 +0900 -@@ -0,0 +1,147 @@ -+/* -+ * include/security/pgace.h -+ * headers for PostgreSQL Access Control Extensions (PGACE) -+ * Copyright 2007 KaiGai Kohei -+ */ -+#ifndef PGACE_H -+#define PGACE_H -+ -+#include "access/htup.h" -+#include "commands/trigger.h" -+#include "executor/execdesc.h" -+#include "nodes/parsenodes.h" -+#include "utils/builtins.h" -+#include "utils/rel.h" -+ -+/* -+ * SECURITY_SYSATTR_NAME is the name of system column name -+ * for security attribute, defined in pg_config.h -+ * If it is not defined, security attribute support is disabled -+ * -+ * see, src/include/pg_config.h -+ */ -+ -+/****************************************************************** -+ * Initialize / Finalize related hooks -+ ******************************************************************/ -+extern Size pgaceShmemSize(void); -+extern void pgaceInitialize(bool is_bootstrap); -+extern bool pgaceInitializePostmaster(void); -+extern void pgaceFinalizePostmaster(void); -+ -+/****************************************************************** -+ * SQL proxy hooks -+ ******************************************************************/ -+extern List *pgaceProxyQuery(List *queryList); -+extern void pgacePortalStart(Portal portal); -+extern void pgaceExecutorStart(QueryDesc *queryDesc, int eflags); -+ -+/****************************************************************** -+ * HeapTuple modification hooks -+ ******************************************************************/ -+extern bool pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, -+ bool is_internal, bool with_returning); -+extern bool pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, -+ bool is_internal, bool with_returning); -+extern bool pgaceHeapTupleDelete(Relation rel, ItemPointer otid, -+ bool is_internal, bool with_returning); -+ -+/****************************************************************** -+ * Extended SQL statement hooks -+ ******************************************************************/ -+extern DefElem *pgaceGramSecurityItem(char *defname, char *value); -+extern bool pgaceIsGramSecurityItem(DefElem *defel); -+extern void pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel); -+ -+/****************************************************************** -+ * DATABASE related hooks -+ ******************************************************************/ -+extern void pgaceSetDatabaseParam(const char *name, char *argstring); -+extern void pgaceGetDatabaseParam(const char *name); -+ -+/****************************************************************** -+ * FUNCTION related hooks -+ ******************************************************************/ -+extern void pgaceCallFunction(FmgrInfo *finfo); -+extern bool pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata); -+extern void pgaceCallFunctionFastPath(FmgrInfo *finfo); -+extern Datum pgacePreparePlanCheck(Relation rel); -+extern void pgaceRestorePlanCheck(Relation rel, Datum pgace_saved); -+ -+/****************************************************************** -+ * TABLE related hooks -+ ******************************************************************/ -+extern void pgaceLockTable(Oid relid); -+ -+/****************************************************************** -+ * COPY TO/COPY FROM statement hooks -+ ******************************************************************/ -+extern void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom); -+extern bool pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple); -+ -+/****************************************************************** -+ * Loadable shared library module hooks -+ ******************************************************************/ -+extern void pgaceLoadSharedModule(const char *filename); -+ -+/****************************************************************** -+ * Binary Large Object (BLOB) hooks -+ ******************************************************************/ -+extern void pgaceLargeObjectGetSecurity(HeapTuple tuple); -+extern void pgaceLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security); -+extern void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple); -+extern void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple); -+extern void pgaceLargeObjectRead(Relation rel, HeapTuple tuple); -+extern void pgaceLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup); -+extern void pgaceLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup); -+extern void pgaceLargeObjectImport(int fd); -+extern void pgaceLargeObjectExport(int fd, Oid loid); -+ -+/****************************************************************** -+ * Security Label hooks -+ ******************************************************************/ -+extern char *pgaceSecurityLabelIn(char *seclabel); -+extern char *pgaceSecurityLabelOut(char *seclabel); -+extern char *pgaceSecurityLabelCheckValid(char *seclabel); -+extern char *pgaceSecurityLabelOfLabel(char *new_label); -+ -+/****************************************************************** -+ * Extended node type hooks -+ ******************************************************************/ -+extern Node *pgaceCopyObject(Node *orig); -+extern bool pgaceOutObject(StringInfo str, Node *node); -+extern void *pgaceReadObject(char *token); -+ -+/****************************************************************** -+ * PGACE common facilities (not a hooks) -+ ******************************************************************/ -+/* Security attribute system column support */ -+extern bool pgaceIsSecuritySystemColumn(int attrno); -+extern void pgaceFetchSecurityAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, Oid *tts_security); -+extern void pgaceTransformSelectStmt(List *targetList); -+extern void pgaceTransformInsertStmt(List **p_icolumns, List **p_attrnos, List *targetList); -+ -+/* Extended SQL statements related */ -+extern List *pgaceRelationAttrList(CreateStmt *stmt); -+extern void pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, List *pgace_attr_list); -+extern void pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple, List *pgace_attr_list); -+extern void pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd); -+ -+/* SQL functions */ -+extern Datum security_label_in(PG_FUNCTION_ARGS); -+extern Datum security_label_out(PG_FUNCTION_ARGS); -+extern Datum security_label_raw_in(PG_FUNCTION_ARGS); -+extern Datum security_label_raw_out(PG_FUNCTION_ARGS); -+extern Datum text_to_security_label(PG_FUNCTION_ARGS); -+extern Datum security_label_to_text(PG_FUNCTION_ARGS); -+extern Datum lo_get_security(PG_FUNCTION_ARGS); -+extern Datum lo_set_security(PG_FUNCTION_ARGS); -+ -+#endif // PGACE_H -diff -rpNU3 base/src/include/utils/syscache.h pgace/src/include/utils/syscache.h ---- base/src/include/utils/syscache.h 2008-01-07 23:51:33.000000000 +0900 -+++ pgace/src/include/utils/syscache.h 2008-01-08 01:39:49.000000000 +0900 -@@ -76,6 +76,8 @@ - #define TSTEMPLATEOID 45 - #define TYPENAMENSP 46 - #define TYPEOID 47 -+#define SECURITYOID 48 -+#define SECURITYLABEL 49 - - extern void InitCatalogCache(void); - extern void InitCatalogCachePhase2(void); diff --git a/sepostgresql-policy-8.3.7-2.patch b/sepostgresql-policy-8.3.7-2.patch new file mode 100644 index 0000000..7404657 --- /dev/null +++ b/sepostgresql-policy-8.3.7-2.patch @@ -0,0 +1,821 @@ +diff -rpNU3 base/src/backend/security/sepgsql/policy/Makefile sepgsql/src/backend/security/sepgsql/policy/Makefile +--- base/src/backend/security/sepgsql/policy/Makefile 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/policy/Makefile 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,41 @@ ++# ++# contrib/sepgsql_policy/Makefile ++# Makefile of security policy module for SE-PostgreSQL ++# ++top_builddir = ../../../../.. ++include $(top_builddir)/src/Makefile.global ++ ++policy_basedir := /usr/share/selinux ++policy_makefile := $(policy_basedir)/devel/Makefile ++policy_types := targeted mls ++policy := $(strip $(shell $(AWK) -F= '/^SELINUXTYPE/{ print $$2 }' /etc/selinux/config)) ++package_names := sepostgresql sepostgresql-devel ++prefix_ptn := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g" ++bindir_ptn := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g" ++libdir_ptn := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g" ++ ++all: ++ $(foreach pkg, $(package_names), $(foreach p, $(policy_types), $(MAKE) $(MAKEOVERRIDES) policy=$(p) $(pkg).pp;)) ++ $(foreach pkg, $(package_names), test -e $(pkg).pp.$(policy) && ln -sf $(pkg).pp.$(policy) $(pkg).pp;) ++ ++.install-policy: ++ test -d $(DESTDIR)$(policy_basedir)/$(policy) || install -d $(DESTDIR)$(policy_basedir)/$(policy) ++ $(foreach pkg, $(package_names), install -p -m 644 $(pkg).pp.$(policy) $(DESTDIR)$(policy_basedir)/$(policy)/$(pkg).pp;) ++ ++install: all ++ $(foreach p, $(policy_types), $(MAKE) $(MAKEOVERRIDES) policy=$(p) .install-policy;) ++ ++%.pp: %.te %.if %.fc ++ rm -f $@ ++ $(MAKE) NAME=$(policy) -f $(policy_makefile) $@ ++ mv $@ $@.$(policy) ++ ++sepostgresql-devel.fc: sepostgresql.fc.template ++ cat $< | grep -v ^/var | sed -e $(prefix_ptn) -e $(bindir_ptn) -e $(libdir_ptn) > $@ ++ ++sepostgresql.fc: sepostgresql.fc.template ++ cat $< | sed -e $(prefix_ptn) -e $(bindir_ptn) -e $(libdir_ptn) > $@ ++ ++clean: ++ $(MAKE) -f $(policy_makefile) clean ++ rm -f *.pp.* *.fc +diff -rpNU3 base/src/backend/security/sepgsql/policy/README sepgsql/src/backend/security/sepgsql/policy/README +--- base/src/backend/security/sepgsql/policy/README 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/policy/README 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,49 @@ ++The security policy module of SE-PostgreSQL ++------------------------------------------- ++ ++o Introduction ++ ++ We provide two kind of security policy modules. ++ ++ One is "sepostgresql.pp" which contains full-set of security policy ++ and suitable for legacy base policy (selinux-policy-3.4.1, or prior). ++ ++ The other is "sepostgresql-devel.pp" which provides several booleans ++ for developers, and suitable for newer base policy (selinux-policy-3.4.2, ++ or later). ++ ++ In the selinux-policy-3.4.2, most part of the policy got upstreamed. ++ So, we don't need to install "sepostgresql.pp" explicitly on the newer ++ base security policy. ++ ++ If you need to run regression test, or (don't) want to generate access ++ logs, install "sepostgresql-devel.pp" and turn on/off booleans. ++ ++o Build & Installation ++ ++ $ cd src/backend/security/sepgsql/policy ++ $ make ++ $ su ++ # /usr/sbin/semodule -i sepostgresql-devel.pp ++ or ++ # /usr/sbin/semodule -i sepostgresql.pp ++ ++o Booleans ++ ++- sepgsql_enable_users_ddl (default: on) ++ This boolean enables to control to execute DDL statement come from ++ confined users. ++ ++- sepgsql_enable_auditallow (default: off) ++ This boolean enables to generate access allow logs except for tuple ++ level. ++ ++- sepgsql_enable_auditdeny (default: on) ++ This boolean enables to generata access denied logs except for tuple ++ level. ++ ++- sepgsql_regression_test_mode (default: off) ++ This boolean provides several permission to run regression test on ++ your home directory. It enables to load shared library files deployed ++ on home directory. ++ However, we don't recommend it to turn on in the operation phase. +diff -rpNU3 base/src/backend/security/sepgsql/policy/sepostgresql-devel.if sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.if +--- base/src/backend/security/sepgsql/policy/sepostgresql-devel.if 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.if 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1 @@ ++## There are no interface declaration +diff -rpNU3 base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te +--- base/src/backend/security/sepgsql/policy/sepostgresql-devel.te 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te 2009-02-26 21:30:17.000000000 +0900 +@@ -0,0 +1,120 @@ ++policy_module(sepostgresql-devel, 3.23) ++ ++gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute sepgsql_client_type; ++ attribute sepgsql_unconfined_type; ++ ++ attribute sepgsql_database_type; ++ attribute sepgsql_table_type; ++ attribute sepgsql_sysobj_table_type; ++ attribute sepgsql_procedure_type; ++ attribute sepgsql_blob_type; ++ attribute sepgsql_module_type; ++ ++ # for regression test ++ type bin_t; ++ type user_home_t; ++ type sepgsql_trusted_proc_t; ++ ++ attribute tmpfile; ++ attribute user_ptynode; ++') ++ ++################################# ++# ++# Domain for Testcases ++# ++ ++role sepgsql_test_r; ++ ++userdom_unpriv_user_template(sepgsql_test) ++ ++ifdef(`postgresql_role', ` ++ postgresql_role(sepgsql_test_r, sepgsql_test_t) ++',` ++ postgresql_userdom_template(sepgsql_test, sepgsql_test_t, sepgsql_test_r) ++') ++ ++allow sepgsql_test_t tmpfile : dir search_dir_perms; ++allow sepgsql_test_t tmpfile : file rw_file_perms; ++allow sepgsql_test_t user_ptynode : chr_file rw_file_perms; ++ ++optional_policy(` ++ gen_require(` ++ type unconfined_t; ++ role unconfined_r; ++ ') ++ allow unconfined_t sepgsql_test_t : process transition; ++ role unconfined_r types sepgsql_test_t; ++ role unconfined_r types sepgsql_trusted_proc_t; ++') ++ ++################################# ++# ++# SE-PostgreSQL Declarations ++# ++ ++## ++##

++## Allow to generate auditallow logs ++##

++##
++gen_tunable(sepgsql_enable_auditallow, false) ++ ++## ++##

++## Allow to generate auditdeny logs ++##

++##
++gen_tunable(sepgsql_enable_auditdeny, true) ++ ++## ++##

++## Allow widespread permissions for regression test ++## Don't set TRUE on operation phase ++##

++##
++gen_tunable(sepgsql_regression_test_mode, false) ++ ++######################################## ++# ++# SE-PostgreSQL audit switch for debugging ++# ++tunable_policy(`sepgsql_enable_auditallow',` ++ auditallow domain sepgsql_database_type : db_database *; ++ auditallow domain sepgsql_table_type : db_table *; ++ auditallow domain sepgsql_table_type : db_column *; ++ auditallow domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; ++ auditallow domain sepgsql_procedure_type : db_procedure *; ++ auditallow domain sepgsql_blob_type : db_blob *; ++ auditallow domain sepgsql_module_type : db_database { install_module }; ++ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++tunable_policy(`! sepgsql_enable_auditdeny',` ++ dontaudit domain sepgsql_database_type : db_database *; ++ dontaudit domain sepgsql_table_type : db_table *; ++ dontaudit domain sepgsql_table_type : db_column *; ++ dontaudit domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; ++ dontaudit domain sepgsql_procedure_type : db_procedure *; ++ dontaudit domain sepgsql_blob_type : db_blob *; ++ dontaudit domain sepgsql_module_type : db_database { install_module }; ++ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++######################################## ++# ++# SE-PostgreSQL regression test mode switch ++# ++tunable_policy(`sepgsql_regression_test_mode',` ++ allow sepgsql_client_type user_home_t : db_database { install_module }; ++ allow sepgsql_unconfined_type user_home_t : db_database { install_module }; ++ allow sepgsql_database_type user_home_t : db_database { load_module }; ++') +diff -rpNU3 base/src/backend/security/sepgsql/policy/sepostgresql.fc.template sepgsql/src/backend/security/sepgsql/policy/sepostgresql.fc.template +--- base/src/backend/security/sepgsql/policy/sepostgresql.fc.template 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/policy/sepostgresql.fc.template 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,15 @@ ++# ++# SE-PostgreSQL install path ++# ++%%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0) ++ ++%%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++%%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) ++%%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++%%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0) ++ ++%%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0) ++ ++/var/lib/sepgsql(/.*)? gen_context(system_u:object_r:postgresql_db_t,s0) ++/var/lib/sepgsql/pgstartup\.log gen_context(system_u:object_r:postgresql_log_t,s0) ++/var/log/sepostgresql\.log.* -- gen_context(system_u:object_r:postgresql_log_t,s0) +diff -rpNU3 base/src/backend/security/sepgsql/policy/sepostgresql.if sepgsql/src/backend/security/sepgsql/policy/sepostgresql.if +--- base/src/backend/security/sepgsql/policy/sepostgresql.if 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/policy/sepostgresql.if 2008-07-11 17:12:06.000000000 +0900 +@@ -0,0 +1,259 @@ ++####################################### ++## ++## The userdomain template for the SE-PostgreSQL. ++## ++## ++## This template creates a delivered types which are used ++## for given userdomains. ++## ++## ++## ++## The prefix of the user domain (e.g., user ++## is the prefix for user_t). ++## ++## ++## ++## ++## The type of the user domain. ++## ++## ++## ++## ++## The role associated with the user domain. ++## ++## ++# ++template(`sepgsql_userdom_template',` ++ gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute sepgsql_client_type; ++ attribute sepgsql_database_type; ++ attribute sepgsql_sysobj_table_type; ++ ++ type sepgsql_trusted_proc_t; ++ type sepgsql_trusted_proc_exec_t; ++ ') ++ ++ ######################################## ++ # ++ # Declarations ++ # ++ ++ typeattribute $2 sepgsql_client_type; ++ ++ type $1_sepgsql_blob_t; ++ sepgsql_blob_object($1_sepgsql_blob_t) ++ ++ type $1_sepgsql_proc_exec_t; ++ sepgsql_procedure_object($1_sepgsql_proc_exec_t) ++ ++ type $1_sepgsql_sysobj_t; ++ sepgsql_system_table_object($1_sepgsql_sysobj_t) ++ ++ type $1_sepgsql_table_t; ++ sepgsql_table_object($1_sepgsql_table_t) ++ ++ role $3 types sepgsql_trusted_proc_t; ++ ++ ############################## ++ # ++ # Client local policy ++ # ++ ++ tunable_policy(`sepgsql_enable_users_ddl',` ++ allow $2 $1_sepgsql_table_t : db_table { create drop }; ++ type_transition $2 sepgsql_database_type:db_table $1_sepgsql_table_t; ++ ++ allow $2 $1_sepgsql_table_t : db_column { create drop }; ++ ++ allow $2 $1_sepgsql_sysobj_t : db_tuple { update insert delete }; ++ type_transition $2 sepgsql_sysobj_table_type:db_tuple $1_sepgsql_sysobj_t; ++ ') ++ ++ allow $2 $1_sepgsql_table_t : db_table { getattr setattr use select update insert delete }; ++ allow $2 $1_sepgsql_table_t : db_column { getattr setattr use select update insert }; ++ allow $2 $1_sepgsql_table_t : db_tuple { use select update insert delete }; ++ allow $2 $1_sepgsql_sysobj_t : db_tuple { use select }; ++ ++ allow $2 $1_sepgsql_proc_exec_t : db_procedure { create drop getattr setattr execute }; ++ type_transition $2 sepgsql_database_type:db_procedure $1_sepgsql_proc_exec_t; ++ ++ allow $2 $1_sepgsql_blob_t : db_blob { create drop getattr setattr read write }; ++ type_transition $2 sepgsql_database_type:db_blob $1_sepgsql_blob_t; ++ ++ allow $2 sepgsql_trusted_proc_t:process transition; ++ type_transition $2 sepgsql_trusted_proc_exec_t:process sepgsql_trusted_proc_t; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL loadable shared library module ++## ++## ++## ++## Type marked as a database object type. ++## ++## ++# ++interface(`sepgsql_loadable_module',` ++ gen_require(` ++ attribute sepgsql_module_type; ++ ') ++ ++ typeattribute $1 sepgsql_module_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL database object type ++## ++## ++## ++## Type marked as a database object type. ++## ++## ++# ++interface(`sepgsql_database_object',` ++ gen_require(` ++ attribute sepgsql_database_type; ++ ') ++ ++ typeattribute $1 sepgsql_database_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL table/column/tuple object type ++## ++## ++## ++## Type marked as a table/column/tuple object type. ++## ++## ++# ++interface(`sepgsql_table_object',` ++ gen_require(` ++ attribute sepgsql_table_type; ++ ') ++ ++ typeattribute $1 sepgsql_table_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL system table/column/tuple object type ++## ++## ++## ++## Type marked as a table/column/tuple object type. ++## ++## ++# ++interface(`sepgsql_system_table_object',` ++ gen_require(` ++ attribute sepgsql_table_type; ++ attribute sepgsql_sysobj_table_type; ++ ') ++ ++ typeattribute $1 sepgsql_table_type; ++ typeattribute $1 sepgsql_sysobj_table_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL procedure object type ++## ++## ++## ++## Type marked as a database object type. ++## ++## ++# ++interface(`sepgsql_procedure_object',` ++ gen_require(` ++ attribute sepgsql_procedure_type; ++ ') ++ ++ typeattribute $1 sepgsql_procedure_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL binary large object type ++## ++## ++## ++## Type marked as a database binary large object type. ++## ++## ++# ++interface(`sepgsql_blob_object',` ++ gen_require(` ++ attribute sepgsql_blob_type; ++ ') ++ ++ typeattribute $1 sepgsql_blob_type; ++') ++ ++######################################## ++## ++## Allow the specified domain unprivileged accesses to unifined database objects ++## managed by SE-PostgreSQL, ++## ++## ++## ++## Domain allowed access. ++## ++## ++# ++interface(`sepgsql_unpriv_client',` ++ gen_require(` ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute sepgsql_client_type; ++ attribute sepgsql_database_type; ++ ++ type sepgsql_table_t; ++ type sepgsql_proc_t; ++ type sepgsql_blob_t; ++ ++ type sepgsql_trusted_proc_t; ++ type sepgsql_trusted_proc_exec_t; ++ ') ++ ++ typeattribute $1 sepgsql_client_type; ++ ++ type_transition $1 sepgsql_database_type:db_table sepgsql_table_t; ++ type_transition $1 sepgsql_database_type:db_procedure sepgsql_proc_t; ++ type_transition $1 sepgsql_database_type:db_blob sepgsql_blob_t; ++ ++ type_transition $1 sepgsql_trusted_proc_exec_t:process sepgsql_trusted_proc_t; ++ allow $1 sepgsql_trusted_proc_t:process transition; ++') ++ ++######################################## ++## ++## Allow the specified domain unconfined accesses to any database objects ++## managed by SE-PostgreSQL, ++## ++## ++## ++## Domain allowed access. ++## ++## ++# ++interface(`sepgsql_unconfined',` ++ gen_require(` ++ attribute sepgsql_unconfined_type; ++ ') ++ ++ typeattribute $1 sepgsql_unconfined_type; ++') +diff -rpNU3 base/src/backend/security/sepgsql/policy/sepostgresql.te sepgsql/src/backend/security/sepgsql/policy/sepostgresql.te +--- base/src/backend/security/sepgsql/policy/sepostgresql.te 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/policy/sepostgresql.te 2008-07-11 17:12:06.000000000 +0900 +@@ -0,0 +1,308 @@ ++policy_module(sepostgresql, 3.11) ++ ++gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute domain, home_type; ++ type postgresql_t, unlabeled_t; ++ ++ role system_r; ++') ++ ++################################# ++# ++# SE-PostgreSQL Declarations ++# ++ ++## ++##

++## Allow to generate auditallow logs ++##

++##
++gen_tunable(sepgsql_enable_auditallow, false) ++ ++## ++##

++## Allow to generate auditdeny logs ++##

++##
++gen_tunable(sepgsql_enable_auditdeny, true) ++ ++## ++##

++## Allow unprivileged users to execute DDL statement ++##

++##
++gen_tunable(sepgsql_enable_users_ddl, true) ++ ++## ++##

++## Allow widespread permissions for regression test ++## Don't set TRUE on operation phase ++##

++##
++gen_tunable(sepgsql_regression_test_mode, false) ++ ++# database clients attribute ++attribute sepgsql_client_type; ++attribute sepgsql_unconfined_type; ++ ++# database objects attribute ++attribute sepgsql_database_type; ++attribute sepgsql_table_type; ++attribute sepgsql_sysobj_table_type; ++attribute sepgsql_procedure_type; ++attribute sepgsql_blob_type; ++attribute sepgsql_module_type; ++ ++# database object types ++type sepgsql_blob_t; ++sepgsql_blob_object(sepgsql_blob_t) ++ ++type sepgsql_db_t; ++sepgsql_database_object(sepgsql_db_t) ++ ++type sepgsql_fixed_table_t; ++sepgsql_table_object(sepgsql_fixed_table_t) ++ ++type sepgsql_proc_t; ++sepgsql_procedure_object(sepgsql_proc_t) ++ ++type sepgsql_ro_blob_t; ++sepgsql_blob_object(sepgsql_ro_blob_t) ++ ++type sepgsql_ro_table_t; ++sepgsql_table_object(sepgsql_ro_table_t) ++ ++type sepgsql_secret_blob_t; ++sepgsql_blob_object(sepgsql_secret_blob_t) ++ ++type sepgsql_secret_table_t; ++sepgsql_table_object(sepgsql_secret_table_t) ++ ++type sepgsql_sysobj_t; ++sepgsql_system_table_object(sepgsql_sysobj_t) ++ ++type sepgsql_table_t; ++sepgsql_table_object(sepgsql_table_t) ++ ++type sepgsql_trusted_proc_exec_t; ++sepgsql_procedure_object(sepgsql_trusted_proc_exec_t) ++ ++# Trusted Procedure Domain ++type sepgsql_trusted_proc_t; ++domain_type(sepgsql_trusted_proc_t) ++sepgsql_unconfined(sepgsql_trusted_proc_t) ++role system_r types sepgsql_trusted_proc_t; ++ ++######################################## ++# ++# SE-PostgreSQL Local Policy ++# ++allow postgresql_t self:netlink_selinux_socket create_socket_perms; ++selinux_get_enforce_mode(postgresql_t) ++selinux_validate_context(postgresql_t) ++selinux_compute_access_vector(postgresql_t) ++selinux_compute_create_context(postgresql_t) ++selinux_compute_relabel_context(postgresql_t) ++seutil_libselinux_linked(postgresql_t) ++ ++allow postgresql_t sepgsql_database_type:db_database *; ++type_transition postgresql_t postgresql_t:db_database sepgsql_db_t; ++ ++allow postgresql_t sepgsql_module_type:db_database install_module; ++allow postgresql_t sepgsql_table_type:{ db_table db_column db_tuple } *; ++allow postgresql_t sepgsql_procedure_type:db_procedure *; ++allow postgresql_t sepgsql_blob_type:db_blob *; ++ ++# server specific type transitions ++type_transition postgresql_t sepgsql_database_type:db_table sepgsql_sysobj_t; ++type_transition postgresql_t sepgsql_database_type:db_procedure sepgsql_proc_t; ++type_transition postgresql_t sepgsql_database_type:db_blob sepgsql_blob_t; ++ ++# Database/Loadable module ++allow sepgsql_database_type sepgsql_module_type:db_database load_module; ++ ++######################################## ++# ++# Rules common to all clients ++# ++ ++# Client domain constraint ++allow sepgsql_client_type sepgsql_db_t:db_database { getattr access get_param set_param }; ++type_transition sepgsql_client_type sepgsql_client_type:db_database sepgsql_db_t; ++ ++allow sepgsql_client_type sepgsql_fixed_table_t:db_table { getattr use select insert }; ++allow sepgsql_client_type sepgsql_fixed_table_t:db_column { getattr use select insert }; ++allow sepgsql_client_type sepgsql_fixed_table_t:db_tuple { use select insert }; ++ ++allow sepgsql_client_type sepgsql_table_t:db_table { getattr use select update insert delete }; ++allow sepgsql_client_type sepgsql_table_t:db_column { getattr use select update insert }; ++allow sepgsql_client_type sepgsql_table_t:db_tuple { use select update insert delete }; ++ ++allow sepgsql_client_type sepgsql_ro_table_t:db_table { getattr use select }; ++allow sepgsql_client_type sepgsql_ro_table_t:db_column { getattr use select }; ++allow sepgsql_client_type sepgsql_ro_table_t:db_tuple { use select }; ++ ++allow sepgsql_client_type sepgsql_secret_table_t:db_table getattr; ++allow sepgsql_client_type sepgsql_secret_table_t:db_column getattr; ++ ++allow sepgsql_client_type sepgsql_sysobj_t:db_table { getattr use select }; ++allow sepgsql_client_type sepgsql_sysobj_t:db_column { getattr use select }; ++allow sepgsql_client_type sepgsql_sysobj_t:db_tuple { use select }; ++ ++allow sepgsql_client_type sepgsql_proc_t:db_procedure { getattr execute }; ++allow sepgsql_client_type sepgsql_trusted_proc_t:db_procedure { getattr execute entrypoint }; ++ ++allow sepgsql_client_type sepgsql_blob_t:db_blob { create drop getattr setattr read write }; ++allow sepgsql_client_type sepgsql_ro_blob_t:db_blob { getattr read }; ++allow sepgsql_client_type sepgsql_secret_blob_t:db_blob getattr; ++ ++tunable_policy(`sepgsql_enable_users_ddl',` ++ allow sepgsql_client_type sepgsql_table_t:db_table { create drop setattr }; ++ allow sepgsql_client_type sepgsql_table_t:db_column { create drop setattr }; ++ allow sepgsql_client_type sepgsql_sysobj_t:db_tuple { update insert delete }; ++') ++ ++######################################## ++# ++# Unconfined access to this module ++# ++ ++allow sepgsql_unconfined_type sepgsql_database_type:db_database *; ++allow sepgsql_unconfined_type sepgsql_table_type:{ db_table db_column db_tuple } *; ++allow sepgsql_unconfined_type sepgsql_blob_type:db_blob *; ++allow sepgsql_unconfined_type { sepgsql_proc_t sepgsql_trusted_proc_t }:db_procedure *; ++allow sepgsql_unconfined_type sepgsql_procedure_type:db_procedure { create drop getattr setattr relabelfrom relabelto }; ++allow sepgsql_unconfined_type sepgsql_module_type:db_database install_module; ++ ++type_transition sepgsql_unconfined_type sepgsql_unconfined_type:db_database sepgsql_db_t; ++type_transition sepgsql_unconfined_type sepgsql_database_type:db_table sepgsql_table_t; ++type_transition sepgsql_unconfined_type sepgsql_database_type:db_procedure sepgsql_proc_t; ++type_transition sepgsql_unconfined_type sepgsql_database_type:db_blob sepgsql_blob_t; ++ ++ ++######################################## ++# ++# Allow permission to external domains ++# ++ ++# relabelfrom for invalid security context ++allow sepgsql_unconfined_type unlabeled_t:db_database { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_table { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_procedure { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_column { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_tuple { update relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_blob { setattr relabelfrom }; ++ ++# administrative client domain ++optional_policy(` ++ gen_require(` ++ type unconfined_t; ++ ') ++ sepgsql_unconfined(unconfined_t) ++') ++ ++optional_policy(` ++ gen_require(` ++ type sysadm_t; ++ ') ++ sepgsql_unconfined(sysadm_t) ++') ++ ++# unprivilleged client domain ++optional_policy(` ++ gen_require(` ++ type user_t; ++ role user_r; ++ ') ++ sepgsql_userdom_template(user,user_t,user_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type staff_t; ++ role staff_r; ++ ') ++ sepgsql_userdom_template(staff,staff_t,staff_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type guest_t; ++ role guest_r; ++ ') ++ sepgsql_userdom_template(guest,guest_t,guest_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type xguest_t; ++ role xguest_r; ++ ') ++ sepgsql_userdom_template(xguest,xguest_t,xguest_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type httpd_t; ++ ') ++ sepgsql_unpriv_client(httpd_t) ++') ++ ++optional_policy(` ++ gen_require(` ++ type httpd_sys_script_t; ++ ') ++ sepgsql_unpriv_client(httpd_sys_script_t) ++') ++ ++# SE-PostgreSQL loadable modules ++optional_policy(` ++ gen_require(` ++ type lib_t, textrel_shlib_t; ++ ') ++ sepgsql_loadable_module(lib_t) ++ sepgsql_loadable_module(textrel_shlib_t) ++') ++ ++######################################## ++# ++# SE-PostgreSQL audit switch for debugging ++# ++tunable_policy(`sepgsql_enable_auditallow',` ++ auditallow domain sepgsql_database_type : db_database *; ++ auditallow domain sepgsql_table_type : db_table *; ++ auditallow domain sepgsql_table_type : db_column *; ++ auditallow domain sepgsql_procedure_type : db_procedure *; ++ auditallow domain sepgsql_blob_type : db_blob *; ++ auditallow domain sepgsql_module_type : db_database { install_module }; ++ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++tunable_policy(`! sepgsql_enable_auditdeny',` ++ dontaudit domain sepgsql_database_type : db_database *; ++ dontaudit domain sepgsql_table_type : db_table *; ++ dontaudit domain sepgsql_table_type : db_column *; ++ dontaudit domain sepgsql_procedure_type : db_procedure *; ++ dontaudit domain sepgsql_blob_type : db_blob *; ++ dontaudit domain sepgsql_module_type : db_database { install_module }; ++ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++dontaudit domain { sepgsql_table_type - sepgsql_sysobj_table_type } : db_tuple { use select update insert delete }; ++ ++######################################## ++# ++# SE-PostgreSQL regression test mode switch ++# ++tunable_policy(`sepgsql_regression_test_mode',` ++ allow sepgsql_client_type home_type : db_database { install_module }; ++ allow sepgsql_unconfined_type home_type : db_database { install_module }; ++ allow sepgsql_database_type home_type : db_database { load_module }; ++') diff --git a/sepostgresql-sepgsql-8.3.1-2.patch b/sepostgresql-sepgsql-8.3.1-2.patch deleted file mode 100644 index 415edb2..0000000 --- a/sepostgresql-sepgsql-8.3.1-2.patch +++ /dev/null @@ -1,4833 +0,0 @@ -diff -rpNU3 pgace/configure sepgsql/configure ---- pgace/configure 2008-03-19 10:08:35.000000000 +0900 -+++ sepgsql/configure 2008-03-19 10:19:23.000000000 +0900 -@@ -314,7 +314,7 @@ ac_includes_default="\ - # include - #endif" - --ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS' -+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib enable_selinux EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS' - ac_subst_files='' - - # Initialize some variables set by options. -@@ -871,6 +871,7 @@ Optional Features: - --enable-cassert enable assertion checks (for debugging) - --enable-thread-safety make client libraries thread-safe - --enable-thread-safety-force force thread-safety despite thread test failure -+ --enable-selinux build with NSA SELinux support - --disable-largefile omit support for large files - - Optional Packages: -@@ -4619,6 +4620,118 @@ fi; - - - # -+# NSA SELinux support -+# -+ -+pgac_args="$pgac_args enable_selinux" -+ -+# Check whether --enable-selinux or --disable-selinux was given. -+if test "${enable_selinux+set}" = set; then -+ enableval="$enable_selinux" -+ -+ case $enableval in -+ yes) -+ : -+ ;; -+ no) -+ : -+ ;; -+ *) -+ { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 -+echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} -+ { (exit 1); exit 1; }; } -+ ;; -+ esac -+ -+else -+ enable_selinux=no -+ -+fi; -+ -+if test "$enable_selinux" = yes; then -+ echo "$as_me:$LINENO: checking for getpeercon in -lselinux" >&5 -+echo $ECHO_N "checking for getpeercon in -lselinux... $ECHO_C" >&6 -+if test "${ac_cv_lib_selinux_getpeercon+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lselinux $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any gcc2 internal prototype to avoid an error. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+/* We use char because int might match the return type of a gcc2 -+ builtin and then its argument prototype would still apply. */ -+char getpeercon (); -+int -+main () -+{ -+getpeercon (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 -+ (eval $ac_link) 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && -+ { ac_try='test -z "$ac_c_werror_flag" -+ || test ! -s conftest.err' -+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 -+ (eval $ac_try) 2>&5 -+ ac_status=$? -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); }; } && -+ { ac_try='test -s conftest$ac_exeext' -+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 -+ (eval $ac_try) 2>&5 -+ ac_status=$? -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); }; }; then -+ ac_cv_lib_selinux_getpeercon=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ac_cv_lib_selinux_getpeercon=no -+fi -+rm -f conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon" >&5 -+echo "${ECHO_T}$ac_cv_lib_selinux_getpeercon" >&6 -+if test $ac_cv_lib_selinux_getpeercon = yes; then -+ cat >>confdefs.h <<\_ACEOF -+#define SECURITY_SYSATTR_NAME "security_context" -+_ACEOF -+ -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_SELINUX 1 -+_ACEOF -+ -+ -+else -+ { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5 -+echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;} -+ { (exit 1); exit 1; }; } -+fi -+ -+fi -+ -+# - # Elf - # - -@@ -26006,6 +26119,7 @@ s,@with_libxml@,$with_libxml,;t t - s,@with_libxslt@,$with_libxslt,;t t - s,@with_system_tzdata@,$with_system_tzdata,;t t - s,@with_zlib@,$with_zlib,;t t -+s,@enable_selinux@,$enable_selinux,;t t - s,@EGREP@,$EGREP,;t t - s,@ELF_SYS@,$ELF_SYS,;t t - s,@LDFLAGS_SL@,$LDFLAGS_SL,;t t -diff -rpNU3 pgace/configure.in sepgsql/configure.in ---- pgace/configure.in 2008-03-19 10:08:35.000000000 +0900 -+++ sepgsql/configure.in 2008-03-19 10:19:23.000000000 +0900 -@@ -626,6 +626,19 @@ PGAC_ARG_BOOL(with, zlib, yes, - AC_SUBST(with_zlib) - - # -+# NSA SELinux support -+# -+PGAC_ARG_BOOL(enable, selinux, no, -+ [ --enable-selinux build with NSA SELinux support]) -+if test "$enable_selinux" = yes; then -+ AC_CHECK_LIB(selinux, getpeercon, -+ AC_DEFINE(SECURITY_SYSATTR_NAME, "security_context") -+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1) -+ AC_SUBST(enable_selinux), -+ AC_MSG_ERROR("--enable-selinux requires libselinux.")) -+fi -+ -+# - # Elf - # - -diff -rpNU3 pgace/src/Makefile.global.in sepgsql/src/Makefile.global.in ---- pgace/src/Makefile.global.in 2007-11-18 02:56:38.000000000 +0900 -+++ sepgsql/src/Makefile.global.in 2007-11-22 23:10:13.000000000 +0900 -@@ -165,6 +165,7 @@ enable_rpath = @enable_rpath@ - enable_nls = @enable_nls@ - enable_debug = @enable_debug@ - enable_dtrace = @enable_dtrace@ -+enable_selinux = @enable_selinux@ - enable_thread_safety = @enable_thread_safety@ - - python_includespec = @python_includespec@ -diff -rpNU3 pgace/src/backend/Makefile sepgsql/src/backend/Makefile ---- pgace/src/backend/Makefile 2008-01-08 01:39:49.000000000 +0900 -+++ sepgsql/src/backend/Makefile 2008-01-08 12:56:27.000000000 +0900 -@@ -32,6 +32,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) - # The backend doesn't need everything that's in LIBS, however - LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) - -+# SELinux support needs to link libselinux -+ifeq ($(enable_selinux), yes) -+LIBS += -lselinux -+endif -+ - ########################################################################## - - all: submake-libpgport postgres $(POSTGRES_IMP) -diff -rpNU3 pgace/src/backend/security/Makefile sepgsql/src/backend/security/Makefile ---- pgace/src/backend/security/Makefile 2008-03-13 23:25:01.000000000 +0900 -+++ sepgsql/src/backend/security/Makefile 2008-03-13 23:37:15.000000000 +0900 -@@ -10,6 +10,11 @@ include $(top_builddir)/src/Makefile.glo - - OBJS := pgaceCommon.o pgaceHooks.o - -+ifeq ($(enable_selinux), yes) -+OBJS += sepgsql/core.o sepgsql/hooks.o \ -+ sepgsql/permissions.o sepgsql/proxy.o -+endif -+ - all: SUBSYS.o - - SUBSYS.o: $(OBJS) -diff -rpNU3 pgace/src/backend/security/pgaceHooks.c sepgsql/src/backend/security/pgaceHooks.c ---- pgace/src/backend/security/pgaceHooks.c 2008-03-13 23:25:01.000000000 +0900 -+++ sepgsql/src/backend/security/pgaceHooks.c 2008-03-13 23:37:15.000000000 +0900 -@@ -8,6 +8,12 @@ - - #include "security/pgace.h" - -+#ifdef HAVE_SELINUX -+#include "executor/executor.h" -+#include "security/pgace.h" -+#include "security/sepgsql.h" -+#endif /* HAVE_SELINUX */ -+ - /****************************************************************** - * Initialize / Finalize related hooks - ******************************************************************/ -@@ -19,6 +25,10 @@ - */ - Size pgaceShmemSize(void) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlShmemSize(); -+#endif - return (Size) 0; - } - -@@ -30,6 +40,10 @@ Size pgaceShmemSize(void) - */ - void pgaceInitialize(bool is_bootstrap) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlInitialize(is_bootstrap); -+#endif - /* do nothing */ - } - -@@ -40,6 +54,10 @@ void pgaceInitialize(bool is_bootstrap) - */ - bool pgaceInitializePostmaster(void) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlInitializePostmaster(); -+#endif - return true; - } - -@@ -49,6 +67,10 @@ bool pgaceInitializePostmaster(void) - */ - void pgaceFinalizePostmaster(void) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlFinalizePostmaster(); -+#endif - /* do nothing */ - } - -@@ -65,6 +87,19 @@ void pgaceFinalizePostmaster(void) - */ - List *pgaceProxyQuery(List *queryList) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) { -+ List *newList = NIL; -+ ListCell *l; -+ -+ foreach (l, queryList) { -+ Query *q = (Query *) lfirst(l); -+ -+ newList = list_concat(newList, sepgsqlProxyQuery(q)); -+ } -+ queryList = newList; -+ } -+#endif - return queryList; - } - -@@ -87,6 +122,12 @@ void pgacePortalStart(Portal portal) - */ - void pgaceExecutorStart(QueryDesc *queryDesc, int eflags) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled() && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) { -+ Assert(queryDesc->plannedstmt != NULL); -+ sepgsqlVerifyQuery(queryDesc->plannedstmt); -+ } -+#endif - /* do nothing */ - } - -@@ -107,6 +148,10 @@ void pgaceExecutorStart(QueryDesc *query - bool pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, - bool is_internal, bool with_returning) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlHeapTupleInsert(rel, tuple, is_internal, with_returning); -+#endif - return true; - } - -@@ -124,6 +169,10 @@ bool pgaceHeapTupleInsert(Relation rel, - bool pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, - bool is_internal, bool with_returning) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlHeapTupleUpdate(rel, otid, newtup, is_internal, with_returning); -+#endif - return true; - } - -@@ -140,6 +189,10 @@ bool pgaceHeapTupleUpdate(Relation rel, - bool pgaceHeapTupleDelete(Relation rel, ItemPointer otid, - bool is_internal, bool with_returning) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlHeapTupleDelete(rel, otid, is_internal, with_returning); -+#endif - return true; - } - -@@ -157,6 +210,10 @@ bool pgaceHeapTupleDelete(Relation rel, - */ - DefElem *pgaceGramSecurityItem(char *defname, char *value) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlGramSecurityItem(defname, value); -+#endif - return NULL; - } - -@@ -168,6 +225,10 @@ DefElem *pgaceGramSecurityItem(char *def - */ - bool pgaceIsGramSecurityItem(DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlIsGramSecurityItem(defel); -+#endif - return false; - } - -@@ -181,6 +242,10 @@ bool pgaceIsGramSecurityItem(DefElem *de - */ - void pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlGramCreateRelation(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -194,6 +259,10 @@ void pgaceGramCreateRelation(Relation re - */ - void pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlGramCreateAttribute(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -207,6 +276,10 @@ void pgaceGramCreateAttribute(Relation r - */ - void pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlGramAlterRelation(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -220,6 +293,10 @@ void pgaceGramAlterRelation(Relation rel - */ - void pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlGramAlterAttribute(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -233,6 +310,10 @@ void pgaceGramAlterAttribute(Relation re - */ - void pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlGramCreateDatabase(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -246,6 +327,10 @@ void pgaceGramCreateDatabase(Relation re - */ - void pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlGramAlterDatabase(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -259,6 +344,10 @@ void pgaceGramAlterDatabase(Relation rel - */ - void pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlGramCreateFunction(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -272,6 +361,10 @@ void pgaceGramCreateFunction(Relation re - */ - void pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlGramAlterFunction(rel, tuple, defel); -+#endif - /* do nothing */ - } - -@@ -288,6 +381,10 @@ void pgaceGramAlterFunction(Relation rel - */ - void pgaceSetDatabaseParam(const char *name, char *argstring) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlSetDatabaseParam(name, argstring); -+#endif - /* do nothing */ - } - -@@ -298,6 +395,10 @@ void pgaceSetDatabaseParam(const char *n - */ - void pgaceGetDatabaseParam(const char *name) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlGetDatabaseParam(name); -+#endif - /* do nothing */ - } - -@@ -313,6 +414,10 @@ void pgaceGetDatabaseParam(const char *n - */ - void pgaceCallFunction(FmgrInfo *finfo) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlCallFunction(finfo, false); -+#endif - /* do nothing */ - } - -@@ -328,6 +433,10 @@ void pgaceCallFunction(FmgrInfo *finfo) - */ - bool pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlCallFunctionTrigger(finfo, tgdata); -+#endif - return true; - } - -@@ -339,6 +448,10 @@ bool pgaceCallFunctionTrigger(FmgrInfo * - */ - void pgaceCallFunctionFastPath(FmgrInfo *finfo) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlCallFunction(finfo, true); -+#endif - /* do nothing */ - } - -@@ -350,6 +463,14 @@ void pgaceCallFunctionFastPath(FmgrInfo - */ - Datum pgacePreparePlanCheck(Relation rel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) { -+ Oid saved; -+ -+ saved = sepgsqlPreparePlanCheck(rel); -+ return ObjectIdGetDatum(saved); -+ } -+#endif - return (Datum) 0; - } - -@@ -363,6 +484,10 @@ Datum pgacePreparePlanCheck(Relation rel - */ - void pgaceRestorePlanCheck(Relation rel, Datum pgace_saved) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlRestorePlanCheck(rel, DatumGetObjectId(pgace_saved)); -+#endif - /* do nothing */ - } - -@@ -377,6 +502,10 @@ void pgaceRestorePlanCheck(Relation rel, - */ - void pgaceLockTable(Oid relid) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLockTable(relid); -+#endif - /* do nothing */ - } - -@@ -392,6 +521,10 @@ void pgaceLockTable(Oid relid) - * @isFrom : true, if the given statement is 'COPY FROM' - */ - void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlCopyTable(rel, attNumList, isFrom); -+#endif - /* do nothing */ - } - -@@ -405,6 +538,10 @@ void pgaceCopyTable(Relation rel, List * - * @tuple : the target tuple - */ - bool pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlCopyToTuple(rel, attNumList, tuple); -+#endif - return true; - } - -@@ -419,6 +556,10 @@ bool pgaceCopyToTuple(Relation rel, List - * @filename : full path name of the shared library module - */ - void pgaceLoadSharedModule(const char *filename) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLoadSharedModule(filename); -+#endif - /* do nothing */ - } - -@@ -433,7 +574,12 @@ void pgaceLoadSharedModule(const char *f - * @tuple : a tuple which is a part of the target largeobject. - */ - void pgaceLargeObjectGetSecurity(HeapTuple tuple) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectGetSecurity(tuple); -+#else - elog(ERROR, "PGACE: There is no guest module."); -+#endif - } - - /* -@@ -443,7 +589,12 @@ void pgaceLargeObjectGetSecurity(HeapTup - * @lo_security : new security attribute specified - */ - void pgaceLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectSetSecurity(tuple, lo_security); -+#else - elog(ERROR, "PGACE: There is no guest module."); -+#endif - } - - /* -@@ -453,6 +604,10 @@ void pgaceLargeObjectSetSecurity(HeapTup - * @tuple : a new tuple for the new large object - */ - void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectCreate(rel, tuple); -+#endif - /* do nothing */ - } - -@@ -464,6 +619,10 @@ void pgaceLargeObjectCreate(Relation rel - * @tuple : one of the tuples within the target large object - */ - void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectDrop(rel, tuple); -+#endif - /* do nothing */ - } - -@@ -474,6 +633,10 @@ void pgaceLargeObjectDrop(Relation rel, - * @tuple : the head tuple within the given large object - */ - void pgaceLargeObjectRead(Relation rel, HeapTuple tuple) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectRead(rel, tuple); -+#endif - /* do nothing */ - } - -@@ -485,6 +648,10 @@ void pgaceLargeObjectRead(Relation rel, - * @oldtup : the head tuple in older version, if exist - */ - void pgaceLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectWrite(rel, newtup, oldtup); -+#endif - /* do nothing */ - } - -@@ -496,6 +663,10 @@ void pgaceLargeObjectWrite(Relation rel, - * @headtup : the head tuple to be truncated. NULL means this BLOB will be expanded. - */ - void pgaceLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectTruncate(rel, loid, headtup); -+#endif - /* do nothing */ - } - -@@ -505,6 +676,10 @@ void pgaceLargeObjectTruncate(Relation r - * @fd : file descriptor to be inported - */ - void pgaceLargeObjectImport(int fd) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectImport(); -+#endif - /* do nothing */ - } - -@@ -515,6 +690,10 @@ void pgaceLargeObjectImport(int fd) { - * @loid : large object to be exported - */ - void pgaceLargeObjectExport(int fd, Oid loid) { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlLargeObjectExport(); -+#endif - /* do nothing */ - } - -@@ -531,6 +710,10 @@ void pgaceLargeObjectExport(int fd, Oid - */ - char *pgaceSecurityLabelIn(char *seclabel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ seclabel = sepgsqlSecurityLabelIn(seclabel); -+#endif - return seclabel; - } - -@@ -543,6 +726,10 @@ char *pgaceSecurityLabelIn(char *seclabe - */ - char *pgaceSecurityLabelOut(char *seclabel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ seclabel = sepgsqlSecurityLabelOut(seclabel); -+#endif - return seclabel; - } - -@@ -559,6 +746,10 @@ char *pgaceSecurityLabelOut(char *seclab - */ - char *pgaceSecurityLabelCheckValid(char *seclabel) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlSecurityLabelCheckValid(seclabel); -+#endif - return seclabel; - } - -@@ -571,6 +762,10 @@ char *pgaceSecurityLabelCheckValid(char - */ - char *pgaceSecurityLabelOfLabel(char *new_label) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlSecurityLabelOfLabel(new_label); -+#endif - return pstrdup("unlabeled"); - } - -@@ -589,6 +784,10 @@ char *pgaceSecurityLabelOfLabel(char *ne - */ - Node *pgaceCopyObject(Node *orig) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlCopyObject(orig); -+#endif - return NULL; - } - -@@ -603,6 +802,10 @@ Node *pgaceCopyObject(Node *orig) - */ - bool pgaceOutObject(StringInfo str, Node *node) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ sepgsqlOutObject(str, node); -+#endif - return false; - } - -@@ -615,6 +818,10 @@ bool pgaceOutObject(StringInfo str, Node - */ - void *pgaceReadObject(char *token) - { -+#ifdef HAVE_SELINUX -+ if (sepgsqlIsEnabled()) -+ return sepgsqlReadObject(token); -+#endif - return NULL; - } - -@@ -626,3 +833,26 @@ void *pgaceReadObject(char *token) - * In this section, you can put function stubs when your security - * module is not activated. - */ -+#ifndef HAVE_SELINUX -+/* -+ * SE-PostgreSQL adds three functions. -+ * When it is disabled, call them causes an error. -+ */ -+Datum sepgsql_getcon(PG_FUNCTION_ARGS) -+{ -+ elog(ERROR, "%s is not implemented", __FUNCTION__); -+ PG_RETURN_VOID(); -+} -+ -+Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS) -+{ -+ elog(ERROR, "%s is not implemented", __FUNCTION__); -+ PG_RETURN_VOID(); -+} -+ -+Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS) -+{ -+ elog(ERROR, "%s is not implemented", __FUNCTION__); -+ PG_RETURN_VOID(); -+} -+#endif /* HAVE_SELINUX */ -diff -rpNU3 pgace/src/backend/security/sepgsql/core.c sepgsql/src/backend/security/sepgsql/core.c ---- pgace/src/backend/security/sepgsql/core.c 1970-01-01 09:00:00.000000000 +0900 -+++ sepgsql/src/backend/security/sepgsql/core.c 2008-02-04 17:40:05.000000000 +0900 -@@ -0,0 +1,994 @@ -+/* -+ * src/backend/security/sepgsqlCore.c -+ * SE-PostgreSQL core facilities like userspace AVC, policy state monitoring. -+ * -+ * Copyright (c) 2007 KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/heapam.h" -+#include "access/genam.h" -+#include "access/tupdesc.h" -+#include "access/xact.h" -+#include "catalog/pg_database.h" -+#include "libpq/libpq-be.h" -+#include "libpq/pqsignal.h" -+#include "miscadmin.h" -+#include "security/pgace.h" -+#include "security/sepgsql.h" -+#include "storage/lwlock.h" -+#include "utils/builtins.h" -+#include "utils/fmgroids.h" -+#include "utils/rel.h" -+#include "utils/syscache.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static struct { -+ struct { -+ char *name; /* name of object class */ -+ uint16 inum; /* internal identifier number */ -+ } tclass; -+ struct { -+ char *name; /* name of access vector */ -+ uint32 inum; /* internal identifier number */ -+ } av_perms[sizeof(access_vector_t) * 8]; -+} selinux_catalog[] = { -+ { -+ { "db_database", SECCLASS_DB_DATABASE }, -+ { -+ { "create", DB_DATABASE__CREATE }, -+ { "drop", DB_DATABASE__DROP }, -+ { "getattr", DB_DATABASE__GETATTR }, -+ { "setattr", DB_DATABASE__SETATTR }, -+ { "relabelfrom", DB_DATABASE__RELABELFROM }, -+ { "relabelto", DB_DATABASE__RELABELTO }, -+ { "access", DB_DATABASE__ACCESS }, -+ { "install_module", DB_DATABASE__INSTALL_MODULE }, -+ { "load_module", DB_DATABASE__LOAD_MODULE }, -+ { "get_param", DB_DATABASE__GET_PARAM }, -+ { "set_param", DB_DATABASE__SET_PARAM }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ { "db_table", SECCLASS_DB_TABLE }, -+ { -+ { "create", DB_TABLE__CREATE }, -+ { "drop", DB_TABLE__DROP }, -+ { "getattr", DB_TABLE__GETATTR }, -+ { "setattr", DB_TABLE__SETATTR }, -+ { "relabelfrom", DB_TABLE__RELABELFROM }, -+ { "relabelto", DB_TABLE__RELABELTO }, -+ { "use", DB_TABLE__USE }, -+ { "select", DB_TABLE__SELECT }, -+ { "update", DB_TABLE__UPDATE }, -+ { "insert", DB_TABLE__INSERT }, -+ { "delete", DB_TABLE__DELETE }, -+ { "lock", DB_TABLE__LOCK }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ { "db_procedure", SECCLASS_DB_PROCEDURE }, -+ { -+ { "create", DB_PROCEDURE__CREATE }, -+ { "drop", DB_PROCEDURE__DROP }, -+ { "getattr", DB_PROCEDURE__GETATTR }, -+ { "setattr", DB_PROCEDURE__SETATTR }, -+ { "relabelfrom", DB_PROCEDURE__RELABELFROM }, -+ { "relabelto", DB_PROCEDURE__RELABELTO }, -+ { "execute", DB_PROCEDURE__EXECUTE }, -+ { "entrypoint", DB_PROCEDURE__ENTRYPOINT }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ { "db_column", SECCLASS_DB_COLUMN }, -+ { -+ { "create", DB_COLUMN__CREATE }, -+ { "drop", DB_COLUMN__DROP }, -+ { "getattr", DB_COLUMN__GETATTR }, -+ { "setattr", DB_COLUMN__SETATTR }, -+ { "relabelfrom", DB_COLUMN__RELABELFROM }, -+ { "relabelto", DB_COLUMN__RELABELTO }, -+ { "use", DB_COLUMN__USE }, -+ { "select", DB_COLUMN__SELECT }, -+ { "update", DB_COLUMN__UPDATE }, -+ { "insert", DB_COLUMN__INSERT }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ { "db_tuple", SECCLASS_DB_TUPLE }, -+ { -+ { "relabelfrom", DB_TUPLE__RELABELFROM }, -+ { "relabelto", DB_TUPLE__RELABELTO }, -+ { "use", DB_TUPLE__USE }, -+ { "select", DB_TUPLE__SELECT }, -+ { "update", DB_TUPLE__UPDATE }, -+ { "insert", DB_TUPLE__INSERT }, -+ { "delete", DB_TUPLE__DELETE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ { "db_blob", SECCLASS_DB_BLOB }, -+ { -+ { "create", DB_BLOB__CREATE }, -+ { "drop", DB_BLOB__DROP }, -+ { "getattr", DB_BLOB__GETATTR }, -+ { "setattr", DB_BLOB__SETATTR }, -+ { "relabelfrom", DB_BLOB__RELABELFROM }, -+ { "relabelto", DB_BLOB__RELABELTO }, -+ { "read", DB_BLOB__READ }, -+ { "write", DB_BLOB__WRITE }, -+ { "import", DB_BLOB__IMPORT }, -+ { "export", DB_BLOB__EXPORT }, -+ { NULL, 0UL }, -+ } -+ }, -+}; -+#define NUM_SELINUX_CATALOG (sizeof(selinux_catalog) / sizeof(selinux_catalog[0])) -+ -+static const char *sepgsql_class_to_string(uint16 tclass) -+{ -+ int i; -+ -+ for (i=0; i < NUM_SELINUX_CATALOG; i++) { -+ if (selinux_catalog[i].tclass.inum == tclass) -+ return selinux_catalog[i].tclass.name; -+ } -+ /* because tclass didn't match with userspace object classes, -+ * its external representation is always same as internal one */ -+ return security_class_to_string((security_class_t) tclass); -+} -+ -+static const char *sepgsql_av_perm_to_string(uint16 tclass, uint32 perm) -+{ -+ int i, j; -+ -+ for (i=0; i < NUM_SELINUX_CATALOG; i++) { -+ if (selinux_catalog[i].tclass.inum == tclass) { -+ char *perm_name; -+ -+ for (j=0; (perm_name = selinux_catalog[i].av_perms[j].name); j++) { -+ if (selinux_catalog[i].av_perms[j].inum == perm) -+ return perm_name; -+ } -+ return "unknown"; -+ } -+ } -+ /* because tclass/perm didn't match with userspace object classes, -+ * its external representation is always same as internal one */ -+ return security_av_perm_to_string((security_class_t) tclass, (access_vector_t) perm); -+} -+ -+/* -+ * SE-PostgreSQL Internal AVC(Access Vector Cache) implementation. -+ * -+ */ -+struct avc_datum { -+ SHMEM_OFFSET next; -+ -+ Oid ssid; /* subject context */ -+ Oid tsid; /* object context */ -+ uint16 tclass; /* object class */ -+ -+ uint32 allowed; -+ uint32 decided; -+ uint32 auditallow; -+ uint32 auditdeny; -+ -+ Oid create; /* newly created context */ -+ bool is_hot; -+}; -+ -+#define AVC_DATUM_CACHE_SLOTS 512 -+#define AVC_DATUM_CACHE_MAXNODES 800 -+static struct { -+ LWLockId lock; -+ SHMEM_OFFSET slot[AVC_DATUM_CACHE_SLOTS]; -+ SHMEM_OFFSET freelist; -+ int lru_hint; -+ int enforcing; -+ struct avc_datum entry[AVC_DATUM_CACHE_MAXNODES]; -+ -+ /* dynamic object class/av permission mapping */ -+ struct { -+ struct { -+ uint16 internal; -+ security_class_t external; -+ } tclass; -+ struct { -+ uint32 internal; -+ access_vector_t external; -+ } av_perms[sizeof(access_vector_t) * 8]; -+ } catalog[NUM_SELINUX_CATALOG]; -+} *avc_shmem = NULL; -+ -+Size sepgsqlShmemSize(void) -+{ -+ return sizeof(*avc_shmem); -+} -+ -+static void sepgsql_load_class_av_mapping() -+{ -+ extern char *selinux_mnt; -+ char buffer[PATH_MAX]; -+ struct stat st_buf; -+ int i, j, fd, len; -+ -+ if (!selinux_mnt) -+ goto legacy_mapping; -+ -+ /* Does '/selinux/class' exist? */ -+ snprintf(buffer, sizeof(buffer), "%s/class", selinux_mnt); -+ if (lstat(buffer, &st_buf) || !S_ISDIR(st_buf.st_mode)) -+ goto legacy_mapping; -+ -+ for (i=0; i < NUM_SELINUX_CATALOG; i++) { -+ /* obtain external object class number */ -+ snprintf(buffer, sizeof(buffer), "%s/class/%s/index", -+ selinux_mnt, selinux_catalog[i].tclass.name); -+ fd = open(buffer, O_RDONLY); -+ if (fd < 0) -+ goto legacy_mapping; -+ -+ len = read(fd, buffer, sizeof(buffer)); -+ close(fd); -+ if (len < 1) -+ goto legacy_mapping; -+ buffer[len] = '\0'; -+ -+ avc_shmem->catalog[i].tclass.internal -+ = selinux_catalog[i].tclass.inum; -+ avc_shmem->catalog[i].tclass.external -+ = atoi(buffer); -+ -+ /* obtain external access vector number */ -+ for (j=0; selinux_catalog[i].av_perms[j].name; j++) { -+ snprintf(buffer, sizeof(buffer), "%s/class/%s/perms/%s", -+ selinux_mnt, -+ selinux_catalog[i].tclass.name, -+ selinux_catalog[i].av_perms[j].name); -+ fd = open(buffer, O_RDONLY); -+ if (fd < 0) -+ goto legacy_mapping; -+ -+ len = read(fd, buffer, sizeof(buffer)); -+ close(fd); -+ if (len < 1) -+ goto legacy_mapping; -+ buffer[len] = '\0'; -+ -+ avc_shmem->catalog[i].av_perms[j].internal -+ = selinux_catalog[i].av_perms[j].inum; -+ avc_shmem->catalog[i].av_perms[j].external -+ = (0x0001UL << (atoi(buffer) - 1)); -+ } -+ } -+ return; -+ -+legacy_mapping: -+ for (i=0; i < NUM_SELINUX_CATALOG; i++) { -+ uint16 tclass = selinux_catalog[i].tclass.inum; -+ -+ avc_shmem->catalog[i].tclass.internal = tclass; -+ avc_shmem->catalog[i].tclass.external = tclass; -+ -+ for (j=0; selinux_catalog[i].av_perms[j].name; j++) { -+ uint32 av_perm = selinux_catalog[i].av_perms[j].inum; -+ -+ avc_shmem->catalog[i].av_perms[j].internal = av_perm; -+ avc_shmem->catalog[i].av_perms[j].external = av_perm; -+ } -+ } -+ return; -+} -+ -+static void sepgsql_avc_reset() -+{ -+ int i, enforcing; -+ -+ enforcing = security_getenforce(); -+ Assert(enforcing==0 || enforcing==1); -+ -+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); -+ -+ for (i=0; i < AVC_DATUM_CACHE_SLOTS; i++) -+ avc_shmem->slot[i] = INVALID_OFFSET; -+ avc_shmem->freelist = INVALID_OFFSET; -+ for (i=0; i < AVC_DATUM_CACHE_MAXNODES; i++) { -+ struct avc_datum *avd = avc_shmem->entry + i; -+ -+ memset(avd, 0, sizeof(struct avc_datum)); -+ avd->next = avc_shmem->freelist; -+ avc_shmem->freelist = MAKE_OFFSET(avd); -+ } -+ sepgsql_load_class_av_mapping(); -+ avc_shmem->enforcing = enforcing; -+ -+ LWLockRelease(avc_shmem->lock); -+} -+ -+static void sepgsql_avc_init() -+{ -+ bool found_avc; -+ -+ avc_shmem = ShmemInitStruct("SELinux userspace AVC", -+ sepgsqlShmemSize(), &found_avc); -+ if (!found_avc) { -+ avc_shmem->lock = LWLockAssign(); -+ sepgsql_avc_reset(); -+ } -+} -+ -+static uint32 sepgsql_validate_av_perms(security_class_t tclass, access_vector_t perms) -+{ -+ /* we have to hold LW_SHARED lock at least */ -+ int i, j; -+ -+ for (i=0; i < NUM_SELINUX_CATALOG; i++) { -+ if (avc_shmem->catalog[i].tclass.external == tclass) { -+ uint32 __perms = 0; -+ -+ for (j=0; j < sizeof(access_vector_t) * 8; j++) { -+ if (avc_shmem->catalog[i].av_perms[j].external & perms) -+ __perms |= avc_shmem->catalog[i].av_perms[j].internal; -+ } -+ return __perms; -+ } -+ } -+ return (uint32) perms; -+} -+ -+static void sepgsql_compute_avc_datum(Oid ssid, Oid tsid, uint16 tclass, -+ struct avc_datum *avd) -+{ -+ security_class_t tclass_external = tclass; -+ security_context_t scon, tcon, ncon; -+ struct av_decision x; -+ Datum tmp; -+ int i; -+ -+ memset(avd, 0, sizeof(struct avc_datum)); -+ tmp = DirectFunctionCall1(security_label_raw_out, -+ ObjectIdGetDatum(ssid)); -+ scon = DatumGetCString(tmp); -+ tmp = DirectFunctionCall1(security_label_raw_out, -+ ObjectIdGetDatum(tsid)); -+ tcon = DatumGetCString(tmp); -+ -+ LWLockAcquire(avc_shmem->lock, LW_SHARED); -+ /* translate internal tclass into external one, to query the kernel */ -+ for (i=0; i < NUM_SELINUX_CATALOG; i++) { -+ if (avc_shmem->catalog[i].tclass.internal == tclass) { -+ tclass_external = avc_shmem->catalog[i].tclass.external; -+ break; -+ } -+ } -+ -+ if (security_compute_av_raw(scon, tcon, tclass_external, 0, &x)) -+ elog(ERROR, "SELinux: could not compute an access vector decision" -+ " scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass); -+ if (security_compute_create_raw(scon, tcon, tclass_external, &ncon) != 0) -+ elog(ERROR, "SELinux: could not compute an implicit security context" -+ " scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass); -+ -+ avd->ssid = ssid; -+ avd->tsid = tsid; -+ avd->tclass = tclass; -+ -+ avd->allowed = sepgsql_validate_av_perms(tclass_external, x.allowed); -+ avd->decided = sepgsql_validate_av_perms(tclass_external, x.decided); -+ avd->auditallow = sepgsql_validate_av_perms(tclass_external, x.auditallow); -+ avd->auditdeny = sepgsql_validate_av_perms(tclass_external, x.auditdeny); -+ LWLockRelease(avc_shmem->lock); -+ -+ PG_TRY(); -+ { -+ tmp = DirectFunctionCall1(security_label_raw_in, -+ CStringGetDatum(ncon)); -+ avd->create = DatumGetObjectId(tmp); -+ } -+ PG_CATCH(); -+ { -+ freecon(ncon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ -+ pfree(scon); -+ pfree(tcon); -+ freecon(ncon); -+} -+ -+static Oid sepgsql_compute_relabel(Oid ssid, Oid tsid, uint16 tclass) -+{ -+ security_context_t scon, tcon, ncon; -+ Oid nsid; -+ Datum tmp; -+ -+ tmp = DirectFunctionCall1(security_label_raw_out, -+ ObjectIdGetDatum(ssid)); -+ scon = DatumGetCString(tmp); -+ tmp = DirectFunctionCall1(security_label_raw_out, -+ ObjectIdGetDatum(tsid)); -+ tcon = DatumGetCString(tmp); -+ -+ if (security_compute_relabel_raw(scon, tcon, tclass, &ncon) != 0) -+ elog(ERROR, "SELinux: could not compute a relabeled security context" -+ " scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass); -+ -+ PG_TRY(); -+ { -+ tmp = DirectFunctionCall1(security_label_raw_in, -+ CStringGetDatum(ncon)); -+ nsid = DatumGetObjectId(tmp); -+ } -+ PG_CATCH(); -+ { -+ freecon(ncon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ -+ freecon(ncon); -+ pfree(scon); -+ pfree(tcon); -+ -+ return nsid; -+} -+ -+static bool __avc_audit(uint32 perms, struct avc_datum *avd, char *objname, -+ char *audit_buf, int buflen) -+{ -+ /* we have to hold LW_SHARED lock at least */ -+ uint32 denied, audited, mask; -+ char *context; -+ int ofs = 0; -+ -+ denied = perms & ~avd->allowed; -+ audited = denied ? (denied & avd->auditdeny) : (perms & avd->auditallow); -+ if (!audited) -+ return false; -+ -+ ofs += snprintf(audit_buf + ofs, buflen - ofs, "%s {", -+ denied ? "denied" : "granted"); -+ for (mask=1; mask; mask <<= 1) { -+ if (audited & mask) { -+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " %s", -+ sepgsql_av_perm_to_string(avd->tclass, mask)); -+ } -+ } -+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " }"); -+ -+ context = DatumGetCString(DirectFunctionCall1(security_label_out, -+ ObjectIdGetDatum(avd->ssid))); -+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " scontext=%s", context); -+ pfree(context); -+ -+ context = DatumGetCString(DirectFunctionCall1(security_label_out, -+ ObjectIdGetDatum(avd->tsid))); -+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " tcontext=%s", context); -+ pfree(context); -+ -+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " tclass=%s", -+ sepgsql_class_to_string(avd->tclass)); -+ if (objname) -+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " name=%s", objname); -+ -+ return true; -+} -+ -+static inline int sepgsql_avc_hash(Oid ssid, Oid tsid, uint16 tclass) -+{ -+ return ((uint32)ssid ^ ((uint32)tsid << 2) ^ tclass) % AVC_DATUM_CACHE_SLOTS; -+} -+ -+static struct avc_datum * -+sepgsql_avc_lookup(Oid ssid, Oid tsid, uint16 tclass, uint32 perms) -+{ -+ /* we have to hold LW_SHARED lock at least */ -+ struct avc_datum *avd; -+ SHMEM_OFFSET curr; -+ int hashkey = sepgsql_avc_hash(ssid, tsid, tclass); -+ -+ for (curr = avc_shmem->slot[hashkey]; -+ SHM_OFFSET_VALID(curr); -+ curr = avd->next) { -+ avd = (void *)MAKE_PTR(curr); -+ if (avd->ssid==ssid && avd->tsid==tsid && avd->tclass==tclass -+ && (perms & avd->decided)==perms) -+ return avd; -+ } -+ return NULL; -+} -+ -+static void sepgsql_avc_reclaim() { -+ /* we have to hold LW_EXCLUSIVE lock */ -+ SHMEM_OFFSET *prev, next; -+ struct avc_datum *avd; -+ -+ while (!SHM_OFFSET_VALID(avc_shmem->freelist)) { -+ prev = avc_shmem->slot + avc_shmem->lru_hint; -+ next = *prev; -+ while (!SHM_OFFSET_VALID(next)) { -+ avd = (void *)MAKE_PTR(next); -+ next = avd->next; -+ if (avd->is_hot) { -+ avd->is_hot = false; -+ } else { -+ *prev = avd->next; -+ avd->next = avc_shmem->freelist; -+ avc_shmem->freelist = MAKE_OFFSET(avd); -+ } -+ avd = (void *)MAKE_PTR(next); -+ } -+ avc_shmem->lru_hint = (avc_shmem->lru_hint + 1) % AVC_DATUM_CACHE_SLOTS; -+ } -+} -+ -+static void sepgsql_avc_insert(struct avc_datum *tmp) -+{ -+ /* we have to hold LW_EXCLUSIVE lock */ -+ struct avc_datum *avd; -+ int hashkey; -+ -+ avd = sepgsql_avc_lookup(tmp->ssid, tmp->tsid, tmp->tclass, tmp->decided); -+ if (avd) -+ return; -+ -+ if (!SHM_OFFSET_VALID(avc_shmem->freelist)) -+ sepgsql_avc_reclaim(); -+ Assert(SHM_OFFSET_VALID(avc_shmem->freelist)); -+ -+ avd = (void *)MAKE_PTR(avc_shmem->freelist); -+ avc_shmem->freelist = avd->next; -+ -+ memcpy(avd, tmp, sizeof(struct avc_datum)); -+ avd->is_hot = true; -+ -+ hashkey = sepgsql_avc_hash(avd->ssid, avd->tsid, avd->tclass); -+ avd->next = avc_shmem->slot[hashkey]; -+ avc_shmem->slot[hashkey] = MAKE_OFFSET(avd); -+ -+ return; -+} -+ -+static bool __avc_permission(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, -+ char *objname, struct avc_datum *local_avd) -+{ -+ struct avc_datum *avd; -+ uint32 denied; -+ bool rc = true; -+ bool wlock = false; -+ -+ LWLockAcquire(avc_shmem->lock, LW_SHARED); -+retry: -+ avd = sepgsql_avc_lookup(ssid, tsid, tclass, perms); -+ if (!avd) { -+ LWLockRelease(avc_shmem->lock); -+ -+ sepgsql_compute_avc_datum(ssid, tsid, tclass, local_avd); -+ -+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); -+ wlock = true; -+ sepgsql_avc_insert(local_avd); -+ } else { -+ memcpy(local_avd, avd, sizeof(struct avc_datum)); -+ } -+ denied = perms & ~local_avd->allowed; -+ if (!perms || denied) { -+ if (avc_shmem->enforcing) { -+ errno = EACCES; -+ rc = false; -+ } else { -+ if (!wlock) { -+ /* update avd need LW_EXCLUSIVE lock onto shmem */ -+ LWLockRelease(avc_shmem->lock); -+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); -+ wlock = true; -+ goto retry; -+ } -+ /* grant permission to avoid flood of access denied log */ -+ if (!avd) -+ avd = sepgsql_avc_lookup(ssid, tsid, tclass, perms); -+ if (avd) -+ avd->allowed |= denied; -+ } -+ } -+ LWLockRelease(avc_shmem->lock); -+ -+ return rc; -+} -+ -+void sepgsql_avc_permission(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, char *objname) -+{ -+ struct avc_datum local_avd; -+ char audit_buf[4096]; -+ bool rc; -+ -+ rc = __avc_permission(ssid, tsid, tclass, perms, objname, &local_avd); -+ if (__avc_audit(perms, &local_avd, objname, -+ audit_buf, sizeof(audit_buf))) { -+ elog(rc ? NOTICE : ERROR, "SELinux: %s", audit_buf); -+ } else if (rc != true) { -+ elog(ERROR, "SELinux: security policy violation."); -+ } -+} -+ -+bool sepgsql_avc_permission_noabort(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, char *objname) -+{ -+ struct avc_datum local_avd; -+ char audit_buf[4096]; -+ bool rc; -+ -+ rc = __avc_permission(ssid, tsid, tclass, perms, objname, &local_avd); -+ if (__avc_audit(perms, &local_avd, objname, -+ audit_buf, sizeof(audit_buf))) { -+ elog(NOTICE, "SELinux: %s", audit_buf); -+ } -+ return rc; -+} -+ -+Oid sepgsql_avc_createcon(Oid ssid, Oid tsid, uint16 tclass) -+{ -+ struct avc_datum *avd, local_avd; -+ Oid nsid; -+ -+ LWLockAcquire(avc_shmem->lock, LW_SHARED); -+ avd = sepgsql_avc_lookup(ssid, tsid, tclass, 0); -+ if (!avd) { -+ LWLockRelease(avc_shmem->lock); -+ -+ sepgsql_compute_avc_datum(ssid, tsid, tclass, &local_avd); -+ -+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); -+ sepgsql_avc_insert(&local_avd); -+ nsid = local_avd.create; -+ } else { -+ nsid = avd->create; -+ } -+ LWLockRelease(avc_shmem->lock); -+ -+ return nsid; -+} -+ -+Oid sepgsql_avc_relabelcon(Oid ssid, Oid tsid, uint16 tclass) -+{ -+ /* currently no avc support on relabeling */ -+ return sepgsql_compute_relabel(ssid, tsid, tclass); -+} -+ -+/* sepgsql_getcon() -- returns a security context of client */ -+Datum -+sepgsql_getcon(PG_FUNCTION_ARGS) -+{ -+ PG_RETURN_OID(sepgsqlGetClientContext()); -+} -+ -+/* sepgsql_system_getcon() -- obtain the server's context */ -+static Oid sepgsql_system_getcon() -+{ -+ security_context_t context; -+ Oid ssid; -+ -+ if (getcon_raw(&context) != 0) -+ elog(ERROR, "SELinux: could not obtain security context of server process"); -+ -+ PG_TRY(); -+ { -+ ssid = DatumGetObjectId(DirectFunctionCall1(security_label_raw_in, -+ CStringGetDatum(context))); -+ } -+ PG_CATCH(); -+ { -+ freecon(context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(context); -+ return ssid; -+} -+ -+/* sepgsql_system_getpeercon() -- obtain the client's context */ -+static Oid sepgsql_system_getpeercon(int sockfd) -+{ -+ security_context_t context, __context; -+ Oid ssid; -+ -+ if (getpeercon_raw(sockfd, &context)) { -+ /* we can set finally fallbacked context */ -+ __context = getenv("SEPGSQL_FALLBACK_CONTEXT"); -+ if (!__context) -+ elog(ERROR, "SELinux: could not obtain security context of database client"); -+ if (security_check_context(__context) || -+ selinux_trans_to_raw_context(__context, &context)) -+ elog(ERROR, "SELinux: '%s' is not a valid context", __context); -+ } -+ -+ PG_TRY(); -+ { -+ ssid = DatumGetObjectId(DirectFunctionCall1(security_label_raw_in, -+ CStringGetDatum(context))); -+ } -+ PG_CATCH(); -+ { -+ freecon(context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(context); -+ return ssid; -+} -+ -+/* -+ * SE-PostgreSQL core functions -+ * -+ * sepgsqlGetServerContext() -- obtains server's context -+ * sepgsqlGetClientContext() -- obtains client's context via getpeercon() -+ * sepgsqlSetClientContext() -- changes client's context for trusted procedure -+ * sepgsqlInitialize() -- called when initializing 'postgres' includes bootstraping -+ * sepgsqlInitializePostmaster() -- called when initializing 'postmaster' -+ * sepgsqlFinalizePostmaster() -- called when finalizing 'postmaster' to kill -+ * policy state monitoring process. -+ * sepgsqlMonitoringPolicyState() -- is implementation of policy state monitoring -+ * process. -+ * -+ */ -+static Oid sepgsqlServerContext = InvalidOid; -+static Oid sepgsqlClientContext = InvalidOid; -+ -+Oid sepgsqlGetServerContext() -+{ -+ return sepgsqlServerContext; -+} -+ -+Oid sepgsqlGetClientContext() -+{ -+ return sepgsqlClientContext; -+} -+ -+void sepgsqlSetClientContext(Oid new_context) -+{ -+ sepgsqlClientContext = new_context; -+} -+ -+Oid sepgsqlGetDatabaseContext() -+{ -+ HeapTuple tuple; -+ Oid datcon; -+ -+ if (IsBootstrapProcessingMode()) { -+ return sepgsql_avc_createcon(sepgsqlGetClientContext(), -+ sepgsqlGetServerContext(), -+ SECCLASS_DB_DATABASE); -+ } -+ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(MyDatabaseId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for database %u", MyDatabaseId); -+ datcon = HeapTupleGetSecurity(tuple); -+ ReleaseSysCache(tuple); -+ -+ return datcon; -+} -+ -+char *sepgsqlGetDatabaseName() -+{ -+ Form_pg_database dat_form; -+ HeapTuple tuple; -+ char *datname; -+ -+ if (IsBootstrapProcessingMode()) -+ return NULL; -+ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(MyDatabaseId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for database %u", MyDatabaseId); -+ dat_form = (Form_pg_database) GETSTRUCT(tuple); -+ datname = pstrdup(NameStr(dat_form->datname)); -+ ReleaseSysCache(tuple); -+ -+ return datname; -+} -+ -+void sepgsqlInitialize(bool is_bootstrap) -+{ -+ sepgsql_avc_init(); -+ -+ if (IsBootstrapProcessingMode()) { -+ sepgsqlServerContext = sepgsql_system_getcon(); -+ sepgsqlClientContext = sepgsql_system_getcon(); -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ sepgsqlGetDatabaseContext(), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__ACCESS, -+ NULL); -+ return; -+ } -+ -+ /* obtain security context of server process */ -+ sepgsqlServerContext = sepgsql_system_getcon(); -+ -+ /* obtain security context of client process */ -+ if (MyProcPort != NULL) { -+ sepgsqlClientContext = sepgsql_system_getpeercon(MyProcPort->sock); -+ } else { -+ sepgsqlClientContext = sepgsql_system_getcon(); -+ } -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ sepgsqlGetDatabaseContext(), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__ACCESS, -+ sepgsqlGetDatabaseName()); -+} -+ -+/* sepgsqlMonitoringPolicyState() is worker process to monitor -+ * the status of SELinux policy. When it is changed, light after the worker -+ * thread receive a notification via netlink socket. The notification is -+ * delivered into any PostgreSQL instance by reseting shared avc. -+ */ -+static void sepgsqlMonitoringPolicyState_SIGHUP(int signum) -+{ -+ elog(NOTICE, "SELinux: userspace AVC reset"); -+ sepgsql_avc_reset(); -+} -+ -+static int sepgsqlMonitoringPolicyState() -+{ -+ char buffer[2048]; -+ struct sockaddr_nl addr; -+ socklen_t addrlen; -+ struct nlmsghdr *nlh; -+ int i, rc, nl_sockfd; -+ -+ /* close listen port */ -+ for (i=3; !close(i); i++); -+ -+ /* map shared memory segment */ -+ sepgsql_avc_init(); -+ -+ /* setup the signal handler */ -+ pqinitmask(); -+ pqsignal(SIGHUP, sepgsqlMonitoringPolicyState_SIGHUP); -+ pqsignal(SIGINT, SIG_DFL); -+ pqsignal(SIGQUIT, SIG_DFL); -+ pqsignal(SIGTERM, SIG_DFL); -+ pqsignal(SIGUSR1, SIG_DFL); -+ pqsignal(SIGUSR2, SIG_DFL); -+ pqsignal(SIGCHLD, SIG_DFL); -+ PG_SETMASK(&UnBlockSig); -+ -+ /* open netlink socket */ -+ nl_sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX); -+ if (nl_sockfd < 0) { -+ elog(NOTICE, "SELinux: could not open netlink socket"); -+ return 1; -+ } -+ -+ memset(&addr, 0, sizeof(addr)); -+ addr.nl_family = AF_NETLINK; -+ addr.nl_groups = SELNL_GRP_AVC; -+ if (bind(nl_sockfd, (struct sockaddr *)&addr, sizeof(addr))) { -+ elog(NOTICE, "SELinux: could not bint netlink socket"); -+ return 1; -+ } -+ -+ /* waiting loop */ -+ while (true) { -+ addrlen = sizeof(addr); -+ rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0, -+ (struct sockaddr *)&addr, &addrlen); -+ if (rc < 0) { -+ if (errno == EINTR) -+ continue; -+ elog(NOTICE, "SELinux: netlink recvfrom() errno=%d (%s)", -+ errno, strerror(errno)); -+ return 1; -+ } -+ -+ if (addrlen != sizeof(addr)) { -+ elog(NOTICE, "SELinux: netlink address truncated (len=%d)", addrlen); -+ return 1; -+ } -+ -+ if (addr.nl_pid) { -+ elog(NOTICE, "SELinux: netlink received spoofed packet from: %u", addr.nl_pid); -+ continue; -+ } -+ -+ if (rc == 0) { -+ elog(NOTICE, "SELinux: netlink received EOF on socket"); -+ return 1; -+ } -+ -+ nlh = (struct nlmsghdr *)buffer; -+ -+ if (nlh->nlmsg_flags & MSG_TRUNC -+ || nlh->nlmsg_len > (unsigned int)rc) { -+ elog(NOTICE, "SELinux: netlink incomplete netlink message"); -+ return 1; -+ } -+ -+ switch (nlh->nlmsg_type) { -+ case NLMSG_ERROR: { -+ struct nlmsgerr *err = NLMSG_DATA(nlh); -+ if (err->error == 0) -+ break; -+ elog(NOTICE, "SELinux: netlink error message %d", -err->error); -+ return 1; -+ } -+ case SELNL_MSG_SETENFORCE: { -+ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh); -+ elog(NOTICE, "SELinux: netlink received setenforce notice (enforcing=%d)", msg->val); -+ sepgsql_avc_reset(); -+ break; -+ } -+ case SELNL_MSG_POLICYLOAD: { -+ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh); -+ elog(NOTICE, "SELinux: netlink received policyload notice (seqno=%d)", msg->seqno); -+ sepgsql_avc_reset(); -+ break; -+ } -+ default: -+ elog(NOTICE, "SELinux: netlink unknown message type (%d)", nlh->nlmsg_type); -+ return 1; -+ } -+ } -+ return 0; -+} -+ -+static pid_t MonitoringPolicyStatePid = -1; -+ -+int sepgsqlInitializePostmaster() -+{ -+ MonitoringPolicyStatePid = fork(); -+ if (MonitoringPolicyStatePid == 0) { -+ exit(sepgsqlMonitoringPolicyState()); -+ } else if (MonitoringPolicyStatePid < 0) { -+ elog(NOTICE, "SELinux: could not create a policy state monitoring process."); -+ return false; -+ } -+ return true; -+} -+ -+void sepgsqlFinalizePostmaster() -+{ -+ int status; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (MonitoringPolicyStatePid > 0) { -+ if (kill(MonitoringPolicyStatePid, SIGTERM) < 0) { -+ elog(NOTICE, "SELinux: could not kill(%u, SIGTERM), (%s)", -+ MonitoringPolicyStatePid, strerror(errno)); -+ return; -+ } -+ waitpid(MonitoringPolicyStatePid, &status, 0); -+ } -+} -+ -+bool sepgsqlIsEnabled() -+{ -+ static int enabled = -1; -+ -+ if (enabled < 0) -+ enabled = is_selinux_enabled(); -+ -+ return enabled > 0 ? true : false; -+} -diff -rpNU3 pgace/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c ---- pgace/src/backend/security/sepgsql/hooks.c 1970-01-01 09:00:00.000000000 +0900 -+++ sepgsql/src/backend/security/sepgsql/hooks.c 2008-02-04 17:40:05.000000000 +0900 -@@ -0,0 +1,667 @@ -+/* -+ * src/backend/sepgsqlHooks.c -+ * SE-PostgreSQL hooks -+ * -+ * Copyright 2007 KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/heapam.h" -+#include "access/genam.h" -+#include "access/skey.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_database.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_proc.h" -+#include "miscadmin.h" -+#include "nodes/makefuncs.h" -+#include "security/pgace.h" -+#include "security/sepgsql.h" -+#include "utils/fmgroids.h" -+#include "utils/syscache.h" -+#include -+#include -+#include -+#include -+ -+static HeapTuple __getHeapTupleFromItemPointer(Relation rel, ItemPointer tid) -+{ -+ /* obtain an old tuple */ -+ Buffer buffer; -+ PageHeader dp; -+ ItemId lp; -+ HeapTupleData tuple; -+ HeapTuple oldtup; -+ -+ buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid)); -+ LockBuffer(buffer, BUFFER_LOCK_SHARE); -+ -+ dp = (PageHeader) BufferGetPage(buffer); -+ lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); -+ -+ Assert(ItemIdIsUsed(lp)); -+ -+ tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); -+ tuple.t_len = ItemIdGetLength(lp); -+ tuple.t_self = *tid; -+ tuple.t_tableOid = RelationGetRelid(rel); -+ oldtup = heap_copytuple(&tuple); -+ -+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); -+ ReleaseBuffer(buffer); -+ -+ return oldtup; -+} -+ -+/******************************************************************************* -+ * Extended SQL statement hooks -+ *******************************************************************************/ -+DefElem *sepgsqlGramSecurityItem(char *defname, char *value) -+{ -+ DefElem *n = NULL; -+ if (!strcmp(defname, "context")) -+ n = makeDefElem(pstrdup(defname), (Node *) makeString(value)); -+ return n; -+} -+ -+bool sepgsqlIsGramSecurityItem(DefElem *defel) -+{ -+ Assert(IsA(defel, DefElem)); -+ if (defel->defname && !strcmp(defel->defname, "context")) -+ return true; -+ return false; -+} -+ -+static void __put_gram_context(HeapTuple tuple, DefElem *defel) -+{ -+ if (defel) { -+ Oid newcon = DirectFunctionCall1(security_label_in, -+ CStringGetDatum(strVal(defel->arg))); -+ HeapTupleSetSecurity(tuple, newcon); -+ } -+} -+ -+void sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+void sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+void sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+void sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+void sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+void sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+void sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+void sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) -+{ -+ __put_gram_context(tuple, defel); -+} -+ -+/******************************************************************************* -+ * DATABASE object related hooks -+ *******************************************************************************/ -+ -+void sepgsqlGetDatabaseParam(const char *name) -+{ -+ HeapTuple tuple; -+ NameData audit_name; -+ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(MyDatabaseId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database %u", MyDatabaseId); -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__GET_PARAM, -+ sepgsqlGetTupleName(DatabaseRelationId, tuple, &audit_name)); -+ ReleaseSysCache(tuple); -+} -+ -+void sepgsqlSetDatabaseParam(const char *name, char *argstring) -+{ -+ HeapTuple tuple; -+ NameData audit_name; -+ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(MyDatabaseId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database %u", MyDatabaseId); -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__SET_PARAM, -+ sepgsqlGetTupleName(DatabaseRelationId, tuple, &audit_name)); -+ ReleaseSysCache(tuple); -+} -+ -+/******************************************************************************* -+ * RELATION(Table)/ATTRIBTUE(column) object related hooks -+ *******************************************************************************/ -+void sepgsqlLockTable(Oid relid) -+{ -+ HeapTuple tuple; -+ Form_pg_class classForm; -+ NameData name; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); -+ classForm = (Form_pg_class) GETSTRUCT(tuple); -+ -+ if (classForm->relkind == RELKIND_RELATION) -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_TABLE, -+ DB_TABLE__LOCK, -+ sepgsqlGetTupleName(RelationRelationId, tuple, &name)); -+ ReleaseSysCache(tuple); -+} -+ -+/******************************************************************************* -+ * PROCEDURE related hooks -+ *******************************************************************************/ -+ -+static Datum __callTrustedProcedure(PG_FUNCTION_ARGS) -+{ -+ Oid orig_client_con; -+ Datum retval; -+ -+ /* save original security context */ -+ orig_client_con = sepgsqlGetClientContext(); -+ /* set exec context */ -+ sepgsqlSetClientContext(DatumGetObjectId(fcinfo->flinfo->fn_pgace_data)); -+ PG_TRY(); -+ { -+ retval = fcinfo->flinfo->fn_pgace_addr(fcinfo); -+ } -+ PG_CATCH(); -+ { -+ sepgsqlSetClientContext(orig_client_con); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ sepgsqlSetClientContext(orig_client_con); -+ -+ return retval; -+} -+ -+void sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check) -+{ -+ HeapTuple tuple; -+ NameData name; -+ Oid execcon; -+ uint32 perms = DB_PROCEDURE__EXECUTE; -+ -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(finfo->fn_oid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for procedure %u", finfo->fn_oid); -+ -+ /* check trusted procedure */ -+ execcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_PROCESS); -+ if (sepgsqlGetClientContext() != execcon) { -+ finfo->fn_pgace_addr = finfo->fn_addr; -+ finfo->fn_pgace_data = ObjectIdGetDatum(execcon); -+ finfo->fn_addr = __callTrustedProcedure; -+ -+ perms |= DB_PROCEDURE__ENTRYPOINT; -+ } -+ -+ if (with_perm_check) { -+ /* check procedure:{execute entrypoint} permission */ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_PROCEDURE, -+ perms, -+ sepgsqlGetTupleName(ProcedureRelationId, tuple, &name)); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+bool sepgsqlCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata) -+{ -+ Relation rel = tgdata->tg_relation; -+ HeapTuple newtup = NULL; -+ HeapTuple oldtup = NULL; -+ -+ if (TRIGGER_FIRED_FOR_STATEMENT(tgdata->tg_event)) -+ return true; /* statement trigger does not contain any tuple */ -+ if (TRIGGER_FIRED_BY_INSERT(tgdata->tg_event)) { -+ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) -+ newtup = tgdata->tg_trigtuple; -+ } else if (TRIGGER_FIRED_BY_UPDATE(tgdata->tg_event)) { -+ oldtup = tgdata->tg_trigtuple; -+ if (TRIGGER_FIRED_AFTER(tgdata->tg_event) -+ && HeapTupleGetSecurity(oldtup) != HeapTupleGetSecurity(tgdata->tg_newtuple)) -+ newtup = tgdata->tg_newtuple; -+ } else if (TRIGGER_FIRED_BY_DELETE(tgdata->tg_event)) { -+ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) -+ oldtup = tgdata->tg_trigtuple; -+ } else { -+ elog(ERROR, "SELinux: unexpected trigger event type (%u)", tgdata->tg_event); -+ } -+ if (oldtup && !sepgsqlCheckTuplePerms(rel, oldtup, NULL, SEPGSQL_PERMS_SELECT, false)) -+ return false; -+ if (newtup && !sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_SELECT, false)) -+ return false; -+ -+ sepgsqlCallFunction(finfo, false); -+ -+ return true; -+} -+ -+/******************************************************************************* -+ * LOAD shared library module hook -+ *******************************************************************************/ -+void sepgsqlLoadSharedModule(const char *filename) -+{ -+ security_context_t filecon; -+ Datum filecon_sid; -+ -+ if (getfilecon_raw(filename, &filecon) < 1) -+ elog(ERROR, "SELinux: could not obtain security context of %s", filename); -+ PG_TRY(); -+ { -+ filecon_sid = DirectFunctionCall1(security_label_raw_in, -+ CStringGetDatum(filecon)); -+ } -+ PG_CATCH(); -+ { -+ freecon(filecon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(filecon); -+ -+ sepgsql_avc_permission(sepgsqlGetDatabaseContext(), -+ DatumGetObjectId(filecon_sid), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__LOAD_MODULE, -+ (char *) filename); -+} -+ -+/******************************************************************************* -+ * Binary Large Object hooks -+ *******************************************************************************/ -+void sepgsqlLargeObjectGetSecurity(HeapTuple tuple) { -+ Oid lo_security = HeapTupleGetSecurity(tuple); -+ NameData name; -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ lo_security, -+ SECCLASS_DB_BLOB, -+ DB_BLOB__GETATTR, -+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name)); -+} -+ -+void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security) -+{ -+ NameData name; -+ -+ /* check db_blob:{setattr relabelfrom} */ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_BLOB, -+ DB_BLOB__SETATTR | DB_BLOB__RELABELFROM, -+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name)); -+ -+ /* check db_blob:{relabelto} */ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ lo_security, -+ SECCLASS_DB_BLOB, -+ DB_BLOB__RELABELTO, -+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name)); -+} -+ -+void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple) -+{ -+ Oid newcon = sepgsqlComputeImplicitContext(rel, tuple); -+ NameData name; -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ newcon, -+ SECCLASS_DB_BLOB, -+ DB_BLOB__CREATE, -+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name)); -+ HeapTupleSetSecurity(tuple, newcon); -+} -+ -+void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple) -+{ -+ NameData name; -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_BLOB, -+ DB_BLOB__DROP, -+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name)); -+} -+ -+void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple) -+{ -+ sepgsqlCheckTuplePerms(rel, tuple, NULL, -+ SEPGSQL_PERMS_SELECT | SEPGSQL_PERMS_READ, true); -+} -+ -+void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup) -+{ -+ ScanKeyData skey; -+ SysScanDesc sd; -+ HeapTuple tuple; -+ Oid loid; -+ -+ /* update existing region */ -+ if (HeapTupleIsValid(oldtup)) { -+ HeapTupleSetSecurity(newtup, HeapTupleGetSecurity(oldtup)); -+ sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_UPDATE, true); -+ return; -+ } -+ -+ /* insert a new large object page */ -+ loid = ((Form_pg_largeobject) GETSTRUCT(newtup))->loid; -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotSelf, 1, &skey); -+ tuple = systable_getnext(sd); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: large object %u does not exist", loid); -+ HeapTupleSetSecurity(newtup, HeapTupleGetSecurity(tuple)); -+ sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_UPDATE, true); -+ systable_endscan(sd); -+} -+ -+void sepgsqlLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup) { -+ ScanKeyData skey; -+ SysScanDesc sd; -+ HeapTuple tuple; -+ -+ /* simple truncating case */ -+ if (HeapTupleIsValid(headtup)) { -+ sepgsqlCheckTuplePerms(rel, headtup, NULL, SEPGSQL_PERMS_UPDATE, true); -+ return; -+ } -+ -+ /* terminated in a hole */ -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotNow, 1, &skey); -+ tuple = systable_getnext(sd); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: large object %u does not exist", loid); -+ sepgsqlCheckTuplePerms(rel, tuple, NULL, SEPGSQL_PERMS_UPDATE, true); -+ systable_endscan(sd); -+} -+ -+void sepgsqlLargeObjectImport() -+{ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ sepgsqlGetServerContext(), -+ SECCLASS_DB_BLOB, -+ DB_BLOB__IMPORT, -+ NULL); -+} -+ -+void sepgsqlLargeObjectExport() -+{ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ sepgsqlGetServerContext(), -+ SECCLASS_DB_BLOB, -+ DB_BLOB__EXPORT, -+ NULL); -+} -+ -+/******************************************************************************* -+ * security_label hooks -+ *******************************************************************************/ -+char *sepgsqlSecurityLabelIn(char *context) { -+ security_context_t raw_context, canonical_context; -+ char *result; -+ int rc; -+ -+ rc = selinux_trans_to_raw_context(context, &raw_context); -+ if (rc) -+ elog(ERROR, "SELinux: could not translate MLS label"); -+ -+ rc = security_canonicalize_context_raw(raw_context, &canonical_context); -+ freecon(raw_context); -+ if (rc) -+ elog(ERROR, "SELinux: could not formalize security context"); -+ -+ PG_TRY(); -+ { -+ result = pstrdup(canonical_context); -+ } -+ PG_CATCH(); -+ { -+ freecon(canonical_context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(canonical_context); -+ -+ return result; -+} -+ -+char *sepgsqlSecurityLabelOut(char *raw_context) { -+ security_context_t context; -+ char *result; -+ -+ if (selinux_raw_to_trans_context(raw_context, &context)) -+ elog(ERROR, "could not translate MLS label"); -+ PG_TRY(); -+ { -+ result = pstrdup(context); -+ } -+ PG_CATCH(); -+ { -+ freecon(context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(context); -+ -+ return result; -+} -+ -+char *sepgsqlSecurityLabelCheckValid(char *context) { -+ security_context_t unlbl_con; -+ char *unlbl_result = NULL; -+ -+ if (context && !security_check_context_raw(context)) -+ return context; -+ -+ /* context is invalid one */ -+ if (security_get_initial_context_raw("unlabeled", &unlbl_con)) -+ elog(ERROR, "SELinux: could not assign an alternative security context"); -+ PG_TRY(); -+ { -+ unlbl_result = pstrdup(unlbl_con); -+ } -+ PG_CATCH(); -+ { -+ freecon(unlbl_con); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(unlbl_con); -+ -+ return unlbl_result; -+} -+ -+char *sepgsqlSecurityLabelOfLabel(char *context) { -+ HeapTuple tuple; -+ security_context_t scon, tcon, ncon, _ncon; -+ int rc; -+ -+ /* obtain the security context of pg_security */ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(SecurityRelationId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for pg_security"); -+ tcon = DatumGetCString(DirectFunctionCall1(security_label_raw_out, -+ ObjectIdGetDatum(HeapTupleGetSecurity(tuple)))); -+ ReleaseSysCache(tuple); -+ -+ /* obtain server's context */ -+ rc = getcon_raw(&scon); -+ if (rc) -+ elog(ERROR, "SELinux: could not obtain server's context"); -+ -+ /* compute pg_selinux tuple context */ -+ rc = security_compute_create_raw(scon, tcon, SECCLASS_DB_TUPLE, &ncon); -+ pfree(tcon); -+ freecon(scon); -+ if (rc) -+ elog(ERROR, "SELinux: could not compute label of pg_security"); -+ -+ /* copy tuple's context */ -+ PG_TRY(); -+ { -+ _ncon = pstrdup(ncon); -+ } -+ PG_CATCH(); -+ { -+ freecon(ncon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ -+ freecon(ncon); -+ -+ return _ncon; -+} -+ -+/****************************************************************** -+ * HeapTuple modification hooks -+ ******************************************************************/ -+static bool __TrustedRelationForInternal(Relation rel) -+{ -+ if (RelationGetForm(rel)->relkind != RELKIND_RELATION) -+ return true; -+ -+ switch (RelationGetRelid(rel)) { -+ case LargeObjectRelationId: -+ case SecurityRelationId: -+ return true; -+ break; -+ } -+ return false; -+} -+ -+bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, -+ bool is_internal, bool with_returning) -+{ -+ uint32 perms; -+ -+ /* default context for no explicit labeled tuple */ -+ if (HeapTupleGetSecurity(tuple) == InvalidOid) { -+ Oid newcon = sepgsqlComputeImplicitContext(rel, tuple); -+ HeapTupleSetSecurity(tuple, newcon); -+ } -+ if (is_internal && __TrustedRelationForInternal(rel)) -+ return true; -+ -+ perms = SEPGSQL_PERMS_INSERT; -+ if (with_returning) -+ perms |= SEPGSQL_PERMS_SELECT; -+ -+ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, is_internal); -+} -+ -+bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, -+ bool is_internal, bool with_returning) -+{ -+ HeapTuple oldtup; -+ uint32 perms; -+ bool rc = true; -+ -+ oldtup = __getHeapTupleFromItemPointer(rel, otid); -+ -+ if (HeapTupleGetSecurity(newtup) == InvalidOid) { -+ /* keep old context for no explicit labeled tuple */ -+ HeapTupleSetSecurity(newtup, HeapTupleGetSecurity(oldtup)); -+ } -+ -+ if (is_internal && __TrustedRelationForInternal(rel)) -+ goto out; -+ -+ if (is_internal) { -+ perms = SEPGSQL_PERMS_UPDATE; -+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup)) -+ perms |= SEPGSQL_PERMS_RELABELFROM; -+ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal); -+ if (!rc) -+ goto out; -+ } -+ -+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup)) { -+ perms = SEPGSQL_PERMS_RELABELTO; -+ if (with_returning) -+ perms |= SEPGSQL_PERMS_SELECT; -+ rc = sepgsqlCheckTuplePerms(rel, newtup, oldtup, perms, is_internal); -+ } -+out: -+ heap_freetuple(oldtup); -+ return rc; -+} -+ -+bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, -+ bool is_internal, bool with_returning) -+{ -+ HeapTuple oldtup; -+ uint32 perms; -+ bool rc = true; -+ -+ if (is_internal) { -+ if (__TrustedRelationForInternal(rel)) -+ return true; -+ -+ oldtup = __getHeapTupleFromItemPointer(rel, otid); -+ perms = SEPGSQL_PERMS_DELETE; -+ if (with_returning) -+ perms |= SEPGSQL_PERMS_SELECT; -+ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal); -+ heap_freetuple(oldtup); -+ } -+ return rc; -+} -diff -rpNU3 pgace/src/backend/security/sepgsql/permissions.c sepgsql/src/backend/security/sepgsql/permissions.c ---- pgace/src/backend/security/sepgsql/permissions.c 1970-01-01 09:00:00.000000000 +0900 -+++ sepgsql/src/backend/security/sepgsql/permissions.c 2008-02-04 17:40:05.000000000 +0900 -@@ -0,0 +1,587 @@ -+/* -+ * src/backend/security/sepgsqlPerms.c -+ * SE-PostgreSQL permission checking functions -+ * -+ * Copyright (c) 2007 KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/genam.h" -+#include "access/heapam.h" -+#include "catalog/catalog.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_authid.h" -+#include "catalog/pg_class.h" -+#include "catalog/pg_database.h" -+#include "catalog/pg_language.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_security.h" -+#include "catalog/pg_trigger.h" -+#include "catalog/pg_type.h" -+#include "miscadmin.h" -+#include "security/pgace.h" -+#include "security/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/fmgroids.h" -+#include "utils/syscache.h" -+#include "utils/typcache.h" -+ -+/* -+ * If we have to refere a object which is newly inserted or updated -+ * in the same command, SearchSysCache() returns NULL because it use -+ * SnapshowNow internally. The followings are fallback routine to -+ * avoid a failed cache lookup. -+ */ -+static Oid __lookupRelationForm(Oid relid, Form_pg_class classForm) { -+ Relation rel; -+ SysScanDesc scan; -+ ScanKeyData skey; -+ HeapTuple tuple; -+ Oid t_security; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(tuple)) { -+ if (classForm) -+ memcpy(classForm, GETSTRUCT(tuple), sizeof(FormData_pg_class)); -+ t_security = HeapTupleGetSecurity(tuple); -+ ReleaseSysCache(tuple); -+ return t_security; -+ } -+ -+ rel = heap_open(RelationRelationId, AccessShareLock); -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ scan = systable_beginscan(rel, ClassOidIndexId, -+ true, SnapshotSelf, 1, &skey); -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); -+ -+ if (classForm) -+ memcpy(classForm, GETSTRUCT(tuple), sizeof(FormData_pg_class)); -+ t_security = HeapTupleGetSecurity(tuple); -+ -+ systable_endscan(scan); -+ heap_close(rel, AccessShareLock); -+ -+ return t_security; -+} -+ -+static uint32 __sepgsql_perms_to_common_perms(uint32 perms) { -+ uint32 __perms = 0; -+ -+ Assert((perms & ~SEPGSQL_PERMS_ALL) == 0); -+ __perms |= (perms & SEPGSQL_PERMS_USE ? COMMON_DATABASE__GETATTR : 0); -+ __perms |= (perms & SEPGSQL_PERMS_SELECT ? COMMON_DATABASE__GETATTR : 0); -+ __perms |= (perms & SEPGSQL_PERMS_UPDATE ? COMMON_DATABASE__SETATTR : 0); -+ __perms |= (perms & SEPGSQL_PERMS_INSERT ? COMMON_DATABASE__CREATE : 0); -+ __perms |= (perms & SEPGSQL_PERMS_DELETE ? COMMON_DATABASE__DROP : 0); -+ __perms |= (perms & SEPGSQL_PERMS_RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0); -+ __perms |= (perms & SEPGSQL_PERMS_RELABELTO ? COMMON_DATABASE__RELABELTO : 0); -+ -+ return __perms; -+} -+ -+static uint32 __sepgsql_perms_to_tuple_perms(uint32 perms) { -+ uint32 __perms = 0; -+ -+ Assert((perms & ~SEPGSQL_PERMS_ALL) == 0); -+ __perms |= (perms & SEPGSQL_PERMS_USE ? DB_TUPLE__USE : 0); -+ __perms |= (perms & SEPGSQL_PERMS_SELECT ? DB_TUPLE__SELECT : 0); -+ __perms |= (perms & SEPGSQL_PERMS_UPDATE ? DB_TUPLE__UPDATE : 0); -+ __perms |= (perms & SEPGSQL_PERMS_INSERT ? DB_TUPLE__INSERT : 0); -+ __perms |= (perms & SEPGSQL_PERMS_DELETE ? DB_TUPLE__DELETE : 0); -+ __perms |= (perms & SEPGSQL_PERMS_RELABELFROM ? DB_TUPLE__RELABELFROM : 0); -+ __perms |= (perms & SEPGSQL_PERMS_RELABELTO ? DB_TUPLE__RELABELTO : 0); -+ -+ return __perms; -+} -+ -+char *sepgsqlGetTupleName(Oid relid, HeapTuple tuple, NameData *name) -+{ -+ switch (relid) { -+ case AttributeRelationId: { -+ Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple); -+ HeapTuple reltup; -+ -+ if (IsBootstrapProcessingMode()) { -+ strncpy(NameStr(*name), -+ NameStr(attr->attname), -+ NAMEDATALEN); -+ return NameStr(*name); -+ } -+ reltup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(attr->attrelid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(reltup)) { -+ strncpy(NameStr(*name), -+ NameStr(attr->attname), -+ NAMEDATALEN); -+ return NameStr(*name); -+ } -+ snprintf(NameStr(*name), NAMEDATALEN, "%s.%s", -+ NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname), -+ NameStr(attr->attname)); -+ ReleaseSysCache(reltup); -+ return NameStr(*name); -+ } -+ case AuthIdRelationId: { -+ strncpy(NameStr(*name), -+ NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname), -+ NAMEDATALEN); -+ return NameStr(*name); -+ } -+ case RelationRelationId: { -+ strncpy(NameStr(*name), -+ NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname), -+ NAMEDATALEN); -+ return NameStr(*name); -+ } -+ case DatabaseRelationId: { -+ strncpy(NameStr(*name), -+ NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname), -+ NAMEDATALEN); -+ return NameStr(*name); -+ } -+ case LargeObjectRelationId: { -+ snprintf(NameStr(*name), NAMEDATALEN, "loid:%u", -+ ((Form_pg_largeobject) GETSTRUCT(tuple))->loid); -+ return NameStr(*name); -+ } -+ case ProcedureRelationId: { -+ strncpy(NameStr(*name), -+ NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname), -+ NAMEDATALEN); -+ return NameStr(*name); -+ } -+ case TriggerRelationId: { -+ strncpy(NameStr(*name), -+ NameStr(((Form_pg_trigger) GETSTRUCT(tuple))->tgname), -+ NAMEDATALEN); -+ return NameStr(*name); -+ } -+ case TypeRelationId: { -+ snprintf(NameStr(*name), NAMEDATALEN, "pg_type::%s", -+ NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname)); -+ return NameStr(*name); -+ } -+ default: -+ if (HeapTupleGetOid(tuple) != InvalidOid) { -+ snprintf(NameStr(*name), NAMEDATALEN, "relid:%u,oid:%u", -+ relid, HeapTupleGetOid(tuple)); -+ return NameStr(*name); -+ } -+ break; -+ } -+ return NULL; -+} -+ -+static void __check_pg_attribute(HeapTuple tuple, HeapTuple oldtup, -+ uint32 *p_perms, uint16 *p_tclass) -+{ -+ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ FormData_pg_class classForm; -+ -+ switch (attrForm->attrelid) { -+ case TypeRelationId: -+ case ProcedureRelationId: -+ case AttributeRelationId: -+ case RelationRelationId: -+ /* those are pure relation */ -+ break; -+ default: -+ __lookupRelationForm(attrForm->attrelid, &classForm); -+ if (classForm.relkind != RELKIND_RELATION) { -+ *p_tclass = SECCLASS_DB_TUPLE; -+ *p_perms = __sepgsql_perms_to_tuple_perms(*p_perms); -+ return; -+ } -+ break; -+ } -+ *p_tclass = SECCLASS_DB_COLUMN; -+ *p_perms = __sepgsql_perms_to_common_perms(*p_perms); -+ if (HeapTupleIsValid(oldtup)) { -+ Form_pg_attribute oldForm = (Form_pg_attribute) GETSTRUCT(oldtup); -+ -+ if (oldForm->attisdropped != true && attrForm->attisdropped == true) -+ *p_perms |= DB_COLUMN__DROP; -+ } -+} -+ -+static void __check_pg_largeobject(HeapTuple tuple, HeapTuple oldtup, -+ uint32 *p_perms, uint16 *p_tclass) -+{ -+ Form_pg_largeobject loForm = (Form_pg_largeobject) GETSTRUCT(tuple); -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc sd; -+ HeapTuple exttup; -+ uint32 perms = 0; -+ -+ perms |= (*p_perms & SEPGSQL_PERMS_USE ? DB_BLOB__GETATTR : 0); -+ perms |= (*p_perms & SEPGSQL_PERMS_SELECT ? DB_BLOB__GETATTR : 0); -+ perms |= (*p_perms & SEPGSQL_PERMS_UPDATE ? DB_BLOB__SETATTR | DB_BLOB__WRITE : 0); -+ perms |= (*p_perms & SEPGSQL_PERMS_RELABELFROM ? DB_BLOB__RELABELFROM : 0); -+ perms |= (*p_perms & SEPGSQL_PERMS_READ ? DB_BLOB__READ : 0); -+ perms |= (*p_perms & SEPGSQL_PERMS_WRITE ? DB_BLOB__WRITE : 0); -+ -+ if (*p_perms & SEPGSQL_PERMS_INSERT) { -+ perms |= DB_BLOB__SETATTR | DB_BLOB__WRITE; -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loForm->loid)); -+ rel = heap_open(LargeObjectRelationId, AccessShareLock); -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotSelf, 1, &skey); -+ /* INSERT the first one means create a largeobject */ -+ exttup = systable_getnext(sd); -+ if (!HeapTupleIsValid(exttup)) { -+ perms |= DB_BLOB__CREATE; -+ } else if (HeapTupleGetSecurity(tuple) != HeapTupleGetSecurity(exttup)) { -+ elog(ERROR, "SELinux: inconsistent security context specified"); -+ } -+ systable_endscan(sd); -+ heap_close(rel, AccessShareLock); -+ } -+ -+ if (*p_perms & SEPGSQL_PERMS_DELETE) { -+ bool found = false; -+ -+ perms |= DB_BLOB__SETATTR | DB_BLOB__WRITE; -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loForm->loid)); -+ rel = heap_open(LargeObjectRelationId, AccessShareLock); -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotSelf, 1, &skey); -+ while ((exttup = systable_getnext(sd))) { -+ int __pageno = ((Form_pg_largeobject) GETSTRUCT(exttup))->pageno; -+ -+ if (loForm->pageno != __pageno) { -+ found = true; -+ break; -+ } -+ } -+ systable_endscan(sd); -+ heap_close(rel, AccessShareLock); -+ -+ /* -+ * If this tuple is the last one with given large object, -+ * it means to drop the whole of large object. -+ */ -+ if (!found) -+ perms |= DB_BLOB__DROP; -+ } -+ -+ /* -+ * SE-PostgreSQL does not allow different security contexts are -+ * held in a single large object. -+ */ -+ if (*p_perms & SEPGSQL_PERMS_RELABELTO) { -+ bool found = false; -+ -+ perms |= DB_BLOB__RELABELTO; -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loForm->loid)); -+ rel = heap_open(LargeObjectRelationId, AccessShareLock); -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotSelf, 1, &skey); -+ while ((exttup = systable_getnext(sd))) { -+ int __pageno = ((Form_pg_largeobject) GETSTRUCT(exttup))->pageno; -+ -+ if (loForm->pageno != __pageno) { -+ found = true; -+ break; -+ } -+ } -+ systable_endscan(sd); -+ heap_close(rel, AccessShareLock); -+ -+ if (found) -+ elog(ERROR, -+ "SELinux: It's not possible a part of tuples within" -+ " a single large object to have different security context." -+ " You can use lo_set_security() instead."); -+ } -+ *p_tclass = SECCLASS_DB_BLOB; -+ *p_perms = perms; -+} -+ -+static void __check_pg_proc(HeapTuple tuple, HeapTuple oldtup, -+ uint32 *p_perms, uint16 *p_tclass) -+{ -+ uint32 perms = __sepgsql_perms_to_common_perms(*p_perms); -+ Form_pg_proc procForm = (Form_pg_proc) GETSTRUCT(tuple); -+ -+ if (procForm->prolang == ClanguageId) { -+ Datum oldbin, newbin; -+ bool isnull, verify = false; -+ -+ newbin = SysCacheGetAttr(PROCOID, tuple, -+ Anum_pg_proc_probin, &isnull); -+ if (!isnull) { -+ if (perms & DB_PROCEDURE__CREATE) { -+ verify = true; -+ } else if (HeapTupleIsValid(oldtup)) { -+ oldbin = SysCacheGetAttr(PROCOID, oldtup, -+ Anum_pg_proc_probin, &isnull); -+ if (isnull || DatumGetBool(DirectFunctionCall2(textne, oldbin, newbin))) -+ verify = true; -+ } -+ -+ if (verify) { -+ char *filename; -+ security_context_t filecon; -+ Datum filesid; -+ -+ /* <-- database:module_install --> */ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ sepgsqlGetDatabaseContext(), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__INSTALL_MODULE, -+ NULL); -+ -+ /* <-- database:module_install --> */ -+ filename = DatumGetCString(DirectFunctionCall1(textout, newbin)); -+ filename = expand_dynamic_library_name(filename); -+ if (getfilecon_raw(filename, &filecon) < 1) -+ elog(ERROR, "could not obtain the security context of '%s'", filename); -+ PG_TRY(); -+ { -+ filesid = DirectFunctionCall1(security_label_raw_in, -+ CStringGetDatum(filecon)); -+ } -+ PG_CATCH(); -+ { -+ freecon(filecon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(filecon); -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ DatumGetObjectId(filesid), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__INSTALL_MODULE, -+ filename); -+ } -+ } -+ } -+ *p_perms = perms; -+ *p_tclass = SECCLASS_DB_PROCEDURE; -+} -+ -+static void __check_pg_relation(HeapTuple tuple, HeapTuple oldtup, -+ uint32 *p_perms, uint16 *p_tclass) -+{ -+ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); -+ if (classForm->relkind == RELKIND_RELATION) { -+ *p_tclass = SECCLASS_DB_TABLE; -+ *p_perms = __sepgsql_perms_to_common_perms(*p_perms); -+ } else { -+ *p_tclass = SECCLASS_DB_TUPLE; -+ *p_perms = __sepgsql_perms_to_tuple_perms(*p_perms); -+ } -+} -+ -+static bool __check_tuple_perms(Oid tableoid, Oid tcontext, uint32 perms, -+ HeapTuple tuple, HeapTuple oldtup, bool abort) -+{ -+ uint16 tclass; -+ bool rc = true; -+ -+ Assert(tuple != NULL); -+ -+ switch (tableoid) { -+ case DatabaseRelationId: /* pg_database */ -+ perms = __sepgsql_perms_to_common_perms(perms); -+ tclass = SECCLASS_DB_DATABASE; -+ break; -+ -+ case RelationRelationId: /* pg_class */ -+ __check_pg_relation(tuple, oldtup, &perms, &tclass); -+ break; -+ -+ case AttributeRelationId: /* pg_attribute */ -+ __check_pg_attribute(tuple, oldtup, &perms, &tclass); -+ break; -+ -+ case ProcedureRelationId: /* pg_proc */ -+ __check_pg_proc(tuple, oldtup, &perms, &tclass); -+ break; -+ -+ case LargeObjectRelationId: /* pg_largeobject */ -+ __check_pg_largeobject(tuple, oldtup, &perms, &tclass); -+ break; -+ -+ default: -+ perms = __sepgsql_perms_to_tuple_perms(perms); -+ tclass = SECCLASS_DB_TUPLE; -+ break; -+ } -+ -+ if (perms) { -+ NameData name; -+ -+ if (abort) { -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ tcontext, -+ tclass, -+ perms, -+ sepgsqlGetTupleName(tableoid, tuple, &name)); -+ } else { -+ rc = sepgsql_avc_permission_noabort(sepgsqlGetClientContext(), -+ tcontext, -+ tclass, -+ perms, -+ sepgsqlGetTupleName(tableoid, tuple, &name)); -+ } -+ } -+ return rc; -+} -+ -+/* -+ * MEMO: we cannot obtain system column from RECORD datatype. -+ * If those are necesasry, they should be separately delivered. -+ */ -+Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS) -+{ -+ Oid tableoid = PG_GETARG_OID(0); -+ Oid tcontext = PG_GETARG_OID(1); -+ uint32 perms = PG_GETARG_UINT32(2); -+ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(3); -+ HeapTupleData tuple; -+ -+ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); -+ ItemPointerSetInvalid(&tuple.t_self); -+ tuple.t_tableOid = tableoid; -+ tuple.t_data = rec; -+ -+ PG_RETURN_BOOL(__check_tuple_perms(tableoid, tcontext, perms, &tuple, NULL, false)); -+} -+ -+Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS) -+{ -+ Oid tableoid = PG_GETARG_OID(0); -+ Oid tcontext = PG_GETARG_OID(1); -+ uint32 perms = PG_GETARG_UINT32(2); -+ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(3); -+ HeapTupleData tuple; -+ -+ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); -+ ItemPointerSetInvalid(&tuple.t_self); -+ tuple.t_tableOid = tableoid; -+ tuple.t_data = rec; -+ -+ PG_RETURN_BOOL(__check_tuple_perms(tableoid, tcontext, perms, &tuple, NULL, true)); -+} -+ -+bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup, uint32 perms, bool abort) -+{ -+ return __check_tuple_perms(RelationGetRelid(rel), -+ HeapTupleGetSecurity(tuple), -+ perms, -+ tuple, -+ oldtup, -+ abort); -+} -+ -+Oid sepgsqlComputeImplicitContext(Relation rel, HeapTuple tuple) { -+ uint16 tclass; -+ Oid tcon; -+ -+ switch (RelationGetRelid(rel)) { -+ case DatabaseRelationId: /* pg_database */ -+ tclass = SECCLASS_DB_DATABASE; -+ tcon = sepgsqlGetServerContext(); -+ break; -+ -+ case RelationRelationId: { /* pg_class */ -+ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); -+ if (classForm->relkind == RELKIND_RELATION) { -+ tclass = SECCLASS_DB_TABLE; -+ tcon = sepgsqlGetDatabaseContext(); -+ break; -+ } -+ tcon = __lookupRelationForm(RelationRelationId, NULL); -+ tclass = SECCLASS_DB_TUPLE; -+ break; -+ } -+ case AttributeRelationId: { /* pg_attribute */ -+ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ FormData_pg_class classForm; -+ -+ /* special case in bootstraping mode */ -+ if (IsBootstrapProcessingMode() -+ && (attrForm->attrelid == TypeRelationId || -+ attrForm->attrelid == ProcedureRelationId || -+ attrForm->attrelid == AttributeRelationId || -+ attrForm->attrelid == RelationRelationId)) { -+ tcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), -+ sepgsqlGetDatabaseContext(), -+ SECCLASS_DB_TABLE); -+ tclass = SECCLASS_DB_COLUMN; -+ break; -+ } -+ tcon = __lookupRelationForm(attrForm->attrelid, &classForm); -+ tclass = (classForm.relkind == RELKIND_RELATION -+ ? SECCLASS_DB_COLUMN -+ : SECCLASS_DB_TUPLE); -+ break; -+ } -+ case ProcedureRelationId: -+ tclass = SECCLASS_DB_PROCEDURE; -+ tcon = sepgsqlGetDatabaseContext(); -+ break; -+ -+ case LargeObjectRelationId: { /* pg_largeobject */ -+ ScanKeyData skey; -+ SysScanDesc sd; -+ HeapTuple lotup; -+ Oid loid, lo_security = InvalidOid; -+ -+ loid = ((Form_pg_largeobject) GETSTRUCT(tuple))->loid; -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotSelf, 1, &skey); -+ lotup = systable_getnext(sd); -+ if (HeapTupleIsValid(lotup)) -+ lo_security = HeapTupleGetSecurity(lotup); -+ systable_endscan(sd); -+ /* Inherit previous page's security context */ -+ if (lo_security != InvalidOid) -+ return lo_security; -+ /* compute newly created one */ -+ tclass = SECCLASS_DB_BLOB; -+ tcon = sepgsqlGetDatabaseContext(); -+ break; -+ } -+ case TypeRelationId: /* pg_type */ -+ if (IsBootstrapProcessingMode()) { -+ /* special case in early phase */ -+ tcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), -+ sepgsqlGetDatabaseContext(), -+ SECCLASS_DB_TABLE); -+ tclass = SECCLASS_DB_TUPLE; -+ break; -+ } -+ default: -+ tclass = SECCLASS_DB_TUPLE; -+ tcon = __lookupRelationForm(RelationGetRelid(rel), NULL); -+ break; -+ } -+ return sepgsql_avc_createcon(sepgsqlGetClientContext(), tcon, tclass); -+} -diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/security/sepgsql/proxy.c ---- pgace/src/backend/security/sepgsql/proxy.c 1970-01-01 09:00:00.000000000 +0900 -+++ sepgsql/src/backend/security/sepgsql/proxy.c 2008-03-11 16:03:12.000000000 +0900 -@@ -0,0 +1,1618 @@ -+/* -+ * src/backend/security/sepgsqlProxy.c -+ * SE-PostgreSQL Query Proxy function to walk on query node tree -+ * and append tuple filter. -+ * -+ * Copyright KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/genam.h" -+#include "access/heapam.h" -+#include "catalog/heap.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_class.h" -+#include "catalog/pg_database.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_operator.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_trigger.h" -+#include "catalog/pg_type.h" -+#include "executor/spi.h" -+#include "nodes/makefuncs.h" -+#include "nodes/readfuncs.h" -+#include "optimizer/plancat.h" -+#include "parser/parse_relation.h" -+#include "parser/parse_target.h" -+#include "parser/parsetree.h" -+#include "security/pgace.h" -+#include "security/sepgsql.h" -+#include "storage/lock.h" -+#include "utils/fmgroids.h" -+#include "utils/syscache.h" -+ -+/* SE-PostgreSQL Evaluation Item */ -+#define T_SEvalItem (T_TIDBitmap + 1) /* must be unique identifier */ -+ -+typedef struct SEvalItem { -+ NodeTag type; -+ uint16 tclass; -+ uint32 perms; -+ union { -+ struct { -+ Oid relid; -+ bool inh; -+ } c; /* for pg_class */ -+ struct { -+ Oid relid; -+ bool inh; -+ AttrNumber attno; -+ } a; /* for pg_attribute */ -+ struct { -+ Oid funcid; -+ } p; /* for pg_proc */ -+ }; -+} SEvalItem; -+ -+/* query stack definition for outer references */ -+typedef struct queryChain { -+ struct queryChain *parent; -+ Query *tail; -+} queryChain; -+ -+static inline queryChain *upperQueryChain(queryChain *qc, int lvup) { -+ while (lvup > 0) { -+ Assert(!!qc->parent); -+ qc = qc->parent; -+ lvup--; -+ } -+ return qc; -+} -+ -+static inline Query *getQueryFromChain(queryChain *qc) { -+ return qc->tail; -+} -+ -+/* static definitions for proxy functions */ -+static List *proxyRteRelation(List *selist, queryChain *qc, int rtindex, Node **quals); -+static List *proxyRteSubQuery(List *selist, queryChain *qc, Query *query); -+static List *proxyJoinTree(List *selist, queryChain *qc, Node *n, Node **quals); -+static List *proxySetOperations(List *selist, queryChain *qc, Node *n); -+ -+/* static */ -+static List *sepgsqlWalkExpr(List *selist, queryChain *qc, Node *n, int flags); -+#define WKFLAG_INTERNAL_USE (0x0001) -+ -+/* ----------------------------------------------------------- -+ * addEvalXXXX -- add evaluation items into Query->SEvalItemList. -+ * Those are used for execution phase. -+ * ----------------------------------------------------------- */ -+static List *__addEvalPgClass(List *selist, Oid relid, bool inh, uint32 perms) -+{ -+ SEvalItem *se; -+ ListCell *l; -+ -+ foreach (l, selist) { -+ se = (SEvalItem *) lfirst(l); -+ if (se->tclass == SECCLASS_DB_TABLE -+ && se->c.relid == relid -+ && se->c.inh == inh) { -+ se->perms |= perms; -+ return selist; -+ } -+ } -+ /* not found */ -+ se = makeNode(SEvalItem); -+ se->tclass = SECCLASS_DB_TABLE; -+ se->perms = perms; -+ se->c.relid = relid; -+ se->c.inh = inh; -+ return lappend(selist, se); -+} -+ -+static List *addEvalPgClass(List *selist, RangeTblEntry *rte, uint32 perms) -+{ -+ rte->requiredPerms |= (perms & DB_TABLE__USE ? SEPGSQL_PERMS_USE : 0); -+ rte->requiredPerms |= (perms & DB_TABLE__SELECT ? SEPGSQL_PERMS_SELECT : 0); -+ rte->requiredPerms |= (perms & DB_TABLE__INSERT ? SEPGSQL_PERMS_INSERT : 0); -+ rte->requiredPerms |= (perms & DB_TABLE__UPDATE ? SEPGSQL_PERMS_UPDATE : 0); -+ rte->requiredPerms |= (perms & DB_TABLE__DELETE ? SEPGSQL_PERMS_DELETE : 0); -+ -+ /* for 'pg_largeobject' */ -+ if (rte->relid == LargeObjectRelationId && (perms & DB_TABLE__DELETE)) -+ rte->requiredPerms |= SEPGSQL_PERMS_WRITE; -+ -+ return __addEvalPgClass(selist, rte->relid, rte->inh, perms); -+} -+ -+static List *__addEvalPgAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 perms) -+{ -+ ListCell *l; -+ SEvalItem *se; -+ -+ foreach (l, selist) { -+ se = (SEvalItem *) lfirst(l); -+ if (se->tclass == SECCLASS_DB_COLUMN -+ && se->a.relid == relid -+ && se->a.inh == inh -+ && se->a.attno == attno) { -+ se->perms |= perms; -+ return selist; -+ } -+ } -+ /* not found */ -+ se = makeNode(SEvalItem); -+ se->tclass = SECCLASS_DB_COLUMN; -+ se->perms = perms; -+ se->a.relid = relid; -+ se->a.inh = inh; -+ se->a.attno = attno; -+ -+ return lappend(selist, se); -+} -+ -+static List *addEvalPgAttribute(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms) -+{ -+ uint32 t_perms = 0; -+ -+ /* for table:{ ... } permission */ -+ t_perms |= (perms & DB_COLUMN__USE ? DB_TABLE__USE : 0); -+ t_perms |= (perms & DB_COLUMN__SELECT ? DB_TABLE__SELECT : 0); -+ t_perms |= (perms & DB_COLUMN__INSERT ? DB_TABLE__INSERT : 0); -+ t_perms |= (perms & DB_COLUMN__UPDATE ? DB_TABLE__UPDATE : 0); -+ selist = addEvalPgClass(selist, rte, t_perms); -+ -+ /* for 'security_context' */ -+ if (attno == SecurityAttributeNumber -+ && (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT))) -+ rte->requiredPerms |= SEPGSQL_PERMS_RELABELFROM; -+ -+ /* for 'pg_largeobject' */ -+ if (rte->relid == LargeObjectRelationId) { -+ if ((perms & DB_COLUMN__SELECT) && attno == Anum_pg_largeobject_data) -+ rte->requiredPerms |= SEPGSQL_PERMS_READ; -+ if ((perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT)) && attno > 0) -+ rte->requiredPerms |= SEPGSQL_PERMS_WRITE; -+ } -+ -+ return __addEvalPgAttribute(selist, rte->relid, rte->inh, attno, perms); -+} -+ -+static List *addEvalPgProc(List *selist, Oid funcid, uint32 perms) -+{ -+ ListCell *l; -+ SEvalItem *se; -+ -+ foreach (l, selist) { -+ se = (SEvalItem *) lfirst(l); -+ if (se->tclass == SECCLASS_DB_PROCEDURE -+ && se->p.funcid == funcid) { -+ se->perms |= perms; -+ return selist; -+ } -+ } -+ se = makeNode(SEvalItem); -+ se->tclass = SECCLASS_DB_PROCEDURE; -+ se->perms = perms; -+ se->p.funcid = funcid; -+ -+ return lappend(selist, se); -+} -+ -+static List *addEvalTriggerAccess(List *selist, Oid relid, bool is_inh, int cmdType) -+{ -+ Relation rel; -+ SysScanDesc scan; -+ ScanKeyData skey; -+ HeapTuple tuple; -+ bool checked = false; -+ -+ Assert(cmdType == CMD_INSERT || cmdType == CMD_UPDATE || cmdType == CMD_DELETE); -+ -+ rel = heap_open(TriggerRelationId, AccessShareLock); -+ ScanKeyInit(&skey, -+ Anum_pg_trigger_tgrelid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ scan = systable_beginscan(rel, TriggerRelidNameIndexId, -+ true, SnapshotNow, 1, &skey); -+ while (HeapTupleIsValid((tuple = systable_getnext(scan)))) { -+ Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple); -+ -+ if (!trigForm->tgenabled) -+ continue; -+ -+ if ((cmdType == CMD_INSERT && !TRIGGER_FOR_INSERT(trigForm->tgtype)) -+ || (cmdType == CMD_UPDATE && !TRIGGER_FOR_UPDATE(trigForm->tgtype)) -+ || (cmdType == CMD_DELETE && !TRIGGER_FOR_DELETE(trigForm->tgtype))) -+ continue; -+ -+ /* per STATEMENT trigger cannot refer whole of a tuple */ -+ if (!TRIGGER_FOR_ROW(trigForm->tgtype)) -+ continue; -+ -+ /* BEFORE-ROW-INSERT trigger cannot refer whole of a tuple */ -+ if (TRIGGER_FOR_BEFORE(trigForm->tgtype) && TRIGGER_FOR_INSERT(trigForm->tgtype)) -+ continue; -+ -+ selist = addEvalPgProc(selist, trigForm->tgfoid, DB_PROCEDURE__EXECUTE); -+ if (!checked) { -+ HeapTuple reltup; -+ Form_pg_class classForm; -+ AttrNumber attnum; -+ -+ reltup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ classForm = (Form_pg_class) GETSTRUCT(reltup); -+ -+ selist = __addEvalPgClass(selist, relid, false, DB_TABLE__SELECT); -+ for (attnum = FirstLowInvalidHeapAttributeNumber + 1; attnum <= 0; attnum++) { -+ if (attnum == ObjectIdAttributeNumber && !classForm->relhasoids) -+ continue; -+ selist = __addEvalPgAttribute(selist, relid, false, attnum, DB_COLUMN__SELECT); -+ } -+ ReleaseSysCache(reltup); -+ -+ checked = true; -+ } -+ } -+ systable_endscan(scan); -+ heap_close(rel, AccessShareLock); -+ -+ if (is_inh) { -+ List *child_list = find_inheritance_children(relid); -+ ListCell *l; -+ -+ foreach(l, child_list) -+ selist = addEvalTriggerAccess(selist, lfirst_oid(l), is_inh, cmdType); -+ } -+ -+ return selist; -+} -+ -+/* ******************************************************************************* -+ * walkExpr() -- walk on expression tree recursively to pick up and to construct -+ * a SEvalItem list related to expression node. -+ * It is evaluated at later phase. -+ * *******************************************************************************/ -+static List *walkVarHelper(List *selist, queryChain *qc, Var *var, int flags) -+{ -+ RangeTblEntry *rte; -+ Query *query; -+ Node *n; -+ -+ Assert(IsA(var, Var)); -+ if (!qc) -+ elog(ERROR, "SELinux: Var node should not appear in parameter list"); -+ -+ qc = upperQueryChain(qc, var->varlevelsup); -+ query = getQueryFromChain(qc); -+ rte = list_nth(query->rtable, var->varno - 1); -+ Assert(IsA(rte, RangeTblEntry)); -+ -+ switch (rte->rtekind) { -+ case RTE_RELATION: -+ /* table:{select/use} and column:{select/use} */ -+ selist = addEvalPgAttribute(selist, rte, var->varattno, -+ (flags & WKFLAG_INTERNAL_USE) -+ ? DB_COLUMN__USE : DB_COLUMN__SELECT); -+ break; -+ case RTE_JOIN: -+ n = list_nth(rte->joinaliasvars, var->varattno - 1); -+ selist = sepgsqlWalkExpr(selist, qc, n, flags); -+ break; -+ case RTE_SUBQUERY: -+ /* In normal cases, rte->relid equals zero for subquery. -+ * If rte->relid has none-zero value, it's rewritten subquery -+ * for outer join handling. -+ */ -+ if (rte->relid) { -+ Query *sqry = rte->subquery; -+ RangeTblEntry *srte; -+ TargetEntry *tle; -+ Var *svar; -+ -+ Assert(sqry->commandType == CMD_SELECT); -+ Assert(list_length(sqry->rtable) == 1); -+ -+ srte = (RangeTblEntry *) list_nth(sqry->rtable, 0); -+ Assert(srte->rtekind == RTE_RELATION); -+ Assert(srte->relid == rte->relid); -+ -+ if (var->varattno < 1) { -+ ListCell *l; -+ bool found = false; -+ -+ foreach(l, sqry->targetList) { -+ TargetEntry *tle = lfirst(l); -+ -+ Assert(IsA(tle, TargetEntry)); -+ if (IsA(tle->expr, Const)) -+ continue; -+ -+ svar = (Var *) tle->expr; -+ Assert(IsA(svar, Var)); -+ if (svar->varattno == var->varattno) { -+ var->varattno = tle->resno; -+ found = true; -+ break; -+ } -+ } -+ if (!found) { -+ AttrNumber resno = list_length(sqry->targetList) + 1; -+ svar = makeVar(1, -+ var->varattno, -+ var->vartype, -+ var->vartypmod, -+ 0); -+ tle = makeTargetEntry((Expr *) svar, resno, NULL, false); -+ var->varattno = resno; -+ sqry->targetList = lappend(sqry->targetList, tle); -+ } -+ } else { -+ tle = list_nth(sqry->targetList, var->varattno - 1); -+ Assert(IsA(tle, TargetEntry)); -+ if (!IsA(tle->expr, Var)) -+ elog(ERROR, "SELinux: refering to dropped column (relid=%u, attno=%d)", -+ rte->relid, var->varattno); -+ svar = (Var *) tle->expr; -+ } -+ /* table:{select/use} and column:{select/use} */ -+ selist = addEvalPgAttribute(selist, srte, svar->varattno, -+ (flags & WKFLAG_INTERNAL_USE) -+ ? DB_COLUMN__USE : DB_COLUMN__SELECT); -+ } -+ break; -+ case RTE_SPECIAL: -+ case RTE_FUNCTION: -+ case RTE_VALUES: -+ break; -+ default: -+ elog(ERROR, "SELinux: unexpected rtekind (%d)", rte->rtekind); -+ break; -+ } -+ return selist; -+} -+ -+static List *walkOpExprHelper(List *selist, Oid opid) -+{ -+ HeapTuple tuple; -+ Form_pg_operator oprform; -+ -+ tuple = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(opid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for operator %u", opid); -+ oprform = (Form_pg_operator) GETSTRUCT(tuple); -+ -+ selist = addEvalPgProc(selist, oprform->oprcode, DB_PROCEDURE__EXECUTE); -+ /* NOTE: opr->oprrest and opr->oprjoin are internal use only -+ * and have no effect onto the data references, so we don't -+ * apply any checkings for them. -+ */ -+ ReleaseSysCache(tuple); -+ -+ return selist; -+} -+ -+static List *sepgsqlWalkExpr(List *selist, queryChain *qc, Node *node, int flags) -+{ -+ if (node == NULL) -+ return selist; -+ -+ switch (nodeTag(node)) { -+ case T_Const: -+ case T_Param: -+ case T_CaseTestExpr: -+ case T_CoerceToDomainValue: -+ case T_SetToDefault: -+ case T_CurrentOfExpr: -+ /* do nothing */ -+ break; -+ case T_List: { -+ ListCell *l; -+ -+ foreach (l, (List *) node) -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) lfirst(l), flags); -+ break; -+ } -+ case T_Var: { -+ selist = walkVarHelper(selist, qc, (Var *) node, flags); -+ break; -+ } -+ case T_FuncExpr: { -+ FuncExpr *func = (FuncExpr *) node; -+ -+ selist = addEvalPgProc(selist, func->funcid, DB_PROCEDURE__EXECUTE); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) func->args, flags); -+ break; -+ } -+ case T_Aggref: { -+ Aggref *aggref = (Aggref *) node; -+ -+ selist = addEvalPgProc(selist, aggref->aggfnoid, DB_PROCEDURE__EXECUTE); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aggref->args, flags); -+ break; -+ } -+ case T_OpExpr: -+ case T_DistinctExpr: /* typedef of OpExpr */ -+ case T_NullIfExpr: /* typedef of OpExpr */ -+ { -+ OpExpr *op = (OpExpr *) node; -+ -+ selist = walkOpExprHelper(selist, op->opno); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) op->args, flags); -+ break; -+ } -+ case T_ScalarArrayOpExpr: { -+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; -+ -+ selist = walkOpExprHelper(selist, saop->opno); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) saop->args, flags); -+ break; -+ } -+ case T_BoolExpr: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((BoolExpr *) node)->args, flags); -+ break; -+ } -+ case T_ArrayRef: { -+ ArrayRef *aref = (ArrayRef *) node; -+ -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refupperindexpr, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->reflowerindexpr, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refexpr, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refassgnexpr, flags); -+ break; -+ } -+ case T_SubLink: { -+ SubLink *slink = (SubLink *) node; -+ -+ Assert(IsA(slink->subselect, Query)); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) slink->testexpr, flags); -+ selist = proxyRteSubQuery(selist, qc, (Query *) slink->subselect); -+ break; -+ } -+ case T_SortClause: -+ case T_GroupClause: /* typedef of SortClause */ -+ { -+ SortClause *sort = (SortClause *) node; -+ Query *query = getQueryFromChain(qc); -+ ListCell *l; -+ -+ foreach (l, query->targetList) { -+ TargetEntry *tle = (TargetEntry *) lfirst(l); -+ Assert(IsA(tle, TargetEntry)); -+ if (tle->ressortgroupref == sort->tleSortGroupRef) { -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, flags); -+ break; -+ } -+ } -+ break; -+ } -+ case T_CoerceToDomain: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((CoerceToDomain *) node)->arg, flags); -+ break; -+ } -+ case T_CaseExpr: { -+ CaseExpr *ce = (CaseExpr *) node; -+ -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->arg, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->args, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->defresult, flags); -+ break; -+ } -+ case T_CaseWhen: { -+ CaseWhen *casewhen = (CaseWhen *) node; -+ -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) casewhen->expr, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) casewhen->result, flags); -+ break; -+ } -+ case T_RelabelType: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((RelabelType *) node)->arg, flags); -+ break; -+ } -+ case T_CoerceViaIO: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((CoerceViaIO *) node)->arg, flags); -+ break; -+ } -+ case T_ArrayCoerceExpr: { -+ ArrayCoerceExpr *ace = (ArrayCoerceExpr *) node; -+ -+ if (ace->elemfuncid != InvalidOid) -+ selist = addEvalPgProc(selist, ace->elemfuncid, DB_PROCEDURE__EXECUTE); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ace->arg, flags); -+ -+ break; -+ } -+ case T_CoalesceExpr: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((CoalesceExpr *) node)->args, flags); -+ break; -+ } -+ case T_MinMaxExpr: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((MinMaxExpr *) node)->args, flags); -+ break; -+ } -+ case T_XmlExpr: { -+ XmlExpr *xe = (XmlExpr *) node; -+ -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) xe->named_args, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) xe->args, flags); -+ break; -+ } -+ case T_NullTest: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((NullTest *) node)->arg, flags); -+ break; -+ } -+ case T_BooleanTest: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((BooleanTest *) node)->arg, flags); -+ break; -+ } -+ case T_FieldSelect: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((FieldSelect *) node)->arg, flags); -+ break; -+ } -+ case T_FieldStore: { -+ FieldStore *fstore = (FieldStore *) node; -+ -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) fstore->arg, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) fstore->newvals, flags); -+ break; -+ } -+ case T_ArrayExpr: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((ArrayExpr *) node)->elements, flags); -+ break; -+ } -+ case T_RowExpr: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((RowExpr *) node)->args, flags); -+ break; -+ } -+ case T_RowCompareExpr: { -+ RowCompareExpr *rce = (RowCompareExpr *) node; -+ ListCell *l; -+ -+ foreach (l, rce->opnos) -+ selist = walkOpExprHelper(selist, lfirst_oid(l)); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) rce->largs, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) rce->rargs, flags); -+ break; -+ } -+ case T_ConvertRowtypeExpr: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((ConvertRowtypeExpr *) node)->arg, flags); -+ break; -+ } -+ default: -+ elog(NOTICE, "SELinux: node with tag %d is ignored => %s", -+ nodeTag(node), nodeToString(node)); -+ break; -+ } -+ return selist; -+} -+ -+/* ******************************************************************************* -+ * proxyRteXXXX() -- check any relation type objects in the required query, -+ * including general relation, outer|inner|cross join and subquery. -+ * -+ * sepgsqlProxyQuery() is called just after query rewriting phase to constract -+ * a list of SEvalItems. It is attached into Query->pgaceList and evaluated by -+ * sepgsqlVerifyQuery() at later phase. -+ * *******************************************************************************/ -+ -+static Oid fnoid_sepgsql_tuple_perm = F_SEPGSQL_TUPLE_PERMS; -+ -+/* -+ * When we use LEFT OUTER JOIN, any condition defined at ON clause are not -+ * considered to filter tuples, so left-hand relation have to be re-written -+ * as a subquery to filter violated tuples. -+ */ -+static List *makePseudoTargetList(Oid relid) { -+ HeapTuple reltup, atttup; -+ Form_pg_class classForm; -+ Form_pg_attribute attrForm; -+ AttrNumber attno, relnatts; -+ TargetEntry *tle; -+ Expr *expr; -+ List *targetList = NIL; -+ -+ reltup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(reltup)) -+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); -+ -+ classForm = (Form_pg_class) GETSTRUCT(reltup); -+ relnatts = classForm->relnatts; -+ for (attno = 1; attno <= relnatts; attno++) { -+ atttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relid), -+ Int16GetDatum(attno), -+ 0, 0); -+ if (!HeapTupleIsValid(atttup)) -+ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %s", -+ attno, NameStr(classForm->relname)); -+ attrForm = (Form_pg_attribute) GETSTRUCT(atttup); -+ if (attrForm->attisdropped) { -+ expr = (Expr *) makeNullConst(INT4OID, -1); -+ } else { -+ expr = (Expr *) makeVar(1, -+ attno, -+ attrForm->atttypid, -+ attrForm->atttypmod, -+ 0); -+ } -+ tle = makeTargetEntry(expr, attno, NULL, false); -+ targetList = lappend(targetList, tle); -+ ReleaseSysCache(atttup); -+ -+ Assert(list_length(targetList) == attno); -+ } -+ ReleaseSysCache(reltup); -+ -+ return targetList; -+} -+ -+static void rewriteOuterJoinTree(Node *n, Query *query, bool is_outer_join) -+{ -+ RangeTblRef *rtr, *srtr; -+ RangeTblEntry *rte, *srte; -+ Query *sqry; -+ FromExpr *sfrm; -+ -+ if (IsA(n, RangeTblRef)) { -+ if (!is_outer_join) -+ return; -+ -+ rtr = (RangeTblRef *) n; -+ rte = list_nth(query->rtable, rtr->rtindex - 1); -+ Assert(IsA(rte, RangeTblEntry)); -+ if (rte->rtekind != RTE_RELATION) -+ return; -+ -+ /* setup alternative query */ -+ sqry = makeNode(Query); -+ sqry->commandType = CMD_SELECT; -+ sqry->targetList = makePseudoTargetList(rte->relid); -+ -+ srte = copyObject(rte); -+ sqry->rtable = list_make1(srte); -+ -+ srtr = makeNode(RangeTblRef); -+ srtr->rtindex = 1; -+ -+ sfrm = makeNode(FromExpr); -+ sfrm->fromlist = list_make1(srtr); -+ sfrm->quals = NULL; -+ -+ sqry->jointree = sfrm; -+ sqry->hasSubLinks = false; -+ sqry->hasAggs = false; -+ -+ rte->rtekind = RTE_SUBQUERY; -+ rte->subquery = sqry; -+ } else if (IsA(n, FromExpr)) { -+ FromExpr *f = (FromExpr *)n; -+ ListCell *l; -+ -+ foreach (l, f->fromlist) -+ rewriteOuterJoinTree(lfirst(l), query, false); -+ } else if (IsA(n, JoinExpr)) { -+ JoinExpr *j = (JoinExpr *) n; -+ -+ rewriteOuterJoinTree(j->larg, query, -+ (j->jointype == JOIN_LEFT || j->jointype == JOIN_FULL)); -+ rewriteOuterJoinTree(j->rarg, query, -+ (j->jointype == JOIN_RIGHT || j->jointype == JOIN_FULL)); -+ } else { -+ elog(ERROR, "SELinux: unexpected node type (%d) in Query->jointree", nodeTag(n)); -+ } -+} -+ -+static List *proxyRteRelation(List *selist, queryChain *qc, int rtindex, Node **quals) -+{ -+ Query *query; -+ RangeTblEntry *rte; -+ Relation rel; -+ TupleDesc tdesc; -+ uint32 perms; -+ -+ query = getQueryFromChain(qc); -+ rte = list_nth(query->rtable, rtindex - 1); -+ rel = relation_open(rte->relid, AccessShareLock); -+ tdesc = RelationGetDescr(rel); -+ -+ /* setup tclass and access vector */ -+ perms = rte->requiredPerms & SEPGSQL_PERMS_ALL; -+ -+ /* append sepgsql_tuple_perm(relid, record, perms) */ -+ if (perms) { -+ Var *v1, *v2, *v4; -+ Const *c3; -+ FuncExpr *func; -+ -+ /* 1st arg : Oid of the target relation */ -+ v1 = makeVar(rtindex, TableOidAttributeNumber, OIDOID, -1, 0); -+ -+ /* 2nd arg : Security Attribute of tuple */ -+ v2 = makeVar(rtindex, SecurityAttributeNumber, OIDOID, -1, 0); -+ -+ /* 3rd arg : permission set */ -+ c3 = makeConst(INT4OID, -1, sizeof(int32), Int32GetDatum(perms), false, true); -+ -+ /* 4th arg : RECORD of the target relation */ -+ v4 = makeVar(rtindex, 0, RelationGetForm(rel)->reltype, -1, 0); -+ -+ /* append sepgsql_tuple_perm */ -+ func = makeFuncExpr(fnoid_sepgsql_tuple_perm, BOOLOID, -+ list_make4(v1, v2, c3, v4), COERCE_DONTCARE); -+ if (*quals == NULL) { -+ *quals = (Node *) func; -+ } else { -+ *quals = (Node *) makeBoolExpr(AND_EXPR, list_make2(func, *quals)); -+ } -+ } -+ relation_close(rel, NoLock); -+ -+ return selist; -+} -+ -+static List *proxyRteOuterJoin(List *selist, queryChain *qc, Query *query) -+{ -+ queryChain qcData; -+ ListCell *l; -+ -+ qcData.parent = qc; -+ qcData.tail = query; -+ qc = &qcData; -+ -+ selist = proxyRteRelation(selist, qc, 1, &query->jointree->quals); -+ -+ /* clean-up polluted RangeTblEntry */ -+ foreach (l, query->rtable) { -+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); -+ rte->requiredPerms &= ~SEPGSQL_PERMS_ALL; -+ } -+ -+ return selist; -+} -+ -+static List *__checkSelectTargets(List *selist, Query *query, Node *node) -+{ -+ if (node == NULL) -+ return selist; -+ -+ if (IsA(node, RangeTblRef)) { -+ RangeTblRef *rtr = (RangeTblRef *) node; -+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); -+ -+ switch (rte->rtekind) { -+ case RTE_RELATION: -+ selist = addEvalPgClass(selist, rte, DB_TABLE__SELECT); -+ break; -+ case RTE_SUBQUERY: -+ if (rte->relid) { -+ Query *sqry = rte->subquery; -+ RangeTblEntry *srte = rt_fetch(1, sqry->rtable); -+ -+ selist = addEvalPgClass(selist, srte, DB_TABLE__SELECT); -+ } -+ break; -+ default: -+ /* do nothing */ -+ break; -+ } -+ } else if (IsA(node, JoinExpr)) { -+ JoinExpr *j = (JoinExpr *) node; -+ -+ selist = __checkSelectTargets(selist, query, j->larg); -+ selist = __checkSelectTargets(selist, query, j->rarg); -+ } else if (IsA(node, FromExpr)) { -+ FromExpr *fm = (FromExpr *)node; -+ ListCell *l; -+ -+ foreach (l, fm->fromlist) -+ selist = __checkSelectTargets(selist, query, lfirst(l)); -+ } else { -+ elog(ERROR, "SELinux: unexpected node type (%d) at Query->fromlist", nodeTag(node)); -+ } -+ return selist; -+} -+ -+static List *proxyRteSubQuery(List *selist, queryChain *qc, Query *query) -+{ -+ CmdType cmdType = query->commandType; -+ RangeTblEntry *rte = NULL; -+ queryChain qcData; -+ ListCell *l; -+ -+ /* query chain setup */ -+ qcData.parent = qc; -+ qcData.tail = query; -+ qc = &qcData; -+ -+ /* rewrite outer join */ -+ rewriteOuterJoinTree((Node *) query->jointree, query, false); -+ -+ switch (cmdType) { -+ case CMD_SELECT: -+ selist = __checkSelectTargets(selist, query, (Node *)query->jointree); -+ -+ case CMD_UPDATE: -+ case CMD_INSERT: -+ foreach (l, query->targetList) { -+ TargetEntry *tle = lfirst(l); -+ bool is_security_attr = false; -+ uint32 perms; -+ Assert(IsA(tle, TargetEntry)); -+ -+ if (tle->resjunk && tle->resname -+ && !strcmp(tle->resname, SECURITY_SYSATTR_NAME)) -+ is_security_attr = true; -+ -+ /* pure junk target entries */ -+ if (tle->resjunk && !is_security_attr) { -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, WKFLAG_INTERNAL_USE); -+ continue; -+ } -+ -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, 0); -+ -+ if (cmdType == CMD_SELECT) -+ continue; -+ -+ rte = list_nth(query->rtable, query->resultRelation - 1); -+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind==RTE_RELATION); -+ perms = (cmdType == CMD_UPDATE ? DB_COLUMN__UPDATE : DB_COLUMN__INSERT); -+ -+ selist = addEvalPgAttribute(selist, -+ rte, -+ is_security_attr ? SecurityAttributeNumber : tle->resno, -+ perms); -+ } -+ break; -+ -+ case CMD_DELETE: -+ rte = list_nth(query->rtable, query->resultRelation - 1); -+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind==RTE_RELATION); -+ selist = addEvalPgClass(selist, rte, DB_TABLE__DELETE); -+ break; -+ -+ default: -+ elog(ERROR, "SELinux: unexpected cmdType = %d", cmdType); -+ break; -+ } -+ -+ /* permission mark on RETURNING clause, if necessary */ -+ foreach (l, query->returningList) { -+ TargetEntry *te = lfirst(l); -+ Assert(IsA(te, TargetEntry)); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) te->expr, 0); -+ } -+ -+ /* permission mark on the WHERE/HAVING clause */ -+ selist = sepgsqlWalkExpr(selist, qc, query->jointree->quals, -+ WKFLAG_INTERNAL_USE); -+ selist = sepgsqlWalkExpr(selist, qc, query->havingQual, -+ WKFLAG_INTERNAL_USE); -+ -+ /* permission mark on the ORDER BY clause */ -+ // MEMO: no need to walk it again, it is checked as junk entries -+ //selist = sepgsqlWalkExpr(selist, qc, (Node *) query->sortClause, WKFLAG_INTERNAL_USE); -+ -+ /* permission mark on the GROUP BY/HAVING clause */ -+ // MEMO: no need to walk it again, it is checked as junk entries -+ //selist = sepgsqlWalkExpr(selist, qc, (Node *) query->groupClause, WKFLAG_INTERNAL_USE); -+ -+ /* permission mark on the UNION/INTERSECT/EXCEPT */ -+ selist = proxySetOperations(selist, qc, query->setOperations); -+ -+ /* append sepgsql_permission() on the FROM clause/USING clause -+ * for SELECT/UPDATE/DELETE statement. -+ * The target Relation of INSERT is noe necessary to append it -+ */ -+ selist = proxyJoinTree(selist, qc, (Node *) query->jointree, -+ &query->jointree->quals); -+ -+ /* clean-up polluted RangeTblEntry */ -+ foreach (l, query->rtable) { -+ rte = (RangeTblEntry *) lfirst(l); -+ rte->requiredPerms &= ~SEPGSQL_PERMS_ALL; -+ } -+ -+ return selist; -+} -+ -+static List *proxyJoinTree(List *selist, queryChain *qc, Node *n, Node **quals) -+{ -+ Query *query = getQueryFromChain(qc); -+ -+ if (n == NULL) -+ return selist; -+ -+ if (IsA(n, RangeTblRef)) { -+ RangeTblRef *rtr = (RangeTblRef *) n; -+ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1); -+ Assert(IsA(rte, RangeTblEntry)); -+ -+ switch (rte->rtekind) { -+ case RTE_RELATION: -+ selist = proxyRteRelation(selist, qc, rtr->rtindex, quals); -+ break; -+ case RTE_SUBQUERY: -+ selist = (rte->relid -+ ? proxyRteOuterJoin(selist, qc, rte->subquery) -+ : proxyRteSubQuery(selist, qc, rte->subquery)); -+ break; -+ case RTE_FUNCTION: { -+ FuncExpr *f = (FuncExpr *) rte->funcexpr; -+ -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) f, 0); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) f->args, 0); -+ break; -+ } -+ case RTE_VALUES: -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) rte->values_lists, 0); -+ break; -+ default: -+ elog(ERROR, "SELinux: unexpected rtekinf = %d at fromList", rte->rtekind); -+ break; -+ } -+ } else if (IsA(n, FromExpr)) { -+ FromExpr *f = (FromExpr *)n; -+ ListCell *l; -+ -+ selist = sepgsqlWalkExpr(selist, qc, f->quals, WKFLAG_INTERNAL_USE); -+ foreach (l, f->fromlist) -+ selist = proxyJoinTree(selist, qc, lfirst(l), quals); -+ } else if (IsA(n, JoinExpr)) { -+ JoinExpr *j = (JoinExpr *) n; -+ -+ selist = sepgsqlWalkExpr(selist, qc, j->quals, WKFLAG_INTERNAL_USE); -+ selist = proxyJoinTree(selist, qc, j->larg, &j->quals); -+ selist = proxyJoinTree(selist, qc, j->rarg, &j->quals); -+ } else { -+ elog(ERROR, "SELinux: unexpected node type (%d) at Query->jointree", nodeTag(n)); -+ } -+ return selist; -+} -+ -+static List *proxySetOperations(List *selist, queryChain *qc, Node *n) -+{ -+ Query *query = getQueryFromChain(qc); -+ -+ if (n == NULL) -+ return selist; -+ -+ if (IsA(n, RangeTblRef)) { -+ RangeTblRef *rtr = (RangeTblRef *) n; -+ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1); -+ -+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind == RTE_SUBQUERY); -+ -+ selist = proxyRteSubQuery(selist, qc, rte->subquery); -+ } else if (IsA(n, SetOperationStmt)) { -+ SetOperationStmt *op = (SetOperationStmt *) n; -+ -+ selist = proxySetOperations(selist, qc, (Node *) op->larg); -+ selist = proxySetOperations(selist, qc, (Node *) op->rarg); -+ } else { -+ elog(ERROR, "SELinux: setOperationsTree contains => %s", nodeToString(n)); -+ } -+ -+ return selist; -+} -+ -+static List *proxyGeneralQuery(Query *query) -+{ -+ List *selist = NIL; -+ -+ selist = proxyRteSubQuery(selist, NULL, query); -+ query->pgaceItem = (Node *) selist; -+ -+ return list_make1(query); -+} -+ -+static List *proxyExecuteStmt(Query *query) -+{ -+ List *selist = NIL; -+ ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt; -+ queryChain qcData; -+ -+ Assert(nodeTag(query->utilityStmt) == T_ExecuteStmt); -+ -+ qcData.parent = NULL; -+ qcData.tail = query; -+ selist = sepgsqlWalkExpr(selist, &qcData, (Node *) estmt->params, 0); -+ query->pgaceItem = (Node *) selist; -+ -+ return list_make1(query); -+} -+ -+static Query *convertTruncateToDelete(Relation rel) -+{ -+ Query *query = makeNode(Query); -+ RangeTblEntry *rte; -+ RangeTblRef *rtr; -+ -+ rte = addRangeTableEntryForRelation(NULL, rel, NULL, false, false); -+ rte->requiredPerms = ACL_DELETE; -+ rtr = makeNode(RangeTblRef); -+ rtr->rtindex = 1; -+ -+ query->commandType = CMD_DELETE; -+ query->rtable = list_make1(rte); -+ query->jointree = makeNode(FromExpr); -+ query->jointree->fromlist = list_make1(rtr); -+ query->jointree->quals = NULL; -+ query->resultRelation = rtr->rtindex; -+ query->hasSubLinks = false; -+ query->hasAggs = false; -+ -+ sepgsqlProxyQuery(query); -+ -+ return query; -+} -+ -+static List *proxyTruncateStmt(Query *query) -+{ -+ TruncateStmt *stmt = (TruncateStmt *) query->utilityStmt; -+ Relation rel; -+ Query *subqry; -+ ListCell *l; -+ List *subquery_list = NIL, *subquery_lids = NIL; -+ -+ /* resolve the relation names */ -+ foreach (l, stmt->relations) { -+ RangeVar *rv = lfirst(l); -+ -+ rel = heap_openrv(rv, AccessShareLock); -+ subqry = convertTruncateToDelete(rel); -+ subquery_list = lappend(subquery_list, subqry); -+ subquery_lids = lappend_oid(subquery_lids, RelationGetRelid(rel)); -+ heap_close(rel, NoLock); -+ -+ elog(NOTICE, "SELinux: TRUNCATE %s is replaced unconditional DELETE", -+ RelationGetRelationName(rel)); -+ } -+ -+ if (stmt->behavior == DROP_CASCADE) { -+ subquery_lids = heap_truncate_find_FKs(subquery_lids); -+ foreach (l, subquery_lids) { -+ Oid relid = lfirst_oid(l); -+ -+ rel = heap_open(relid, AccessShareLock); -+ subqry = convertTruncateToDelete(rel); -+ subquery_list = lappend(subquery_list, subqry); -+ heap_close(rel, NoLock); -+ } -+ } -+ return subquery_list; -+} -+ -+List *sepgsqlProxyQuery(Query *query) -+{ -+ List *new_list = NIL; -+ -+ switch (query->commandType) { -+ case CMD_SELECT: -+ case CMD_UPDATE: -+ case CMD_INSERT: -+ case CMD_DELETE: -+ new_list = proxyGeneralQuery(query); -+ break; -+ case CMD_UTILITY: -+ switch (nodeTag(query->utilityStmt)) { -+ case T_TruncateStmt: -+ new_list = proxyTruncateStmt(query); -+ break; -+ case T_ExecuteStmt: -+ new_list = proxyExecuteStmt(query); -+ break; -+ default: -+ new_list = list_make1(query); -+ /* do nothing now */ -+ break; -+ } -+ break; -+ default: -+ elog(ERROR, "SELinux: unexpected command type (%d)", query->commandType); -+ break; -+ } -+ return new_list; -+} -+ -+/* ******************************************************************************* -+ * verifyXXXX() -- checks any SEvalItem attached with Query->pgaceList. -+ * Those are generated in proxyXXXX() phase, and this evaluation is done -+ * just before PortalStart(). -+ * The reason why the checks are delayed is to handle cases when parse -+ * and execute are separated like PREPARE/EXECUTE statement. -+ * *******************************************************************************/ -+static void verifyPgClassPerms(Oid relid, bool inh, uint32 perms) -+{ -+ Form_pg_class pgclass; -+ HeapTuple tuple; -+ NameData name; -+ -+ /* prevent to modify pg_security directly */ -+ if (relid == SecurityRelationId -+ && (perms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE)) != 0) -+ elog(ERROR, "SELinux: user cannot modify pg_security directly"); -+ -+ /* check table:{required permissions} */ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: relation (oid=%u) does not exist", relid); -+ pgclass = (Form_pg_class) GETSTRUCT(tuple); -+ -+ if (pgclass->relkind != RELKIND_RELATION) { -+ ReleaseSysCache(tuple); -+ return; -+ } -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_TABLE, -+ perms, -+ sepgsqlGetTupleName(RelationRelationId, tuple, &name)); -+ ReleaseSysCache(tuple); -+} -+ -+static void verifyPgAttributePerms(Oid relid, bool inh, AttrNumber attno, uint32 perms) -+{ -+ HeapTuple tuple; -+ Form_pg_class classForm; -+ Form_pg_attribute attrForm; -+ NameData name; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: relation (oid=%u) does not exist", relid); -+ classForm = (Form_pg_class) GETSTRUCT(tuple); -+ if (classForm->relkind != RELKIND_RELATION) { -+ /* column:{ xxx } checks are applied only column within tables */ -+ ReleaseSysCache(tuple); -+ return; -+ } -+ ReleaseSysCache(tuple); -+ -+ /* 2. verify column perms */ -+ if (attno == 0) { -+ /* RECORD type permission check */ -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ -+ ScanKeyInit(&skey, -+ Anum_pg_attribute_attrelid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ -+ rel = heap_open(AttributeRelationId, AccessShareLock); -+ scan = systable_beginscan(rel, AttributeRelidNumIndexId, -+ true, SnapshotNow, 1, &skey); -+ while ((tuple = systable_getnext(scan)) != NULL) { -+ attrForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ if (attrForm->attisdropped || attrForm->attnum < 1) -+ continue; -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_COLUMN, -+ perms, -+ sepgsqlGetTupleName(AttributeRelationId, tuple, &name)); -+ } -+ systable_endscan(scan); -+ heap_close(rel, AccessShareLock); -+ -+ return; -+ } -+ -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relid), -+ Int16GetDatum(attno), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u", attno, relid); -+ -+ /* check column:{required permissions} */ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_COLUMN, -+ perms, -+ sepgsqlGetTupleName(AttributeRelationId, tuple, &name)); -+ ReleaseSysCache(tuple); -+} -+ -+static void verifyPgProcPerms(Oid funcid, uint32 perms) -+{ -+ HeapTuple tuple; -+ NameData name; -+ Oid newcon; -+ -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(funcid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for procedure %d", funcid); -+ -+ /* compute domain transition */ -+ newcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_PROCESS); -+ if (newcon != sepgsqlGetClientContext()) -+ perms |= DB_PROCEDURE__ENTRYPOINT; -+ -+ /* check procedure executiong permission */ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_PROCEDURE, -+ perms, -+ sepgsqlGetTupleName(ProcedureRelationId, tuple, &name)); -+ -+ /* check domain transition, if necessary */ -+ if (newcon != sepgsqlGetClientContext()) { -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ newcon, -+ SECCLASS_PROCESS, -+ PROCESS__TRANSITION, -+ NULL); -+ } -+ -+ ReleaseSysCache(tuple); -+} -+ -+static List *__expandPgClassInheritance(List *selist, Oid relid, uint32 perms) -+{ -+ List *child_list = find_inheritance_children(relid); -+ ListCell *l; -+ -+ foreach (l, child_list) { -+ selist = __addEvalPgClass(selist, lfirst_oid(l), false, perms); -+ selist = __expandPgClassInheritance(selist, lfirst_oid(l), perms); -+ } -+ return selist; -+} -+ -+static List *__expandPgAttributeInheritance(List *selist, Oid relid, char *attname, uint32 perms) -+{ -+ List *child_list = find_inheritance_children(relid); -+ ListCell *l; -+ -+ foreach (l, child_list) { -+ Form_pg_attribute attrForm; -+ HeapTuple tuple; -+ -+ if (!attname) { -+ /* attname == NULL means RECORD reference */ -+ selist = __addEvalPgAttribute(selist, lfirst_oid(l), false, 0, perms); -+ selist = __expandPgAttributeInheritance(selist, lfirst_oid(l), NULL, perms); -+ continue; -+ } -+ -+ tuple = SearchSysCacheAttName(lfirst_oid(l), attname); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for attribute %s of relation %u", -+ attname, lfirst_oid(l)); -+ attrForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ selist = __addEvalPgAttribute(selist, lfirst_oid(l), false, attrForm->attnum, perms); -+ selist = __expandPgAttributeInheritance(selist, lfirst_oid(l), attname, perms); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ return selist; -+} -+ -+static List *expandSEvalListInheritance(List *selist) { -+ List *result = NIL; -+ ListCell *l; -+ -+ foreach (l, selist) { -+ SEvalItem *se = (SEvalItem *) lfirst(l); -+ -+ result = lappend(result, se); -+ switch (se->tclass) { -+ case SECCLASS_DB_TABLE: -+ if (se->c.inh) { -+ se->c.inh = false; -+ result = __expandPgClassInheritance(result, -+ se->c.relid, -+ se->perms); -+ } -+ break; -+ case SECCLASS_DB_COLUMN: -+ if (se->a.inh) { -+ Form_pg_attribute attrForm; -+ HeapTuple tuple; -+ -+ se->a.inh = false; -+ if (se->a.attno == 0) { -+ result = __expandPgAttributeInheritance(result, -+ se->a.relid, -+ NULL, -+ se->perms); -+ break; -+ } -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(se->a.relid), -+ Int16GetDatum(se->a.attno), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u", -+ se->a.attno, se->a.relid); -+ attrForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ -+ result = __expandPgAttributeInheritance(result, -+ se->a.relid, -+ NameStr(attrForm->attname), -+ se->perms); -+ ReleaseSysCache(tuple); -+ } -+ break; -+ } -+ } -+ return result; -+} -+ -+static void execVerifyQuery(List *selist) -+{ -+ ListCell *l; -+ -+ foreach (l, selist) { -+ SEvalItem *se = lfirst(l); -+ -+ switch (se->tclass) { -+ case SECCLASS_DB_TABLE: -+ verifyPgClassPerms(se->c.relid, se->c.inh, se->perms); -+ break; -+ case SECCLASS_DB_COLUMN: -+ verifyPgAttributePerms(se->a.relid, se->a.inh, se->a.attno, se->perms); -+ break; -+ case SECCLASS_DB_PROCEDURE: -+ verifyPgProcPerms(se->p.funcid, se->perms); -+ break; -+ default: -+ elog(ERROR, "SELinux: unexpected SEvalItem (tclass: %d)", se->tclass); -+ break; -+ } -+ } -+} -+ -+void sepgsqlVerifyQuery(PlannedStmt *pstmt) -+{ -+ RangeTblEntry *rte; -+ List *selist; -+ ListCell *l; -+ -+ if (!pstmt->pgaceItem) -+ return; -+ Assert(IsA(pstmt->pgaceItem, List)); -+ selist = (List *) pstmt->pgaceItem; -+ -+ /* expand table inheritances */ -+ selist = expandSEvalListInheritance(selist); -+ -+ /* add checks for access via trigger function */ -+ foreach(l, pstmt->resultRelations) { -+ Index rindex = lfirst_int(l); -+ -+ rte = rt_fetch(rindex, pstmt->rtable); -+ Assert(IsA(rte, RangeTblEntry)); -+ -+ selist = addEvalTriggerAccess(selist, rte->relid, rte->inh, pstmt->commandType); -+ } -+ execVerifyQuery(selist); -+} -+ -+/* ******************************************************************************* -+ * PGACE hooks: we cannon the following hooks in sepgsqlHooks.c because they -+ * refers static defined variables in sepgsqlProxy.c -+ * *******************************************************************************/ -+ -+/* ---------------------------------------------------------- -+ * COPY TO/COPY FROM statement hooks -+ * ---------------------------------------------------------- */ -+void sepgsqlCopyTable(Relation rel, List *attNumList, bool isFrom) -+{ -+ List *selist = NIL; -+ ListCell *l; -+ -+ /* on 'COPY FROM SELECT ...' cases, any checkings are done in select.c */ -+ if (rel == NULL) -+ return; -+ -+ /* no need to check non-table relation */ -+ if (RelationGetForm(rel)->relkind != RELKIND_RELATION) -+ return; -+ -+ selist = __addEvalPgClass(selist, RelationGetRelid(rel), false, -+ isFrom ? DB_TABLE__INSERT : DB_TABLE__SELECT); -+ foreach (l, attNumList) { -+ AttrNumber attnum = lfirst_int(l); -+ -+ selist = __addEvalPgAttribute(selist, RelationGetRelid(rel), false, attnum, -+ isFrom ? DB_COLUMN__INSERT : DB_COLUMN__SELECT); -+ } -+ -+ /* check call trigger function */ -+ if (isFrom) -+ selist = addEvalTriggerAccess(selist, RelationGetRelid(rel), false, CMD_INSERT); -+ -+ execVerifyQuery(selist); -+} -+ -+bool sepgsqlCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) -+{ -+ uint32 perms = SEPGSQL_PERMS_SELECT; -+ -+ /* for 'pg_largeobject' */ -+ if (RelationGetRelid(rel) == LargeObjectRelationId) { -+ ListCell *l; -+ -+ foreach (l, attNumList) { -+ AttrNumber attnum = lfirst_int(l); -+ if (attnum == Anum_pg_largeobject_data) { -+ perms |= SEPGSQL_PERMS_READ; -+ break; -+ } -+ } -+ } -+ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, false); -+} -+ -+/* ---------------------------------------------------------- -+ * node copy/print hooks -+ * ---------------------------------------------------------- */ -+Node *sepgsqlCopyObject(Node *__oldnode) { -+ SEvalItem *oldnode, *newnode; -+ -+ if (nodeTag(__oldnode) != T_SEvalItem) -+ return NULL; -+ oldnode = (SEvalItem *) __oldnode; -+ -+ newnode = makeNode(SEvalItem); -+ newnode->tclass = oldnode->tclass; -+ newnode->perms = oldnode->perms; -+ switch (oldnode->tclass) { -+ case SECCLASS_DB_TABLE: -+ newnode->c.relid = oldnode->c.relid; -+ newnode->c.inh = oldnode->c.inh; -+ break; -+ case SECCLASS_DB_COLUMN: -+ newnode->a.relid = oldnode->a.relid; -+ newnode->a.attno = oldnode->a.attno; -+ newnode->a.inh = oldnode->a.inh; -+ break; -+ case SECCLASS_DB_PROCEDURE: -+ newnode->p.funcid = oldnode->p.funcid; -+ break; -+ default: -+ elog(ERROR, "SELinux: unexpected SEvalItem node (tclass: %d)", oldnode->tclass); -+ break; -+ } -+ return (Node *) newnode; -+} -+ -+bool sepgsqlOutObject(StringInfo str, Node *node) { -+ SEvalItem *seitem = (SEvalItem *) node; -+ -+ if (nodeTag(node) != T_SEvalItem) -+ return false; -+ -+ appendStringInfoString(str, "SEVALITEM"); -+ appendStringInfo(str, ":tclass %u", seitem->tclass); -+ appendStringInfo(str, ":perms %u", seitem->perms); -+ switch(seitem->tclass) { -+ case SECCLASS_DB_TABLE: -+ appendStringInfo(str, ":c.relid %u", seitem->c.relid); -+ appendStringInfo(str, ":c.inh %s", seitem->c.inh ? "true" : "false"); -+ break; -+ case SECCLASS_DB_COLUMN: -+ appendStringInfo(str, ":a.relid %u", seitem->a.relid); -+ appendStringInfo(str, ":a.inh %s", seitem->a.inh ? "true" : "false"); -+ appendStringInfo(str, ":a.attno %u", seitem->a.attno); -+ break; -+ case SECCLASS_DB_PROCEDURE: -+ appendStringInfo(str, ":p.funcid %u", seitem->p.funcid); -+ break; -+ default: -+ elog(ERROR, "SELinux: unexpected SEvalItem node (tclass: %d)", seitem->tclass); -+ break; -+ } -+ return true; -+} -+ -+void *sepgsqlReadObject(char *token) -+{ -+ SEvalItem *seitem; -+ int length; -+ -+ if (strcmp(token, "SEVALITEM")) -+ return NULL; -+ -+ seitem = makeNode(SEvalItem); -+ -+ /* :tclass */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->tclass = atoi(token); -+ -+ /* :perms */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->perms = (unsigned int) strtoul(token, NULL, 10); -+ -+ switch (seitem->tclass) { -+ case SECCLASS_DB_TABLE: -+ /* :c.relid */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->c.relid = (unsigned int) strtoul(token, NULL, 10); -+ -+ /* :c.inh */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->c.inh = (strcmp(token, "true") == 0 ? true : false); -+ break; -+ -+ case SECCLASS_DB_COLUMN: -+ /* :a.relid */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->a.relid = (unsigned int) strtoul(token, NULL, 10); -+ -+ /* :a.inh */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->a.inh = (strcmp(token, "true") == 0 ? true : false); -+ -+ /* :a.attno */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->a.attno = atoi(token); -+ break; -+ -+ case SECCLASS_DB_PROCEDURE: -+ /* :p.funcid */ -+ token = pg_strtok(&length); -+ token = pg_strtok(&length); -+ seitem->p.funcid = (unsigned int) strtoul(token, NULL, 10); -+ break; -+ -+ default: -+ elog(ERROR, "SELinux: unexpected SEvalItem node (tclass: %d)", seitem->tclass); -+ break; -+ } -+ return (void *) seitem; -+} -+ -+/* ---------------------------------------------------------- -+ * special cases in foreign key constraint -+ * ---------------------------------------------------------- */ -+Oid sepgsqlPreparePlanCheck(Relation rel) { -+ Oid pgace_saved = fnoid_sepgsql_tuple_perm; -+ fnoid_sepgsql_tuple_perm = F_SEPGSQL_TUPLE_PERMS_ABORT; -+ return pgace_saved; -+} -+ -+void sepgsqlRestorePlanCheck(Relation rel, Oid pgace_saved) { -+ fnoid_sepgsql_tuple_perm = pgace_saved; -+} -diff -rpNU3 pgace/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h ---- pgace/src/include/catalog/pg_proc.h 2008-01-08 01:39:49.000000000 +0900 -+++ sepgsql/src/include/catalog/pg_proc.h 2008-01-08 12:56:27.000000000 +0900 -@@ -4123,6 +4123,11 @@ DATA(insert OID = 3409 ( security_label_ - DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 1 0 f f t f v 1 3403 "26" _null_ _null_ _null_ lo_get_security - _null_ _null_ )); - DATA(insert OID = 3411 ( lo_set_security PGNSP PGUID 12 1 0 f f t f v 2 16 "26 3403" _null_ _null_ _null_ lo_set_security - _null_ _null_ )); - -+/* SE-PostgreSQL related function */ -+DATA(insert OID = 3420 ( sepgsql_getcon PGNSP PGUID 12 1 0 f f t f v 0 3403 "" _null_ _null_ _null_ sepgsql_getcon - _null_ _null_ )); -+DATA(insert OID = 3421 ( sepgsql_tuple_perms PGNSP PGUID 12 1 0 f f t f v 4 16 "26 3403 23 2249" _null_ _null_ _null_ sepgsql_tuple_perms - _null_ _null_ )); -+DATA(insert OID = 3422 ( sepgsql_tuple_perms_abort PGNSP PGUID 12 1 0 f f t f v 4 16 "26 3403 23 2249" _null_ _null_ _null_ sepgsql_tuple_perms_abort - _null_ _null_ )); -+ - /* enum related procs */ - DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 f f t f i 1 3500 "2275" _null_ _null_ _null_ anyenum_in - _null_ _null_ )); - DESCR("I/O"); -diff -rpNU3 pgace/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in ---- pgace/src/include/pg_config.h.in 2008-01-28 16:14:33.000000000 +0900 -+++ sepgsql/src/include/pg_config.h.in 2008-01-28 16:19:11.000000000 +0900 -@@ -366,6 +366,9 @@ - /* Define to 1 if you have the header file. */ - #undef HAVE_SECURITY_PAM_APPL_H - -+/* Define to 1 if you enable NSA SELinux support */ -+#undef HAVE_SELINUX -+ - /* Define to 1 if you have the `setproctitle' function. */ - #undef HAVE_SETPROCTITLE - -diff -rpNU3 pgace/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h ---- pgace/src/include/security/sepgsql.h 1970-01-01 09:00:00.000000000 +0900 -+++ sepgsql/src/include/security/sepgsql.h 2008-02-04 17:40:05.000000000 +0900 -@@ -0,0 +1,140 @@ -+#ifndef SEPGSQL_H -+#define SEPGSQL_H -+ -+/* system catalogs */ -+#include "catalog/pg_security.h" -+#include "lib/stringinfo.h" -+#include "nodes/nodes.h" -+#include "nodes/parsenodes.h" -+#include "storage/large_object.h" -+ -+#include -+#include -+#include -+ -+/* -+ * Permission codes of internal representation -+ */ -+#define SEPGSQL_PERMS_USE (1UL << (N_ACL_RIGHTS + 0)) -+#define SEPGSQL_PERMS_SELECT (1UL << (N_ACL_RIGHTS + 1)) -+#define SEPGSQL_PERMS_UPDATE (1UL << (N_ACL_RIGHTS + 2)) -+#define SEPGSQL_PERMS_INSERT (1UL << (N_ACL_RIGHTS + 3)) -+#define SEPGSQL_PERMS_DELETE (1UL << (N_ACL_RIGHTS + 4)) -+#define SEPGSQL_PERMS_RELABELFROM (1UL << (N_ACL_RIGHTS + 5)) -+#define SEPGSQL_PERMS_RELABELTO (1UL << (N_ACL_RIGHTS + 6)) -+#define SEPGSQL_PERMS_READ (1UL << (N_ACL_RIGHTS + 7)) -+#define SEPGSQL_PERMS_WRITE (1UL << (N_ACL_RIGHTS + 8)) -+#define SEPGSQL_PERMS_ALL ((SEPGSQL_PERMS_WRITE << 1) - SEPGSQL_PERMS_USE) -+ -+/* -+ * The implementation of PGACE/SE-PostgreSQL hooks -+ */ -+ -+/* Initialize / Finalize related hooks */ -+extern Size sepgsqlShmemSize(void); -+extern void sepgsqlInitialize(bool is_bootstrap); -+extern int sepgsqlInitializePostmaster(void); -+extern void sepgsqlFinalizePostmaster(void); -+ -+/* SQL proxy hooks */ -+extern List *sepgsqlProxyQuery(Query *query); -+extern void sepgsqlVerifyQuery(PlannedStmt *pstmt); -+ -+/* HeapTuple modification hooks */ -+extern bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, -+ bool is_internal, bool with_returning); -+extern bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, -+ bool is_internal, bool with_returning); -+extern bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, -+ bool is_internal, bool with_returning); -+ -+/* Extended SQL statement hooks */ -+extern DefElem *sepgsqlGramSecurityItem(char *defname, char *value); -+extern bool sepgsqlIsGramSecurityItem(DefElem *defel); -+extern void sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel); -+extern void sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel); -+ -+/* DATABASE related hooks */ -+extern void sepgsqlSetDatabaseParam(const char *name, char *argstring); -+extern void sepgsqlGetDatabaseParam(const char *name); -+ -+/* FUNCTION related hooks */ -+extern void sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check); -+extern bool sepgsqlCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata); -+extern Oid sepgsqlPreparePlanCheck(Relation rel); -+extern void sepgsqlRestorePlanCheck(Relation rel, Oid pgace_saved); -+ -+/* TABLE related hooks */ -+extern void sepgsqlLockTable(Oid relid); -+extern bool sepgsqlAlterTable(Relation rel, AlterTableCmd *cmd); -+ -+/* COPY TO/COPY FROM statement hooks */ -+extern void sepgsqlCopyTable(Relation rel, List *attnumlist, bool is_from); -+extern bool sepgsqlCopyToTuple(Relation rel, List *attnumlist, HeapTuple tuple); -+ -+/* Loadable shared library module hooks */ -+extern void sepgsqlLoadSharedModule(const char *filename); -+ -+/* Binary Large Object (BLOB) hooks */ -+extern void sepgsqlLargeObjectGetSecurity(HeapTuple tuple); -+extern void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security); -+extern void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple); -+extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple); -+extern void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple); -+extern void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup); -+extern void sepgsqlLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup); -+extern void sepgsqlLargeObjectImport(void); -+extern void sepgsqlLargeObjectExport(void); -+ -+/* Security Label hooks */ -+extern char *sepgsqlSecurityLabelIn(char *context); -+extern char *sepgsqlSecurityLabelOut(char *context); -+extern char *sepgsqlSecurityLabelCheckValid(char *context); -+extern char *sepgsqlSecurityLabelOfLabel(char *context); -+ -+/* Extended node type hooks */ -+extern Node *sepgsqlCopyObject(Node *node); -+extern bool sepgsqlOutObject(StringInfo str, Node *node); -+extern void *sepgsqlReadObject(char *token); -+ -+/* -+ * SE-PostgreSQL core functions -+ * src/backend/security/sepgsql/core.c -+ */ -+extern bool sepgsqlIsEnabled(void); -+extern Oid sepgsqlGetServerContext(void); -+extern Oid sepgsqlGetClientContext(void); -+extern void sepgsqlSetClientContext(Oid new_ctx); -+extern Oid sepgsqlGetDatabaseContext(void); -+extern char *sepgsqlGetDatabaseName(void); -+ -+/* userspace access vector cache related */ -+extern void sepgsql_avc_permission(Oid ssid, Oid tsid, uint16 tclass, -+ uint32 perms, char *objname); -+extern bool sepgsql_avc_permission_noabort(Oid ssid, Oid tsid, uint16 tclass, -+ uint32 perms, char *objname); -+extern Oid sepgsql_avc_createcon(Oid ssid, Oid tsid, uint16 tclass); -+extern Oid sepgsql_avc_relabelcon(Oid ssid, Oid tsid, uint16 tclass); -+ -+/* -+ * SE-PostgreSQL permission evaluation related -+ * src/backend/security/sepgsql/permission.c -+ */ -+extern char *sepgsqlGetTupleName(Oid relid, HeapTuple tuple, NameData *name); -+extern Oid sepgsqlComputeImplicitContext(Relation rel, HeapTuple tuple); -+extern bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup, -+ uint32 perms, bool abort); -+/* -+ * SE-PostgreSQL SQL FUNCTIONS -+ */ -+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); -+extern Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS); -+extern Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS); -+ -+#endif /* SEPGSQL_H */ diff --git a/sepostgresql-sepgsql-8.3.7-2.patch b/sepostgresql-sepgsql-8.3.7-2.patch new file mode 100644 index 0000000..ef2c562 --- /dev/null +++ b/sepostgresql-sepgsql-8.3.7-2.patch @@ -0,0 +1,12650 @@ +diff -rpNU3 base/configure sepgsql/configure +--- base/configure 2009-03-15 17:47:25.000000000 +0900 ++++ sepgsql/configure 2009-03-15 17:53:20.000000000 +0900 +@@ -314,7 +314,7 @@ ac_includes_default="\ + # include + #endif" + +-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS' ++ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib enable_selinux EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS' + ac_subst_files='' + + # Initialize some variables set by options. +@@ -871,6 +871,7 @@ Optional Features: + --enable-cassert enable assertion checks (for debugging) + --enable-thread-safety make client libraries thread-safe + --enable-thread-safety-force force thread-safety despite thread test failure ++ --enable-selinux build with SELinux support + --disable-largefile omit support for large files + + Optional Packages: +@@ -4619,6 +4620,115 @@ fi; + + + # ++# SELinux support ++# ++ ++pgac_args="$pgac_args enable_selinux" ++ ++# Check whether --enable-selinux or --disable-selinux was given. ++if test "${enable_selinux+set}" = set; then ++ enableval="$enable_selinux" ++ ++ case $enableval in ++ yes) ++ : ++ ;; ++ no) ++ : ++ ;; ++ *) ++ { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 ++echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} ++ { (exit 1); exit 1; }; } ++ ;; ++ esac ++ ++else ++ enable_selinux=no ++ ++fi; ++ ++if test "$enable_selinux" = yes; then ++ echo "$as_me:$LINENO: checking for getpeercon in -lselinux" >&5 ++echo $ECHO_N "checking for getpeercon in -lselinux... $ECHO_C" >&6 ++if test "${ac_cv_lib_selinux_getpeercon+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lselinux $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any gcc2 internal prototype to avoid an error. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++/* We use char because int might match the return type of a gcc2 ++ builtin and then its argument prototype would still apply. */ ++char getpeercon (); ++int ++main () ++{ ++getpeercon (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ++ (eval $ac_link) 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && ++ { ac_try='test -z "$ac_c_werror_flag" ++ || test ! -s conftest.err' ++ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); }; } && ++ { ac_try='test -s conftest$ac_exeext' ++ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); }; }; then ++ ac_cv_lib_selinux_getpeercon=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ac_cv_lib_selinux_getpeercon=no ++fi ++rm -f conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon" >&5 ++echo "${ECHO_T}$ac_cv_lib_selinux_getpeercon" >&6 ++if test $ac_cv_lib_selinux_getpeercon = yes; then ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_SELINUX 1 ++_ACEOF ++ ++ ++else ++ { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5 ++echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;} ++ { (exit 1); exit 1; }; } ++fi ++ ++fi ++ ++# + # Elf + # + +@@ -26019,6 +26129,7 @@ s,@with_libxml@,$with_libxml,;t t + s,@with_libxslt@,$with_libxslt,;t t + s,@with_system_tzdata@,$with_system_tzdata,;t t + s,@with_zlib@,$with_zlib,;t t ++s,@enable_selinux@,$enable_selinux,;t t + s,@EGREP@,$EGREP,;t t + s,@ELF_SYS@,$ELF_SYS,;t t + s,@LDFLAGS_SL@,$LDFLAGS_SL,;t t +diff -rpNU3 base/configure.in sepgsql/configure.in +--- base/configure.in 2009-03-15 17:47:25.000000000 +0900 ++++ sepgsql/configure.in 2009-03-15 17:53:20.000000000 +0900 +@@ -626,6 +626,19 @@ PGAC_ARG_BOOL(with, zlib, yes, + AC_SUBST(with_zlib) + + # ++# SELinux support ++# ++PGAC_ARG_BOOL(enable, selinux, no, ++ [ --enable-selinux build with SELinux support]) ++if test "$enable_selinux" = yes; then ++ AC_CHECK_LIB(selinux, getpeercon, ++ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, ++ [SE-PostgreSQL feature is enabled]) ++ AC_SUBST(enable_selinux), ++ AC_MSG_ERROR("--enable-selinux requires libselinux.")) ++fi ++ ++# + # Elf + # + +diff -rpNU3 base/contrib/sepgsql_policy/Makefile sepgsql/contrib/sepgsql_policy/Makefile +--- base/contrib/sepgsql_policy/Makefile 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/contrib/sepgsql_policy/Makefile 2008-06-26 11:44:44.000000000 +0900 +@@ -0,0 +1,40 @@ ++# ++# contrib/sepgsql_policy/Makefile ++# Makefile of security policy module for SE-PostgreSQL ++# ++top_builddir = ../.. ++include $(top_builddir)/src/Makefile.global ++ ++policy_basedir := /usr/share/selinux ++policy_makefile := $(policy_basedir)/devel/Makefile ++policy_types := targeted mls ++policy := $(strip $(shell $(AWK) -F= '/^SELINUXTYPE/{ print $$2 }' /etc/selinux/config)) ++package_names := sepostgresql sepostgresql-devel ++prefix_ptn := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g" ++bindir_ptn := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g" ++libdir_ptn := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g" ++ ++all: ++ $(foreach pkg, $(package_names), $(foreach p, $(policy_types), $(MAKE) $(MAKEOVERRIDES) policy=$(p) $(pkg).pp;)) ++ $(foreach pkg, $(package_names), test -e $(pkg).pp.$(policy) && ln -sf $(pkg).pp.$(policy) $(pkg).pp;) ++ ++.install-policy: ++ test -d $(DESTDIR)$(policy_basedir)/$(policy) || install -d $(DESTDIR)$(policy_basedir)/$(policy) ++ $(foreach pkg, $(package_names), install -p -m 644 $(pkg).pp.$(policy) $(DESTDIR)$(policy_basedir)/$(policy)/$(pkg).pp;) ++ ++install: all ++ $(foreach p, $(policy_types), $(MAKE) $(MAKEOVERRIDES) policy=$(p) .install-policy;) ++ ++%.pp: %.te %.if %.fc ++ $(MAKE) NAME=$(policy) -f $(policy_makefile) $@ ++ mv $@ $@.$(policy) ++ ++sepostgresql-devel.fc: sepostgresql.fc.template ++ cat $< | grep -v ^/var | sed -e $(prefix_ptn) -e $(bindir_ptn) -e $(libdir_ptn) > $@ ++ ++sepostgresql.fc: sepostgresql.fc.template ++ cat $< | sed -e $(prefix_ptn) -e $(bindir_ptn) -e $(libdir_ptn) > $@ ++ ++clean: ++ $(MAKE) -f $(policy_makefile) clean ++ rm -f *.pp.* *.fc +diff -rpNU3 base/contrib/sepgsql_policy/README sepgsql/contrib/sepgsql_policy/README +--- base/contrib/sepgsql_policy/README 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/contrib/sepgsql_policy/README 2008-06-19 13:12:15.000000000 +0900 +@@ -0,0 +1,50 @@ ++The security policy module of SE-PostgreSQL ++------------------------------------------- ++ ++o Introduction ++ ++ We provide two kind of security policy modules. ++ ++ One is "sepostgresql.pp" which contains full-set of security policy ++ and suitable for legacy base policy (selinux-policy-3.4.1, or prior). ++ ++ The other is "sepostgresql-devel.pp" which provides several booleans ++ for developers, and suitable for newer base policy (selinux-policy-3.4.2, ++ or later). ++ ++ In the selinux-policy-3.4.2, most part of the policy got upstreamed. ++ So, we don't need to install "sepostgresql.pp" explicitly on the newer ++ base security policy. ++ ++ If you need to run regression test, or (don't) want to generate access ++ logs, install "sepostgresql-devel.pp" and turn on/off booleans. ++ ++o Build & Installation ++ ++ $ cd contrib/sepgsql_policy ++ $ make ++ $ su ++ # /usr/sbin/semodule -i sepostgresql-devel.pp ++ or ++ # /usr/sbin/semodule -i sepostgresql.pp ++ ++o Booleans ++ ++- sepgsql_enable_users_ddl (default: on) ++ This boolean enables to control to execute DDL statement come from ++ confined users. ++ ++- sepgsql_enable_auditallow (default: off) ++ This boolean enables to generate access allow logs except for tuple ++ level. ++ ++- sepgsql_enable_auditdeny (default: on) ++ This boolean enables to generata access denied logs except for tuple ++ level. ++ ++- sepgsql_regression_test_mode (default: off) ++ This boolean provides several permission to run regression test on ++ your home directory. It enables to load shared library files deployed ++ on home directory. ++ However, we don't recommend it to turn on in the operation phase. ++ +diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql-devel.if sepgsql/contrib/sepgsql_policy/sepostgresql-devel.if +--- base/contrib/sepgsql_policy/sepostgresql-devel.if 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/contrib/sepgsql_policy/sepostgresql-devel.if 2008-06-19 13:12:15.000000000 +0900 +@@ -0,0 +1 @@ ++## There are no interface declaration +diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql-devel.te sepgsql/contrib/sepgsql_policy/sepostgresql-devel.te +--- base/contrib/sepgsql_policy/sepostgresql-devel.te 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/contrib/sepgsql_policy/sepostgresql-devel.te 2008-06-26 11:44:44.000000000 +0900 +@@ -0,0 +1,82 @@ ++policy_module(sepostgresql-devel, 3.11) ++ ++gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute domain, home_type; ++ attribute sepgsql_client_type; ++ attribute sepgsql_unconfined_type; ++ ++ attribute sepgsql_database_type; ++ attribute sepgsql_table_type; ++ attribute sepgsql_sysobj_table_type; ++ attribute sepgsql_procedure_type; ++ attribute sepgsql_blob_type; ++ attribute sepgsql_module_type; ++') ++ ++################################# ++# ++# SE-PostgreSQL Declarations ++# ++ ++## ++##

++## Allow to generate auditallow logs ++##

++##
++gen_tunable(sepgsql_enable_auditallow, false) ++ ++## ++##

++## Allow to generate auditdeny logs ++##

++##
++gen_tunable(sepgsql_enable_auditdeny, true) ++ ++## ++##

++## Allow widespread permissions for regression test ++## Don't set TRUE on operation phase ++##

++##
++gen_tunable(sepgsql_regression_test_mode, false) ++ ++######################################## ++# ++# SE-PostgreSQL audit switch for debugging ++# ++tunable_policy(`sepgsql_enable_auditallow',` ++ auditallow domain sepgsql_database_type : db_database *; ++ auditallow domain sepgsql_table_type : db_table *; ++ auditallow domain sepgsql_table_type : db_column *; ++ auditallow domain sepgsql_procedure_type : db_procedure *; ++ auditallow domain sepgsql_blob_type : db_blob *; ++ auditallow domain sepgsql_module_type : db_database { install_module }; ++ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++tunable_policy(`! sepgsql_enable_auditdeny',` ++ dontaudit domain sepgsql_database_type : db_database *; ++ dontaudit domain sepgsql_table_type : db_table *; ++ dontaudit domain sepgsql_table_type : db_column *; ++ dontaudit domain sepgsql_procedure_type : db_procedure *; ++ dontaudit domain sepgsql_blob_type : db_blob *; ++ dontaudit domain sepgsql_module_type : db_database { install_module }; ++ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++######################################## ++# ++# SE-PostgreSQL regression test mode switch ++# ++tunable_policy(`sepgsql_regression_test_mode',` ++ allow sepgsql_client_type home_type : db_database { install_module }; ++ allow sepgsql_unconfined_type home_type : db_database { install_module }; ++ allow sepgsql_database_type home_type : db_database { load_module }; ++') +diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql.fc.template sepgsql/contrib/sepgsql_policy/sepostgresql.fc.template +--- base/contrib/sepgsql_policy/sepostgresql.fc.template 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/contrib/sepgsql_policy/sepostgresql.fc.template 2008-06-14 19:20:56.000000000 +0900 +@@ -0,0 +1,15 @@ ++# ++# SE-PostgreSQL install path ++# ++%%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0) ++ ++%%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++%%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) ++%%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++%%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0) ++ ++%%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0) ++ ++/var/lib/sepgsql(/.*)? gen_context(system_u:object_r:postgresql_db_t,s0) ++/var/lib/sepgsql/pgstartup\.log gen_context(system_u:object_r:postgresql_log_t,s0) ++/var/log/sepostgresql\.log.* -- gen_context(system_u:object_r:postgresql_log_t,s0) +diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql.if sepgsql/contrib/sepgsql_policy/sepostgresql.if +--- base/contrib/sepgsql_policy/sepostgresql.if 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/contrib/sepgsql_policy/sepostgresql.if 2008-06-26 11:44:44.000000000 +0900 +@@ -0,0 +1,259 @@ ++####################################### ++## ++## The userdomain template for the SE-PostgreSQL. ++## ++## ++## This template creates a delivered types which are used ++## for given userdomains. ++## ++## ++## ++## The prefix of the user domain (e.g., user ++## is the prefix for user_t). ++## ++## ++## ++## ++## The type of the user domain. ++## ++## ++## ++## ++## The role associated with the user domain. ++## ++## ++# ++template(`sepostgresql_userdom_template',` ++ gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute sepgsql_client_type; ++ attribute sepgsql_database_type; ++ attribute sepgsql_sysobj_table_type; ++ ++ type sepgsql_trusted_proc_t; ++ type sepgsql_trusted_proc_exec_t; ++ ') ++ ++ ######################################## ++ # ++ # Declarations ++ # ++ ++ typeattribute $2 sepgsql_client_type; ++ ++ type $1_sepgsql_blob_t; ++ postgresql_blob_object($1_sepgsql_blob_t) ++ ++ type $1_sepgsql_proc_exec_t; ++ postgresql_procedure_object($1_sepgsql_proc_exec_t) ++ ++ type $1_sepgsql_sysobj_t; ++ postgresql_system_table_object($1_sepgsql_sysobj_t) ++ ++ type $1_sepgsql_table_t; ++ postgresql_table_object($1_sepgsql_table_t) ++ ++ role $3 types sepgsql_trusted_proc_t; ++ ++ ############################## ++ # ++ # Client local policy ++ # ++ ++ tunable_policy(`sepgsql_enable_users_ddl',` ++ allow $2 $1_sepgsql_table_t : db_table { create drop }; ++ type_transition $2 sepgsql_database_type:db_table $1_sepgsql_table_t; ++ ++ allow $2 $1_sepgsql_table_t : db_column { create drop }; ++ ++ allow $2 $1_sepgsql_sysobj_t : db_tuple { update insert delete }; ++ type_transition $2 sepgsql_sysobj_table_type:db_tuple $1_sepgsql_sysobj_t; ++ ') ++ ++ allow $2 $1_sepgsql_table_t : db_table { getattr setattr use select update insert delete }; ++ allow $2 $1_sepgsql_table_t : db_column { getattr setattr use select update insert }; ++ allow $2 $1_sepgsql_table_t : db_tuple { use select update insert delete }; ++ allow $2 $1_sepgsql_sysobj_t : db_tuple { use select }; ++ ++ allow $2 $1_sepgsql_proc_exec_t : db_procedure { create drop getattr setattr execute }; ++ type_transition $2 sepgsql_database_type:db_procedure $1_sepgsql_proc_exec_t; ++ ++ allow $2 $1_sepgsql_blob_t : db_blob { create drop getattr setattr read write }; ++ type_transition $2 sepgsql_database_type:db_blob $1_sepgsql_blob_t; ++ ++ allow $2 sepgsql_trusted_proc_t:process transition; ++ type_transition $2 sepgsql_trusted_proc_exec_t:process sepgsql_trusted_proc_t; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL loadable shared library module ++## ++## ++## ++## Type marked as a database object type. ++## ++## ++# ++interface(`sepostgresql_loadable_module',` ++ gen_require(` ++ attribute sepgsql_module_type; ++ ') ++ ++ typeattribute $1 sepgsql_module_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL database object type ++## ++## ++## ++## Type marked as a database object type. ++## ++## ++# ++interface(`sepostgresql_database_object',` ++ gen_require(` ++ attribute sepgsql_database_type; ++ ') ++ ++ typeattribute $1 sepgsql_database_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL table/column/tuple object type ++## ++## ++## ++## Type marked as a table/column/tuple object type. ++## ++## ++# ++interface(`sepostgresql_table_object',` ++ gen_require(` ++ attribute sepgsql_table_type; ++ ') ++ ++ typeattribute $1 sepgsql_table_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL system table/column/tuple object type ++## ++## ++## ++## Type marked as a table/column/tuple object type. ++## ++## ++# ++interface(`sepostgresql_system_table_object',` ++ gen_require(` ++ attribute sepgsql_table_type; ++ attribute sepgsql_sysobj_table_type; ++ ') ++ ++ typeattribute $1 sepgsql_table_type; ++ typeattribute $1 sepgsql_sysobj_table_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL procedure object type ++## ++## ++## ++## Type marked as a database object type. ++## ++## ++# ++interface(`sepostgresql_procedure_object',` ++ gen_require(` ++ attribute sepgsql_procedure_type; ++ ') ++ ++ typeattribute $1 sepgsql_procedure_type; ++') ++ ++######################################## ++## ++## Marks as a SE-PostgreSQL binary large object type ++## ++## ++## ++## Type marked as a database binary large object type. ++## ++## ++# ++interface(`sepostgresql_blob_object',` ++ gen_require(` ++ attribute sepgsql_blob_type; ++ ') ++ ++ typeattribute $1 sepgsql_blob_type; ++') ++ ++######################################## ++## ++## Allow the specified domain unprivileged accesses to unifined database objects ++## managed by SE-PostgreSQL, ++## ++## ++## ++## Domain allowed access. ++## ++## ++# ++interface(`sepostgresql_unpriv_client',` ++ gen_require(` ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute sepgsql_client_type; ++ attribute sepgsql_database_type; ++ ++ type sepgsql_table_t; ++ type sepgsql_proc_t; ++ type sepgsql_blob_t; ++ ++ type sepgsql_trusted_proc_t; ++ type sepgsql_trusted_proc_exec_t; ++ ') ++ ++ typeattribute $1 sepgsql_client_type; ++ ++ type_transition $1 sepgsql_database_type:db_table sepgsql_table_t; ++ type_transition $1 sepgsql_database_type:db_procedure sepgsql_proc_t; ++ type_transition $1 sepgsql_database_type:db_blob sepgsql_blob_t; ++ ++ type_transition $1 sepgsql_trusted_proc_exec_t:process sepgsql_trusted_proc_t; ++ allow $1 sepgsql_trusted_proc_t:process transition; ++') ++ ++######################################## ++## ++## Allow the specified domain unconfined accesses to any database objects ++## managed by SE-PostgreSQL, ++## ++## ++## ++## Domain allowed access. ++## ++## ++# ++interface(`sepostgresql_unconfined',` ++ gen_require(` ++ attribute sepgsql_unconfined_type; ++ ') ++ ++ typeattribute $1 sepgsql_unconfined_type; ++') +diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql.te sepgsql/contrib/sepgsql_policy/sepostgresql.te +--- base/contrib/sepgsql_policy/sepostgresql.te 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/contrib/sepgsql_policy/sepostgresql.te 2008-06-26 13:45:05.000000000 +0900 +@@ -0,0 +1,308 @@ ++policy_module(sepostgresql, 3.11) ++ ++gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute domain, home_type; ++ type postgresql_t, unlabeled_t; ++ ++ role system_r; ++') ++ ++################################# ++# ++# SE-PostgreSQL Declarations ++# ++ ++## ++##

++## Allow to generate auditallow logs ++##

++##
++gen_tunable(sepgsql_enable_auditallow, false) ++ ++## ++##

++## Allow to generate auditdeny logs ++##

++##
++gen_tunable(sepgsql_enable_auditdeny, true) ++ ++## ++##

++## Allow unprivileged users to execute DDL statement ++##

++##
++gen_tunable(sepgsql_enable_users_ddl, true) ++ ++## ++##

++## Allow widespread permissions for regression test ++## Don't set TRUE on operation phase ++##

++##
++gen_tunable(sepgsql_regression_test_mode, false) ++ ++# database clients attribute ++attribute sepgsql_client_type; ++attribute sepgsql_unconfined_type; ++ ++# database objects attribute ++attribute sepgsql_database_type; ++attribute sepgsql_table_type; ++attribute sepgsql_sysobj_table_type; ++attribute sepgsql_procedure_type; ++attribute sepgsql_blob_type; ++attribute sepgsql_module_type; ++ ++# database object types ++type sepgsql_blob_t; ++sepostgresql_blob_object(sepgsql_blob_t) ++ ++type sepgsql_db_t; ++sepostgresql_database_object(sepgsql_db_t) ++ ++type sepgsql_fixed_table_t; ++sepostgresql_table_object(sepgsql_fixed_table_t) ++ ++type sepgsql_proc_t; ++sepostgresql_procedure_object(sepgsql_proc_t) ++ ++type sepgsql_ro_blob_t; ++sepostgresql_blob_object(sepgsql_ro_blob_t) ++ ++type sepgsql_ro_table_t; ++sepostgresql_table_object(sepgsql_ro_table_t) ++ ++type sepgsql_secret_blob_t; ++sepostgresql_blob_object(sepgsql_secret_blob_t) ++ ++type sepgsql_secret_table_t; ++sepostgresql_table_object(sepgsql_secret_table_t) ++ ++type sepgsql_sysobj_t; ++sepostgresql_system_table_object(sepgsql_sysobj_t) ++ ++type sepgsql_table_t; ++sepostgresql_table_object(sepgsql_table_t) ++ ++type sepgsql_trusted_proc_exec_t; ++sepostgresql_procedure_object(sepgsql_trusted_proc_exec_t) ++ ++# Trusted Procedure Domain ++type sepgsql_trusted_proc_t; ++domain_type(sepgsql_trusted_proc_t) ++sepostgresql_unconfined(sepgsql_trusted_proc_t) ++role system_r types sepgsql_trusted_proc_t; ++ ++######################################## ++# ++# SE-PostgreSQL Local Policy ++# ++allow postgresql_t self:netlink_selinux_socket create_socket_perms; ++selinux_get_enforce_mode(postgresql_t) ++selinux_validate_context(postgresql_t) ++selinux_compute_access_vector(postgresql_t) ++selinux_compute_create_context(postgresql_t) ++selinux_compute_relabel_context(postgresql_t) ++seutil_libselinux_linked(postgresql_t) ++ ++allow postgresql_t sepgsql_database_type:db_database *; ++type_transition postgresql_t postgresql_t:db_database sepgsql_db_t; ++ ++allow postgresql_t sepgsql_module_type:db_database install_module; ++allow postgresql_t sepgsql_table_type:{ db_table db_column db_tuple } *; ++allow postgresql_t sepgsql_procedure_type:db_procedure *; ++allow postgresql_t sepgsql_blob_type:db_blob *; ++ ++# server specific type transitions ++type_transition postgresql_t sepgsql_database_type:db_table sepgsql_sysobj_t; ++type_transition postgresql_t sepgsql_database_type:db_procedure sepgsql_proc_t; ++type_transition postgresql_t sepgsql_database_type:db_blob sepgsql_blob_t; ++ ++# Database/Loadable module ++allow sepgsql_database_type sepgsql_module_type:db_database load_module; ++ ++######################################## ++# ++# Rules common to all clients ++# ++ ++# Client domain constraint ++allow sepgsql_client_type sepgsql_db_t:db_database { getattr access get_param set_param }; ++type_transition sepgsql_client_type sepgsql_client_type:db_database sepgsql_db_t; ++ ++allow sepgsql_client_type sepgsql_fixed_table_t:db_table { getattr use select insert }; ++allow sepgsql_client_type sepgsql_fixed_table_t:db_column { getattr use select insert }; ++allow sepgsql_client_type sepgsql_fixed_table_t:db_tuple { use select insert }; ++ ++allow sepgsql_client_type sepgsql_table_t:db_table { getattr use select update insert delete }; ++allow sepgsql_client_type sepgsql_table_t:db_column { getattr use select update insert }; ++allow sepgsql_client_type sepgsql_table_t:db_tuple { use select update insert delete }; ++ ++allow sepgsql_client_type sepgsql_ro_table_t:db_table { getattr use select }; ++allow sepgsql_client_type sepgsql_ro_table_t:db_column { getattr use select }; ++allow sepgsql_client_type sepgsql_ro_table_t:db_tuple { use select }; ++ ++allow sepgsql_client_type sepgsql_secret_table_t:db_table getattr; ++allow sepgsql_client_type sepgsql_secret_table_t:db_column getattr; ++ ++allow sepgsql_client_type sepgsql_sysobj_t:db_table { getattr use select }; ++allow sepgsql_client_type sepgsql_sysobj_t:db_column { getattr use select }; ++allow sepgsql_client_type sepgsql_sysobj_t:db_tuple { use select }; ++ ++allow sepgsql_client_type sepgsql_proc_t:db_procedure { getattr execute }; ++allow sepgsql_client_type sepgsql_trusted_proc_t:db_procedure { getattr execute entrypoint }; ++ ++allow sepgsql_client_type sepgsql_blob_t:db_blob { create drop getattr setattr read write }; ++allow sepgsql_client_type sepgsql_ro_blob_t:db_blob { getattr read }; ++allow sepgsql_client_type sepgsql_secret_blob_t:db_blob getattr; ++ ++tunable_policy(`sepgsql_enable_users_ddl',` ++ allow sepgsql_client_type sepgsql_table_t:db_table { create drop setattr }; ++ allow sepgsql_client_type sepgsql_table_t:db_column { create drop setattr }; ++ allow sepgsql_client_type sepgsql_sysobj_t:db_tuple { update insert delete }; ++') ++ ++######################################## ++# ++# Unconfined access to this module ++# ++ ++allow sepgsql_unconfined_type sepgsql_database_type:db_database *; ++allow sepgsql_unconfined_type sepgsql_table_type:{ db_table db_column db_tuple } *; ++allow sepgsql_unconfined_type sepgsql_blob_type:db_blob *; ++allow sepgsql_unconfined_type { sepgsql_proc_t sepgsql_trusted_proc_t }:db_procedure *; ++allow sepgsql_unconfined_type sepgsql_procedure_type:db_procedure { create drop getattr setattr relabelfrom relabelto }; ++allow sepgsql_unconfined_type sepgsql_module_type:db_database install_module; ++ ++type_transition sepgsql_unconfined_type sepgsql_unconfined_type:db_database sepgsql_db_t; ++type_transition sepgsql_unconfined_type sepgsql_database_type:db_table sepgsql_table_t; ++type_transition sepgsql_unconfined_type sepgsql_database_type:db_procedure sepgsql_proc_t; ++type_transition sepgsql_unconfined_type sepgsql_database_type:db_blob sepgsql_blob_t; ++ ++ ++######################################## ++# ++# Allow permission to external domains ++# ++ ++# relabelfrom for invalid security context ++allow sepgsql_unconfined_type unlabeled_t:db_database { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_table { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_procedure { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_column { setattr relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_tuple { update relabelfrom }; ++allow sepgsql_unconfined_type unlabeled_t:db_blob { setattr relabelfrom }; ++ ++# administrative client domain ++optional_policy(` ++ gen_require(` ++ type unconfined_t; ++ ') ++ sepostgresql_unconfined(unconfined_t) ++') ++ ++optional_policy(` ++ gen_require(` ++ type sysadm_t; ++ ') ++ sepostgresql_unconfined(sysadm_t) ++') ++ ++# unprivilleged client domain ++optional_policy(` ++ gen_require(` ++ type user_t; ++ role user_r; ++ ') ++ sepostgresql_userdom_template(user,user_t,user_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type staff_t; ++ role staff_r; ++ ') ++ sepostgresql_userdom_template(staff,staff_t,staff_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type guest_t; ++ role guest_r; ++ ') ++ sepostgresql_userdom_template(guest,guest_t,guest_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type xguest_t; ++ role xguest_r; ++ ') ++ sepostgresql_userdom_template(xguest,xguest_t,xguest_r) ++') ++ ++optional_policy(` ++ gen_require(` ++ type httpd_t; ++ ') ++ sepostgresql_unpriv_client(httpd_t) ++') ++ ++optional_policy(` ++ gen_require(` ++ type httpd_sys_script_t; ++ ') ++ sepostgresql_unpriv_client(httpd_sys_script_t) ++') ++ ++# SE-PostgreSQL loadable modules ++optional_policy(` ++ gen_require(` ++ type lib_t, textrel_shlib_t; ++ ') ++ sepostgresql_loadable_module(lib_t) ++ sepostgresql_loadable_module(textrel_shlib_t) ++') ++ ++######################################## ++# ++# SE-PostgreSQL audit switch for debugging ++# ++tunable_policy(`sepgsql_enable_auditallow',` ++ auditallow domain sepgsql_database_type : db_database *; ++ auditallow domain sepgsql_table_type : db_table *; ++ auditallow domain sepgsql_table_type : db_column *; ++ auditallow domain sepgsql_procedure_type : db_procedure *; ++ auditallow domain sepgsql_blob_type : db_blob *; ++ auditallow domain sepgsql_module_type : db_database { install_module }; ++ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++tunable_policy(`! sepgsql_enable_auditdeny',` ++ dontaudit domain sepgsql_database_type : db_database *; ++ dontaudit domain sepgsql_table_type : db_table *; ++ dontaudit domain sepgsql_table_type : db_column *; ++ dontaudit domain sepgsql_procedure_type : db_procedure *; ++ dontaudit domain sepgsql_blob_type : db_blob *; ++ dontaudit domain sepgsql_module_type : db_database { install_module }; ++ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++dontaudit domain { sepgsql_table_type - sepgsql_sysobj_table_type } : db_tuple { use select update insert delete }; ++ ++######################################## ++# ++# SE-PostgreSQL regression test mode switch ++# ++tunable_policy(`sepgsql_regression_test_mode',` ++ allow sepgsql_client_type home_type : db_database { install_module }; ++ allow sepgsql_unconfined_type home_type : db_database { install_module }; ++ allow sepgsql_database_type home_type : db_database { load_module }; ++') +diff -rpNU3 base/src/Makefile.global.in sepgsql/src/Makefile.global.in +--- base/src/Makefile.global.in 2007-11-17 20:15:40.000000000 +0900 ++++ sepgsql/src/Makefile.global.in 2008-06-14 02:36:58.000000000 +0900 +@@ -165,6 +165,7 @@ enable_rpath = @enable_rpath@ + enable_nls = @enable_nls@ + enable_debug = @enable_debug@ + enable_dtrace = @enable_dtrace@ ++enable_selinux = @enable_selinux@ + enable_thread_safety = @enable_thread_safety@ + + python_includespec = @python_includespec@ +diff -rpNU3 base/src/backend/Makefile sepgsql/src/backend/Makefile +--- base/src/backend/Makefile 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/Makefile 2008-06-14 02:36:58.000000000 +0900 +@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.glo + + DIRS = access bootstrap catalog parser commands executor lib libpq \ + main nodes optimizer port postmaster regex rewrite \ +- storage tcop tsearch utils $(top_builddir)/src/timezone ++ security storage tcop tsearch utils $(top_builddir)/src/timezone + + SUBSYSOBJS = $(DIRS:%=%/SUBSYS.o) + +@@ -32,6 +32,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) + # The backend doesn't need everything that's in LIBS, however + LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) + ++# SELinux support needs to link libselinux ++ifeq ($(enable_selinux), yes) ++LIBS += -lselinux ++endif ++ + ########################################################################## + + all: submake-libpgport postgres $(POSTGRES_IMP) +diff -rpNU3 base/src/backend/access/common/heaptuple.c sepgsql/src/backend/access/common/heaptuple.c +--- base/src/backend/access/common/heaptuple.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/access/common/heaptuple.c 2008-12-28 01:06:59.000000000 +0900 +@@ -67,6 +67,7 @@ + #include "access/heapam.h" + #include "access/tuptoaster.h" + #include "executor/tuptable.h" ++#include "security/pgace.h" + + + /* Does att's datatype allow packing into the 1-byte-header varlena format? */ +@@ -473,6 +474,7 @@ heap_attisnull(HeapTuple tup, int attnum + case MinCommandIdAttributeNumber: + case MaxTransactionIdAttributeNumber: + case MaxCommandIdAttributeNumber: ++ case SecurityLabelAttributeNumber: + /* these are never null */ + break; + +@@ -785,6 +787,9 @@ heap_getsysattr(HeapTuple tup, int attnu + case TableOidAttributeNumber: + result = ObjectIdGetDatum(tup->t_tableOid); + break; ++ case SecurityLabelAttributeNumber: ++ result = pgaceHeapGetSecurityLabelSysattr(tup); ++ break; + default: + elog(ERROR, "invalid attnum: %d", attnum); + result = 0; /* keep compiler quiet */ +@@ -909,6 +914,9 @@ heap_form_tuple(TupleDesc tupleDescripto + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); + ++ if (tupleDescriptor->tdhasseclabel) ++ len += sizeof(Oid); ++ + hoff = len = MAXALIGN(len); /* align user data safely */ + + data_len = heap_compute_data_size(tupleDescriptor, values, isnull); +@@ -940,6 +948,9 @@ heap_form_tuple(TupleDesc tupleDescripto + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; + ++ if (tupleDescriptor->tdhasseclabel) ++ td->t_infomask |= HEAP_HAS_SECLABEL; ++ + heap_fill_tuple(tupleDescriptor, + values, + isnull, +@@ -1020,6 +1031,9 @@ heap_formtuple(TupleDesc tupleDescriptor + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); + ++ if (tupleDescriptor->tdhasseclabel) ++ len += sizeof(Oid); ++ + hoff = len = MAXALIGN(len); /* align user data safely */ + + data_len = ComputeDataSize(tupleDescriptor, values, nulls); +@@ -1051,6 +1065,9 @@ heap_formtuple(TupleDesc tupleDescriptor + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; + ++ if (tupleDescriptor->tdhasseclabel) ++ td->t_infomask |= HEAP_HAS_SECLABEL; ++ + DataFill(tupleDescriptor, + values, + nulls, +@@ -1129,6 +1146,8 @@ heap_modify_tuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecLabel(newTuple)) ++ HeapTupleSetSecLabel(newTuple, HeapTupleGetSecLabel(tuple)); + + return newTuple; + } +@@ -1201,6 +1220,8 @@ heap_modifytuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecLabel(newTuple)) ++ HeapTupleSetSecLabel(newTuple, HeapTupleGetSecLabel(tuple)); + + return newTuple; + } +@@ -1847,6 +1868,9 @@ heap_form_minimal_tuple(TupleDesc tupleD + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); + ++ if (tupleDescriptor->tdhasseclabel) ++ len += sizeof(Oid); ++ + hoff = len = MAXALIGN(len); /* align user data safely */ + + data_len = heap_compute_data_size(tupleDescriptor, values, isnull); +@@ -1868,6 +1892,9 @@ heap_form_minimal_tuple(TupleDesc tupleD + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + tuple->t_infomask = HEAP_HASOID; + ++ if (tupleDescriptor->tdhasseclabel) ++ tuple->t_infomask |= HEAP_HAS_SECLABEL; ++ + heap_fill_tuple(tupleDescriptor, + values, + isnull, +@@ -1965,6 +1992,7 @@ minimal_tuple_from_heap_tuple(HeapTuple + HeapTuple + heap_addheader(int natts, /* max domain index */ + bool withoid, /* reserve space for oid */ ++ bool withsecurity, /* reserve space for security */ + Size structlen, /* its length */ + void *structure) /* pointer to the struct */ + { +@@ -1979,6 +2007,10 @@ heap_addheader(int natts, /* max domain + hoff = offsetof(HeapTupleHeaderData, t_bits); + if (withoid) + hoff += sizeof(Oid); ++ ++ if (withsecurity) ++ hoff += sizeof(Oid); ++ + hoff = MAXALIGN(hoff); + len = hoff + structlen; + +@@ -1997,6 +2029,9 @@ heap_addheader(int natts, /* max domain + if (withoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; + ++ if (withsecurity) ++ td->t_infomask |= HEAP_HAS_SECLABEL; ++ + memcpy((char *) td + hoff, structure, structlen); + + return tuple; +diff -rpNU3 base/src/backend/access/common/tupdesc.c sepgsql/src/backend/access/common/tupdesc.c +--- base/src/backend/access/common/tupdesc.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/access/common/tupdesc.c 2008-12-28 01:06:59.000000000 +0900 +@@ -84,6 +84,7 @@ CreateTemplateTupleDesc(int natts, bool + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhasseclabel = false; /* set a proper value, if necessary */ + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -117,6 +118,7 @@ CreateTupleDesc(int natts, bool hasoid, + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhasseclabel = false; /* set a proper value, if necessary */ + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -146,6 +148,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhasseclabel = tupdesc->tdhasseclabel; + + return desc; + } +@@ -204,6 +207,7 @@ CreateTupleDescCopyConstr(TupleDesc tupd + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhasseclabel = tupdesc->tdhasseclabel; + + return desc; + } +@@ -310,6 +314,8 @@ equalTupleDescs(TupleDesc tupdesc1, Tupl + return false; + if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) + return false; ++ if (tupdesc1->tdhasseclabel != tupdesc2->tdhasseclabel) ++ return false; + + for (i = 0; i < tupdesc1->natts; i++) + { +diff -rpNU3 base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c +--- base/src/backend/access/heap/heapam.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/access/heap/heapam.c 2009-02-02 11:58:34.000000000 +0900 +@@ -50,6 +50,7 @@ + #include "catalog/namespace.h" + #include "miscadmin.h" + #include "pgstat.h" ++#include "security/pgace.h" + #include "storage/procarray.h" + #include "storage/smgr.h" + #include "utils/datum.h" +@@ -1949,6 +1950,12 @@ heap_insert(Relation relation, HeapTuple + Oid + simple_heap_insert(Relation relation, HeapTuple tup) + { ++ if (!pgaceHeapTupleInsert(relation, tup, true, false)) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("could not insert tuple on \"%s\" due to pgace security", ++ RelationGetRelationName(relation)))); ++ + return heap_insert(relation, tup, GetCurrentCommandId(true), true, true); + } + +@@ -2230,6 +2237,12 @@ simple_heap_delete(Relation relation, It + ItemPointerData update_ctid; + TransactionId update_xmax; + ++ if (!pgaceHeapTupleDelete(relation, tid, true, false)) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("could not delete tuple on \"%s\" due to pgace security", ++ RelationGetRelationName(relation)))); ++ + result = heap_delete(relation, tid, + &update_ctid, &update_xmax, + GetCurrentCommandId(true), InvalidSnapshot, +@@ -2874,6 +2887,12 @@ simple_heap_update(Relation relation, It + ItemPointerData update_ctid; + TransactionId update_xmax; + ++ if (!pgaceHeapTupleUpdate(relation, otid, tup, true, false)) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("could not update tuple on \"%s\" due to pgace security", ++ RelationGetRelationName(relation)))); ++ + result = heap_update(relation, otid, tup, + &update_ctid, &update_xmax, + GetCurrentCommandId(true), InvalidSnapshot, +diff -rpNU3 base/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/heap/tuptoaster.c +--- base/src/backend/access/heap/tuptoaster.c 2008-09-25 15:09:40.000000000 +0900 ++++ sepgsql/src/backend/access/heap/tuptoaster.c 2009-01-14 15:06:52.000000000 +0900 +@@ -35,6 +35,7 @@ + #include "access/tuptoaster.h" + #include "access/xact.h" + #include "catalog/catalog.h" ++#include "security/pgace.h" + #include "utils/fmgroids.h" + #include "utils/pg_lzcompress.h" + #include "utils/typcache.h" +@@ -589,6 +590,8 @@ toast_insert_or_update(Relation rel, Hea + hoff += BITMAPLEN(numAttrs); + if (newtup->t_data->t_infomask & HEAP_HASOID) + hoff += sizeof(Oid); ++ if (newtup->t_data->t_infomask & HEAP_HAS_SECLABEL) ++ hoff += sizeof(Oid); + hoff = MAXALIGN(hoff); + Assert(hoff == newtup->t_data->t_hoff); + /* now convert to a limit on the tuple data size */ +@@ -838,6 +841,8 @@ toast_insert_or_update(Relation rel, Hea + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (olddata->t_infomask & HEAP_HAS_SECLABEL) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +@@ -989,6 +994,8 @@ toast_flatten_tuple_attribute(Datum valu + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (olddata->t_infomask & HEAP_HAS_SECLABEL) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +@@ -1173,6 +1180,12 @@ toast_save_datum(Relation rel, Datum val + memcpy(VARDATA(&chunk_data), data_p, chunk_size); + toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); + ++ if (!pgaceHeapTupleInsert(toastrel, toasttup, true, false)) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("could not insert tuple \"%s\" due to pgace security", ++ RelationGetRelationName(toastrel)))); ++ + heap_insert(toastrel, toasttup, mycid, use_wal, use_fsm); + + /* +diff -rpNU3 base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y +--- base/src/backend/bootstrap/bootparse.y 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/bootstrap/bootparse.y 2008-12-28 01:06:59.000000000 +0900 +@@ -42,6 +42,7 @@ + #include "nodes/pg_list.h" + #include "nodes/primnodes.h" + #include "rewrite/prs2lock.h" ++#include "security/pgace.h" + #include "storage/block.h" + #include "storage/fd.h" + #include "storage/ipc.h" +@@ -194,6 +195,13 @@ Boot_CreateStmt: + RELKIND_RELATION, + $3, + true); ++ /* ++ * fixup boot_reldesc->rd_att->tdhasseclabel ++ */ ++ boot_reldesc->rd_rel->relkind = RELKIND_RELATION; ++ boot_reldesc->rd_att->tdhasseclabel ++ = pgaceTupleDescHasSecLabel(boot_reldesc, NIL); ++ + elog(DEBUG4, "bootstrap relation created"); + } + else +@@ -212,7 +220,8 @@ Boot_CreateStmt: + 0, + ONCOMMIT_NOOP, + (Datum) 0, +- true); ++ true, ++ NIL); + elog(DEBUG4, "relation created with oid %u", id); + } + do_end(); +diff -rpNU3 base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c +--- base/src/backend/bootstrap/bootstrap.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/bootstrap/bootstrap.c 2008-12-28 01:06:59.000000000 +0900 +@@ -31,6 +31,7 @@ + #include "nodes/makefuncs.h" + #include "postmaster/bgwriter.h" + #include "postmaster/walwriter.h" ++#include "security/pgace.h" + #include "storage/freespace.h" + #include "storage/ipc.h" + #include "storage/proc.h" +@@ -499,6 +500,8 @@ BootstrapModeMain(void) + */ + boot_yyparse(); + ++ pgacePostBootstrapingMode(); ++ + /* Perform a checkpoint to ensure everything's down to disk */ + SetProcessingMode(NormalProcessing); + CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); +@@ -796,6 +799,7 @@ InsertOneTuple(Oid objectid) + tupDesc = CreateTupleDesc(numattr, + RelationGetForm(boot_reldesc)->relhasoids, + attrtypes); ++ tupDesc->tdhasseclabel = pgaceTupleDescHasSecLabel(boot_reldesc, NIL); + tuple = heap_formtuple(tupDesc, values, Blanks); + if (objectid != (Oid) 0) + HeapTupleSetOid(tuple, objectid); +diff -rpNU3 base/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefile +--- base/src/backend/catalog/Makefile 2007-09-11 10:53:53.000000000 +0900 ++++ sepgsql/src/backend/catalog/Makefile 2008-06-14 02:36:58.000000000 +0900 +@@ -35,6 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_sr + pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ + pg_database.h pg_tablespace.h pg_pltemplate.h \ + pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ ++ pg_security.h \ + pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ + pg_ts_parser.h pg_ts_template.h \ + toasting.h indexing.h \ +diff -rpNU3 base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalog.c +--- base/src/backend/catalog/catalog.c 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/catalog/catalog.c 2008-06-14 02:36:58.000000000 +0900 +@@ -30,6 +30,7 @@ + #include "catalog/pg_database.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_pltemplate.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_shdepend.h" + #include "catalog/pg_shdescription.h" + #include "catalog/pg_tablespace.h" +@@ -257,6 +258,7 @@ IsSharedRelation(Oid relationId) + relationId == AuthMemRelationId || + relationId == DatabaseRelationId || + relationId == PLTemplateRelationId || ++ relationId == SecurityRelationId || + relationId == SharedDescriptionRelationId || + relationId == SharedDependRelationId || + relationId == TableSpaceRelationId) +@@ -269,6 +271,8 @@ IsSharedRelation(Oid relationId) + relationId == DatabaseNameIndexId || + relationId == DatabaseOidIndexId || + relationId == PLTemplateNameIndexId || ++ relationId == SecurityOidIndexId || ++ relationId == SecuritySeclabelIndexId || + relationId == SharedDescriptionObjIndexId || + relationId == SharedDependDependerIndexId || + relationId == SharedDependReferenceIndexId || +diff -rpNU3 base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c +--- base/src/backend/catalog/heap.c 2009-03-15 17:47:25.000000000 +0900 ++++ sepgsql/src/backend/catalog/heap.c 2009-03-15 17:53:20.000000000 +0900 +@@ -53,6 +53,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_expr.h" + #include "parser/parse_relation.h" ++#include "security/pgace.h" + #include "storage/smgr.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -67,7 +68,8 @@ static void AddNewRelationTuple(Relation + Oid new_rel_oid, Oid new_type_oid, + Oid relowner, + char relkind, +- Datum reloptions); ++ Datum reloptions, ++ List *pgace_attr_list); + static Oid AddNewRelationType(const char *typeName, + Oid typeNamespace, + Oid new_rel_oid, +@@ -145,7 +147,13 @@ static FormData_pg_attribute a7 = { + true, 'p', 'i', true, false, false, true, 0 + }; + +-static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; ++static FormData_pg_attribute a8 = { ++ 0, {SecurityLabelAttributeName}, TEXTOID, 0, -1, ++ SecurityLabelAttributeNumber, 0, -1, -1, ++ false, 'x', 'i', true, false, false, true, 0 ++}; ++ ++static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; + + /* + * This function returns a Form_pg_attribute pointer for a system attribute. +@@ -185,6 +193,18 @@ SystemAttributeByName(const char *attnam + return NULL; + } + ++/* ++ * This function returns true, if the given attribute number is writable ++ * system column. If not, returns false. ++ */ ++bool ++SystemAttributeIsWritable(AttrNumber attnum) ++{ ++ if (attnum == SecurityLabelAttributeNumber) ++ return true; ++ ++ return false; ++} + + /* ---------------------------------------------------------------- + * XXX END OF UGLY HARD CODED BADNESS XXX +@@ -468,7 +488,8 @@ AddNewAttributeTuples(Oid new_rel_oid, + TupleDesc tupdesc, + char relkind, + bool oidislocal, +- int oidinhcount) ++ int oidinhcount, ++ List *pgace_attr_list) + { + const Form_pg_attribute *dpp; + int i; +@@ -501,8 +522,10 @@ AddNewAttributeTuples(Oid new_rel_oid, + + tup = heap_addheader(Natts_pg_attribute, + false, ++ RelationGetDescr(rel)->tdhasseclabel, + ATTRIBUTE_TUPLE_SIZE, + (void *) *dpp); ++ pgaceCreateAttributeCommon(rel, tup, pgace_attr_list); + + simple_heap_insert(rel, tup); + +@@ -538,6 +561,7 @@ AddNewAttributeTuples(Oid new_rel_oid, + + tup = heap_addheader(Natts_pg_attribute, + false, ++ RelationGetDescr(rel)->tdhasseclabel, + ATTRIBUTE_TUPLE_SIZE, + (void *) *dpp); + attStruct = (Form_pg_attribute) GETSTRUCT(tup); +@@ -593,7 +617,8 @@ void + InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +- Datum reloptions) ++ Datum reloptions, ++ List *pgace_attr_list) + { + Form_pg_class rd_rel = new_rel_desc->rd_rel; + Datum values[Natts_pg_class]; +@@ -643,12 +668,16 @@ InsertPgClassTuple(Relation pg_class_des + * be embarrassing to do this sort of thing in polite company. + */ + HeapTupleSetOid(tup, new_rel_oid); ++ pgaceCreateRelationCommon(pg_class_desc, tup, pgace_attr_list); + + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_class_desc, tup); + + CatalogUpdateIndexes(pg_class_desc, tup); + ++ /* temporary use for this tuple */ ++ InsertSysCache(RelationGetRelid(pg_class_desc), tup); ++ + heap_freetuple(tup); + } + +@@ -666,7 +695,8 @@ AddNewRelationTuple(Relation pg_class_de + Oid new_type_oid, + Oid relowner, + char relkind, +- Datum reloptions) ++ Datum reloptions, ++ List *pgace_attr_list) + { + Form_pg_class new_rel_reltup; + +@@ -726,7 +756,7 @@ AddNewRelationTuple(Relation pg_class_de + new_rel_desc->rd_att->tdtypeid = new_type_oid; + + /* Now build and insert the tuple */ +- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); ++ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions, pgace_attr_list); + } + + +@@ -794,7 +824,8 @@ heap_create_with_catalog(const char *rel + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +- bool allow_system_table_mods) ++ bool allow_system_table_mods, ++ List *pgace_attr_list) + { + Relation pg_class_desc; + Relation new_rel_desc; +@@ -968,13 +999,20 @@ heap_create_with_catalog(const char *rel + new_type_oid, + ownerid, + relkind, +- reloptions); ++ reloptions, ++ pgace_attr_list); + + /* + * now add tuples to pg_attribute for the attributes in our new relation. + */ + AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, +- oidislocal, oidinhcount); ++ oidislocal, oidinhcount, pgace_attr_list); ++ ++ /* ++ * Fixup rel->rd_att->tdhasseclabel ++ */ ++ new_rel_desc->rd_att->tdhasseclabel ++ = pgaceTupleDescHasSecLabel(new_rel_desc, NIL); + + /* + * Make a dependency link to force the relation to be deleted if its +diff -rpNU3 base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c +--- base/src/backend/catalog/index.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/catalog/index.c 2009-02-02 11:58:34.000000000 +0900 +@@ -45,6 +45,7 @@ + #include "optimizer/clauses.h" + #include "optimizer/var.h" + #include "parser/parse_expr.h" ++#include "security/pgace.h" + #include "storage/procarray.h" + #include "storage/smgr.h" + #include "utils/builtins.h" +@@ -315,6 +316,7 @@ AppendAttributeTuples(Relation indexRela + + new_tuple = heap_addheader(Natts_pg_attribute, + false, ++ RelationGetDescr(pg_attribute)->tdhasseclabel, + ATTRIBUTE_TUPLE_SIZE, + (void *) indexTupDesc->attrs[i]); + +@@ -602,6 +604,12 @@ index_create(Oid heapRelationId, + Assert(indexRelationId == RelationGetRelid(indexRelation)); + + /* ++ * Fixup rel->rd_att->tdhasseclabel ++ */ ++ indexRelation->rd_att->tdhasseclabel ++ = pgaceTupleDescHasSecLabel(indexRelation, NIL); ++ ++ /* + * Obtain exclusive lock on it. Although no other backends can see it + * until we commit, this prevents deadlock-risk complaints from lock + * manager in cases such as CLUSTER. +@@ -624,7 +632,7 @@ index_create(Oid heapRelationId, + */ + InsertPgClassTuple(pg_class, indexRelation, + RelationGetRelid(indexRelation), +- reloptions); ++ reloptions, NIL); + + /* done with pg_class */ + heap_close(pg_class, RowExclusiveLock); +diff -rpNU3 base/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/pg_aggregate.c +--- base/src/backend/catalog/pg_aggregate.c 2008-01-14 22:59:48.000000000 +0900 ++++ sepgsql/src/backend/catalog/pg_aggregate.c 2008-06-14 02:36:58.000000000 +0900 +@@ -213,8 +213,9 @@ AggregateCreate(const char *aggName, + PointerGetDatum(NULL), /* parameterModes */ + PointerGetDatum(NULL), /* parameterNames */ + PointerGetDatum(NULL), /* proconfig */ +- 1, /* procost */ +- 0); /* prorows */ ++ 1, /* procost */ ++ 0, /* prorows */ ++ NULL); /* PGACE opaque */ + + /* + * Okay to create the pg_aggregate entry. +diff -rpNU3 base/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c +--- base/src/backend/catalog/pg_largeobject.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/catalog/pg_largeobject.c 2008-06-14 02:36:58.000000000 +0900 +@@ -18,6 +18,7 @@ + #include "access/heapam.h" + #include "catalog/indexing.h" + #include "catalog/pg_largeobject.h" ++#include "security/pgace.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + +@@ -57,6 +58,8 @@ LargeObjectCreate(Oid loid) + + ntup = heap_formtuple(pg_largeobject->rd_att, values, nulls); + ++ pgaceLargeObjectCreate(pg_largeobject, ntup); ++ + /* + * Insert it + */ +@@ -78,6 +81,7 @@ LargeObjectDrop(Oid loid) + ScanKeyData skey[1]; + SysScanDesc sd; + HeapTuple tuple; ++ void *pgaceItem = NULL; + + ScanKeyInit(&skey[0], + Anum_pg_largeobject_loid, +@@ -91,6 +95,7 @@ LargeObjectDrop(Oid loid) + + while ((tuple = systable_getnext(sd)) != NULL) + { ++ pgaceLargeObjectDrop(pg_largeobject, tuple, &pgaceItem); + simple_heap_delete(pg_largeobject, &tuple->t_self); + found = true; + } +diff -rpNU3 base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c +--- base/src/backend/catalog/pg_proc.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/catalog/pg_proc.c 2008-06-14 02:36:58.000000000 +0900 +@@ -27,6 +27,7 @@ + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "parser/parse_type.h" ++#include "security/pgace.h" + #include "tcop/pquery.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -74,7 +75,8 @@ ProcedureCreate(const char *procedureNam + Datum parameterNames, + Datum proconfig, + float4 procost, +- float4 prorows) ++ float4 prorows, ++ void *pgaceItem) + { + Oid retval; + int parameterCount; +@@ -339,6 +341,7 @@ ProcedureCreate(const char *procedureNam + + /* Okay, do it... */ + tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces); ++ pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem); + simple_heap_update(rel, &tup->t_self, tup); + + ReleaseSysCache(oldtup); +@@ -348,6 +351,7 @@ ProcedureCreate(const char *procedureNam + { + /* Creating a new procedure */ + tup = heap_formtuple(tupDesc, values, nulls); ++ pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem); + simple_heap_insert(rel, tup); + is_update = false; + } +diff -rpNU3 base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c +--- base/src/backend/catalog/toasting.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/catalog/toasting.c 2008-11-24 12:05:32.000000000 +0900 +@@ -199,7 +199,8 @@ create_toast_table(Relation rel, Oid toa + 0, + ONCOMMIT_NOOP, + (Datum) 0, +- true); ++ true, ++ NIL); + + /* make the toast relation visible, else index creation will fail */ + CommandCounterIncrement(); +diff -rpNU3 base/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c +--- base/src/backend/commands/cluster.c 2008-02-03 01:11:28.000000000 +0900 ++++ sepgsql/src/backend/commands/cluster.c 2008-12-28 01:06:59.000000000 +0900 +@@ -666,7 +666,8 @@ make_new_heap(Oid OIDOldHeap, const char + 0, + ONCOMMIT_NOOP, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ NIL); + + ReleaseSysCache(tuple); + +@@ -857,6 +858,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOl + if (NewHeap->rd_rel->relhasoids) + HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); + ++ /* Preserve SID, if any */ ++ if (HeapTupleHasSecLabel(tuple)) ++ HeapTupleSetSecLabel(copiedTuple, HeapTupleGetSecLabel(tuple)); ++ + /* The heap rewrite module does the rest */ + rewrite_heap_tuple(rwstate, tuple, copiedTuple); + +diff -rpNU3 base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c +--- base/src/backend/commands/copy.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/commands/copy.c 2008-12-28 01:06:59.000000000 +0900 +@@ -22,6 +22,7 @@ + + #include "access/heapam.h" + #include "access/xact.h" ++#include "catalog/heap.h" + #include "catalog/namespace.h" + #include "catalog/pg_type.h" + #include "commands/copy.h" +@@ -34,6 +35,7 @@ + #include "optimizer/planner.h" + #include "parser/parse_relation.h" + #include "rewrite/rewriteHandler.h" ++#include "security/pgace.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -159,6 +161,10 @@ typedef struct CopyStateData + char *raw_buf; + int raw_buf_index; /* next byte to process */ + int raw_buf_len; /* total # of bytes stored */ ++ ++ /* dump/restore support for security_label */ ++ FmgrInfo seclabel_out_function; ++ bool seclabel_force_quot; + } CopyStateData; + + typedef CopyStateData *CopyState; +@@ -242,7 +248,7 @@ static const char BinarySignature[11] = + /* non-export function prototypes */ + static void DoCopyTo(CopyState cstate); + static void CopyTo(CopyState cstate); +-static void CopyOneRowTo(CopyState cstate, Oid tupleOid, ++static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid secLabelId, + Datum *values, bool *nulls); + static void CopyFrom(CopyState cstate); + static bool CopyReadLine(CopyState cstate); +@@ -1073,6 +1079,8 @@ DoCopy(const CopyStmt *stmt, const char + /* Generate or convert list of attributes to process */ + cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); + ++ pgaceCopyTable(cstate->rel, cstate->attnumlist, is_from); ++ + num_phys_attrs = tupDesc->natts; + + /* Convert FORCE QUOTE name list to per-column flags, check validity */ +@@ -1089,11 +1097,32 @@ DoCopy(const CopyStmt *stmt, const char + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = tupDesc->attrs[attnum - 1]; ++ ++ Assert(attForm != NULL); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); +- cstate->force_quote_flags[attnum - 1] = true; ++ NameStr(attForm->attname)))); ++ } ++ ++ switch (attnum) ++ { ++ case SecurityLabelAttributeNumber: ++ cstate->seclabel_force_quot = true; ++ break; ++ ++ default: ++ cstate->force_quote_flags[attnum - 1] = true; ++ break; ++ } + } + } + +@@ -1111,10 +1140,24 @@ DoCopy(const CopyStmt *stmt, const char + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = tupDesc->attrs[attnum - 1]; ++ ++ Assert(attForm != NULL); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); ++ NameStr(attForm->attname)))); ++ } ++ if (SystemAttributeIsWritable(attnum)) ++ continue; /* ignore, if specified */ ++ + cstate->force_notnull_flags[attnum - 1] = true; + } + } +@@ -1242,6 +1285,9 @@ DoCopyTo(CopyState cstate) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a directory", cstate->filename))); ++ ++ pgaceCopyFile(cstate->rel, fileno(cstate->copy_file), ++ cstate->filename, false); + } + + PG_TRY(); +@@ -1305,16 +1351,30 @@ CopyTo(CopyState cstate) + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; ++ FmgrInfo *out_fmgr; ++ Form_pg_attribute attForm; ++ ++ switch (attnum) ++ { ++ case SecurityLabelAttributeNumber: ++ attForm = SystemAttributeDefinition(attnum, true); ++ out_fmgr = &cstate->seclabel_out_function; ++ break; ++ default: /* user columns */ ++ attForm = attr[attnum - 1]; ++ out_fmgr = &cstate->out_functions[attnum - 1]; ++ break; ++ } + + if (cstate->binary) +- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeBinaryOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); + else +- getTypeOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); +- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); ++ fmgr_info(out_func_oid, out_fmgr); + } + + /* +@@ -1369,7 +1429,14 @@ CopyTo(CopyState cstate) + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +- colname = NameStr(attr[attnum - 1]->attname); ++ if (SystemAttributeIsWritable(attnum)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, true); ++ colname = NameStr(attForm->attname); ++ } ++ else ++ colname = NameStr(attr[attnum - 1]->attname); + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +@@ -1395,11 +1462,17 @@ CopyTo(CopyState cstate) + { + CHECK_FOR_INTERRUPTS(); + ++ if (!pgaceCopyToTuple(cstate->rel, cstate->attnumlist, tuple)) ++ continue; ++ + /* Deconstruct the tuple ... faster than repeated heap_getattr */ + heap_deform_tuple(tuple, tupDesc, values, nulls); + + /* Format and send the data */ +- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); ++ CopyOneRowTo(cstate, ++ HeapTupleGetOid(tuple), ++ HeapTupleGetSecLabel(tuple), ++ values, nulls); + } + + heap_endscan(scandesc); +@@ -1425,7 +1498,8 @@ CopyTo(CopyState cstate) + * Emit one row during CopyTo(). + */ + static void +-CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) ++CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid secLabelId, ++ Datum *values, bool *nulls) + { + bool need_delim = false; + FmgrInfo *out_functions = cstate->out_functions; +@@ -1464,8 +1538,10 @@ CopyOneRowTo(CopyState cstate, Oid tuple + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); +- Datum value = values[attnum - 1]; +- bool isnull = nulls[attnum - 1]; ++ Datum value; ++ bool isnull; ++ bool force_quot; ++ FmgrInfo *out_fmgr; + + if (!cstate->binary) + { +@@ -1474,6 +1550,23 @@ CopyOneRowTo(CopyState cstate, Oid tuple + need_delim = true; + } + ++ switch (attnum) ++ { ++ case SecurityLabelAttributeNumber: ++ value = CStringGetTextDatum(pgaceSidToSecurityLabel(secLabelId)); ++ isnull = false; ++ force_quot = cstate->seclabel_force_quot; ++ out_fmgr = &cstate->seclabel_out_function; ++ break; ++ ++ default: ++ value = values[attnum - 1]; ++ isnull = nulls[attnum - 1]; ++ force_quot = cstate->force_quote_flags[attnum - 1]; ++ out_fmgr = &out_functions[attnum - 1]; ++ break; ++ } ++ + if (isnull) + { + if (!cstate->binary) +@@ -1485,11 +1578,9 @@ CopyOneRowTo(CopyState cstate, Oid tuple + { + if (!cstate->binary) + { +- string = OutputFunctionCall(&out_functions[attnum - 1], +- value); ++ string = OutputFunctionCall(out_fmgr, value); + if (cstate->csv_mode) +- CopyAttributeOutCSV(cstate, string, +- cstate->force_quote_flags[attnum - 1], ++ CopyAttributeOutCSV(cstate, string, force_quot, + list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, string); +@@ -1498,8 +1589,7 @@ CopyOneRowTo(CopyState cstate, Oid tuple + { + bytea *outputbytes; + +- outputbytes = SendFunctionCall(&out_functions[attnum - 1], +- value); ++ outputbytes = SendFunctionCall(out_fmgr, value); + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); +@@ -1633,10 +1723,13 @@ CopyFrom(CopyState cstate) + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; ++ FmgrInfo seclabel_in_function; + Oid *typioparams; + Oid oid_typioparam; ++ Oid seclabel_typioparam; + int attnum; + int i; ++ ListCell *l; + Oid in_func_oid; + Datum *values; + char *nulls; +@@ -1737,6 +1830,9 @@ CopyFrom(CopyState cstate) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a directory", cstate->filename))); ++ ++ pgaceCopyFile(cstate->rel, fileno(cstate->copy_file), ++ cstate->filename, true); + } + + tupDesc = RelationGetDescr(cstate->rel); +@@ -1872,6 +1968,24 @@ CopyFrom(CopyState cstate) + fmgr_info(in_func_oid, &oid_in_function); + } + ++ foreach (l, cstate->attnumlist) ++ { ++ switch (lfirst_int(l)) ++ { ++ case SecurityLabelAttributeNumber: ++ if (!cstate->binary) ++ getTypeInputInfo(TEXTOID, ++ &in_func_oid, ++ &seclabel_typioparam); ++ else ++ getTypeBinaryInputInfo(TEXTOID, ++ &in_func_oid, ++ &seclabel_typioparam); ++ fmgr_info(in_func_oid, &seclabel_in_function); ++ break; ++ } ++ } ++ + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (char *) palloc(num_phys_attrs * sizeof(char)); + +@@ -1904,6 +2018,7 @@ CopyFrom(CopyState cstate) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; ++ Datum loaded_seclabel = PointerGetDatum(NULL); + + CHECK_FOR_INTERRUPTS(); + +@@ -1978,6 +2093,37 @@ CopyFrom(CopyState cstate) + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (SystemAttributeIsWritable(attnum)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, true); ++ ++ if (fieldno >= fldct) ++ ereport(ERROR, ++ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), ++ errmsg("missing data for column \"%s\"", ++ NameStr(attForm->attname)))); ++ string = field_strings[fieldno++]; ++ cstate->cur_attname = NameStr(attForm->attname); ++ cstate->cur_attval = string; ++ if (string) ++ { ++ switch (attnum) ++ { ++ case SecurityLabelAttributeNumber: ++ loaded_seclabel ++ = InputFunctionCall(&seclabel_in_function, ++ string, ++ seclabel_typioparam, ++ attForm->atttypmod); ++ break; ++ } ++ } ++ cstate->cur_attname = NULL; ++ cstate->cur_attval = NULL; ++ continue; ++ } ++ + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), +@@ -2048,6 +2194,31 @@ CopyFrom(CopyState cstate) + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (SystemAttributeIsWritable(attnum)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, false); ++ Datum tmp; ++ ++ cstate->cur_attname = NameStr(attForm->attname); ++ i++; ++ ++ switch (attnum) ++ { ++ case SecurityLabelAttributeNumber: ++ tmp = CopyReadBinaryAttribute(cstate, i, ++ &seclabel_in_function, ++ seclabel_typioparam, ++ attForm->atttypmod, ++ &isnull); ++ if (!isnull) ++ loaded_seclabel = tmp; ++ break; ++ } ++ cstate->cur_attname = NULL; ++ continue; ++ } ++ + cstate->cur_attname = NameStr(attr[m]->attname); + i++; + values[m] = CopyReadBinaryAttribute(cstate, +@@ -2079,6 +2250,11 @@ CopyFrom(CopyState cstate) + + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); ++ if (loaded_seclabel != PointerGetDatum(NULL)) ++ { ++ char *label = TextDatumGetCString(loaded_seclabel); ++ HeapTupleSetSecLabel(tuple, pgaceSecurityLabelToSid(label)); ++ } + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); +@@ -2102,6 +2278,9 @@ CopyFrom(CopyState cstate) + } + } + ++ if (!skip_tuple && !pgaceHeapTupleInsert(cstate->rel, tuple, false, false)) ++ skip_tuple = true; ++ + if (!skip_tuple) + { + /* Place tuple in tuple slot */ +@@ -3364,6 +3543,16 @@ CopyGetAttnums(TupleDesc tupDesc, Relati + break; + } + } ++ ++ /* Is it writable system column? */ ++ if (attnum == InvalidAttrNumber) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeByName(name, tupDesc->tdhasoid); ++ if (attForm && SystemAttributeIsWritable(attForm->attnum)) ++ attnum = attForm->attnum; ++ } ++ + if (attnum == InvalidAttrNumber) + { + if (rel != NULL) +@@ -3413,7 +3602,9 @@ copy_dest_receive(TupleTableSlot *slot, + slot_getallattrs(slot); + + /* And send the data */ +- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); ++ CopyOneRowTo(cstate, ++ InvalidOid, InvalidOid, ++ slot->tts_values, slot->tts_isnull); + } + + /* +diff -rpNU3 base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c +--- base/src/backend/commands/dbcommands.c 2008-11-05 09:57:00.000000000 +0900 ++++ sepgsql/src/backend/commands/dbcommands.c 2008-11-05 10:01:30.000000000 +0900 +@@ -40,6 +40,7 @@ + #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/bgwriter.h" ++#include "security/pgace.h" + #include "storage/freespace.h" + #include "storage/ipc.h" + #include "storage/procarray.h" +@@ -100,6 +101,7 @@ createdb(const CreatedbStmt *stmt) + DefElem *dtemplate = NULL; + DefElem *dencoding = NULL; + DefElem *dconnlimit = NULL; ++ DefElem *dpgace_item = NULL; + char *dbname = stmt->dbname; + char *dbowner = NULL; + const char *dbtemplate = NULL; +@@ -160,6 +162,13 @@ createdb(const CreatedbStmt *stmt) + errmsg("LOCATION is not supported anymore"), + errhint("Consider using tablespaces instead."))); + } ++ else if (pgaceIsGramSecurityItem(defel)) { ++ if (dpgace_item) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ dpgace_item = defel; ++ } + else + elog(ERROR, "option \"%s\" not recognized", + defel->defname); +@@ -433,6 +442,7 @@ createdb(const CreatedbStmt *stmt) + new_record, new_record_nulls); + + HeapTupleSetOid(tuple, dboid); ++ pgaceGramCreateDatabase(pg_database_rel, tuple, dpgace_item); + + simple_heap_insert(pg_database_rel, tuple); + +@@ -858,6 +868,7 @@ AlterDatabase(AlterDatabaseStmt *stmt) + ListCell *option; + int connlimit = -1; + DefElem *dconnlimit = NULL; ++ DefElem *dpgace_item = NULL; + Datum new_record[Natts_pg_database]; + char new_record_nulls[Natts_pg_database]; + char new_record_repl[Natts_pg_database]; +@@ -875,6 +886,13 @@ AlterDatabase(AlterDatabaseStmt *stmt) + errmsg("conflicting or redundant options"))); + dconnlimit = defel; + } ++ else if (pgaceIsGramSecurityItem(defel)) { ++ if (dpgace_item) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ dpgace_item = defel; ++ } + else + elog(ERROR, "option \"%s\" not recognized", + defel->defname); +@@ -920,6 +938,7 @@ AlterDatabase(AlterDatabaseStmt *stmt) + + newtuple = heap_modifytuple(tuple, RelationGetDescr(rel), new_record, + new_record_nulls, new_record_repl); ++ pgaceGramAlterDatabase(rel, newtuple, dpgace_item); + simple_heap_update(rel, &tuple->t_self, newtuple); + + /* Update indexes */ +diff -rpNU3 base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c +--- base/src/backend/commands/functioncmds.c 2009-03-15 17:47:25.000000000 +0900 ++++ sepgsql/src/backend/commands/functioncmds.c 2009-03-15 17:53:20.000000000 +0900 +@@ -47,6 +47,7 @@ + #include "miscadmin.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -412,7 +413,8 @@ compute_attributes_sql_style(List *optio + bool *security_definer, + ArrayType **proconfig, + float4 *procost, +- float4 *prorows) ++ float4 *prorows, ++ DefElem **pgaceItem) + { + ListCell *option; + DefElem *as_item = NULL; +@@ -444,6 +446,14 @@ compute_attributes_sql_style(List *optio + errmsg("conflicting or redundant options"))); + language_item = defel; + } ++ else if (pgaceIsGramSecurityItem(defel)) ++ { ++ if (*pgaceItem) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ *pgaceItem = defel; ++ } + else if (compute_common_attribute(defel, + &volatility_item, + &strict_item, +@@ -624,6 +634,7 @@ CreateFunction(CreateFunctionStmt *stmt) + HeapTuple languageTuple; + Form_pg_language languageStruct; + List *as_clause; ++ DefElem *pgaceItem = NULL; + + /* Convert list of names to a name and namespace */ + namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, +@@ -647,7 +658,7 @@ CreateFunction(CreateFunctionStmt *stmt) + compute_attributes_sql_style(stmt->options, + &as_clause, &language, + &volatility, &isStrict, &security, +- &proconfig, &procost, &prorows); ++ &proconfig, &procost, &prorows, &pgaceItem); + + /* Convert language name to canonical case */ + languageName = case_translate_language_name(language); +@@ -801,7 +812,8 @@ CreateFunction(CreateFunctionStmt *stmt) + PointerGetDatum(parameterNames), + PointerGetDatum(proconfig), + procost, +- prorows); ++ prorows, ++ pgaceItem); + } + + +@@ -1151,6 +1163,7 @@ AlterFunction(AlterFunctionStmt *stmt) + List *set_items = NIL; + DefElem *cost_item = NULL; + DefElem *rows_item = NULL; ++ DefElem *pgaceItem = NULL; + + rel = heap_open(ProcedureRelationId, RowExclusiveLock); + +@@ -1182,6 +1195,15 @@ AlterFunction(AlterFunctionStmt *stmt) + { + DefElem *defel = (DefElem *) lfirst(l); + ++ if (pgaceIsGramSecurityItem(defel)) { ++ if (pgaceItem) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ pgaceItem = defel; ++ continue; ++ } ++ + if (compute_common_attribute(defel, + &volatility_item, + &strict_item, +@@ -1252,6 +1274,7 @@ AlterFunction(AlterFunctionStmt *stmt) + tup = heap_modifytuple(tup, RelationGetDescr(rel), + repl_val, repl_null, repl_repl); + } ++ pgaceGramAlterFunction(rel, tup, pgaceItem); + + /* Do the update */ + simple_heap_update(rel, &tup->t_self, tup); +diff -rpNU3 base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c +--- base/src/backend/commands/lockcmds.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/commands/lockcmds.c 2008-06-14 02:36:58.000000000 +0900 +@@ -18,6 +18,7 @@ + #include "catalog/namespace.h" + #include "commands/lockcmds.h" + #include "miscadmin.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + +@@ -59,6 +60,8 @@ LockTableCommand(LockStmt *lockstmt) + aclcheck_error(aclresult, ACL_KIND_CLASS, + get_rel_name(reloid)); + ++ pgaceLockTable(reloid); ++ + if (lockstmt->nowait) + rel = relation_open_nowait(reloid, lockstmt->mode); + else +diff -rpNU3 base/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c +--- base/src/backend/commands/proclang.c 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/commands/proclang.c 2008-06-14 02:36:58.000000000 +0900 +@@ -146,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt + PointerGetDatum(NULL), + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ NULL); + } + + /* +@@ -179,7 +180,8 @@ CreateProceduralLanguage(CreatePLangStmt + PointerGetDatum(NULL), + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ NULL); + } + } + else +diff -rpNU3 base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c +--- base/src/backend/commands/tablecmds.c 2008-11-05 09:57:00.000000000 +0900 ++++ sepgsql/src/backend/commands/tablecmds.c 2009-01-21 17:26:07.000000000 +0900 +@@ -57,6 +57,7 @@ + #include "parser/parser.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteHandler.h" ++#include "security/pgace.h" + #include "storage/smgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -434,7 +435,8 @@ DefineRelation(CreateStmt *stmt, char re + parentOidCount, + stmt->oncommit, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ pgaceRelationAttrList(stmt)); + + StoreCatalogInheritance(relationId, inheritOids); + +@@ -598,6 +600,8 @@ ExecuteTruncate(TruncateStmt *stmt) + heap_truncate_check_FKs(rels, false); + #endif + ++ pgaceExecTruncate(rels); ++ + /* + * OK, truncate each table. + */ +@@ -2031,6 +2035,7 @@ ATPrepCmd(List **wqueue, Relation rel, A + case AT_DisableRule: + case AT_AddInherit: /* INHERIT / NO INHERIT */ + case AT_DropInherit: ++ case AT_SetSecurityLabel: + ATSimplePermissions(rel, false); + /* These commands never recurse */ + /* No command-specific prep needed */ +@@ -2253,6 +2258,9 @@ ATExecCmd(AlteredTableInfo *tab, Relatio + case AT_DropInherit: + ATExecDropInherit(rel, (RangeVar *) cmd->def); + break; ++ case AT_SetSecurityLabel: ++ pgaceAlterRelationCommon(rel, cmd); ++ break; + default: /* oops */ + elog(ERROR, "unrecognized alter table type: %d", + (int) cmd->subtype); +@@ -2591,11 +2599,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oi + if (newrel) + { + Oid tupOid = InvalidOid; ++ Oid tupSid = InvalidOid; + + /* Extract data from old tuple */ + heap_deform_tuple(tuple, oldTupDesc, values, isnull); + if (oldTupDesc->tdhasoid) + tupOid = HeapTupleGetOid(tuple); ++ if (HeapTupleHasSecLabel(tuple)) ++ tupSid = HeapTupleGetSecLabel(tuple); + + /* Set dropped attributes to null in new tuple */ + foreach(lc, dropped_attrs) +@@ -2627,6 +2638,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oi + /* Preserve OID, if any */ + if (newTupDesc->tdhasoid) + HeapTupleSetOid(tuple, tupOid); ++ /* Preserve Security ID, if any */ ++ if (tupSid != InvalidOid) ++ HeapTupleSetSecLabel(tuple, tupSid); + } + + /* Now check any constraints on the possibly-changed tuple */ +@@ -3113,6 +3127,7 @@ ATExecAddColumn(AlteredTableInfo *tab, R + + attributeTuple = heap_addheader(Natts_pg_attribute, + false, ++ RelationGetDescr(attrdesc)->tdhasseclabel, + ATTRIBUTE_TUPLE_SIZE, + (void *) &attributeD); + +diff -rpNU3 base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c +--- base/src/backend/commands/trigger.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/commands/trigger.c 2009-02-02 11:58:34.000000000 +0900 +@@ -31,6 +31,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "security/pgace.h" + #include "tcop/utility.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -1551,10 +1552,16 @@ ExecCallTriggerFunc(TriggerData *trigdat + * call. + */ + if (finfo->fn_oid == InvalidOid) ++ { + fmgr_info(trigdata->tg_trigger->tgfoid, finfo); ++ pgaceCallFunction(finfo); ++ } + + Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid); + ++ if (!pgaceCallTriggerFunction(trigdata)) ++ return (HeapTuple) NULL; ++ + /* + * If doing EXPLAIN ANALYZE, start charging time to this trigger. + */ +@@ -1574,6 +1581,7 @@ ExecCallTriggerFunc(TriggerData *trigdat + */ + InitFunctionCallInfoData(fcinfo, finfo, 0, (Node *) trigdata, NULL); + ++ + result = FunctionCallInvoke(&fcinfo); + + MemoryContextSwitchTo(oldContext); +@@ -1971,6 +1979,20 @@ ExecBRUpdateTriggers(EState *estate, Res + if (newSlot != NULL) + intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot); + ++ /* ++ * The before-row-triggers are fired prior to transcribing system ++ * attributes from the old tuple to the new one. When no explicit ++ * new values are given, we have to preserve them, so the following ++ * code do it to avoid to make triggers get confusion. ++ */ ++ if (HeapTupleHasOid(newtuple) && ++ !OidIsValid(HeapTupleGetOid(newtuple))) ++ HeapTupleSetOid(newtuple, HeapTupleGetOid(trigtuple)); ++ ++ if (HeapTupleHasSecLabel(newtuple) && ++ !OidIsValid(HeapTupleGetSecLabel(newtuple))) ++ HeapTupleSetSecLabel(newtuple, HeapTupleGetSecLabel(trigtuple)); ++ + LocTriggerData.type = T_TriggerData; + LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | + TRIGGER_EVENT_ROW | +diff -rpNU3 base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/execJunk.c +--- base/src/backend/executor/execJunk.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/execJunk.c 2008-11-21 23:11:55.000000000 +0900 +@@ -60,7 +60,8 @@ + * An optional resultSlot can be passed as well. + */ + JunkFilter * +-ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) ++ExecInitJunkFilter(List *targetList, bool hasoid, bool hassecurity, ++ TupleTableSlot *slot) + { + JunkFilter *junkfilter; + TupleDesc cleanTupType; +@@ -72,7 +73,7 @@ ExecInitJunkFilter(List *targetList, boo + /* + * Compute the tuple descriptor for the cleaned tuple. + */ +- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); ++ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecurity); + + /* + * Use the given slot, or make a new slot if we weren't given one. +diff -rpNU3 base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c +--- base/src/backend/executor/execMain.c 2008-09-25 15:09:40.000000000 +0900 ++++ sepgsql/src/backend/executor/execMain.c 2008-12-28 01:06:59.000000000 +0900 +@@ -49,6 +49,7 @@ + #include "parser/parse_clause.h" + #include "parser/parse_expr.h" + #include "parser/parsetree.h" ++#include "security/pgace.h" + #include "storage/smgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -139,6 +140,8 @@ ExecutorStart(QueryDesc *queryDesc, int + Assert(queryDesc != NULL); + Assert(queryDesc->estate == NULL); + ++ pgaceExecutorStart(queryDesc, eflags); ++ + /* + * If the transaction is read-only, we need to check if any writes are + * planned to non-temporary tables. EXPLAIN is considered read-only. +@@ -738,16 +741,16 @@ InitPlan(QueryDesc *queryDesc, int eflag + for (i = 0; i < as_nplans; i++) + { + PlanState *subplan = appendplans[i]; ++ Relation resultRel = resultRelInfo->ri_RelationDesc; + JunkFilter *j; + + if (operation == CMD_UPDATE) +- ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, +- subplan->plan->targetlist); ++ ExecCheckPlanOutput(resultRel, subplan->plan->targetlist); + + j = ExecInitJunkFilter(subplan->plan->targetlist, +- resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, +- ExecAllocTableSlot(estate->es_tupleTable)); +- ++ RelationGetDescr(resultRel)->tdhasoid, ++ RelationGetDescr(resultRel)->tdhasseclabel, ++ ExecAllocTableSlot(estate->es_tupleTable)); + /* + * Since it must be UPDATE/DELETE, there had better be a + * "ctid" junk attribute in the tlist ... but ctid could +@@ -789,7 +792,7 @@ InitPlan(QueryDesc *queryDesc, int eflag + planstate->plan->targetlist); + + j = ExecInitJunkFilter(planstate->plan->targetlist, +- tupType->tdhasoid, ++ tupType->tdhasoid, tupType->tdhasseclabel, + ExecAllocTableSlot(estate->es_tupleTable)); + estate->es_junkFilter = j; + if (estate->es_result_relation_info) +@@ -848,7 +851,7 @@ InitPlan(QueryDesc *queryDesc, int eflag + * We assume all the sublists will generate the same output tupdesc. + */ + tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), +- false); ++ false, false); + + /* Set up a slot for the output of the RETURNING projection(s) */ + slot = ExecAllocTableSlot(estate->es_tupleTable); +@@ -1171,6 +1174,42 @@ ExecContextForcesOids(PlanState *plansta + return false; + } + ++/* ++ * ExecContextForcesSecLabel ++ * ++ * We need to ensure that result tuples have space for security attribute, ++ * if the security mechanism are going to be stored it into the given ++ * relation. ++ * The hook gives relation identifier and its kind as a hint. However, ++ * the relation identifer can be InvalidOid when the relation is to ++ * be newly created via SELECT INTO, because the creation of the relation ++ * will be done after invocation of the function. ++ */ ++bool ExecContextForcesSecLabel(PlanState *planstate, bool *hasseclabel) ++{ ++ if (planstate->state->es_select_into) ++ { ++ IntoClause *into = planstate->state->es_plannedstmt->intoClause; ++ ++ Assert(into != NULL); ++ ++ *hasseclabel = pgaceTupleDescHasSecLabel(NULL, into->options); ++ return true; ++ } ++ else ++ { ++ ResultRelInfo *ri = planstate->state->es_result_relation_info; ++ ++ if (ri && ri->ri_RelationDesc) ++ { ++ *hasseclabel = pgaceTupleDescHasSecLabel(ri->ri_RelationDesc, NIL); ++ return true; ++ } ++ } ++ ++ return false; ++} ++ + /* ---------------------------------------------------------------- + * ExecEndPlan + * +@@ -1251,6 +1290,61 @@ ExecEndPlan(PlanState *planstate, EState + } + } + ++/* ++ * fetchWritableSystemAttribute() fetches writable system column data ++ * using Junkfilter, and saves them at TupleTableSlot temporary. ++ * ++ * storeWritableSystemAttribute() copies these fetched data into ++ * header structure of HeapTuple. ++ */ ++static void ++fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, ++ Datum *tts_seclabel) ++{ ++ AttrNumber attno; ++ Datum datum; ++ bool isnull; ++ ++ /* for Security Label */ ++ attno = ExecFindJunkAttribute(junkfilter, SecurityLabelAttributeName); ++ if (attno != InvalidAttrNumber) ++ { ++ datum = ExecGetJunkAttribute(slot, attno, &isnull); ++ if (isnull) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("null value in column \"%s\" violates not-null constraint", ++ SecurityLabelAttributeName))); ++ *tts_seclabel = datum; ++ } ++} ++ ++static void ++storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple) ++{ ++ /* for security attribute */ ++ if (HeapTupleHasSecLabel(tuple)) ++ { ++ if (!DatumGetPointer(slot->tts_seclabel)) ++ HeapTupleSetSecLabel(tuple, InvalidOid); ++ else ++ { ++ char *label = TextDatumGetCString(slot->tts_seclabel); ++ ++ HeapTupleSetSecLabel(tuple, ++ pgaceSecurityLabelToSid(label)); ++ } ++ } ++ else if (DatumGetPointer(slot->tts_seclabel)) ++ { ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("enhanced security mechanism does not allocate " ++ "a field to store security attribute for relation: %s", ++ RelationGetRelationName(rel)))); ++ } ++} ++ + /* ---------------------------------------------------------------- + * ExecutePlan + * +@@ -1318,6 +1412,8 @@ ExecutePlan(EState *estate, + + for (;;) + { ++ Datum tts_seclabel = PointerGetDatum(NULL); ++ + /* Reset the per-output-tuple exprcontext */ + ResetPerTupleExprContext(estate); + +@@ -1442,6 +1538,11 @@ lnext: ; + } + + /* ++ * extract writable system attribute ++ */ ++ fetchWritableSystemAttribute(junkfilter, slot, &tts_seclabel); ++ ++ /* + * extract the 'ctid' junk attribute. + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE) +@@ -1468,6 +1569,7 @@ lnext: ; + if (operation != CMD_DELETE) + slot = ExecFilterJunk(junkfilter, slot); + } ++ slot->tts_seclabel = tts_seclabel; + + /* + * now that we have a tuple, do the appropriate thing with it.. either +@@ -1588,6 +1690,8 @@ ExecInsert(TupleTableSlot *slot, + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); ++ + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) +@@ -1624,6 +1728,13 @@ ExecInsert(TupleTableSlot *slot, + ExecConstraints(resultRelInfo, slot, estate); + + /* ++ * Mandatory access controls of the tuple ++ */ ++ if (!pgaceHeapTupleInsert(resultRelationDesc, tuple, ++ false, !!resultRelInfo->ri_projectReturning)) ++ return; ++ ++ /* + * insert the tuple + * + * Note: heap_insert returns the tid (location) of the new tuple in the +@@ -1690,6 +1801,10 @@ ExecDelete(ItemPointer tupleid, + return; + } + ++ if (!pgaceHeapTupleDelete(resultRelationDesc, tupleid, ++ false, !!resultRelInfo->ri_projectReturning)) ++ return; ++ + /* + * delete the tuple + * +@@ -1826,6 +1941,8 @@ ExecUpdate(TupleTableSlot *slot, + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); ++ + /* BEFORE ROW UPDATE Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) +@@ -1870,6 +1987,13 @@ lreplace:; + ExecConstraints(resultRelInfo, slot, estate); + + /* ++ * Mandatory access controls of the tuple ++ */ ++ if (!pgaceHeapTupleUpdate(resultRelationDesc, tupleid, tuple, ++ false, !!resultRelInfo->ri_projectReturning)) ++ return; ++ ++ /* + * replace the heap tuple + * + * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that +@@ -2733,7 +2857,8 @@ OpenIntoRel(QueryDesc *queryDesc) + 0, + into->onCommit, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ NIL); + + FreeTupleDesc(tupdesc); + +@@ -2839,6 +2964,12 @@ intorel_receive(TupleTableSlot *slot, De + + tuple = ExecCopySlotTuple(slot); + ++ storeWritableSystemAttribute(estate->es_into_relation_descriptor, slot, tuple); ++ if (!pgaceHeapTupleInsert(estate->es_into_relation_descriptor, tuple, false, false)) { ++ heap_freetuple(tuple); ++ return; ++ } ++ + heap_insert(estate->es_into_relation_descriptor, + tuple, + estate->es_output_cid, +diff -rpNU3 base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c +--- base/src/backend/executor/execQual.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/executor/execQual.c 2009-02-02 11:58:34.000000000 +0900 +@@ -47,6 +47,7 @@ + #include "nodes/makefuncs.h" + #include "optimizer/planmain.h" + #include "parser/parse_expr.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1010,6 +1011,7 @@ init_fcache(Oid foid, FuncExprState *fca + + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); ++ pgaceCallFunction(&fcache->func); + + /* Initialize additional info */ + fcache->setArgsValid = false; +@@ -3679,6 +3681,8 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprS + + /* Initialize additional info */ + astate->elemfunc.fn_expr = (Node *) acoerce; ++ ++ pgaceCallFunction(&astate->elemfunc); + } + + /* +diff -rpNU3 base/src/backend/executor/execScan.c sepgsql/src/backend/executor/execScan.c +--- base/src/backend/executor/execScan.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/execScan.c 2009-02-25 22:31:25.000000000 +0900 +@@ -20,6 +20,7 @@ + + #include "executor/executor.h" + #include "miscadmin.h" ++#include "security/pgace.h" + #include "utils/memutils.h" + + +@@ -53,6 +54,7 @@ ExecScan(ScanState *node, + ProjectionInfo *projInfo; + ExprDoneCond isDone; + TupleTableSlot *resultSlot; ++ Scan *scan = (Scan *)node->ps.plan; + + /* + * Fetch data from node +@@ -64,7 +66,7 @@ ExecScan(ScanState *node, + * If we have neither a qual to check nor a projection to do, just skip + * all the overhead and return the raw scan tuple. + */ +- if (!qual && !projInfo) ++ if (!qual && !projInfo && !scan->pgaceTuplePerms) + return (*accessMtd) (node); + + /* +@@ -127,9 +129,14 @@ ExecScan(ScanState *node, + * check for non-nil qual here to avoid a function call to ExecQual() + * when the qual is nil ... saves only a few cycles, but they add up + * ... ++ * And security check for tuple level access controls at the last. + */ +- if (!qual || ExecQual(qual, econtext, false)) ++ if (pgaceExecScan(scan, node->ss_currentRelation, slot, false) ++ && (!qual || ExecQual(qual, econtext, false))) + { ++ /* special care for FK checks */ ++ pgaceExecScan(scan, node->ss_currentRelation, slot, true); ++ + /* + * Found a satisfactory scan tuple. + */ +@@ -197,6 +204,7 @@ tlist_matches_tupdesc(PlanState *ps, Lis + int numattrs = tupdesc->natts; + int attrno; + bool hasoid; ++ bool hasseclabel; + ListCell *tlist_item = list_head(tlist); + + /* Check the tlist attributes */ +@@ -240,12 +248,16 @@ tlist_matches_tupdesc(PlanState *ps, Lis + return false; /* tlist too long */ + + /* +- * If the plan context requires a particular hasoid setting, then that has +- * to match, too. ++ * If the plan context requires a particular hasoid/hassecurity setting, ++ * then they have to match, too. + */ + if (ExecContextForcesOids(ps, &hasoid) && + hasoid != tupdesc->tdhasoid) + return false; + ++ if (ExecContextForcesSecLabel(ps, &hasseclabel) && ++ hasseclabel != tupdesc->tdhasseclabel) ++ return false; ++ + return true; + } +diff -rpNU3 base/src/backend/executor/execTuples.c sepgsql/src/backend/executor/execTuples.c +--- base/src/backend/executor/execTuples.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/execTuples.c 2008-12-28 01:06:59.000000000 +0900 +@@ -100,7 +100,7 @@ + + + static TupleDesc ExecTypeFromTLInternal(List *targetList, +- bool hasoid, bool skipjunk); ++ bool hasoid, bool hassecurity, bool skipjunk); + + + /* ---------------------------------------------------------------- +@@ -921,9 +921,9 @@ ExecInitNullTupleSlot(EState *estate, Tu + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecTypeFromTL(List *targetList, bool hasoid) ++ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel) + { +- return ExecTypeFromTLInternal(targetList, hasoid, false); ++ return ExecTypeFromTLInternal(targetList, hasoid, hasseclabel, false); + } + + /* ---------------------------------------------------------------- +@@ -933,13 +933,14 @@ ExecTypeFromTL(List *targetList, bool ha + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecCleanTypeFromTL(List *targetList, bool hasoid) ++ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel) + { +- return ExecTypeFromTLInternal(targetList, hasoid, true); ++ return ExecTypeFromTLInternal(targetList, hasoid, hasseclabel, true); + } + + static TupleDesc +-ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) ++ExecTypeFromTLInternal(List *targetList, ++ bool hasoid, bool hasseclabel, bool skipjunk) + { + TupleDesc typeInfo; + ListCell *l; +@@ -951,6 +952,7 @@ ExecTypeFromTLInternal(List *targetList, + else + len = ExecTargetListLength(targetList); + typeInfo = CreateTemplateTupleDesc(len, hasoid); ++ typeInfo->tdhasseclabel = hasseclabel; + + foreach(l, targetList) + { +diff -rpNU3 base/src/backend/executor/execUtils.c sepgsql/src/backend/executor/execUtils.c +--- base/src/backend/executor/execUtils.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/execUtils.c 2008-12-28 01:06:59.000000000 +0900 +@@ -506,6 +506,7 @@ void + ExecAssignResultTypeFromTL(PlanState *planstate) + { + bool hasoid; ++ bool hassecurity; + TupleDesc tupDesc; + + if (ExecContextForcesOids(planstate, &hasoid)) +@@ -518,12 +519,15 @@ ExecAssignResultTypeFromTL(PlanState *pl + hasoid = false; + } + ++ if (!ExecContextForcesSecLabel(planstate, &hassecurity)) ++ hassecurity = false; ++ + /* + * ExecTypeFromTL needs the parse-time representation of the tlist, not a + * list of ExprStates. This is good because some plan nodes don't bother + * to set up planstate->targetlist ... + */ +- tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); ++ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecurity); + ExecAssignResultType(planstate, tupDesc); + } + +diff -rpNU3 base/src/backend/executor/functions.c sepgsql/src/backend/executor/functions.c +--- base/src/backend/executor/functions.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/functions.c 2008-11-21 23:11:55.000000000 +0900 +@@ -995,7 +995,7 @@ check_sql_fn_retval(Oid func_id, Oid ret + * what the caller expects will happen at runtime. + */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return true; + } + Assert(tupdesc); +diff -rpNU3 base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/nodeAgg.c +--- base/src/backend/executor/nodeAgg.c 2008-11-05 09:57:00.000000000 +0900 ++++ sepgsql/src/backend/executor/nodeAgg.c 2009-01-16 17:07:29.000000000 +0900 +@@ -80,6 +80,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_expr.h" + #include "parser/parse_oper.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1398,6 +1399,8 @@ ExecInitAgg(Agg *node, EState *estate, i + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(aggref->aggfnoid)); + ++ pgaceCallAggFunction(aggTuple); ++ + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; + peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; + +@@ -1461,11 +1464,13 @@ ExecInitAgg(Agg *node, EState *estate, i + + fmgr_info(transfn_oid, &peraggstate->transfn); + peraggstate->transfn.fn_expr = (Node *) transfnexpr; ++ pgaceCallFunction(&peraggstate->transfn); + + if (OidIsValid(finalfn_oid)) + { + fmgr_info(finalfn_oid, &peraggstate->finalfn); + peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; ++ pgaceCallFunction(&peraggstate->finalfn); + } + + get_typlenbyval(aggref->aggtype, +diff -rpNU3 base/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executor/nodeMergejoin.c +--- base/src/backend/executor/nodeMergejoin.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/nodeMergejoin.c 2009-01-16 17:07:29.000000000 +0900 +@@ -98,6 +98,7 @@ + #include "executor/execdefs.h" + #include "executor/nodeMergejoin.h" + #include "miscadmin.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" +@@ -221,6 +222,7 @@ MJExamineQuals(List *mergeclauses, + + /* Set up the fmgr lookup information */ + fmgr_info(cmpproc, &(clause->cmpfinfo)); ++ pgaceCallFunction(&clause->cmpfinfo); + + /* Fill the additional comparison-strategy flags */ + if (opstrategy == BTLessStrategyNumber) +diff -rpNU3 base/src/backend/executor/nodeSubplan.c sepgsql/src/backend/executor/nodeSubplan.c +--- base/src/backend/executor/nodeSubplan.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/nodeSubplan.c 2008-11-21 23:11:55.000000000 +0900 +@@ -855,7 +855,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanSt + * (hack alert!). The righthand expressions will be evaluated in our + * own innerecontext. + */ +- tupDesc = ExecTypeFromTL(leftptlist, false); ++ tupDesc = ExecTypeFromTL(leftptlist, false, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projLeft = ExecBuildProjectionInfo(lefttlist, +@@ -863,7 +863,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanSt + slot, + NULL); + +- tupDesc = ExecTypeFromTL(rightptlist, false); ++ tupDesc = ExecTypeFromTL(rightptlist, false, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projRight = ExecBuildProjectionInfo(righttlist, +diff -rpNU3 base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c +--- base/src/backend/executor/spi.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/executor/spi.c 2009-02-02 11:58:34.000000000 +0900 +@@ -631,6 +631,8 @@ SPI_modifytuple(Relation rel, HeapTuple + mtuple->t_tableOid = tuple->t_tableOid; + if (rel->rd_att->tdhasoid) + HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecLabel(tuple)) ++ HeapTupleSetSecLabel(mtuple, HeapTupleGetSecLabel(tuple)); + } + else + { +diff -rpNU3 base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c +--- base/src/backend/libpq/be-fsstubs.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/libpq/be-fsstubs.c 2008-06-14 02:36:58.000000000 +0900 +@@ -45,6 +45,7 @@ + #include "libpq/be-fsstubs.h" + #include "libpq/libpq-fs.h" + #include "miscadmin.h" ++#include "security/pgace.h" + #include "storage/fd.h" + #include "storage/large_object.h" + #include "utils/memutils.h" +@@ -154,6 +155,8 @@ lo_read(int fd, char *buf, int len) + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("invalid large-object descriptor: %d", fd))); + ++ pgaceLargeObjectRead(cookies[fd], len); ++ + status = inv_read(cookies[fd], buf, len); + + return status; +@@ -175,6 +178,8 @@ lo_write(int fd, const char *buf, int le + errmsg("large object descriptor %d was not opened for writing", + fd))); + ++ pgaceLargeObjectWrite(cookies[fd], len); ++ + status = inv_write(cookies[fd], buf, len); + + return status; +@@ -359,6 +364,11 @@ lo_import(PG_FUNCTION_ARGS) + lobjOid = inv_create(InvalidOid); + + /* ++ * check permission to import a file into this object ++ */ ++ pgaceLargeObjectImport(lobjOid, FileRawDescriptor(fd), fnamebuf); ++ ++ /* + * read in from the filesystem and write to the inversion object + */ + lobj = inv_open(lobjOid, INV_WRITE, fscxt); +@@ -433,6 +443,10 @@ lo_export(PG_FUNCTION_ARGS) + (errcode_for_file_access(), + errmsg("could not create server file \"%s\": %m", + fnamebuf))); ++ /* ++ * check permission to export this object into a file ++ */ ++ pgaceLargeObjectExport(lobjId, FileRawDescriptor(fd), fnamebuf); + + /* + * read in from the inversion file and write to the filesystem +@@ -468,6 +482,8 @@ lo_truncate(PG_FUNCTION_ARGS) + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("invalid large-object descriptor: %d", fd))); + ++ pgaceLargeObjectTruncate(cookies[fd], len); ++ + inv_truncate(cookies[fd], len); + + PG_RETURN_INT32(0); +diff -rpNU3 base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c +--- base/src/backend/nodes/copyfuncs.c 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/nodes/copyfuncs.c 2009-02-17 13:32:34.000000000 +0900 +@@ -24,6 +24,7 @@ + + #include "nodes/plannodes.h" + #include "nodes/relation.h" ++#include "nodes/security.h" + #include "utils/datum.h" + + +@@ -85,6 +86,7 @@ _copyPlannedStmt(PlannedStmt *from) + COPY_NODE_FIELD(rowMarks); + COPY_NODE_FIELD(relationOids); + COPY_SCALAR_FIELD(nParamExec); ++ COPY_NODE_FIELD(pgaceItem); + + return newnode; + } +@@ -226,6 +228,7 @@ CopyScanFields(Scan *from, Scan *newnode + CopyPlanFields((Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(scanrelid); ++ COPY_SCALAR_FIELD(pgaceTuplePerms); + } + + /* +@@ -1523,6 +1526,7 @@ _copyRangeTblEntry(RangeTblEntry *from) + COPY_SCALAR_FIELD(inFromCl); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); ++ COPY_SCALAR_FIELD(pgaceTuplePerms); + + return newnode; + } +@@ -1789,6 +1793,7 @@ _copyColumnDef(ColumnDef *from) + COPY_NODE_FIELD(raw_default); + COPY_STRING_FIELD(cooked_default); + COPY_NODE_FIELD(constraints); ++ COPY_NODE_FIELD(pgaceItem); + + return newnode; + } +@@ -1869,6 +1874,7 @@ _copyQuery(Query *from) + COPY_NODE_FIELD(limitCount); + COPY_NODE_FIELD(rowMarks); + COPY_NODE_FIELD(setOperations); ++ COPY_NODE_FIELD(pgaceItem); + + return newnode; + } +@@ -2105,6 +2111,7 @@ _copyCreateStmt(CreateStmt *from) + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(oncommit); + COPY_STRING_FIELD(tablespacename); ++ COPY_NODE_FIELD(pgaceItem); + + return newnode; + } +@@ -2998,6 +3005,25 @@ _copyValue(Value *from) + return newnode; + } + ++/* **************************************************************** ++ * nodes/security.h copy functions ++ * **************************************************************** ++ */ ++static SelinuxEvalItem * ++_copySelinuxEvalItem(SelinuxEvalItem *from) ++{ ++ SelinuxEvalItem *newnode = makeNode(SelinuxEvalItem); ++ ++ COPY_SCALAR_FIELD(relid); ++ COPY_SCALAR_FIELD(inh); ++ ++ COPY_SCALAR_FIELD(relperms); ++ COPY_SCALAR_FIELD(nattrs); ++ COPY_POINTER_FIELD(attperms, from->nattrs * sizeof(uint32)); ++ ++ return newnode; ++} ++ + /* + * copyObject + * +@@ -3600,6 +3626,9 @@ copyObject(void *from) + case T_XmlSerialize: + retval = _copyXmlSerialize(from); + break; ++ case T_SelinuxEvalItem: ++ retval = _copySelinuxEvalItem(from); ++ break; + + default: + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from)); +diff -rpNU3 base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c +--- base/src/backend/nodes/equalfuncs.c 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/nodes/equalfuncs.c 2009-01-21 17:02:57.000000000 +0900 +@@ -26,6 +26,7 @@ + #include "postgres.h" + + #include "nodes/relation.h" ++#include "nodes/security.h" + #include "utils/datum.h" + + +@@ -768,6 +769,7 @@ _equalQuery(Query *a, Query *b) + COMPARE_NODE_FIELD(limitCount); + COMPARE_NODE_FIELD(rowMarks); + COMPARE_NODE_FIELD(setOperations); ++ COMPARE_NODE_FIELD(pgaceItem); + + return true; + } +@@ -970,6 +972,7 @@ _equalCreateStmt(CreateStmt *a, CreateSt + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(oncommit); + COMPARE_STRING_FIELD(tablespacename); ++ COMPARE_NODE_FIELD(pgaceItem); + + return true; + } +@@ -1818,6 +1821,7 @@ _equalColumnDef(ColumnDef *a, ColumnDef + COMPARE_NODE_FIELD(raw_default); + COMPARE_STRING_FIELD(cooked_default); + COMPARE_NODE_FIELD(constraints); ++ COMPARE_NODE_FIELD(pgaceItem); + + return true; + } +@@ -1925,6 +1929,21 @@ _equalXmlSerialize(XmlSerialize *a, XmlS + } + + /* ++ * Stuff from nodes/security.h ++ */ ++static bool ++_equalSelinuxEvalItem(SelinuxEvalItem *a, SelinuxEvalItem *b) ++{ ++ COMPARE_SCALAR_FIELD(relid); ++ COMPARE_SCALAR_FIELD(inh); ++ COMPARE_SCALAR_FIELD(relperms); ++ COMPARE_SCALAR_FIELD(nattrs); ++ COMPARE_POINTER_FIELD(attperms, a->nattrs * sizeof(uint32)); ++ ++ return true; ++} ++ ++/* + * Stuff from pg_list.h + */ + +@@ -2527,6 +2546,9 @@ equal(void *a, void *b) + case T_XmlSerialize: + retval = _equalXmlSerialize(a, b); + break; ++ case T_SelinuxEvalItem: ++ retval = _equalSelinuxEvalItem(a, b); ++ break; + + default: + elog(ERROR, "unrecognized node type: %d", +diff -rpNU3 base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c +--- base/src/backend/nodes/outfuncs.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/nodes/outfuncs.c 2009-02-02 11:58:34.000000000 +0900 +@@ -26,6 +26,7 @@ + #include "lib/stringinfo.h" + #include "nodes/plannodes.h" + #include "nodes/relation.h" ++#include "nodes/security.h" + #include "utils/datum.h" + + +@@ -252,6 +253,7 @@ _outPlannedStmt(StringInfo str, PlannedS + WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(relationOids); + WRITE_INT_FIELD(nParamExec); ++ WRITE_NODE_FIELD(pgaceItem); + } + + /* +@@ -282,6 +284,7 @@ _outScanInfo(StringInfo str, Scan *node) + _outPlanInfo(str, (Plan *) node); + + WRITE_UINT_FIELD(scanrelid); ++ WRITE_UINT_FIELD(pgaceTuplePerms); + } + + /* +@@ -1376,6 +1379,7 @@ _outRelOptInfo(StringInfo str, RelOptInf + WRITE_BOOL_FIELD(has_eclass_joins); + WRITE_BITMAPSET_FIELD(index_outer_relids); + WRITE_NODE_FIELD(index_inner_paths); ++ WRITE_UINT_FIELD(pgaceTuplePerms); + } + + static void +@@ -1545,6 +1549,7 @@ _outCreateStmt(StringInfo str, CreateStm + WRITE_NODE_FIELD(options); + WRITE_ENUM_FIELD(oncommit, OnCommitAction); + WRITE_STRING_FIELD(tablespacename); ++ WRITE_NODE_FIELD(pgaceItem); + } + + static void +@@ -1660,6 +1665,7 @@ _outColumnDef(StringInfo str, ColumnDef + WRITE_NODE_FIELD(raw_default); + WRITE_STRING_FIELD(cooked_default); + WRITE_NODE_FIELD(constraints); ++ WRITE_NODE_FIELD(pgaceItem); + } + + static void +@@ -1749,6 +1755,7 @@ _outQuery(StringInfo str, Query *node) + WRITE_NODE_FIELD(limitCount); + WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(setOperations); ++ WRITE_NODE_FIELD(pgaceItem); + } + + static void +@@ -1834,6 +1841,7 @@ _outRangeTblEntry(StringInfo str, RangeT + WRITE_BOOL_FIELD(inFromCl); + WRITE_UINT_FIELD(requiredPerms); + WRITE_OID_FIELD(checkAsUser); ++ WRITE_UINT_FIELD(pgaceTuplePerms); + } + + static void +@@ -2046,6 +2054,29 @@ _outFkConstraint(StringInfo str, FkConst + WRITE_BOOL_FIELD(skip_validation); + } + ++/***************************************************************************** ++ * ++ * Stuff from nodes/security.h ++ * ++ *****************************************************************************/ ++static void ++_outSelinuxEvalItem(StringInfo str, SelinuxEvalItem *node) ++{ ++ int i; ++ ++ WRITE_NODE_TYPE("SELINUXEVALITEM"); ++ ++ WRITE_OID_FIELD(relid); ++ WRITE_BOOL_FIELD(inh); ++ ++ WRITE_UINT_FIELD(relperms); ++ WRITE_UINT_FIELD(nattrs); ++ ++ appendStringInfo(str, " :attperms ["); ++ for (i = 0; i < node->nattrs; i++) ++ appendStringInfo(str, " %u", node->attperms[i]); ++ appendStringInfo(str, " ]"); ++} + + /* + * _outNode - +@@ -2439,6 +2470,9 @@ _outNode(StringInfo str, void *obj) + case T_XmlSerialize: + _outXmlSerialize(str, obj); + break; ++ case T_SelinuxEvalItem: ++ _outSelinuxEvalItem(str, obj); ++ break; + + default: + +diff -rpNU3 base/src/backend/nodes/readfuncs.c sepgsql/src/backend/nodes/readfuncs.c +--- base/src/backend/nodes/readfuncs.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/nodes/readfuncs.c 2009-01-21 17:02:57.000000000 +0900 +@@ -24,6 +24,7 @@ + + #include "nodes/parsenodes.h" + #include "nodes/readfuncs.h" ++#include "nodes/security.h" + + + /* +@@ -154,6 +155,7 @@ _readQuery(void) + READ_NODE_FIELD(limitCount); + READ_NODE_FIELD(rowMarks); + READ_NODE_FIELD(setOperations); ++ READ_NODE_FIELD(pgaceItem); + + READ_DONE(); + } +@@ -1003,10 +1005,49 @@ _readRangeTblEntry(void) + READ_BOOL_FIELD(inFromCl); + READ_UINT_FIELD(requiredPerms); + READ_OID_FIELD(checkAsUser); ++ READ_UINT_FIELD(pgaceTuplePerms); + + READ_DONE(); + } + ++/* ++ * Stuff from nodes/security.h ++ */ ++static SelinuxEvalItem * ++_readSelinuxEvalItem(void) ++{ ++ int i; ++ ++ READ_LOCALS(SelinuxEvalItem); ++ ++ READ_OID_FIELD(relid); ++ READ_BOOL_FIELD(inh); ++ ++ READ_UINT_FIELD(relperms); ++ READ_UINT_FIELD(nattrs); ++ ++ /* ++ * TODO: This part should be moved to readArray() ? ++ */ ++ local_node->attperms = palloc0(local_node->nattrs * sizeof(uint32)); ++ ++ token = pg_strtok(&length); /* skip :attperms */ ++ token = pg_strtok(&length); /* read '[' */ ++ if (token == NULL || strcmp(token, "[") != 0) ++ elog(ERROR, "expected \"[\" to start array, but got \"%s\"", ++ token ? (const char *) token : "[NULL]"); ++ for (i = 0; i < local_node->nattrs; i++) ++ { ++ token = pg_strtok(&length); ++ local_node->attperms[i] = atoui(token); ++ } ++ token = pg_strtok(&length); /* read ']' */ ++ if (token == NULL || strcmp(token, "[") != 0) ++ elog(ERROR, "expected \"[\" to end array, but got \"%s\"", ++ token ? (const char *) token : "[NULL]"); ++ ++ READ_DONE(); ++} + + /* + * parseNodeString +@@ -1124,6 +1165,8 @@ parseNodeString(void) + return_value = _readNotifyStmt(); + else if (MATCH("DECLARECURSOR", 13)) + return_value = _readDeclareCursorStmt(); ++ else if (MATCH("SELINUXEVALITEM", 15)) ++ return_value = _readSelinuxEvalItem(); + else + { + elog(ERROR, "badly formatted node string \"%.32s\"...", token); +diff -rpNU3 base/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/optimizer/plan/createplan.c +--- base/src/backend/optimizer/plan/createplan.c 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/optimizer/plan/createplan.c 2008-06-14 02:36:58.000000000 +0900 +@@ -287,6 +287,12 @@ create_scan_plan(PlannerInfo *root, Path + } + + /* ++ * The guest of PGACE can refer plan->pgaceTuplePerms to apply ++ * tuple level access control in the pgaceExecScan() hook. ++ */ ++ ((Scan *)plan)->pgaceTuplePerms = rel->pgaceTuplePerms; ++ ++ /* + * If there are any pseudoconstant clauses attached to this node, insert a + * gating Result node that evaluates the pseudoconstants as one-time + * quals. +diff -rpNU3 base/src/backend/optimizer/plan/planner.c sepgsql/src/backend/optimizer/plan/planner.c +--- base/src/backend/optimizer/plan/planner.c 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/optimizer/plan/planner.c 2008-11-24 20:26:40.000000000 +0900 +@@ -197,6 +197,7 @@ standard_planner(Query *parse, int curso + result->rowMarks = parse->rowMarks; + result->relationOids = glob->relationOids; + result->nParamExec = list_length(glob->paramlist); ++ result->pgaceItem = parse->pgaceItem; + + return result; + } +diff -rpNU3 base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimizer/util/clauses.c +--- base/src/backend/optimizer/util/clauses.c 2008-09-25 15:09:40.000000000 +0900 ++++ sepgsql/src/backend/optimizer/util/clauses.c 2008-12-28 01:06:59.000000000 +0900 +@@ -39,6 +39,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_expr.h" + #include "tcop/tcopprot.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/datum.h" +@@ -3057,6 +3058,9 @@ inline_function(Oid funcid, Oid result_t + if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) + return NULL; + ++ if (!pgaceAllowFunctionInlined(funcid, func_tuple)) ++ return NULL; ++ + /* + * Setup error traceback support for ereport(). This is so that we can + * finger the function that bad information came from. +diff -rpNU3 base/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimizer/util/relnode.c +--- base/src/backend/optimizer/util/relnode.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/optimizer/util/relnode.c 2008-06-14 02:36:58.000000000 +0900 +@@ -90,6 +90,7 @@ build_simple_rel(PlannerInfo *root, int + rel->has_eclass_joins = false; + rel->index_outer_relids = NULL; + rel->index_inner_paths = NIL; ++ rel->pgaceTuplePerms = rte->pgaceTuplePerms; + + /* Check type of rtable entry */ + switch (rte->rtekind) +diff -rpNU3 base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c +--- base/src/backend/parser/analyze.c 2009-03-15 17:47:25.000000000 +0900 ++++ sepgsql/src/backend/parser/analyze.c 2009-03-15 17:53:20.000000000 +0900 +@@ -24,6 +24,7 @@ + + #include "postgres.h" + ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "nodes/makefuncs.h" + #include "optimizer/clauses.h" +@@ -36,6 +37,7 @@ + #include "parser/parse_relation.h" + #include "parser/parse_target.h" + #include "parser/parsetree.h" ++#include "security/pgace.h" + + + typedef struct +@@ -616,14 +618,15 @@ transformInsertStmt(ParseState *pstate, + Expr *expr = (Expr *) lfirst(lc); + ResTarget *col; + TargetEntry *tle; ++ AttrNumber anum = (AttrNumber) lfirst_int(attnos); + + col = (ResTarget *) lfirst(icols); + Assert(IsA(col, ResTarget)); + + tle = makeTargetEntry(expr, +- (AttrNumber) lfirst_int(attnos), ++ anum, + col->name, +- false); ++ anum < 0 ? true : false); + qry->targetList = lappend(qry->targetList, tle); + + icols = lnext(icols); +@@ -721,6 +724,46 @@ transformInsertRow(ParseState *pstate, L + return result; + } + ++static void ++transformSelectIntoSystemColumn(ParseState *pstate, Query *qry) ++{ ++ ListCell *l; ++ uint32 system_attrs = 0; ++ bool relhasoids ++ = interpretOidsOption(qry->intoClause->options); ++ ++ foreach (l, qry->targetList) { ++ Form_pg_attribute attr; ++ TargetEntry *tle = lfirst(l); ++ ++ if (tle->resjunk) ++ continue; ++ ++ attr = SystemAttributeByName(tle->resname, relhasoids); ++ if (attr && SystemAttributeIsWritable(attr->attnum)) ++ { ++ uint32 mask = (1<<(-attr->attnum)); ++ ++ /* duplication checks */ ++ if (system_attrs & mask) ++ continue; ++ system_attrs |= mask; ++ ++ if (exprType((Node *) tle->expr) != attr->atttypid) ++ { ++ tle->expr = ++ (Expr *) coerce_to_target_type(pstate, ++ (Node *) tle->expr, ++ exprType((Node *) tle->expr), ++ attr->atttypid, ++ attr->atttypmod, ++ COERCION_IMPLICIT, ++ COERCE_IMPLICIT_CAST); ++ } ++ tle->resjunk = true; ++ } ++ } ++} + + /* + * transformSelectStmt - +@@ -787,6 +830,7 @@ transformSelectStmt(ParseState *pstate, + if (stmt->intoClause) + { + qry->intoClause = stmt->intoClause; ++ transformSelectIntoSystemColumn(pstate, qry); + if (stmt->intoClause->colNames) + applyColumnNames(qry->targetList, stmt->intoClause->colNames); + } +diff -rpNU3 base/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y +--- base/src/backend/parser/gram.y 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/parser/gram.y 2008-12-28 01:06:59.000000000 +0900 +@@ -56,6 +56,7 @@ + #include "commands/defrem.h" + #include "nodes/makefuncs.h" + #include "parser/gramparse.h" ++#include "security/pgace.h" + #include "storage/lmgr.h" + #include "utils/date.h" + #include "utils/datetime.h" +@@ -351,6 +352,8 @@ static Node *makeXmlExpr(XmlExprOp op, c + %type OptTableSpace OptConsTableSpace OptTableSpaceOwner + %type opt_check_option + ++%type OptSecurityItem SecurityItem ++ + %type xml_attribute_el + %type xml_attribute_list xml_attributes + %type xml_root_version opt_xml_root_standalone +@@ -1637,6 +1640,24 @@ alter_table_cmd: + n->def = (Node *) $3; + $$ = (Node *)n; + } ++ /* ALTER TABLE CONTEXT = '...' */ ++ | SecurityItem ++ { ++ AlterTableCmd *n = makeNode(AlterTableCmd); ++ n->subtype = AT_SetSecurityLabel; ++ n->name = NULL; ++ n->def = (Node *) $1; ++ $$ = (Node *) n; ++ } ++ /* ALTER TABLE ALTER [COLUMN] CONTEXT = '...' */ ++ | ALTER opt_column ColId SecurityItem ++ { ++ AlterTableCmd *n = makeNode(AlterTableCmd); ++ n->subtype = AT_SetSecurityLabel; ++ n->name = $3; ++ n->def = (Node *) $4; ++ $$ = (Node *) n; ++ } + | alter_rel_cmd + { + $$ = $1; +@@ -1883,7 +1904,7 @@ opt_using: + *****************************************************************************/ + + CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' +- OptInherit OptWith OnCommitOption OptTableSpace ++ OptInherit OptWith OnCommitOption OptTableSpace OptSecurityItem + { + CreateStmt *n = makeNode(CreateStmt); + $4->istemp = $2; +@@ -1894,10 +1915,11 @@ CreateStmt: CREATE OptTemp TABLE qualifi + n->options = $9; + n->oncommit = $10; + n->tablespacename = $11; ++ n->pgaceItem = (Node *) $12; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE qualified_name OF qualified_name +- '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace ++ '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecurityItem + { + /* SQL99 CREATE TABLE OF (cols) seems to be satisfied + * by our inheritance capabilities. Let's try it... +@@ -1911,6 +1933,7 @@ CreateStmt: CREATE OptTemp TABLE qualifi + n->options = $10; + n->oncommit = $11; + n->tablespacename = $12; ++ n->pgaceItem = (Node *) $13; + $$ = (Node *)n; + } + ; +@@ -1953,13 +1976,14 @@ TableElement: + | TableConstraint { $$ = $1; } + ; + +-columnDef: ColId Typename ColQualList ++columnDef: ColId Typename ColQualList OptSecurityItem + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typename = $2; + n->constraints = $3; + n->is_local = true; ++ n->pgaceItem = (Node *) $4; + $$ = (Node *)n; + } + ; +@@ -4278,6 +4302,10 @@ common_func_opt_item: + /* we abuse the normal content of a DefElem here */ + $$ = makeDefElem("set", (Node *)$1); + } ++ | SecurityItem ++ { ++ $$ = $1; ++ } + ; + + createfunc_opt_item: +@@ -5361,6 +5389,10 @@ createdb_opt_item: + { + $$ = makeDefElem("owner", NULL); + } ++ | SecurityItem ++ { ++ $$ = $1; ++ } + ; + + /* +@@ -5409,6 +5441,10 @@ alterdb_opt_item: + { + $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4)); + } ++ | SecurityItem ++ { ++ $$ = $1; ++ } + ; + + +@@ -8736,6 +8772,28 @@ target_el: a_expr AS ColLabel + } + ; + ++/***************************************************************************** ++ * ++ * PGACE Security Items ++ * ++ *****************************************************************************/ ++ ++OptSecurityItem: ++ SecurityItem { $$ = $1; } ++ | /* EMPTY */ { $$ = NULL; } ++ ; ++ ++SecurityItem: ++ IDENT '=' Sconst ++ { ++ DefElem *node = makeDefElem($1, (Node *) makeString($3)); ++ ++ if (!pgaceIsGramSecurityItem(node)) ++ yyerror("syntax error"); ++ ++ $$ = node; ++ } ++ ; + + /***************************************************************************** + * +diff -rpNU3 base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/parse_target.c +--- base/src/backend/parser/parse_target.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/parser/parse_target.c 2008-12-05 17:58:33.000000000 +0900 +@@ -14,6 +14,7 @@ + */ + #include "postgres.h" + ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "commands/dbcommands.h" + #include "funcapi.h" +@@ -26,6 +27,7 @@ + #include "parser/parse_relation.h" + #include "parser/parse_target.h" + #include "parser/parse_type.h" ++#include "security/pgace.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" + #include "utils/typcache.h" +@@ -333,16 +335,33 @@ transformAssignedExpr(ParseState *pstate + Oid attrtype; /* type of target column */ + int32 attrtypmod; + Relation rd = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rd)->relhasoids; + + Assert(rd != NULL); +- if (attrno <= 0) +- ereport(ERROR, +- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +- errmsg("cannot assign to system column \"%s\"", +- colname), +- parser_errposition(pstate, location))); +- attrtype = attnumTypeId(rd, attrno); +- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ if (attrno > 0) ++ { ++ attrtype = attnumTypeId(rd, attrno); ++ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ } ++ else ++ { ++ Form_pg_attribute attr ++ = SystemAttributeDefinition(attrno, relhasoids); ++ if (attr && SystemAttributeIsWritable(attrno)) ++ { ++ attrtype = attr->atttypid; ++ attrtypmod = attr->atttypmod; ++ } ++ else ++ { ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("cannot assign to system column \"%s\"", ++ colname), ++ parser_errposition(pstate, location))); ++ return NULL; /* compiler kindness */ ++ } ++ } + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +@@ -483,6 +502,9 @@ updateTargetListEntry(ParseState *pstate + */ + tle->resno = (AttrNumber) attrno; + tle->resname = colname; ++ ++ if (SystemAttributeIsWritable(attrno)) ++ tle->resjunk = true; + } + + +@@ -749,6 +771,7 @@ checkInsertTargets(ParseState *pstate, L + Bitmapset *wholecols = NULL; + Bitmapset *partialcols = NULL; + ListCell *tl; ++ uint32 system_attrs = 0; + + foreach(tl, cols) + { +@@ -757,14 +780,37 @@ checkInsertTargets(ParseState *pstate, L + int attrno; + + /* Lookup column name, ereport on failure */ +- attrno = attnameAttNum(pstate->p_target_relation, name, false); ++ attrno = attnameAttNum(pstate->p_target_relation, name, true); + if (attrno == InvalidAttrNumber) ++ { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); ++ } ++ else if (attrno < 0) ++ { ++ if (SystemAttributeIsWritable(attrno)) ++ { ++ uint32 mask = (1<<(-attrno)); ++ ++ if ((system_attrs & mask) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_DUPLICATE_COLUMN), ++ errmsg("column \"%s\" specified more than once", name), ++ parser_errposition(pstate, col->location))); ++ system_attrs |= mask; ++ *attrnos = lappend_int(*attrnos, attrno); ++ continue; ++ } ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), ++ errmsg("column \"%s\" of relation \"%s\" is system column", ++ name, RelationGetRelationName(pstate->p_target_relation)), ++ parser_errposition(pstate, col->location))); ++ } + + /* + * Check for duplicates, but only of whole columns --- we allow +diff -rpNU3 base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c +--- base/src/backend/postmaster/postmaster.c 2008-09-25 15:09:40.000000000 +0900 ++++ sepgsql/src/backend/postmaster/postmaster.c 2008-09-25 15:22:04.000000000 +0900 +@@ -107,6 +107,7 @@ + #include "postmaster/pgarch.h" + #include "postmaster/postmaster.h" + #include "postmaster/syslogger.h" ++#include "security/pgace.h" + #include "storage/fd.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +@@ -214,7 +215,8 @@ static pid_t StartupPID = 0, + AutoVacPID = 0, + PgArchPID = 0, + PgStatPID = 0, +- SysLoggerPID = 0; ++ SysLoggerPID = 0, ++ pgaceWorkerPID = 0; + + /* Startup/shutdown state */ + #define NoShutdown 0 +@@ -1321,6 +1323,10 @@ ServerLoop(void) + if (PgStatPID == 0 && pmState == PM_RUN) + PgStatPID = pgstat_start(); + ++ /* If we have lost the pgace worker (if needed), try to start a new one */ ++ if (pgaceWorkerPID == 0 && pmState == PM_RUN) ++ pgaceWorkerPID = pgaceStartupWorkerProcess(); ++ + /* + * Touch the socket and lock file every 58 minutes, to ensure that + * they are not removed by overzealous /tmp-cleaning tasks. We assume +@@ -1911,6 +1917,8 @@ SIGHUP_handler(SIGNAL_ARGS) + signal_child(PgArchPID, SIGHUP); + if (SysLoggerPID != 0) + signal_child(SysLoggerPID, SIGHUP); ++ if (pgaceWorkerPID != 0) ++ signal_child(pgaceWorkerPID, SIGHUP); + /* PgStatPID does not currently need SIGHUP */ + + /* Reload authentication config files too */ +@@ -1968,6 +1976,9 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the pgace worker too */ ++ if (pgaceWorkerPID != 0) ++ signal_child(pgaceWorkerPID, SIGTERM); + pmState = PM_WAIT_BACKENDS; + } + +@@ -2006,6 +2017,9 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the walwriter too */ ++ if (pgaceWorkerPID != 0) ++ signal_child(pgaceWorkerPID, SIGTERM); + pmState = PM_WAIT_BACKENDS; + } + +@@ -2039,6 +2053,8 @@ pmdie(SIGNAL_ARGS) + signal_child(PgArchPID, SIGQUIT); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGQUIT); ++ if (pgaceWorkerPID != 0) ++ signal_child(pgaceWorkerPID, SIGQUIT); + ExitPostmaster(0); + break; + } +@@ -2287,6 +2303,16 @@ reaper(SIGNAL_ARGS) + continue; + } + ++ /* Was it the PGACE worker process? */ ++ if (pid == pgaceWorkerPID) ++ { ++ pgaceWorkerPID = 0; ++ if (!EXIT_STATUS_0(exitstatus)) ++ LogChildExit(LOG, _("PGACE worker process"), ++ pid, exitstatus); ++ continue; ++ } ++ + /* + * Else do standard backend child cleanup. + */ +@@ -2454,6 +2480,18 @@ HandleChildCrash(int pid, int exitstatus + signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + ++ /* Take care of the pgace worker too */ ++ if (pid == pgaceWorkerPID) ++ pgaceWorkerPID = 0; ++ else if (pgaceWorkerPID != 0 && !FatalError) ++ { ++ ereport(DEBUG2, ++ (errmsg_internal("sending %s to process %d", ++ (SendStop ? "SIGSTOP" : "SIGQUIT"), ++ (int) pgaceWorkerPID))); ++ signal_child(pgaceWorkerPID, (SendStop ? SIGSTOP : SIGQUIT)); ++ } ++ + /* + * Force a power-cycle of the pgarch process too. (This isn't absolutely + * necessary, but it seems like a good idea for robustness, and it +@@ -2573,7 +2611,8 @@ PostmasterStateMachine(void) + StartupPID == 0 && + (BgWriterPID == 0 || !FatalError) && + WalWriterPID == 0 && +- AutoVacPID == 0) ++ AutoVacPID == 0 && ++ pgaceWorkerPID == 0) + { + if (FatalError) + { +diff -rpNU3 base/src/backend/rewrite/rewriteHandler.c sepgsql/src/backend/rewrite/rewriteHandler.c +--- base/src/backend/rewrite/rewriteHandler.c 2008-11-05 09:57:00.000000000 +0900 ++++ sepgsql/src/backend/rewrite/rewriteHandler.c 2009-01-16 10:33:08.000000000 +0900 +@@ -24,6 +24,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteHandler.h" + #include "rewrite/rewriteManip.h" ++#include "security/pgace.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" + #include "commands/trigger.h" +@@ -1919,5 +1920,5 @@ QueryRewrite(Query *parsetree) + if (!foundOriginalQuery && lastInstead != NULL) + lastInstead->canSetTag = true; + +- return results; ++ return pgacePostQueryRewrite(results); + } +diff -rpNU3 base/src/backend/security/Makefile sepgsql/src/backend/security/Makefile +--- base/src/backend/security/Makefile 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/Makefile 2009-01-14 15:33:08.000000000 +0900 +@@ -0,0 +1,32 @@ ++# ++# src/backend/security/Makefile ++# Makefile for Security Purpose Extensions ++# ++# Copyright (c) 2006 - 2007 KaiGai Kohei ++# ++subdir = src/backend/security ++top_builddir = ../../.. ++include $(top_builddir)/src/Makefile.global ++ ++ ++OBJS := pgaceCommon.o pgaceHooks.o ++ ++ifeq ($(enable_selinux), yes) ++OBJS += sepgsql/avc.o sepgsql/core.o sepgsql/hooks.o \ ++ sepgsql/permissions.o sepgsql/proxy.o ++endif ++ ++all: SUBSYS.o ++ ++SUBSYS.o: $(OBJS) ++ $(LD) $(LDREL) $(LDOUT) $@ $^ ++ ++depend dep: ++ $(CC) -MM $(CFLAGS) *.c >depend ++ ++clean: ++ rm -f SUBSYS.o $(OBJS) ++ ++ifeq (depend,$(wildcard depend)) ++include depend ++endif +diff -rpNU3 base/src/backend/security/pgaceCommon.c sepgsql/src/backend/security/pgaceCommon.c +--- base/src/backend/security/pgaceCommon.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/pgaceCommon.c 2009-01-14 15:25:24.000000000 +0900 +@@ -0,0 +1,814 @@ ++ ++/* ++ * src/backend/security/pgaceCommon.c ++ * common framework of security modules ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/hash.h" ++#include "access/heapam.h" ++#include "access/xact.h" ++#include "catalog/catalog.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_security.h" ++#include "catalog/pg_type.h" ++#include "executor/executor.h" ++#include "libpq/be-fsstubs.h" ++#include "miscadmin.h" ++#include "nodes/makefuncs.h" ++#include "nodes/parsenodes.h" ++#include "parser/parse_expr.h" ++#include "security/pgace.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++#include ++#include ++ ++/***************************************************************************** ++ * GUC Parameter Support ++ *****************************************************************************/ ++ ++int pgace_feature; ++char *pgace_feature_string; ++ ++const char * ++pgaceAssignFeatureString(const char *value, bool doit, GucSource source) ++{ ++ char *result; ++ ++ if (strcmp(value, "none") == 0) ++ { ++ pgace_feature = PGACE_FEATURE_NONE; ++ result = strdup(value); ++ } ++#ifdef HAVE_SELINUX ++ else if (strcmp(value, "selinux") == 0) ++ { ++ pgace_feature = PGACE_FEATURE_SELINUX; ++ result = strdup(value); ++ } ++#endif ++ else ++ { ++ pgace_feature = PGACE_FEATURE_NONE; ++ result = strdup("none"); ++ } ++ ++ return result; ++} ++ ++/***************************************************************************** ++ * Extended SQL statements support ++ *****************************************************************************/ ++ ++/* ++ * PGACE enables to create a new table labed as explicitly specified security ++ * attribute. It is implemented as an extension of SQL statement like: ++ * CREATE TABLE memo ( ++ * id integer primary key, ++ * msg TEXT ++ * ) CONTEXT = 'system_u:object_r:sepgsql_secret_table_t'; ++ * ++ * The specified security attribute is chained as a list of DefElem object, ++ * at CreateStmt->pgaceItem for a table, ColumnDef->pgaceItem for a column. ++ * ++ * These items are generated at pgaceGramSecurityItem() hook invoked from ++ * parser/gram.y. Then, pgaceRelationAttrList() pick them up and re-organize ++ * as a list, to pass it as an argument of heap_create_with_catalog(). ++ * ++ * When the list is not NIL, it means user specifies a security attribute ++ * explicitly for a newly created table or column. ++ * pgaceGramCreateRelation() and pgaceGramCreateAttribute() are invoked ++ * just before inserting a new tuple into system catalog, and PGACE ++ * framework invokes pgaceGramCreateRelation() and/or pgaceGramCreateAttribute() ++ * hooks to give a chance the gurst to attach proper security attributes. ++ */ ++ ++List * ++pgaceRelationAttrList(CreateStmt *stmt) ++{ ++ List *result = NIL; ++ ListCell *l; ++ DefElem *defel, *newel; ++ ++ if (stmt->pgaceItem) ++ { ++ defel = (DefElem *) stmt->pgaceItem; ++ ++ Assert(IsA(defel, DefElem)); ++ ++ if (!pgaceIsGramSecurityItem(defel)) ++ elog(ERROR, "node is not a pgace security item"); ++ newel = makeDefElem(NULL, (Node *) copyObject(defel)); ++ result = lappend(result, newel); ++ } ++ ++ foreach(l, stmt->tableElts) ++ { ++ ColumnDef *cdef = (ColumnDef *) lfirst(l); ++ ++ defel = (DefElem *) cdef->pgaceItem; ++ ++ if (defel) ++ { ++ Assert(IsA(defel, DefElem)); ++ ++ if (!pgaceIsGramSecurityItem(defel)) ++ elog(ERROR, "node is not a pgace security item"); ++ newel = makeDefElem(pstrdup(cdef->colname), ++ (Node *) copyObject(defel)); ++ result = lappend(result, newel); ++ } ++ } ++ return result; ++} ++ ++void ++pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, List *pgaceAttrList) ++{ ++ ListCell *l; ++ ++ foreach(l, pgaceAttrList) ++ { ++ DefElem *defel = (DefElem *) lfirst(l); ++ ++ if (!defel->defname) ++ { ++ Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg)); ++ pgaceGramCreateRelation(rel, tuple, (DefElem *) defel->arg); ++ break; ++ } ++ } ++} ++ ++void ++pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple, ++ List *pgaceAttrList) ++{ ++ Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple); ++ ListCell *l; ++ ++ foreach(l, pgaceAttrList) ++ { ++ DefElem *defel = lfirst(l); ++ ++ if (!defel->defname) ++ continue; /* for table */ ++ if (strcmp(defel->defname, NameStr(attr->attname)) == 0) ++ { ++ Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg)); ++ pgaceGramCreateAttribute(rel, tuple, (DefElem *) defel->arg); ++ break; ++ } ++ } ++} ++ ++/* ++ * pgaceAlterRelationCommon() ++ * ++ * This function is invoked when a user requires to change security attribute ++ * of table/column with "ALTER TABLE" statement. ++ * ++ * When a user attempt to relabel a table, PGACE invokes alterRelationCommon() ++ * and it gives the guest module a chance to set a new security attribute of ++ * specified table. ++ * When a user attempt to relabel a column, PGACE invokes alterAttributeCommon() ++ * and it gives the guest module a chance to set a new security attribute of ++ * specified column. ++ */ ++ ++static void ++alterRelationCommon(Relation rel, DefElem *defel) ++{ ++ Relation pg_class; ++ HeapTuple tuple; ++ ++ pg_class = heap_open(RelationRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopy(RELOID, ++ ObjectIdGetDatum(RelationGetRelid(rel)), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation '%s'", ++ RelationGetRelationName(rel)); ++ pgaceGramAlterRelation(rel, tuple, defel); ++ ++ simple_heap_update(pg_class, &tuple->t_self, tuple); ++ CatalogUpdateIndexes(pg_class, tuple); ++ ++ heap_freetuple(tuple); ++ heap_close(pg_class, RowExclusiveLock); ++} ++ ++static void ++alterAttributeCommon(Relation rel, char *colName, DefElem *defel) ++{ ++ Relation pg_attr; ++ HeapTuple tuple; ++ ++ pg_attr = heap_open(AttributeRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for attribute '%s' of relation '%s'", ++ colName, RelationGetRelationName(rel)); ++ pgaceGramAlterAttribute(rel, tuple, defel); ++ ++ simple_heap_update(pg_attr, &tuple->t_self, tuple); ++ CatalogUpdateIndexes(pg_attr, tuple); ++ ++ heap_freetuple(tuple); ++ heap_close(pg_attr, RowExclusiveLock); ++} ++ ++void ++pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd) ++{ ++ DefElem *defel = (DefElem *) cmd->def; ++ ++ Assert(IsA(defel, DefElem)); ++ ++ if (!pgaceIsGramSecurityItem(defel)) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("PGACE: unsupported security item"))); ++ ++ if (!cmd->name) ++ { ++ alterRelationCommon(rel, defel); ++ } ++ else ++ { ++ alterAttributeCommon(rel, cmd->name, defel); ++ } ++} ++ ++/***************************************************************************** ++ * security attribute management ++ *****************************************************************************/ ++ ++/* ++ * The following functions enables to manage security attribute of each tuple ++ * (including ones within system catalog). ++ * ++ * Security attribute has these features: ++ * 1. It is imported/exported with text representation, like ++ * 'system_u:object_r:sepgsql_table_t:s0' ++ * 2. In generally, many tuples share a same security attribute. ++ * (They are grouped by security attribute in other word.) ++ * 3. A object can have one security attribute at most. ++ * (It can have a state of unlabeled.) ++ * ++ * PGACE utilizes a newly added system catalog of pg_security to store text ++ * representation of security attribute efficiently. Any tuple has a object id ++ * of a tuple within pg_security system catalog, we call it as a security id. ++ * ++ * Users can show security attribute as if it stored text data, but any tuple ++ * has a security id which has a length of sizeof(Oid), without text data. ++ * It is translated each other when it is exported/imported. ++ * ++ * pgaceSidToSecurityLabel() returns a text representation for a given security, ++ * id, and pgaceSecurityLabelToSid() returns a security id for a give text ++ * representation. (If a given text representation was not found on pg_security ++ * system catalog, PGACE inserts a new entry automatically.) ++ * ++ * In the very early phase (invoked by initdb), pg_security system catalos is ++ * not available yet. The earlySecurityLabelToSid() and earlySidToSecurityLabel() ++ * is used to hold relationships between security id and text representation. ++ * These relationships are stored at the end of bootstraping mode by ++ * pgacePostBootstrapingMode(). It write any cached relationships into pg_security ++ * system catalog. ++ */ ++ ++typedef struct earlySeclabel ++{ ++ struct earlySeclabel *next; ++ Oid sid; ++ char label[1]; ++} earlySeclabel; ++ ++static earlySeclabel *earlySeclabelList = NULL; ++ ++static Oid ++earlySecurityLabelToSid(char *label) ++{ ++ earlySeclabel *es; ++ Oid minsid = SecurityRelationId; ++ ++ for (es = earlySeclabelList; es != NULL; es = es->next) ++ { ++ if (!strcmp(label, es->label)) ++ return es->sid; ++ if (es->sid < minsid) ++ minsid = es->sid; ++ } ++ /* ++ * not found ++ */ ++ es = malloc(sizeof(earlySeclabel) + strlen(label)); ++ es->next = earlySeclabelList; ++ es->sid = minsid - 1; ++ strcpy(es->label, label); ++ earlySeclabelList = es; ++ ++ return es->sid; ++} ++ ++static char * ++earlySidToSecurityLabel(Oid sid) ++{ ++ earlySeclabel *es; ++ ++ for (es = earlySeclabelList; es != NULL; es = es->next) ++ { ++ if (es->sid == sid) ++ return pstrdup(es->label); ++ } ++ ++ return NULL; /* not found */ ++} ++ ++void ++pgacePostBootstrapingMode(void) ++{ ++ Relation rel; ++ CatalogIndexState ind; ++ HeapTuple tuple; ++ earlySeclabel *es, *_es; ++ Oid meta_sid; ++ Datum value; ++ bool isnull; ++ ++ if (!earlySeclabelList) ++ return; ++ ++ StartTransactionCommand(); ++ ++ meta_sid = earlySecurityLabelToSid(pgaceSecurityLabelOfLabel()); ++ ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ ind = CatalogOpenIndexes(rel); ++ ++ for (es = earlySeclabelList; es != NULL; es = _es) ++ { ++ _es = es->next; ++ ++ value = DirectFunctionCall1(textin, CStringGetDatum(es->label)); ++ isnull = false; ++ tuple = heap_form_tuple(RelationGetDescr(rel), &value, &isnull); ++ ++ HeapTupleSetOid(tuple, es->sid); ++ if (HeapTupleHasSecLabel(tuple)) ++ HeapTupleSetSecLabel(tuple, meta_sid); ++ ++ simple_heap_insert(rel, tuple); ++ CatalogIndexInsert(ind, tuple); ++ ++ heap_freetuple(tuple); ++ ++ free(es); ++ } ++ CatalogCloseIndexes(ind); ++ heap_close(rel, RowExclusiveLock); ++ ++ CommitTransactionCommand(); ++} ++ ++/* ++ * pgaceLookupSecurityId() ++ * ++ * The PGACE guest subsystem can use this interface to get a security id ++ * for a given text representation. ++ */ ++Oid ++pgaceLookupSecurityId(char *raw_label) ++{ ++ Oid labelOid, labelSid; ++ HeapTuple tuple; ++ ++ if (IsBootstrapProcessingMode()) ++ return earlySecurityLabelToSid(raw_label); ++ ++ /* ++ * lookup syscache at first ++ */ ++ tuple = SearchSysCache(SECURITYLABEL, ++ CStringGetTextDatum(raw_label), ++ 0, 0, 0); ++ if (HeapTupleIsValid(tuple)) ++ { ++ labelOid = HeapTupleGetOid(tuple); ++ ReleaseSysCache(tuple); ++ } ++ else ++ { ++ /* ++ * not found, insert a new one into pg_security ++ */ ++ Relation rel; ++ CatalogIndexState ind; ++ char *slabel; ++ Datum labelTxt; ++ bool isnull; ++ ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ ++ slabel = pgaceSecurityLabelOfLabel(); ++ ++ if (!slabel) ++ { ++ labelSid = InvalidOid; ++ labelOid = GetNewOid(rel); ++ } ++ else if (!strcmp(raw_label, slabel)) ++ { ++ labelOid = labelSid = GetNewOid(rel); ++ } ++ else ++ { ++ labelSid = pgaceLookupSecurityId(slabel); ++ labelOid = GetNewOid(rel); ++ } ++ ++ ind = CatalogOpenIndexes(rel); ++ ++ labelTxt = CStringGetTextDatum(raw_label); ++ isnull = false; ++ tuple = heap_form_tuple(RelationGetDescr(rel), ++ &labelTxt, &isnull); ++ if (HeapTupleHasSecLabel(tuple)) ++ HeapTupleSetSecLabel(tuple, labelSid); ++ HeapTupleSetOid(tuple, labelOid); ++ ++ simple_heap_insert(rel, tuple); ++ CatalogIndexInsert(ind, tuple); ++ ++ /* ++ * NOTE: ++ * We also have to insert a cache entry of new tuple of ++ * pg_security for temporary usage. ++ * If user tries to apply same security attribute twice ++ * or more within same command id, PGACE cannot decide ++ * whether it should be inserted, or not, because it ++ * cannot scan the prior one with SnapshotNow. ++ * ++ * A cache entry inserted will be invalidated on the ++ * next CommandIdIncrement(). ++ * The purpose of InsertSysCache() here is to prevent ++ * duplicate insertion ++ */ ++ InsertSysCache(RelationGetRelid(rel), tuple); ++ ++ CatalogCloseIndexes(ind); ++ heap_close(rel, RowExclusiveLock); ++ } ++ ++ return labelOid; ++} ++ ++Oid ++pgaceSecurityLabelToSid(char *label) ++{ ++ char *raw_label = pgaceTranslateSecurityLabelIn(label); ++ ++ if (!pgaceCheckValidSecurityLabel(raw_label)) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("PGACE: invalid security label: %s", raw_label))); ++ ++ return pgaceLookupSecurityId(raw_label); ++} ++ ++/* ++ * pgaceLookupSecurityLabel() ++ * ++ * The PGACE guest module can use this interface to get a text representation ++ * in raw-format, without cosmetic translation. ++ */ ++char * ++pgaceLookupSecurityLabel(Oid sid) ++{ ++ HeapTuple tuple; ++ Datum labelTxt; ++ char *label; ++ bool isnull; ++ ++ if (!OidIsValid(sid)) ++ return NULL; ++ ++ if (IsBootstrapProcessingMode()) ++ return earlySidToSecurityLabel(sid); ++ ++ tuple = SearchSysCache(SECURITYOID, ++ ObjectIdGetDatum(sid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ return NULL; ++ ++ labelTxt = SysCacheGetAttr(SECURITYOID, ++ tuple, Anum_pg_security_seclabel, &isnull); ++ Assert(!isnull); ++ label = TextDatumGetCString(labelTxt); ++ ReleaseSysCache(tuple); ++ ++ return label; ++} ++ ++char * ++pgaceSidToSecurityLabel(Oid sid) ++{ ++ char *label; ++ ++ label = pgaceLookupSecurityLabel(sid); ++ if (!label || !pgaceCheckValidSecurityLabel(label)) ++ label = pgaceUnlabeledSecurityLabel(); ++ ++ if (!label) ++ return pstrdup(""); ++ ++ return pgaceTranslateSecurityLabelOut(label); ++} ++ ++Datum ++pgaceHeapGetSecurityLabelSysattr(HeapTuple tuple) ++{ ++ Oid sid = HeapTupleGetSecLabel(tuple); ++ ++ return CStringGetTextDatum(pgaceSidToSecurityLabel(sid)); ++} ++ ++/***************************************************************************** ++ * Set/Get security attribute of Large Object ++ *****************************************************************************/ ++ ++/* ++ * lo_get_security() ++ * ++ * This function returns a security attribute of large object ++ * in TEXT representation. ++ * ++ * It assumes the first page means the whole of large object. ++ * The guest of PGACE should pay effort to keep its consistency. ++ */ ++Datum ++lo_get_security(PG_FUNCTION_ARGS) ++{ ++ Oid loid = PG_GETARG_OID(0); ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid sid; ++ ++ rel = heap_open(LargeObjectRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, ++ SnapshotNow, 1, &skey); ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", loid))); ++ pgaceLargeObjectGetSecurity(rel, tuple); ++ sid = HeapTupleGetSecLabel(tuple); ++ ++ systable_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ ++ return CStringGetTextDatum(pgaceSidToSecurityLabel(sid)); ++} ++ ++/* ++ * lo_set_security() ++ * ++ * This function set a new security attribute of a large object. ++ * It scans pg_largeobject system catalog with a given loid, ++ * and invokes pgaceLargeObjectSetSecurity() for each page frame. ++ */ ++Datum ++lo_set_security(PG_FUNCTION_ARGS) ++{ ++ Oid loid = PG_GETARG_OID(0); ++ Datum labelTxt = PG_GETARG_DATUM(1); ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc sd; ++ HeapTuple oldtup, newtup; ++ CatalogIndexState indstate; ++ Oid sid; ++ List *okList = NIL; ++ bool found = false; ++ ++ sid = pgaceSecurityLabelToSid(TextDatumGetCString(labelTxt)); ++ ++ ScanKeyInit(&skey, ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, ++ F_OIDEQ, ObjectIdGetDatum(loid)); ++ ++ rel = heap_open(LargeObjectRelationId, RowExclusiveLock); ++ ++ indstate = CatalogOpenIndexes(rel); ++ ++ sd = systable_beginscan(rel, ++ LargeObjectLOidPNIndexId, true, ++ SnapshotNow, 1, &skey); ++ ++ while ((oldtup = systable_getnext(sd)) != NULL) ++ { ++ ListCell *l; ++ ++ newtup = heap_copytuple(oldtup); ++ HeapTupleSetSecLabel(newtup, sid); ++ ++ foreach (l, okList) ++ { ++ if (HeapTupleGetSecLabel(oldtup) == lfirst_oid(l)) ++ goto skip; /* already checked */ ++ } ++ okList = lappend_oid(okList, HeapTupleGetSecLabel(oldtup)); ++ ++ pgaceLargeObjectSetSecurity(rel, newtup, oldtup); ++ skip: ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ CatalogUpdateIndexes(rel, newtup); ++ found = true; ++ } ++ systable_endscan(sd); ++ CatalogCloseIndexes(indstate); ++ heap_close(rel, RowExclusiveLock); ++ ++ CommandCounterIncrement(); ++ ++ if (!found) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", loid))); ++ ++ PG_RETURN_BOOL(true); ++} ++ ++/****************************************************************** ++ * Function stubs related to security modules ++ ******************************************************************/ ++ ++/* ++ * Legacy functions support ++ */ ++Datum security_label_in(PG_FUNCTION_ARGS); ++Datum security_label_out(PG_FUNCTION_ARGS); ++Datum security_label_raw_in(PG_FUNCTION_ARGS); ++Datum security_label_raw_out(PG_FUNCTION_ARGS); ++Datum text_to_security_label(PG_FUNCTION_ARGS); ++Datum security_label_to_text(PG_FUNCTION_ARGS); ++ ++Datum ++security_label_in(PG_FUNCTION_ARGS) ++{ ++ return DirectFunctionCall1(textin, PG_GETARG_DATUM(0)); ++} ++ ++Datum ++security_label_out(PG_FUNCTION_ARGS) ++{ ++ return DirectFunctionCall1(textout, PG_GETARG_DATUM(0)); ++} ++ ++Datum ++security_label_raw_in(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("%s is no longer supported", __FUNCTION__))); ++ PG_RETURN_VOID(); ++} ++ ++Datum ++security_label_raw_out(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("%s is no longer supported", __FUNCTION__))); ++ PG_RETURN_VOID(); ++} ++ ++Datum ++text_to_security_label(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("%s is no longer supported", __FUNCTION__))); ++ PG_RETURN_VOID(); ++} ++ ++Datum ++security_label_to_text(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("%s is no longer supported", __FUNCTION__))); ++ PG_RETURN_VOID(); ++} ++ ++/* ++ * If the guest of PGACE added its specific functions, it has to put ++ * function stubs on the following section, because the guest modules ++ * are not compiled and linked when it is disabled. ++ * It can cause a build problem in other environments. ++ */ ++#ifndef HAVE_SELINUX ++ ++static Datum ++unavailable_function(const char *fn_name, int error_code) ++{ ++ ereport(ERROR, ++ (errcode(error_code), ++ errmsg("%s is not available", fn_name))); ++ PG_RETURN_VOID(); ++} ++ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_getservcon(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_get_user(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_get_role(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_get_type(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_get_range(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_set_user(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_set_role(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_set_type(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++Datum ++sepgsql_set_range(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__, ++ ERRCODE_SELINUX_ERROR); ++} ++ ++#endif /* HAVE_SELINUX */ +diff -rpNU3 base/src/backend/security/pgaceHooks.c sepgsql/src/backend/security/pgaceHooks.c +--- base/src/backend/security/pgaceHooks.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/pgaceHooks.c 2009-02-25 22:31:25.000000000 +0900 +@@ -0,0 +1,1462 @@ ++/* ++ * src/backend/security/pgaceHooks.c ++ * Security hooks in PostgreSQL Access Control Extension (PGACE) ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#include "postgres.h" ++ ++#include "security/pgace.h" ++ ++/* ++ * GUC parameter: pgace_feature ++ * It allows users to choose an enhanced security feature. ++ * It has a state of 'none' in the default, so you should ++ * specify it explicitly with initdb --pgace-feature=FEATURE. ++ */ ++int pgace_feature; ++ ++/* ++ * PGACE (PostgreSQL Access Control Extension) ++ * ++ * It provides a set of security hooks at strategic points and ++ * common facilities to manage security attribute of database ++ * objects. Its purpose is to allow various kind of enhanced ++ * security features with minimum impact to the core PostgreSQL ++ * codes. ++ * In generally, individual security feature has its own access ++ * control model, policy and granuality, however, they also have ++ * facilities to be shared commonly. ++ * ++ * The one is a set of security hooks. All the enhanced security ++ * codes have to be invoked via the hooks, and return a proper ++ * value or raise an error, if necessary. ++ * When you add a new security feature, you need the following steps. ++ * 1. add a option to 'pgace_feature' parameter. ++ * 2. modify hooks to invoke your security feature. ++ * Please note that you don't need to modify all the hooks. ++ * If you don't provide any feature, please keep it as is. ++ * ++ * Example: pgaceHeapTupleInsert() hook ++ * ------------------------------------ ++ * bool ++ * pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, ++ * bool is_internal, bool with_returning) ++ * { ++ * switch (pgace_feature) ++ * { ++ * #ifdef HAVE_SELINUX ++ * case PGACE_FEATURE_SELINUX: ++ * if (sepgsqlIsEnabled()) ++ * return sepgsqlHeapTupleInsert(rel, tuple, ++ * is_internal, ++ * with_returning); ++ * break; ++ * #endif ++ * #ifdef HAVE_FOO_SECURITY ++ * case PGACE_FEATURE_FOO_SECURITY: ++ * return fooSecurityHeapTupleInsert(rel, tuple, ++ * is_internal, ++ * with_returning); ++ * break; ++ * #endif ++ * default: ++ * break; ++ * } ++ * return true; ++ * } ++ * ------------------------------------ ++ * If your security feature has platform dependency, related code ++ * should be enclosed by #ifdef ... #endif block. ++ * (In this case, it is named as FOO_SECURITY.) ++ * The pgace_feature shows what enhanced security feature is activated ++ * in this system. If your security feature is chosen, it can be invoked ++ * via pgaceHeapTupleInsert() just before a new tuple is inserted on ++ * the target relation. Your fooSecurityHeapTupleInsert() can make its ++ * decision based on its policy and given informations. ++ * This hook requires to return 'true' or 'false'. If it returns 'false', ++ * it will be skipped to insert the given tuple. ++ * ++ * The other is facilities to manage security attribtue of database ++ * objects. They have text representation as most of secure operating ++ * system doing, but it is not stored in each tuples directly, to reduce ++ * storage comsumption. ++ * We can fetch them via HeapTupleGetSecLabel(tuple) macro. It is stored ++ * as a Oid value (called as security identifier) which indicates pg_security ++ * system catalog. It holds mapping between security identifier and security ++ * attribute in text representation. ++ * User can see/set security attribute of database objects via security_label ++ * system column. ++ */ ++ ++/****************************************************************** ++ * Initialization hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceShmemSize ++ * ++ * This hook has to return the size of shared memory required ++ * by the guest. If it needs no shared memory region, it should ++ * return 0. ++ */ ++Size ++pgaceShmemSize(void) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlShmemSize(); ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ return (Size) 0; ++} ++ ++/* ++ * pgaceInitialize ++ * ++ * This hook is invoked when a new PostgreSQL instance is created. ++ * The guest can use this hook to initialize itself. ++ * ++ * is_bootstrap is true, if bootstraping mode. ++ */ ++void ++pgaceInitialize(bool is_bootstrap) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlInitialize(is_bootstrap); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceStartupWorkerProcess ++ * ++ * The guest can create a worker process in this hook, if necessary. ++ * (currently, PGACE does not support multiple worker processes.) ++ * ++ * This hooks has to return the PID of child process. It is managed ++ * by postmaster in the same way to manage the other children. ++ * So, the worker process has to be available to handle signals. ++ * ++ * If unnecessary, it has to return (pid_t) 0. ++ */ ++pid_t ++pgaceStartupWorkerProcess(void) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlStartupWorkerProcess(); ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ return (pid_t) 0; ++} ++ ++/****************************************************************** ++ * SQL proxy hooks ++ ******************************************************************/ ++ ++/* ++ * pgacePostQueryRewrite ++ * ++ * This hook is invoked just after query is rewritten. ++ * ++ * The guest can check/modify/replace given query trees in this ++ * hook, if necessary. ++ * queryList is a list of Query object processes by rewriter. ++ */ ++List * ++pgacePostQueryRewrite(List *queryList) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlPostQueryRewrite(queryList); ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ return queryList; ++} ++ ++/* ++ * pgaceExecutorStart ++ * ++ * This hook is invoked on the head of ExecutorStart(). ++ * ++ * The arguments of this hook are come from the ones of ExecutorStart ++ * as is. ++ */ ++void ++pgaceExecutorStart(QueryDesc *queryDesc, int eflags) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlExecutorStart(queryDesc, eflags); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceRowlvBehaviorSwitchTo ++ * changes internal state during FK constraint checks ++ */ ++bool ++pgaceRowlvBehaviorSwitchTo(bool new_abort) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlRowlvBehaviorSwitchTo(new_abort); ++ break; ++#endif ++ default: ++ break; ++ } ++ return new_abort; ++} ++ ++/* ++ * pgaceExecScan ++ * ++ * This hook is invoked on ExecScan for each tuple fetched. ++ * The guest can check its visibility, and can skip to scan the given ++ * tuple. If this hook returns false, the tuple is filtered from the ++ * result set or the target of updates/deletion. ++ * ++ * Otherwise, it has to return true. ++ * ++ * The guest can refer Scan::pgaceTuplePerms (declared as uint32). ++ * It is a copy come from RangeTblEntry::pgaceTuplePerms set in ++ * the previous phase. It can be used to mark what permissions are ++ * required to scanned tuples. ++ */ ++bool ++pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlExecScan(scan, rel, slot, abort); ++ break; ++#endif ++ default: ++ break; ++ } ++ return true; ++} ++ ++/* ++ * pgaceProcessUtility ++ * ++ * This hooks is invoked on the head of ProcessUtility(). ++ */ ++void ++pgaceProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlProcessUtility(parsetree, params, isTopLevel); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/****************************************************************** ++ * HeapTuple modification hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceHeapTupleInsert ++ * ++ * This hooks is invoked just before a new tuple is inserted. ++ * If it returns false, inserting the given tuple is skipped. ++ * (or generates an error, if we cannot skip it simply.) ++ * ++ * The guest has to set a security attribute of a newly inserted ++ * tuple, if necessary and when user does not specify it explicitly. ++ * ++ * arguments: ++ * - rel is the target relation to be inserted. ++ * - tuple is the new tuple to be inserted. ++ * - is_internal is a bool to show whether it directly come from ++ * user's query, or not. ++ * - with_returning is a bool to show whether this INSERT statement ++ * has RETURNING clause, or not. ++ */ ++bool ++pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, ++ bool is_internal, bool with_returning) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlHeapTupleInsert(rel, tuple, ++ is_internal, ++ with_returning); ++ break; ++#endif ++ default: ++ break; ++ } ++ return true; ++} ++ ++/* ++ * pgaceHeapTupleUpdate ++ * ++ * This hook is invoked just before a tuple is updated. ++ * If it returns false, updating the given tuple is skipped. ++ * (or generates an error, if we cannot skip it simply.) ++ * ++ * The guest has to preserve a security attribute of the updated ++ * tuple, if necessary and when user specify its new security ++ * attribute explicitly. ++ * ++ * arguments: ++ * - rel is the target relation to be updated. ++ * - otid is the ItemPointer of the tuple with older version. ++ * - newtup is the tuple to be updated. ++ * - is_internal is a bool to show whether it directly come from ++ * user's query, or not. ++ * - with_returning is a bool to show whether this INSERT statement ++ * has RETURNING clause, or not. ++ */ ++bool ++pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, ++ bool is_internal, bool with_returning) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlHeapTupleUpdate(rel, otid, newtup, ++ is_internal, ++ with_returning); ++ break; ++#endif ++ default: ++ break; ++ } ++ return true; ++} ++ ++/* ++ * pgaceHeapTupleDelete ++ * ++ * This hook is invoked just before a tuple is deleted. ++ * If it returns false, deleting the given tuple is skipped. ++ * (or generates an error, if we cannot skip it simply.) ++ * ++ * arguments: ++ * - rel is the target relation to be deleted. ++ * - otid is the ItemPointer of the tuple to be deleted. ++ * - is_internal is a bool to show whether it directly come from ++ * user's query, or not. ++ * - with_returning is a bool to show whether this INSERT statement ++ * has RETURNING clause, or not. ++ */ ++bool ++pgaceHeapTupleDelete(Relation rel, ItemPointer otid, ++ bool is_internal, bool with_returning) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlHeapTupleDelete(rel, otid, ++ is_internal, ++ with_returning); ++ break; ++#endif ++ default: ++ break; ++ } ++ return true; ++} ++ ++/****************************************************************** ++ * Extended SQL statement hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceIsGramSecurityItem ++ * ++ * PGACE framework provides its guest to manage security attribute ++ * for some kind of database obejcts, using an enhanced SQL statement. ++ * ++ * For example: ++ * CREATE TABLE tbl ( ++ * x integer, ++ * y text ++ * ) security_label = 'system_u:object_r:sepgsql_table_t:Classified'; ++ * ++ * This hook is invoked during parsing given queries at parser/gram.y. ++ * It generates a DefElem object which holds explicitly specified ++ * security attribute. If working guest support the feature and the ++ * given DefElem has correct pair of defname and argument string, ++ * this hook should return true. ++ * In ths above example, the given DefElem has "security_label" as ++ * defname, and "system_u:object_r:sepgsql_table_t:Classified" as ++ * its argument string. ++ */ ++bool ++pgaceIsGramSecurityItem(DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlIsGramSecurityItem(defel); ++ break; ++#endif ++ default: ++ break; ++ } ++ return false; ++} ++ ++/* ++ * The series of following hooks has three arguments. ++ * - rel is an opened relation of the target system catalog. ++ * - tuple is a new tuple to be inserted/updated. ++ * - defel is a DefElem object checked in pgaceIsGramSecurityItem(). ++ */ ++ ++/* ++ * pgaceGramCreateRelation ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before inserting a new tuple into pg_class system catalog on ++ * the processing of CREATE TABLE. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a new relation. ++ */ ++void ++pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramCreateRelation(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of table " ++ "via CREATE TABLE"))); ++} ++ ++/* ++ * pgaceGramCreateAttribute ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before inserting a new tuple into pg_attribute system catalog on ++ * the processing of CREATE TABLE. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a new column. ++ */ ++void ++pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramCreateAttribute(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of column " ++ "via CREATE TABLE"))); ++} ++ ++/* ++ * pgaceGramAlterRelation ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before updating an older tuple of pg_class system catalog on ++ * the processing of ALTER TABLE. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a table. ++ */ ++void ++pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramAlterRelation(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of table " ++ "via ALTER TABLE"))); ++} ++ ++/* ++ * pgaceGramAlterAttribute ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before updating an older tuple of pg_attribute system catalog on ++ * the processing of ALTER TABLE. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a column. ++ */ ++void ++pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramAlterAttribute(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of column " ++ "via ALTER TABLE"))); ++} ++ ++/* ++ * pgaceGramCreateDatabase ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before inserting a new tuple into pg_database system catalog on ++ * the processing of CREATE DATABASE. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a database. ++ */ ++void ++pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramCreateDatabase(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of database " ++ "via CREATE DATABASE"))); ++} ++ ++/* ++ * pgaceGramAlterDatabase ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before updating an older tuple of pg_database system catalog on ++ * the processing of ALTER DATABASE. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a database. ++ */ ++void ++pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramAlterDatabase(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of database " ++ "via ALTER DATABASE"))); ++} ++ ++/* ++ * pgaceGramCreateFunction ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before inserting a new tuple into pg_proc system catalog on ++ * the processing of CREATE FUNCTION. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a function. ++ */ ++void ++pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramCreateFunction(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of function " ++ "via CREATE FUNCTION"))); ++} ++ ++/* ++ * pgaceGramAlterFunction ++ * ++ * This hook invoked to apply an explicitly specified security attribute ++ * just before updating an older tuple of pg_proc system catalog on ++ * the processing of ALTER FUNCTION. ++ * The guest can attach the required security attribute for the given ++ * tuple which means a function. ++ */ ++void ++pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramAlterFunction(rel, tuple, defel); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ if (defel) ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("unable to set security attribute of function " ++ "via ALTER FUNCTION"))); ++} ++ ++/****************************************************************** ++ * DATABASE related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceSetDatabaseParam ++ * ++ * This hook is invoked just before putting a new value on a GUC ++ * variable. ++ * ++ * arguments: ++ * - name is a name of GUC variable. ++ * - argstring is its new value. NULL means user tries to reset ++ * the given GUC variable. ++ */ ++void ++pgaceSetDatabaseParam(const char *name, char *argstring) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlSetDatabaseParam(name, argstring); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceGetDatabaseParam ++ * ++ * This hook is invoked just before reffering a GUC variable. ++ * ++ * arguments: ++ * - name is a name of GUC variable. ++ */ ++void ++pgaceGetDatabaseParam(const char *name) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlGetDatabaseParam(name); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/****************************************************************** ++ * FUNCTION related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceCallFunction ++ * ++ * This hook is invoked when a function is invoked as a part ++ * of the given query. It provides a FmgrInfo object of the ++ * function, so the guest can store its opaque data within ++ * FmgrInfo::fn_pgaceItem. ++ */ ++void ++pgaceCallFunction(FmgrInfo *finfo) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlCallFunction(finfo); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceCallAggFunction ++ * ++ * This hook is invoked when an aggregate function is invoked ++ * in the given query. pgaceCallFunction() is also invoked for ++ * its transate function and finalize function. ++ * ++ * arguments: ++ * - aggTuple is the tuple of target aggregate function stored ++ * in pg_aggregate system catalog. ++ */ ++void ++pgaceCallAggFunction(HeapTuple aggTuple) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlCallAggFunction(aggTuple); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceCallFunctionTrigger ++ * ++ * This hook is invoked just before executing trigger function. ++ * If it returns false, the trigger function is not invoked and ++ * caller receives a NULL tuple as a result. ++ * (It also means skip to update/delete the tuple in BR-triggers.) ++ */ ++bool ++pgaceCallTriggerFunction(TriggerData *tgdata) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlCallTriggerFunction(tgdata); ++ break; ++#endif ++ default: ++ break; ++ } ++ return true; ++} ++ ++/* ++ * pgaceAllowInlineFunction ++ * ++ * This hook gives guest a chance to make decision just before ++ * a set-returning function is inlined. ++ * ++ * arguments: ++ * - fnoid is oid of the function to be inlined. ++ * - func_tuple is tuple of the function stored in pg_proc. ++ */ ++bool ++pgaceAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlAllowFunctionInlined(fnoid, func_tuple); ++ break; ++#endif ++ default: ++ break; ++ } ++ return true; ++} ++ ++/****************************************************************** ++ * TABLE related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceLockTable ++ * ++ * This hook is invoked when user tries to LOCK a table explicitly. ++ * The argument of relid shows the target relation id. ++ */ ++void ++pgaceLockTable(Oid relid) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLockTable(relid); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceExecTruncate ++ * ++ * This hook is invoked just before it truncate tables. ++ * The argument is a list of already opened relations with ++ * AccessExclusiveLock. ++ */ ++void ++pgaceExecTruncate(List *trunk_rels) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlExecTruncate(trunk_rels); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/****************************************************************** ++ * COPY TO/COPY FROM statement hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceCopyTable ++ * ++ * This hook is invoked before executing COPY TO/COPY FROM statement, ++ * to give the guest a chance to check tables/columns appeared in. ++ * ++ * arguments: ++ * - rel is the target relation of this COPY TO/FROM statement. ++ * It can be NULL, when COPY (SELECT ...) TO ... is given. ++ * - attNumList is a list of attribute number ++ * - isFrom is a bool to show the direction of the COPY ++ */ ++void ++pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlCopyTable(rel, attNumList, isFrom); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceCopyFile ++ * ++ * This hook is invoked just after a target file is opened ++ * at COPY TO/COPY FROM statement to give the guest a chance to ++ * check whether it allows to read/write the file. ++ * ++ * arguments: ++ * - rel is the target relation of this COPY TO/FROM statement. ++ * It can be NULL, when COPY (SELECT ...) TO ... is given. ++ * - isFrom is a bool to show the direction of the COPY ++ * - fdesc is the file descriptor of the target file opened. ++ * - filename is the filename of fdesc ++ */ ++void ++pgaceCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlCopyFile(rel, fdesc, filename, isFrom); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceCopyToTuple ++ * ++ * This hook is invoked just before output of a fetched tuple on ++ * processing COPY TO statement, to give the guest a chance to make ++ * a decision whether the given tuple is visible, or not. ++ * If it returns false, the given tuple is not exported, as if it ++ * does not exist on the target relation. ++ * Elsewhere, ++ * ++ * arguments: ++ * - rel is the target relation of this ++ * - attNumList is a list of attribute number ++ * - tuple is a tuple to be checked ++ */ ++bool ++pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlCopyToTuple(rel, attNumList, tuple); ++ break; ++#endif ++ default: ++ break; ++ } ++ return true; ++} ++ ++/****************************************************************** ++ * Loadable shared library module hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceLoadSharedModule ++ * ++ * This hook is invoked before loading a shared library module, ++ * to give the guest a change to confirm whether the required ++ * module is safe, or not. ++ * ++ * This hook can be also invoked implicitly when a user tries ++ * to call a function implemented within external modules. ++ */ ++void ++pgaceLoadSharedModule(const char *filename) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLoadSharedModule(filename); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/****************************************************************** ++ * Binary Large Object (BLOB) hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceLargeObjectCreate ++ * ++ * This hooks is invoked just before the first tuple of a new large ++ * object is inserted, to give the guest a change to make its ++ * decision and attach proper security context for the tuple. ++ * ++ * The argument of rel is the opened pg_largeobject system catalog. ++ */ ++void ++pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectCreate(rel, tuple); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceLargeObjectDrop ++ * ++ * This hook is invoked just before each tuple of a large object ++ * are deleted, to give the guest a change to make its decision. ++ * ++ * The argument of pgaceItem is an opaque data, the guest can ++ * use it discreationally. ++ */ ++void ++pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectDrop(rel, tuple, pgaceItem); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceLargeObjectRead ++ * ++ * This hook is invoked at the head of lo_read(). ++ * If the guest allows a large object to have non-uniform security ++ * attributes (not a unique one for each page frame), using HeapTuple ++ * related hooks are more recommendable. ++ */ ++void ++pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectRead(lodesc, length); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceLargeObjectWrite ++ * ++ * This hook is invoked at the head of lo_write(). ++ */ ++void ++pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectWrite(lodesc, length); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceLargeObjectTruncate ++ * ++ * This hook is invoked at the head of lo_truncate(). ++ */ ++void ++pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectTruncate(lodesc, offset); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceLargeObjectImport ++ * ++ * This hook is invoked just before importing the given file. ++ */ ++void ++pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectImport(loid, fdesc, filename); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceLargeObjectExport ++ * ++ * This hook is invoked just before exporting the given large object. ++ */ ++void ++pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectExport(loid, fdesc, filename); ++ break; ++#endif ++ default: ++ break; ++ } ++} ++ ++/* ++ * pgaceLargeObjectGetSecurity ++ * ++ * This hook is invoked when user requires to run lo_get_security() ++ * Note that PGACE assumes the security attribute of first page frame ++ * of large object represents its security attribute. ++ */ ++void ++pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectGetSecurity(rel, tuple); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("no enhanced security feature is available."))); ++} ++ ++/* ++ * pgaceLargeObjectSetSecurity ++ * ++ * This hook is invoked when user requires to run lo_set_security(), ++ * for each tuple within a given large object, which have unchecked ++ * security attribute. In other word, PGACE does not require the guest ++ * to check permission toward same security attribute twice, or more. ++ */ ++void ++pgaceLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectSetSecurity(rel, newtup, oldtup); ++ return; ++ } ++ break; ++#endif ++ default: ++ break; ++ } ++ ereport(ERROR, ++ (errcode(ERRCODE_PGACE_ERROR), ++ errmsg("no enhanced security feature is available."))); ++} ++ ++/****************************************************************** ++ * Security Label hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceTupleDescHasSecurity ++ * ++ * This hook enables to control the value in TupleDesc->tdhasseclabel. ++ * If it returns true, sizeof(Oid) bytes are allocated at the header ++ * of HeapTupleHeader structure. ++ * ++ * The 'rel' argument can be NULL, when we make a decision for newly ++ * created relation via SELECT INTO/CREATE TABLE AS. In this case, ++ * unparsed relation options are delivered. ++ */ ++bool ++pgaceTupleDescHasSecLabel(Relation rel, List *relopts) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlTupleDescHasSecLabel(rel, relopts); ++ break; ++#endif ++ default: ++ break; ++ } ++ return false; ++} ++ ++/* ++ * pgaceTranslateSecurityLabelIn ++ * ++ * This hook enables the guest to translate a text representation ++ * of a given security attribute in external format into internal ++ * raw-format. It is invoked when user specifies security attribute ++ * explicitly in INSERT/UPDATE statement, to translate it into ++ * raw-internal format. ++ * ++ * It has to return a palloc()'ed Cstring, as a raw-internal format. ++ * ++ * In SE-PostgreSQL it supports translation in MLS/MCS labels like: ++ * "system_u:object_r:sepgsql_table_t:SystemHigh" ++ * <--> "system_u:object_r:sepgsql_table_t:s0:c0.c1023" ++ */ ++char * ++pgaceTranslateSecurityLabelIn(char *seclabel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlTranslateSecurityLabelIn(seclabel); ++ break; ++#endif ++ default: ++ break; ++ } ++ return seclabel; ++} ++ ++/* ++ * pgaceTranslateSecurityLabelOut ++ * ++ * This hook enables the guest to translate a text representation ++ * of a given security attribute in internal format into cosmetic ++ * external format. ++ */ ++char * ++pgaceTranslateSecurityLabelOut(char *seclabel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlTranslateSecurityLabelOut(seclabel); ++ break; ++#endif ++ default: ++ break; ++ } ++ return seclabel; ++} ++ ++/* ++ * pgaceValidateSecurityLabel ++ * ++ * This hook enables the guest to validate the given security attribute ++ * in raw-internal format. ++ */ ++bool ++pgaceCheckValidSecurityLabel(char *seclabel) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlCheckValidSecurityLabel(seclabel); ++ break; ++#endif ++ default: ++ break; ++ } ++ return false; ++} ++ ++/* ++ * pgaceUnlabeledSecurityLabel ++ * ++ * This hooks allows the guest to provide an alternative security ++ * attribute, when no valid text representation found on pg_security. ++ * The hooks has to return an alternative attribute palloc()'ed. ++ */ ++char * ++pgaceUnlabeledSecurityLabel(void) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlUnlabeledSecurityLabel(); ++ break; ++#endif ++ default: ++ break; ++ } ++ return NULL; ++} ++ ++/* ++ * pgaceSecurityLabelOfLabel ++ * ++ * This hook has to return the security attribute of a newly inserted ++ * tuple withing pg_security system catalog. Note that we need a special ++ * handling in the case of pg_security. If a new tuple requires a quite ++ * new security attribute which is not on pg_security, its insertion ++ * invokes one more insertion into pg_security. In the result, it makes ++ * infinite function invocation. ++ * This hook is used to avoid such a situation. The guest has to return ++ * a text represented security attribute. ++ */ ++char * ++pgaceSecurityLabelOfLabel(void) ++{ ++ switch (pgace_feature) ++ { ++#ifdef HAVE_SELINUX ++ case PGACE_FEATURE_SELINUX: ++ if (sepgsqlIsEnabled()) ++ return sepgsqlSecurityLabelOfLabel(); ++ break; ++#endif ++ default: ++ break; ++ } ++ return NULL; ++} +diff -rpNU3 base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c +--- base/src/backend/security/sepgsql/avc.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/avc.c 2009-01-22 14:10:59.000000000 +0900 +@@ -0,0 +1,1202 @@ ++ ++/* ++ * src/backend/security/sepgsql/avc.c ++ * SE-PostgreSQL userspace access vector cache ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#include "postgres.h" ++ ++#include "access/hash.h" ++#include "libpq/pqsignal.h" ++#include "postmaster/postmaster.h" ++#include "security/pgace.h" ++#include "storage/ipc.h" ++#include "storage/lwlock.h" ++#include "utils/memutils.h" ++#include "utils/syscache.h" ++#include ++#include ++#include ++#include ++ ++/* ++ * uAVC: userspace Access Vector Cache ++ * ++ * SE-PostgreSQL makes inqueries for SELinux to check whether the security ++ * policy allows the required action, or not. However, it need to invoke ++ * system call because SELinux is a kernel feature and it hold its security ++ * policy in the kernel memory. ++ * ++ * uAVC enables to reduce the number of kernel invocation, with caching ++ * the result of inquiries. When we have to make a decision based on the ++ * security policy of SELinux, it tries to find up an appropriate cache ++ * entry on the uAVC. If exist, we don't need to invoke a system call ++ * and can reduce unnecessary overhead. ++ * ++ * If not exist, SE-PostgreSQL makes a new cache entry based on the ++ * result of inquiries, and chains it on uAVC to prepare the following ++ * decision makings. ++ * ++ * uAVC has a version number to check whether it is now valid, or not. ++ * Not need to say, uAVC cache entry has to be invalid just after ++ * policy reloaded or state change. ++ * If it is not match the latest one, updated by the policy state ++ * monitoring process, uAVC has to be reseted. ++ */ ++ ++/* ++ * Dynamic object class/access vector mapping ++ * ++ * SELinux exports the list of object classes (it means kind of object, like ++ * file or table) and access vectors (it means permission set, like read, ++ * select, ...) under /selinux/class. ++ * It enables to provide userspace object managers a interface to get what ++ * codes should be used to ask SELinux. ++ * ++ * libselinux provides an API to translate a string expression and a code ++ * used by the loaded security policy. These correspondences are not assured ++ * over the bound of policy loading, so we have to reload the mapping after ++ * in-kernel policy is reloaded, or its state is changed. ++ */ ++static struct ++{ ++ struct ++ { ++ const char *name; ++ security_class_t internal; ++ } tclass; ++ struct ++ { ++ char *name; ++ access_vector_t internal; ++ } av_perms[sizeof(access_vector_t) * 8]; ++} selinux_catalog[] = { ++ { ++ { "db_database", SECCLASS_DB_DATABASE}, ++ { ++ { "create", DB_DATABASE__CREATE }, ++ { "drop", DB_DATABASE__DROP }, ++ { "getattr", DB_DATABASE__GETATTR }, ++ { "setattr", DB_DATABASE__SETATTR }, ++ { "relabelfrom", DB_DATABASE__RELABELFROM }, ++ { "relabelto", DB_DATABASE__RELABELTO }, ++ { "access", DB_DATABASE__ACCESS }, ++ { "install_module", DB_DATABASE__INSTALL_MODULE }, ++ { "load_module", DB_DATABASE__LOAD_MODULE }, ++ { "get_param", DB_DATABASE__GET_PARAM }, ++ { "set_param", DB_DATABASE__SET_PARAM }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_table", SECCLASS_DB_TABLE}, ++ { ++ { "create", DB_TABLE__CREATE }, ++ { "drop", DB_TABLE__DROP }, ++ { "getattr", DB_TABLE__GETATTR }, ++ { "setattr", DB_TABLE__SETATTR }, ++ { "relabelfrom", DB_TABLE__RELABELFROM }, ++ { "relabelto", DB_TABLE__RELABELTO }, ++ { "use", DB_TABLE__USE }, ++ { "select", DB_TABLE__SELECT }, ++ { "update", DB_TABLE__UPDATE }, ++ { "insert", DB_TABLE__INSERT }, ++ { "delete", DB_TABLE__DELETE }, ++ { "lock", DB_TABLE__LOCK }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_procedure", SECCLASS_DB_PROCEDURE}, ++ { ++ { "create", DB_PROCEDURE__CREATE }, ++ { "drop", DB_PROCEDURE__DROP }, ++ { "getattr", DB_PROCEDURE__GETATTR }, ++ { "setattr", DB_PROCEDURE__SETATTR }, ++ { "relabelfrom", DB_PROCEDURE__RELABELFROM }, ++ { "relabelto", DB_PROCEDURE__RELABELTO }, ++ { "execute", DB_PROCEDURE__EXECUTE }, ++ { "entrypoint", DB_PROCEDURE__ENTRYPOINT }, ++ { "install", DB_PROCEDURE__INSTALL }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_column", SECCLASS_DB_COLUMN}, ++ { ++ { "create", DB_COLUMN__CREATE }, ++ { "drop", DB_COLUMN__DROP }, ++ { "getattr", DB_COLUMN__GETATTR }, ++ { "setattr", DB_COLUMN__SETATTR }, ++ { "relabelfrom", DB_COLUMN__RELABELFROM }, ++ { "relabelto", DB_COLUMN__RELABELTO }, ++ { "use", DB_COLUMN__USE }, ++ { "select", DB_COLUMN__SELECT }, ++ { "update", DB_COLUMN__UPDATE }, ++ { "insert", DB_COLUMN__INSERT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_tuple", SECCLASS_DB_TUPLE }, ++ { ++ { "relabelfrom", DB_TUPLE__RELABELFROM}, ++ { "relabelto", DB_TUPLE__RELABELTO}, ++ { "use", DB_TUPLE__USE}, ++ { "select", DB_TUPLE__SELECT}, ++ { "update", DB_TUPLE__UPDATE}, ++ { "insert", DB_TUPLE__INSERT}, ++ { "delete", DB_TUPLE__DELETE}, ++ { NULL, 0UL}, ++ } ++ }, ++ { ++ { "db_blob", SECCLASS_DB_BLOB }, ++ { ++ { "create", DB_BLOB__CREATE}, ++ { "drop", DB_BLOB__DROP}, ++ { "getattr", DB_BLOB__GETATTR}, ++ { "setattr", DB_BLOB__SETATTR}, ++ { "relabelfrom", DB_BLOB__RELABELFROM}, ++ { "relabelto", DB_BLOB__RELABELTO}, ++ { "read", DB_BLOB__READ}, ++ { "write", DB_BLOB__WRITE}, ++ { "import", DB_BLOB__IMPORT}, ++ { "export", DB_BLOB__EXPORT}, ++ { NULL, 0UL}, ++ } ++ }, ++}; ++ ++static MemoryContext AvcMemCtx; ++ ++#define AVC_HASH_NUM_SLOTS 256 ++#define AVC_HASH_NUM_NODES 600 ++ ++static sig_atomic_t avc_version; ++static bool avc_enforcing; ++ ++typedef struct ++{ ++ uint32 hash_key; ++ ++ security_class_t tclass; ++ Oid tsid; /* target security context */ ++ ++ security_context_t ncontext; /* newcontext */ ++ Oid nsid; /* newly created security context */ ++ ++ access_vector_t allowed; ++ access_vector_t decided; ++ access_vector_t auditallow; ++ access_vector_t auditdeny; ++ ++ bool hot_cache; ++} avc_datum; ++ ++typedef struct avc_page ++{ ++ struct avc_page *prev; ++ struct avc_page *next; ++ ++ security_context_t scontext; ++ ++ List *slot[AVC_HASH_NUM_SLOTS]; ++ ++ uint32 lru_hint; ++} avc_page; ++ ++static avc_page *current_avc_page = NULL; ++static uint32 avc_datum_count = 0; ++ ++/* ++ * selinux_state ++ * ++ * This structure shows the global state of SELinux and its security ++ * policy, and it is assigned on shared memory region. ++ * ++ * The most significant variable is selinux_state->version. ++ * Any instance can refer this variable to confirm current sequence ++ * number of policy state, without locking. ++ * ++ * The only process able to update this variable is policy state ++ * monitoring process forked by postmaster. It can receive notifications ++ * from the kernel via netlink socket, and it update selinux_state->version ++ * to encourage any instance to reflush its uAVC. ++ * ++ * When we read rest of variable, we have to hold SepgsqlAvcLock LWlock ++ * as a reader. enforceing shows the current SELinux working mode. ++ * catalog shows the mapping set of security classes and access vectors. ++ */ ++struct ++{ ++ /* ++ * only state monitoring process can update version. ++ * any other process can read it without locks. ++ */ ++ volatile sig_atomic_t version; ++ ++ bool enforcing; ++ ++ struct ++ { ++ struct ++ { ++ security_class_t internal; ++ security_class_t external; ++ } tclass; ++ struct ++ { ++ access_vector_t internal; ++ access_vector_t external; ++ } av_perms[sizeof(access_vector_t) * 8]; ++ } catalog[lengthof(selinux_catalog)]; ++} *selinux_state = NULL; ++ ++Size ++sepgsqlShmemSize(void) ++{ ++ return sizeof(*selinux_state); ++} ++ ++/* ++ * load_class_av_mapping ++ * ++ * This function rebuild the mapping set of security classes and access ++ * vectors on selinux_state. It has to be invoked by the policy state ++ * monitoring process with SepgsqlAvcLock in LW_EXCLUSIVE. ++ */ ++static void ++load_class_av_mapping(void) ++{ ++ int i, j; ++ ++ memset(selinux_state->catalog, 0, sizeof(selinux_state->catalog)); ++ ++ for (i = 0; i < lengthof(selinux_catalog); i++) ++ { ++ selinux_state->catalog[i].tclass.internal ++ = selinux_catalog[i].tclass.internal; ++ selinux_state->catalog[i].tclass.external ++ = string_to_security_class(selinux_catalog[i].tclass.name); ++ ++ for (j = 0; selinux_catalog[i].av_perms[j].name; j++) ++ { ++ selinux_state->catalog[i].av_perms[j].internal ++ = selinux_catalog[i].av_perms[j].internal; ++ selinux_state->catalog[i].av_perms[j].external ++ = string_to_av_perm(selinux_state->catalog[i].tclass.external, ++ selinux_catalog[i].av_perms[j].name); ++ } ++ } ++} ++ ++/* ++ * trans_to_external_tclass ++ * translates internal object class number into external one ++ * needed to communicate with in-kernel SELinux. ++ */ ++static security_class_t ++trans_to_external_tclass(security_class_t i_tclass) ++{ ++ /* have to hold SepgsqlAvcLock with LW_SHARED */ ++ int i; ++ ++ for (i = 0; i < lengthof(selinux_catalog); i++) ++ { ++ if (selinux_state->catalog[i].tclass.internal == i_tclass) ++ return selinux_state->catalog[i].tclass.external; ++ } ++ return i_tclass; /* use it as is for kernel classes */ ++} ++ ++/* ++ * trans_to_internal_perms ++ * translates external permission bits into internal ones ++ * needed to understand the answer from in-kernel SELinux. ++ * If in-kernel SELinux doesn't define required permissions, ++ * it sets/clears undefined bits based on caller's preference. ++ * It enables SE-PostgreSQL to work on legacy security policy. ++ */ ++static access_vector_t ++trans_to_internal_perms(security_class_t e_tclass, access_vector_t e_perms, ++ bool set_if_undefined) ++{ ++ /* have to hold SepgsqlAvcLock with LW_SHARED */ ++ access_vector_t i_perms = 0UL; ++ access_vector_t undef_mask = 0UL; ++ int i, j; ++ ++ for (i = 0; i < lengthof(selinux_catalog); i++) ++ { ++ if (selinux_state->catalog[i].tclass.external != e_tclass) ++ continue; ++ ++ for (j = 0; j < sizeof(access_vector_t) * 8; j++) ++ { ++ if (selinux_state->catalog[i].av_perms[j].external == 0) ++ undef_mask |= (1UL << j); ++ else if (selinux_state->catalog[i].av_perms[j].external & e_perms) ++ i_perms |= selinux_state->catalog[i].av_perms[j].internal; ++ } ++ ++ if (set_if_undefined) ++ i_perms |= undef_mask; ++ else ++ i_perms &= ~undef_mask; ++ ++ return i_perms; ++ } ++ return e_perms; /* use it as is for kernel classes */ ++} ++ ++/* ++ * sepgsql_class_to_string ++ * sepgsql_av_perm_to_string ++ * returns string representation of given object class and permission. ++ * Please note that given code have internal ones, so we cannot use ++ * libselinux's facility, because it assumes 'external code'. ++ * (Kernel object classes are ABI, so these are stable.) ++ */ ++static const char * ++sepgsql_class_to_string(security_class_t tclass) ++{ ++ int i; ++ ++ for (i = 0; i < lengthof(selinux_catalog); i++) ++ { ++ if (selinux_catalog[i].tclass.internal == tclass) ++ return selinux_catalog[i].tclass.name; ++ } ++ /* ++ * tclass is stable for kernel object classes. ++ */ ++ return security_class_to_string(tclass); ++} ++ ++static const char * ++sepgsql_av_perm_to_string(security_class_t tclass, access_vector_t perm) ++{ ++ int i, j; ++ ++ for (i = 0; i < lengthof(selinux_catalog); i++) ++ { ++ if (selinux_catalog[i].tclass.internal == tclass) ++ { ++ char *perm_name; ++ ++ for (j = 0; (perm_name = selinux_catalog[i].av_perms[j].name); j++) ++ { ++ if (selinux_catalog[i].av_perms[j].internal == perm) ++ return perm_name; ++ } ++ return "unknown"; ++ } ++ } ++ /* ++ * tclass/perms are stable for kernel object classes. ++ */ ++ return security_av_perm_to_string(tclass, perm); ++} ++ ++/* ++ * sepgsql_avc_reset ++ * clears all uAVC entries and update its version. ++ */ ++static void ++sepgsql_avc_reset(void) ++{ ++ MemoryContextReset(AvcMemCtx); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ ++ avc_version = selinux_state->version; ++ switch (sepostgresql_mode) ++ { ++ case SEPGSQL_MODE_DEFAULT: ++ avc_enforcing = selinux_state->enforcing; ++ break; ++ case SEPGSQL_MODE_PERMISSIVE: ++ avc_enforcing = false; ++ break; ++ case SEPGSQL_MODE_ENFORCING: ++ avc_enforcing = false; ++ break; ++ default: ++ elog(FATAL, "SELinux: undefined state in SE-PostgreSQL"); ++ break; ++ } ++ ++ current_avc_page = NULL; ++ ++ avc_datum_count = 0; ++ ++ LWLockRelease(SepgsqlAvcLock); ++ ++ sepgsqlAvcSwitchClientContext(sepgsqlGetClientContext()); ++} ++ ++/* ++ * sepgsql_avc_reclaim ++ * reclaims recently unused uAVC entries, when the number of ++ * caches overs AVC_HASH_NUM_NODES. ++ */ ++static void ++sepgsql_avc_reclaim(void) ++{ ++ ListCell *l; ++ avc_page *avp; ++ avc_datum *cache; ++ int loop; ++ ++ Assert(current_avc_page != NULL); ++ ++ for (avp = current_avc_page->next; true; avp = avp->next) ++ { ++ for (loop = 0; loop < AVC_HASH_NUM_SLOTS; loop++) ++ { ++ if (avc_datum_count < AVC_HASH_NUM_NODES) ++ return; ++ ++ avp->lru_hint = (avp->lru_hint + 1) % AVC_HASH_NUM_SLOTS; ++ foreach (l, avp->slot[avp->lru_hint]) ++ { ++ cache = lfirst(l); ++ ++ if (cache->hot_cache) ++ { ++ cache->hot_cache = false; ++ continue; ++ } ++ ++ list_delete_ptr(avp->slot[avp->lru_hint], cache); ++ pfree(cache); ++ avc_datum_count--; ++ } ++ } ++ } ++} ++ ++/* ++ * avc_audit_common ++ * generates an audit message on the give string buffer based on ++ * the given av_decision which means the resutl of permission checks. ++ */ ++static bool ++avc_audit_common(char *buffer, uint32 buflen, ++ avc_datum *cache, access_vector_t perms, ++ const char *scontext, const char *tcontext, const char *objname) ++{ ++ access_vector_t denied, audited, mask; ++ security_context_t svcon, tvcon; ++ uint32 ofs = 0; ++ ++ denied = perms & ~cache->allowed; ++ audited = denied ? (denied & cache->auditdeny) : (perms & cache->auditallow); ++ ++ if (audited == 0) ++ return false; ++ ++ ofs += snprintf(buffer + ofs, buflen - ofs, "%s {", ++ denied ? "denied" : "granted"); ++ for (mask = 1; mask != 0; mask <<= 1) ++ { ++ if ((audited & mask) != 0) ++ ofs += snprintf(buffer + ofs, buflen - ofs, " %s", ++ sepgsql_av_perm_to_string(cache->tclass, mask)); ++ } ++ ofs += snprintf(buffer + ofs, buflen - ofs, " } "); ++ ++ if (!scontext) ++ svcon = sepgsqlTranslateSecurityLabelOut(current_avc_page->scontext); ++ else ++ svcon = sepgsqlTranslateSecurityLabelOut(scontext); ++ ++ if (!tcontext) ++ tvcon = pgaceSidToSecurityLabel(cache->tsid); ++ else ++ tvcon = sepgsqlTranslateSecurityLabelOut(tcontext); ++ ++ ofs += snprintf(buffer + ofs, buflen - ofs, ++ "scontext=%s tcontext=%s tclass=%s", ++ svcon, tvcon, sepgsql_class_to_string(cache->tclass)); ++ ++ pfree(svcon); ++ pfree(tvcon); ++ if (objname) ++ ofs += snprintf(buffer + ofs, buflen - ofs, " name=%s", objname); ++ ++ return true; ++} ++ ++/* ++ * avc_permission_common ++ * makes decision and output audit messages based on given avc_datum. ++ * If required permissions are not completely allowed, it raises an ++ * error or returns 'false' when permissive mode. ++ */ ++static bool ++avc_permission_common(avc_datum *cache, access_vector_t perms, bool abort, ++ const char *scontext, const char *tcontext, const char *objname) ++{ ++ char audit_buffer[2048]; ++ access_vector_t denied; ++ bool audit; ++ bool rc = true; ++ ++ audit = avc_audit_common(audit_buffer, sizeof(audit_buffer), ++ cache, perms, scontext, tcontext, objname); ++ ++ denied = perms & ~cache->allowed; ++ if (!perms || denied) ++ { ++ if (avc_enforcing) ++ rc = false; ++ else ++ { ++ /* ++ * In permissive mode, once denied permissions are ++ * allowed to avoid a flood of denied logs. ++ */ ++ cache->allowed |= perms; ++ } ++ } ++ ++ if (audit) ++ { ++ ereport((!rc && abort) ? ERROR : NOTICE, ++ (errcode(ERRCODE_SELINUX_AUDIT), ++ errmsg("SELinux: %s", audit_buffer))); ++ } ++ else if (!rc && abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_AUDIT), ++ errmsg("SELinux: security policy violation"))); ++ ++ return rc; ++} ++ ++/* ++ * avc_make_entry ++ * makes a query to in-kernel SELinux and an avc_datum object to ++ * cache the result of SELinux's decision for access rights and ++ * default security context. ++ */ ++#define avc_hash_key(tsid,tclass) ((tsid) ^ ((tclass) << 2)) ++ ++static avc_datum * ++avc_make_entry(Oid tsid, security_class_t tclass) ++{ ++ security_context_t scontext, tcontext, ncontext; ++ security_class_t e_tclass; ++ MemoryContext oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ struct av_decision avd; ++ avc_datum *cache; ++ uint32 hash_key, index; ++ ++ hash_key = avc_hash_key(tsid, tclass); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ cache = palloc0(sizeof(avc_datum)); ++ cache->hash_key = hash_key; ++ cache->tsid = tsid; ++ cache->tclass = tclass; ++ ++ scontext = current_avc_page->scontext; ++ tcontext = pgaceLookupSecurityLabel(tsid); ++ if (!tcontext || !pgaceCheckValidSecurityLabel(tcontext)) ++ tcontext = pgaceUnlabeledSecurityLabel(); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ ++ e_tclass = trans_to_external_tclass(tclass); ++ ++ if (security_compute_av_raw(scontext, tcontext, e_tclass, 0, &avd) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not compute av_decision: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, sepgsql_class_to_string(tclass)))); ++ ++ cache->allowed = trans_to_internal_perms(e_tclass, avd.allowed, true); ++ cache->decided = trans_to_internal_perms(e_tclass, avd.decided, false); ++ cache->auditallow = trans_to_internal_perms(e_tclass, avd.auditallow, false); ++ cache->auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny, false); ++ cache->hot_cache = true; ++ ++ if (security_compute_create_raw(scontext, tcontext, e_tclass, &ncontext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not compute new context: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, sepgsql_class_to_string(tclass)))); ++ pfree(tcontext); ++ ++ LWLockRelease(SepgsqlAvcLock); ++ ++ PG_TRY(); ++ { ++ cache->ncontext = pstrdup(ncontext); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ freecon(ncontext); ++ ++ sepgsql_avc_reclaim(); ++ ++ current_avc_page->slot[index] ++ = lcons(cache, current_avc_page->slot[index]); ++ ++ avc_datum_count++; ++ ++ MemoryContextSwitchTo(oldctx); ++ ++ return cache; ++} ++ ++static avc_datum *avc_lookup(Oid tsid, security_class_t tclass) ++{ ++ avc_datum *cache = NULL; ++ uint32 hash_key, index; ++ ListCell *l; ++ ++ /* ++ * check avc invalidation ++ */ ++ if (avc_version != selinux_state->version) ++ sepgsql_avc_reset(); ++ ++ /* ++ * lookup avc entry ++ */ ++ hash_key = avc_hash_key(tsid, tclass); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ foreach (l, current_avc_page->slot[index]) ++ { ++ cache = lfirst(l); ++ if (cache->hash_key == hash_key ++ && cache->tclass == tclass ++ && cache->tsid == tsid) ++ { ++ cache->hot_cache = true; ++ return cache; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * sepgsqlAvcSwitchClientContext() ++ * switches current avc_page. ++ * ++ * NOTE: In most cases, SE-PostgreSQL checks whether client is allowed ++ * to do required actions (like SELECT, UPDATE, ...) on the targets. ++ * Both of client and targets have its security context, and all rules ++ * are described as relationship between security context of a client, ++ * a target and kind of actions. ++ * However, the security context of client is unchanged in SE-PostgreSQL ++ * (an exception is invocation of trusted procedure), so we can omit ++ * to compare security context of client with entries of uAVC. ++ * The avc_page is a set of avc_datum sorted out by the security context ++ * of client, so we can lookup correct avc_datum on currently focued ++ * avc_page without comparing the security context of client. ++ * The reason why we don't not use a unique uAVC is the security context ++ * of client does not have its security identifier on pg_security, so ++ * it requires strcmp() for each entries, but it is heavier than integer ++ * comparisons. ++ * Thus we have to switch current avc_page, whenever the security context ++ * of client changes (via trusted procedure). It makes performance well ++ * in most cases. ++ */ ++void sepgsqlAvcSwitchClientContext(security_context_t newcontext) ++{ ++ MemoryContext oldctx; ++ avc_page *avp; ++ int i; ++ ++ if (current_avc_page) ++ { ++ avp = current_avc_page; ++ do { ++ if (!strcmp(avp->scontext, newcontext)) ++ { ++ current_avc_page = avp; ++ return; ++ } ++ avp = avp->next; ++ } while (avp != current_avc_page); ++ } ++ ++ /* create a new avc_page */ ++ oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ avp = palloc0(sizeof(avc_page)); ++ avp->scontext = pstrdup(newcontext); ++ MemoryContextSwitchTo(oldctx); ++ ++ for (i=0; i < AVC_HASH_NUM_SLOTS; i++) ++ avp->slot[i] = NIL; ++ ++ if (!current_avc_page) ++ { ++ avp->next = avp->prev = avp; ++ } ++ else ++ { ++ avp->next = current_avc_page; ++ avp->prev = current_avc_page->prev; ++ avp->prev->next = avp; ++ avp->next->prev = avp; ++ } ++ current_avc_page = avp; ++} ++ ++/* ++ * sepgsqlClientHasPermission ++ * checks client's privileges on given objects via uAVC. ++ * It raised an error, if required actions are violated. ++ */ ++void ++sepgsqlClientHasPermission(Oid tsid, security_class_t tclass, ++ access_vector_t perms, ++ const char *objname) ++{ ++ avc_datum *cache = avc_lookup(tsid, tclass); ++ ++ if (!cache) ++ cache = avc_make_entry(tsid, tclass); ++ ++ avc_permission_common(cache, perms, true, NULL, NULL, objname); ++} ++ ++/* ++ * sepgsqlClientHasPermissionNoAbort ++ * checks client's privileges on given objects via uAVC. ++ * It returns false, if required actions are violated. ++ */ ++bool ++sepgsqlClientHasPermissionNoAbort(Oid tsid, security_class_t tclass, ++ access_vector_t perms, ++ const char *objname) ++{ ++ avc_datum *cache = avc_lookup(tsid, tclass); ++ ++ if (!cache) ++ cache = avc_make_entry(tsid, tclass); ++ ++ return avc_permission_common(cache, perms, false, NULL, NULL, objname); ++} ++ ++/* ++ * sepgsqlClientCreateSid ++ * returns security identifier of newly created database object. ++ * Please note that you don't have to invoke this function for ++ * object classes except for database objects. It have a possibility ++ * to make an entry on pg_security via pgaceSecurityLabelToSid(), ++ * but it should be restricted to database object. ++ */ ++Oid ++sepgsqlClientCreateSid(Oid tsid, security_class_t tclass) ++{ ++ avc_datum *cache = avc_lookup(tsid, tclass); ++ ++ if (!cache || cache->nsid == InvalidOid) ++ { ++ if (!cache) ++ cache = avc_make_entry(tsid, tclass); ++ cache->nsid = pgaceSecurityLabelToSid(cache->ncontext); ++ } ++ return cache->nsid; ++} ++ ++/* ++ * sepgsqlClientCreateContext ++ * returns security context (string representation) of newly ++ * created object. It is available for any kind of object ++ * classes. ++ */ ++security_context_t ++sepgsqlClientCreateContext(Oid tsid, security_class_t tclass) ++{ ++ avc_datum *cache = avc_lookup(tsid, tclass); ++ ++ if (!cache) ++ cache = avc_make_entry(tsid, tclass); ++ ++ return pstrdup(cache->ncontext); ++} ++ ++/* ++ * sepgsql_shmem_init ++ * attaches shared memory segment. ++ */ ++static void ++sepgsql_shmem_init(void) ++{ ++ bool found; ++ ++ selinux_state = ShmemInitStruct("SELinux policy state", ++ sepgsqlShmemSize(), &found); ++ if (!found) ++ { ++ int enforcing = security_getenforce(); ++ ++ Assert(enforcing == 0 || enforcing == 1); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->version = 0; ++ selinux_state->enforcing = enforcing; ++ load_class_av_mapping(); ++ ++ LWLockRelease(SepgsqlAvcLock); ++ } ++} ++ ++/* ++ * sepgsqlAvcInit ++ * initialize local uAVC facility. ++ */ ++void ++sepgsqlAvcInit(void) ++{ ++ /* ++ * local memory context ++ */ ++ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, ++ "SE-PostgreSQL userspace avc", ++ ALLOCSET_DEFAULT_MINSIZE, ++ ALLOCSET_DEFAULT_INITSIZE, ++ ALLOCSET_DEFAULT_MAXSIZE); ++ sepgsql_shmem_init(); ++ ++ /* ++ * reset local avc ++ */ ++ sepgsql_avc_reset(); ++} ++ ++/* ++ * sepgsqlComputePermission ++ * sepgsqlComputeCreateContext ++ * ++ * The following two functions make a query to in-kernel SELinux ++ * without userspace caches, due to some reasons. ++ * The uAVC can cover most of cases, but some of corner cases are ++ * not suitable for uAVC structure, so we need uncached interfaces. ++ * For example, uAVC is unavailable when we tries to load a shared ++ * library module, because security context of the library does not ++ * have its security identifier, so we cannot put it on uAVC. ++ */ ++bool ++sepgsqlComputePermission(const security_context_t scontext, ++ const security_context_t tcontext, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname) ++{ ++ security_context_t svcon, tvcon; ++ security_class_t e_tclass; ++ struct av_decision avd; ++ avc_datum cache; ++ bool rc; ++ ++ svcon = (!security_check_context_raw(scontext) ++ ? scontext : sepgsqlGetUnlabeledContext()); ++ tvcon = (!security_check_context_raw(tcontext) ++ ? tcontext : sepgsqlGetUnlabeledContext()); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ e_tclass = trans_to_external_tclass(tclass); ++ ++ if (security_compute_av_raw(svcon, tvcon, e_tclass, 0, &avd) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not compute an av_decision" ++ " scontext=%s tcontext=%s tclass=%s", ++ svcon, tvcon, security_class_to_string(e_tclass)))); ++ ++ cache.tclass = tclass; ++ cache.allowed = trans_to_internal_perms(e_tclass, avd.allowed, true); ++ cache.decided = trans_to_internal_perms(e_tclass, avd.decided, false); ++ cache.auditallow = trans_to_internal_perms(e_tclass, avd.auditallow, false); ++ cache.auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny, false); ++ LWLockRelease(SepgsqlAvcLock); ++ ++ rc = avc_permission_common(&cache, perms, true, svcon, tvcon, objname); ++ ++ if (svcon != scontext) ++ pfree(svcon); ++ if (tvcon != tcontext) ++ pfree(tvcon); ++ ++ return rc; ++} ++ ++security_context_t ++sepgsqlComputeCreateContext(const security_context_t scontext, ++ const security_context_t tcontext, ++ security_class_t tclass) ++{ ++ security_context_t svcon, tvcon, nwcon, copy; ++ security_class_t e_tclass; ++ ++ svcon = (!security_check_context_raw(scontext) ++ ? scontext : sepgsqlGetUnlabeledContext()); ++ tvcon = (!security_check_context_raw(tcontext) ++ ? tcontext : sepgsqlGetUnlabeledContext()); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ e_tclass = trans_to_external_tclass(tclass); ++ ++ if (security_compute_create_raw(svcon, tvcon, e_tclass, &nwcon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not compute a default context" ++ " scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, security_class_to_string(e_tclass)))); ++ ++ LWLockRelease(SepgsqlAvcLock); ++ ++ if (svcon != scontext) ++ pfree(svcon); ++ if (tvcon != tcontext) ++ pfree(tvcon); ++ ++ PG_TRY(); ++ { ++ copy = pstrdup(nwcon); ++ } ++ PG_CATCH(); ++ { ++ freecon(nwcon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ freecon(nwcon); ++ ++ return copy; ++} ++ ++/* ++ * SELinux state monitoring process ++ * ++ * This process is forked from postmaster to monitor the state of SELinux. ++ * SELinux can make a notifier message to userspace object manager via ++ * netlink socket. When it receives the message, it updates selinux_state ++ * structure assigned on shared memory region to make any instance reset ++ * its AVC soon. ++ */ ++ ++static bool sepgsqlStateMonitorAlive = true; ++ ++static void ++sepgsqlStateMonitorSIGHUP(SIGNAL_ARGS) ++{ ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_INFO), ++ errmsg("SELinux: invalidate userspace avc"))); ++ selinux_state->version = selinux_state->version + 1; ++} ++ ++static int ++sepgsqlStateMonitorMain() ++{ ++ char buffer[2048]; ++ struct sockaddr_nl addr; ++ socklen_t addrlen; ++ struct nlmsghdr *nlh; ++ int rc, nl_sockfd; ++ ++ /* ++ * map shared memory segment ++ */ ++ sepgsql_shmem_init(); ++ ++ /* ++ * setup the signal handler ++ */ ++ pqinitmask(); ++ pqsignal(SIGHUP, sepgsqlStateMonitorSIGHUP); ++ pqsignal(SIGINT, SIG_IGN); ++ pqsignal(SIGTERM, exit); ++ pqsignal(SIGQUIT, exit); ++ pqsignal(SIGUSR1, SIG_IGN); ++ pqsignal(SIGUSR2, SIG_IGN); ++ pqsignal(SIGCHLD, SIG_DFL); ++ PG_SETMASK(&UnBlockSig); ++ ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_INFO), ++ errmsg("SELinux: policy state monitor process (pid: %u)", ++ getpid()))); ++ /* ++ * open netlink socket ++ */ ++ nl_sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX); ++ if (nl_sockfd < 0) ++ { ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not open netlink socket"))); ++ return 1; ++ } ++ memset(&addr, 0, sizeof(addr)); ++ addr.nl_family = AF_NETLINK; ++ addr.nl_groups = SELNL_GRP_AVC; ++ if (bind(nl_sockfd, (struct sockaddr *) &addr, sizeof(addr))) ++ { ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not bind netlink socket"))); ++ return 1; ++ } ++ ++ /* ++ * waiting loop ++ */ ++ while (sepgsqlStateMonitorAlive) ++ { ++ addrlen = sizeof(addr); ++ rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0, ++ (struct sockaddr *) &addr, &addrlen); ++ if (rc < 0) ++ { ++ if (errno == EINTR) ++ continue; ++ ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: error on netlink recvfrom(): %s", ++ strerror(errno)))); ++ return 1; ++ } ++ ++ if (addrlen != sizeof(addr)) ++ { ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: netlink address truncated (len=%d)", ++ addrlen))); ++ return 1; ++ } ++ ++ if (addr.nl_pid) ++ { ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: netlink received spoofed packet from: %u", ++ addr.nl_pid))); ++ continue; ++ } ++ ++ if (rc == 0) ++ { ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: netlink received EOF"))); ++ return 1; ++ } ++ ++ nlh = (struct nlmsghdr *) buffer; ++ if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned int) rc) ++ { ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: netlink incomplete message"))); ++ return 1; ++ } ++ ++ switch (nlh->nlmsg_type) ++ { ++ case SELNL_MSG_SETENFORCE: ++ { ++ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh); ++ ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_INFO), ++ errmsg("SELinux: setenforce notifier" ++ " (enforcing=%d)", msg->val))); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ load_class_av_mapping(); ++ ++ /* ++ * userspace avc invalidation ++ */ ++ selinux_state->version = selinux_state->version + 1; ++ selinux_state->enforcing = msg->val ? true : false; ++ ++ LWLockRelease(SepgsqlAvcLock); ++ break; ++ } ++ case SELNL_MSG_POLICYLOAD: ++ { ++ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh); ++ ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_INFO), ++ errmsg("policyload notifier (seqno=%d)", ++ msg->seqno))); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ load_class_av_mapping(); ++ /* ++ * userspace avc invalidation ++ */ ++ selinux_state->version = selinux_state->version + 1; ++ ++ LWLockRelease(SepgsqlAvcLock); ++ break; ++ } ++ case NLMSG_ERROR: ++ { ++ struct nlmsgerr *err = NLMSG_DATA(nlh); ++ ++ if (err->error == 0) ++ break; ++ ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: netlink error: %s", ++ strerror(-err->error)))); ++ return 1; ++ } ++ default: ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("netlink unknown message type (%d)", ++ nlh->nlmsg_type))); ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++pid_t ++sepgsqlStartupWorkerProcess(void) ++{ ++ pid_t chld; ++ ++ chld = fork(); ++ if (chld == 0) ++ { ++ ClosePostmasterPorts(false); ++ ++ on_exit_reset(); ++ ++ exit(sepgsqlStateMonitorMain()); ++ } ++ else if (chld > 0) ++ return chld; ++ ++ return (pid_t) 0; ++} +diff -rpNU3 base/src/backend/security/sepgsql/core.c sepgsql/src/backend/security/sepgsql/core.c +--- base/src/backend/security/sepgsql/core.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/core.c 2009-01-24 22:44:20.000000000 +0900 +@@ -0,0 +1,672 @@ ++ ++/* ++ * src/backend/security/sepgsqlCore.c ++ * SE-PostgreSQL core facilities ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_database.h" ++#include "catalog/pg_security.h" ++#include "libpq/libpq.h" ++#include "miscadmin.h" ++#include "security/pgace.h" ++#include "utils/builtins.h" ++#include "utils/syscache.h" ++#include ++ ++int sepostgresql_mode; ++char *sepostgresql_mode_string; ++ ++static security_context_t serverContext = NULL; ++static security_context_t clientContext = NULL; ++static security_context_t unlabeledContext = NULL; ++ ++const security_context_t ++sepgsqlGetServerContext(void) ++{ ++ Assert(serverContext != NULL); ++ return serverContext; ++} ++ ++const security_context_t ++sepgsqlGetClientContext(void) ++{ ++ Assert(clientContext != NULL); ++ return clientContext; ++} ++ ++const security_context_t ++sepgsqlGetDatabaseContext(void) ++{ ++ security_context_t result; ++ ++ if (IsBootstrapProcessingMode()) ++ { ++ static security_context_t dbcontext = NULL; ++ ++ if (!dbcontext) ++ { ++ if (security_compute_create_raw(sepgsqlGetClientContext(), ++ sepgsqlGetClientContext(), ++ SECCLASS_DB_DATABASE, ++ &dbcontext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get database context"))); ++ } ++ result = pstrdup(dbcontext); ++ } ++ else ++ { ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId); ++ ++ result = pgaceLookupSecurityLabel(HeapTupleGetSecLabel(tuple)); ++ if (!result || !pgaceCheckValidSecurityLabel(result)) ++ result = pgaceUnlabeledSecurityLabel(); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ return result; ++} ++ ++Oid ++sepgsqlGetDatabaseSecurityId(void) ++{ ++ Oid sid; ++ ++ if (IsBootstrapProcessingMode()) ++ { ++ security_context_t dcontext ++ = sepgsqlGetDatabaseContext(); ++ ++ sid = pgaceSecurityLabelToSid(dcontext); ++ } ++ else ++ { ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId); ++ ++ sid = HeapTupleGetSecLabel(tuple); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ return sid; ++} ++ ++const security_context_t ++sepgsqlGetUnlabeledContext(void) ++{ ++ if (unlabeledContext) ++ return unlabeledContext; ++ ++ if (security_get_initial_context_raw("unlabeled", &unlabeledContext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get unlabeled context"))); ++ ++ return unlabeledContext; ++} ++ ++const security_context_t ++sepgsqlSwitchClientContext(security_context_t new_context) ++{ ++ security_context_t original_context = clientContext; ++ ++ clientContext = new_context; ++ ++ sepgsqlAvcSwitchClientContext(new_context); ++ ++ return original_context; ++} ++ ++static void ++initContexts(void) ++{ ++ /* ++ * server context ++ */ ++ if (getcon_raw(&serverContext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get server process context"))); ++ ++ /* ++ * client context ++ */ ++ if (!MyProcPort) ++ { ++ /* ++ * When the proces is not invoked as a backend of clietnt, ++ * it works as a server process and as a client process ++ * in same time. ++ */ ++ clientContext = serverContext; ++ } ++ else ++ { ++ if (getpeercon_raw(MyProcPort->sock, &clientContext) < 0) ++ { ++ /* ++ * fallbacked security context ++ * ++ * When getpeercon() API does not obtain the context of ++ * peer process, SEPGSQL_FALLBACK_CONTEXT environment ++ * variable is used as an alternative security context ++ * of the peer. ++ * ++ * getpeercon() needs the following condition to fail: ++ * - Connection come from remote host, ++ * - and, there is no labeled ipsec configuration between ++ * localhost and remote host. ++ * - and, there is no static fallbacked context configuration ++ * for the remote host. ++ */ ++ char *fallback = getenv("SEPGSQL_FALLBACK_CONTEXT"); ++ ++ if (!fallback) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg ++ ("SELinux: could not get client process context"))); ++ ++ if (security_check_context(fallback) < 0 ++ || selinux_trans_to_raw_context(fallback, &clientContext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: %s is not a valid context", ++ fallback))); ++ } ++ } ++} ++ ++/* ++ * sepgsqlInitialize ++ * ++ * It initializes SE-PostgreSQL itself including assignment of shared ++ * memory segment, reset of AVC, obtaining the client/server security ++ * context and checks whether the client can access the required database, ++ * or not. ++ */ ++void ++sepgsqlInitialize(bool bootstrap) ++{ ++ char *dbname; ++ ++ initContexts(); ++ ++ sepgsqlAvcInit(); ++ ++ /* ++ * check db_database:{ access } ++ */ ++ if (IsBootstrapProcessingMode()) ++ dbname = "template1"; ++ else ++ { ++ Form_pg_database dbForm; ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database %u", ++ MyDatabaseId); ++ dbForm = (Form_pg_database) GETSTRUCT(tuple); ++ ++ dbname = pstrdup(NameStr(dbForm->datname)); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ sepgsqlComputePermission(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__ACCESS, ++ dbname); ++} ++ ++/* ++ * sepgsqlIsEnabled ++ * ++ * This function returns the state of SE-PostgreSQL when PGACE hooks ++ * are invoked, to prevent to call sepgsqlXXXX() functions when ++ * SE-PostgreSQL is disabled. ++ * ++ * We can config the state of SE-PostgreSQL in $PGDATA/postgresql.conf. ++ * The GUC option "sepostgresql" can have the following four parameter. ++ * ++ * - default : It always follows the in-kernel SELinux state. When it ++ * works in Enforcing mode, SE-PostgreSQL also works in ++ * Enforcing mode. Changes of in-kernel state are delivered ++ * to userspace SE-PostgreSQL soon, and SELinux state ++ * monitoring process updates it rapidly. ++ * - enforcing : It always works in Enforcing mode. In-kernel SELinux ++ * has to be enabled. ++ * - permissive : It always works in Permissive mode. In-kernel SELinux ++ * has to be enabled. ++ * - disabled : It disables SE-PostgreSQL feature. It works as if ++ * original PostgreSQL ++ */ ++const char *sepgsqlAssignModeString(const char *value, bool doit, GucSource source) ++{ ++ SepgsqlModeType config_mode = SEPGSQL_MODE_DEFAULT; ++ ++ if (strcmp(value, "default") == 0) ++ config_mode = SEPGSQL_MODE_DEFAULT; ++ else if (strcmp(value, "enforcing") == 0) ++ config_mode = SEPGSQL_MODE_ENFORCING; ++ else if (strcmp(value, "permissive") == 0) ++ config_mode = SEPGSQL_MODE_PERMISSIVE; ++ else if (strcmp(value, "disabled") == 0) ++ config_mode = SEPGSQL_MODE_DISABLED; ++ else ++ ereport(GUC_complaint_elevel(source), ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ "SELinux: unexpected mode: %s", value)); ++ if (doit) ++ sepostgresql_mode = config_mode; ++ ++ return value; ++} ++ ++bool ++sepgsqlIsEnabled(void) ++{ ++ static int enabled = -1; ++ ++ if (enabled < 0) ++ { ++ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) ++ enabled = 0; ++ else ++ { ++ enabled = is_selinux_enabled(); ++ if (enabled == 0 /* in-kernel SELinux is disabled */ ++ && sepostgresql_mode != SEPGSQL_MODE_DEFAULT) ++ { ++ ereport(FATAL, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: disabled in kernel, but sepostgresql = %s", ++ SEPGSQL_MODE_ENFORCING ? "enforcing" : "permissive"))); ++ } ++ } ++ } ++ ++ return enabled > 0 ? true : false; ++} ++ ++/* ++ * sepgsql_getcon(void) ++ * ++ * It returns security context of client ++ */ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ security_context_t context; ++ Datum labelTxt; ++ ++ if (pgace_feature != PGACE_FEATURE_SELINUX || !sepgsqlIsEnabled()) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: disabled now"))); ++ ++ if (selinux_raw_to_trans_context(clientContext, &context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not translate mls label"))); ++ PG_TRY(); ++ { ++ labelTxt = CStringGetTextDatum(context); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++ ++ PG_RETURN_DATUM(labelTxt); ++} ++ ++/* ++ * sepgsql_getcon(void) ++ * ++ * It returns security context of server process ++ */ ++Datum ++sepgsql_getservcon(PG_FUNCTION_ARGS) ++{ ++ security_context_t context; ++ Datum labelTxt; ++ ++ if (pgace_feature != PGACE_FEATURE_SELINUX || !sepgsqlIsEnabled()) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: disabled now"))); ++ ++ if (selinux_raw_to_trans_context(serverContext, &context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not translate mls label"))); ++ PG_TRY(); ++ { ++ labelTxt = CStringGetTextDatum(context); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++ ++ PG_RETURN_DATUM(labelTxt); ++} ++ ++static void ++parse_to_context(security_context_t context, ++ char **user, char **role, char **type, char **range) ++{ ++ security_context_t raw_context; ++ ++ if (pgace_feature != PGACE_FEATURE_SELINUX || !sepgsqlIsEnabled()) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: disabled now"))); ++ ++ if (selinux_trans_to_raw_context(context, &raw_context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not translate mls label"))); ++ PG_TRY(); ++ { ++ char *tmp; ++ ++ tmp = pstrdup(strtok(raw_context, ":")); ++ if (user) ++ *user = tmp; ++ tmp = pstrdup(strtok(NULL, ":")); ++ if (role) ++ *role = tmp; ++ tmp = pstrdup(strtok(NULL, ":")); ++ if (type) ++ *type = tmp; ++ if (is_selinux_mls_enabled()) ++ { ++ tmp = pstrdup(strtok(NULL, "\0")); ++ if (range) ++ *range = tmp; ++ } ++ else if (range) ++ *range = NULL; ++ } ++ PG_CATCH(); ++ { ++ freecon(raw_context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(raw_context); ++} ++ ++/* ++ * text sepgsql_get_user(text) ++ * ++ * It picks up the USER field of given security context. ++ */ ++Datum ++sepgsql_get_user(PG_FUNCTION_ARGS) ++{ ++ char *user; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ &user, NULL, NULL, NULL); ++ PG_RETURN_TEXT_P(CStringGetTextDatum(user)); ++} ++ ++/* ++ * text sepgsql_set_user(text, text) ++ * ++ * It replaces the USER field of given security context by the second argument. ++ */ ++Datum ++sepgsql_set_user(PG_FUNCTION_ARGS) ++{ ++ char *user, *role, *type, *range; ++ char buffer[1024]; ++ security_context_t newcon; ++ Datum result; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ &user, &role, &type, &range); ++ if (range) ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", ++ TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type, range); ++ else ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s", ++ TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type); ++ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not set a new user"))); ++ PG_TRY(); ++ { ++ result = CStringGetTextDatum(newcon); ++ } ++ PG_CATCH(); ++ { ++ freecon(newcon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(newcon); ++ ++ PG_RETURN_DATUM(result); ++} ++ ++/* ++ * text sepgsql_get_role(text) ++ * ++ * It picks up the ROLE field of given security context. ++ */ ++Datum ++sepgsql_get_role(PG_FUNCTION_ARGS) ++{ ++ char *role; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ NULL, &role, NULL, NULL); ++ PG_RETURN_TEXT_P(CStringGetTextDatum(role)); ++} ++ ++/* ++ * text sepgsql_set_user(text, text) ++ * ++ * It replaces the ROLE field of given security context by the second argument. ++ */ ++Datum ++sepgsql_set_role(PG_FUNCTION_ARGS) ++{ ++ char *user, *role, *type, *range; ++ char buffer[1024]; ++ security_context_t newcon; ++ Datum result; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ &user, &role, &type, &range); ++ if (range) ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", ++ user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type, range); ++ else ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s", ++ user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type); ++ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not set a new role"))); ++ PG_TRY(); ++ { ++ result = CStringGetTextDatum(newcon); ++ } ++ PG_CATCH(); ++ { ++ freecon(newcon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(newcon); ++ ++ PG_RETURN_DATUM(result); ++} ++ ++/* ++ * text sepgsql_get_type(text) ++ * ++ * It picks up the TYPE field of given security context. ++ */ ++Datum ++sepgsql_get_type(PG_FUNCTION_ARGS) ++{ ++ char *type; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ NULL, NULL, &type, NULL); ++ PG_RETURN_TEXT_P(CStringGetTextDatum(type)); ++} ++ ++/* ++ * text sepgsql_set_user(text, text) ++ * ++ * It replaces the TYPE field of given security context by the second argument. ++ */ ++Datum ++sepgsql_set_type(PG_FUNCTION_ARGS) ++{ ++ char *user, *role, *type, *range; ++ char buffer[1024]; ++ security_context_t newcon; ++ Datum result; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ &user, &role, &type, &range); ++ if (range) ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", ++ user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1)), range); ++ else ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s", ++ user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1))); ++ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not set a new type"))); ++ PG_TRY(); ++ { ++ result = CStringGetTextDatum(newcon); ++ } ++ PG_CATCH(); ++ { ++ freecon(newcon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(newcon); ++ ++ PG_RETURN_DATUM(result); ++} ++ ++/* ++ * text sepgsql_get_range(text) ++ * ++ * It picks up the RANGE field of given security context. ++ */ ++Datum ++sepgsql_get_range(PG_FUNCTION_ARGS) ++{ ++ char *range; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ NULL, NULL, NULL, &range); ++ PG_RETURN_TEXT_P(CStringGetTextDatum(range)); ++} ++ ++/* ++ * text sepgsql_set_user(text, text) ++ * ++ * It replaces the RANGE field of given security context by the second argument. ++ */ ++Datum ++sepgsql_set_range(PG_FUNCTION_ARGS) ++{ ++ char *user, *role, *type, *range; ++ char buffer[1024]; ++ security_context_t newcon; ++ Datum result; ++ ++ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), ++ &user, &role, &type, &range); ++ if (range) ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", ++ user, role, type, TextDatumGetCString(PG_GETARG_TEXT_P(1))); ++ else ++ snprintf(buffer, sizeof(buffer), "%s:%s:%s", user, role, type); ++ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not set a new range"))); ++ PG_TRY(); ++ { ++ result = CStringGetTextDatum(newcon); ++ } ++ PG_CATCH(); ++ { ++ freecon(newcon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(newcon); ++ ++ PG_RETURN_DATUM(result); ++} ++ ++/* ++ * SE-PostgreSQL legacy function support ++ */ ++Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS); ++Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS); ++ ++Datum ++sepgsql_tuple_perms(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("%s is no longer supported", __FUNCTION__))); ++ PG_RETURN_VOID(); ++} ++ ++Datum ++sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("%s is no longer supported", __FUNCTION__))); ++ PG_RETURN_VOID(); ++} +diff -rpNU3 base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c +--- base/src/backend/security/sepgsql/hooks.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/hooks.c 2009-02-26 21:08:58.000000000 +0900 +@@ -0,0 +1,1160 @@ ++/* ++ * src/backend/security/sepgsql/hooks.c ++ * implementations of PGACE framework ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/genam.h" ++#include "access/skey.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_aggregate.h" ++#include "catalog/pg_amproc.h" ++#include "catalog/pg_cast.h" ++#include "catalog/pg_conversion.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_operator.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_security.h" ++#include "catalog/pg_trigger.h" ++#include "catalog/pg_ts_parser.h" ++#include "catalog/pg_ts_template.h" ++#include "catalog/pg_type.h" ++#include "miscadmin.h" ++#include "nodes/makefuncs.h" ++#include "security/pgace.h" ++#include "utils/fmgroids.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++#include ++#include ++#include ++#include ++ ++/******************************************************************************* ++ * Extended SQL statement hooks ++ *******************************************************************************/ ++bool ++sepgsqlIsGramSecurityItem(DefElem *defel) ++{ ++ Assert(IsA(defel, DefElem)); ++ ++ if (defel->defname && ++ strcmp(defel->defname, SecurityLabelAttributeName) == 0) ++ return true; ++ ++ return false; ++} ++ ++static void ++putExplicitContext(HeapTuple tuple, DefElem *defel) ++{ ++ if (defel) ++ { ++ Oid sid = pgaceSecurityLabelToSid(strVal(defel->arg)); ++ ++ HeapTupleSetSecLabel(tuple, sid); ++ } ++} ++ ++void ++sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++void ++sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++void ++sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++void ++sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++void ++sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++void ++sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++void ++sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++void ++sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++ putExplicitContext(tuple, defel); ++} ++ ++/******************************************************************************* ++ * DATABASE object related hooks ++ *******************************************************************************/ ++ ++void ++sepgsqlGetDatabaseParam(const char *name) ++{ ++ HeapTuple tuple; ++ const char *audit_name; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database %u", ++ MyDatabaseId); ++ ++ audit_name = sepgsqlTupleName(DatabaseRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__GET_PARAM, ++ audit_name); ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsqlSetDatabaseParam(const char *name, char *argstring) ++{ ++ HeapTuple tuple; ++ const char *audit_name; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database %u", ++ MyDatabaseId); ++ ++ audit_name = sepgsqlTupleName(DatabaseRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__SET_PARAM, ++ audit_name); ++ ReleaseSysCache(tuple); ++} ++ ++/******************************************************************************* ++ * RELATION(Table)/ATTRIBTUE(column) object related hooks ++ *******************************************************************************/ ++void ++sepgsqlLockTable(Oid relid) ++{ ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); ++ ++ if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION) ++ { ++ const char *audit_name ++ = sepgsqlTupleName(RelationRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_TABLE, ++ DB_TABLE__LOCK, ++ audit_name); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsqlExecTruncate(List *trunc_rels) ++{ ++ ListCell *l; ++ ++ foreach (l, trunc_rels) ++ { ++ const char *audit_name; ++ HeapTuple tuple; ++ HeapScanDesc scan; ++ Relation rel = (Relation) lfirst(l); ++ ++ if (RelationGetForm(rel)->relkind != RELKIND_RELATION) ++ continue; ++ ++ /* ++ * check db_table:{delete} ++ */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(RelationGetRelid(rel)), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", ++ RelationGetRelid(rel)); ++ ++ audit_name = sepgsqlTupleName(RelationRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_TABLE, ++ DB_TABLE__DELETE, ++ audit_name); ++ ReleaseSysCache(tuple); ++ ++ /* ++ * check db_tuple:{delete} ++ */ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) ++ { ++ sepgsqlCheckTuplePerms(rel, tuple, NULL, ++ SEPGSQL_PERMS_DELETE, true); ++ } ++ heap_endscan(scan); ++ } ++} ++ ++/******************************************************************************* ++ * PROCEDURE related hooks ++ *******************************************************************************/ ++ ++typedef struct ++{ ++ PGFunction fn_addr; ++ security_context_t fn_con; ++} sepgsql_fn_info; ++ ++static Datum ++invokeTrustedProcedure(PG_FUNCTION_ARGS) ++{ ++ sepgsql_fn_info *sefinfo = fcinfo->flinfo->fn_pgaceItem; ++ security_context_t orig_context; ++ Datum retval; ++ ++ /* ++ * set new domain ++ */ ++ orig_context = sepgsqlSwitchClientContext(sefinfo->fn_con); ++ ++ PG_TRY(); ++ { ++ retval = sefinfo->fn_addr(fcinfo); ++ } ++ PG_CATCH(); ++ { ++ sepgsqlSwitchClientContext(orig_context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ sepgsqlSwitchClientContext(orig_context); ++ ++ return retval; ++} ++ ++void ++sepgsqlCallFunction(FmgrInfo *finfo) ++{ ++ MemoryContext oldctx; ++ HeapTuple tuple; ++ security_context_t newcon; ++ access_vector_t perms = DB_PROCEDURE__EXECUTE; ++ const char *audit_name; ++ ++ if (IsBootstrapProcessingMode()) ++ return; /* under initialization of pg_proc */ ++ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(finfo->fn_oid), ++ 0, 0, 0); ++ Assert(HeapTupleIsValid(tuple)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for procedure %u", finfo->fn_oid); ++ ++ oldctx = MemoryContextSwitchTo(finfo->fn_mcxt); ++ /* ++ * check trusted procedure ++ */ ++ newcon = sepgsqlClientCreateContext(HeapTupleGetSecLabel(tuple), ++ SECCLASS_PROCESS); ++ if (strcmp(newcon, sepgsqlGetClientContext()) != 0) ++ { ++ sepgsql_fn_info *sefinfo ++ = palloc0(sizeof(sepgsql_fn_info)); ++ ++ sefinfo->fn_addr = finfo->fn_addr; ++ sefinfo->fn_con = newcon; ++ finfo->fn_addr = invokeTrustedProcedure; ++ finfo->fn_pgaceItem = sefinfo; ++ ++ perms |= DB_PROCEDURE__ENTRYPOINT; ++ } ++ else ++ pfree(newcon); ++ ++ MemoryContextSwitchTo(oldctx); ++ ++ audit_name = sepgsqlTupleName(ProcedureRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_PROCEDURE, ++ perms, ++ audit_name); ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsqlCallAggFunction(HeapTuple aggTuple) ++{ ++ Form_pg_aggregate aggForm ++ = (Form_pg_aggregate) GETSTRUCT(aggTuple); ++ HeapTuple tuple; ++ const char *audit_name; ++ ++ /* check pg_proc.oid = pg_aggregate.aggfnoid */ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(aggForm->aggfnoid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for procedure %u", ++ aggForm->aggfnoid); ++ ++ audit_name = sepgsqlTupleName(ProcedureRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_PROCEDURE, ++ DB_PROCEDURE__EXECUTE, ++ audit_name); ++ ReleaseSysCache(tuple); ++} ++ ++bool ++sepgsqlCallTriggerFunction(TriggerData *tgdata) ++{ ++ Relation rel = tgdata->tg_relation; ++ HeapTuple newtup = NULL; ++ HeapTuple oldtup = NULL; ++ ++ /* ++ * We don't need to check tuple permissions for ++ * statement triggers ++ */ ++ if (TRIGGER_FIRED_FOR_STATEMENT(tgdata->tg_event)) ++ return true; ++ ++ if (TRIGGER_FIRED_BY_INSERT(tgdata->tg_event)) ++ { ++ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) ++ newtup = tgdata->tg_trigtuple; ++ } ++ else if (TRIGGER_FIRED_BY_UPDATE(tgdata->tg_event)) ++ { ++ oldtup = tgdata->tg_trigtuple; ++ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) ++ { ++ Oid securityId = HeapTupleGetSecLabel(tgdata->tg_newtuple); ++ ++ if (HeapTupleGetSecLabel(oldtup) != securityId) ++ newtup = tgdata->tg_newtuple; ++ } ++ } ++ else if (TRIGGER_FIRED_BY_DELETE(tgdata->tg_event)) ++ { ++ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) ++ oldtup = tgdata->tg_trigtuple; ++ } ++ else ++ { ++ elog(ERROR, "SELinux: unexpected trigger event type (%u)", ++ tgdata->tg_event); ++ } ++ if (oldtup && !sepgsqlCheckTuplePerms(rel, oldtup, NULL, ++ SEPGSQL_PERMS_SELECT, false)) ++ return false; ++ if (newtup && !sepgsqlCheckTuplePerms(rel, newtup, NULL, ++ SEPGSQL_PERMS_SELECT, false)) ++ return false; ++ ++ return true; ++} ++ ++bool sepgsqlAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple) ++{ ++ security_context_t newcon; ++ const char *audit_name; ++ ++ /* ++ * If function is defined as trusted procedure, we always should ++ * not allow it to be inlined, and actual permission checks are ++ * done later phase. ++ */ ++ newcon = sepgsqlClientCreateContext(HeapTupleGetSecLabel(func_tuple), ++ SECCLASS_PROCESS); ++ if (strcmp(newcon, sepgsqlGetClientContext()) != 0) ++ return false; ++ ++ audit_name = sepgsqlTupleName(ProcedureRelationId, func_tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(func_tuple), ++ SECCLASS_DB_PROCEDURE, ++ DB_PROCEDURE__EXECUTE, ++ audit_name); ++ return true; ++} ++ ++/* ++ * sepgsqlCheckProcedureInstall ++ * checks permission: db_procedure:{install}, when client tries to modify ++ * a system catalog which contains procedure id to invoke it later. ++ * Because these functions are invoked internally, to search a table with ++ * a special index algorithm for example, the security policy has to prevent ++ * malicious user-defined functions to be installed. ++ */ ++static void ++checkProcedureInstall(Oid proc_oid) ++{ ++ if (!OidIsValid(proc_oid)) ++ return; ++ ++ if (IsBootstrapProcessingMode()) ++ { ++ /* ++ * We assume all procedures have same security context ++ * in bootstrap processing mode, because no one can ++ * relabel it. ++ */ ++ Oid proc_sid ++ = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_PROCEDURE); ++ sepgsqlClientHasPermission(proc_sid, ++ SECCLASS_DB_PROCEDURE, ++ DB_PROCEDURE__INSTALL, ++ NULL); ++ } ++ else ++ { ++ HeapTuple protup; ++ const char *audit_name; ++ ++ protup = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(proc_oid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(protup)) ++ return; ++ ++ audit_name = sepgsqlTupleName(ProcedureRelationId, protup); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(protup), ++ SECCLASS_DB_PROCEDURE, ++ DB_PROCEDURE__INSTALL, ++ audit_name); ++ ReleaseSysCache(protup); ++ } ++} ++ ++#define CHECK_PROC_INSTALL_HANDLER(catalog,member,newtup,oldtup) \ ++ do { \ ++ if (!HeapTupleIsValid(oldtup)) \ ++ checkProcedureInstall(((Form_##catalog) GETSTRUCT(newtup))->member); \ ++ else if (((Form_##catalog) GETSTRUCT(newtup))->member \ ++ != ((Form_##catalog) GETSTRUCT(oldtup))->member) \ ++ checkProcedureInstall(((Form_##catalog) GETSTRUCT(oldtup))->member); \ ++ } while(0) ++ ++static void ++sepgsqlCheckProcedureInstall(Relation rel, HeapTuple newtup, HeapTuple oldtup) ++{ ++ /* ++ * Some of system catalog can be configured to invoke functions ++ * implicitly. It checks permission to prevent implicit invocation ++ * of malicious functions. ++ */ ++ switch (RelationGetRelid(rel)) ++ { ++ case AggregateRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggfnoid, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggtransfn, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggfinalfn, newtup, oldtup); ++ break; ++ ++ case AccessMethodRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_am, aminsert, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, ambeginscan, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amgettuple, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amgetmulti, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amrescan, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amendscan, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, ammarkpos, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amrestrpos, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, ambuild, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, ambulkdelete, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amvacuumcleanup, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amcostestimate, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_am, amoptions, newtup, oldtup); ++ break; ++ ++ case AccessMethodProcedureRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_amproc, amproc, newtup, oldtup); ++ break; ++ ++ case CastRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_cast, castfunc, newtup, oldtup); ++ break; ++ ++ case ConversionRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_conversion, conproc, newtup, oldtup); ++ break; ++ ++ case LanguageRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_language, lanplcallfoid, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_language, lanvalidator, newtup, oldtup); ++ break; ++ ++ case OperatorRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_operator, oprcode, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_operator, oprrest, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_operator, oprjoin, newtup, oldtup); ++ break; ++ ++ case TriggerRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_trigger, tgfoid, newtup, oldtup); ++ break; ++ ++ case TSParserRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsstart, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prstoken, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsend, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsheadline, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prslextype, newtup, oldtup); ++ break; ++ ++ case TSTemplateRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_ts_template, tmplinit, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_ts_template, tmpllexize, newtup, oldtup); ++ break; ++ ++ case TypeRelationId: ++ CHECK_PROC_INSTALL_HANDLER(pg_type, typinput, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_type, typoutput, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_type, typreceive, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_type, typsend, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_type, typmodin, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_type, typmodout, newtup, oldtup); ++ CHECK_PROC_INSTALL_HANDLER(pg_type, typanalyze, newtup, oldtup); ++ break; ++ } ++} ++ ++/******************************************************************************* ++ * LOAD shared library module hook ++ *******************************************************************************/ ++void ++sepgsqlLoadSharedModule(const char *filename) ++{ ++ security_context_t filecon; ++ ++ if (getfilecon_raw(filename, &filecon) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not access file \"%s\": %m", filename))); ++ PG_TRY(); ++ { ++ sepgsqlComputePermission(sepgsqlGetDatabaseContext(), ++ filecon, ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__LOAD_MODULE, ++ filename); ++ } ++ PG_CATCH(); ++ { ++ freecon(filecon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(filecon); ++} ++ ++/******************************************************************************* ++ * Binary Large Object hooks ++ *******************************************************************************/ ++ ++void ++sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple) ++{ ++ const char *audit_name; ++ ++ sepgsqlSetDefaultContext(rel, tuple); ++ ++ audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__CREATE, ++ audit_name); ++} ++ ++void ++sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem) ++{ ++ Oid security_id = HeapTupleGetSecLabel(tuple); ++ List *okList = (List *) (*pgaceItem); ++ ListCell *l; ++ const char *audit_name; ++ ++ foreach (l, okList) ++ { ++ if (security_id == lfirst_oid(l)) ++ return; /* already allowed */ ++ } ++ ++ audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple); ++ sepgsqlClientHasPermission(security_id, ++ SECCLASS_DB_BLOB, ++ DB_BLOB__DROP, ++ audit_name); ++ ++ *pgaceItem = lappend_oid(okList, security_id); ++} ++ ++static void ++checkLargeObjectPages(Oid loid, Snapshot snapshot, ++ int32 start_pageno, int32 end_pageno, ++ access_vector_t perms) ++{ ++ Relation rel; ++ HeapTuple tuple; ++ SysScanDesc sd; ++ ScanKeyData skey[2]; ++ List *okList = NIL; ++ ++ rel = heap_open(LargeObjectRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey[0], ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ if (start_pageno <= 0) ++ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, ++ true, snapshot, 1, skey); ++ else ++ { ++ ScanKeyInit(&skey[1], ++ Anum_pg_largeobject_pageno, ++ BTGreaterEqualStrategyNumber, F_INT4GE, ++ Int32GetDatum(start_pageno)); ++ ++ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, ++ true, snapshot, 2, skey); ++ } ++ ++ while ((tuple = systable_getnext(sd)) != NULL) ++ { ++ Form_pg_largeobject loForm ++ = (Form_pg_largeobject) GETSTRUCT(tuple); ++ Oid security_id; ++ ListCell *l; ++ const char *audit_name; ++ ++ if (end_pageno >= 0 && loForm->pageno > end_pageno) ++ break; ++ ++ security_id = HeapTupleGetSecLabel(tuple); ++ ++ foreach (l, okList) ++ { ++ if (security_id == lfirst_oid(l)) ++ goto skip; ++ } ++ okList = lappend_oid(okList, security_id); ++ ++ audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple); ++ sepgsqlClientHasPermission(security_id, ++ SECCLASS_DB_BLOB, ++ perms, ++ audit_name); ++ skip: ++ ; ++ } ++ systable_endscan(sd); ++ ++ list_free(okList); ++ ++ heap_close(rel, NoLock); ++} ++ ++void ++sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int32 length) ++{ ++ int32 start_pageno = lodesc->offset / LOBLKSIZE; ++ int32 end_pageno = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE; ++ ++ checkLargeObjectPages(lodesc->id, lodesc->snapshot, ++ start_pageno, end_pageno, DB_BLOB__READ); ++} ++ ++void ++sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int32 length) ++{ ++ int32 start_pageno = lodesc->offset / LOBLKSIZE; ++ int32 end_pageno = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE; ++ ++ checkLargeObjectPages(lodesc->id, lodesc->snapshot, ++ start_pageno, end_pageno, DB_BLOB__WRITE); ++} ++ ++void ++sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int32 offset) ++{ ++ int32 start_pageno = lodesc->offset / LOBLKSIZE; ++ ++ checkLargeObjectPages(lodesc->id, lodesc->snapshot, ++ start_pageno, -1, DB_BLOB__WRITE); ++} ++ ++void ++sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename) ++{ ++ security_context_t tcontext; ++ security_class_t tclass ++ = sepgsqlFileObjectClass(fdesc, filename); ++ ++ if (fgetfilecon_raw(fdesc, &tcontext) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not get security context \"%s\": %m", filename))); ++ PG_TRY(); ++ { ++ sepgsqlComputePermission(sepgsqlGetClientContext(), ++ tcontext, ++ tclass, ++ FILE__READ, ++ filename); ++ } ++ PG_CATCH(); ++ { ++ freecon(tcontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(tcontext); ++ ++ checkLargeObjectPages(loid, SnapshotNow, -1, -1, ++ DB_BLOB__WRITE | DB_BLOB__IMPORT); ++} ++ ++void ++sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename) ++{ ++ security_context_t tcontext; ++ security_class_t tclass ++ = sepgsqlFileObjectClass(fdesc, filename); ++ ++ if (fgetfilecon_raw(fdesc, &tcontext) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not security context \"%s\": %m", filename))); ++ PG_TRY(); ++ { ++ sepgsqlComputePermission(sepgsqlGetClientContext(), ++ tcontext, ++ tclass, ++ FILE__WRITE, ++ filename); ++ } ++ PG_CATCH(); ++ { ++ freecon(tcontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(tcontext); ++ ++ checkLargeObjectPages(loid, SnapshotNow, -1, -1, ++ DB_BLOB__READ | DB_BLOB__EXPORT); ++} ++ ++void ++sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple) ++{ ++ const char *audit_name ++ = sepgsqlTupleName(LargeObjectRelationId, tuple); ++ ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__GETATTR, ++ audit_name); ++} ++ ++void ++sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup) ++{ ++ const char *audit_name; ++ ++ if (HeapTupleGetSecLabel(newtup) == HeapTupleGetSecLabel(oldtup)) ++ return; ++ ++ audit_name = sepgsqlTupleName(LargeObjectRelationId, oldtup); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(oldtup), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__SETATTR | DB_BLOB__RELABELFROM, ++ audit_name); ++ /* ++ * check db_blob:{setattr relabelto} ++ */ ++ audit_name = sepgsqlTupleName(LargeObjectRelationId, newtup); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(newtup), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__RELABELTO, ++ audit_name); ++} ++ ++/******************************************************************************* ++ * ExecScan hooks ++ *******************************************************************************/ ++static bool abort_on_violated_tuple = false; ++ ++bool ++sepgsqlRowlvBehaviorSwitchTo(bool new_abort) ++{ ++ bool old_abort = abort_on_violated_tuple; ++ ++ abort_on_violated_tuple = new_abort; ++ ++ return old_abort; ++} ++ ++bool ++sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort) ++{ ++ HeapTuple tuple; ++ uint32 perms = (scan->pgaceTuplePerms & SEPGSQL_PERMS_MASK); ++ ++ if (abort_on_violated_tuple != abort) ++ return true; /* no need to do here */ ++ ++ if (perms == 0) ++ return true; ++ ++ tuple = ExecMaterializeSlot(slot); ++ ++ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, ++ abort_on_violated_tuple); ++} ++ ++/******************************************************************************* ++ * security_label hooks ++ *******************************************************************************/ ++bool ++sepgsqlTupleDescHasSecLabel(Relation rel, List *relopts) ++{ ++ /* ++ * Newly created table via SELECT INTO/CREATE TABLE AS ++ */ ++ if (rel == NULL) ++ return sepostgresql_row_level; ++ ++ if (RelationGetForm(rel)->relkind != RELKIND_RELATION && ++ RelationGetForm(rel)->relkind != RELKIND_SEQUENCE) ++ return false; ++ ++ if (RelationGetRelid(rel) == DatabaseRelationId || ++ RelationGetRelid(rel) == RelationRelationId || ++ RelationGetRelid(rel) == AttributeRelationId || ++ RelationGetRelid(rel) == ProcedureRelationId || ++ RelationGetRelid(rel) == LargeObjectRelationId) ++ return true; ++ ++ return sepostgresql_row_level; ++} ++ ++char * ++sepgsqlTranslateSecurityLabelIn(const char *context) ++{ ++ security_context_t i_context; ++ char *result; ++ ++ if (selinux_trans_to_raw_context((security_context_t) context, &i_context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not translate mls label"))); ++ PG_TRY(); ++ { ++ result = pstrdup(i_context); ++ } ++ PG_CATCH(); ++ { ++ freecon(i_context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(i_context); ++ ++ return result; ++} ++ ++char * ++sepgsqlTranslateSecurityLabelOut(const char *context) ++{ ++ security_context_t o_context; ++ char *result; ++ ++ if (selinux_raw_to_trans_context((security_context_t) context, &o_context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not translate mls label"))); ++ PG_TRY(); ++ { ++ result = pstrdup(o_context); ++ } ++ PG_CATCH(); ++ { ++ freecon(o_context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(o_context); ++ ++ return result; ++} ++ ++/* ++ * sepgsqlCheckValidSecurityLabel() checks whether the given ++ * security context is valid on the current working security ++ * policy, or not. ++ * If it's invalid, sepgsqlUnlabeledSecurityLabel() is invoked ++ * at the next to get an alternative security label. ++ */ ++bool ++sepgsqlCheckValidSecurityLabel(char *context) ++{ ++ if (security_check_context_raw((security_context_t) context) < 0) ++ return false; ++ ++ return true; ++} ++ ++char * ++sepgsqlUnlabeledSecurityLabel(void) ++{ ++ security_context_t unlabeled; ++ char *result; ++ ++ if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get unlabeled initial context"))); ++ PG_TRY(); ++ { ++ result = pstrdup(unlabeled); ++ } ++ PG_CATCH(); ++ { ++ freecon(unlabeled); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(unlabeled); ++ ++ return result; ++} ++ ++char * ++sepgsqlSecurityLabelOfLabel(void) ++{ ++ security_context_t table_context, tuple_context; ++ HeapTuple tuple; ++ ++ /* ++ * obtain security context of pg_security ++ */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(SecurityRelationId), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", ++ SecurityRelationId); ++ ++ table_context = pgaceLookupSecurityLabel(HeapTupleGetSecLabel(tuple)); ++ if (!table_context || !pgaceCheckValidSecurityLabel(table_context)) ++ table_context = pgaceUnlabeledSecurityLabel(); ++ ++ tuple_context = sepgsqlComputeCreateContext(sepgsqlGetServerContext(), ++ table_context, SECCLASS_DB_TUPLE); ++ pfree(table_context); ++ ++ ReleaseSysCache(tuple); ++ ++ return tuple_context; ++} ++ ++/****************************************************************** ++ * HeapTuple modification hooks ++ ******************************************************************/ ++static HeapTuple ++getHeapTupleFromItemPointer(Relation rel, ItemPointer tid) ++{ ++ /* ++ * obtain an old tuple ++ */ ++ Buffer buffer; ++ PageHeader dp; ++ ItemId lp; ++ HeapTupleData tuple; ++ HeapTuple oldtup; ++ ++ buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid)); ++ LockBuffer(buffer, BUFFER_LOCK_SHARE); ++ ++ dp = (PageHeader) BufferGetPage(buffer); ++ lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); ++ ++ Assert(ItemIdIsNormal(lp)); ++ ++ tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); ++ tuple.t_len = ItemIdGetLength(lp); ++ tuple.t_self = *tid; ++ tuple.t_tableOid = RelationGetRelid(rel); ++ oldtup = heap_copytuple(&tuple); ++ ++ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ++ ReleaseBuffer(buffer); ++ ++ return oldtup; ++} ++ ++static bool ++isTrustedRelation(Relation rel, bool is_internal) ++{ ++ if (!is_internal) ++ return false; ++ ++ if (RelationGetForm(rel)->relkind != RELKIND_RELATION) ++ return true; ++ ++ switch (RelationGetRelid(rel)) ++ { ++ case LargeObjectRelationId: ++ case SecurityRelationId: ++ return true; ++ } ++ return false; ++} ++ ++bool ++sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, ++ bool is_internal, bool with_returning) ++{ ++ uint32 perms; ++ ++ sepgsqlCheckProcedureInstall(rel, tuple, NULL); ++ ++ if (!OidIsValid(HeapTupleGetSecLabel(tuple))) ++ { ++ /* ++ * If user gives no valid security context, ++ * it assigns a default one on the new tuple. ++ */ ++ if (HeapTupleHasSecLabel(tuple)) ++ sepgsqlSetDefaultContext(rel, tuple); ++ } ++ else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: unable to insert " ++ "pg_largeobject.security_context"))); ++ ++ if (isTrustedRelation(rel, is_internal)) ++ return true; ++ ++ perms = SEPGSQL_PERMS_INSERT; ++ if (with_returning) ++ perms |= SEPGSQL_PERMS_SELECT; ++ ++ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, is_internal); ++} ++ ++bool ++sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, ++ bool is_internal, bool with_returning) ++{ ++ Oid relid = RelationGetRelid(rel); ++ HeapTuple oldtup; ++ uint32 perms = 0; ++ bool rc = true; ++ bool relabel = false; ++ ++ oldtup = getHeapTupleFromItemPointer(rel, otid); ++ ++ sepgsqlCheckProcedureInstall(rel, newtup, oldtup); ++ ++ if (!OidIsValid(HeapTupleGetSecLabel(newtup))) ++ { ++ /* ++ * If user does not specify new security context ++ * explicitly, it preserves a security context of ++ * older tuple. ++ */ ++ Oid sid = HeapTupleGetSecLabel(oldtup); ++ ++ if (HeapTupleHasSecLabel(newtup)) ++ HeapTupleSetSecLabel(newtup, sid); ++ } ++ else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: unable to update " ++ "pg_largeobject.security_context"))); ++ ++ if (isTrustedRelation(rel, is_internal)) ++ return true; ++ ++ if (HeapTupleGetSecLabel(newtup) != HeapTupleGetSecLabel(oldtup) || ++ sepgsqlTupleObjectClass(relid, newtup) != sepgsqlTupleObjectClass(relid, oldtup)) ++ relabel = true; ++ ++ if (is_internal) ++ perms |= SEPGSQL_PERMS_UPDATE; ++ if (relabel) ++ perms |= SEPGSQL_PERMS_RELABELFROM; ++ rc = sepgsqlCheckTuplePerms(rel, oldtup, newtup, perms, is_internal); ++ if (!rc) ++ goto out; ++ ++ if (relabel) ++ { ++ perms = SEPGSQL_PERMS_RELABELTO; ++ if (with_returning) ++ perms |= SEPGSQL_PERMS_SELECT; ++ rc = sepgsqlCheckTuplePerms(rel, newtup, NULL, perms, is_internal); ++ } ++ out: ++ heap_freetuple(oldtup); ++ return rc; ++} ++ ++bool ++sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, ++ bool is_internal, bool with_returning) ++{ ++ HeapTuple oldtup; ++ uint32 perms = 0; ++ bool rc; ++ ++ if (isTrustedRelation(rel, is_internal)) ++ return true; ++ if (is_internal) ++ perms |= SEPGSQL_PERMS_DELETE; ++ ++ oldtup = getHeapTupleFromItemPointer(rel, otid); ++ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal); ++ heap_freetuple(oldtup); ++ ++ return rc; ++} +diff -rpNU3 base/src/backend/security/sepgsql/permissions.c sepgsql/src/backend/security/sepgsql/permissions.c +--- base/src/backend/security/sepgsql/permissions.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/permissions.c 2009-02-26 21:08:58.000000000 +0900 +@@ -0,0 +1,636 @@ ++ ++/* ++ * src/backend/security/sepgsql/permissions.c ++ * applies SE-PostgreSQL permission checks ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/genam.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_type.h" ++#include "miscadmin.h" ++#include "security/pgace.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++#include ++ ++/* ++ * It can be configured via a GUC variable to toggle ++ * row-level access controls. ++ */ ++bool sepostgresql_row_level = true; ++ ++/* ++ * sepgsqlTupleName ++ * returns an identifier string to generate audit record for ++ * the given tuple. Please note that its results can indicate ++ * an address within the given tuple, so we should not refer ++ * the returned pointer after HeapTuple is released. ++ */ ++const char * ++sepgsqlTupleName(Oid relid, HeapTuple tuple) ++{ ++ static char buffer[NAMEDATALEN * 2 + 10]; ++ ++ switch (relid) ++ { ++ case DatabaseRelationId: ++ return NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ ++ case RelationRelationId: ++ return NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); ++ ++ case AttributeRelationId: ++ if (!IsBootstrapProcessingMode()) ++ { ++ Form_pg_attribute attForm ++ = (Form_pg_attribute) GETSTRUCT(tuple); ++ char *relname ++ = get_rel_name(attForm->attrelid); ++ ++ if (relname) ++ { ++ snprintf(buffer, sizeof(buffer), "%s.%s", ++ relname, NameStr(attForm->attname)); ++ pfree(relname); ++ return buffer; ++ } ++ } ++ return NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); ++ ++ case ProcedureRelationId: ++ return NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); ++ ++ case LargeObjectRelationId: ++ snprintf(buffer, sizeof(buffer), "loid:%u", ++ ((Form_pg_largeobject) GETSTRUCT(tuple))->loid); ++ return buffer; ++ } ++ return NULL; /* No tuple name for audit record */ ++} ++ ++/* ++ * sepgsqlFileObjectClass ++ * ++ * It returns proper object class of filesystem object already opened. ++ * It is necessary to check privileges voluntarily. ++ */ ++security_class_t ++sepgsqlFileObjectClass(int fdesc, const char *filename) ++{ ++ struct stat stbuf; ++ ++ if (fstat(fdesc, &stbuf) != 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file \"%s\": %m", filename))); ++ ++ if (S_ISDIR(stbuf.st_mode)) ++ return SECCLASS_DIR; ++ else if (S_ISCHR(stbuf.st_mode)) ++ return SECCLASS_CHR_FILE; ++ else if (S_ISBLK(stbuf.st_mode)) ++ return SECCLASS_BLK_FILE; ++ else if (S_ISFIFO(stbuf.st_mode)) ++ return SECCLASS_FIFO_FILE; ++ else if (S_ISLNK(stbuf.st_mode)) ++ return SECCLASS_LNK_FILE; ++ else if (S_ISSOCK(stbuf.st_mode)) ++ return SECCLASS_SOCK_FILE; ++ ++ return SECCLASS_FILE; ++} ++ ++/* ++ * sepgsqlTupleObjectClass ++ * ++ * It returns proper object class of given tuple ++ */ ++security_class_t ++sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) ++{ ++ Form_pg_class clsForm; ++ Form_pg_attribute attForm; ++ ++ switch (relid) ++ { ++ case DatabaseRelationId: ++ return SECCLASS_DB_DATABASE; ++ ++ case RelationRelationId: ++ clsForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (clsForm->relkind == RELKIND_RELATION) ++ return SECCLASS_DB_TABLE; ++ break; ++ ++ case AttributeRelationId: ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ if (attForm->attrelid == TypeRelationId || ++ attForm->attrelid == ProcedureRelationId || ++ attForm->attrelid == AttributeRelationId || ++ attForm->attrelid == RelationRelationId || ++ get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) ++ return SECCLASS_DB_COLUMN; ++ break; ++ ++ case ProcedureRelationId: ++ return SECCLASS_DB_PROCEDURE; ++ ++ case LargeObjectRelationId: ++ return SECCLASS_DB_BLOB; ++ } ++ ++ return SECCLASS_DB_TUPLE; ++} ++ ++/* ++ * sepgsqlCheckTuplePerms ++ * ++ * This function evaluates given permission set (SEPGSQL_PERMS_*) onto the ++ * given tuple, with translating them into proper SELinux permission. ++ * ++ * Accesses to some of system catalog has special meanings. DELETE a tuple ++ * within pg_class also means DROP TABLE for instance. In this case, ++ * SE-PostgreSQL translate given SEPGSQL_PERMS_DELETE into DB_TABLE__DROP ++ * to keep consistency of user operation. To delete a tuple within pg_class ++ * always means dropping a table independent from what SQL statement is ++ * used. ++ * ++ * Thus, checks for some of system catalog need to modify given permission ++ * set at checkTuplePermsXXXX() functions. ++ */ ++static access_vector_t ++sepgsqlPermsToCommonAv(uint32 perms) ++{ ++ access_vector_t result = 0; ++ ++ result |= (perms & SEPGSQL_PERMS_USE ? COMMON_DATABASE__GETATTR : 0); ++ result |= (perms & SEPGSQL_PERMS_SELECT ? COMMON_DATABASE__GETATTR : 0); ++ result |= (perms & SEPGSQL_PERMS_UPDATE ? COMMON_DATABASE__SETATTR : 0); ++ result |= (perms & SEPGSQL_PERMS_INSERT ? COMMON_DATABASE__CREATE : 0); ++ result |= (perms & SEPGSQL_PERMS_DELETE ? COMMON_DATABASE__DROP : 0); ++ result |= (perms & SEPGSQL_PERMS_RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0); ++ result |= (perms & SEPGSQL_PERMS_RELABELTO ? COMMON_DATABASE__RELABELTO : 0); ++ ++ return result; ++} ++ ++static access_vector_t ++sepgsqlPermsToDatabaseAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) ++{ ++ return sepgsqlPermsToCommonAv(perms); ++} ++ ++static access_vector_t ++sepgsqlPermsToTableAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) ++{ ++ return sepgsqlPermsToCommonAv(perms); ++} ++ ++static access_vector_t ++sepgsqlPermsToProcedureAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) ++{ ++ access_vector_t result = sepgsqlPermsToCommonAv(perms); ++ Form_pg_proc proForm; ++ HeapTuple protup; ++ Datum probin; ++ bool isnull; ++ ++ /* ++ * Check permission for loadable module installation ++ */ ++ protup = HeapTupleIsValid(newtup) ? newtup : tuple; ++ proForm = (Form_pg_proc) GETSTRUCT(protup); ++ ++ if (proForm->prolang == ClanguageId) ++ { ++ bool need_check = false; ++ ++ probin = SysCacheGetAttr(PROCOID, protup, ++ Anum_pg_proc_probin, ++ &isnull); ++ if (!isnull) ++ { ++ if (result & DB_PROCEDURE__CREATE) ++ need_check = true; ++ else if (HeapTupleIsValid(newtup)) ++ { ++ Form_pg_proc oldForm = (Form_pg_proc) GETSTRUCT(tuple); ++ ++ if (oldForm->prolang != proForm->prolang) ++ need_check = true; ++ else ++ { ++ Datum oldbin = SysCacheGetAttr(PROCOID, tuple, ++ Anum_pg_proc_probin, ++ &isnull); ++ if (isnull) ++ need_check = true; ++ else ++ { ++ Datum comp = DirectFunctionCall2(byteane, oldbin, probin); ++ need_check = DatumGetBool(comp); ++ } ++ } ++ } ++ ++ if (need_check) ++ { ++ char *filename = TextDatumGetCString(probin); ++ ++ sepgsqlCheckModuleInstallPerms(filename); ++ } ++ } ++ } ++ ++ return result; ++} ++ ++static access_vector_t ++sepgsqlPermsToColumnAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) ++{ ++ access_vector_t result = sepgsqlPermsToCommonAv(perms); ++ ++ if (HeapTupleIsValid(newtup)) ++ { ++ Form_pg_attribute oldatt = (Form_pg_attribute) GETSTRUCT(tuple); ++ Form_pg_attribute newatt = (Form_pg_attribute) GETSTRUCT(newtup); ++ ++ if (!oldatt->attisdropped && newatt->attisdropped) ++ result |= DB_COLUMN__DROP; ++ if (oldatt->attisdropped && !newatt->attisdropped) ++ result |= DB_COLUMN__CREATE; ++ } ++ return result; ++} ++ ++static access_vector_t ++sepgsqlPermsToTupleAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) ++{ ++ access_vector_t result = 0; ++ ++ result |= (perms & SEPGSQL_PERMS_USE ? DB_TUPLE__USE : 0); ++ result |= (perms & SEPGSQL_PERMS_SELECT ? DB_TUPLE__SELECT : 0); ++ result |= (perms & SEPGSQL_PERMS_UPDATE ? DB_TUPLE__UPDATE : 0); ++ result |= (perms & SEPGSQL_PERMS_INSERT ? DB_TUPLE__INSERT : 0); ++ result |= (perms & SEPGSQL_PERMS_DELETE ? DB_TUPLE__DELETE : 0); ++ result |= (perms & SEPGSQL_PERMS_RELABELFROM ? DB_TUPLE__RELABELFROM : 0); ++ result |= (perms & SEPGSQL_PERMS_RELABELTO ? DB_TUPLE__RELABELTO : 0); ++ ++ return result; ++} ++ ++static access_vector_t ++sepgsqlPermsToBlobAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) ++{ ++ access_vector_t result = sepgsqlPermsToCommonAv(perms); ++ ++ /* ++ * NOTE: INSERT tuples into pg_largeobject has a possibility to create ++ * a new largeobject, if the given loid is not exist on the current ++ * pg_largeobject. Ditto for DELETE statement, it also has a possibility ++ * to drop a largeobject, if it removes all tuples within a large object. ++ * ++ * UPDATE pg_largeobject.loid has a possibility to create and drop ++ * a largeobject in same time, so we need to check it when loid is ++ * changed. ++ * ++ * db_blob:{create} and db_blob:{drop} should be evaluated for ++ * creation/deletion of largeobject, but we have to check pg_largeobject ++ * with SnapshotSelf whether there is one or more tuple having same loid, ++ * or not, on each tuple insertion or deletion. ++ * ++ * So, we assume any INSERT means db_blob:{create}, any DELETE means ++ * db_blob:{drop}. ++ */ ++ result |= (perms & SEPGSQL_PERMS_INSERT ? DB_BLOB__WRITE : 0); ++ if (perms & SEPGSQL_PERMS_UPDATE) ++ { ++ result |= DB_BLOB__WRITE; ++ ++ if (((Form_pg_largeobject) GETSTRUCT(tuple))->loid != ++ ((Form_pg_largeobject) GETSTRUCT(newtup))->loid) ++ result |= (DB_BLOB__CREATE | DB_BLOB__DROP); ++ } ++ result |= (perms & SEPGSQL_PERMS_DELETE ? DB_BLOB__WRITE : 0); ++ result |= (perms & SEPGSQL_PERMS_READ ? DB_BLOB__READ : 0); ++ ++ return result; ++} ++ ++bool ++sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple newtup, ++ uint32 perms, bool abort) ++{ ++ security_class_t tclass; ++ access_vector_t av = 0; ++ bool rc = true; ++ ++ Assert(HeapTupleIsValid(tuple)); ++ ++ tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple); ++ ++ switch (tclass) ++ { ++ case SECCLASS_DB_DATABASE: ++ av = sepgsqlPermsToDatabaseAv(perms, tuple, newtup); ++ break; ++ ++ case SECCLASS_DB_TABLE: ++ av = sepgsqlPermsToTableAv(perms, tuple, newtup); ++ break; ++ ++ case SECCLASS_DB_PROCEDURE: ++ av = sepgsqlPermsToProcedureAv(perms, tuple, newtup); ++ break; ++ ++ case SECCLASS_DB_COLUMN: ++ av = sepgsqlPermsToColumnAv(perms, tuple, newtup); ++ break; ++ ++ case SECCLASS_DB_BLOB: ++ av = sepgsqlPermsToBlobAv(perms, tuple, newtup); ++ break; ++ ++ default: /* SECCLASS_DB_TUPLE */ ++ if (sepostgresql_row_level) ++ av = sepgsqlPermsToTupleAv(perms, tuple, newtup); ++ break; ++ } ++ ++ if (av) ++ { ++ const char *audit_name ++ = sepgsqlTupleName(RelationGetRelid(rel), tuple); ++ ++ if (abort) ++ { ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ tclass, av, audit_name); ++ } ++ else ++ { ++ rc = sepgsqlClientHasPermissionNoAbort(HeapTupleGetSecLabel(tuple), ++ tclass, av, audit_name); ++ } ++ } ++ ++ return rc; ++} ++ ++/* ++ * sepgsqlCheckModuleInstallPerms ++ * ++ * It checks client's privilege to install a new shared loadable file. ++ */ ++void ++sepgsqlCheckModuleInstallPerms(const char *filename) ++{ ++ security_context_t file_context; ++ Form_pg_database dbform; ++ HeapTuple dbtup; ++ char *fullpath; ++ ++ /* (client) <-- db_database:module_install --> (database) */ ++ dbtup = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(dbtup)) ++ elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId); ++ ++ dbform = (Form_pg_database) GETSTRUCT(dbtup); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(dbtup), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__INSTALL_MODULE, ++ NameStr(dbform->datname)); ++ ReleaseSysCache(dbtup); ++ ++ /* (client) <-- db_databse:module_install --> (*.so file) */ ++ fullpath = expand_dynamic_library_name(filename); ++ if (getfilecon_raw(fullpath, &file_context) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not access file \"%s\": %m", fullpath))); ++ PG_TRY(); ++ { ++ sepgsqlComputePermission(sepgsqlGetClientContext(), ++ file_context, ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__INSTALL_MODULE, ++ fullpath); ++ } ++ PG_CATCH(); ++ { ++ freecon(file_context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(file_context); ++} ++ ++/* ++ * sepgsqlSetDefaultContext ++ * ++ * This function attach a proper security context for a newly inserted tuple, ++ * refering the security policy. ++ * In the default, any tuple inherits the security context of its table. ++ * However, we have several exception for some of system catalog. It come from ++ * TYPE_TRANSITION rules in the security policy. ++ */ ++static Oid ++sepgsqlDefaultDatabaseContext(Relation rel, HeapTuple tuple) ++{ ++ security_context_t newcon; ++ ++ newcon = sepgsqlComputeCreateContext(sepgsqlGetClientContext(), ++ sepgsqlGetClientContext(), ++ SECCLASS_DB_DATABASE); ++ return pgaceSecurityLabelToSid(newcon); ++} ++ ++static Oid ++sepgsqlDefaultTableContext(Relation rel, HeapTuple tuple) ++{ ++ return sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_TABLE); ++} ++ ++static Oid ++sepgsqlDefaultProcedureContext(Relation rel, HeapTuple tuple) ++{ ++ return sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_PROCEDURE); ++} ++ ++static Oid ++sepgsqlDefaultColumnContext(Relation rel, HeapTuple tuple) ++{ ++ Form_pg_attribute attForm; ++ Oid tblsid; ++ ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ if (IsBootstrapProcessingMode() && ++ (attForm->attrelid == TypeRelationId || ++ attForm->attrelid == ProcedureRelationId || ++ attForm->attrelid == AttributeRelationId || ++ attForm->attrelid == RelationRelationId)) ++ { ++ /* ++ * We cannot access relation caches on very early phase ++ * in bootstrap, so it assumes tables has default security ++ * context and unlabeled by initdb. ++ */ ++ tblsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_TABLE); ++ } ++ else ++ { ++ HeapTuple reltup ++ = SearchSysCache(RELOID, ++ ObjectIdGetDatum(attForm->attrelid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "SELinux: cache lookup failed for relation: %u", ++ attForm->attrelid); ++ ++ tblsid = HeapTupleGetSecLabel(reltup); ++ ++ ReleaseSysCache(reltup); ++ } ++ ++ return sepgsqlClientCreateSid(tblsid, SECCLASS_DB_COLUMN); ++} ++ ++static Oid ++sepgsqlDefaultTupleContext(Relation rel, HeapTuple tuple) ++{ ++ Oid tblsid; ++ ++ if (IsBootstrapProcessingMode() && ++ (RelationGetRelid(rel) == TypeRelationId || ++ RelationGetRelid(rel) == ProcedureRelationId || ++ RelationGetRelid(rel) == AttributeRelationId || ++ RelationGetRelid(rel) == RelationRelationId)) ++ { ++ /* ++ * We cannot access relation caches on very early phase ++ * in bootstrap, so it assumes tables has default security ++ * context and unlabeled by initdb. ++ */ ++ tblsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_TABLE); ++ } ++ else ++ { ++ HeapTuple reltup ++ = SearchSysCache(RELOID, ++ ObjectIdGetDatum(RelationGetRelid(rel)), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "SELinux: cache lookup failed for relation: %u", ++ RelationGetRelid(rel)); ++ ++ tblsid = HeapTupleGetSecLabel(reltup); ++ ++ ReleaseSysCache(reltup); ++ } ++ ++ return sepgsqlClientCreateSid(tblsid, SECCLASS_DB_TUPLE); ++} ++ ++static Oid ++sepgsqlDefaultBlobContext(Relation rel, HeapTuple tuple) ++{ ++ /* ++ * NOTE: ++ * A new tuple to be inserted into pg_largeobject inherits ++ * a security context of prior tuples of same large object. ++ * The "SnapshotNow" is available for this purpose because ++ * lo_create() invokes CommandCounterIncrement() just after ++ * creation of a new large object. ++ * ++ * If we can find no prior tuples, it means this action to ++ * insert the first page, or client invokes INSERT INTO ... ++ * with multiple tuples with same loid. However, these ++ * tuples are labeled by same TYPE_TRANSITION rules in both ++ * cases. So, there are no differences. ++ */ ++ Form_pg_largeobject loForm ++ = (Form_pg_largeobject) GETSTRUCT(tuple); ++ ScanKeyData skey; ++ SysScanDesc scan; ++ HeapTuple lotup; ++ Oid newsid = InvalidOid; ++ ++ ScanKeyInit(&skey, ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loForm->loid)); ++ scan = systable_beginscan(rel, ++ LargeObjectLOidPNIndexId, true, ++ SnapshotNow, 1, &skey); ++ while ((lotup = systable_getnext(scan)) != NULL) ++ { ++ newsid = HeapTupleGetSecLabel(lotup); ++ if (OidIsValid(newsid)) ++ break; ++ } ++ systable_endscan(scan); ++ ++ if (!OidIsValid(newsid)) ++ { ++ newsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_BLOB); ++ } ++ ++ return newsid; ++} ++ ++void ++sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple) ++{ ++ security_class_t tclass; ++ Oid newsid; ++ ++ Assert(HeapTupleHasSecLabel(tuple)); ++ tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple); ++ ++ switch (tclass) ++ { ++ case SECCLASS_DB_DATABASE: ++ newsid = sepgsqlDefaultDatabaseContext(rel, tuple); ++ break; ++ case SECCLASS_DB_TABLE: ++ newsid = sepgsqlDefaultTableContext(rel, tuple); ++ break; ++ case SECCLASS_DB_PROCEDURE: ++ newsid = sepgsqlDefaultProcedureContext(rel, tuple); ++ break; ++ case SECCLASS_DB_COLUMN: ++ newsid = sepgsqlDefaultColumnContext(rel, tuple); ++ break; ++ case SECCLASS_DB_BLOB: ++ newsid = sepgsqlDefaultBlobContext(rel, tuple); ++ break; ++ default: /* SECCLASS_DB_TUPLE */ ++ newsid = sepgsqlDefaultTupleContext(rel, tuple); ++ break; ++ } ++ ++ HeapTupleSetSecLabel(tuple, newsid); ++} +diff -rpNU3 base/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/security/sepgsql/proxy.c +--- base/src/backend/security/sepgsql/proxy.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsql/proxy.c 2009-02-26 21:08:58.000000000 +0900 +@@ -0,0 +1,1076 @@ ++/* ++ * src/backend/security/sepgsql/proxy.c ++ * Proxying the given Query trees via SE-PostgreSQL ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "catalog/heap.h" ++#include "catalog/indexing.h" ++#include "catalog/namespace.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_constraint.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_operator.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_security.h" ++#include "catalog/pg_trigger.h" ++#include "catalog/pg_type.h" ++#include "executor/executor.h" ++#include "nodes/security.h" ++#include "optimizer/clauses.h" ++#include "optimizer/plancat.h" ++#include "optimizer/prep.h" ++#include "optimizer/tlist.h" ++#include "parser/parsetree.h" ++#include "security/pgace.h" ++#include "storage/lock.h" ++#include "utils/array.h" ++#include "utils/fmgroids.h" ++#include "utils/fmgrtab.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++/* ++ * sepgsqlWalkerContext ++ * ++ * This structure holds a context during analyzing a given query. ++ * selist is a list of SEvalItemXXX objects to enumerate appared ++ * tables and columns. These are evaluated later, just before ++ * executing query. ++ * is_internal_use shows the current state whether the current ++ * Node is chained with target list, or conditional clause. ++ */ ++typedef struct sepgsqlWalkerContext ++{ ++ struct sepgsqlWalkerContext *parent; ++ Query *query; /* Query structure of current layer */ ++ List *selist; /* list of SEvalItemXXX */ ++ bool is_internal_use; ++} sepgsqlWalkerContext; ++ ++#define seitem_index_to_attno(index) \ ++ ((index) + FirstLowInvalidHeapAttributeNumber + 1) ++#define seitem_attno_to_index(attno) \ ++ ((attno) - FirstLowInvalidHeapAttributeNumber - 1) ++ ++ ++/* ++ * addEvalRelation ++ * addEvalRelationRTE ++ * ++ * These functions add a given relation into selist, if it is not ++ * contained yet. In addition, addEvalRelationRTE also marks required ++ * permissions on rte->pgaceTuplePerms. It is delivered to Scan object ++ * and we can use it on ExecScan hook to apply tuple-level access ++ * controls. ++ */ ++static List * ++addEvalRelation(List *selist, Oid relid, bool inh, uint32 perms) ++{ ++ SelinuxEvalItem *seitem; ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ ListCell *l; ++ ++ foreach (l, selist) ++ { ++ seitem = (SelinuxEvalItem *) lfirst(l); ++ Assert(IsA(seitem, SelinuxEvalItem)); ++ ++ if (seitem->relid == relid && seitem->inh == inh) ++ { ++ seitem->relperms |= perms; ++ return selist; ++ } ++ } ++ ++ /* not found, so create a new one */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ seitem = makeNode(SelinuxEvalItem); ++ seitem->relid = relid; ++ seitem->inh = inh; ++ seitem->relperms = perms; ++ seitem->nattrs = seitem_attno_to_index(relForm->relnatts) + 1; ++ seitem->attperms = palloc0(seitem->nattrs * sizeof(uint32)); ++ ++ ReleaseSysCache(tuple); ++ ++ return lappend(selist, seitem); ++} ++ ++static List * ++addEvalRelationRTE(List *selist, RangeTblEntry *rte, uint32 perms) ++{ ++ rte->pgaceTuplePerms |= (perms & DB_TABLE__USE ? SEPGSQL_PERMS_USE : 0); ++ rte->pgaceTuplePerms |= (perms & DB_TABLE__SELECT ? SEPGSQL_PERMS_SELECT : 0); ++ rte->pgaceTuplePerms |= (perms & DB_TABLE__UPDATE ? SEPGSQL_PERMS_UPDATE : 0); ++ rte->pgaceTuplePerms |= (perms & DB_TABLE__DELETE ? SEPGSQL_PERMS_DELETE : 0); ++ ++ return addEvalRelation(selist, rte->relid, rte->inh, perms); ++} ++ ++/* ++ * addEvalAttribute ++ * addEvalAttributeRTE ++ * ++ * These functions add a given attribute into selist, if it is not ++ * contained yet. In addition, addEvalAttributeRTE also marks required ++ * permissions on rte->pgaceTuplePerms. It is delivered to Scan object ++ * and we can use it on ExecScan hook to apply tuple-level access ++ * controls. ++ */ ++static List * ++addEvalAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 perms) ++{ ++ SelinuxEvalItem *seitem; ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ ListCell *l; ++ int index = seitem_attno_to_index(attno); ++ ++ foreach (l, selist) ++ { ++ seitem = (SelinuxEvalItem *) lfirst(l); ++ Assert(IsA(seitem, SelinuxEvalItem)); ++ ++ if (seitem->relid == relid && seitem->inh == inh) ++ { ++ if (index >= seitem->nattrs) ++ { ++ uint32 *attperms, nattrs; ++ ++ /* ++ * NOTE: the following step has a possibility that ++ * index number overs seitem->nattrs ++ * ++ * 1. PREPARE p AS SELECT t FROM t; ++ * 2. ALTER TABLE t ADD COLUMN x int; ++ * 3. EXECUTE p; ++ * ++ * Because whole-row-reference is extracted to ++ * references to all the user columns, so table ++ * may have different number of columns between ++ * state.1 and state.3. ++ * In this case, we need to rebuild seitem->attperms ++ */ ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ nattrs = seitem_attno_to_index(relForm->relnatts) + 1; ++ attperms = palloc0(nattrs * sizeof(uint32)); ++ memcpy(attperms, seitem->attperms, ++ seitem->nattrs * sizeof(uint32)); ++ seitem->nattrs = nattrs; ++ seitem->attperms = attperms; ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ if (index < 0 || index >= seitem->nattrs) ++ elog(ERROR, "SELinux: invalid attribute number: %d at relation: %u", ++ attno, relid); ++ ++ seitem->attperms[index] |= perms; ++ ++ return selist; ++ } ++ } ++ ++ /* not found, so create a new one */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ seitem = makeNode(SelinuxEvalItem); ++ seitem->relid = relid; ++ seitem->inh = inh; ++ seitem->relperms = 0; ++ seitem->nattrs = seitem_attno_to_index(relForm->relnatts) + 1; ++ seitem->attperms = palloc0(seitem->nattrs * sizeof(uint32)); ++ if (index < 0 || index >= seitem->nattrs) ++ elog(ERROR, "SELinux: invalid attribute number: %d at relation: %u", ++ attno, relid); ++ seitem->attperms[index] |= perms; ++ ++ ReleaseSysCache(tuple); ++ ++ return lappend(selist, seitem); ++} ++ ++static List * ++addEvalAttributeRTE(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms) ++{ ++ uint32 tbl_perms = 0; ++ ++ tbl_perms |= (perms & DB_COLUMN__USE ? DB_TABLE__USE : 0); ++ tbl_perms |= (perms & DB_COLUMN__SELECT ? DB_TABLE__SELECT : 0); ++ tbl_perms |= (perms & DB_COLUMN__INSERT ? DB_TABLE__INSERT : 0); ++ tbl_perms |= (perms & DB_COLUMN__UPDATE ? DB_TABLE__UPDATE : 0); ++ selist = addEvalRelationRTE(selist, rte, tbl_perms); ++ ++ /* ++ * Special care for pg_largeobject.data ++ */ ++ if ((perms & DB_COLUMN__SELECT) != 0 && ++ rte->relid == LargeObjectRelationId && ++ attno == Anum_pg_largeobject_data) ++ rte->pgaceTuplePerms |= SEPGSQL_PERMS_READ; ++ ++ return addEvalAttribute(selist, rte->relid, rte->inh, attno, perms); ++} ++ ++/* ++ * addEvalForeignKeyConstraint ++ * ++ * This function add special case handling for PK/FK constraints. ++ * invoke trigger function requires to access rights for all attribute ++ * ++ */ ++static List * ++addEvalForeignKeyConstraint(List *selist, Form_pg_trigger trigger) ++{ ++ HeapTuple contup; ++ Datum attdat; ++ ArrayType *attrs; ++ int index; ++ int16 *attnum; ++ bool isnull; ++ ++ contup = SearchSysCache(CONSTROID, ++ ObjectIdGetDatum(trigger->tgconstraint), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(contup)) ++ elog(ERROR, "SELinux: cache lookup failed for constraint %u", ++ trigger->tgconstrrelid); ++ ++ if (trigger->tgfoid == F_RI_FKEY_CHECK_INS || ++ trigger->tgfoid == F_RI_FKEY_CHECK_UPD) ++ attdat = SysCacheGetAttr(CONSTROID, contup, ++ Anum_pg_constraint_conkey, &isnull); ++ else ++ attdat = SysCacheGetAttr(CONSTROID, contup, ++ Anum_pg_constraint_confkey, &isnull); ++ if (isnull) ++ elog(ERROR, "null PK/FK for constraint %u", ++ trigger->tgconstrrelid); ++ attrs = DatumGetArrayTypeP(attdat); ++ ++ if (ARR_NDIM(attrs) != 1 || ++ ARR_HASNULL(attrs) || ++ ARR_ELEMTYPE(attrs) != INT2OID) ++ elog(ERROR, "SELinux: unexpected constraint %u", trigger->tgconstrrelid); ++ ++ attnum = (int16 *) ARR_DATA_PTR(attrs); ++ for (index = 0; index < ARR_DIMS(attrs)[0]; index++) ++ selist = addEvalAttribute(selist, trigger->tgrelid, false, ++ attnum[index], DB_COLUMN__SELECT); ++ ++ ReleaseSysCache(contup); ++ ++ return selist; ++} ++ ++/* ++ * addEvalTriggerFunction ++ * ++ * This function adds needed items into selist, to execute a trigger ++ * function. At least, it requires permission set to execute a function ++ * configured as a trigger, to select a table and whole of columns ++ * because whole of a tuple is delivered to trigger functions. ++ */ ++static List * ++addEvalTriggerFunction(List *selist, Oid relid, int cmdType) ++{ ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData skey; ++ HeapTuple tuple; ++ ++ rel = heap_open(TriggerRelationId, AccessShareLock); ++ ScanKeyInit(&skey, ++ Anum_pg_trigger_tgrelid, ++ BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ++ scan = systable_beginscan(rel, TriggerRelidNameIndexId, ++ true, SnapshotNow, 1, &skey); ++ while (HeapTupleIsValid((tuple = systable_getnext(scan)))) ++ { ++ Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple); ++ Form_pg_class relForm; ++ HeapTuple reltup; ++ ++ /* ++ * Skip not-invoked triggers ++ */ ++ if (!trigForm->tgenabled) ++ continue; ++ if (cmdType == CMD_INSERT && !TRIGGER_FOR_INSERT(trigForm->tgtype)) ++ continue; ++ if (cmdType == CMD_UPDATE && !TRIGGER_FOR_UPDATE(trigForm->tgtype)) ++ continue; ++ if (cmdType == CMD_DELETE && !TRIGGER_FOR_DELETE(trigForm->tgtype)) ++ continue; ++ ++ /* ++ * per STATEMENT trigger cannot refer whole of a tuple ++ */ ++ if (!TRIGGER_FOR_ROW(trigForm->tgtype)) ++ continue; ++ ++ /* ++ * BEFORE-ROW-INSERT trigger cannot refer whole of a tuple ++ */ ++ if (TRIGGER_FOR_BEFORE(trigForm->tgtype) && ++ TRIGGER_FOR_INSERT(trigForm->tgtype)) ++ continue; ++ ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ relForm = (Form_pg_class) GETSTRUCT(reltup); ++ ++ selist = addEvalRelation(selist, relid, false, DB_TABLE__SELECT); ++ ++ if (RI_FKey_trigger_type(trigForm->tgfoid) != RI_TRIGGER_NONE) ++ selist = addEvalForeignKeyConstraint(selist, trigForm); ++ else ++ selist = addEvalAttribute(selist, relid, false, ++ 0, DB_COLUMN__SELECT); ++ ReleaseSysCache(reltup); ++ } ++ systable_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ ++ return selist; ++} ++ ++/* ++ * sepgsqlExprWalker ++ * ++ * This function walks on the given expression tree to pick up ++ * all the appeared tables and columns. Their identifiers are ++ * chains on swc->selist to evaluate permissions on them later. ++ * ++ * walkVarHelper picks up an accessed column and its contained ++ * table, and chains them on swc->selist. ++ * When swc->is_internal_use is true, it means this reference ++ * is checked as "use" permission because its contents are ++ * consumed internally, and not to be returned to client directly. ++ * Otherwise, "select" permission is applied. ++ * ++ * walkQueryHelper walks on Query structure. ++ * The reason why we don't use query_tree_walker() is that ++ * SE-PostgreSQL need to apply different permission between ++ * targetList and havingQual, for example. ++ */ ++ ++static bool ++sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc); ++ ++static void ++sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc, ++ bool is_internal_use); ++ ++/* ++ * wholeRefJoinWalker ++ * ++ * A corner case need to invoke this walker function. ++ * When we use whole-row-reference on RTE_JOIN relation, ++ * it should be extracted to whole-row-references on ++ * sources relations. ++ * ++ * EXAMPLE: ++ * SELECT t4 FROM (t1 JOIN (t2 JOIN t3 USING (a)) USING (b)) AS t4; ++ * ++ * Because RangeTblEntry with RTE_JOIN does not have any identifiers ++ * of its source relations, we have to scan Query->jointree again to ++ * look up sources again. :( ++ */ ++typedef struct ++{ ++ Query *query; ++ int rtindex; ++ /* ++ * rtindex == 0 means we are now walking on the required JoinExpr ++ * or its leafs, so we need to pick up all the appeared relations ++ * under the JoinExpr in this case. ++ */ ++ List *selist; ++ uint32 perms; ++} wholeRefJoinWalkerContext; ++ ++static bool ++wholeRefJoinWalker(Node *node, wholeRefJoinWalkerContext *jwc) ++{ ++ if (!node) ++ return false; ++ ++ if (IsA(node, JoinExpr)) ++ { ++ JoinExpr *j = (JoinExpr *) node; ++ ++ if (j->rtindex == jwc->rtindex) ++ { ++ int rtindex_backup = jwc->rtindex; ++ bool rc; ++ ++ jwc->rtindex = 0; ++ rc = expression_tree_walker(node, wholeRefJoinWalker, jwc); ++ jwc->rtindex = rtindex_backup; ++ ++ return rc; ++ } ++ } ++ else if (IsA(node, RangeTblRef) && jwc->rtindex == 0) ++ { ++ RangeTblRef *rtr = (RangeTblRef *) node; ++ RangeTblEntry *rte = rt_fetch(rtr->rtindex, ++ jwc->query->rtable); ++ if (rte->rtekind == RTE_RELATION) ++ { ++ jwc->selist = addEvalAttributeRTE(jwc->selist, rte, 0, jwc->perms); ++ } ++ } ++ return expression_tree_walker(node, wholeRefJoinWalker, jwc); ++} ++ ++static void ++walkVarHelper(Var *var, sepgsqlWalkerContext *swc) ++{ ++ sepgsqlWalkerContext *cur = swc; ++ Query *query; ++ RangeTblEntry *rte; ++ int lv; ++ ++ Assert(IsA(var, Var)); ++ ++ for (lv = var->varlevelsup; lv > 0; lv--) ++ { ++ Assert(cur->parent != NULL); ++ cur = cur->parent; ++ } ++ query = cur->query; ++ ++ rte = rt_fetch(var->varno, query->rtable); ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ if (rte->rtekind == RTE_RELATION) ++ { ++ uint32 perms = swc->is_internal_use ++ ? DB_COLUMN__USE : DB_COLUMN__SELECT; ++ ++ swc->selist = addEvalAttributeRTE(swc->selist, rte, ++ var->varattno, perms); ++ } ++ else if (rte->rtekind == RTE_JOIN) ++ { ++ if (var->varattno == 0) ++ { ++ wholeRefJoinWalkerContext jwcData; ++ ++ jwcData.query = query; ++ jwcData.rtindex = var->varno; ++ jwcData.selist = swc->selist; ++ jwcData.perms = swc->is_internal_use ++ ? DB_COLUMN__USE : DB_COLUMN__SELECT; ++ ++ wholeRefJoinWalker((Node *)query->jointree, &jwcData); ++ swc->selist = jwcData.selist; ++ } ++ else ++ { ++ Node *node = list_nth(rte->joinaliasvars, ++ var->varattno - 1); ++ sepgsqlExprWalker(node, swc); ++ } ++ } ++} ++ ++static List * ++walkQueryHelper(Query *query, sepgsqlWalkerContext *swc) ++{ ++ sepgsqlWalkerContext swcData; ++ RangeTblEntry *rte; ++ ++ memset(&swcData, 0, sizeof(swcData)); ++ swcData.parent = swc; ++ swcData.selist = (!swc ? NIL : swc->selist); ++ swcData.query = query; ++ ++ if (query->commandType != CMD_DELETE) ++ { ++ ListCell *l; ++ ++ foreach (l, query->targetList) ++ { ++ TargetEntry *tle = lfirst(l); ++ bool is_security = false; ++ ++ Assert(IsA(tle, TargetEntry)); ++ ++ if (tle->resjunk && ++ tle->resname && ++ strcmp(tle->resname, SecurityLabelAttributeName) == 0) ++ is_security = true; ++ ++ if (tle->resjunk && !is_security) ++ { ++ sepgsqlExprWalkerFlags((Node *) tle->expr, &swcData, true); ++ continue; ++ } ++ ++ sepgsqlExprWalkerFlags((Node *) tle->expr, &swcData, false); ++ ++ if (query->commandType != CMD_SELECT) ++ { ++ AttrNumber attno = tle->resno; ++ uint32 perms; ++ ++ if (is_security) ++ attno = SecurityLabelAttributeNumber; ++ ++ if (query->commandType == CMD_UPDATE) ++ perms = DB_COLUMN__UPDATE; ++ else ++ perms = DB_COLUMN__INSERT; ++ ++ rte = rt_fetch(query->resultRelation, query->rtable); ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ swcData.selist ++ = addEvalAttributeRTE(swcData.selist, rte, attno, perms); ++ } ++ } ++ } ++ else ++ { ++ /* no need to check column-level permission for DELETE */ ++ rte = rt_fetch(query->resultRelation, query->rtable); ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ swcData.selist ++ = addEvalRelationRTE(swcData.selist, rte, DB_TABLE__DELETE); ++ } ++ ++ sepgsqlExprWalkerFlags((Node *) query->returningList, &swcData, false); ++ sepgsqlExprWalkerFlags((Node *) query->jointree, &swcData, true); ++ sepgsqlExprWalkerFlags((Node *) query->setOperations, &swcData, true); ++ sepgsqlExprWalkerFlags((Node *) query->havingQual, &swcData, true); ++ sepgsqlExprWalkerFlags((Node *) query->sortClause, &swcData, true); ++ sepgsqlExprWalkerFlags((Node *) query->groupClause, &swcData, true); ++ sepgsqlExprWalkerFlags((Node *) query->limitOffset, &swcData, true); ++ sepgsqlExprWalkerFlags((Node *) query->limitCount, &swcData, true); ++ ++ return swcData.selist; ++} ++ ++static void ++walkRangeTblRefHelper(RangeTblRef *rtr, sepgsqlWalkerContext *swc) ++{ ++ Query *query = swc->query; ++ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); ++ ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ switch (rte->rtekind) ++ { ++ case RTE_RELATION: ++ if (rtr->rtindex != query->resultRelation) ++ swc->selist = addEvalRelationRTE(swc->selist, rte, ++ DB_TABLE__SELECT); ++ break; ++ ++ case RTE_SUBQUERY: ++ swc->selist = walkQueryHelper(rte->subquery, swc); ++ break; ++ ++ case RTE_FUNCTION: ++ sepgsqlExprWalker(rte->funcexpr, swc); ++ break; ++ ++ case RTE_VALUES: ++ sepgsqlExprWalker((Node *) rte->values_lists, swc); ++ break; ++ ++ default: ++ /* do nothing */ ++ break; ++ } ++} ++ ++static void ++walkSortClauseHelper(SortClause *sc, sepgsqlWalkerContext *swc) ++{ ++ Query *query = swc->query; ++ TargetEntry *tle ++ = get_sortgroupref_tle(sc->tleSortGroupRef, ++ query->targetList); ++ ++ Assert(IsA(tle, TargetEntry)); ++ ++ sepgsqlExprWalker((Node *) tle->expr, swc); ++} ++ ++static bool ++sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc) ++{ ++ if (node == NULL) ++ return false; ++ else if (IsA(node, Var)) ++ walkVarHelper((Var *) node, swc); ++ else if (IsA(node, RangeTblRef)) ++ walkRangeTblRefHelper((RangeTblRef *) node, swc); ++ else if (IsA(node, Query)) ++ { ++ swc->selist ++ = walkQueryHelper((Query *) node, swc); ++ } ++ else if (IsA(node, SortClause) || ++ IsA(node, GroupClause)) ++ { ++ walkSortClauseHelper((SortClause *) node, swc); ++ ++ return false; ++ } ++ return expression_tree_walker(node, sepgsqlExprWalker, (void *) swc); ++} ++ ++static void ++sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc, ++ bool is_internal_use) ++{ ++ bool saved_is_internal_use = swc->is_internal_use; ++ ++ swc->is_internal_use = is_internal_use; ++ sepgsqlExprWalker(node, swc); ++ swc->is_internal_use = saved_is_internal_use; ++} ++ ++/* ++ * sepgsqlPostQueryRewrite ++ * ++ * This function is invoked just after given queries are rewritten ++ * via query-rewritter phase. It walks on given query trees to ++ * picks up all appeared tables and columns, and to chains the list ++ * of them on query->pgaceItem. ++ * This list is used to evaluate permissions later, just before ++ * the query execution. ++ * ++ * It do nothing for DDL queries, because these are processed in ++ * sepgsqlProcessUtility() hook. ++ */ ++List * ++sepgsqlPostQueryRewrite(List *queryList) ++{ ++ ListCell *l; ++ ++ foreach (l, queryList) ++ { ++ Query *query = (Query *) lfirst(l); ++ ++ Assert(IsA(query, Query)); ++ ++ if (query->commandType == CMD_SELECT || ++ query->commandType == CMD_UPDATE || ++ query->commandType == CMD_INSERT || ++ query->commandType == CMD_DELETE) ++ { ++ query->pgaceItem ++ = (Node *) walkQueryHelper(query, NULL); ++ } ++ } ++ ++ return queryList; ++} ++ ++/* ++ * checkSelinuxEvalItem ++ * checks give SelinuxEvalItem object based on the security ++ * policy of SELinux. ++ */ ++static void ++checkSelinuxEvalItem(SelinuxEvalItem *seitem) ++{ ++ Form_pg_class relForm; ++ Form_pg_attribute attForm; ++ HeapTuple tuple; ++ AttrNumber attno; ++ const char *audit_name; ++ int index; ++ ++ Assert(IsA(seitem, SelinuxEvalItem)); ++ ++ /* ++ * Prevent to write pg_security by hand ++ */ ++ if (seitem->relid == SecurityRelationId && ++ (seitem->relperms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE))) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not modify pg_security by hand"))); ++ ++ /* ++ * Permission checks on table ++ */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(seitem->relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for relation: %u", ++ seitem->relid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (relForm->relkind != RELKIND_RELATION) ++ { ++ ReleaseSysCache(tuple); ++ return; ++ } ++ ++ audit_name = sepgsqlTupleName(RelationRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_TABLE, ++ seitem->relperms, ++ audit_name); ++ ReleaseSysCache(tuple); ++ ++ /* ++ * Expand whole-row-reference ++ */ ++ index = seitem_attno_to_index(InvalidAttrNumber); ++ if (seitem->attperms[index] != 0) ++ { ++ uint32 perms = seitem->attperms[index]; ++ ++ seitem->attperms[index] = 0; ++ for (index++; index < seitem->nattrs; index++) ++ seitem->attperms[index] |= perms; ++ } ++ ++ /* ++ * Permission checks on columns ++ */ ++ for (index = 0; index < seitem->nattrs; index++) ++ { ++ if (seitem->attperms[index] == 0) ++ continue; ++ ++ attno = seitem_index_to_attno(index); ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(seitem->relid), ++ Int16GetDatum(attno), ++ 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u", ++ attno, seitem->relid); ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ /* ++ * NOTE: When user uses whole-row-reference on a table ++ * which has already dropped column, the column can have ++ * non-zero required permissions, but being ignorable. ++ */ ++ if (attForm->attisdropped) ++ { ++ ReleaseSysCache(tuple); ++ continue; ++ } ++ ++ audit_name = sepgsqlTupleName(AttributeRelationId, tuple); ++ sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), ++ SECCLASS_DB_COLUMN, ++ seitem->attperms[index], ++ audit_name); ++ ReleaseSysCache(tuple); ++ } ++} ++ ++static List * ++expandEvalItemInheritance(List *selist) ++{ ++ List *result = NIL; ++ List *inherits; ++ ListCell *l, *i; ++ int index; ++ ++ foreach (l, selist) ++ { ++ SelinuxEvalItem *seitem = lfirst(l); ++ ++ Assert(IsA(seitem, SelinuxEvalItem)); ++ ++ if (!seitem->inh) ++ { ++ result = lappend(result, seitem); ++ continue; ++ } ++ ++ inherits = find_all_inheritors(seitem->relid); ++ foreach (i, inherits) ++ { ++ result = addEvalRelation(result, lfirst_oid(i), false, ++ seitem->relperms); ++ for (index = 0; index < seitem->nattrs; index++) ++ { ++ Oid relid_inh = lfirst_oid(i); ++ AttrNumber attno; ++ ++ if (seitem->attperms[index] == 0) ++ continue; ++ ++ attno = seitem_index_to_attno(index); ++ if (attno < 1 || seitem->relid == relid_inh) ++ { ++ /* ++ * If attribute is system-column or whole-row-reference, ++ * or inherit relation is itself, we don't need to fix up ++ * attribute number. ++ */ ++ result = addEvalAttribute(result, relid_inh, false, ++ attno, seitem->attperms[index]); ++ continue; ++ } ++ else ++ { ++ char *attname = get_attname(seitem->relid, attno); ++ ++ if (!attname) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ attno, seitem->relid); ++ ++ attno = get_attnum(relid_inh, attname); ++ if (attno == InvalidAttrNumber) ++ elog(ERROR, "cache lookup failed for attribute %s of relation %u", ++ attname, relid_inh); ++ ++ result = addEvalAttribute(result, relid_inh, false, ++ attno, seitem->attperms[index]); ++ pfree(attname); ++ } ++ } ++ } ++ } ++ return result; ++} ++ ++/* ++ * sepgsqlExecutorStart ++ * ++ * This function is invoked at the head of ExecutorStart, to evaluate ++ * permissions to access appeared object within the given query. ++ * Query->pgaceItem is a list of SelinuxEvalItem objects generated in ++ * previous phase, and it is copied to PlannedStmt->pgaceItem in the ++ * optimizer. ++ * This functions expand given selist based on table inheritance, ++ * adds additional permissions related to trigger functions, and ++ * expands whole-row-references. Then, these items are evaluated ++ * based on the security policy of SELinux. ++ */ ++void ++sepgsqlExecutorStart(QueryDesc *queryDesc, int eflags) ++{ ++ PlannedStmt *pstmt = queryDesc->plannedstmt; ++ RangeTblEntry *rte; ++ List *selist; ++ ListCell *l; ++ ++ /* ++ * EXPLAIN statement does not access any object. ++ */ ++ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) ++ return; ++ ++ if (!pstmt->pgaceItem) ++ return; ++ ++ Assert(IsA(pstmt->pgaceItem, List)); ++ selist = copyObject(pstmt->pgaceItem); ++ ++ /* ++ * expand table inheritances ++ */ ++ selist = expandEvalItemInheritance(selist); ++ ++ /* ++ * add checks for access via trigger function ++ */ ++ foreach(l, pstmt->resultRelations) ++ { ++ Index rindex = lfirst_int(l); ++ ++ rte = rt_fetch(rindex, pstmt->rtable); ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ selist = addEvalTriggerFunction(selist, rte->relid, ++ pstmt->commandType); ++ } ++ ++ /* ++ * Check SelinuxEvalItem ++ */ ++ foreach (l, selist) ++ checkSelinuxEvalItem((SelinuxEvalItem *) lfirst(l)); ++} ++ ++/* ++ * -------------------------------------------------------------- ++ * Process Utility hooks ++ * -------------------------------------------------------------- ++ */ ++ ++/* ++ * sepgsqlProcessUtility ++ * ++ * This function is invoked from the head of ProcessUtility(), and ++ * checks given DDL queries. ++ * SE-PostgreSQL catch most of DDL actions on HeapTuple hooks, but ++ * an exception is TRUNCATE statement. ++ */ ++void ++sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel) ++{ ++ switch (nodeTag(parsetree)) ++ { ++ case T_LoadStmt: ++ sepgsqlCheckModuleInstallPerms(((LoadStmt *)parsetree)->filename); ++ break; ++ ++ default: ++ /* do nothing */ ++ break; ++ } ++} ++ ++/* ---------------------------------------------------------- ++ * COPY TO/COPY FROM statement hooks ++ * ---------------------------------------------------------- */ ++ ++/* ++ * sepgsqlCopyTable ++ * ++ * This function checks permission on the target table and columns ++ * of COPY statement. We don't place it at sepgsql/hooks.c because ++ * it internally uses addEvalXXXX() interface statically declared. ++ */ ++void ++sepgsqlCopyTable(Relation rel, List *attNumList, bool isFrom) ++{ ++ List *selist = NIL; ++ ListCell *l; ++ ++ /* ++ * on 'COPY FROM SELECT ...' cases, any checkings are done in select.c ++ */ ++ if (rel == NULL) ++ return; ++ ++ /* ++ * no need to check non-table relation ++ */ ++ if (RelationGetForm(rel)->relkind != RELKIND_RELATION) ++ return; ++ ++ selist = addEvalRelation(selist, RelationGetRelid(rel), false, ++ isFrom ? DB_TABLE__INSERT : DB_TABLE__SELECT); ++ foreach(l, attNumList) ++ { ++ AttrNumber attnum = lfirst_int(l); ++ ++ selist = addEvalAttribute(selist, RelationGetRelid(rel), false, attnum, ++ isFrom ? DB_COLUMN__INSERT : DB_COLUMN__SELECT); ++ } ++ ++ /* ++ * check call trigger function ++ */ ++ if (isFrom) ++ selist = addEvalTriggerFunction(selist, RelationGetRelid(rel), CMD_INSERT); ++ ++ foreach (l, selist) ++ checkSelinuxEvalItem((SelinuxEvalItem *) lfirst(l)); ++} ++ ++/* ++ * sepgsqlCopyFile ++ * ++ * This function check permission whether the client can ++ * read from/write to the given file. ++ */ ++void sepgsqlCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom) ++{ ++ security_context_t context; ++ security_class_t tclass ++ = sepgsqlFileObjectClass(fdesc, filename); ++ ++ if (fgetfilecon_raw(fdesc, &context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get context of %s", filename))); ++ PG_TRY(); ++ { ++ sepgsqlComputePermission(sepgsqlGetClientContext(), ++ context, ++ tclass, ++ isFrom ? FILE__READ : FILE__WRITE, ++ filename); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++} ++ ++/* ++ * sepgsqlCopyToTuple ++ * ++ * This function check permission to read the given tuple. ++ * If not allowed to read, it returns false to skip COPY TO ++ * this tuple. In the result, any violated tuples are filtered ++ * from the result of COPY TO, as if these are not exist. ++ */ ++bool ++sepgsqlCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) ++{ ++ uint32 perms = SEPGSQL_PERMS_SELECT; ++ ++ /* ++ * for 'pg_largeobject' ++ */ ++ if (RelationGetRelid(rel) == LargeObjectRelationId) ++ { ++ ListCell *l; ++ ++ foreach(l, attNumList) ++ { ++ AttrNumber attnum = lfirst_int(l); ++ ++ if (attnum == Anum_pg_largeobject_data) ++ { ++ perms |= SEPGSQL_PERMS_READ; ++ break; ++ } ++ } ++ } ++ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, false); ++} +diff -rpNU3 base/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c +--- base/src/backend/storage/file/fd.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/storage/file/fd.c 2008-06-14 02:36:58.000000000 +0900 +@@ -1241,6 +1241,13 @@ FileTruncate(File file, long offset) + return returnCode; + } + ++int ++FileRawDescriptor(File file) ++{ ++ Assert(FileIsValid(file)); ++ ++ return VfdCache[file].fd; ++} + + /* + * Routines that want to use stdio (ie, FILE*) should use AllocateFile +diff -rpNU3 base/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c +--- base/src/backend/storage/ipc/ipci.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/storage/ipc/ipci.c 2008-06-14 02:36:58.000000000 +0900 +@@ -25,6 +25,7 @@ + #include "postmaster/autovacuum.h" + #include "postmaster/bgwriter.h" + #include "postmaster/postmaster.h" ++#include "security/pgace.h" + #include "storage/freespace.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +@@ -117,6 +118,7 @@ CreateSharedMemoryAndSemaphores(bool mak + #ifdef EXEC_BACKEND + size = add_size(size, ShmemBackendArraySize()); + #endif ++ size = add_size(size, pgaceShmemSize()); + + /* freeze the addin request size and include it */ + addin_request_allowed = false; +diff -rpNU3 base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c +--- base/src/backend/tcop/fastpath.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/tcop/fastpath.c 2009-01-16 17:07:29.000000000 +0900 +@@ -28,6 +28,7 @@ + #include "miscadmin.h" + #include "tcop/fastpath.h" + #include "tcop/tcopprot.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" +@@ -347,6 +348,7 @@ HandleFunctionRequest(StringInfo msgBuf) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(fid)); ++ pgaceCallFunction(&fip->flinfo); + + /* + * Prepare function call info block and insert arguments. +diff -rpNU3 base/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c +--- base/src/backend/tcop/pquery.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/tcop/pquery.c 2008-11-21 23:11:55.000000000 +0900 +@@ -560,7 +560,7 @@ PortalStart(Portal portal, ParamListInfo + Assert(pstmt->returningLists); + portal->tupDesc = + ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), +- false); ++ false, false); + } + + /* +diff -rpNU3 base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c +--- base/src/backend/tcop/utility.c 2008-11-05 09:57:00.000000000 +0900 ++++ sepgsql/src/backend/tcop/utility.c 2008-11-05 10:01:30.000000000 +0900 +@@ -49,6 +49,7 @@ + #include "postmaster/bgwriter.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteRemove.h" ++#include "security/pgace.h" + #include "storage/fd.h" + #include "tcop/pquery.h" + #include "tcop/utility.h" +@@ -397,6 +398,8 @@ ProcessUtility(Node *parsetree, + if (completionTag) + completionTag[0] = '\0'; + ++ pgaceProcessUtility(parsetree, params, isTopLevel); ++ + switch (nodeTag(parsetree)) + { + /* +diff -rpNU3 base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c +--- base/src/backend/utils/adt/ri_triggers.c 2008-09-25 15:09:40.000000000 +0900 ++++ sepgsql/src/backend/utils/adt/ri_triggers.c 2009-02-25 22:31:25.000000000 +0900 +@@ -37,6 +37,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_relation.h" + #include "miscadmin.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" +@@ -3256,6 +3257,7 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPl + int spi_result; + Oid save_userid; + bool save_secdefcxt; ++ bool save_pgace; + Datum vals[RI_MAX_NUMKEYS * 2]; + char nulls[RI_MAX_NUMKEYS * 2]; + +@@ -3336,11 +3338,22 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPl + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); + +- /* Finally we can run the query. */ +- spi_result = SPI_execute_snapshot(qplan, +- vals, nulls, +- test_snapshot, crosscheck_snapshot, +- false, false, limit); ++ save_pgace = pgaceRowlvBehaviorSwitchTo(detectNewRows); ++ PG_TRY(); ++ { ++ /* Finally we can run the query. */ ++ spi_result = SPI_execute_snapshot(qplan, ++ vals, nulls, ++ test_snapshot, crosscheck_snapshot, ++ false, false, limit); ++ } ++ PG_CATCH(); ++ { ++ pgaceRowlvBehaviorSwitchTo(save_pgace); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ pgaceRowlvBehaviorSwitchTo(save_pgace); + + /* Restore UID */ + SetUserIdAndContext(save_userid, save_secdefcxt); +diff -rpNU3 base/src/backend/utils/cache/catcache.c sepgsql/src/backend/utils/cache/catcache.c +--- base/src/backend/utils/cache/catcache.c 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/utils/cache/catcache.c 2009-01-16 17:07:29.000000000 +0900 +@@ -1313,6 +1313,38 @@ ReleaseCatCache(HeapTuple tuple) + CatCacheRemoveCTup(ct->my_cache, ct); + } + ++/* ++ * InsertCatCache ++ * ++ * This function enables to refer a tuple recently inserted, using catcache ++ * until next CommandCounterIncrement. ++ */ ++void InsertCatCache(CatCache *cache, HeapTuple tuple) ++{ ++ ScanKeyData skey[4]; ++ uint32 hashValue; ++ Index hashIndex; ++ bool isnull; ++ int i; ++ ++ /* initialize the search key information */ ++ memcpy(skey, cache->cc_skey, sizeof(skey)); ++ for (i=0; i < cache->cc_nkeys; i++) ++ { ++ skey[i].sk_argument = heap_getattr(tuple, cache->cc_key[i], ++ cache->cc_tupdesc, &isnull); ++ Assert(!isnull); ++ } ++ ++ /* find the hash bucket in which to look for the tuple */ ++ if (cache->cc_tupdesc == NULL) ++ CatalogCacheInitializeCache(cache); ++ hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, skey); ++ hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); ++ ++ /* Insert a new tuple */ ++ CatalogCacheCreateEntry(cache, tuple, hashValue, hashIndex, false); ++} + + /* + * SearchCatCacheList +diff -rpNU3 base/src/backend/utils/cache/plancache.c sepgsql/src/backend/utils/cache/plancache.c +--- base/src/backend/utils/cache/plancache.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/utils/cache/plancache.c 2009-02-02 11:58:34.000000000 +0900 +@@ -880,12 +880,14 @@ PlanCacheComputeResultDesc(List *stmt_li + if (IsA(node, Query)) + { + query = (Query *) node; +- return ExecCleanTypeFromTL(query->targetList, false); ++ return ExecCleanTypeFromTL(query->targetList, ++ false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; +- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); ++ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, ++ false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +@@ -896,13 +898,15 @@ PlanCacheComputeResultDesc(List *stmt_li + { + query = (Query *) node; + Assert(query->returningList); +- return ExecCleanTypeFromTL(query->returningList, false); ++ return ExecCleanTypeFromTL(query->returningList, ++ false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + Assert(pstmt->returningLists); +- return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); ++ return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ++ false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +diff -rpNU3 base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cache/relcache.c +--- base/src/backend/utils/cache/relcache.c 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/backend/utils/cache/relcache.c 2009-02-02 11:58:34.000000000 +0900 +@@ -54,6 +54,7 @@ + #include "optimizer/prep.h" + #include "optimizer/var.h" + #include "rewrite/rewriteDefine.h" ++#include "security/pgace.h" + #include "storage/fd.h" + #include "storage/smgr.h" + #include "utils/builtins.h" +@@ -324,7 +325,13 @@ AllocateRelationDesc(Relation relation, + /* initialize relation tuple form */ + relation->rd_rel = relationForm; + +- /* and allocate attribute tuple form storage */ ++ /* ++ * and allocate attribute tuple form storage ++ * ++ * Please note that relation->rd_att->tdhasseclabel should be fixed ++ * up correctly at RelationBuildTupleDesc(), because security module ++ * may need reloptions info to make its decision. ++ */ + relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, + relationForm->relhasoids); + /* which we mark as a reference-counted tupdesc */ +@@ -877,6 +884,10 @@ RelationBuildDesc(Oid targetRelId, Relat + /* extract reloptions if any */ + RelationParseRelOptions(relation, pg_class_tuple); + ++ /* fixup relation->rd_att->tdhasseclabel */ ++ relation->rd_att->tdhasseclabel ++ = pgaceTupleDescHasSecLabel(relation, NIL); ++ + /* + * initialize the relation lock manager information + */ +@@ -1462,6 +1473,12 @@ formrdesc(const char *relationName, Oid + relation->rd_rel->relfilenode = RelationGetRelid(relation); + + /* ++ * Fixup relation->rd_att->tdhasseclabel ++ */ ++ RelationGetDescr(relation)->tdhasseclabel ++ = pgaceTupleDescHasSecLabel(relation, NIL); ++ ++ /* + * initialize the relation lock manager information + */ + RelationInitLockInfo(relation); /* see lmgr.c */ +@@ -2687,6 +2704,13 @@ BuildHardcodedDescriptor(int natts, Form + + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + ++ /* ++ * NOTE: we assume the returned TupleDesc is only used for ++ * references to toast'ed data, and it is not delivered to ++ * heap_form_tuple(), so TupleDesc->tdhasseclabel does not ++ * give any effect. ++ * We omit to invoke pgaceTupleDescHasSecurity() here. ++ */ + result = CreateTemplateTupleDesc(natts, hasoids); + result->tdtypeid = RECORDOID; /* not right, but we don't care */ + result->tdtypmod = -1; +@@ -3446,6 +3470,12 @@ load_relcache_init_file(void) + rel->rd_options = NULL; + } + ++ /* ++ * fixup rel->rd_att->tdhasseclabel ++ */ ++ rel->rd_att->tdhasseclabel ++ = pgaceTupleDescHasSecLabel(rel, NIL); ++ + /* mark not-null status */ + if (has_not_null) + { +diff -rpNU3 base/src/backend/utils/cache/syscache.c sepgsql/src/backend/utils/cache/syscache.c +--- base/src/backend/utils/cache/syscache.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/utils/cache/syscache.c 2008-06-14 02:36:58.000000000 +0900 +@@ -39,6 +39,7 @@ + #include "catalog/pg_opfamily.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_rewrite.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_ts_config.h" + #include "catalog/pg_ts_config_map.h" +@@ -676,7 +677,31 @@ static const struct cachedesc cacheinfo[ + 0 + }, + 1024 +- } ++ }, ++ {SecurityRelationId, /*SECURITYOID */ ++ SecurityOidIndexId, ++ 0, ++ 1, ++ { ++ ObjectIdAttributeNumber, ++ 0, ++ 0, ++ 0 ++ }, ++ 128 ++ }, ++ {SecurityRelationId, /* SECURITYLABEL */ ++ SecuritySeclabelIndexId, ++ 0, ++ 1, ++ { ++ Anum_pg_security_seclabel, ++ 0, ++ 0, ++ 0 ++ }, ++ 128 ++ }, + }; + + static CatCache *SysCache[ +@@ -784,6 +809,21 @@ ReleaseSysCache(HeapTuple tuple) + } + + /* ++ * InsertSysCache ++ * interts a tuple temporary until next CommandCounterIncrement ++ */ ++void InsertSysCache(Oid relid, HeapTuple tuple) ++{ ++ int cacheId; ++ ++ for (cacheId = 0; cacheId < SysCacheSize; cacheId++) ++ { ++ if (SysCache[cacheId]->cc_reloid == relid) ++ InsertCatCache(SysCache[cacheId], tuple); ++ } ++} ++ ++/* + * SearchSysCacheCopy + * + * A convenience routine that does SearchSysCache and (if successful) +diff -rpNU3 base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c +--- base/src/backend/utils/fmgr/dfmgr.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/utils/fmgr/dfmgr.c 2008-06-26 10:27:05.000000000 +0900 +@@ -22,6 +22,7 @@ + #include "port/dynloader/win32.h" + #endif + #include "miscadmin.h" ++#include "security/pgace.h" + #include "utils/dynamic_loader.h" + #include "utils/hsearch.h" + +@@ -73,7 +74,6 @@ char *Dynamic_library_path; + static void *internal_load_library(const char *libname); + static void internal_unload_library(const char *libname); + static bool file_exists(const char *name); +-static char *expand_dynamic_library_name(const char *name); + static void check_restricted_library_name(const char *name); + static char *substitute_libpath_macro(const char *name); + static char *find_in_dynamic_libpath(const char *basename); +@@ -106,6 +106,9 @@ load_external_function(char *filename, c + /* Expand the possibly-abbreviated filename to an exact path name */ + fullname = expand_dynamic_library_name(filename); + ++ /* Check whether the shared library should be loaded, or not */ ++ pgaceLoadSharedModule(fullname); ++ + /* Load the shared library, unless we already did */ + lib_handle = internal_load_library(fullname); + +@@ -146,6 +149,9 @@ load_file(const char *filename, bool res + /* Expand the possibly-abbreviated filename to an exact path name */ + fullname = expand_dynamic_library_name(filename); + ++ /* Check whether the library should be loaded, or not */ ++ pgaceLoadSharedModule(fullname); ++ + /* Unload the library if currently loaded */ + internal_unload_library(fullname); + +@@ -395,7 +401,7 @@ file_exists(const char *name) + * + * The result will always be freshly palloc'd. + */ +-static char * ++char * + expand_dynamic_library_name(const char *name) + { + bool have_slash; +diff -rpNU3 base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c +--- base/src/backend/utils/init/postinit.c 2008-09-25 15:09:40.000000000 +0900 ++++ sepgsql/src/backend/utils/init/postinit.c 2008-09-25 15:22:04.000000000 +0900 +@@ -31,6 +31,7 @@ + #include "pgstat.h" + #include "postmaster/autovacuum.h" + #include "postmaster/postmaster.h" ++#include "security/pgace.h" + #include "storage/backendid.h" + #include "storage/fd.h" + #include "storage/ipc.h" +@@ -607,6 +608,9 @@ InitPostgres(const char *in_dbname, Oid + if (!bootstrap) + pgstat_bestart(); + ++ /* initialize mandatory access control facilities */ ++ pgaceInitialize(bootstrap); ++ + /* close the transaction we started above */ + if (!bootstrap) + CommitTransactionCommand(); +diff -rpNU3 base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c +--- base/src/backend/utils/misc/guc.c 2008-09-25 15:09:40.000000000 +0900 ++++ sepgsql/src/backend/utils/misc/guc.c 2008-12-28 01:06:59.000000000 +0900 +@@ -54,6 +54,7 @@ + #include "postmaster/postmaster.h" + #include "postmaster/syslogger.h" + #include "postmaster/walwriter.h" ++#include "security/pgace.h" + #include "storage/fd.h" + #include "storage/freespace.h" + #include "tcop/tcopprot.h" +@@ -273,7 +274,6 @@ static bool integer_datetimes; + char *role_string; + char *session_authorization_string; + +- + /* + * Displayable names for context types (enum GucContext) + * +@@ -2460,6 +2460,26 @@ static struct config_string ConfigureNam + }, + #endif /* USE_SSL */ + ++ { ++ {"pgace_feature", PGC_POSTMASTER, UNGROUPED, ++ gettext_noop("A option to choose an enhanced security feature which is " ++ "a guest of PGACE security framework"), ++ NULL, ++ }, ++ &pgace_feature_string, ++ "none", pgaceAssignFeatureString, NULL, ++ }, ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql", PGC_POSTMASTER, PRESET_OPTIONS, ++ gettext_noop("SE-PostgreSQL mode (default|permissive|enforcing|disabled)"), ++ NULL, ++ }, ++ &sepostgresql_mode_string, ++ "default", sepgsqlAssignModeString, NULL, ++ }, ++#endif ++ + /* End-of-list marker */ + { + {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL +@@ -3300,6 +3320,8 @@ ResetAllOptions(void) + { + int i; + ++ pgaceSetDatabaseParam("all", NULL); ++ + for (i = 0; i < num_guc_variables; i++) + { + struct config_generic *gconf = guc_variables[i]; +@@ -4972,6 +4994,7 @@ ExecSetVariableStmt(VariableSetStmt *stm + { + case VAR_SET_VALUE: + case VAR_SET_CURRENT: ++ pgaceSetDatabaseParam(stmt->name, ExtractSetVariableArgs(stmt)); + set_config_option(stmt->name, + ExtractSetVariableArgs(stmt), + (superuser() ? PGC_SUSET : PGC_USERSET), +@@ -5029,6 +5052,7 @@ ExecSetVariableStmt(VariableSetStmt *stm + break; + case VAR_SET_DEFAULT: + case VAR_RESET: ++ pgaceSetDatabaseParam(stmt->name, NULL); + set_config_option(stmt->name, + NULL, + (superuser() ? PGC_SUSET : PGC_USERSET), +@@ -5357,6 +5381,9 @@ EmitWarningsOnPlaceholders(const char *c + void + GetPGVariable(const char *name, DestReceiver *dest) + { ++ /* Check get param permissions */ ++ pgaceGetDatabaseParam(name); ++ + if (guc_name_compare(name, "all") == 0) + ShowAllGUCConfig(dest); + else +diff -rpNU3 base/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backend/utils/misc/postgresql.conf.sample +--- base/src/backend/utils/misc/postgresql.conf.sample 2008-02-03 01:11:28.000000000 +0900 ++++ sepgsql/src/backend/utils/misc/postgresql.conf.sample 2008-12-28 01:19:14.000000000 +0900 +@@ -487,6 +487,12 @@ + + + #------------------------------------------------------------------------------ ++# ENHANCED SECURITY OPTIONS ++#------------------------------------------------------------------------------ ++ ++#pgace_feature = 'none' ++ ++#------------------------------------------------------------------------------ + # CUSTOMIZED OPTIONS + #------------------------------------------------------------------------------ + +diff -rpNU3 base/src/include/access/heapam.h sepgsql/src/include/access/heapam.h +--- base/src/include/access/heapam.h 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/include/access/heapam.h 2008-11-21 23:11:55.000000000 +0900 +@@ -249,7 +249,7 @@ extern void heap_free_minimal_tuple(Mini + extern MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup); + extern HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup); + extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup); +-extern HeapTuple heap_addheader(int natts, bool withoid, ++extern HeapTuple heap_addheader(int natts, bool withoid, bool withsecurity, + Size structlen, void *structure); + + /* in heap/pruneheap.c */ +diff -rpNU3 base/src/include/access/htup.h sepgsql/src/include/access/htup.h +--- base/src/include/access/htup.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/access/htup.h 2008-12-28 01:06:59.000000000 +0900 +@@ -161,7 +161,7 @@ typedef HeapTupleHeaderData *HeapTupleHe + #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ + #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ + #define HEAP_HASOID 0x0008 /* has an object-id field */ +-/* bit 0x0010 is available */ ++#define HEAP_HAS_SECLABEL 0x0010 /* has an security label field */ + #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ + #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ + #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ +@@ -288,6 +288,9 @@ do { \ + (tup)->t_choice.t_datum.datum_typmod = (typmod) \ + ) + ++#define HeapTupleHeaderHasOid(tup) \ ++ ((tup)->t_infomask & HEAP_HASOID) ++ + #define HeapTupleHeaderGetOid(tup) \ + ( \ + ((tup)->t_infomask & HEAP_HASOID) ? \ +@@ -347,6 +350,34 @@ do { \ + (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ + ) + ++#define HeapTupleHeaderHasSecLabel(tup) \ ++ ((tup)->t_infomask & HEAP_HAS_SECLABEL) ++ ++#define HeapTupleHeaderGetSecLabel(tup) \ ++ ( \ ++ HeapTupleHeaderHasSecLabel(tup) \ ++ ? (*((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid)))) \ ++ : InvalidOid \ ++ ) ++ ++#define HeapTupleHeaderSetSecLabel(tup, seclabel) \ ++ do { \ ++ Assert(HeapTupleHeaderHasSecLabel(tup)); \ ++ *((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) = (seclabel); \ ++ } while(0) ++ ++#define HeapTupleHasSecLabel(tuple) \ ++ HeapTupleHeaderHasSecLabel((tuple)->t_data) ++ ++#define HeapTupleGetSecLabel(tuple) \ ++ HeapTupleHeaderGetSecLabel((tuple)->t_data) ++ ++#define HeapTupleSetSecLabel(tuple, seclabel) \ ++ HeapTupleHeaderSetSecLabel((tuple)->t_data, (seclabel)) + + /* + * BITMAPLEN(NATTS) - +@@ -402,8 +433,8 @@ do { \ + #define MaxTransactionIdAttributeNumber (-5) + #define MaxCommandIdAttributeNumber (-6) + #define TableOidAttributeNumber (-7) +-#define FirstLowInvalidHeapAttributeNumber (-8) +- ++#define SecurityLabelAttributeNumber (-8) ++#define FirstLowInvalidHeapAttributeNumber (-9) + + /* + * MinimalTuple is an alternative representation that is used for transient +@@ -548,6 +579,9 @@ typedef HeapTupleData *HeapTuple; + #define HeapTupleClearHeapOnly(tuple) \ + HeapTupleHeaderClearHeapOnly((tuple)->t_data) + ++#define HeapTupleHasOid(tuple) \ ++ HeapTupleHeaderHasOid((tuple)->t_data) ++ + #define HeapTupleGetOid(tuple) \ + HeapTupleHeaderGetOid((tuple)->t_data) + +diff -rpNU3 base/src/include/access/tupdesc.h sepgsql/src/include/access/tupdesc.h +--- base/src/include/access/tupdesc.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/access/tupdesc.h 2008-12-28 01:06:59.000000000 +0900 +@@ -75,6 +75,7 @@ typedef struct tupleDesc + Oid tdtypeid; /* composite type ID for tuple type */ + int32 tdtypmod; /* typmod for tuple type */ + bool tdhasoid; /* tuple has oid attribute in its header */ ++ bool tdhasseclabel; /* tuple has security label in its header */ + int tdrefcount; /* reference count, or -1 if not counting */ + } *TupleDesc; + +diff -rpNU3 base/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h +--- base/src/include/catalog/heap.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/catalog/heap.h 2008-12-05 16:28:22.000000000 +0900 +@@ -52,7 +52,8 @@ extern Oid heap_create_with_catalog(cons + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +- bool allow_system_table_mods); ++ bool allow_system_table_mods, ++ List *pgace_attr_list); + + extern void heap_drop_with_catalog(Oid relid); + +@@ -65,7 +66,8 @@ extern List *heap_truncate_find_FKs(List + extern void InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +- Datum reloptions); ++ Datum reloptions, ++ List *pgace_attr_list); + + extern List *AddRelationRawConstraints(Relation rel, + List *rawColDefaults, +@@ -96,6 +98,8 @@ extern Form_pg_attribute SystemAttribute + extern Form_pg_attribute SystemAttributeByName(const char *attname, + bool relhasoids); + ++extern bool SystemAttributeIsWritable(AttrNumber attnum); ++ + extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind); + + extern void CheckAttributeType(const char *attname, Oid atttypid); +diff -rpNU3 base/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h +--- base/src/include/catalog/indexing.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/catalog/indexing.h 2008-06-14 02:36:58.000000000 +0900 +@@ -252,6 +252,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, + DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); + #define TypeNameNspIndexId 2704 + ++DECLARE_UNIQUE_INDEX(pg_security_oid_index, 3401, on pg_security using btree(oid oid_ops)); ++#define SecurityOidIndexId 3401 ++DECLARE_UNIQUE_INDEX(pg_security_seclabel_index, 3402, on pg_security using btree(seclabel text_ops)); ++#define SecuritySeclabelIndexId 3402 ++ + /* last step of initialization script: build the indexes declared above */ + BUILD_INDICES + +diff -rpNU3 base/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/pg_attribute.h +--- base/src/include/catalog/pg_attribute.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/catalog/pg_attribute.h 2008-12-28 01:06:59.000000000 +0900 +@@ -282,6 +282,7 @@ DATA(insert ( 1247 cmin 29 0 4 -4 0 + DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); ++DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); + + /* ---------------- + * pg_proc +@@ -338,6 +339,7 @@ DATA(insert ( 1255 cmin 29 0 4 -4 0 + DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); ++DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); + + /* ---------------- + * pg_attribute +@@ -386,6 +388,7 @@ DATA(insert ( 1249 cmin 29 0 4 -4 0 + DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); ++DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); + + /* ---------------- + * pg_class +@@ -454,6 +457,7 @@ DATA(insert ( 1259 cmin 29 0 4 -4 0 + DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); + DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); ++DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); + + /* ---------------- + * pg_index +diff -rpNU3 base/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h +--- base/src/include/catalog/pg_proc.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/catalog/pg_proc.h 2008-09-30 11:51:54.000000000 +0900 +@@ -4113,6 +4113,22 @@ DESCR("I/O"); + DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 f f t f i 1 23 "2950" _null_ _null_ _null_ uuid_hash - _null_ _null_ )); + DESCR("hash"); + ++/* PostgreSQL Access Control Extension related functions */ ++DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 1 0 f f t f v 1 25 "26" _null_ _null_ _null_ lo_get_security - _null_ _null_ )); ++DATA(insert OID = 3411 ( lo_set_security PGNSP PGUID 12 1 0 f f t f v 2 16 "26 25" _null_ _null_ _null_ lo_set_security - _null_ _null_ )); ++ ++/* SE-PostgreSQL related function */ ++DATA(insert OID = 3450 ( sepgsql_getcon PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ sepgsql_getcon - _null_ _null_ )); ++DATA(insert OID = 3451 ( sepgsql_getservcon PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ sepgsql_getservcon - _null_ _null_ )); ++DATA(insert OID = 3452 ( sepgsql_get_user PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_user - _null_ _null_ )); ++DATA(insert OID = 3453 ( sepgsql_set_user PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_user - _null_ _null_ )); ++DATA(insert OID = 3454 ( sepgsql_get_role PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_role - _null_ _null_ )); ++DATA(insert OID = 3455 ( sepgsql_set_role PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_role - _null_ _null_ )); ++DATA(insert OID = 3456 ( sepgsql_get_type PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_type - _null_ _null_ )); ++DATA(insert OID = 3457 ( sepgsql_set_type PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_type - _null_ _null_ )); ++DATA(insert OID = 3458 ( sepgsql_get_range PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_range - _null_ _null_ )); ++DATA(insert OID = 3459 ( sepgsql_set_range PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_range - _null_ _null_ )); ++ + /* enum related procs */ + DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 f f t f i 1 3500 "2275" _null_ _null_ _null_ anyenum_in - _null_ _null_ )); + DESCR("I/O"); +@@ -4460,7 +4476,8 @@ extern Oid ProcedureCreate(const char *p + Datum parameterNames, + Datum proconfig, + float4 procost, +- float4 prorows); ++ float4 prorows, ++ void *pgaceItem); + + extern bool function_parse_error_transpose(const char *prosrc); + +diff -rpNU3 base/src/include/catalog/pg_security.h sepgsql/src/include/catalog/pg_security.h +--- base/src/include/catalog/pg_security.h 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/include/catalog/pg_security.h 2008-06-15 22:24:53.000000000 +0900 +@@ -0,0 +1,31 @@ ++/* ++ * src/include/catalog/pg_security.h ++ * Definition of the security label relation (pg_security) ++ * ++ * Copyright (c) 2006 - 2007 KaiGai Kohei ++ */ ++#ifndef PG_SECURITY_H ++#define PG_SECURITY_H ++ ++#define SecurityRelationId 3400 ++ ++CATALOG(pg_security,3400) BKI_SHARED_RELATION ++{ ++ text seclabel; /* text representation of security label */ ++} FormData_pg_security; ++ ++/* ---------------- ++ * Form_pg_security corresponds to a pointer to a tuple with ++ * the format of pg_security relation. ++ * ---------------- ++ */ ++typedef FormData_pg_security *Form_pg_security; ++ ++/* ---------------- ++ * compiler constants for pg_selinux ++ * ---------------- ++ */ ++#define Natts_pg_security 1 ++#define Anum_pg_security_seclabel 1 ++ ++#endif /* PG_SELINUX_H */ +diff -rpNU3 base/src/include/executor/executor.h sepgsql/src/include/executor/executor.h +--- base/src/include/executor/executor.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/executor/executor.h 2008-12-28 01:06:59.000000000 +0900 +@@ -116,7 +116,7 @@ extern TupleHashEntry FindTupleHashEntry + /* + * prototypes from functions in execJunk.c + */ +-extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, ++extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, bool hassecurity, + TupleTableSlot *slot); + extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, + TupleDesc cleanTupType, +@@ -140,6 +140,7 @@ extern void ExecutorEnd(QueryDesc *query + extern void ExecutorRewind(QueryDesc *queryDesc); + extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); + extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); ++extern bool ExecContextForcesSecLabel(PlanState *planstate, bool *hasseclabel); + extern void ExecConstraints(ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, EState *estate); + extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, +@@ -199,8 +200,8 @@ extern void ExecInitScanTupleSlot(EState + extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); + extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, + TupleDesc tupType); +-extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); +-extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); ++extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); ++extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); + extern TupleDesc ExecTypeFromExprList(List *exprList); + extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); + +diff -rpNU3 base/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h +--- base/src/include/executor/tuptable.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/executor/tuptable.h 2008-12-28 01:06:59.000000000 +0900 +@@ -118,6 +118,9 @@ typedef struct TupleTableSlot + MinimalTuple tts_mintuple; /* set if it's a minimal tuple, else NULL */ + HeapTupleData tts_minhdr; /* workspace if it's a minimal tuple */ + long tts_off; /* saved state for slot_deform_tuple */ ++ ++ /* temporary storage variables for writable system column */ ++ Datum tts_seclabel; /* for security label */ + } TupleTableSlot; + + /* +diff -rpNU3 base/src/include/fmgr.h sepgsql/src/include/fmgr.h +--- base/src/include/fmgr.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/fmgr.h 2009-01-16 17:07:29.000000000 +0900 +@@ -52,6 +52,8 @@ typedef struct FmgrInfo + void *fn_extra; /* extra space for use by handler */ + MemoryContext fn_mcxt; /* memory context to store fn_extra in */ + fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ ++ ++ void *fn_pgaceItem; /* PGACE opaque field */ + } FmgrInfo; + + /* +@@ -511,6 +513,7 @@ extern Oid get_call_expr_argtype(fmNodeP + */ + extern char *Dynamic_library_path; + ++extern char *expand_dynamic_library_name(const char *name); + extern PGFunction load_external_function(char *filename, char *funcname, + bool signalNotFound, void **filehandle); + extern PGFunction lookup_external_function(void *filehandle, char *funcname); +diff -rpNU3 base/src/include/libpq/be-fsstubs.h sepgsql/src/include/libpq/be-fsstubs.h +--- base/src/include/libpq/be-fsstubs.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/libpq/be-fsstubs.h 2008-06-14 02:36:58.000000000 +0900 +@@ -36,6 +36,9 @@ extern Datum lo_tell(PG_FUNCTION_ARGS); + extern Datum lo_unlink(PG_FUNCTION_ARGS); + extern Datum lo_truncate(PG_FUNCTION_ARGS); + ++extern Datum lo_get_security(PG_FUNCTION_ARGS); ++extern Datum lo_set_security(PG_FUNCTION_ARGS); ++ + /* + * These are not fmgr-callable, but are available to C code. + * Probably these should have had the underscore-free names, +diff -rpNU3 base/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h +--- base/src/include/nodes/nodes.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/nodes/nodes.h 2009-01-21 17:02:57.000000000 +0900 +@@ -358,7 +358,9 @@ typedef enum NodeTag + */ + T_TriggerData = 950, /* in commands/trigger.h */ + T_ReturnSetInfo, /* in nodes/execnodes.h */ +- T_TIDBitmap /* in nodes/tidbitmap.h */ ++ T_TIDBitmap, /* in nodes/tidbitmap.h */ ++ T_SelinuxEvalItem, /* in nodes/security.h */ ++ T_SEvalItemProcedure, /* in nodes/security.h */ + } NodeTag; + + /* +diff -rpNU3 base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h +--- base/src/include/nodes/parsenodes.h 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/include/nodes/parsenodes.h 2008-06-14 02:36:58.000000000 +0900 +@@ -131,6 +131,7 @@ typedef struct Query + + Node *setOperations; /* set-operation tree if this is top level of + * a UNION/INTERSECT/EXCEPT query */ ++ Node *pgaceItem; /* PGACE: an opaque item for security purpose */ + } Query; + + +@@ -391,6 +392,7 @@ typedef struct ColumnDef + Node *raw_default; /* default value (untransformed parse tree) */ + char *cooked_default; /* nodeToString representation */ + List *constraints; /* other constraints on column */ ++ Node *pgaceItem; /* PGACE: security attribute */ + } ColumnDef; + + /* +@@ -602,6 +604,15 @@ typedef struct RangeTblEntry + bool inFromCl; /* present in FROM clause? */ + AclMode requiredPerms; /* bitmask of required access permissions */ + Oid checkAsUser; /* if valid, check access as this role */ ++ ++ /* ++ * The guest of PGACE can use pgaceTuplePerms to mark permission set ++ * of tuple-level access controls. This field is copied to scan node ++ * (like SeqSan), and it can be refered within pgaceExecScan() hook. ++ * If this hook returns false, the given tuple is filtered from the ++ * result set. ++ */ ++ uint32 pgaceTuplePerms; + } RangeTblEntry; + + /* +@@ -917,7 +928,8 @@ typedef enum AlterTableType + AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */ + AT_DisableRule, /* DISABLE RULE name */ + AT_AddInherit, /* INHERIT parent */ +- AT_DropInherit /* NO INHERIT parent */ ++ AT_DropInherit, /* NO INHERIT parent */ ++ AT_SetSecurityLabel, /* PGACE: set security label */ + } AlterTableType; + + typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ +@@ -1108,6 +1120,7 @@ typedef struct CreateStmt + List *options; /* options from WITH clause */ + OnCommitAction oncommit; /* what do we do at COMMIT? */ + char *tablespacename; /* table space to use, or NULL */ ++ Node *pgaceItem; /* PGACE: security attribute */ + } CreateStmt; + + /* ---------- +diff -rpNU3 base/src/include/nodes/plannodes.h sepgsql/src/include/nodes/plannodes.h +--- base/src/include/nodes/plannodes.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/nodes/plannodes.h 2008-06-14 02:36:58.000000000 +0900 +@@ -73,6 +73,8 @@ typedef struct PlannedStmt + List *relationOids; /* OIDs of relations the plan depends on */ + + int nParamExec; /* number of PARAM_EXEC Params used */ ++ ++ Node *pgaceItem; /* PGACE: an opaque item for security purpose */ + } PlannedStmt; + + /* macro for fetching the Plan associated with a SubPlan node */ +@@ -216,6 +218,14 @@ typedef struct Scan + { + Plan plan; + Index scanrelid; /* relid is index into the range table */ ++ ++ /* ++ * pgaceTuplePerms is used to show permission set to be applied to ++ * tuple-leve access controls by security module. ++ * It is copied from related RangeTblEntry's one when Scan structure ++ * is created. ++ */ ++ uint32 pgaceTuplePerms; + } Scan; + + /* ---------------- +diff -rpNU3 base/src/include/nodes/relation.h sepgsql/src/include/nodes/relation.h +--- base/src/include/nodes/relation.h 2009-02-02 11:47:17.000000000 +0900 ++++ sepgsql/src/include/nodes/relation.h 2009-02-02 11:58:34.000000000 +0900 +@@ -366,6 +366,8 @@ typedef struct RelOptInfo + * list just to avoid recomputing the best inner indexscan repeatedly for + * similar outer relations. See comments for InnerIndexscanInfo. + */ ++ ++ uint32 pgaceTuplePerms; /* copied from RangeTblEntry */ + } RelOptInfo; + + /* +diff -rpNU3 base/src/include/nodes/security.h sepgsql/src/include/nodes/security.h +--- base/src/include/nodes/security.h 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/include/nodes/security.h 2009-01-21 17:02:57.000000000 +0900 +@@ -0,0 +1,40 @@ ++/*------------------------------------------------------------------------- ++ * ++ * src/include/nodes/security.h ++ * definitions for security extention related nodes ++ * ++ * Portions Copyright (c) 2007-2008, PostgreSQL Global Development Group ++ * ++ *------------------------------------------------------------------------- ++ */ ++#ifndef NODES_SECURITY_H ++#define NODES_SECURITY_H ++ ++#include "access/attnum.h" ++#include "nodes/nodes.h" ++ ++/* ++ * SelinuxEvalItem ++ * ++ * Required permissions on tables/columns used by SE-PostgreSQL. ++ * It is constracted just after query rewriter phase, then its ++ * list is checked based on the security policy of operating ++ * system. ++ * ++ * NOTE: attperms array can contains system attributes and ++ * whole-row-reference, so it is indexed as ++ * attperms[(attnum) + FirstLowInvalidHeapAttributeNumber - 1] ++ */ ++typedef struct SelinuxEvalItem ++{ ++ NodeTag type; ++ ++ Oid relid; /* relation id */ ++ bool inh; /* flags to inheritable/only */ ++ ++ uint32 relperms; /* required permissions on table */ ++ uint32 nattrs; /* length of attperms */ ++ uint32 *attperms; /* required permissions on columns */ ++} SelinuxEvalItem; ++ ++#endif /* NODES_SECURITY_H */ +diff -rpNU3 base/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in +--- base/src/include/pg_config.h.in 2008-01-28 16:06:37.000000000 +0900 ++++ sepgsql/src/include/pg_config.h.in 2008-12-12 18:45:55.000000000 +0900 +@@ -366,6 +366,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SECURITY_PAM_APPL_H + ++/* Define to 1 if you enable SELinux support */ ++#undef HAVE_SELINUX ++ + /* Define to 1 if you have the `setproctitle' function. */ + #undef HAVE_SETPROCTITLE + +diff -rpNU3 base/src/include/security/pgace.h sepgsql/src/include/security/pgace.h +--- base/src/include/security/pgace.h 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/include/security/pgace.h 2009-02-25 22:31:25.000000000 +0900 +@@ -0,0 +1,192 @@ ++/* ++ * include/security/pgace.h ++ * headers for PostgreSQL Access Control Extension (PGACE) ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#ifndef PGACE_H ++#define PGACE_H ++ ++#include "access/htup.h" ++#include "commands/trigger.h" ++#include "executor/execdesc.h" ++#include "fmgr.h" ++#include "nodes/params.h" ++#include "nodes/parsenodes.h" ++#include "nodes/plannodes.h" ++#include "storage/large_object.h" ++#include "utils/guc.h" ++#include "utils/rel.h" ++ ++#ifdef HAVE_SELINUX ++#include "security/sepgsql.h" ++#endif ++ ++/* ++ * pgace_feature : GUC parameter to choose an enhanced security feature ++ */ ++typedef enum ++{ ++ PGACE_FEATURE_NONE, ++#ifdef HAVE_SELINUX ++ PGACE_FEATURE_SELINUX, ++#endif ++} PgaceFeatureOpts; ++ ++extern int pgace_feature; ++extern char *pgace_feature_string; ++ ++/* ++ * Attribute names for the system-defined attributes ++ */ ++#define SecurityLabelAttributeName "security_context" ++ ++/* ++ * Initialization hooks ++ */ ++extern Size pgaceShmemSize(void); ++extern void pgaceInitialize(bool is_bootstrap); ++extern pid_t pgaceStartupWorkerProcess(void); ++ ++/* ++ * SQL proxy hooks ++ */ ++extern List *pgacePostQueryRewrite(List *queryList); ++extern void pgaceExecutorStart(QueryDesc *queryDesc, int eflags); ++extern void pgaceProcessUtility(Node *parsetree, ParamListInfo params, ++ bool isTopLevel); ++/* ++ * HeapTuple input/output hooks ++ */ ++extern bool pgaceRowlvBehaviorSwitchTo(bool new_abort); ++extern bool pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort); ++extern bool pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, ++ bool is_internal, bool with_returning); ++extern bool pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, ++ bool is_internal, bool with_returning); ++extern bool pgaceHeapTupleDelete(Relation rel, ItemPointer otid, ++ bool is_internal, bool with_returning); ++/* ++ * Enhanced SQL statements ++ */ ++extern bool pgaceIsGramSecurityItem(DefElem *defel); ++extern void pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel); ++extern void pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel); ++extern void pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel); ++extern void pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel); ++extern void pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel); ++extern void pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel); ++extern void pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel); ++extern void pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel); ++ ++/* ++ * Function related hooks ++ */ ++extern void pgaceCallFunction(FmgrInfo *finfo); ++extern void pgaceCallAggFunction(HeapTuple aggTuple); ++extern bool pgaceCallTriggerFunction(TriggerData *tgdata); ++extern bool pgaceAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple); ++ ++/* ++ * Misc hooks ++ */ ++extern void pgaceSetDatabaseParam(const char *name, char *argstring); ++extern void pgaceGetDatabaseParam(const char *name); ++extern void pgaceExecTruncate(List *trunc_rels); ++extern void pgaceLockTable(Oid relid); ++ ++/* ++ * COPY TO/FROM statement hooks ++ */ ++extern void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom); ++extern void pgaceCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom); ++extern bool pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple); ++ ++/* ++ * Loadable shared library module hooks ++ */ ++extern void pgaceLoadSharedModule(const char *filename); ++ ++/* ++ * Binary Large Object hooks ++ */ ++extern void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple); ++extern void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem); ++extern void pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length); ++extern void pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length); ++extern void pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset); ++extern void pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename); ++extern void pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename); ++extern void pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple); ++extern void pgaceLargeObjectSetSecurity(Relation rel, ++ HeapTuple newtup, HeapTuple oldtup); ++/* ++ * Security Label hooks ++ */ ++extern bool pgaceTupleDescHasSecLabel(Relation rel, List *relopts); ++extern char *pgaceTranslateSecurityLabelIn(char *seclabel); ++extern char *pgaceTranslateSecurityLabelOut(char *seclabel); ++extern bool pgaceCheckValidSecurityLabel(char *seclabel); ++extern char *pgaceUnlabeledSecurityLabel(void); ++extern char *pgaceSecurityLabelOfLabel(void); ++ ++/* ++ * PGACE common facilities (not hooks) ++ */ ++ ++/* security label management */ ++extern void pgacePostBootstrapingMode(void); ++ ++extern Oid pgaceLookupSecurityId(char *label); ++ ++extern char *pgaceLookupSecurityLabel(Oid sid); ++ ++extern Oid pgaceSecurityLabelToSid(char *label); ++ ++extern char *pgaceSidToSecurityLabel(Oid sid); ++ ++/* Enhanced SQL statements related */ ++extern List *pgaceRelationAttrList(CreateStmt *stmt); ++ ++extern void pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, ++ List *pgaceAttrList); ++extern void pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple, ++ List *pgaceAttrList); ++extern void pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd); ++ ++/* Export security system columns */ ++extern Datum pgaceHeapGetSecurityLabelSysattr(HeapTuple tuple); ++ ++/****************************************************************** ++ * Ported utility functions from 8.4devel ++ ******************************************************************/ ++#define CStringGetTextDatum(x) \ ++ (DirectFunctionCall1(textin, CStringGetDatum(x))) ++#define TextDatumGetCString(x) \ ++ (DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(x)))) ++ ++extern const char *pgaceAssignFeatureString(const char *value, bool doit, GucSource source); ++ ++/****************************************************************** ++ * SQL function declaration related to PGACE security framework ++ ******************************************************************/ ++ ++/* ++ * SQL functions ++ */ ++ ++/* SE-PostgreSQL */ ++extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); ++extern Datum sepgsql_getservcon(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_user(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_role(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_type(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_range(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_user(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_role(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_type(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_range(PG_FUNCTION_ARGS); ++ ++#endif // PGACE_H +diff -rpNU3 base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h +--- base/src/include/security/sepgsql.h 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/include/security/sepgsql.h 2009-02-25 22:31:25.000000000 +0900 +@@ -0,0 +1,242 @@ ++/* ++ * src/include/security/sepgsql.h ++ * headers for Security-Enhanced PostgreSQL (SE-PostgreSQL) ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ */ ++#ifndef SEPGSQL_H ++#define SEPGSQL_H ++ ++#include ++#include ++#include ++ ++/* ++ * SE-PostgreSQL modes ++ */ ++typedef enum ++{ ++ SEPGSQL_MODE_DEFAULT, ++ SEPGSQL_MODE_ENFORCING, ++ SEPGSQL_MODE_PERMISSIVE, ++ SEPGSQL_MODE_DISABLED, ++} SepgsqlModeType; ++ ++extern int sepostgresql_mode; ++extern char *sepostgresql_mode_string; ++extern bool sepostgresql_row_level; ++ ++extern const char *sepgsqlAssignModeString(const char *value, bool doit, GucSource source); ++ ++/* ++ * Permission bits delivered to sepgsqlCheckTuplePerms(). ++ * Please note that 0x000000ff of RangeTblEntry->pgaceTuplePerms ++ * are reserved by rowacl. These bits are also stored within ++ * pgaceTuplePerms, we have to avoid to use the lower bits. ++ */ ++#define SEPGSQL_PERMS_USE (1UL << 8) ++#define SEPGSQL_PERMS_SELECT (1UL << 9) ++#define SEPGSQL_PERMS_UPDATE (1UL << 10) ++#define SEPGSQL_PERMS_INSERT (1UL << 11) ++#define SEPGSQL_PERMS_DELETE (1UL << 12) ++#define SEPGSQL_PERMS_RELABELFROM (1UL << 13) ++#define SEPGSQL_PERMS_RELABELTO (1UL << 14) ++#define SEPGSQL_PERMS_READ (1UL << 15) ++#define SEPGSQL_PERMS_MASK (0xffffff00) ++ ++/* ++ * The implementation of PGACE/SE-PostgreSQL hooks ++ */ ++ ++/* Initialize / Finalize related hooks */ ++extern Size sepgsqlShmemSize(void); ++ ++extern void sepgsqlInitialize(bool is_bootstrap); ++ ++extern pid_t sepgsqlStartupWorkerProcess(void); ++ ++/* SQL proxy hooks */ ++extern List *sepgsqlPostQueryRewrite(List *queryList); ++ ++extern void sepgsqlExecutorStart(QueryDesc *queryDesc, int eflags); ++ ++extern void sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel); ++ ++/* ExecScan hooks */ ++extern bool sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort); ++ ++extern bool sepgsqlRowlvBehaviorSwitchTo(bool new_abort); ++ ++/* HeapTuple modification hooks */ ++extern bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, ++ bool is_internal, bool with_returning); ++extern bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, ++ HeapTuple newtup, bool is_internal, ++ bool with_returning); ++extern bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, ++ bool is_internal, bool with_returning); ++ ++/* Extended SQL statement hooks */ ++extern bool sepgsqlIsGramSecurityItem(DefElem *defel); ++ ++extern void sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++extern void sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++extern void sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++extern void sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++extern void sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++extern void sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++extern void sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++extern void sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, ++ DefElem *defel); ++ ++/* DATABASE related hooks */ ++extern void sepgsqlSetDatabaseParam(const char *name, char *argstring); ++ ++extern void sepgsqlGetDatabaseParam(const char *name); ++ ++/* FUNCTION related hooks */ ++extern void sepgsqlCallFunction(FmgrInfo *finfo); ++ ++extern void sepgsqlCallAggFunction(HeapTuple aggTuple); ++ ++extern bool sepgsqlCallTriggerFunction(TriggerData *tgdata); ++ ++extern bool sepgsqlAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple); ++ ++/* TABLE related hooks */ ++extern void sepgsqlLockTable(Oid relid); ++ ++extern void sepgsqlExecTruncate(List *trunc_rels); ++ ++extern bool sepgsqlAlterTable(Relation rel, AlterTableCmd *cmd); ++ ++/* COPY TO/COPY FROM statement hooks */ ++extern void sepgsqlCopyTable(Relation rel, List *attnumlist, bool is_from); ++ ++extern void sepgsqlCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom); ++ ++extern bool sepgsqlCopyToTuple(Relation rel, List *attnumlist, HeapTuple tuple); ++ ++/* Loadable shared library module hooks */ ++extern void sepgsqlLoadSharedModule(const char *filename); ++ ++/* Binary Large Object (BLOB) hooks */ ++extern void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple); ++ ++extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem); ++ ++extern void sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int length); ++ ++extern void sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int length); ++ ++extern void sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int offset); ++ ++extern void sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename); ++ ++extern void sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename); ++ ++extern void sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple); ++ ++extern void sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup); ++ ++/* Security Label hooks */ ++extern bool sepgsqlTupleDescHasSecLabel(Relation rel, List *relopts); ++ ++extern char *sepgsqlTranslateSecurityLabelIn(const char *context); ++ ++extern char *sepgsqlTranslateSecurityLabelOut(const char *context); ++ ++extern bool sepgsqlCheckValidSecurityLabel(char *context); ++ ++extern char *sepgsqlUnlabeledSecurityLabel(void); ++ ++extern char *sepgsqlSecurityLabelOfLabel(void); ++ ++/* ++ * SE-PostgreSQL core functions ++ * src/backend/security/sepgsql/core.c ++ */ ++extern bool sepgsqlIsEnabled(void); ++ ++extern const security_context_t sepgsqlGetServerContext(void); ++ ++extern const security_context_t sepgsqlGetClientContext(void); ++ ++extern const security_context_t sepgsqlGetDatabaseContext(void); ++ ++extern const security_context_t sepgsqlGetUnlabeledContext(void); ++ ++extern const security_context_t sepgsqlSwitchClientContext(security_context_t newcon); ++ ++extern Oid sepgsqlGetDatabaseSecurityId(void); ++ ++/* ++ * SE-PostgreSQL userspace avc functions ++ * src/backend/security/sepgsql/avc.c ++ */ ++extern void sepgsqlAvcInit(void); ++ ++extern void sepgsqlAvcSwitchClientContext(security_context_t context); ++ ++extern void sepgsqlClientHasPermission(Oid target_security_id, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname); ++ ++extern bool sepgsqlClientHasPermissionNoAbort(Oid target_security_id, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname); ++ ++extern Oid sepgsqlClientCreateSid(Oid target_security_id, ++ security_class_t tclass); ++ ++extern security_context_t ++sepgsqlClientCreateContext(Oid target_security_id, ++ security_class_t tclass); ++ ++extern bool sepgsqlComputePermission(const security_context_t scontext, ++ const security_context_t tcontext, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname); ++ ++extern security_context_t ++sepgsqlComputeCreateContext(const security_context_t scontext, ++ const security_context_t tcontext, ++ security_class_t tclass); ++ ++/* ++ * SE-PostgreSQL permission evaluation related ++ * src/backend/security/sepgsql/permission.c ++ */ ++extern const char *sepgsqlTupleName(Oid relid, HeapTuple tuple); ++ ++extern security_class_t sepgsqlFileObjectClass(int fdesc, const char *filename); ++ ++extern security_class_t sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple); ++ ++extern void sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple); ++ ++extern bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple newtup, ++ uint32 perms, bool abort); ++ ++extern void sepgsqlCheckModuleInstallPerms(const char *filename); ++ ++/* ++ * workaround for older libselinux ++ */ ++#ifndef DB_PROCEDURE__INSTALL ++#define DB_PROCEDURE__INSTALL 0x00000100UL ++#endif ++ ++#endif /* SEPGSQL_H */ +diff -rpNU3 base/src/include/storage/fd.h sepgsql/src/include/storage/fd.h +--- base/src/include/storage/fd.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/storage/fd.h 2008-06-14 02:36:58.000000000 +0900 +@@ -67,6 +67,7 @@ extern int FileWrite(File file, char *bu + extern int FileSync(File file); + extern long FileSeek(File file, long offset, int whence); + extern int FileTruncate(File file, long offset); ++extern int FileRawDescriptor(File file); + + /* Operations that allow use of regular stdio --- USE WITH CAUTION */ + extern FILE *AllocateFile(const char *name, const char *mode); +diff -rpNU3 base/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock.h +--- base/src/include/storage/lwlock.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/storage/lwlock.h 2008-06-14 02:36:58.000000000 +0900 +@@ -63,6 +63,7 @@ typedef enum LWLockId + AutovacuumLock, + AutovacuumScheduleLock, + SyncScanLock, ++ SepgsqlAvcLock, + /* Individual lock IDs end here */ + FirstBufMappingLock, + FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, +diff -rpNU3 base/src/include/utils/catcache.h sepgsql/src/include/utils/catcache.h +--- base/src/include/utils/catcache.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/utils/catcache.h 2008-06-14 02:36:58.000000000 +0900 +@@ -172,6 +172,7 @@ extern HeapTuple SearchCatCache(CatCache + Datum v1, Datum v2, + Datum v3, Datum v4); + extern void ReleaseCatCache(HeapTuple tuple); ++extern void InsertCatCache(CatCache *cache, HeapTuple tuple); + + extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys, + Datum v1, Datum v2, +diff -rpNU3 base/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h +--- base/src/include/utils/errcodes.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/utils/errcodes.h 2008-12-02 11:39:45.000000000 +0900 +@@ -339,6 +339,12 @@ + #define ERRCODE_NO_DATA_FOUND MAKE_SQLSTATE('P','0', '0','0','2') + #define ERRCODE_TOO_MANY_ROWS MAKE_SQLSTATE('P','0', '0','0','3') + ++/* Class SE - Security Error (PGACE/SE-PostgreSQL error class) */ ++#define ERRCODE_PGACE_ERROR MAKE_SQLSTATE('S','E', '0','0','0') ++#define ERRCODE_SELINUX_ERROR MAKE_SQLSTATE('S','E', '0','1','1') ++#define ERRCODE_SELINUX_AUDIT MAKE_SQLSTATE('S','E', '0','1','2') ++#define ERRCODE_SELINUX_INFO MAKE_SQLSTATE('S','E', '0','1','3') ++ + /* Class XX - Internal Error (PostgreSQL-specific error class) */ + /* (this is for "can't-happen" conditions and software bugs) */ + #define ERRCODE_INTERNAL_ERROR MAKE_SQLSTATE('X','X', '0','0','0') +diff -rpNU3 base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache.h +--- base/src/include/utils/syscache.h 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/include/utils/syscache.h 2008-06-14 02:36:58.000000000 +0900 +@@ -76,6 +76,8 @@ + #define TSTEMPLATEOID 45 + #define TYPENAMENSP 46 + #define TYPEOID 47 ++#define SECURITYOID 48 ++#define SECURITYLABEL 49 + + extern void InitCatalogCache(void); + extern void InitCatalogCachePhase2(void); +@@ -84,6 +86,8 @@ extern HeapTuple SearchSysCache(int cach + Datum key1, Datum key2, Datum key3, Datum key4); + extern void ReleaseSysCache(HeapTuple tuple); + ++extern void InsertSysCache(Oid relid, HeapTuple tuple); ++ + /* convenience routines */ + extern HeapTuple SearchSysCacheCopy(int cacheId, + Datum key1, Datum key2, Datum key3, Datum key4); +diff -rpNU3 base/src/test/regress/expected/sanity_check.out sepgsql/src/test/regress/expected/sanity_check.out +--- base/src/test/regress/expected/sanity_check.out 2007-11-25 12:49:12.000000000 +0900 ++++ sepgsql/src/test/regress/expected/sanity_check.out 2008-11-24 19:46:15.000000000 +0900 +@@ -111,6 +111,7 @@ SELECT relname, relhasindex + pg_pltemplate | t + pg_proc | t + pg_rewrite | t ++ pg_security | t + pg_shdepend | t + pg_shdescription | t + pg_statistic | t +@@ -149,7 +150,7 @@ SELECT relname, relhasindex + timetz_tbl | f + tinterval_tbl | f + varchar_tbl | f +-(138 rows) ++(139 rows) + + -- + -- another sanity check: every system catalog that has OIDs should have diff --git a/sepostgresql.8 b/sepostgresql.8 index cff2956..9c60ef5 100644 --- a/sepostgresql.8 +++ b/sepostgresql.8 @@ -4,47 +4,67 @@ sepostgresql \- Security-Enhances PostgreSQL .SH "DESCRIPTION" -Security-Enhanced PostgreSQL (SE-PostgreSQL) is an enhancement of PostgreSQL, to apply fine grained mandatory access control for database objects based on the security policy of SELinux. -These features enable to apply flexible integrated access control policy between operating system and database management system, during all stages of the life of the information. +Security-Enhanced PostgreSQL (SE-PostgreSQL) is an enhancement of PostgreSQL, +to apply fine grained mandatory access control for database objects based on +the security policy of SELinux. +These features enable to apply flexible integrated access control policy +on both of operating system and database management system, during all +stages of the life of the information. .PP -This document describes the way to customize SE-PostgreSQL on the default security policy. +This document describes the way to customize SE-PostgreSQL on the default +security policy. .SH "BOOLEANS" -The SELinux policy is customizable via BOOLEAN variable. This variable has two states, 1 (on) or 0 (off). A part of the policy is enabled or disabled depending on related boolean variables. +The SELinux policy is customizable via BOOLEAN variable. This variable has +two states, 1 (on) or 0 (off). We can validate or invalidate a part of the +security policy depending on the state of boolean variables. -\fBsepgsql_enable_unconfined\fP toggles whether \fIunconfined_t\fP and \fIsysadm_t\fP domains are allowed to access database objects without any restruction on type enforcement, or not. -When \fIsepgsql_enable_unconfined\fP is off, those domains are also restricted its operation as other domains begin applied. In the default, it is set to on. -You can set it as follows: - -.EX -setsebool -P sepgsql_enable_unconfined ( \fBon\fP | off ) -.EE - -\fBsepgsql_enable_users_ddl\fP toggles whether non-administrative domain is allowed to use DDL statement like CREATE TABLE and so on. -In the default, it is set to on. You can set it as follows: +\fBsepgsql_enable_users_ddl\fP enables to toggle permissions of confined +users/applications to invoke DDL statement, like CREATE TABLE. It is set to +\fBon\fP in the default. +In most cases, DDL statements are used to set up initial database structure, +and permissions to invoke them are not necessary on operation phase. +You can turn off this boolean as follows: .EX setsebool -P sepgsql_enable_users_ddl ( \fBon\fP | off ) .EE -\fBsepgsql_enable_auditallow\fP toggles output of audit messages in the case when required permission checks are allowed. In the default, it is set to off. You can set it as follows: +Rest of booleans are provided by \fBselinux-devel.pp\fP policy module. +It provides developments/debugs related permissions. +You can install it as follows: + +.EX +semodule -i /usr/share/selinux/targeted/sepostgresql-devel.pp +.EE + +\fBsepgsql_enable_auditallow\fP toggles output of audit messages in the case +when required permission checks are allowed, except for tuples because it +easily make a flood of audit logs. +In the default, it is set to off. You can set it as follows: .EX setsebool -P sepgsql_enable_auditallow ( on | \fBoff\fP ) .EE -\fBsepgsql_enable_auditdeny\fP toggles output of audit messages in the case when required permission checks are denied. In the default, it is set to on. You can set it as follows: +\fBsepgsql_enable_auditdeny\fP toggles output of audit messages in the case +when required permission checks are denied, except for tuples because it +easily make a flood of audit logs. +In the default, it is set to on. You can set it as follows: .EX setsebool -P sepgsql_enable_auditdeny ( \fBon\fP | off ) .EE -\fBsepgsql_enable_audittuple\fP toggles output of audit messages for any tuple. Because audit messages for tuples in a large size table can cause flood of messages, we can set \fIsepgsql_enable_audittuple\fP independently from any other object classes. -Audit messages for tuples are generated in the only case when \fIsepgsql_enable_audittuple\fP and either \fIsepgsql_enable_auditallow\fP or \fIsepgsql_enable_auditdeny\fP are enabled. +\fBsepgsql_regression_test_mode\fP allows to load shared libraries deployed +on user's home directory. We recommend you to keep \fBoff\fP in operation +phase to prevent to load malicious libraries. +However, typical PostgreSQL regression test requires to load it, so we +have to reduce several restriction during the test. In the default, it is set to off. You can set it as follows: .EX -setsebool -P sepgsql_enable_audittuple ( on | \fBoff\fP ) +setsebool -P sepgsql_regression_test_mode ( on | \fBoff\fP ) .EE .SH "TYPES" @@ -53,29 +73,46 @@ setsebool -P sepgsql_enable_audittuple ( on | \fBoff\fP ) It is attched for newly created databases in the default. \fBsepgsql_table_t\fP is a type for tables, columns and tuples. -It is attached for newly created the objects in the default. -Non-administrative clients can do any kinds of operations except for relabeling. +It is the default type of newly created tables by unconfined or +non-roled domain. It allows confined clietns to access with any +kind of operations except for relabeling, so we can use this type +for compatible purpose. \fBsepgsql_secret_table_t\fP is a type for tables, columns and tuples. -Non-administrative clients cannot access the objects with this type. +It never allows confined clients to access, so we can use this type +to store sensitive information. We reccomend to apply trusted procedures +to access tables/columns/tuples with this type under safe operation. \fBsepgsql_ro_table_t\fP is a type for read-only tables, columns and tuples. -Non-administrative clients cannot modify the objects with this type. +It does not allow confined clients to modify any objects with this type. -\fBsepgsql_fixed_table_t\fP is a type for non-manupulatable tables, columns and tuples. -Non-administrative clients cannot update or delete the objects with this type. +\fBsepgsql_fixed_table_t\fP is a type for non-manupulatable tables, columns +and tuples. It does not allow confined clients to update or delete any +objects with this type. + +\fBsepgsql_ROLE_table_t\fP is a type for a role specific tables, columns +and tuples. It allows confined clients with its role to access with any +kind of operations except for relabeling. +It is the default type of newly created tables by confined clients with +its role, and we can use this type to describe role level separation. \fBsepgsql_proc_t\fP is a type for procedures. -It is attached for newly created procedures by adminictrative domain. -Any client can call these procedures with this type. +It is attached for newly created procedures by unconfined clients. +It allows any clients to invoke procedures with this type. +All of PostgreSQL built-in functions are labeled as this type in the default. -\fBsepgsql_userproc_t\fP is a type for procedures. -It is attached for newly created procedures by non-administrative domain. -Administrative domains cannot call the procedure for safety. He have to relabel it into \fIsepgsql_proc_t\fP at first. It is a policy to avoid to execute doubtful code under administrative domain. +\fBsepgsql_ROLE_proc_t\fP is a type for a role specific procedure. +It is attached for newly created procedures by confined clients with its role. +It allows clients with same role to invoke procedure with this type. +Note that unconfined clients cannot invoke this type to avoid to execute +dangerous functions with unconfined authorities. They have to confirm its +contains and relabel to \fBsepgsql_proc_t\fP for its invocation. -\fBsepgsql_trusted_proc_t\fP is a type for trusted procedures. -Calling procedures with this type invokes domain transition. -Then the function works as an administrative domain, so database administrator can provide limited path to access protected object. +\fBsepgsql_trusted_proc_exec_t\fP is a type for trusted procedures. +To call procedures with this type invokes domain transition to +unconfined domain, so it can access any kind of database objects. +We can use this type to provide a secure method to access sensitive +information. \fBsepgsql_blob_t\fP is a type for binary large objects (blob). It is attached for newly created blob in the default. diff --git a/sepostgresql.fc b/sepostgresql.fc deleted file mode 100644 index 849a5de..0000000 --- a/sepostgresql.fc +++ /dev/null @@ -1,10 +0,0 @@ -# -# SE-PostgreSQL install path -# -/usr/bin/sepostgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) -/usr/bin/initdb.sepgsql -- gen_context(system_u:object_r:postgresql_exec_t,s0) -/usr/bin/sepg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) - -/var/lib/sepgsql(/.*)? gen_context(system_u:object_r:postgresql_db_t,s0) -/var/lib/sepgsql/pgstartup\.log gen_context(system_u:object_r:postgresql_log_t,s0) -/var/log/sepostgresql\.log.* -- gen_context(system_u:object_r:postgresql_log_t,s0) diff --git a/sepostgresql.if b/sepostgresql.if deleted file mode 100644 index 1631ee2..0000000 --- a/sepostgresql.if +++ /dev/null @@ -1,88 +0,0 @@ -######################################## -## -## Marks the specified domain as SE-PostgreSQL server process. -## -## -## -## Domain to be marked -## -## -# -interface(`sepgsql_server_domain',` - gen_require(` - attribute sepgsql_server_type; - ') - typeattribute $1 sepgsql_server_type; -') - -######################################## -## -## Allow the specified domain unconfined accesses to any database objects -## managed by SE-PostgreSQL, -## -## -## -## Domain allowed access. -## -## -# -interface(`sepgsql_unconfined_domain',` - gen_require(` - attribute sepgsql_unconfined_type; - attribute sepgsql_client_type; - ') - typeattribute $1 sepgsql_unconfined_type; - typeattribute $1 sepgsql_client_type; -') - -######################################## -## -## Allow the specified domain unprivileged accesses to any database objects -## managed by SE-PostgreSQL, -## -## -## -## Domain allowed access. -## -## -# -interface(`sepgsql_client_domain',` - gen_require(` - attribute sepgsql_client_type; - ') - typeattribute $1 sepgsql_client_type; -') - -######################################## -## -## Allow the specified role to invoke trusted procedures -## -## -## -## The role associated with the domain. -## -## -# -interface(`sepgsql_trusted_procedure_role',` - gen_require(` - type sepgsql_trusted_domain_t; - ') - role $1 types sepgsql_trusted_domain_t; -') - -######################################## -## -## Marks as a SE-PostgreSQL loadable shared library module -## -## -## -## Type marked as a database object type. -## -## -# -interface(`sepgsql_loadable_module',` - gen_require(` - attribute sepgsql_module_type; - ') - typeattribute $1 sepgsql_module_type; -') diff --git a/sepostgresql.init b/sepostgresql.init index a21b39c..e09d4aa 100644 --- a/sepostgresql.init +++ b/sepostgresql.init @@ -7,9 +7,9 @@ # pidfile: /var/run/postmaster.pid #--------------------------------------------------------------------- -PGVERSION="8.3.1" +PGVERSION="8.3.7" PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` -SEPGVERSION="2.179" +SEPGVERSION="2.1770" # source function library . /etc/rc.d/init.d/functions @@ -38,7 +38,7 @@ export SEPGSQL_FALLBACK_CONTEXT # Check that networking is up. test "${NETWORKING}" = "no" && exit 0 -test -f "${SEPGSQL_BIN}/postmaster" || exit 1 +test -f "${SEPGSQL_BIN}/sepostgres" || exit 1 script_result=0 @@ -165,7 +165,7 @@ do_initdb() { test -x /sbin/restorecon && /sbin/restorecon -R "${SEPGSQL_DATA}" # Initialize the database cd ${SEPGSQL_BIN} - /sbin/runuser -- sepgsql -c "./initdb.sepgsql -A 'ident sameuser' ${SEPGSQL_DATA}" \ + /sbin/runuser -- sepgsql -c "./initdb.sepgsql --pgace-feature=selinux -A 'ident sameuser' ${SEPGSQL_DATA}" \ >> "${SEPGSQL_STARTUP_LOG}" 2>&1 < /dev/null if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then echo_success diff --git a/sepostgresql.spec b/sepostgresql.spec index 8dd5330..94493fa 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -4,39 +4,53 @@ # Copyright 2007 KaiGai Kohei # ----------------------------------------------------- -# SELinux policy types -%define selinux_variants mls strict targeted - # SE-PostgreSQL status extension +%define selinux_policy_stores targeted mls +# Check required policy version +%define fedora9 %(rpm -E '%{dist}' | grep -cE '^\.fc[1-9]$') +%if %{fedora9} +%define required_policy_version 3.3.1 +%else +%define required_policy_version 3.4.2 +%endif + + + +%{!?ssl:%define ssl 1} Summary: Security Enhanced PostgreSQL Name: sepostgresql -Version: 8.3.1 -Release: 2.179%{?sepgsql_extension}%{?dist} +Version: 8.3.7 +Release: 2.1770%{?sepgsql_extension}%{?dist} License: BSD Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 Source1: sepostgresql.init -Source2: sepostgresql.if -Source3: sepostgresql.te -Source4: sepostgresql.fc -Source5: sepostgresql.8 -Source6: sepostgresql.logrotate -Patch0: sepostgresql-pgace-8.3.1-2.patch -Patch1: sepostgresql-sepgsql-8.3.1-2.patch -Patch2: sepostgresql-pg_dump-8.3.1-2.patch +Source2: sepostgresql.8 +Source3: sepostgresql.logrotate +Patch0: sepostgresql-sepgsql-8.3.7-2.patch +Patch1: sepostgresql-policy-8.3.7-2.patch +Patch2: sepostgresql-pg_dump-8.3.7-2.patch Patch3: sepostgresql-fedora-prefix.patch BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 -Buildrequires: checkpolicy libselinux-devel >= 2.0.43 selinux-policy-devel selinux-policy >= 3.0.6 +BuildRequires: checkpolicy libselinux-devel >= 2.0.43 +BuildRequires: selinux-policy >= %{required_policy_version} +%if %{fedora9} +BuildRequires: selinux-policy-devel +%endif +%if %{ssl} +BuildRequires: openssl-devel +%endif Requires(pre): shadow-utils Requires(post): policycoreutils /sbin/chkconfig Requires(preun): /sbin/chkconfig /sbin/service Requires(postun): policycoreutils Requires: postgresql-server = %{version} -Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.43 selinux-policy >= 3.0.6 +Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.43 +Requires: selinux-policy >= %{required_policy_version} Requires: tzdata logrotate %description @@ -53,26 +67,17 @@ reference monitor to check any SQL query. %patch1 -p1 %patch2 -p1 %patch3 -p1 -mkdir selinux-policy -cp -p %{SOURCE2} %{SOURCE3} %{SOURCE4} selinux-policy %build CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS -# build Binary Policy Module -pushd selinux-policy -for selinuxvariant in %{selinux_variants} -do - make NAME=${selinuxvariant} -f %{_datadir}/selinux/devel/Makefile - mv %{name}.pp %{name}.pp.${selinuxvariant} - make NAME=${selinuxvariant} -f %{_datadir}/selinux/devel/Makefile clean -done -popd - # build SE-PostgreSQL %configure --disable-rpath \ --enable-selinux \ +%if %{ssl} + --with-openssl \ +%endif %if %{defined sepgextension} --enable-debug \ --enable-cassert \ @@ -83,20 +88,24 @@ popd # parallel build, if possible make %{?_smp_mflags} +%if !%{fedora9} +touch src/backend/security/sepgsql/policy/sepostgresql-devel.fc +make -C src/backend/security/sepgsql/policy +%endif %install rm -rf %{buildroot} -pushd selinux-policy -for selinuxvariant in %{selinux_variants} -do - install -d %{buildroot}%{_datadir}/selinux/${selinuxvariant} - install -p -m 644 %{name}.pp.${selinuxvariant} \ - %{buildroot}%{_datadir}/selinux/${selinuxvariant}/%{name}.pp -done -popd +make DESTDIR=%{buildroot} install -make DESTDIR=%{buildroot} install +%if !%{fedora9} +for store in %{selinux_policy_stores} +do + install -d %{buildroot}%{_datadir}/selinux/${store} + install -p -m 644 src/backend/security/sepgsql/policy/sepostgresql-devel.pp.${store} \ + %{buildroot}%{_datadir}/selinux/${store}/sepostgresql-devel.pp +done +%endif # avoid to conflict with native postgresql package mv %{buildroot}%{_bindir} %{buildroot}%{_bindir}.orig @@ -124,13 +133,13 @@ install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql/backups mkdir -p %{buildroot}%{_initrddir} install -p -m 755 %{SOURCE1} %{buildroot}%{_initrddir}/sepostgresql -# /etc/logrotate.d/ -mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d -install -p -m 644 %{SOURCE6} %{buildroot}%{_sysconfdir}/logrotate.d/sepostgresql - # /usr/share/man/* mkdir -p %{buildroot}%{_mandir}/man8 -install -p -m 644 %{SOURCE5} %{buildroot}%{_mandir}/man8 +install -p -m 644 %{SOURCE2} %{buildroot}%{_mandir}/man8 + +# /etc/logrotate.d/ +mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d +install -p -m 644 %{SOURCE3} %{buildroot}%{_sysconfdir}/logrotate.d/sepostgresql %clean rm -rf %{buildroot} @@ -146,14 +155,17 @@ exit 0 /sbin/chkconfig --add %{name} /sbin/ldconfig -for selinuxvariant in %{selinux_variants} +%if !%{fedora9} +for store in %{selinux_policy_stores} do - %{_sbindir}/semodule -s ${selinuxvariant} -l >& /dev/null || continue; - - %{_sbindir}/semodule -s ${selinuxvariant} -l | egrep -q '^%{name}' && \ - %{_sbindir}/semodule -s ${selinuxvariant} -r %{name} >& /dev/null || : - %{_sbindir}/semodule -s ${selinuxvariant} -i %{_datadir}/selinux/${selinuxvariant}/%{name}.pp >& /dev/null || : + # clean up legacy policy module (now it is unnecessary) + %{_sbindir}/semodule -s ${store} -r sepostgresql >& /dev/null || : + if %{_sbindir}/semodule -s ${store} -l 2>/dev/null | grep -Eq "^sepostgresql-devel"; then + %{_sbindir}/semodule -s ${store} \ + -i %{_datadir}/selinux/${store}/sepostgresql-devel.pp >& /dev/null || : + fi done +%endif # Fix up non-standard file contexts /sbin/fixfiles -R %{name} restore || : @@ -171,12 +183,9 @@ if [ $1 -ge 1 ]; then # rpm -U case /sbin/service %{name} condrestart >/dev/null 2>&1 || : fi if [ $1 -eq 0 ]; then # rpm -e case - for selinuxvariant in %{selinux_variants} + for store in %{selinux_policy_stores} do - %{_sbindir}/semodule -s ${selinuxvariant} -l >& /dev/null || continue; - - %{_sbindir}/semodule -s ${selinuxvariant} -l | egrep -q '^%{name}' && \ - %{_sbindir}/semodule -s ${selinuxvariant} -r %{name} >& /dev/null || : + %{_sbindir}/semodule -s ${store} -r sepostgresql-devel >& /dev/null || : done /sbin/fixfiles -R %{name} restore || : test -d %{_localstatedir}/lib/sepgsql && /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : @@ -184,7 +193,7 @@ fi %files %defattr(-,root,root,-) -%doc COPYRIGHT README HISTORY +%doc COPYRIGHT README %{_initrddir}/sepostgresql %{_sysconfdir}/logrotate.d/sepostgresql %{_bindir}/initdb.sepgsql @@ -205,32 +214,89 @@ fi %{_datadir}/sepgsql/conversion_create.sql %{_datadir}/sepgsql/information_schema.sql %{_datadir}/sepgsql/sql_features.txt -%attr(644,root,root) %{_datadir}/selinux/*/sepostgresql.pp +%if !%{fedora9} +%attr(644,root,root) %{_datadir}/selinux/*/sepostgresql-devel.pp +%endif %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/data %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog -* Sun Mar 9 2008 - sepostgresql-8.3.0-2.129 +* Fri Mar 27 2009 KaiGai Kohei - 8.3.7-2.1770 +- upgrade base PostgreSQL version 8.3.6->8.3.7 + +* Thu Feb 26 2009 KaiGai Kohei - 8.3.6-2.1635 +- bugfix: possible information leak by the order of permission checks + in row level permission checks. + +* Wed Feb 25 2009 Fedora Release Engineering - 8.3.6-3.1518 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Fri Feb 6 2009 - 8.3.6-2.1523 +- upgrade base PostgreSQL version 8.3.5->8.3.6 +- backport features from 8.4devel tree +- security policy fix for Fedora 9 + +* Sat Jan 17 2009 Tomas Mraz - 8.3.5-2.1183 +- rebuild with new openssl + +* Wed Nov 5 2008 - 8.3.5-2.1182 +- upgrade base PostgreSQL version 8.3.4->8.3.5 +- backport cumulative bugfixes from 8.4devel series + +* Thu Oct 2 2008 - 8.3.4-2.1076 +- bugfix: "(null)" audit logs for non-cached decision making. +- A hook is added for "COPY TO/FROM " cases. + +* Sat Sep 27 2008 - 8.3.4-2.1066 +- update base version to 8.3.4 +- sepostgresql.pp was marked as obsolute + +* Tue Sep 23 2008 - 8.3.3-2.1043 +- bugfix: a case when INSERT a FK reference to invisible PK + +* Wed Aug 13 2008 - 8.3.3-2.964 +- bugfix: trusted procedure invokation + +* Fri Jul 11 2008 - 8.3.3-2.952 +- Security policy module updates + +* Fri Jul 11 2008 - 8.3.3-2.945 +- Add OpenSSL support +- backport 8.4devel fixes + +* Sun Jun 15 2008 - 8.3.3-2.889 +- backport 8.4devel features. + +* Fri Jun 13 2008 - 8.3.3-2.869 +- upgrade base PostgreSQL 8.3.1 -> 8.3.3 + +* Wed Apr 30 2008 - 8.3.1-2.197 +- Inconsistent version number format at Changelogs + +* Wed Apr 30 2008 - 8.3.1-2.196 +- BUGFIX: ROW-level control did not work correctly on TRUNCATE + +* Sun Mar 9 2008 - 8.3.0-2.129 - BUGFIX: more conprehensive fixes in "SELECT COUNT(*) ..." -* Sun Mar 2 2008 - sepostgresql-8.3.0-2.120 +* Sun Mar 2 2008 - 8.3.0-2.120 - BUGFIX: CREATE TABLE statement with explicit labeled columns - BUGFIX: SELECT count(*) does not filter unallowed tuples -* Wed Feb 27 2008 - sepostgresql-8.3.0-2.117 +* Wed Feb 27 2008 - 8.3.0-2.117 - ".beta" removed. -* Wed Feb 27 2008 - sepostgresql-8.3.0-2.114 +* Wed Feb 27 2008 - 8.3.0-2.114 - Security policy updates -* Tue Feb 26 2008 - sepostgresql-8.3.0-2.113 +* Tue Feb 26 2008 - 8.3.0-2.113 - BUGFIX: CREATE/ALTER TABLE with CONTEXT='...' did nothing. -* Thu Feb 7 2008 - sepostgresql-8.3.0-2.108 +* Thu Feb 7 2008 - 8.3.0-2.108 - add /etc/logrotate.d/sepostgresql -* Thu Feb 7 2008 - sepostgresql-8.3.0-2.105 +* Thu Feb 7 2008 - 8.3.0-2.105 - update base version to stable 8.3.0 - add tzdata dependency - allow db_database:{get_param set_param} for generic domain @@ -240,17 +306,17 @@ fi - BUGFIX: incorrect permission in DELETE with RETURNING clause - incorrect permission when we read and update security_context in same time. -* Fri Jan 25 2008 - sepostgresql-8.3RC2-2.62 +* Fri Jan 25 2008 - 8.3RC2-2.62 - BUGFIX: add handling to invalid contexts already stored -* Tue Jan 22 2008 - sepostgresql-8.3RC2-2.56 +* Tue Jan 22 2008 - 8.3RC2-2.56 - BUGFIX: lack of locks when refering buffer pages at update/delete hooks - BUGFIX: explicit labeling using SELECT ... INTO statement. -* Sun Jan 20 2008 - sepostgresql-8.3RC2-2.52 +* Sun Jan 20 2008 - 8.3RC2-2.52 - shares /usr/lib/pgsql/*.so libraries, with original postgresql. -* Thu Jan 10 2008 - sepostgresql-8.3RC1-2.37 +* Thu Jan 10 2008 - 8.3RC1-2.37 - add sepg_dump/sepg_dumpall support for 8.3base package. * Mon Nov 26 2007 - 8.3beta3-2.0 diff --git a/sepostgresql.te b/sepostgresql.te deleted file mode 100644 index e5d13b3..0000000 --- a/sepostgresql.te +++ /dev/null @@ -1,353 +0,0 @@ -policy_module(sepostgresql, 2.179) - -gen_require(` - class db_database all_db_database_perms; - class db_table all_db_table_perms; - class db_procedure all_db_procedure_perms; - class db_column all_db_column_perms; - class db_tuple all_db_tuple_perms; - class db_blob all_db_blob_perms; - - type postgresql_t, unlabeled_t; - attribute domain, file_type; - - role system_r; -') - -################################# -# -# SE-PostgreSQL Boolean declarations -# - -## -##

-## Allow to enable unconfined domains -##

-##
-gen_tunable(sepgsql_enable_unconfined, true) - -## -##

-## Allow to generate auditallow logs -##

-##
-gen_tunable(sepgsql_enable_auditallow, false) - -## -##

-## Allow to generate auditdeny logs -##

-##
-gen_tunable(sepgsql_enable_auditdeny, true) - -## -##

-## Allow to generate audit(allow|deny) logs for tuples -##

-##
-gen_tunable(sepgsql_enable_audittuple, false) - -## -##

-## Allow unprivileged users to execute DDL statement -##

-##
-gen_tunable(sepgsql_enable_users_ddl, true) - -################################# -# -# SE-PostgreSQL Type/Attribute declarations -# - -# database subjects -attribute sepgsql_server_type; -attribute sepgsql_client_type; -attribute sepgsql_unconfined_type; - -# database objects attribute -attribute sepgsql_database_type; -attribute sepgsql_table_type; -attribute sepgsql_procedure_type; -attribute sepgsql_blob_type; -attribute sepgsql_module_type; - -# database trusted domain -type sepgsql_trusted_domain_t; - -# database object types -type sepgsql_db_t, sepgsql_database_type; - -type sepgsql_table_t, sepgsql_table_type; -type sepgsql_sysobj_t, sepgsql_table_type; -type sepgsql_secret_table_t, sepgsql_table_type; -type sepgsql_ro_table_t, sepgsql_table_type; -type sepgsql_fixed_table_t, sepgsql_table_type; - -type sepgsql_proc_t, sepgsql_procedure_type; -type sepgsql_user_proc_t, sepgsql_procedure_type; -type sepgsql_trusted_proc_t, sepgsql_procedure_type; - -type sepgsql_blob_t, sepgsql_blob_type; -type sepgsql_ro_blob_t, sepgsql_blob_type; -type sepgsql_secret_blob_t, sepgsql_blob_type; - -typeattribute unlabeled_t sepgsql_database_type; -typeattribute unlabeled_t sepgsql_table_type; -typeattribute unlabeled_t sepgsql_procedure_type; -typeattribute unlabeled_t sepgsql_blob_type; - -######################################## -# -# SE-PostgreSQL Server Local policy -# (sepgsql_server_type) -allow sepgsql_server_type self : netlink_selinux_socket create_socket_perms; -selinux_get_fs_mount(sepgsql_server_type) -selinux_get_enforce_mode(sepgsql_server_type) -selinux_validate_context(sepgsql_server_type) -selinux_compute_access_vector(sepgsql_server_type) -selinux_compute_create_context(sepgsql_server_type) -selinux_compute_relabel_context(sepgsql_server_type) - -allow sepgsql_server_type sepgsql_database_type : db_database *; -allow sepgsql_server_type sepgsql_module_type : db_database { install_module }; -allow sepgsql_server_type sepgsql_table_type : { db_table db_column db_tuple } *; -allow sepgsql_server_type sepgsql_procedure_type : db_procedure *; -allow sepgsql_server_type sepgsql_blob_type : db_blob *; - -# server specific type transitions -type_transition sepgsql_server_type sepgsql_database_type : db_table sepgsql_sysobj_t; -type_transition sepgsql_server_type sepgsql_database_type : db_procedure sepgsql_proc_t; - -######################################## -# -# SE-PostgreSQL Administrative domain local policy -# (sepgsql_unconfined_type) - -tunable_policy(`sepgsql_enable_unconfined',` - allow sepgsql_unconfined_type sepgsql_database_type : db_database *; - allow sepgsql_unconfined_type sepgsql_module_type : db_database { install_module }; - allow sepgsql_unconfined_type sepgsql_table_type : { db_table db_column db_tuple } *; - allow sepgsql_unconfined_type { sepgsql_procedure_type - sepgsql_user_proc_t } : db_procedure *; - allow sepgsql_unconfined_type sepgsql_user_proc_t : db_procedure { create drop getattr setattr relabelfrom relabelto }; - allow sepgsql_unconfined_type sepgsql_blob_type : db_blob *; - allow sepgsql_unconfined_type postgresql_t : db_blob { import export }; - - type_transition { sepgsql_unconfined_type - sepgsql_server_type } sepgsql_database_type : db_procedure sepgsql_proc_t; -',` - type_transition { sepgsql_unconfined_type - sepgsql_server_type } sepgsql_database_type : db_procedure sepgsql_user_proc_t; -') - -######################################## -# -# SE-PostgreSQL Users domain local policy -# (sepgsql_client_type) - -allow sepgsql_client_type sepgsql_db_t : db_database { getattr access get_param set_param}; - -allow sepgsql_client_type sepgsql_table_t : db_table { getattr use select update insert delete }; -allow sepgsql_client_type sepgsql_table_t : db_column { getattr use select update insert }; -allow sepgsql_client_type sepgsql_table_t : db_tuple { use select update insert delete }; - -allow sepgsql_client_type sepgsql_sysobj_t : db_table { getattr use select }; -allow sepgsql_client_type sepgsql_sysobj_t : db_column { getattr use select }; -allow sepgsql_client_type sepgsql_sysobj_t : db_tuple { use select }; -tunable_policy(`sepgsql_enable_users_ddl',` - allow sepgsql_client_type sepgsql_table_t : db_table { create drop setattr }; - allow sepgsql_client_type sepgsql_table_t : db_column { create drop setattr }; - allow sepgsql_client_type sepgsql_sysobj_t : db_tuple { update insert delete }; -') - -allow sepgsql_client_type sepgsql_secret_table_t : db_table { getattr }; -allow sepgsql_client_type sepgsql_secret_table_t : db_column { getattr }; - -allow sepgsql_client_type sepgsql_ro_table_t : db_table { getattr use select }; -allow sepgsql_client_type sepgsql_ro_table_t : db_column { getattr use select }; -allow sepgsql_client_type sepgsql_ro_table_t : db_tuple { use select }; - -allow sepgsql_client_type sepgsql_fixed_table_t : db_table { getattr use select insert }; -allow sepgsql_client_type sepgsql_fixed_table_t : db_column { getattr use select insert }; -allow sepgsql_client_type sepgsql_fixed_table_t : db_tuple { use select insert }; - -allow sepgsql_client_type sepgsql_proc_t : db_procedure { getattr execute }; -allow { sepgsql_client_type - sepgsql_unconfined_type } sepgsql_user_proc_t : db_procedure { create drop getattr setattr execute }; -allow sepgsql_client_type sepgsql_trusted_proc_t : db_procedure { getattr execute entrypoint }; - -allow sepgsql_client_type sepgsql_blob_t : db_blob { create drop getattr setattr read write }; -allow sepgsql_client_type sepgsql_ro_blob_t : db_blob { getattr read }; -allow sepgsql_client_type sepgsql_secret_blob_t : db_blob { getattr }; - -# call trusted procedure -type_transition sepgsql_client_type sepgsql_trusted_proc_t : process sepgsql_trusted_domain_t; -allow sepgsql_client_type sepgsql_trusted_domain_t : process { transition }; - -# type transitions for rest of domains -type_transition domain domain : db_database sepgsql_db_t; -type_transition { domain - sepgsql_server_type } sepgsql_database_type : db_table sepgsql_table_t; -type_transition { domain - sepgsql_server_type - sepgsql_unconfined_type } sepgsql_database_type : db_procedure sepgsql_user_proc_t; -type_transition domain sepgsql_database_type : db_blob sepgsql_blob_t; - -######################################## -# -# SE-PostgreSQL Misc policies -# - -# Trusted Procedure Domain -domain_type(sepgsql_trusted_domain_t) -role system_r types sepgsql_trusted_domain_t; -sepgsql_unconfined_domain(sepgsql_trusted_domain_t) - -# The following permissions are allowed, even if sepgsql_enable_unconfined is disabled. -allow sepgsql_trusted_domain_t sepgsql_database_type : db_database { getattr setattr access get_param set_param}; -allow sepgsql_trusted_domain_t sepgsql_table_type : db_table { getattr use select update insert delete lock }; -allow sepgsql_trusted_domain_t sepgsql_table_type : db_column { getattr use select update insert }; -allow sepgsql_trusted_domain_t sepgsql_table_type : db_tuple { use select update insert delete }; - -allow sepgsql_trusted_domain_t { sepgsql_procedure_type - sepgsql_user_proc_t } : db_procedure { getattr execute }; -allow sepgsql_trusted_domain_t sepgsql_user_proc_t : db_procedure { getattr }; -allow sepgsql_trusted_domain_t sepgsql_blob_type : db_blob { getattr setattr read write }; - -# Database/Loadable module -allow sepgsql_database_type sepgsql_module_type : db_database { load_module }; - -######################################## -# -# SE-PostgreSQL audit switch -# -tunable_policy(`sepgsql_enable_auditallow',` - auditallow domain sepgsql_database_type : db_database all_db_database_perms; - auditallow domain sepgsql_table_type : db_table all_db_table_perms; - auditallow domain sepgsql_table_type : db_column all_db_column_perms; - auditallow domain sepgsql_procedure_type : db_procedure all_db_procedure_perms; - auditallow domain sepgsql_blob_type : db_blob all_db_blob_perms; - auditallow domain sepgsql_server_type : db_blob { import export }; - auditallow domain sepgsql_module_type : db_database { install_module }; -') -tunable_policy(`sepgsql_enable_audittuple && sepgsql_enable_auditallow',` - auditallow domain sepgsql_table_type : db_tuple all_db_tuple_perms; -') -tunable_policy(`! sepgsql_enable_auditdeny',` - dontaudit domain sepgsql_database_type : db_database all_db_database_perms; - dontaudit domain sepgsql_table_type : db_table all_db_table_perms; - dontaudit domain sepgsql_table_type : db_column all_db_column_perms; - dontaudit domain sepgsql_procedure_type : db_procedure all_db_procedure_perms; - dontaudit domain sepgsql_blob_type : db_blob all_db_blob_perms; - dontaudit domain sepgsql_server_type : db_blob { import export }; - dontaudit domain sepgsql_module_type : db_database { install_module }; -') -tunable_policy(`! sepgsql_enable_audittuple || ! sepgsql_enable_auditdeny',` - dontaudit domain sepgsql_table_type : db_tuple all_db_tuple_perms; -') -######################################## -# -# Allow permission to external domains -# - -# server domains -optional_policy(` - gen_require(` - type postgresql_t; - ') - sepgsql_server_domain(postgresql_t) -') - -# unconfined client domain -optional_policy(` - gen_require(` - type unconfined_t; - ') - sepgsql_unconfined_domain(unconfined_t) -') - -optional_policy(` - gen_require(` - type sysadm_t; - ') - sepgsql_unconfined_domain(sysadm_t) -') - -# generic client domain -optional_policy(` - gen_require(` - type user_t; - role user_r; - ') - sepgsql_client_domain(user_t) - sepgsql_trusted_procedure_role(user_r) -') - -optional_policy(` - gen_require(` - type staff_t; - role staff_r; - ') - sepgsql_client_domain(staff_t) - sepgsql_trusted_procedure_role(staff_r) -') - -optional_policy(` - gen_require(` - type user_t; - role user_r; - ') - sepgsql_client_domain(user_t) - sepgsql_trusted_procedure_role(user_r) -') - -optional_policy(` - gen_require(` - type guest_t; - role guest_r; - ') - sepgsql_client_domain(guest_t) - sepgsql_trusted_procedure_role(guest_r) -') - -optional_policy(` - gen_require(` - type xguest_t; - role xguest_r; - ') - sepgsql_client_domain(xguest_t) - sepgsql_trusted_procedure_role(xguest_r) -') - -optional_policy(` - gen_require(` - type httpd_sys_script_t; - ') - sepgsql_client_domain(httpd_sys_script_t) -') - -# RBAC -optional_policy(` - gen_require(` - role unconfined_r; - ') - sepgsql_trusted_procedure_role(unconfined_r) -') - -# loadable module types -optional_policy(` - gen_require(` - type lib_t; - ') - sepgsql_loadable_module(lib_t) -') - -optional_policy(` - gen_require(` - type textrel_shlib_t; - ') - sepgsql_loadable_module(textrel_shlib_t) -') - -######################################## -# -# Hotfixes for labeled networking -# -# NOTE: These changes are to be merged in the later releases. -corenet_tcp_recvfrom_labeled(sepgsql_server_type, sepgsql_client_type) -optional_policy(` - ipsec_match_default_spd(sepgsql_server_type) - ipsec_match_default_spd(sepgsql_client_type) -') diff --git a/sources b/sources index 73cde9a..7a88da9 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -a5e0ed6a85b450dc217ec71da93243a7 postgresql-8.3.1.tar.bz2 +7b7e91a2221e55fe1b167e663217a96d postgresql-8.3.7.tar.bz2