From 18a986d45e245bdb42e7e53a50c3546a7fa8c3e0 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Fri, 11 Jul 2008 06:40:32 +0000 Subject: [PATCH] - Backport features from 8.4devel tree. It contains several bug fixes, design simplification and avoidance toward M$ patent. --- sepostgresql-pg_dump-8.3.3-2.patch | 569 +- sepostgresql-pgace-8.3.3-2.patch | 4084 -------- sepostgresql-sepgsql-8.3.3-2.patch | 14051 ++++++++++++++++++++------- sepostgresql.init | 2 +- sepostgresql.spec | 116 +- 5 files changed, 10705 insertions(+), 8117 deletions(-) delete mode 100644 sepostgresql-pgace-8.3.3-2.patch diff --git a/sepostgresql-pg_dump-8.3.3-2.patch b/sepostgresql-pg_dump-8.3.3-2.patch index 79f39c1..e26e773 100644 --- a/sepostgresql-pg_dump-8.3.3-2.patch +++ b/sepostgresql-pg_dump-8.3.3-2.patch @@ -1,13 +1,303 @@ -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; +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-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,279 @@ ++#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) ++ 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); ++ ++ return !relcontext ? NULL : 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); ++ ++ return !attcontext ? NULL : 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) ++ 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 2008-02-03 01:11:28.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dump.c 2008-07-11 14:10:51.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 tuen on/off SE-PostgreSQL support */ -+#define SELINUX_SYSATTR_NAME "security_context" -+static int enable_selinux = 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, @@ -15,7 +305,7 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c {"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}, ++ {"security-context", no_argument, &pg_ace_feature, PG_ACE_FEATURE_SELINUX}, {NULL, 0, NULL, 0} }; @@ -23,79 +313,45 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c 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 if (strcmp(optarg, "security-context") == 0) ++ pg_ace_feature = PG_ACE_FEATURE_SELINUX; else { fprintf(stderr, -@@ -549,6 +555,24 @@ main(int argc, char **argv) +@@ -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); -+ 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); -+ } -+ } ++ 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 +795,7 @@ help(const char *progname) +@@ -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(_(" --enable-selinux enable to dump security context in SE-PostgreSQL\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")); -@@ -1160,7 +1185,8 @@ dumpTableData_insert(Archive *fout, void +@@ -1160,7 +1169,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), ++ pg_ace_dumpTableDataQuery(pg_ace_feature), fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } -@@ -1774,11 +1800,32 @@ dumpBlobComments(Archive *AH, void *arg) +@@ -1774,11 +1784,14 @@ 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); -+ } ++ pg_ace_dumpBlobComments(pg_ace_feature, AH, g_conn, blobOid); + /* ignore blobs without comments */ if (PQgetisnull(res, i, 1)) @@ -105,15 +361,7 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c 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) +@@ -2926,6 +2939,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 " @@ -121,41 +369,23 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c "from pg_class c " "left join pg_depend d on " "(c.relkind = '%c' and " -@@ -2935,6 +2984,7 @@ getTables(int *numTables) +@@ -2935,6 +2949,7 @@ getTables(int *numTables) "where relkind in ('%c', '%c', '%c', '%c') " "order by c.oid", username_subquery, -+ (!enable_selinux ? "" : ",c." SELINUX_SYSATTR_NAME), ++ pg_ace_dumpClassQuery(pg_ace_feature), 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) +@@ -3131,6 +3146,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 = NULL; -+ if (i_selinux >= 0) -+ tblinfo[i].relsecurity = strdup(PQgetvalue(res, i, i_selinux)); ++ tblinfo[i].relsecurity = pg_ace_dumpClassPreserve(pg_ace_feature, res, i); /* 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 +@@ -4362,11 +4378,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 " @@ -165,19 +395,11 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c "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), ++ pg_ace_dumpAttributeQuery(pg_ace_feature), 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 +@@ -4425,6 +4443,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)); @@ -185,97 +407,67 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c 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 +@@ -4456,6 +4475,8 @@ 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)); ++ tbinfo->attsecurity[j] = pg_ace_dumpAttributePreserve(pg_ace_feature, res, j); } 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) +@@ -6459,8 +6480,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), ++ pg_ace_dumpProcQuery(pg_ace_feature), 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) +@@ -6698,6 +6721,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) if (prosecdef[0] == 't') appendPQExpBuffer(q, " SECURITY DEFINER"); -+ if (proselinux) -+ appendPQExpBuffer(q, " CONTEXT = '%s'", proselinux); ++ 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 -@@ -8779,6 +8856,9 @@ dumpTableSchema(Archive *fout, TableInfo +@@ -8779,6 +8804,8 @@ 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]); ++ pg_ace_dumpAttributePrint(pg_ace_feature, q, tbinfo, j); + actual_atts++; } } -@@ -8826,6 +8906,9 @@ dumpTableSchema(Archive *fout, TableInfo +@@ -8826,6 +8853,8 @@ 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); ++ pg_ace_dumpClassPrint(pg_ace_feature, q, tbinfo); + appendPQExpBuffer(q, ";\n"); /* Loop dumping statistics and storage statements */ -@@ -10243,6 +10326,12 @@ fmtCopyColumnList(const TableInfo *ti) +@@ -10243,6 +10272,10 @@ fmtCopyColumnList(const TableInfo *ti) appendPQExpBuffer(q, "("); needComma = false; + -+ if (enable_selinux) { -+ appendPQExpBuffer(q, SELINUX_SYSATTR_NAME); ++ if (pg_ace_dumpCopyColumnList(pg_ace_feature, q)) + 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 +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 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dump.h 2008-06-15 22:27:55.000000000 +0900 @@ -238,6 +238,7 @@ typedef struct _tableInfo char relkind; char *reltablespace; /* relation tablespace */ @@ -292,16 +484,23 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h /* * 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; +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 tuen on/off SE-PostgreSQL support */ -+#define SELINUX_SYSATTR_NAME "security_context" -+static int enable_selinux = 0; ++/* flag to turn on/off security attribute support */ ++static int pg_ace_feature = PG_ACE_FEATURE_NOTHING; + static FILE *OPF; static char *filename = NULL; @@ -310,67 +509,46 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpal {"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}, ++ {"security-context", no_argument, &pg_ace_feature, PG_ACE_FEATURE_SELINUX}, {NULL, 0, NULL, 0} }; -@@ -290,6 +295,10 @@ main(int argc, char *argv[]) +@@ -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, "enable-selinux") == 0) { -+ appendPQExpBuffer(pgdumpopts, " --enable-selinux"); -+ enable_selinux = 1; -+ } ++ else if (strcmp(optarg, "security-context") == 0) ++ pg_ace_feature = PG_ACE_FEATURE_SELINUX; else { fprintf(stderr, -@@ -300,6 +309,11 @@ main(int argc, char *argv[]) - } - break; +@@ -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"); -+ case 1001: -+ appendPQExpBuffer(pgdumpopts, " --enable-selinux"); -+ enable_selinux = 1; -+ break; -+ - case 0: - break; - -@@ -391,6 +405,24 @@ main(int argc, char *argv[]) + if (optind < argc) + { +@@ -391,6 +400,8 @@ 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); -+ } -+ } ++ pg_ace_dumpCheckServerFeature(pg_ace_feature, conn); + /* * Open the output file if required, otherwise use stdout */ -@@ -505,6 +537,7 @@ help(void) +@@ -505,6 +516,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(_(" --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 +948,18 @@ dumpCreateDB(PGconn *conn) +@@ -915,16 +927,18 @@ dumpCreateDB(PGconn *conn) fprintf(OPF, "--\n-- Database creation\n--\n\n"); if (server_version >= 80100) @@ -385,14 +563,14 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpal "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)); ++ 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 +968,7 @@ dumpCreateDB(PGconn *conn) +@@ -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) @@ -401,7 +579,7 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpal "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) +@@ -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) @@ -410,7 +588,7 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpal "SELECT datname, " "coalesce(" "(select usename from pg_shadow where usesysid=datdba), " -@@ -958,7 +993,7 @@ dumpCreateDB(PGconn *conn) +@@ -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. */ @@ -419,7 +597,7 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpal "SELECT datname, " "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " -@@ -968,6 +1003,7 @@ dumpCreateDB(PGconn *conn) +@@ -968,6 +982,7 @@ dumpCreateDB(PGconn *conn) "FROM pg_database d " "ORDER BY 1"); } @@ -427,20 +605,11 @@ diff -rpNU3 pgace/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpal 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) +@@ -1021,6 +1036,8 @@ dumpCreateDB(PGconn *conn) appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", dbconnlimit); -+ if (enable_selinux && dbsecurity) -+ appendPQExpBuffer(buf, " CONTEXT = '%s'", dbsecurity); ++ pg_ace_dumpDatabasePrint(pg_ace_feature, buf, res, i); + appendPQExpBuffer(buf, ";\n"); diff --git a/sepostgresql-pgace-8.3.3-2.patch b/sepostgresql-pgace-8.3.3-2.patch deleted file mode 100644 index 6ecb3a3..0000000 --- a/sepostgresql-pgace-8.3.3-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-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/commands/dbcommands.c 2008-06-12 22:57:26.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); - -@@ -853,6 +863,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]; -@@ -870,6 +881,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); -@@ -915,6 +933,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-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/commands/proclang.c 2008-06-12 22:57:26.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 pgace/src/backend/commands/tablecmds.c ---- base/src/backend/commands/tablecmds.c 2008-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/commands/tablecmds.c 2008-06-12 22:57:26.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-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/executor/execMain.c 2008-06-12 22:57:26.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. -@@ -1231,6 +1234,8 @@ ExecutePlan(EState *estate, - - for (;;) - { -+ Oid __tts_security = InvalidOid; /* PGACE: explicit security labaling */ -+ - /* Reset the per-output-tuple exprcontext */ - ResetPerTupleExprContext(estate); - -@@ -1355,6 +1360,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); -+ -+ /* - * extract the 'ctid' junk attribute. - */ - if (operation == CMD_UPDATE || operation == CMD_DELETE) -@@ -1381,6 +1393,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 -@@ -1501,6 +1514,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) -@@ -1537,6 +1553,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 -@@ -1603,6 +1626,10 @@ ExecDelete(ItemPointer tupleid, - return; - } - -+ if (!pgaceHeapTupleDelete(resultRelationDesc, tupleid, -+ false, !!resultRelInfo->ri_projectReturning)) -+ return; -+ - /* - * delete the tuple - * -@@ -1739,6 +1766,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) -@@ -1782,6 +1812,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 - * -@@ -2646,7 +2681,8 @@ OpenIntoRel(QueryDesc *queryDesc) - 0, - into->onCommit, - reloptions, -- allowSystemTableMods); -+ allowSystemTableMods, -+ NIL); - - FreeTupleDesc(tupdesc); - -@@ -2752,6 +2788,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-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/optimizer/plan/planner.c 2008-06-12 22:57:26.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-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/tcop/postgres.c 2008-06-12 22:57:26.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-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/utils/adt/ri_triggers.c 2008-06-12 22:57:26.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-06-12 22:34:19.000000000 +0900 -+++ pgace/src/backend/utils/misc/guc.c 2008-06-12 22:57:26.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]; -@@ -4972,6 +4990,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 +5048,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 +5377,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-sepgsql-8.3.3-2.patch b/sepostgresql-sepgsql-8.3.3-2.patch index 5c07b03..2b46490 100644 --- a/sepostgresql-sepgsql-8.3.3-2.patch +++ b/sepostgresql-sepgsql-8.3.3-2.patch @@ -1,6 +1,6 @@ -diff -rpNU3 pgace/configure sepgsql/configure ---- pgace/configure 2008-06-12 23:48:15.000000000 +0900 -+++ sepgsql/configure 2008-06-12 23:52:36.000000000 +0900 +diff -rpNU3 base/configure sepgsql/configure +--- base/configure 2008-06-12 23:42:03.000000000 +0900 ++++ sepgsql/configure 2008-06-14 02:36:58.000000000 +0900 @@ -314,7 +314,7 @@ ac_includes_default="\ # include #endif" @@ -18,7 +18,7 @@ diff -rpNU3 pgace/configure sepgsql/configure --disable-largefile omit support for large files Optional Packages: -@@ -4619,6 +4620,118 @@ fi; +@@ -4619,6 +4620,120 @@ fi; # @@ -116,11 +116,13 @@ diff -rpNU3 pgace/configure sepgsql/configure +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 ++ ++cat >>confdefs.h <<\_ACEOF +#define SECURITY_SYSATTR_NAME "security_context" +_ACEOF + -+ cat >>confdefs.h <<_ACEOF ++ ++cat >>confdefs.h <<_ACEOF +#define HAVE_SELINUX 1 +_ACEOF + @@ -137,7 +139,7 @@ diff -rpNU3 pgace/configure sepgsql/configure # Elf # -@@ -26006,6 +26119,7 @@ s,@with_libxml@,$with_libxml,;t t +@@ -26006,6 +26121,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 @@ -145,10 +147,10 @@ diff -rpNU3 pgace/configure sepgsql/configure 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-06-12 23:48:15.000000000 +0900 -+++ sepgsql/configure.in 2008-06-12 23:52:36.000000000 +0900 -@@ -626,6 +626,19 @@ PGAC_ARG_BOOL(with, zlib, yes, +diff -rpNU3 base/configure.in sepgsql/configure.in +--- base/configure.in 2008-06-12 23:42:03.000000000 +0900 ++++ sepgsql/configure.in 2008-06-14 02:36:58.000000000 +0900 +@@ -626,6 +626,21 @@ PGAC_ARG_BOOL(with, zlib, yes, AC_SUBST(with_zlib) # @@ -158,8 +160,10 @@ diff -rpNU3 pgace/configure.in sepgsql/configure.in + [ --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_DEFINE(SECURITY_SYSATTR_NAME, "security_context", ++ [Enables system column for security attribute support]) ++ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, ++ [Enables SE-PostgreSQL feature]) + AC_SUBST(enable_selinux), + AC_MSG_ERROR("--enable-selinux requires libselinux.")) +fi @@ -168,9 +172,792 @@ diff -rpNU3 pgace/configure.in sepgsql/configure.in # 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 +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@ @@ -179,9 +966,18 @@ diff -rpNU3 pgace/src/Makefile.global.in sepgsql/src/Makefile.global.in 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 +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)) @@ -194,637 +990,3359 @@ diff -rpNU3 pgace/src/backend/Makefile sepgsql/src/backend/Makefile ########################################################################## 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 +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-06-14 02:36:58.000000000 +0900 +@@ -67,6 +67,7 @@ + #include "access/heapam.h" + #include "access/tuptoaster.h" + #include "executor/tuptable.h" ++#include "security/pgace.h" - OBJS := pgaceCommon.o pgaceHooks.o + /* 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,16 @@ heap_getsysattr(HeapTuple tup, int attnu + case TableOidAttributeNumber: + result = ObjectIdGetDatum(tup->t_tableOid); + break; ++#ifdef SECURITY_SYSATTR_NAME ++ case SecurityAttributeNumber: { ++ Oid security_id = HeapTupleGetSecurity(tup); ++ char *sec_label = pgaceSidToSecurityLabel(security_id); ++ ++ result = CStringGetTextDatum(sec_label); ++ pfree(sec_label); ++ break; ++ } ++#endif + default: + elog(ERROR, "invalid attnum: %d", attnum); + result = 0; /* keep compiler quiet */ +@@ -909,6 +923,9 @@ heap_form_tuple(TupleDesc tupleDescripto + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); + ++ if (pgaceSecurityAttributeNecessary()) ++ len += sizeof(Oid); ++ + hoff = len = MAXALIGN(len); /* align user data safely */ + + data_len = heap_compute_data_size(tupleDescriptor, values, isnull); +@@ -940,6 +957,9 @@ heap_form_tuple(TupleDesc tupleDescripto + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; + ++ if (pgaceSecurityAttributeNecessary()) ++ td->t_infomask |= HEAP_HASSECURITY; ++ + heap_fill_tuple(tupleDescriptor, + values, + isnull, +@@ -1020,6 +1040,9 @@ heap_formtuple(TupleDesc tupleDescriptor + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); + ++ if (pgaceSecurityAttributeNecessary()) ++ len += sizeof(Oid); ++ + hoff = len = MAXALIGN(len); /* align user data safely */ + + data_len = ComputeDataSize(tupleDescriptor, values, nulls); +@@ -1051,6 +1074,9 @@ heap_formtuple(TupleDesc tupleDescriptor + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; + ++ if (pgaceSecurityAttributeNecessary()) ++ td->t_infomask |= HEAP_HASSECURITY; ++ + DataFill(tupleDescriptor, + values, + nulls, +@@ -1129,6 +1155,8 @@ heap_modify_tuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecurity(newTuple)) ++ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); + + return newTuple; + } +@@ -1201,6 +1229,8 @@ heap_modifytuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecurity(newTuple)) ++ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); + + return newTuple; + } +@@ -1847,6 +1877,9 @@ heap_form_minimal_tuple(TupleDesc tupleD + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); + ++ if (pgaceSecurityAttributeNecessary()) ++ len += sizeof(Oid); ++ + hoff = len = MAXALIGN(len); /* align user data safely */ + + data_len = heap_compute_data_size(tupleDescriptor, values, isnull); +@@ -1868,6 +1901,9 @@ heap_form_minimal_tuple(TupleDesc tupleD + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + tuple->t_infomask = HEAP_HASOID; + ++ if (pgaceSecurityAttributeNecessary()) ++ tuple->t_infomask |= HEAP_HASSECURITY; ++ + heap_fill_tuple(tupleDescriptor, + values, + isnull, +@@ -1979,6 +2015,10 @@ heap_addheader(int natts, /* max domain + hoff = offsetof(HeapTupleHeaderData, t_bits); + if (withoid) + hoff += sizeof(Oid); ++ ++ if (pgaceSecurityAttributeNecessary()) ++ hoff += sizeof(Oid); ++ + hoff = MAXALIGN(hoff); + len = hoff + structlen; + +@@ -1997,6 +2037,9 @@ heap_addheader(int natts, /* max domain + if (withoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; + ++ if (pgaceSecurityAttributeNecessary()) ++ td->t_infomask |= HEAP_HASSECURITY; ++ + memcpy((char *) td + hoff, structure, structlen); + + return tuple; +diff -rpNU3 base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c +--- base/src/backend/access/heap/heapam.c 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/access/heap/heapam.c 2008-06-14 02:36:58.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 sepgsql/src/backend/access/heap/tuptoaster.c +--- base/src/backend/access/heap/tuptoaster.c 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/access/heap/tuptoaster.c 2008-06-14 02:36:58.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 (pgaceSecurityAttributeNecessary()) ++ 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 (pgaceSecurityAttributeNecessary()) ++ 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 (pgaceSecurityAttributeNecessary()) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +@@ -1175,6 +1182,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 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-06-14 02:36:58.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/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-06-14 02:36:58.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); +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/genbki.sh sepgsql/src/backend/catalog/genbki.sh +--- base/src/backend/catalog/genbki.sh 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/catalog/genbki.sh 2008-06-14 02:36:58.000000000 +0900 +@@ -130,6 +130,16 @@ for dir in $INCLUDE_DIRS; do + fi + done + ++# Get SECURITY_SYSATTR_NAME from pg_config.h ++SECURITY_SYSATTR_NAME_FILTERING="SECURITY_SYSATTR_NAME" ++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'` ++ test -n "$SECURITY_SYSATTR_NAME" && SECURITY_SYSATTR_NAME_FILTERING="^__invalid__pattern__$" ++ break ++ fi ++done ++ + touch ${OUTPUT_PREFIX}.description.$$ + touch ${OUTPUT_PREFIX}.shdescription.$$ + +@@ -144,6 +154,7 @@ touch ${OUTPUT_PREFIX}.shdescription.$$ + # ---------------- + # + cat $INFILES | \ ++grep -v $SECURITY_SYSATTR_NAME_FILTERING | \ + sed -e 's;/\*.*\*/;;g' \ + -e 's;/\*;\ + /*\ +@@ -165,6 +176,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 sepgsql/src/backend/catalog/heap.c +--- base/src/backend/catalog/heap.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/catalog/heap.c 2008-06-18 17:25:44.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}, TEXTOID, 0, -1, ++ SecurityAttributeNumber, 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}; ++#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. +@@ -184,6 +200,20 @@ 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 attno, bool relhasoids) ++{ ++#ifdef SECURITY_SYSATTR_NAME ++ if (pgaceSecurityAttributeNecessary() ++ && attno == SecurityAttributeNumber) ++ return true; ++#endif ++ return false; ++} + + /* ---------------------------------------------------------------- + * XXX END OF UGLY HARD CODED BADNESS XXX +@@ -467,7 +497,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 +533,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 +624,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,12 +675,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); + } + +@@ -665,7 +702,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 +763,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 +829,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 +1002,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 sepgsql/src/backend/catalog/index.c +--- base/src/backend/catalog/index.c 2008-02-03 01:11:28.000000000 +0900 ++++ sepgsql/src/backend/catalog/index.c 2008-06-14 02:36:58.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 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-06-14 02:36:58.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-06-14 02:36:58.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 (HeapTupleHasSecurity(tuple)) ++ HeapTupleSetSecurity(copiedTuple, HeapTupleGetSecurity(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-06-18 17:25:44.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 */ ++ ++ /* security attribute dump/restore support */ ++ FmgrInfo security_out_function; ++ bool security_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 securityOid, + Datum *values, bool *nulls); + static void CopyFrom(CopyState cstate); + static bool CopyReadLine(CopyState cstate); +@@ -277,6 +283,19 @@ static bool CopyGetInt32(CopyState cstat + static void CopySendInt16(CopyState cstate, int16 val); + static bool CopyGetInt16(CopyState cstate, int16 *val); + ++/* ++ * IsWritableSecurityAttribute ++ * ++ * It returns false, if the given attribute is not ++ * a writable security system attribute. ++ */ ++#ifdef SECURITY_SYSATTR_NAME ++#define IsWritableSecurityAttribute(attnum) \ ++ ((attnum) == SecurityAttributeNumber \ ++ && pgaceSecurityAttributeNecessary()) ++#else ++#define IsWritableSecurityAttribute(attnum) (false) ++#endif + + /* + * Send copy start/stop messages for frontend copies. These have changed +@@ -1073,6 +1092,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 +1114,11 @@ 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 (IsWritableSecurityAttribute(attnum)) ++ { ++ cstate->security_force_quot = true; ++ continue; ++ } + cstate->force_quote_flags[attnum - 1] = true; + } + } +@@ -1115,6 +1141,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 (IsWritableSecurityAttribute(attnum)) ++ continue; /* ignore, if specified */ ++ + cstate->force_notnull_flags[attnum - 1] = true; + } + } +@@ -1305,16 +1334,29 @@ CopyTo(CopyState cstate) + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; ++ FmgrInfo *out_fmgr; ++ Form_pg_attribute attForm; ++ ++ if (IsWritableSecurityAttribute(attnum)) ++ { ++ attForm = SystemAttributeDefinition(attnum, false); ++ out_fmgr = &cstate->security_out_function; ++ } ++ else ++ { ++ attForm = attr[attnum - 1]; ++ out_fmgr = &cstate->out_functions[attnum - 1]; ++ } + + 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 +1411,14 @@ CopyTo(CopyState cstate) + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +- colname = NameStr(attr[attnum - 1]->attname); ++ if (IsWritableSecurityAttribute(attnum)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, false); ++ colname = NameStr(attForm->attname); ++ } ++ else ++ colname = NameStr(attr[attnum - 1]->attname); + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +@@ -1395,11 +1444,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), ++ HeapTupleGetSecurity(tuple), ++ values, nulls); + } + + heap_endscan(scandesc); +@@ -1425,7 +1480,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 +1519,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 +1531,22 @@ CopyOneRowTo(CopyState cstate, Oid tuple + need_delim = true; + } + ++ if (IsWritableSecurityAttribute(attnum)) ++ { ++ char *tmp = pgaceSidToSecurityLabel(tupleSecurity); ++ value = CStringGetTextDatum(tmp); ++ 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 +1558,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 +1569,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 +1703,13 @@ CopyFrom(CopyState cstate) + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; ++ FmgrInfo security_in_function; + Oid *typioparams; + Oid oid_typioparam; ++ Oid security_typioparam; + int attnum; + int i; ++ ListCell *l; + Oid in_func_oid; + Datum *values; + char *nulls; +@@ -1872,6 +1945,25 @@ CopyFrom(CopyState cstate) + fmgr_info(in_func_oid, &oid_in_function); + } + ++ foreach (l, cstate->attnumlist) ++ { ++ attnum = lfirst_int(l); ++ ++ if (IsWritableSecurityAttribute(attnum)) ++ { ++ if (!cstate->binary) ++ getTypeInputInfo(TEXTOID, ++ &in_func_oid, ++ &security_typioparam); ++ else ++ getTypeBinaryInputInfo(TEXTOID, ++ &in_func_oid, ++ &security_typioparam); ++ fmgr_info(in_func_oid, &security_in_function); ++ break; ++ } ++ } ++ + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (char *) palloc(num_phys_attrs * sizeof(char)); + +@@ -1904,6 +1996,7 @@ CopyFrom(CopyState cstate) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; ++ Datum loaded_security = PointerGetDatum(NULL); + + CHECK_FOR_INTERRUPTS(); + +@@ -1978,6 +2071,32 @@ CopyFrom(CopyState cstate) + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (IsWritableSecurityAttribute(attnum)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, false); ++ ++ 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) ++ { ++ loaded_security ++ = InputFunctionCall(&security_in_function, ++ string, ++ security_typioparam, ++ attForm->atttypmod); ++ } ++ cstate->cur_attname = NULL; ++ cstate->cur_attval = NULL; ++ continue; ++ } ++ + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), +@@ -2048,6 +2167,27 @@ CopyFrom(CopyState cstate) + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (IsWritableSecurityAttribute(attnum)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, false); ++ Datum tmp; ++ ++ cstate->cur_attname = NameStr(attForm->attname); ++ i++; ++ ++ tmp = CopyReadBinaryAttribute(cstate, ++ i, ++ &security_in_function, ++ security_typioparam, ++ attForm->atttypmod, ++ &isnull); ++ if (!isnull) ++ loaded_security = tmp; ++ cstate->cur_attname = NULL; ++ continue; ++ } ++ + cstate->cur_attname = NameStr(attr[m]->attname); + i++; + values[m] = CopyReadBinaryAttribute(cstate, +@@ -2079,6 +2219,12 @@ CopyFrom(CopyState cstate) + + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); ++ if (loaded_security != PointerGetDatum(NULL)) ++ { ++ Oid security_id ++ = pgaceSecurityLabelToSid(TextDatumGetCString(loaded_security)); ++ HeapTupleSetSecurity(tuple, security_id); ++ } + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); +@@ -2102,6 +2248,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 +3513,18 @@ CopyGetAttnums(TupleDesc tupDesc, Relati + break; + } + } ++ ++ /* Is it writable system column? */ ++ if (attnum == InvalidAttrNumber) ++ { ++ Form_pg_attribute attForm; ++ bool relhasoids = RelationGetForm(rel)->relhasoids; ++ ++ attForm = SystemAttributeByName(name, relhasoids); ++ if (attForm && IsWritableSecurityAttribute(attForm->attnum)) ++ attnum = attForm->attnum; ++ } ++ + if (attnum == InvalidAttrNumber) + { + if (rel != NULL) +@@ -3413,7 +3574,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-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/commands/dbcommands.c 2008-06-14 02:36:58.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); + +@@ -853,6 +863,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]; +@@ -870,6 +881,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); +@@ -915,6 +933,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 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/commands/functioncmds.c 2008-06-14 02:36:58.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/prepare.c sepgsql/src/backend/commands/prepare.c +--- base/src/backend/commands/prepare.c 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/commands/prepare.c 2008-06-14 02:36:58.000000000 +0900 +@@ -27,6 +27,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_type.h" + #include "rewrite/rewriteHandler.h" ++#include "security/pgace.h" + #include "tcop/pquery.h" + #include "tcop/tcopprot.h" + #include "tcop/utility.h" +@@ -359,6 +360,8 @@ EvaluateParams(PreparedStatement *pstmt, + lfirst(l) = expr; + i++; + } ++ /* Evaluate permissions to use parameters */ ++ pgaceEvaluateParams(params); + + /* Prepare the expressions for execution */ + exprstates = (List *) ExecPrepareExpr((Expr *) params, estate); +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-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/commands/tablecmds.c 2008-06-14 02:36:58.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); +@@ -2591,11 +2597,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 (HeapTupleHasSecurity(tuple)) ++ tupSid = HeapTupleGetSecurity(tuple); + + /* Set dropped attributes to null in new tuple */ + foreach(lc, dropped_attrs) +@@ -2627,6 +2636,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oi + /* Preserve OID, if any */ + if (newTupDesc->tdhasoid) + HeapTupleSetOid(tuple, tupOid); ++ /* Preserve Security ID, if any */ ++ if (tupSid != InvalidOid) ++ HeapTupleSetSecurity(tuple, tupSid); + } + + /* Now check any constraints on the possibly-changed tuple */ +diff -rpNU3 base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c +--- base/src/backend/commands/trigger.c 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/commands/trigger.c 2008-06-14 02:36:58.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); + ++ if (!pgaceCallFunctionTrigger(finfo, trigdata)) ++ { ++ MemoryContextSwitchTo(oldContext); ++ return (HeapTuple) DatumGetPointer(NULL); ++ } ++ + result = FunctionCallInvoke(&fcinfo); + + MemoryContextSwitchTo(oldContext); +diff -rpNU3 base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c +--- base/src/backend/executor/execMain.c 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/executor/execMain.c 2008-06-26 10:27:05.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. +@@ -1164,6 +1167,50 @@ 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_security) ++{ ++ AttrNumber attno; ++ Datum datum; ++ bool isnull; ++ ++#ifdef SECURITY_SYSATTR_NAME ++ attno = ExecFindJunkAttribute(junkfilter, SECURITY_SYSATTR_NAME); ++ if (attno != InvalidAttrNumber) ++ { ++ datum = ExecGetJunkAttribute(slot, attno, &isnull); ++ if (!isnull) ++ *tts_security = datum; ++ } ++#endif ++} ++ ++static void ++storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple) ++{ ++ /* for security attribute */ ++ if (HeapTupleHasSecurity(tuple)) ++ { ++ if (!DatumGetPointer(slot->tts_security)) ++ HeapTupleSetSecurity(tuple, InvalidOid); ++ else ++ { ++ char *label = TextDatumGetCString(slot->tts_security); ++ ++ HeapTupleSetSecurity(tuple, ++ pgaceSecurityLabelToSid(label)); ++ } ++ } ++} ++ + /* ---------------------------------------------------------------- + * ExecutePlan + * +@@ -1231,6 +1278,8 @@ ExecutePlan(EState *estate, + + for (;;) + { ++ Datum tts_security = PointerGetDatum(NULL); ++ + /* Reset the per-output-tuple exprcontext */ + ResetPerTupleExprContext(estate); + +@@ -1355,6 +1404,11 @@ lnext: ; + } + + /* ++ * extract writable system attribute ++ */ ++ fetchWritableSystemAttribute(junkfilter, slot, &tts_security); ++ ++ /* + * extract the 'ctid' junk attribute. + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE) +@@ -1381,6 +1435,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 +@@ -1501,6 +1556,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) +@@ -1537,6 +1594,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 +@@ -1603,6 +1667,10 @@ ExecDelete(ItemPointer tupleid, + return; + } + ++ if (!pgaceHeapTupleDelete(resultRelationDesc, tupleid, ++ false, !!resultRelInfo->ri_projectReturning)) ++ return; ++ + /* + * delete the tuple + * +@@ -1739,6 +1807,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) +@@ -1783,6 +1853,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 +@@ -2646,7 +2723,8 @@ OpenIntoRel(QueryDesc *queryDesc) + 0, + into->onCommit, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ NIL); + + FreeTupleDesc(tupdesc); + +@@ -2752,6 +2830,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 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/executor/execQual.c 2008-06-14 02:36:58.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" +@@ -1772,6 +1773,8 @@ ExecEvalOper(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/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 2008-06-14 02:36:58.000000000 +0900 +@@ -20,6 +20,7 @@ + + #include "executor/executor.h" + #include "miscadmin.h" ++#include "security/pgace.h" + #include "utils/memutils.h" + + +@@ -48,7 +49,7 @@ TupleTableSlot * + ExecScan(ScanState *node, + ExecScanAccessMtd accessMtd) /* function returning a tuple */ + { +- ExprContext *econtext; ++ ExprContext *econtext = node->ps.ps_ExprContext; + List *qual; + ProjectionInfo *projInfo; + ExprDoneCond isDone; +@@ -65,7 +66,22 @@ ExecScan(ScanState *node, + * all the overhead and return the raw scan tuple. + */ + if (!qual && !projInfo) +- return (*accessMtd) (node); ++ { ++ while (true) ++ { ++ resultSlot = (*accessMtd) (node); ++ ++ if (TupIsNull(resultSlot)) ++ break; ++ ++ if (pgaceExecScan((Scan *)node->ps.plan, ++ node->ss_currentRelation, resultSlot)) ++ break; ++ ++ ResetExprContext(econtext); ++ } ++ return resultSlot; ++ } + + /* + * Check to see if we're still projecting out tuples from a previous scan +@@ -87,7 +103,6 @@ ExecScan(ScanState *node, + * storage allocated in the previous tuple cycle. Note this can't happen + * until we're done projecting out tuples from a scan tuple. + */ +- econtext = node->ps.ps_ExprContext; + ResetExprContext(econtext); + + /* +@@ -127,8 +142,11 @@ 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 ((!qual || ExecQual(qual, econtext, false)) ++ && pgaceExecScan((Scan *)node->ps.plan, ++ node->ss_currentRelation, slot)) + { + /* + * Found a satisfactory scan tuple. +diff -rpNU3 base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c +--- base/src/backend/executor/spi.c 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/executor/spi.c 2008-06-14 02:36:58.000000000 +0900 +@@ -606,6 +606,8 @@ SPI_modifytuple(Relation rel, HeapTuple + mtuple->t_tableOid = tuple->t_tableOid; + if (rel->rd_att->tdhasoid) + HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecurity(tuple)) ++ HeapTupleSetSecurity(mtuple, HeapTupleGetSecurity(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 2008-06-14 02:36:58.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; + } +@@ -1523,6 +1525,7 @@ _copyRangeTblEntry(RangeTblEntry *from) + COPY_SCALAR_FIELD(inFromCl); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); ++ COPY_SCALAR_FIELD(pgaceTuplePerms); + + return newnode; + } +@@ -1789,6 +1792,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 +1873,7 @@ _copyQuery(Query *from) + COPY_NODE_FIELD(limitCount); + COPY_NODE_FIELD(rowMarks); + COPY_NODE_FIELD(setOperations); ++ COPY_NODE_FIELD(pgaceItem); + + return newnode; + } +@@ -2105,6 +2110,7 @@ _copyCreateStmt(CreateStmt *from) + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(oncommit); + COPY_STRING_FIELD(tablespacename); ++ COPY_NODE_FIELD(pgaceItem); + + return newnode; + } +@@ -2998,6 +3004,49 @@ _copyValue(Value *from) + return newnode; + } + ++/* **************************************************************** ++ * nodes/security.h copy functions ++ * **************************************************************** ++ */ ++static SEvalItemRelation * ++_copySEvalItemRelation(SEvalItemRelation *from) ++{ ++ SEvalItemRelation *newnode = makeNode(SEvalItemRelation); ++ ++ COPY_SCALAR_FIELD(perms); ++ ++ COPY_SCALAR_FIELD(relid); ++ COPY_SCALAR_FIELD(inh); ++ ++ return newnode; ++} ++ ++static SEvalItemAttribute * ++_copySEvalItemAttribute(SEvalItemAttribute *from) ++{ ++ SEvalItemAttribute *newnode = makeNode(SEvalItemAttribute); ++ ++ COPY_SCALAR_FIELD(perms); ++ ++ COPY_SCALAR_FIELD(relid); ++ COPY_SCALAR_FIELD(inh); ++ COPY_SCALAR_FIELD(attno); ++ ++ return newnode; ++} ++ ++static SEvalItemProcedure * ++_copySEvalItemProcedure(SEvalItemProcedure *from) ++{ ++ SEvalItemProcedure *newnode = makeNode(SEvalItemProcedure); ++ ++ COPY_SCALAR_FIELD(perms); ++ ++ COPY_SCALAR_FIELD(funcid); ++ ++ return newnode; ++} ++ + /* + * copyObject + * +@@ -3600,6 +3649,15 @@ copyObject(void *from) + case T_XmlSerialize: + retval = _copyXmlSerialize(from); + break; ++ case T_SEvalItemRelation: ++ retval = _copySEvalItemRelation(from); ++ break; ++ case T_SEvalItemAttribute: ++ retval = _copySEvalItemAttribute(from); ++ break; ++ case T_SEvalItemProcedure: ++ retval = _copySEvalItemProcedure(from); ++ break; + + default: + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from)); +diff -rpNU3 base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c +--- base/src/backend/nodes/outfuncs.c 2008-01-14 22:59:48.000000000 +0900 ++++ sepgsql/src/backend/nodes/outfuncs.c 2008-06-14 02:36:58.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); + } + + /* +@@ -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 +@@ -1833,6 +1836,7 @@ _outRangeTblEntry(StringInfo str, RangeT + WRITE_BOOL_FIELD(inFromCl); + WRITE_UINT_FIELD(requiredPerms); + WRITE_OID_FIELD(checkAsUser); ++ WRITE_UINT_FIELD(pgaceTuplePerms); + } + + static void +@@ -2045,6 +2049,43 @@ _outFkConstraint(StringInfo str, FkConst + WRITE_BOOL_FIELD(skip_validation); + } + ++/***************************************************************************** ++ * ++ * Stuff from nodes/security.h ++ * ++ *****************************************************************************/ ++static void ++_outSEvalItemRelation(StringInfo str, SEvalItemRelation *node) ++{ ++ WRITE_NODE_TYPE("SEVALITEMRELATION"); ++ ++ WRITE_UINT_FIELD(perms); ++ ++ WRITE_OID_FIELD(relid); ++ WRITE_BOOL_FIELD(inh); ++} ++ ++static void ++_outSEvalItemAttribute(StringInfo str, SEvalItemAttribute *node) ++{ ++ WRITE_NODE_TYPE("SEVALITEMATTRIBUTE"); ++ ++ WRITE_UINT_FIELD(perms); ++ ++ WRITE_OID_FIELD(relid); ++ WRITE_BOOL_FIELD(inh); ++ WRITE_INT_FIELD(attno); ++} ++ ++static void ++_outSEvalItemProcedure(StringInfo str, SEvalItemProcedure *node) ++{ ++ WRITE_NODE_TYPE("SEVALITEMPROCEDURE"); ++ ++ WRITE_UINT_FIELD(perms); ++ ++ WRITE_OID_FIELD(funcid); ++} + + /* + * _outNode - +@@ -2438,6 +2479,15 @@ _outNode(StringInfo str, void *obj) + case T_XmlSerialize: + _outXmlSerialize(str, obj); + break; ++ case T_SEvalItemRelation: ++ _outSEvalItemRelation(str, obj); ++ break; ++ case T_SEvalItemAttribute: ++ _outSEvalItemAttribute(str, obj); ++ break; ++ case T_SEvalItemProcedure: ++ _outSEvalItemProcedure(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 2008-06-14 02:36:58.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,52 @@ _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 SEvalItemRelation * ++_readSEvalItemRelation(void) ++{ ++ READ_LOCALS(SEvalItemRelation); ++ ++ READ_UINT_FIELD(perms); ++ ++ READ_OID_FIELD(relid); ++ READ_BOOL_FIELD(inh); ++ ++ READ_DONE(); ++} ++ ++static SEvalItemAttribute * ++_readSEvalItemAttribute(void) ++{ ++ READ_LOCALS(SEvalItemAttribute); ++ ++ READ_UINT_FIELD(perms); ++ ++ READ_OID_FIELD(relid); ++ READ_BOOL_FIELD(inh); ++ READ_INT_FIELD(attno); ++ ++ READ_DONE(); ++} ++ ++static SEvalItemProcedure * ++_readSEvalItemProcedure(void) ++{ ++ READ_LOCALS(SEvalItemProcedure); ++ ++ READ_UINT_FIELD(perms); ++ ++ READ_OID_FIELD(funcid); ++ ++ READ_DONE(); ++} + + /* + * parseNodeString +@@ -1124,6 +1168,12 @@ parseNodeString(void) + return_value = _readNotifyStmt(); + else if (MATCH("DECLARECURSOR", 13)) + return_value = _readDeclareCursorStmt(); ++ else if (MATCH("SEVALITEMRELATION", 17)) ++ return_value = _readSEvalItemRelation(); ++ else if (MATCH("SEVALITEMATTRIBUTE", 18)) ++ return_value = _readSEvalItemAttribute(); ++ else if (MATCH("SEVALITEMPROCEDURE", 18)) ++ return_value = _readSEvalItemProcedure(); + 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-06-14 02:36:58.000000000 +0900 +@@ -38,6 +38,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_oper.h" + #include "parser/parsetree.h" ++#include "security/pgace.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + +@@ -97,7 +98,7 @@ planner(Query *parse, int cursorOptions, + { + PlannedStmt *result; + +- if (planner_hook) ++ if (planner_hook && pgaceIsAllowPlannerHook()) + result = (*planner_hook) (parse, cursorOptions, boundParams); + else + result = standard_planner(parse, cursorOptions, boundParams); +@@ -197,6 +198,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/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 2008-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/parser/analyze.c 2008-06-18 17:25:44.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 +@@ -563,14 +565,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); +@@ -668,6 +671,45 @@ 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, relhasoids)) ++ { ++ 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 - +@@ -734,6 +776,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-06-14 02:36:58.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 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-07-11 14:10:51.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,31 @@ 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) ++ if (attrno > 0) ++ { ++ attrtype = attnumTypeId(rd, attrno); ++ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ } ++ else if (SystemAttributeIsWritable(attrno, relhasoids)) ++ { ++ Form_pg_attribute attr; ++ ++ attr = SystemAttributeDefinition(attrno, relhasoids); ++ 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))); +- attrtype = attnumTypeId(rd, attrno); +- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ return NULL; /* compiler kindness */ ++ } + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +@@ -467,6 +484,9 @@ updateTargetListEntry(ParseState *pstate + List *indirection, + int location) + { ++ bool relhasoids ++ = RelationGetForm(pstate->p_target_relation)->relhasoids; ++ + /* Fix up expression as needed */ + tle->expr = transformAssignedExpr(pstate, + tle->expr, +@@ -483,6 +503,9 @@ updateTargetListEntry(ParseState *pstate + */ + tle->resno = (AttrNumber) attrno; + tle->resname = colname; ++ ++ if (SystemAttributeIsWritable(attrno, relhasoids)) ++ tle->resjunk = true; + } + + +@@ -749,6 +772,7 @@ checkInsertTargets(ParseState *pstate, L + Bitmapset *wholecols = NULL; + Bitmapset *partialcols = NULL; + ListCell *tl; ++ uint32 system_attrs = 0; + + foreach(tl, cols) + { +@@ -757,14 +781,40 @@ 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) ++ { ++ bool relhasoids ++ = RelationGetForm(pstate->p_target_relation)->relhasoids; ++ ++ if (SystemAttributeIsWritable(attrno, relhasoids)) ++ { ++ 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-01-14 22:59:48.000000000 +0900 ++++ sepgsql/src/backend/postmaster/postmaster.c 2008-06-14 02:36:58.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-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/rewrite/rewriteHandler.c 2008-06-26 10:27:05.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,7 @@ QueryRewrite(Query *parsetree) + if (!foundOriginalQuery && lastInstead != NULL) + lastInstead->canSetTag = true; + ++ results = pgaceProxyQuery(results); ++ + return 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 2008-06-14 02:36:58.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 ++ +ifeq ($(enable_selinux), yes) -+OBJS += sepgsql/core.o sepgsql/hooks.o \ ++OBJS += sepgsql/avc.o 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 */ ++all: SUBSYS.o + - /****************************************************************** - * 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; ++SUBSYS.o: $(OBJS) ++ $(LD) $(LDREL) $(LDOUT) $@ $^ + -+ foreach (l, queryList) { -+ Query *q = (Query *) lfirst(l); ++depend dep: ++ $(CC) -MM $(CFLAGS) *.c >depend + -+ 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; ++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 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,709 @@ + -+ 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. ++ * 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 + * -+ * Copyright (c) 2007 KaiGai Kohei + */ +#include "postgres.h" + -+#include "access/heapam.h" +#include "access/genam.h" -+#include "access/tupdesc.h" ++#include "access/hash.h" ++#include "access/heapam.h" +#include "access/xact.h" -+#include "catalog/pg_database.h" -+#include "libpq/libpq-be.h" -+#include "libpq/pqsignal.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 "security/sepgsql.h" -+#include "storage/lwlock.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" -+#include "utils/rel.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++#include ++#include ++ ++/***************************************************************************** ++ * GUC Parameter Support ++ *****************************************************************************/ ++ ++/* ++ * pgaceShowsFeatureIdentifier ++ * ++ * It is invoked when 'pgace_feature' is refered, and it has to return ++ * an identifier of the guest. ++ */ ++const char * ++pgaceShowSecurityFeature(void) ++{ ++ return pgaceSecurityFeatureIdentity(); ++} ++ ++/***************************************************************************** ++ * 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 *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)) == 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); ++ } ++ elog(ERROR, "security id: %u is not a valid identifier", sid); ++ return NULL; /* for compiler kindness */ ++} ++ ++void ++pgacePostBootstrapingMode(void) ++{ ++ Relation rel; ++ CatalogIndexState ind; ++ HeapTuple tuple; ++ earlySeclabel *es, *_es; ++ Oid meta_sid; ++ Datum value; ++ char 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 = ' '; ++ tuple = heap_formtuple(RelationGetDescr(rel), &value, &isnull); ++ ++ HeapTupleSetOid(tuple, es->sid); ++ HeapTupleSetSecurity(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; ++ ++ /* ++ * valid label checks ++ */ ++ raw_label = pgaceValidateSecurityLabel(raw_label); ++ ++ 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; ++ char isnull; ++ ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ ++ slabel = pgaceSecurityLabelOfLabel(); ++ ++ if (!strcmp(raw_label, slabel)) ++ { ++ labelOid = labelSid = GetNewOid(rel); ++ } ++ else ++ { ++ labelSid = pgaceLookupSecurityId(slabel); ++ labelOid = GetNewOid(rel); ++ } ++ ++ ind = CatalogOpenIndexes(rel); ++ ++ labelTxt = CStringGetTextDatum(raw_label); ++ isnull = ' '; ++ tuple = heap_formtuple(RelationGetDescr(rel), ++ &labelTxt, &isnull); ++ HeapTupleSetSecurity(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); ++ ++ 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 security_id) ++{ ++ HeapTuple tuple; ++ Datum labelTxt; ++ char *label, isnull; ++ ++ if (security_id == InvalidOid) ++ return pgaceValidateSecurityLabel(NULL); ++ ++ if (IsBootstrapProcessingMode()) ++ return earlySidToSecurityLabel(security_id); ++ ++ tuple = SearchSysCache(SECURITYOID, ++ ObjectIdGetDatum(security_id), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "security id: %u is not a valid identifier", security_id); ++ ++ labelTxt = SysCacheGetAttr(SECURITYOID, ++ tuple, Anum_pg_security_seclabel, &isnull); ++ Assert(!isnull); ++ label = TextDatumGetCString(labelTxt); ++ ReleaseSysCache(tuple); ++ ++ return label; ++} ++ ++char * ++pgaceSidToSecurityLabel(Oid security_id) ++{ ++ char *label = pgaceLookupSecurityLabel(security_id); ++ ++ label = pgaceTranslateSecurityLabelOut(label); ++ Assert(label != NULL); ++ ++ return label; ++} ++ ++/***************************************************************************** ++ * 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 security_id; ++ ++ 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); ++ security_id = HeapTupleGetSecurity(tuple); ++ ++ systable_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ ++ return CStringGetTextDatum(pgaceSidToSecurityLabel(security_id)); ++} ++ ++/* ++ * 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 security_id; ++ List *okList = NIL; ++ bool found = false; ++ ++ security_id = 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); ++ HeapTupleSetSecurity(newtup, security_id); ++ ++ foreach (l, okList) ++ { ++ if (HeapTupleGetSecurity(oldtup) == lfirst_oid(l)) ++ goto skip; /* already checked */ ++ } ++ okList = lappend_oid(okList, HeapTupleGetSecurity(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 ++ ******************************************************************/ ++ ++/* ++ * 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 ++sepgsql_is_disabled(const char *function) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("%s is not implemented", function))); ++ PG_RETURN_VOID(); ++} ++ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_getservcon(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_user(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_role(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_type(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_range(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_user(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_role(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_type(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_range(PG_FUNCTION_ARGS) ++{ ++ return sepgsql_is_disabled(__FUNCTION__); ++} ++ ++#endif /* HAVE_SELINUX */ +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 2008-07-11 14:14:24.000000000 +0900 +@@ -0,0 +1,1123 @@ ++ ++/* ++ * 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 -+#include -+#include -+#include +#include + -+static struct { -+ struct { -+ char *name; /* name of object class */ -+ uint16 inum; /* internal identifier number */ ++/* ++ * 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; /* name of access vector */ -+ uint32 inum; /* internal identifier number */ ++ struct ++ { ++ char *name; ++ access_vector_t internal; + } av_perms[sizeof(access_vector_t) * 8]; +} selinux_catalog[] = { + { -+ { "db_database", SECCLASS_DB_DATABASE }, ++ { "db_database", SECCLASS_DB_DATABASE}, + { + { "create", DB_DATABASE__CREATE }, + { "drop", DB_DATABASE__DROP }, @@ -837,11 +4355,11 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/core.c sepgsql/src/backend/securi + { "load_module", DB_DATABASE__LOAD_MODULE }, + { "get_param", DB_DATABASE__GET_PARAM }, + { "set_param", DB_DATABASE__SET_PARAM }, -+ { NULL, 0UL }, ++ { NULL, 0UL }, + } + }, + { -+ { "db_table", SECCLASS_DB_TABLE }, ++ { "db_table", SECCLASS_DB_TABLE}, + { + { "create", DB_TABLE__CREATE }, + { "drop", DB_TABLE__DROP }, @@ -849,17 +4367,17 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/core.c sepgsql/src/backend/securi + { "setattr", DB_TABLE__SETATTR }, + { "relabelfrom", DB_TABLE__RELABELFROM }, + { "relabelto", DB_TABLE__RELABELTO }, -+ { "use", DB_TABLE__USE }, ++ { "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 }, ++ { NULL, 0UL }, + } + }, + { -+ { "db_procedure", SECCLASS_DB_PROCEDURE }, ++ { "db_procedure", SECCLASS_DB_PROCEDURE}, + { + { "create", DB_PROCEDURE__CREATE }, + { "drop", DB_PROCEDURE__DROP }, @@ -869,11 +4387,11 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/core.c sepgsql/src/backend/securi + { "relabelto", DB_PROCEDURE__RELABELTO }, + { "execute", DB_PROCEDURE__EXECUTE }, + { "entrypoint", DB_PROCEDURE__ENTRYPOINT }, -+ { NULL, 0UL }, ++ { NULL, 0UL }, + } + }, + { -+ { "db_column", SECCLASS_DB_COLUMN }, ++ { "db_column", SECCLASS_DB_COLUMN}, + { + { "create", DB_COLUMN__CREATE }, + { "drop", DB_COLUMN__DROP }, @@ -885,336 +4403,369 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/core.c sepgsql/src/backend/securi + { "select", DB_COLUMN__SELECT }, + { "update", DB_COLUMN__UPDATE }, + { "insert", DB_COLUMN__INSERT }, -+ { NULL, 0UL }, ++ { 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 }, ++ { "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 }, ++ { "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; ++static MemoryContext AvcMemCtx; + -+ 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); ++#define AVC_HASH_NUM_SLOTS 256 ++#define AVC_HASH_NUM_NODES 600 ++ ++typedef struct ++{ ++ uint32 hash_key; ++ ++ security_context_t scon; /* source security context */ ++ security_context_t tcon; /* target security context */ ++ Oid tsid; /* target security id, if exist */ ++ security_class_t tclass; /* object class number */ ++ ++ security_context_t ncon; /* newly created security context */ ++ Oid nsid; /* security id of ncon, if exist */ ++ ++ access_vector_t allowed; ++ access_vector_t decided; ++ access_vector_t auditallow; ++ access_vector_t auditdeny; ++ ++ bool hot_cache; ++} avc_datum; ++static sig_atomic_t avc_version; ++static bool avc_enforcing; ++static List *avc_slot[AVC_HASH_NUM_SLOTS]; ++static uint32 avc_datum_count = 0; ++static uint32 avc_lru_hint = 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[NUM_SELINUX_CATALOG]; ++} *selinux_state = NULL; ++ ++Size ++sepgsqlShmemSize(void) ++{ ++ return sizeof(*selinux_state); +} + -+static const char *sepgsql_av_perm_to_string(uint16 tclass, uint32 perm) ++/* ++ * 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; ++ security_class_t tclass; ++ access_vector_t av_perms; ++ int i, j; + -+ for (i=0; i < NUM_SELINUX_CATALOG; i++) { -+ if (selinux_catalog[i].tclass.inum == tclass) { -+ char *perm_name; ++ for (i = 0; i < NUM_SELINUX_CATALOG; i++) ++ { ++ tclass = string_to_security_class(selinux_catalog[i].tclass.name); ++ if (!tclass) ++ tclass = selinux_catalog[i].tclass.internal; + -+ for (j=0; (perm_name = selinux_catalog[i].av_perms[j].name); j++) { -+ if (selinux_catalog[i].av_perms[j].inum == perm) ++ selinux_state->catalog[i].tclass.internal ++ = selinux_catalog[i].tclass.internal; ++ selinux_state->catalog[i].tclass.external = tclass; ++ ++ for (j = 0; selinux_catalog[i].av_perms[j].name; j++) ++ { ++ av_perms = ++ string_to_av_perm(tclass, selinux_catalog[i].av_perms[j].name); ++ if (!av_perms) ++ av_perms = selinux_catalog[i].av_perms[j].internal; ++ ++ selinux_state->catalog[i].av_perms[j].internal ++ = selinux_catalog[i].av_perms[j].internal; ++ selinux_state->catalog[i].av_perms[j].external = av_perms; ++ } ++ } ++} ++ ++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 < NUM_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 */ ++} ++ ++static access_vector_t ++trans_to_internal_perms(security_class_t e_tclass, access_vector_t e_perms) ++{ ++ /* have to hold SepgsqlAvcLock with LW_SHARED */ ++ access_vector_t i_perms = 0UL; ++ int i, j; ++ ++ for (i = 0; i < NUM_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 & e_perms) != 0) ++ i_perms |= selinux_state->catalog[i].av_perms[j].internal; ++ } ++ return i_perms; ++ } ++ return e_perms; /* use it as is for kernel classes */ ++} ++ ++static const char * ++sepgsql_class_to_string(security_class_t tclass) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_SELINUX_CATALOG; i++) ++ { ++ if (selinux_catalog[i].tclass.internal == tclass) ++ return selinux_catalog[i].tclass.name; ++ } ++ /* ++ * tclass is always same as external one, 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 < NUM_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"; + } + } -+ /* 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); ++ /* ++ * tclass is always same as external one, for kernel object classes ++ */ ++ return security_av_perm_to_string(tclass, perm); +} + +/* -+ * SE-PostgreSQL Internal AVC(Access Vector Cache) implementation. -+ * ++ * sepgsql_avc_reset ++ * ++ * This function clears all current avc entries, and update its version. + */ -+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) ++static void ++sepgsql_avc_reset(void) +{ -+ return sizeof(*avc_shmem); ++ int i; ++ ++ MemoryContextReset(AvcMemCtx); ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ ++ avc_version = selinux_state->version; ++ if (!strcmp(sepostgresql_mode, "default")) ++ avc_enforcing = selinux_state->enforcing; ++ else if (!strcmp(sepostgresql_mode, "permissive")) ++ avc_enforcing = false; ++ else if (!strcmp(sepostgresql_mode, "enforcing")) ++ avc_enforcing = true; ++ else ++ elog(FATAL, "SELinux: undefined state (sepostgresql = %s)", sepostgresql_mode); ++ ++ for (i = 0; i < AVC_HASH_NUM_SLOTS; i++) ++ avc_slot[i] = NIL; ++ avc_datum_count = 0; ++ ++ LWLockRelease(SepgsqlAvcLock); +} + -+static void sepgsql_load_class_av_mapping() ++/* ++ * sepgsql_avc_reclaim ++ * ++ * This function reclaims recently not-used avc entries, ++ * when the number of caches overs AVC_HASH_NUM_NODES ++ */ ++static void ++sepgsql_avc_reclaim(void) +{ -+ extern char *selinux_mnt; -+ char buffer[PATH_MAX]; -+ struct stat st_buf; -+ int i, j, fd, len; ++ List *slot; ++ ListCell *l; ++ avc_datum *cache; + -+ if (!selinux_mnt) -+ goto legacy_mapping; ++ while (avc_datum_count > AVC_HASH_NUM_NODES) ++ { ++ avc_lru_hint = (avc_lru_hint + 1) % AVC_HASH_NUM_SLOTS; ++ slot = avc_slot[avc_lru_hint]; ++ foreach(l, slot) ++ { ++ cache = lfirst(l); + -+ /* 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; ++ if (cache->hot_cache) ++ { ++ cache->hot_cache = false; ++ continue; + } -+ return __perms; ++ list_delete_ptr(slot, cache); ++ pfree(cache); ++ avc_datum_count--; + } + } -+ return (uint32) perms; +} + -+static void sepgsql_compute_avc_datum(Oid ssid, Oid tsid, uint16 tclass, -+ struct avc_datum *avd) ++static void ++sepgsql_avc_insert(avc_datum *cache, uint32 hash_key) +{ -+ security_class_t tclass_external = tclass; -+ security_context_t scon, tcon, ncon; -+ struct av_decision x; -+ Datum tmp; -+ int i; ++ uint32 index; + -+ 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); ++ cache->hash_key = hash_key; ++ index = hash_key % AVC_HASH_NUM_SLOTS; + -+ 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; -+ } -+ } ++ /* ++ * reclaim avc, if needed ++ */ ++ sepgsql_avc_reclaim(); + -+ 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); ++ avc_slot[index] = lcons(cache, avc_slot[index]); + -+ avd->ssid = ssid; -+ avd->tsid = tsid; -+ avd->tclass = tclass; ++ avc_datum_count++; ++} + -+ 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); ++/* ++ * sepgsql_avc_compute ++ * ++ * This function compute an avc cache for the given subject/target ++ * context and object class, based on results of inquiries to SELinux. ++ */ ++static void ++sepgsql_avc_compute(const security_context_t scon, ++ const security_context_t tcon, ++ security_class_t tclass, ++ avc_datum *cache) ++{ ++ security_class_t e_tclass; ++ security_context_t svcon, tvcon, ncon; ++ struct av_decision avd; ++ ++ svcon = (!security_check_context_raw(scon) ++ ? scon : sepgsqlGetUnlabeledContext()); ++ tvcon = (!security_check_context_raw(tcon) ++ ? tcon : 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 a new avc entry" ++ " scon=%s tcon=%s tclass=%u", svcon, tvcon, tclass))); ++ if (security_compute_create_raw(svcon, tvcon, e_tclass, &ncon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not compute a new avc entry" ++ " scon=%s tcon=%s tclass=%u", svcon, tvcon, tclass))); ++ ++ cache->allowed = trans_to_internal_perms(e_tclass, avd.allowed); ++ cache->decided = trans_to_internal_perms(e_tclass, avd.decided); ++ cache->auditallow = trans_to_internal_perms(e_tclass, avd.auditallow); ++ cache->auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny); ++ cache->hot_cache = true; ++ ++ LWLockRelease(SepgsqlAvcLock); + + 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); ++ cache->scon = pstrdup(scon); ++ cache->tcon = pstrdup(tcon); ++ cache->ncon = pstrdup(ncon); ++ cache->tclass = tclass; + } + PG_CATCH(); + { @@ -1224,566 +4775,1272 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/core.c sepgsql/src/backend/securi + 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) ++/* ++ * avc_lookup_entry ++ * avc_lookup_entry_sid ++ * ++ * These function lookup the required avc_datum on AVC. ++ * It returns avc_datum object which has required tag. ++ * If not found, NULL will be returned. ++ */ ++static avc_datum * ++avc_lookup_entry(const security_context_t scon, ++ const security_context_t tcon, ++ security_class_t tclass, uint32 hash) +{ -+ /* we have to hold LW_SHARED lock at least */ -+ uint32 denied, audited, mask; -+ char *context; -+ int ofs = 0; ++ uint32 index = hash % AVC_HASH_NUM_SLOTS; ++ avc_datum *cache; ++ ListCell *l; + -+ denied = perms & ~avd->allowed; -+ audited = denied ? (denied & avd->auditdeny) : (perms & avd->auditallow); -+ if (!audited) -+ return false; ++ foreach (l, avc_slot[index]) ++ { ++ cache = lfirst(l); + -+ 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)); ++ if (cache->hash_key == hash ++ && cache->tclass == tclass ++ && strcmp(cache->scon, scon) == 0 ++ && strcmp(cache->tcon, tcon) == 0) ++ { ++ cache->hot_cache = true; ++ return cache; + } + } -+ 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) ++static avc_datum * ++avc_lookup_entry_sid(const security_context_t scon, ++ Oid tsid, security_class_t tclass, uint32 hash) +{ -+ /* we have to hold LW_EXCLUSIVE lock */ -+ struct avc_datum *avd; -+ int hashkey; ++ uint32 index = hash % AVC_HASH_NUM_SLOTS; ++ avc_datum *cache; ++ ListCell *l; + -+ avd = sepgsql_avc_lookup(tmp->ssid, tmp->tsid, tmp->tclass, tmp->decided); -+ if (avd) -+ return; ++ foreach (l, avc_slot[index]) ++ { ++ cache = lfirst(l); + -+ 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; ++ if (cache->hash_key == hash ++ && cache->tclass == tclass ++ && cache->tsid == tsid ++ && strcmp(cache->scon, scon) == 0) ++ { ++ cache->hot_cache = true; ++ return cache; + } + } -+ 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; ++ return NULL; +} + +/* -+ * SE-PostgreSQL core functions ++ * avc_audit_common + * -+ * 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. ++ * This function makes an audit message on the given Cstring buffer, ++ * based on the given av_decision (which is the result of permission ++ * checks). ++ */ ++static bool ++avc_audit_common(char *buffer, uint32 buflen, avc_datum *cache, ++ access_vector_t perms, 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, " } "); ++ ++ svcon = sepgsqlTranslateSecurityLabelOut(cache->scon); ++ tvcon = sepgsqlTranslateSecurityLabelOut(cache->tcon); ++ 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; ++} ++ ++/* ++ * sepgsqlAvcPermission ++ * sepgsqlAvcPermissionSid ++ * ++ * These functions make a dicision for the given action, and an audit ++ * record if necessary. When the required action is not allowed by ++ * the policy and "abort" is true, these functions aborts current ++ * transaction. Elsewhere, it returns the result simply. + * ++ * They tries to lookup an cached entry on uAVC. If it does not found ++ * on uAVC, it create a new entry and insert it for the future usage. ++ * In most cases, this feature enables to reduce the number of kernel ++ * invocation. ++ * ++ * The only difference between two API is we can use security id as ++ * second argument of sepgsqlAvcPermissionSid(). In most cases to ++ * invoke AVC functions, we have to evaluate permissions onto required ++ * tuple holding security id. It enables to reduce overhead to translate ++ * security id and text representation. + */ -+static Oid sepgsqlServerContext = InvalidOid; -+static Oid sepgsqlClientContext = InvalidOid; -+ -+Oid sepgsqlGetServerContext() ++static inline uint32 ++sepgsql_avc_hash(const security_context_t scon, const security_context_t tcon, ++ Oid security_id, security_class_t tclass) +{ -+ return sepgsqlServerContext; ++ uint32 hash = 0; ++ ++ hash ^= (scon ? DatumGetUInt32(hash_any((unsigned char *) scon, strlen(scon))) : 0); ++ hash ^= (tcon ? DatumGetUInt32(hash_any((unsigned char *) tcon, strlen(tcon))) : 0); ++ hash ^= DatumGetUInt32(hash_any((unsigned char *) &security_id, sizeof(Oid))); ++ hash ^= (tclass << 2); ++ ++ return hash; +} + -+Oid sepgsqlGetClientContext() ++static bool ++avc_permission_common(avc_datum *cache, access_vector_t perms, ++ const char *objname, bool abort) +{ -+ return sepgsqlClientContext; -+} ++ char audit_buffer[2048]; ++ access_vector_t denied; ++ bool audit; ++ bool rc = true; + -+void sepgsqlSetClientContext(Oid new_context) -+{ -+ sepgsqlClientContext = new_context; -+} ++ audit = avc_audit_common(audit_buffer, sizeof(audit_buffer), ++ cache, perms, objname); + -+Oid sepgsqlGetDatabaseContext() -+{ -+ HeapTuple tuple; -+ Oid datcon; -+ -+ if (IsBootstrapProcessingMode()) { -+ return sepgsql_avc_createcon(sepgsqlGetClientContext(), -+ sepgsqlGetServerContext(), -+ SECCLASS_DB_DATABASE); ++ 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; ++ } + } + -+ 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); ++ 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 datcon; ++ return rc; +} + -+char *sepgsqlGetDatabaseName() ++bool ++sepgsqlAvcPermission(const security_context_t scon, ++ const security_context_t tcon, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname, bool abort) +{ -+ Form_pg_database dat_form; -+ HeapTuple tuple; -+ char *datname; ++ avc_datum *cache; ++ uint32 hash; + -+ if (IsBootstrapProcessingMode()) -+ return NULL; ++ /* ++ * check avc invalidation ++ */ ++ if (avc_version != selinux_state->version) ++ sepgsql_avc_reset(); + -+ 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); ++ /* ++ * lookup avc entry ++ */ ++ hash = sepgsql_avc_hash(scon, tcon, InvalidOid, tclass); ++ cache = avc_lookup_entry(scon, tcon, tclass, hash); ++ if (!cache) ++ { ++ /* ++ * not found, make a new avc entry ++ */ ++ MemoryContext oldctx ++ = MemoryContextSwitchTo(AvcMemCtx); + -+ 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; ++ cache = palloc0(sizeof(avc_datum)); ++ sepgsql_avc_compute(scon, tcon, tclass, cache); ++ ++ sepgsql_avc_insert(cache, hash); ++ ++ MemoryContextSwitchTo(oldctx); + } + -+ /* 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()); ++ return avc_permission_common(cache, perms, objname, abort); +} + -+/* 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. ++bool ++sepgsqlAvcPermissionSid(const security_context_t scon, Oid tsid, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname, bool abort) ++{ ++ avc_datum *cache; ++ uint32 hash; ++ ++ /* ++ * check avc invalidation ++ */ ++ if (avc_version != selinux_state->version) ++ sepgsql_avc_reset(); ++ ++ /* ++ * lookup avc entry ++ */ ++ hash = sepgsql_avc_hash(scon, NULL, tsid, tclass); ++ cache = avc_lookup_entry_sid(scon, tsid, tclass, hash); ++ if (!cache) ++ { ++ /* ++ * not found, make a new avc entry ++ */ ++ MemoryContext oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ security_context_t tcon; ++ ++ cache = palloc0(sizeof(avc_datum)); ++ ++ tcon = pgaceLookupSecurityLabel(tsid); ++ sepgsql_avc_compute(scon, tcon, tclass, cache); ++ pfree(tcon); ++ ++ cache->tsid = tsid; ++ ++ sepgsql_avc_insert(cache, hash); ++ ++ MemoryContextSwitchTo(oldctx); ++ } ++ ++ return avc_permission_common(cache, perms, objname, abort); ++} ++ ++/* ++ * sepgsqlAvcCreateCon ++ * sepgsqlAvcCreateConSid ++ * ++ * These functions returns a security context or security id of newly ++ * created object based on the security policy. + */ -+static void sepgsqlMonitoringPolicyState_SIGHUP(int signum) ++security_context_t ++sepgsqlAvcCreateCon(const security_context_t scon, ++ const security_context_t tcon, security_class_t tclass) +{ -+ elog(NOTICE, "SELinux: userspace AVC reset"); ++ avc_datum *cache; ++ uint32 hash; ++ ++ /* ++ * check avc invalidation ++ */ ++ if (avc_version != selinux_state->version) ++ sepgsql_avc_reset(); ++ ++ /* ++ * lookup avc entry ++ */ ++ hash = sepgsql_avc_hash(scon, tcon, InvalidOid, tclass); ++ cache = avc_lookup_entry(scon, tcon, tclass, hash); ++ if (!cache) ++ { ++ /* ++ * not found, make a new avc entry ++ */ ++ MemoryContext oldctx ++ = MemoryContextSwitchTo(AvcMemCtx); ++ ++ cache = palloc0(sizeof(avc_datum)); ++ sepgsql_avc_compute(scon, tcon, tclass, cache); ++ ++ sepgsql_avc_insert(cache, hash); ++ ++ MemoryContextSwitchTo(oldctx); ++ } ++ return pstrdup(cache->ncon); ++} ++ ++Oid ++sepgsqlAvcCreateConSid(const security_context_t scon, Oid tsid, ++ security_class_t tclass) ++{ ++ avc_datum *cache; ++ uint32 hash; ++ ++ /* ++ * check avc invalidation ++ */ ++ if (avc_version != selinux_state->version) ++ sepgsql_avc_reset(); ++ ++ /* ++ * lookup avc entry ++ */ ++ hash = sepgsql_avc_hash(scon, NULL, tsid, tclass); ++ cache = avc_lookup_entry_sid(scon, tsid, tclass, hash); ++ if (!cache) ++ { ++ /* ++ * not found, make a new avc entry ++ */ ++ MemoryContext oldctx ++ = MemoryContextSwitchTo(AvcMemCtx); ++ security_context_t tcon; ++ ++ cache = palloc0(sizeof(avc_datum)); ++ ++ tcon = pgaceLookupSecurityLabel(tsid); ++ sepgsql_avc_compute(scon, tcon, tclass, cache); ++ pfree(tcon); ++ ++ cache->tsid = tsid; ++ ++ sepgsql_avc_insert(cache, hash); ++ ++ MemoryContextSwitchTo(oldctx); ++ } ++ if (!cache->nsid) ++ { ++ Oid nsid = pgaceSecurityLabelToSid(cache->ncon); ++ ++ cache->nsid = nsid; ++ } ++ return cache->nsid; ++} ++ ++/* ++ * sepgsqlAvcInit ++ * ++ * Initialize local memory context and assign shared memory segment ++ */ ++void ++sepgsqlAvcInit(void) ++{ ++ bool found; ++ ++ /* ++ * local memory ++ */ ++ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, ++ "SE-PostgreSQL userspace avc", ++ ALLOCSET_DEFAULT_MINSIZE, ++ ALLOCSET_DEFAULT_INITSIZE, ++ ALLOCSET_DEFAULT_MAXSIZE); ++ ++ /* ++ * shared memory ++ */ ++ 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); ++ } ++ ++ /* ++ * reset local avc ++ */ + sepgsql_avc_reset(); +} + -+static int sepgsqlMonitoringPolicyState() ++/* ++ * 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) +{ -+ char buffer[2048]; ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_INFO), ++ errmsg("SELinux: reset userspace avc"))); ++ sepgsql_avc_reset(); ++} ++ ++static int ++sepgsqlStateMonitorMain() ++{ ++ char buffer[2048]; + struct sockaddr_nl addr; -+ socklen_t addrlen; ++ socklen_t addrlen; + struct nlmsghdr *nlh; -+ int i, rc, nl_sockfd; ++ int rc, nl_sockfd; + -+ /* close listen port */ -+ for (i=3; !close(i); i++); ++ /* ++ * map shared memory segment ++ */ ++ sepgsqlAvcInit(); + -+ /* map shared memory segment */ -+ sepgsql_avc_init(); -+ -+ /* setup the signal handler */ ++ /* ++ * 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(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); + -+ /* open netlink socket */ ++ 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) { -+ elog(NOTICE, "SELinux: could not open netlink socket"); ++ 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))) { -+ elog(NOTICE, "SELinux: could not bint netlink socket"); ++ 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 (true) { ++ /* ++ * waiting loop ++ */ ++ while (sepgsqlStateMonitorAlive) ++ { + addrlen = sizeof(addr); + rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0, -+ (struct sockaddr *)&addr, &addrlen); -+ if (rc < 0) { ++ (struct sockaddr *) &addr, &addrlen); ++ if (rc < 0) ++ { + if (errno == EINTR) + continue; -+ elog(NOTICE, "SELinux: netlink recvfrom() errno=%d (%s)", -+ errno, strerror(errno)); ++ ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: error on netlink recvfrom(): %s", ++ strerror(errno)))); + return 1; + } + -+ if (addrlen != sizeof(addr)) { -+ elog(NOTICE, "SELinux: netlink address truncated (len=%d)", addrlen); ++ if (addrlen != sizeof(addr)) ++ { ++ ereport(NOTICE, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("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); ++ 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) { -+ elog(NOTICE, "SELinux: netlink received EOF on socket"); ++ 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) { -+ elog(NOTICE, "SELinux: netlink incomplete netlink message"); ++ 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 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; ++ 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; +} + -+static pid_t MonitoringPolicyStatePid = -1; -+ -+int sepgsqlInitializePostmaster() ++pid_t ++sepgsqlStartupWorkerProcess(void) +{ -+ MonitoringPolicyStatePid = fork(); -+ if (MonitoringPolicyStatePid == 0) { -+ exit(sepgsqlMonitoringPolicyState()); -+ } else if (MonitoringPolicyStatePid < 0) { -+ elog(NOTICE, "SELinux: could not create a policy state monitoring process."); -+ return false; ++ pid_t chld; ++ ++ chld = fork(); ++ if (chld == 0) ++ { ++ ClosePostmasterPorts(false); ++ ++ on_exit_reset(); ++ ++ exit(sepgsqlStateMonitorMain()); + } -+ return true; ++ 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 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,638 @@ ++ ++/* ++ * 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/syscache.h" ++#include ++ ++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; +} + -+void sepgsqlFinalizePostmaster() ++const security_context_t ++sepgsqlGetClientContext(void) +{ -+ int status; ++ Assert(clientContext != NULL); ++ return clientContext; ++} + -+ if (!sepgsqlIsEnabled()) -+ return; ++const security_context_t ++sepgsqlGetDatabaseContext(void) ++{ ++ security_context_t dcontext; + -+ if (MonitoringPolicyStatePid > 0) { -+ if (kill(MonitoringPolicyStatePid, SIGTERM) < 0) { -+ elog(NOTICE, "SELinux: could not kill(%u, SIGTERM), (%s)", -+ MonitoringPolicyStatePid, strerror(errno)); -+ return; ++ if (IsBootstrapProcessingMode()) ++ { ++ security_context_t tmp; ++ ++ if (security_compute_create_raw(sepgsqlGetClientContext(), ++ sepgsqlGetClientContext(), ++ SECCLASS_DB_DATABASE, &tmp) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get database context"))); ++ PG_TRY(); ++ { ++ dcontext = pstrdup(tmp); ++ } ++ PG_CATCH(); ++ { ++ freecon(tmp); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(tmp); ++ } ++ else ++ { ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId); ++ ++ dcontext = pgaceLookupSecurityLabel(HeapTupleGetSecurity(tuple)); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ return dcontext; ++} ++ ++Oid ++sepgsqlGetDatabaseSecurityId(void) ++{ ++ Oid security_id; ++ ++ if (IsBootstrapProcessingMode()) ++ { ++ security_context_t dcontext ++ = sepgsqlGetDatabaseContext(); ++ ++ security_id = 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); ++ ++ security_id = HeapTupleGetSecurity(tuple); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ return security_id; ++} ++ ++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; ++ ++ 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))); + } -+ waitpid(MonitoringPolicyStatePid, &status, 0); + } +} + -+bool sepgsqlIsEnabled() ++/* ++ * 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) +{ -+ static int enabled = -1; ++ char *dbname; ++ ++ sepgsqlAvcInit(); ++ ++ initContexts(); ++ ++ /* ++ * 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); ++ } ++ ++ sepgsqlAvcPermission(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__ACCESS, ++ dbname, ++ true); ++} ++ ++/* ++ * 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 ++ */ ++ ++bool ++sepgsqlIsEnabled(void) ++{ ++ static int enabled = -1; + + if (enabled < 0) -+ enabled = is_selinux_enabled(); ++ { ++ if (strcmp(sepostgresql_mode, "disabled") == 0) ++ enabled = 0; ++ else ++ { ++ int rc = is_selinux_enabled(); ++ ++ if (strcmp(sepostgresql_mode, "default") == 0) ++ enabled = rc; ++ else if (strcmp(sepostgresql_mode, "permissice") == 0 ++ || strcmp(sepostgresql_mode, "enforcing") == 0) ++ { ++ if (rc == 0) ++ ereport(FATAL, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: disabled in kernel, but sepostgresql = %s", ++ sepostgresql_mode))); ++ enabled = 1; ++ } ++ else ++ { ++ ereport(FATAL, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: unknown state sepostgresql = %s", ++ sepostgresql_mode))); ++ } ++ } ++ } + + 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 ++ * sepgsql_getcon(void) ++ * ++ * It returns security context of client ++ */ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ security_context_t context; ++ Datum labelTxt; ++ ++ if (!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 (!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 (!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); ++} +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 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,882 @@ ++/* ++ * 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 + * -+ * Copyright 2007 KaiGai Kohei + */ +#include "postgres.h" + @@ -1800,19 +6057,728 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/secur +#include "security/sepgsql.h" +#include "utils/fmgroids.h" +#include "utils/syscache.h" ++#include "utils/tqual.h" +#include +#include +#include +#include + -+static HeapTuple __getHeapTupleFromItemPointer(Relation rel, ItemPointer tid) ++/******************************************************************************* ++ * Extended SQL statement hooks ++ *******************************************************************************/ ++DefElem * ++sepgsqlGramSecurityItem(char *defname, char *value) +{ -+ /* obtain an old tuple */ ++ DefElem *n = NULL; ++ ++ if (strcmp(defname, "security_context") == 0) ++ n = makeDefElem(pstrdup(defname), (Node *) makeString(value)); ++ return n; ++} ++ ++bool ++sepgsqlIsGramSecurityItem(DefElem *defel) ++{ ++ Assert(IsA(defel, DefElem)); ++ ++ if (defel->defname && strcmp(defel->defname, "security_context") == 0) ++ return true; ++ return false; ++} ++ ++static void ++putExplicitContext(HeapTuple tuple, DefElem *defel) ++{ ++ if (defel) ++ { ++ Oid security_id = pgaceSecurityLabelToSid(strVal(defel->arg)); ++ ++ HeapTupleSetSecurity(tuple, security_id); ++ } ++} ++ ++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; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database %u", ++ MyDatabaseId); ++ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__GET_PARAM, ++ sepgsqlTupleName(DatabaseRelationId, tuple), ++ true); ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsqlSetDatabaseParam(const char *name, char *argstring) ++{ ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for database %u", ++ MyDatabaseId); ++ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__SET_PARAM, ++ sepgsqlTupleName(DatabaseRelationId, tuple), ++ true); ++ 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) ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_TABLE, ++ DB_TABLE__LOCK, ++ sepgsqlTupleName(RelationRelationId, tuple), ++ true); ++ ReleaseSysCache(tuple); ++} ++ ++/******************************************************************************* ++ * 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, bool with_perm_check) ++{ ++ MemoryContext oldctx; ++ HeapTuple tuple; ++ security_context_t procon, newcon; ++ access_vector_t 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); ++ ++ oldctx = MemoryContextSwitchTo(finfo->fn_mcxt); ++ /* ++ * check trusted procedure ++ */ ++ procon = pgaceLookupSecurityLabel(HeapTupleGetSecurity(tuple)); ++ ++ newcon = sepgsqlAvcCreateCon(sepgsqlGetClientContext(), ++ procon, ++ 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); ++ ++ pfree(procon); ++ ++ MemoryContextSwitchTo(oldctx); ++ ++ if (with_perm_check) ++ { ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_PROCEDURE, ++ perms, ++ sepgsqlTupleName(ProcedureRelationId, tuple), ++ true); ++ } ++ ++ 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; ++ ++ if (getfilecon_raw(filename, &filecon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get context of %s", filename))); ++ PG_TRY(); ++ { ++ sepgsqlAvcPermission(sepgsqlGetDatabaseContext(), ++ filecon, ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__LOAD_MODULE, ++ filename, ++ true); ++ } ++ PG_CATCH(); ++ { ++ freecon(filecon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(filecon); ++} ++ ++/******************************************************************************* ++ * Binary Large Object hooks ++ *******************************************************************************/ ++ ++void ++sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple) ++{ ++ sepgsqlSetDefaultContext(rel, tuple); ++ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__CREATE, ++ sepgsqlTupleName(RelationGetRelid(rel), tuple), ++ true); ++} ++ ++void ++sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem) ++{ ++ Oid security_id = HeapTupleGetSecurity(tuple); ++ List *okList = (List *) (*pgaceItem); ++ ListCell *l; ++ ++ foreach (l, okList) ++ { ++ if (security_id == lfirst_oid(l)) ++ return; /* already allowed */ ++ } ++ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ security_id, ++ SECCLASS_DB_BLOB, ++ DB_BLOB__DROP, ++ sepgsqlTupleName(RelationGetRelid(rel), tuple), ++ true); ++ ++ *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; ++ ++ if (end_pageno >= 0 && loForm->pageno > end_pageno) ++ break; ++ ++ security_id = HeapTupleGetSecurity(tuple); ++ ++ foreach (l, okList) ++ { ++ if (security_id == lfirst_oid(l)) ++ goto skip; ++ } ++ okList = lappend_oid(okList, security_id); ++ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ security_id, ++ SECCLASS_DB_BLOB, ++ perms, ++ sepgsqlTupleName(RelationGetRelid(rel), tuple), ++ true); ++ 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; ++ ++ if (fgetfilecon_raw(fdesc, &tcontext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get context of %s", filename))); ++ PG_TRY(); ++ { ++ sepgsqlAvcPermission(sepgsqlGetClientContext(), ++ tcontext, ++ SECCLASS_FILE, ++ FILE__READ, ++ filename, ++ true); ++ } ++ 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; ++ ++ if (fgetfilecon_raw(fdesc, &tcontext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get context of %s", filename))); ++ PG_TRY(); ++ { ++ sepgsqlAvcPermission(sepgsqlGetClientContext(), ++ tcontext, ++ SECCLASS_FILE, ++ FILE__WRITE, ++ filename, ++ true); ++ } ++ 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) ++{ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__GETATTR, ++ sepgsqlTupleName(RelationGetRelid(rel), tuple), ++ true); ++} ++ ++void ++sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup) ++{ ++ if (HeapTupleGetSecurity(newtup) == HeapTupleGetSecurity(oldtup)) ++ return; ++ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(oldtup), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__SETATTR | DB_BLOB__RELABELFROM, ++ sepgsqlTupleName(RelationGetRelid(rel), oldtup), ++ true); ++ ++ /* ++ * check db_blob:{setattr relabelto} ++ */ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(newtup), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__RELABELTO, ++ sepgsqlTupleName(RelationGetRelid(rel), newtup), ++ true); ++} ++ ++/******************************************************************************* ++ * ExecScan hooks ++ *******************************************************************************/ ++static bool abort_on_violated_tuple = false; ++ ++bool ++sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot) ++{ ++ HeapTuple tuple; ++ uint32 perms = scan->pgaceTuplePerms; ++ ++ if (perms == 0) ++ return true; ++ ++ tuple = ExecMaterializeSlot(slot); ++ ++ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, ++ abort_on_violated_tuple); ++} ++ ++/* ---------------------------------------------------------- ++ * special cases in foreign key constraint ++ * ---------------------------------------------------------- */ ++void ++sepgsqlPreparePlanCheck(Relation rel, Datum *pgace_saved) ++{ ++ /* store the current status */ ++ *pgace_saved = BoolGetDatum(abort_on_violated_tuple); ++ ++ abort_on_violated_tuple = true; ++} ++ ++void ++sepgsqlRestorePlanCheck(Relation rel, Datum pgace_saved) ++{ ++ abort_on_violated_tuple = DatumGetBool(pgace_saved); ++} ++ ++/******************************************************************************* ++ * security_label hooks ++ *******************************************************************************/ ++char * ++sepgsqlTranslateSecurityLabelIn(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(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; ++} ++ ++/* ++ * sepgsqlValidateSecurityLabel() checks whether the given context ++ * is valid for the current policy, or not. ++ * If not valid, it returns alternative context. ++ */ ++char * ++sepgsqlValidateSecurityLabel(char *context) ++{ ++ security_context_t unlabeled; ++ char *result; ++ ++ if (context != NULL) ++ { ++ if (security_check_context_raw((security_context_t) context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: %s is invalid security context", ++ context))); ++ return context; ++ } ++ ++ if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get unlabeled 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(HeapTupleGetSecurity(tuple)); ++ ++ tuple_context = sepgsqlAvcCreateCon(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; ++ HeapTuple oldtup; + + buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid)); + LockBuffer(buffer, BUFFER_LOCK_SHARE); @@ -1820,7 +6786,7 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/secur + dp = (PageHeader) BufferGetPage(buffer); + lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); + -+ Assert(ItemIdIsUsed(lp)); ++ Assert(ItemIdIsNormal(lp)); + + tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); + tuple.t_len = ItemIdGetLength(lp); @@ -1834,552 +6800,42 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/secur + return oldtup; +} + -+/******************************************************************************* -+ * Extended SQL statement hooks -+ *******************************************************************************/ -+DefElem *sepgsqlGramSecurityItem(char *defname, char *value) ++static bool ++isTrustedRelation(Relation rel, bool is_internal) +{ -+ 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)) ++ if (!is_internal) + 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; ++ switch (RelationGetRelid(rel)) ++ { ++ case LargeObjectRelationId: ++ case SecurityRelationId: ++ return true; + } + return false; +} + -+bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, -+ bool is_internal, bool with_returning) ++bool ++sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, ++ bool is_internal, bool with_returning) +{ -+ uint32 perms; ++ 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)) ++ /* ++ * default context for no explicit labeled tuple ++ */ ++ if (HeapTupleGetSecurity(tuple) == InvalidOid) ++ sepgsqlSetDefaultContext(rel, tuple); ++ else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: not allowed to insert into " ++ "pg_largeobject.security_context"))); ++ ++ if (isTrustedRelation(rel, is_internal)) + return true; + + perms = SEPGSQL_PERMS_INSERT; @@ -2389,78 +6845,94 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/secur + return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, is_internal); +} + -+bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, -+ bool is_internal, bool with_returning) ++bool ++sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, ++ bool is_internal, bool with_returning) +{ -+ HeapTuple oldtup; -+ uint32 perms; -+ bool rc = true; ++ HeapTuple oldtup; ++ uint32 perms; ++ bool rc = true; + -+ oldtup = __getHeapTupleFromItemPointer(rel, otid); ++ oldtup = getHeapTupleFromItemPointer(rel, otid); + -+ if (HeapTupleGetSecurity(newtup) == InvalidOid) { -+ /* keep old context for no explicit labeled tuple */ -+ HeapTupleSetSecurity(newtup, HeapTupleGetSecurity(oldtup)); ++ if (HeapTupleGetSecurity(newtup) == InvalidOid) ++ { ++ Oid security_id = HeapTupleGetSecurity(oldtup); ++ ++ HeapTupleSetSecurity(newtup, security_id); + } ++ else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: not allowed to update " ++ "pg_largeobject.security_context"))); + -+ if (is_internal && __TrustedRelationForInternal(rel)) -+ goto out; ++ if (isTrustedRelation(rel, is_internal)) ++ return true; + -+ if (is_internal) { ++ if (is_internal) ++ { + perms = SEPGSQL_PERMS_UPDATE; + if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup)) + perms |= SEPGSQL_PERMS_RELABELFROM; ++ else if (with_returning) ++ perms |= SEPGSQL_PERMS_SELECT; + rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal); + if (!rc) + goto out; + } + -+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup)) { ++ 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: ++ out: + heap_freetuple(oldtup); + return rc; +} + -+bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, -+ bool is_internal, bool with_returning) ++bool ++sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, ++ bool is_internal, bool with_returning) +{ -+ HeapTuple oldtup; -+ uint32 perms; -+ bool rc = true; ++ HeapTuple oldtup; ++ uint32 perms = SEPGSQL_PERMS_DELETE; ++ bool rc; + -+ if (is_internal) { -+ if (__TrustedRelationForInternal(rel)) -+ return true; ++ if (!is_internal) ++ return true; ++ ++ if (isTrustedRelation(rel, is_internal)) ++ return true; ++ ++ oldtup = getHeapTupleFromItemPointer(rel, otid); ++ if (with_returning) ++ perms |= SEPGSQL_PERMS_SELECT; ++ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal); ++ heap_freetuple(oldtup); + -+ 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 @@ +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 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,597 @@ ++ +/* -+ * src/backend/security/sepgsqlPerms.c -+ * SE-PostgreSQL permission checking functions ++ * 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 + * -+ * Copyright (c) 2007 KaiGai Kohei + */ +#include "postgres.h" + -+#include "access/genam.h" +#include "access/heapam.h" -+#include "catalog/catalog.h" ++#include "access/genam.h" +#include "catalog/indexing.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_authid.h" @@ -2474,358 +6946,281 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/permissions.c sepgsql/src/backend +#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" ++#include "utils/tqual.h" ++ ++static access_vector_t ++sepgsql_perms_to_common_perms(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 ++sepgsql_perms_to_tuple_perms(uint32 perms) ++{ ++ 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; ++} ++ ++const char * ++sepgsqlTupleName(Oid relid, HeapTuple tuple) ++{ ++ static char buffer[NAMEDATALEN * 3]; ++ ++ switch (relid) ++ { ++ case AttributeRelationId: ++ { ++ Form_pg_attribute attForm ++ = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ if (!IsBootstrapProcessingMode()) ++ { ++ HeapTuple exttup = SearchSysCache(RELOID, ++ ObjectIdGetDatum ++ (attForm->attrelid), ++ 0, 0, 0); ++ ++ if (HeapTupleIsValid(exttup)) ++ { ++ snprintf(buffer, sizeof(buffer), "%s.%s", ++ NameStr(((Form_pg_class) GETSTRUCT(exttup))->relname), ++ NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname)); ++ ReleaseSysCache(exttup); ++ break; ++ } ++ } ++ snprintf(buffer, sizeof(buffer), "%s", ++ NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname)); ++ break; ++ } ++ case AuthIdRelationId: ++ snprintf(buffer, sizeof(buffer), "%s", ++ NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname)); ++ break; ++ ++ case RelationRelationId: ++ snprintf(buffer, sizeof(buffer), "%s", ++ NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)); ++ break; ++ ++ case DatabaseRelationId: ++ snprintf(buffer, sizeof(buffer), "%s", ++ NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname)); ++ break; ++ ++ case LargeObjectRelationId: ++ snprintf(buffer, sizeof(buffer), "loid:%u", ++ ((Form_pg_largeobject) GETSTRUCT(tuple))->loid); ++ break; ++ ++ case ProcedureRelationId: ++ snprintf(buffer, sizeof(buffer), "%s", ++ NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname)); ++ break; ++ ++ case TriggerRelationId: ++ snprintf(buffer, sizeof(buffer), "%s", ++ NameStr(((Form_pg_trigger) GETSTRUCT(tuple))->tgname)); ++ break; ++ ++ case TypeRelationId: ++ snprintf(buffer, sizeof(buffer), "pg_type::%s", ++ NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname)); ++ break; ++ default: ++ /* ++ * this tuple has no name ++ */ ++ return NULL; ++ } ++ return buffer; ++} + +/* -+ * 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. ++ * 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 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) ++static void ++checkTuplePermsAttribute(HeapTuple tuple, HeapTuple oldtup, ++ access_vector_t *p_perms, ++ security_class_t *p_tclass) +{ -+ switch (relid) { -+ case AttributeRelationId: { -+ Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple); -+ HeapTuple reltup; ++ Form_pg_attribute attForm, oldForm; ++ 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; ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ switch (attForm->attrelid) ++ { ++ case TypeRelationId: ++ case ProcedureRelationId: ++ case AttributeRelationId: ++ case RelationRelationId: ++ /* ++ * those are pure relation ++ */ ++ break; ++ default: ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(attForm->attrelid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", ++ attForm->attrelid); ++ if (RELKIND_RELATION != ++ ((Form_pg_class) GETSTRUCT(reltup))->relkind) ++ { ++ *p_tclass = SECCLASS_DB_TUPLE; ++ *p_perms = sepgsql_perms_to_tuple_perms(*p_perms); ++ ReleaseSysCache(reltup); ++ return; ++ } ++ ReleaseSysCache(reltup); ++ 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); ++ *p_perms = sepgsql_perms_to_common_perms(*p_perms); ++ if (HeapTupleIsValid(oldtup)) ++ { ++ oldForm = (Form_pg_attribute) GETSTRUCT(oldtup); + -+ if (oldForm->attisdropped != true && attrForm->attisdropped == true) ++ if (oldForm->attisdropped != true && attForm->attisdropped == true) + *p_perms |= DB_COLUMN__DROP; + } +} + -+static void __check_pg_largeobject(HeapTuple tuple, HeapTuple oldtup, -+ uint32 *p_perms, uint16 *p_tclass) ++static void ++checkTuplePermsLargeObject(HeapTuple tuple, HeapTuple oldtup, ++ access_vector_t *p_perms, ++ security_class_t *p_tclass) +{ -+ Form_pg_largeobject loForm = (Form_pg_largeobject) GETSTRUCT(tuple); -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc sd; -+ HeapTuple exttup; -+ uint32 perms = 0; ++ access_vector_t perms; + -+ 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); ++ /* ++ * 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. ++ * ++ * 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}. ++ */ ++ perms = sepgsql_perms_to_common_perms(*p_perms); ++ perms |= (*p_perms & SEPGSQL_PERMS_INSERT ? DB_BLOB__WRITE : 0); ++ perms |= (*p_perms & SEPGSQL_PERMS_DELETE ? DB_BLOB__WRITE : 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) ++static void ++checkTuplePermsProcedure(HeapTuple tuple, HeapTuple oldtup, ++ access_vector_t *p_perms, ++ security_class_t *p_tclass) +{ -+ uint32 perms = __sepgsql_perms_to_common_perms(*p_perms); ++ access_vector_t 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; ++ 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) { ++ newbin = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull); ++ if (!isnull) ++ { ++ if (perms & DB_PROCEDURE__CREATE) ++ { + verify = true; -+ } else if (HeapTupleIsValid(oldtup)) { ++ } ++ else if (HeapTupleIsValid(oldtup)) ++ { + oldbin = SysCacheGetAttr(PROCOID, oldtup, + Anum_pg_proc_probin, &isnull); -+ if (isnull || DatumGetBool(DirectFunctionCall2(textne, oldbin, newbin))) ++ if (isnull || ++ DatumGetBool(DirectFunctionCall2(textne, oldbin, newbin))) + verify = true; + } + -+ if (verify) { -+ char *filename; -+ security_context_t filecon; -+ Datum filesid; ++ if (verify) ++ { ++ char *file_name; ++ security_context_t file_context; + -+ /* <-- database:module_install --> */ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ sepgsqlGetDatabaseContext(), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__INSTALL_MODULE, -+ NULL); ++ /* ++ * <-- database:module_install --> ++ */ ++ sepgsqlAvcPermission(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__INSTALL_MODULE, ++ NULL, true); + -+ /* <-- 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); ++ /* ++ * <-- database:module_install --> ++ */ ++ file_name = DatumGetCString(DirectFunctionCall1(textout, newbin)); ++ file_name = expand_dynamic_library_name(file_name); ++ if (getfilecon_raw(file_name, &file_context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not get context of %s", ++ file_name))); + PG_TRY(); + { -+ filesid = DirectFunctionCall1(security_label_raw_in, -+ CStringGetDatum(filecon)); ++ sepgsqlAvcPermission(sepgsqlGetClientContext(), ++ file_context, ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__INSTALL_MODULE, ++ file_name, true); + } + PG_CATCH(); + { -+ freecon(filecon); ++ freecon(file_context); + PG_RE_THROW(); + } + PG_END_TRY(); -+ freecon(filecon); -+ -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ DatumGetObjectId(filesid), -+ SECCLASS_DB_DATABASE, -+ DB_DATABASE__INSTALL_MODULE, -+ filename); ++ freecon(file_context); + } + } + } @@ -2833,220 +7228,308 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/permissions.c sepgsql/src/backend + *p_tclass = SECCLASS_DB_PROCEDURE; +} + -+static void __check_pg_relation(HeapTuple tuple, HeapTuple oldtup, -+ uint32 *p_perms, uint16 *p_tclass) ++static void ++checkTuplePermsRelation(HeapTuple tuple, HeapTuple oldtup, ++ access_vector_t *p_perms, ++ security_class_t *p_tclass) +{ + Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); -+ if (classForm->relkind == RELKIND_RELATION) { ++ ++ if (classForm->relkind == RELKIND_RELATION) ++ { + *p_tclass = SECCLASS_DB_TABLE; -+ *p_perms = __sepgsql_perms_to_common_perms(*p_perms); -+ } else { ++ *p_perms = sepgsql_perms_to_common_perms(*p_perms); ++ } ++ else ++ { + *p_tclass = SECCLASS_DB_TUPLE; -+ *p_perms = __sepgsql_perms_to_tuple_perms(*p_perms); ++ *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) ++bool ++sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup, ++ uint32 perms, bool abort) +{ -+ uint16 tclass; -+ bool rc = true; ++ security_class_t 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; ++ switch (RelationGetRelid(rel)) ++ { ++ case DatabaseRelationId: /* pg_datbase */ ++ 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 RelationRelationId: /* pg_class */ ++ checkTuplePermsRelation(tuple, oldtup, &perms, &tclass); ++ break; + -+ case AttributeRelationId: /* pg_attribute */ -+ __check_pg_attribute(tuple, oldtup, &perms, &tclass); -+ break; ++ case AttributeRelationId: /* pg_attribute */ ++ checkTuplePermsAttribute(tuple, oldtup, &perms, &tclass); ++ break; + -+ case ProcedureRelationId: /* pg_proc */ -+ __check_pg_proc(tuple, oldtup, &perms, &tclass); -+ break; ++ case ProcedureRelationId: /* pg_proc */ ++ checkTuplePermsProcedure(tuple, oldtup, &perms, &tclass); ++ break; + -+ case LargeObjectRelationId: /* pg_largeobject */ -+ __check_pg_largeobject(tuple, oldtup, &perms, &tclass); -+ break; ++ case LargeObjectRelationId: /* pg_largeobject */ ++ checkTuplePermsLargeObject(tuple, oldtup, &perms, &tclass); ++ break; + -+ default: -+ perms = __sepgsql_perms_to_tuple_perms(perms); -+ tclass = SECCLASS_DB_TUPLE; -+ break; ++ default: ++ perms = sepgsql_perms_to_tuple_perms(perms); ++ tclass = SECCLASS_DB_TUPLE; ++ break; + } + -+ if (perms) { -+ NameData name; ++ if (perms) ++ { ++ const char *objname = sepgsqlTupleName(RelationGetRelid(rel), tuple); + -+ 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)); -+ } ++ rc = sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ tclass, perms, objname, abort); + } + return rc; +} + +/* -+ * MEMO: we cannot obtain system column from RECORD datatype. -+ * If those are necesasry, they should be separately delivered. ++ * 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. + */ -+Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS) ++ ++static void ++setDefaultContextDatabase(Relation rel, HeapTuple tuple) +{ -+ 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; ++ security_context_t ncontext; ++ Oid security_id; + -+ 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)); ++ ncontext = sepgsqlAvcCreateCon(sepgsqlGetClientContext(), ++ sepgsqlGetClientContext(), ++ SECCLASS_DB_DATABASE); ++ security_id = pgaceSecurityLabelToSid(ncontext); ++ HeapTupleSetSecurity(tuple, security_id); +} + -+Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS) ++static void ++setDefaultContextRelation(Relation rel, HeapTuple tuple) +{ -+ 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; ++ Oid dbsid, tblsid; + -+ 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)); ++ dbsid = sepgsqlGetDatabaseSecurityId(); ++ tblsid = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(), ++ dbsid, ++ SECCLASS_DB_TABLE); ++ HeapTupleSetSecurity(tuple, tblsid); +} + -+bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup, uint32 perms, bool abort) ++static void ++setDefaultContextAttribute(Relation rel, HeapTuple tuple) +{ -+ return __check_tuple_perms(RelationGetRelid(rel), -+ HeapTupleGetSecurity(tuple), -+ perms, -+ tuple, -+ oldtup, -+ abort); -+} ++ HeapTuple reltup; ++ Oid security_id; ++ Form_pg_class clsForm; ++ Form_pg_attribute attForm ++ = (Form_pg_attribute) GETSTRUCT(tuple); + -+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; -+ } ++ switch (attForm->attrelid) ++ { ++ case TypeRelationId: + case ProcedureRelationId: -+ tclass = SECCLASS_DB_PROCEDURE; -+ tcon = sepgsqlGetDatabaseContext(); -+ break; ++ case AttributeRelationId: ++ case RelationRelationId: ++ /* ++ * we cannot touch these relations at very early phase in bootstrap ++ */ ++ if (IsBootstrapProcessingMode()) ++ { ++ Oid security_id; + -+ 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; ++ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_TABLE); ++ HeapTupleSetSecurity(tuple, security_id); + break; + } + default: -+ tclass = SECCLASS_DB_TUPLE; -+ tcon = __lookupRelationForm(RelationGetRelid(rel), NULL); ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(attForm->attrelid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", ++ attForm->attrelid); ++ clsForm = (Form_pg_class) GETSTRUCT(reltup); ++ ++ security_id ++ = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(reltup), ++ (clsForm->relkind == RELKIND_RELATION ++ ? SECCLASS_DB_COLUMN ++ : SECCLASS_DB_TUPLE)); ++ HeapTupleSetSecurity(tuple, security_id); ++ ++ ReleaseSysCache(reltup); + break; + } -+ return sepgsql_avc_createcon(sepgsqlGetClientContext(), tcon, tclass); ++ return; +} -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-04-30 10:19:50.000000000 +0900 -@@ -0,0 +1,1617 @@ ++ ++static void ++setDefaultContextProcedure(Relation rel, HeapTuple tuple) ++{ ++ Oid security_id; ++ ++ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_PROCEDURE); ++ HeapTupleSetSecurity(tuple, security_id); ++} ++ ++static void ++setDefaultContextLargeObject(Relation rel, HeapTuple tuple) ++{ ++ /* ++ * NOTE: ++ * A new tuple to be inserted into pg_largeobject inheris ++ * security context of tuple with same large object id. ++ * We can scan it with SnapshotNow because lo_create invokes ++ * CommandCounterIncrement() just after create a new large ++ * object. ++ * ++ * If no page found, it means this action is to insert the ++ * first page, or user run INSERT INTO ... statement with ++ * multiple tuples with same loid. ++ * However, these newly inserted tuples are labeled by ++ * 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 security_id = 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) ++ { ++ security_id = HeapTupleGetSecurity(lotup); ++ if (security_id != InvalidOid) ++ break; ++ } ++ systable_endscan(scan); ++ ++ if (security_id == InvalidOid) ++ { ++ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseSecurityId(), ++ SECCLASS_DB_BLOB); ++ } ++ HeapTupleSetSecurity(tuple, security_id); ++} ++ ++void ++sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple) ++{ ++ security_context_t ncontext; ++ HeapTuple reltup; ++ Oid security_id; ++ ++ switch (RelationGetRelid(rel)) ++ { ++ case DatabaseRelationId: ++ setDefaultContextDatabase(rel, tuple); ++ return; ++ ++ case RelationRelationId: ++ { ++ Form_pg_class clsForm ++ = (Form_pg_class) GETSTRUCT(tuple); ++ ++ if (clsForm->relkind == RELKIND_RELATION) ++ { ++ setDefaultContextRelation(rel, tuple); ++ return; ++ } ++ } ++ break; ++ ++ case AttributeRelationId: ++ setDefaultContextAttribute(rel, tuple); ++ return; ++ ++ case ProcedureRelationId: ++ setDefaultContextProcedure(rel, tuple); ++ return; ++ ++ case LargeObjectRelationId: ++ setDefaultContextLargeObject(rel, tuple); ++ return; ++ ++ case TypeRelationId: ++ if (IsBootstrapProcessingMode()) ++ { ++ /* ++ * we cannot touch system cache in very early phase ++ */ ++ security_context_t tcontext ++ = sepgsqlAvcCreateCon(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_TABLE); ++ ncontext = sepgsqlAvcCreateCon(sepgsqlGetClientContext(), ++ tcontext, ++ SECCLASS_DB_TUPLE); ++ security_id = pgaceSecurityLabelToSid(ncontext); ++ HeapTupleSetSecurity(tuple, security_id); ++ ++ return; ++ } ++ break; ++ } ++ /* ++ * normal or user defined relation ++ */ ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(RelationGetRelid(rel)), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", ++ RelationGetRelid(rel)); ++ ++ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(reltup), ++ SECCLASS_DB_TUPLE); ++ HeapTupleSetSecurity(tuple, security_id); ++ ++ ReleaseSysCache(reltup); ++} +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 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,1461 @@ ++ +/* -+ * src/backend/security/sepgsqlProxy.c -+ * SE-PostgreSQL Query Proxy function to walk on query node tree -+ * and append tuple filter. ++ * src/backend/security/sepgsql/proxy.c ++ * proxy routines to pick up all appeared columns, functions, ... ++ * within given queries, and apply mandatory access controls. ++ * ++ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California + * -+ * Copyright KaiGai Kohei + */ +#include "postgres.h" + @@ -3054,6 +7537,7 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/secur +#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_database.h" @@ -3061,206 +7545,258 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/secur +#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 "executor/executor.h" ++#include "nodes/security.h" ++#include "optimizer/clauses.h" +#include "optimizer/plancat.h" -+#include "parser/parse_relation.h" -+#include "parser/parse_target.h" ++#include "optimizer/prep.h" ++#include "optimizer/tlist.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" ++#include "utils/tqual.h" + -+/* SE-PostgreSQL Evaluation Item */ -+#define T_SEvalItem (T_TIDBitmap + 1) /* must be unique identifier */ ++/* ++ * queryStack ++ * ++ * This structure represents a hierarchical relationshipt ++ * between subqueries. When a Var node has positive varlevelsup, ++ * it refers upper level Query structure using the chain of ++ * queryStack. ++ */ ++typedef struct queryStack ++{ ++ struct queryStack *parent; ++ Query *query; ++} queryStack; + -+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; ++/* ++ * sepgsqlWalkerContext ++ * ++ * This structure holds a context during analyzing a given query. ++ * selist is a list of SEvalItemXXX objects to enumerate appared ++ * tables, columns and functions. It is 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 ++{ ++ List *selist; /* List of SEvalItem */ + -+/* query stack definition for outer references */ -+typedef struct queryChain { -+ struct queryChain *parent; -+ Query *tail; -+} queryChain; ++ struct queryStack *qstack; + -+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; -+} ++ bool is_internal_use; ++} sepgsqlWalkerContext; + +/* 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 void proxyRteSubQuery(sepgsqlWalkerContext *swc, Query *query); + -+/* static */ -+static List *sepgsqlWalkExpr(List *selist, queryChain *qc, Node *n, int flags); -+#define WKFLAG_INTERNAL_USE (0x0001) ++static bool sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc); + -+/* ----------------------------------------------------------- -+ * addEvalXXXX -- add evaluation items into Query->SEvalItemList. -+ * Those are used for execution phase. -+ * ----------------------------------------------------------- */ -+static List *__addEvalPgClass(List *selist, Oid relid, bool inh, uint32 perms) ++static void execVerifyQuery(List *selist); ++ ++/* ++ * 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) +{ -+ SEvalItem *se; -+ ListCell *l; ++ SEvalItemRelation *ser; + -+ foreach (l, selist) { -+ se = (SEvalItem *) lfirst(l); -+ if (se->tclass == SECCLASS_DB_TABLE -+ && se->c.relid == relid -+ && se->c.inh == inh) { -+ se->perms |= perms; ++ ListCell *l; ++ ++ foreach(l, selist) ++ { ++ ser = (SEvalItemRelation *) lfirst(l); ++ if (IsA(ser, SEvalItemRelation) ++ && ser->relid == relid ++ && ser->inh == inh) ++ { ++ ser->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); ++ /* ++ * not found ++ */ ++ ser = makeNode(SEvalItemRelation); ++ ser->perms = perms; ++ ser->relid = relid; ++ ser->inh = inh; ++ ++ return lappend(selist, ser); +} + -+static List *addEvalPgClass(List *selist, RangeTblEntry *rte, uint32 perms) ++static List * ++addEvalRelationRTE(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); ++ 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__INSERT ? SEPGSQL_PERMS_INSERT : 0); ++ rte->pgaceTuplePerms |= ++ (perms & DB_TABLE__UPDATE ? SEPGSQL_PERMS_UPDATE : 0); ++ rte->pgaceTuplePerms |= ++ (perms & DB_TABLE__DELETE ? SEPGSQL_PERMS_DELETE : 0); + -+ /* for 'pg_largeobject' */ ++ /* ++ * for 'pg_largeobject' ++ */ + if (rte->relid == LargeObjectRelationId && (perms & DB_TABLE__DELETE)) -+ rte->requiredPerms |= SEPGSQL_PERMS_WRITE; ++ rte->pgaceTuplePerms |= SEPGSQL_PERMS_WRITE; + -+ return __addEvalPgClass(selist, rte->relid, rte->inh, perms); ++ return addEvalRelation(selist, rte->relid, rte->inh, perms); +} + -+static List *__addEvalPgAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 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) +{ -+ ListCell *l; -+ SEvalItem *se; ++ SEvalItemAttribute *sea; + -+ 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; ++ ListCell *l; ++ ++ foreach(l, selist) ++ { ++ sea = (SEvalItemAttribute *) lfirst(l); ++ if (IsA(sea, SEvalItemAttribute) ++ && sea->relid == relid ++ && sea->inh == inh ++ && sea->attno == attno) ++ { ++ sea->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; ++ /* ++ * not found ++ */ ++ sea = makeNode(SEvalItemAttribute); ++ sea->perms = perms; ++ sea->relid = relid; ++ sea->inh = inh; ++ sea->attno = attno; + -+ return lappend(selist, se); ++ return lappend(selist, sea); +} + -+static List *addEvalPgAttribute(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms) ++static List * ++addEvalAttributeRTE(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms) +{ -+ uint32 t_perms = 0; ++ uint32 t_perms = 0; + -+ /* for table:{ ... } permission */ -+ t_perms |= (perms & DB_COLUMN__USE ? DB_TABLE__USE : 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); ++ selist = addEvalRelationRTE(selist, rte, t_perms); + -+ /* for 'security_context' */ ++ /* ++ * for 'security_context' ++ */ + if (attno == SecurityAttributeNumber + && (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT))) -+ rte->requiredPerms |= SEPGSQL_PERMS_RELABELFROM; ++ rte->pgaceTuplePerms |= SEPGSQL_PERMS_RELABELFROM; + -+ /* for 'pg_largeobject' */ -+ if (rte->relid == LargeObjectRelationId) { ++ /* ++ * 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; ++ rte->pgaceTuplePerms |= SEPGSQL_PERMS_READ; ++ if ((perms & DB_COLUMN__UPDATE) && attno == Anum_pg_largeobject_data) ++ rte->pgaceTuplePerms |= SEPGSQL_PERMS_WRITE; + } + -+ return __addEvalPgAttribute(selist, rte->relid, rte->inh, attno, perms); ++ return addEvalAttribute(selist, rte->relid, rte->inh, attno, perms); +} + -+static List *addEvalPgProc(List *selist, Oid funcid, uint32 perms) ++/* ++ * addEvalPgProc ++ * ++ * This function adds a given procedure into selist, if it is not ++ * contained yet. ++ */ ++static List * ++addEvalPgProc(List *selist, Oid funcid, uint32 perms) +{ -+ ListCell *l; -+ SEvalItem *se; ++ SEvalItemProcedure *sep; + -+ foreach (l, selist) { -+ se = (SEvalItem *) lfirst(l); -+ if (se->tclass == SECCLASS_DB_PROCEDURE -+ && se->p.funcid == funcid) { -+ se->perms |= perms; ++ ListCell *l; ++ ++ foreach(l, selist) ++ { ++ sep = (SEvalItemProcedure *) lfirst(l); ++ if (IsA(sep, SEvalItemProcedure) ++ && sep->funcid == funcid) ++ { ++ sep->perms |= perms; + return selist; + } + } -+ se = makeNode(SEvalItem); -+ se->tclass = SECCLASS_DB_PROCEDURE; -+ se->perms = perms; -+ se->p.funcid = funcid; ++ /* ++ * not found ++ */ ++ sep = makeNode(SEvalItemProcedure); ++ sep->perms = perms; ++ sep->funcid = funcid; + -+ return lappend(selist, se); ++ return lappend(selist, sep); +} + -+static List *addEvalTriggerAccess(List *selist, Oid relid, bool is_inh, int cmdType) ++/* ++ * addEvalTriggerAccess ++ * ++ * 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 * ++addEvalTriggerAccess(List *selist, Oid relid, bool is_inh, int cmdType) +{ -+ Relation rel; ++ Relation rel; + SysScanDesc scan; + ScanKeyData skey; -+ HeapTuple tuple; -+ bool checked = false; ++ HeapTuple tuple; ++ bool checked = false; + -+ Assert(cmdType == CMD_INSERT || cmdType == CMD_UPDATE || cmdType == CMD_DELETE); ++ 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)); ++ BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + scan = systable_beginscan(rel, TriggerRelidNameIndexId, + true, SnapshotNow, 1, &skey); -+ while (HeapTupleIsValid((tuple = systable_getnext(scan)))) { ++ while (HeapTupleIsValid((tuple = systable_getnext(scan)))) ++ { + Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple); + + if (!trigForm->tgenabled) @@ -3271,30 +7807,38 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/secur + || (cmdType == CMD_DELETE && !TRIGGER_FOR_DELETE(trigForm->tgtype))) + continue; + -+ /* per STATEMENT trigger cannot refer whole of a tuple */ ++ /* ++ * 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)) ++ /* ++ * 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; ++ selist = addEvalPgProc(selist, trigForm->tgfoid, ++ DB_PROCEDURE__EXECUTE); ++ if (!checked) ++ { ++ HeapTuple reltup; + Form_pg_class classForm; -+ AttrNumber attnum; ++ AttrNumber attnum; + -+ reltup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); ++ 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) ++ selist = addEvalRelation(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); ++ selist = addEvalAttribute(selist, relid, false, attnum, ++ DB_COLUMN__SELECT); + } + ReleaseSysCache(reltup); + @@ -3304,1058 +7848,785 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/secur + systable_endscan(scan); + heap_close(rel, AccessShareLock); + -+ if (is_inh) { -+ List *child_list = find_inheritance_children(relid); -+ ListCell *l; ++ 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); ++ 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) ++/* ++ * sepgsqlExprWalker ++ * ++ * This function walks on the given node tree recursively, to pick up ++ * all appeared tables, columns and functions. Their identifiers are ++ * chained swc->selist, and evaluated later. ++ * ++ * walkVarHelper and walkOpExprHelper are used to simplify its ++ * implementation. If swx->is_internal_use is true, it add a "use" ++ * permission to be evaluate, or a "select" permission otherwise. ++ */ ++static void ++walkVarHelper(sepgsqlWalkerContext *swc, Var *var) +{ + RangeTblEntry *rte; -+ Query *query; -+ Node *n; ++ queryStack *qstack; ++ Query *query; ++ int lv; + + 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); ++ /* ++ * resolve external Var reference ++ */ ++ qstack = swc->qstack; ++ lv = var->varlevelsup; ++ while (lv > 0) ++ { ++ Assert(!!qstack->parent); ++ qstack = qstack->parent; ++ lv--; ++ } ++ query = qstack->query; ++ if (!query) ++ elog(ERROR, "SELinux: could not walk T_Var node in this context"); ++ ++ rte = rt_fetch(var->varno, query->rtable); + 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->rtekind == RTE_RELATION) ++ { ++ /* ++ * table:{select/use} and column:{select/use} + */ -+ if (rte->relid) { -+ Query *sqry = rte->subquery; -+ RangeTblEntry *srte; -+ TargetEntry *tle; -+ Var *svar; ++ swc->selist = addEvalAttributeRTE(swc->selist, rte, var->varattno, ++ swc->is_internal_use ++ ? DB_COLUMN__USE : DB_COLUMN__SELECT); + -+ 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; ++ else if (rte->rtekind == RTE_JOIN) ++ { ++ Node *node = list_nth(rte->joinaliasvars, ++ var->varattno - 1); ++ ++ sepgsqlExprWalker(node, swc); ++ } +} + -+static List *walkOpExprHelper(List *selist, Oid opid) ++static void ++walkOpExprHelper(sepgsqlWalkerContext *swc, Oid opid) +{ -+ HeapTuple tuple; ++ HeapTuple tuple; + Form_pg_operator oprform; + -+ tuple = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(opid), -+ 0, 0, 0); ++ 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 ++ swc->selist = addEvalPgProc(swc->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) ++static bool ++sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc) +{ + if (node == NULL) -+ return selist; ++ return false; + -+ 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 */ ++ switch (nodeTag(node)) + { -+ OpExpr *op = (OpExpr *) node; ++ case T_Var: ++ walkVarHelper(swc, (Var *) node); ++ break; + -+ selist = walkOpExprHelper(selist, op->opno); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) op->args, flags); -+ break; -+ } -+ case T_ScalarArrayOpExpr: { -+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; ++ case T_FuncExpr: ++ swc->selist = addEvalPgProc(swc->selist, ++ ((FuncExpr *) node)->funcid, ++ DB_PROCEDURE__EXECUTE); ++ break; + -+ 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; ++ case T_Aggref: ++ swc->selist = addEvalPgProc(swc->selist, ++ ((Aggref *) node)->aggfnoid, ++ DB_PROCEDURE__EXECUTE); ++ break; + -+ 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; ++ case T_OpExpr: ++ case T_DistinctExpr: /* typedef of OpExpr */ ++ case T_NullIfExpr: /* typedef of OpExpr */ ++ walkOpExprHelper(swc, ((OpExpr *) node)->opno); ++ break; + -+ 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; ++ case T_ScalarArrayOpExpr: ++ walkOpExprHelper(swc, ((ScalarArrayOpExpr *) node)->opno); ++ break; + -+ 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); ++ case T_SubLink: ++ { ++ SubLink *slink = (SubLink *) node; ++ ++ Assert(IsA(slink->subselect, Query)); ++ ++ proxyRteSubQuery(swc, (Query *) slink->subselect); + break; + } -+ } -+ break; -+ } -+ case T_CoerceToDomain: { -+ selist = sepgsqlWalkExpr(selist, qc, -+ (Node *) ((CoerceToDomain *) node)->arg, flags); -+ break; -+ } -+ case T_CaseExpr: { -+ CaseExpr *ce = (CaseExpr *) node; ++ case T_ArrayCoerceExpr: ++ { ++ ArrayCoerceExpr *ace = (ArrayCoerceExpr *) 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; ++ if (ace->elemfuncid != InvalidOid) ++ swc->selist = addEvalPgProc(swc->selist, ++ ace->elemfuncid, ++ DB_PROCEDURE__EXECUTE); ++ break; ++ } ++ case T_RowCompareExpr: ++ { ++ RowCompareExpr *rce = (RowCompareExpr *) node; ++ ListCell *l; + -+ 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; ++ foreach(l, rce->opnos) walkOpExprHelper(swc, lfirst_oid(l)); ++ break; ++ } ++ case T_SortClause: ++ case T_GroupClause: ++ { ++ SortClause *sc = (SortClause *) node; ++ Query *q = swc->qstack->query; ++ TargetEntry *tle ++ = get_sortgroupref_tle(sc->tleSortGroupRef, q->targetList); + -+ if (ace->elemfuncid != InvalidOid) -+ selist = addEvalPgProc(selist, ace->elemfuncid, DB_PROCEDURE__EXECUTE); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ace->arg, flags); ++ Assert(IsA(tle, TargetEntry)); + -+ 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; ++ walkOpExprHelper(swc, sc->sortop); ++ sepgsqlExprWalker((Node *)tle->expr, swc); ++ } ++ return false; /* expression_tree_walker does not suppor them */ + -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) xe->named_args, flags); -+ selist = sepgsqlWalkExpr(selist, qc, (Node *) xe->args, flags); -+ break; ++ default: ++ 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; ++ return expression_tree_walker(node, sepgsqlExprWalker, (void *) swc); +} + -+/* ******************************************************************************* -+ * 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 bool ++sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc, bool is_internal_use) ++{ ++ bool saved_is_internal_use = swc->is_internal_use; ++ bool rc; + -+static Oid fnoid_sepgsql_tuple_perm = F_SEPGSQL_TUPLE_PERMS; ++ swc->is_internal_use = is_internal_use; ++ rc = sepgsqlExprWalker(node, swc); ++ swc->is_internal_use = saved_is_internal_use; ++ ++ return rc; ++} + +/* -+ * 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. ++ * checkSelectFromExpr ++ * ++ * It appends SEvalItem of any relation within FROM clause into ++ * selist recursively. ++ * + */ -+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) ++static void ++checkSelectFromExpr(sepgsqlWalkerContext *swc, Query *query, Node *node) +{ + if (node == NULL) -+ return selist; ++ return; + -+ if (IsA(node, RangeTblRef)) { -+ RangeTblRef *rtr = (RangeTblRef *) node; -+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); ++ switch (nodeTag(node)) ++ { ++ case T_RangeTblRef: ++ { ++ RangeTblRef *rtr = (RangeTblRef *) node; + -+ 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); ++ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); + -+ selist = addEvalPgClass(selist, srte, DB_TABLE__SELECT); ++ if (rte->rtekind == RTE_RELATION) ++ swc->selist = ++ addEvalRelationRTE(swc->selist, rte, DB_TABLE__SELECT); ++ break; ++ } ++ case T_JoinExpr: ++ { ++ JoinExpr *j = (JoinExpr *) node; ++ ++ checkSelectFromExpr(swc, query, j->larg); ++ checkSelectFromExpr(swc, query, j->rarg); ++ break; ++ } ++ case T_FromExpr: ++ { ++ FromExpr *f = (FromExpr *) node; ++ ListCell *l; ++ ++ foreach(l, f->fromlist) ++ checkSelectFromExpr(swc, query, lfirst(l)); ++ break; + } -+ 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)); ++ elog(ERROR, "SELinux: unexpected node type (%d) on fromlist", ++ nodeTag(node)); + } -+ return selist; +} + -+static List *proxyRteSubQuery(List *selist, queryChain *qc, Query *query) ++/* ++ * proxyJoinTree ++ * ++ * It appends SEvalItem of WHERE/JOIN ON clause, nodes in VALUE ++ * clause or function which returns a relation, or invokes ++ * proxyRteSubQuery recursively. ++ */ ++static void ++proxyJoinTree(sepgsqlWalkerContext *swc, Node *node) +{ -+ CmdType cmdType = query->commandType; ++ Query *query = swc->qstack->query; ++ ++ if (node == NULL) ++ return; ++ ++ switch (nodeTag(node)) ++ { ++ case T_RangeTblRef: ++ { ++ RangeTblRef *rtr = (RangeTblRef *) node; ++ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); ++ ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ switch (rte->rtekind) ++ { ++ case RTE_SUBQUERY: ++ proxyRteSubQuery(swc, rte->subquery); ++ break; ++ ++ case RTE_FUNCTION: ++ sepgsqlExprWalkerFlags(rte->funcexpr, swc, false); ++ break; ++ ++ case RTE_VALUES: ++ sepgsqlExprWalkerFlags((Node *) rte->values_lists, swc, ++ false); ++ break; ++ ++ default: ++ break; ++ } ++ break; ++ } ++ case T_FromExpr: ++ { ++ FromExpr *f = (FromExpr *) node; ++ ListCell *l; ++ ++ sepgsqlExprWalkerFlags(f->quals, swc, true); ++ foreach(l, f->fromlist) proxyJoinTree(swc, lfirst(l)); ++ break; ++ } ++ case T_JoinExpr: ++ { ++ JoinExpr *j = (JoinExpr *) node; ++ ++ sepgsqlExprWalkerFlags(j->quals, swc, true); ++ proxyJoinTree(swc, j->larg); ++ proxyJoinTree(swc, j->rarg); ++ ++ break; ++ } ++ default: ++ elog(ERROR, "SELinux: unexpected node type (%d) at jointree", ++ nodeTag(node)); ++ break; ++ } ++} ++ ++/* ++ * proxySetOperations ++ * ++ * It walks on a query tree recursively when set operations ++ * (UNION, INTERSECT, EXCEPT) are used. ++ * ++ */ ++static void ++proxySetOperations(sepgsqlWalkerContext *swc, Node *node) ++{ ++ Query *query = swc->qstack->query; ++ ++ if (node == NULL) ++ return; ++ ++ switch (nodeTag(node)) ++ { ++ case T_RangeTblRef: ++ { ++ RangeTblRef *rtr = (RangeTblRef *) node; ++ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); ++ ++ Assert(IsA(rte, RangeTblEntry) ++ && rte->rtekind == RTE_SUBQUERY); ++ proxyRteSubQuery(swc, rte->subquery); ++ ++ break; ++ } ++ case T_SetOperationStmt: ++ { ++ SetOperationStmt *sop = (SetOperationStmt *) node; ++ ++ proxySetOperations(swc, sop->larg); ++ proxySetOperations(swc, sop->rarg); ++ break; ++ } ++ default: ++ elog(ERROR, "SELinux enexpected node (%d) in setOperations tree", ++ nodeTag(node)); ++ break; ++ } ++} ++ ++/* ++ * proxyRteSubQuery ++ * ++ * It walks on the given DML Query to enumerate all appeared tables, ++ * columns and functions which include implementations of operator. ++ * While its walking, it generates a list of SEvalItemXXXX object ++ * to be evaluated later, and marks required permission on ++ * RangeTblEntry->pgaceTuplePerms. The swc->selist is copied to ++ * PlannedStmt->pgaceItem and evaluated on the hook invoked from ++ * the executor. RangeTblEntry->pgaceTuplePerms is copied to ++ * Scan->pgaceTuplePerms and it can be refered at sepgsqlExecScan() ++ * hook to apply tuple-level access controls. ++ */ ++static void ++proxyRteSubQuery(sepgsqlWalkerContext *swc, Query *query) ++{ ++ CmdType cmdType = query->commandType; + RangeTblEntry *rte = NULL; -+ queryChain qcData; -+ ListCell *l; ++ struct queryStack qsData; ++ 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 ++ /* ++ * push a query to queryStack + */ -+ selist = proxyJoinTree(selist, qc, (Node *) query->jointree, -+ &query->jointree->quals); ++ qsData.parent = swc->qstack; ++ qsData.query = query; ++ swc->qstack = &qsData; + -+ /* clean-up polluted RangeTblEntry */ -+ foreach (l, query->rtable) { -+ rte = (RangeTblEntry *) lfirst(l); -+ rte->requiredPerms &= ~SEPGSQL_PERMS_ALL; ++ switch (cmdType) ++ { ++ case CMD_SELECT: ++ /* ++ * add db_table:{select} for any relation in FROM clause ++ */ ++ checkSelectFromExpr(swc, query, (Node *) query->jointree); ++ ++ case CMD_UPDATE: ++ case CMD_INSERT: ++ foreach(l, query->targetList) ++ { ++ TargetEntry *tle = lfirst(l); ++ bool is_security_attr = false; ++ ++ Assert(IsA(tle, TargetEntry)); ++ ++ if (tle->resjunk && tle->resname && ++ strcmp(tle->resname, SECURITY_SYSATTR_NAME) == 0) ++ is_security_attr = true; ++ ++ /* ++ * Result set of junk target entries are not shown ++ * to users, so it is evaluated with "use" permission. ++ */ ++ if (tle->resjunk && !is_security_attr) ++ { ++ sepgsqlExprWalkerFlags((Node *) tle->expr, swc, true); ++ continue; ++ } ++ ++ sepgsqlExprWalkerFlags((Node *) tle->expr, swc, false); ++ ++ if (cmdType == CMD_UPDATE || cmdType == CMD_INSERT) ++ { ++ /* ++ * Add SEvalItem for the target of INSERT/UPDATE ++ */ ++ AttrNumber attno ++ = is_security_attr ++ ? SecurityAttributeNumber : tle->resno; ++ uint32 perms ++ = cmdType == CMD_UPDATE ++ ? DB_COLUMN__UPDATE : DB_COLUMN__INSERT; ++ ++ rte = rt_fetch(query->resultRelation, query->rtable); ++ Assert(IsA(rte, RangeTblEntry) ++ && rte->rtekind == RTE_RELATION); ++ ++ swc->selist ++ = addEvalAttributeRTE(swc->selist, rte, attno, perms); ++ } ++ } ++ break; ++ ++ case CMD_DELETE: ++ /* ++ * NOTE: ++ * column level checks are not applied on DELETE. ++ */ ++ rte = rt_fetch(query->resultRelation, query->rtable); ++ Assert(IsA(rte, RangeTblEntry) && rte->rtekind == RTE_RELATION); ++ ++ swc->selist = addEvalRelationRTE(swc->selist, rte, ++ DB_TABLE__DELETE); ++ break; ++ ++ default: ++ elog(ERROR, "SELinux: unexpected cmdType = %d", cmdType); ++ break; + } + -+ return selist; ++ /* ++ * RETURNING clause requires "select" permission ++ */ ++ foreach(l, query->returningList) ++ { ++ TargetEntry *te = lfirst(l); ++ ++ Assert(IsA(te, TargetEntry)); ++ ++ sepgsqlExprWalkerFlags((Node *) te->expr, swc, false); ++ } ++ /* ++ * WHERE/JOIN ... ON/HAVING/ORDER BY/GROUP BY ... clause ++ * to apply "use" permission ++ */ ++ proxyJoinTree(swc, (Node *) query->jointree); ++ sepgsqlExprWalkerFlags(query->havingQual, swc, true); ++ sepgsqlExprWalkerFlags((Node *) query->sortClause, swc, true); ++ sepgsqlExprWalkerFlags((Node *) query->groupClause, swc, true); ++ ++ /* ++ * permission mark on the UNION/INTERSECT/EXCEPT ++ */ ++ proxySetOperations(swc, query->setOperations); ++ ++ /* ++ * pop a query to queryStack ++ */ ++ swc->qstack = qsData.parent; +} + -+static List *proxyJoinTree(List *selist, queryChain *qc, Node *n, Node **quals) ++/* ++ * sepgsqlProxyQuery ++ * ++ * This function is invoked just after the given queries rewritten ++ * by the query rewriter. It invokes proxyRteSubQuery() for any ++ * DML queries to pick up all appeared database object and stores ++ * the list of them into Query->pgaceItem to evaluate later. ++ * ++ * It does not do anything for DDL queries because it is processed ++ * on sepgsqlProcessUtility() hook. ++ */ ++List * ++sepgsqlProxyQuery(List *queryList) +{ -+ Query *query = getQueryFromChain(qc); ++ List *newList = NIL; ++ ListCell *l; + -+ if (n == NULL) -+ return selist; ++ foreach (l, queryList) ++ { ++ Query *query = (Query *) lfirst(l); + -+ if (IsA(n, RangeTblRef)) { -+ RangeTblRef *rtr = (RangeTblRef *) n; -+ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1); -+ Assert(IsA(rte, RangeTblEntry)); ++ Assert(IsA(query, Query)); + -+ 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; ++ switch (query->commandType) ++ { ++ case CMD_SELECT: ++ case CMD_UPDATE: ++ case CMD_INSERT: ++ case CMD_DELETE: ++ { ++ sepgsqlWalkerContext swcData; + -+ 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); ++ memset(&swcData, 0, sizeof(swcData)); ++ ++ proxyRteSubQuery(&swcData, query); ++ query->pgaceItem = (Node *) swcData.selist; ++ ++ newList = lappend(newList, query); ++ } + break; + default: -+ elog(ERROR, "SELinux: unexpected rtekinf = %d at fromList", rte->rtekind); ++ newList = lappend(newList, query); + 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 List *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; -+ -+ elog(NOTICE, "SELinux: TRUNCATE %s is replaced unconditional DELETE", -+ RelationGetRelationName(rel)); -+ -+ return sepgsqlProxyQuery(query); -+} -+ -+static List *proxyTruncateStmt(Query *query) -+{ -+ TruncateStmt *stmt = (TruncateStmt *) query->utilityStmt; -+ Relation rel; -+ ListCell *l; -+ List *subquery_list = NIL; -+ List *subquery_lids = NIL; -+ -+ /* resolve the relation names */ -+ foreach (l, stmt->relations) { -+ RangeVar *rv = lfirst(l); -+ -+ rel = heap_openrv(rv, AccessShareLock); -+ subquery_list = list_concat(subquery_list, -+ convertTruncateToDelete(rel)); -+ subquery_lids = lappend_oid(subquery_lids, -+ RelationGetRelid(rel)); -+ heap_close(rel, AccessShareLock); + } + -+ 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); -+ subquery_list = list_concat(subquery_list, -+ convertTruncateToDelete(rel)); -+ heap_close(rel, AccessShareLock); -+ } -+ } -+ return subquery_list; ++ return newList; +} + -+List *sepgsqlProxyQuery(Query *query) ++/* ++ * sepgsqlEvaluateParams ++ * ++ * It checks permissions to execute functions just before ++ * parameter list is generated. ++ */ ++void ++sepgsqlEvaluateParams(List *params) +{ -+ List *new_list = NIL; ++ sepgsqlWalkerContext swcData; + -+ 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; ++ queryStack qsData; ++ ++ memset(&qsData, 0, sizeof(queryStack)); ++ memset(&swcData, 0, sizeof(sepgsqlWalkerContext)); ++ swcData.qstack = &qsData; ++ ++ sepgsqlExprWalkerFlags((Node *) params, &swcData, false); ++ ++ execVerifyQuery(swcData.selist); +} + -+/* ******************************************************************************* -+ * 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; ++/* ++ * verityXXXX() ++ * ++ * These functions are invoked from execVerifyQuery, to evaluate ++ * SEvalItemXXXX objects generated at sepgsqlProxyQuery(). ++ */ + -+ /* prevent to modify pg_security directly */ ++/* ++ * verifyPgClassPerms ++ * ++ * It evaluates SEvalItemRelation object to access tables. ++ */ ++static void ++verifyPgClassPerms(Oid relid, bool inh, uint32 perms) ++{ ++ HeapTuple tuple; ++ ++ /* ++ * 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"); ++ && (perms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE))) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: user cannot modify pg_security directly"))); + -+ /* check table:{required permissions} */ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); ++ /* ++ * 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); ++ elog(ERROR, "SELinux: cache lookup failed for relation: %u", relid); + -+ if (pgclass->relkind != RELKIND_RELATION) { -+ ReleaseSysCache(tuple); -+ return; ++ if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION) ++ { ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_TABLE, ++ (access_vector_t) perms, ++ sepgsqlTupleName(RelationRelationId, tuple), ++ true); + } -+ -+ 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) ++/* ++ * verifyPgAttributePerms ++ * ++ * It evaluates SEvalItemAttribute to access columns. ++ */ ++static void ++verifyPgAttributePerms(Oid relid, bool inh, AttrNumber attno, uint32 perms) +{ -+ HeapTuple tuple; -+ Form_pg_class classForm; -+ Form_pg_attribute attrForm; -+ NameData name; ++ Form_pg_class clsForm; ++ HeapTuple tuple; + -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); ++ 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 */ ++ elog(ERROR, "SELinux: cache lookup failed for relation: %u", relid); ++ ++ clsForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (clsForm->relkind != RELKIND_RELATION) ++ { + ReleaseSysCache(tuple); + return; + } + ReleaseSysCache(tuple); + -+ /* 2. verify column perms */ -+ if (attno == 0) { -+ /* RECORD type permission check */ -+ Relation rel; ++ /* ++ * 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)); ++ 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) ++ while ((tuple = systable_getnext(scan)) != NULL) ++ { ++ Form_pg_attribute attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ if (attForm->attisdropped || attForm->attnum < 1) + continue; -+ sepgsql_avc_permission(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_DB_COLUMN, -+ perms, -+ sepgsqlGetTupleName(AttributeRelationId, tuple, &name)); ++ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_COLUMN, ++ perms, ++ sepgsqlTupleName(AttributeRelationId, tuple), ++ true); + } + systable_endscan(scan); + heap_close(rel, AccessShareLock); + + return; + } -+ ++ /* ++ * check required column's permission ++ */ + tuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), -+ Int16GetDatum(attno), -+ 0, 0); ++ Int16GetDatum(attno), 0, 0); + if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u", attno, relid); ++ 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)); ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_COLUMN, ++ perms, ++ sepgsqlTupleName(AttributeRelationId, tuple), ++ true); + ReleaseSysCache(tuple); +} + -+static void verifyPgProcPerms(Oid funcid, uint32 perms) ++/* ++ * verifyPgProcedurePerms ++ * ++ * It evaluates SEvalItemProcedure object to access tables. ++ */ ++static void ++verifyPgProcPerms(Oid funcid, uint32 perms) +{ -+ HeapTuple tuple; -+ NameData name; -+ Oid newcon; ++ HeapTuple tuple; ++ security_context_t tcon, ncon; + -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(funcid), -+ 0, 0, 0); ++ tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for procedure %d", funcid); ++ /* ++ * check domain transition ++ */ ++ tcon = pgaceLookupSecurityLabel(HeapTupleGetSecurity(tuple)); + -+ /* compute domain transition */ -+ newcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), -+ HeapTupleGetSecurity(tuple), -+ SECCLASS_PROCESS); -+ if (newcon != sepgsqlGetClientContext()) ++ ncon = sepgsqlAvcCreateCon(sepgsqlGetClientContext(), ++ tcon, ++ SECCLASS_PROCESS); ++ if (strcmp(sepgsqlGetClientContext(), ncon)) ++ { + 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); ++ sepgsqlAvcPermission(sepgsqlGetClientContext(), ++ ncon, ++ SECCLASS_PROCESS, ++ PROCESS__TRANSITION, ++ NULL, true); + } ++ pfree(ncon); ++ pfree(tcon); ++ ++ /* ++ * check procedure executiong permission ++ */ ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_PROCEDURE, ++ perms, ++ sepgsqlTupleName(ProcedureRelationId, tuple), ++ true); + + ReleaseSysCache(tuple); +} + -+static List *__expandPgClassInheritance(List *selist, Oid relid, uint32 perms) ++/* ++ * expandSEvalItemInheritance ++ * ++ * When a request to table/column is inheritable, we have to expand ++ * the target to child relations, because accessing a column within ++ * parent table also means accessing a column within child relation ++ * in same time. ++ * ++ * For example, when t2 and t3 inherits t1, we have to check permission ++ * on t2.x and t3.x for the request to t1.x. ++ * It is impossible to be done before, because we have a chance to ++ * change inheritance relationships between PREPARE and EXECUTE. ++ * So, we have to check it in execution phase. ++ */ ++static List * ++expandRelationInheritance(List *selist, Oid relid, uint32 perms) +{ -+ List *child_list = find_inheritance_children(relid); -+ ListCell *l; ++ List *inherits = find_all_inheritors(relid); ++ ListCell *l; ++ ++ foreach(l, inherits) ++ selist = addEvalRelation(selist, lfirst_oid(l), false, perms); + -+ 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) ++static List * ++expandAttributeInheritance(List *selist, Oid relid, char *attname, ++ uint32 perms) +{ -+ List *child_list = find_inheritance_children(relid); -+ ListCell *l; ++ List *inherits = find_all_inheritors(relid); ++ ListCell *l; + -+ foreach (l, child_list) { -+ Form_pg_attribute attrForm; -+ HeapTuple tuple; ++ foreach(l, inherits) ++ { ++ Form_pg_attribute attr; + -+ if (!attname) { -+ /* attname == NULL means RECORD reference */ -+ selist = __addEvalPgAttribute(selist, lfirst_oid(l), false, 0, perms); -+ selist = __expandPgAttributeInheritance(selist, lfirst_oid(l), NULL, perms); ++ HeapTuple tuple; ++ ++ if (!attname) ++ { ++ selist = addEvalAttribute(selist, lfirst_oid(l), false, 0, perms); + continue; + } + + tuple = SearchSysCacheAttName(lfirst_oid(l), attname); + if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "SELinux: cache lookup failed for attribute %s of relation %u", ++ 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); ++ ++ attr = (Form_pg_attribute) GETSTRUCT(tuple); ++ selist = addEvalAttribute(selist, lfirst_oid(l), false, ++ attr->attnum, perms); + + ReleaseSysCache(tuple); + } @@ -4363,155 +8634,347 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/secur + return selist; +} + -+static List *expandSEvalListInheritance(List *selist) { -+ List *result = NIL; -+ ListCell *l; ++static List * ++expandSEvalItemInheritance(List *selist) ++{ ++ SEvalItemRelation *ser; ++ SEvalItemAttribute *sea; ++ List *result = NIL; ++ ListCell *l; + -+ foreach (l, selist) { -+ SEvalItem *se = (SEvalItem *) lfirst(l); ++ foreach(l, selist) ++ { ++ Node *node = 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; ++ result = lappend(result, node); ++ switch (nodeTag(node)) ++ { ++ case T_SEvalItemRelation: ++ ser = (SEvalItemRelation *) node; ++ if (ser->inh) ++ { ++ ser->inh = false; ++ result = expandRelationInheritance(result, ++ ser->relid, ser->perms); + } -+ 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); ++ break; + -+ result = __expandPgAttributeInheritance(result, -+ se->a.relid, -+ NameStr(attrForm->attname), -+ se->perms); -+ ReleaseSysCache(tuple); -+ } -+ break; ++ case T_SEvalItemAttribute: ++ sea = (SEvalItemAttribute *) node; ++ if (sea->inh) ++ { ++ Form_pg_attribute attr; ++ HeapTuple tuple; ++ ++ sea->inh = false; ++ if (sea->attno == 0) ++ { ++ result = expandAttributeInheritance(result, ++ sea->relid, ++ NULL, sea->perms); ++ break; ++ } ++ ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(sea->relid), ++ Int16GetDatum(sea->attno), 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, ++ "SELinux: cache lookup failed for attribute %d of relation %u", ++ sea->attno, sea->relid); ++ attr = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ result = expandAttributeInheritance(result, ++ sea->relid, ++ NameStr(attr->attname), ++ sea->perms); ++ ReleaseSysCache(tuple); ++ } ++ break; ++ ++ case T_SEvalItemProcedure: ++ /* ++ * do nothing ++ */ ++ break; ++ ++ default: ++ elog(ERROR, "SELinux: Invalid node type (%d) in SEvalItemList", ++ nodeTag(node)); ++ break; + } + } + return result; +} + -+static void execVerifyQuery(List *selist) ++/* ++ * execVerifyQuery ++ * ++ * This function scans the given list, and invokes proper function ++ * to evaluate it. ++ */ ++static void ++execVerifyQuery(List *selist) +{ -+ ListCell *l; ++ SEvalItemRelation *ser; ++ SEvalItemAttribute *sea; ++ SEvalItemProcedure *sep; ++ ListCell *l; + -+ foreach (l, selist) { -+ SEvalItem *se = lfirst(l); ++ foreach(l, selist) ++ { ++ Node *node = 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; ++ switch (nodeTag(node)) ++ { ++ case T_SEvalItemRelation: ++ ser = (SEvalItemRelation *) node; ++ verifyPgClassPerms(ser->relid, ser->inh, ser->perms); ++ break; ++ ++ case T_SEvalItemAttribute: ++ sea = (SEvalItemAttribute *) node; ++ verifyPgAttributePerms(sea->relid, sea->inh, sea->attno, ++ sea->perms); ++ break; ++ ++ case T_SEvalItemProcedure: ++ sep = (SEvalItemProcedure *) node; ++ verifyPgProcPerms(sep->funcid, sep->perms); ++ break; ++ ++ default: ++ elog(ERROR, "SELinux: Invalid node type (%d) in SEvalItemList", ++ nodeTag(node)); ++ break; + } + } +} + -+void sepgsqlVerifyQuery(PlannedStmt *pstmt) ++/* ++ * sepgsqlVerifyQuery ++ * ++ * 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 SEvalItemXXXX objects generated in ++ * previous phase, and it is copied to PlannedStmt->pgaceItem in the ++ * optimizer. ++ * sepgsqlVerifyQuery expand relations/columns and append permissions ++ * to execute trigger functions, if necessary. ++ */ ++void ++sepgsqlVerifyQuery(PlannedStmt *pstmt, int eflags) +{ + RangeTblEntry *rte; -+ List *selist; -+ ListCell *l; ++ List *selist; ++ ListCell *l; + ++ /* ++ * EXPLAIN statement does not access any object. ++ */ ++ if ((eflags & EXEC_FLAG_EXPLAIN_ONLY) != 0) ++ return; + if (!pstmt->pgaceItem) + return; ++ + Assert(IsA(pstmt->pgaceItem, List)); -+ selist = (List *) pstmt->pgaceItem; ++ selist = copyObject(pstmt->pgaceItem); + -+ /* expand table inheritances */ -+ selist = expandSEvalListInheritance(selist); ++ /* ++ * expand table inheritances ++ */ ++ selist = expandSEvalItemInheritance(selist); + -+ /* add checks for access via trigger function */ -+ foreach(l, pstmt->resultRelations) { -+ Index rindex = lfirst_int(l); ++ /* ++ * 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); ++ 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 -+ * *******************************************************************************/ ++/* ++ * -------------------------------------------------------------- ++ * Process Utility hooks ++ * -------------------------------------------------------------- ++ */ ++ ++/* ++ * checkTruncateStmt ++ * ++ * This function checks permissions of tuples within the given ++ * tables before TRUNCATE them. Because its meanings are same ++ * as unconditional DELETE logically, SE-PostgreSQL attempt to ++ * apply same permission for them operation. ++ * If there is a violated tuple at most, it stops to execute ++ * TRUNCATE and abort current trunsaction. ++ */ ++static void ++checkTruncateStmt(TruncateStmt *stmt) ++{ ++ Relation rel; ++ HeapScanDesc scan; ++ HeapTuple tuple; ++ List *relidList = NIL; ++ ListCell *l; ++ ++ foreach(l, stmt->relations) ++ { ++ RangeVar *rv = lfirst(l); ++ ++ relidList = lappend_oid(relidList, RangeVarGetRelid(rv, false)); ++ } ++ ++ if (stmt->behavior == DROP_CASCADE) ++ { ++ relidList = list_concat(relidList, heap_truncate_find_FKs(relidList)); ++ } ++ ++ foreach(l, relidList) ++ { ++ Oid relid = lfirst_oid(l); ++ ++ /* ++ * 1. db_table:{delete} ++ */ ++ tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_TABLE, ++ DB_TABLE__DELETE, ++ sepgsqlTupleName(RelationRelationId, tuple), ++ true); ++ ReleaseSysCache(tuple); ++ ++ /* ++ * 2. db_tuple:{delete} ++ */ ++ rel = heap_open(relid, AccessShareLock); ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) ++ { ++ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_TUPLE, ++ DB_TUPLE__DELETE, ++ sepgsqlTupleName(RelationRelationId, tuple), ++ true); ++ } ++ heap_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ } ++} ++ ++/* ++ * 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_TruncateStmt: ++ checkTruncateStmt((TruncateStmt *) parsetree); ++ break; ++ default: ++ /* ++ * do nothing ++ */ ++ break; ++ } ++} + +/* ---------------------------------------------------------- + * 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 */ ++/* ++ * 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 */ ++ /* ++ * 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 = addEvalRelation(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); ++ selist = addEvalAttribute(selist, RelationGetRelid(rel), false, attnum, ++ isFrom ? DB_COLUMN__INSERT : DB_COLUMN__SELECT); + } + -+ /* check call trigger function */ ++ /* ++ * check call trigger function ++ */ + if (isFrom) -+ selist = addEvalTriggerAccess(selist, RelationGetRelid(rel), false, CMD_INSERT); ++ selist = addEvalTriggerAccess(selist, RelationGetRelid(rel), ++ false, CMD_INSERT); + + execVerifyQuery(selist); +} + -+bool sepgsqlCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) ++/* ++ * 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; ++ uint32 perms = SEPGSQL_PERMS_SELECT; + -+ /* for 'pg_largeobject' */ -+ if (RelationGetRelid(rel) == LargeObjectRelationId) { -+ ListCell *l; ++ /* ++ * for 'pg_largeobject' ++ */ ++ if (RelationGetRelid(rel) == LargeObjectRelationId) ++ { ++ ListCell *l; + -+ foreach (l, attNumList) { -+ AttrNumber attnum = lfirst_int(l); -+ if (attnum == Anum_pg_largeobject_data) { ++ foreach(l, attNumList) ++ { ++ AttrNumber attnum = lfirst_int(l); ++ ++ if (attnum == Anum_pg_largeobject_data) ++ { + perms |= SEPGSQL_PERMS_READ; + break; + } @@ -4519,163 +8982,839 @@ diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/secur + } + 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_ )); +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 2008-06-14 02:36:58.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 sepgsql/src/backend/tcop/postgres.c +--- base/src/backend/tcop/postgres.c 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/tcop/postgres.c 2008-06-14 02:36:58.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/utility.c sepgsql/src/backend/tcop/utility.c +--- base/src/backend/tcop/utility.c 2008-03-19 09:48:23.000000000 +0900 ++++ sepgsql/src/backend/tcop/utility.c 2008-06-14 02:36:58.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-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/utils/adt/ri_triggers.c 2008-06-14 02:36:58.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); ++ pgacePreparePlanCheck(query_rel, &save_pgace); ++ 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/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 2008-06-14 02:36:58.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/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-01-07 23:51:33.000000000 +0900 ++++ sepgsql/src/backend/utils/init/postinit.c 2008-06-26 10:27:05.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(); + ++ /* 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-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/backend/utils/misc/guc.c 2008-07-11 14:10:51.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,11 +269,12 @@ static int max_index_keys; + static int max_identifier_length; + static int block_size; + static bool integer_datetimes; ++static char *pgace_security_feature; + + /* should be static, but commands/variable.c needs to get at these */ + char *role_string; + char *session_authorization_string; +- ++char *sepostgresql_mode; + + /* + * Displayable names for context types (enum GucContext) +@@ -2460,6 +2462,27 @@ static struct config_string ConfigureNam + }, + #endif /* USE_SSL */ + ++ { ++ {"pgace_security_feature", PGC_INTERNAL, PRESET_OPTIONS, ++ gettext_noop("Shows the guest of PGACE security framework"), ++ NULL, ++ GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE ++ }, ++ &pgace_security_feature, ++ NULL, NULL, pgaceShowSecurityFeature, ++ }, ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql", PGC_POSTMASTER, PRESET_OPTIONS, ++ gettext_noop("SE-PostgreSQL working mode"), ++ gettext_noop("Valid values are DEFAULT, PERMISSIVE, ENFORCING, DISABLED"), ++ 0, ++ }, ++ &sepostgresql_mode, ++ "default", NULL, NULL, ++ }, ++#endif ++ + /* End-of-list marker */ + { + {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL +@@ -3300,6 +3323,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 +4997,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 +5055,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 +5384,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/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-06-14 02:36:58.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,34 @@ do { \ + (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ + ) + ++#define HeapTupleHeaderHasSecurity(tup) \ ++ ((tup)->t_infomask & HEAP_HASSECURITY) ++ ++#define HeapTupleHeaderGetSecurity(tup) \ ++ ( \ ++ HeapTupleHeaderHasSecurity(tup) \ ++ ? (*((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (((tup)->t_infomask & HEAP_HASOID) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid)))) \ ++ : InvalidOid \ ++ ) ++ ++#define HeapTupleHeaderSetSecurity(tup, security) \ ++ do { \ ++ Assert(HeapTupleHeaderHasSecurity(tup)); \ ++ *((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (((tup)->t_infomask & HEAP_HASOID) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) = (security); \ ++ } while(0) ++ ++#define HeapTupleHasSecurity(tuple) \ ++ HeapTupleHeaderHasSecurity((tuple)->t_data) ++ ++#define HeapTupleGetSecurity(tuple) \ ++ HeapTupleHeaderGetSecurity((tuple)->t_data) ++ ++#define HeapTupleSetSecurity(tuple, security) \ ++ HeapTupleHeaderSetSecurity((tuple)->t_data, (security)) + + /* + * BITMAPLEN(NATTS) - +@@ -402,8 +430,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 + + /* + * MinimalTuple is an alternative representation that is used for transient +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-06-18 17:25:44.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 attno, bool relhasoids); ++ + 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-06-14 02:36:58.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 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_SYSATTR_NAME 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_SYSATTR_NAME 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_SYSATTR_NAME 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-06-14 02:36:58.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 = 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_ )); ++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"); -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 +@@ -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/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-06-14 02:36:58.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_security; /* for security attribute */ + } 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 2008-06-14 02:36:58.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 2008-06-14 02:36:58.000000000 +0900 +@@ -358,7 +358,10 @@ 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_SEvalItemRelation, /* in nodes/security.h */ ++ T_SEvalItemAttribute, /* 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 2008-06-12 22:34:19.000000000 +0900 ++++ sepgsql/src/include/nodes/relation.h 2008-06-14 02:36:58.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 2008-06-15 22:24:53.000000000 +0900 +@@ -0,0 +1,58 @@ ++/*------------------------------------------------------------------------- ++ * ++ * 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" ++ ++/* ++ * SEvalItemRelation ++ * ++ * SE-PostgreSQL permission evaluation item for a relation ++ */ ++typedef struct SEvalItemRelation { ++ NodeTag type; ++ ++ uint32 perms; ++ ++ Oid relid; ++ bool inh; ++} SEvalItemRelation; ++ ++/* ++ * SEvalItemAttribute ++ * ++ * SE-PostgreSQL permission evaluation item for an attribute ++ */ ++typedef struct SEvalItemAttribute { ++ NodeTag type; ++ ++ uint32 perms; ++ ++ Oid relid; ++ bool inh; ++ AttrNumber attno; ++} SEvalItemAttribute; ++ ++/* ++ * SEvalItemProcedure ++ * ++ * SE-PostgreSQL permission evaluation item for a procedure ++ */ ++typedef struct SEvalItemProcedure { ++ NodeTag type; ++ ++ uint32 perms; ++ ++ Oid funcid; ++} SEvalItemProcedure; ++ ++#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-06-14 02:36:58.000000000 +0900 @@ -366,6 +366,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SECURITY_PAM_APPL_H @@ -4686,147 +9825,1497 @@ diff -rpNU3 pgace/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in /* 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 @@ +@@ -637,6 +640,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 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 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,1183 @@ ++/* ++ * 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 "nodes/parsenodes.h" ++#include "storage/large_object.h" ++#include "utils/builtins.h" ++#include "utils/rel.h" ++ ++#ifdef HAVE_SELINUX ++#include "security/sepgsql.h" ++#endif ++ ++/* ++ * The definitions of PGACE hooks are follows: ++ * ++ * These are declared as static inline functions which give us no effect ++ * in the default (no security modules are enabled), and independent from ++ * its platform. ++ * ++ * The purpose of PGACE framework is to provide a security subsystems ++ * common hooks to apply its access controls, and minimize the impact ++ * to add a new security subsystem. ++ * ++ * (*) We calls the security subsystem implemented on PGACE framework ++ * as "the guest", in this comment. ++ * ++ * When a security module uses this framework, is has to add a #ifdef ++ * ... #endif block into the needed hooks, as follows: ++ * ++ * ------------ ++ * static inline 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 ++ * #ifdef HAVE_FOO_SECURITY ++ * if (fooIsEnabled()) ++ * return fooHeapTupleInsert(rel, tuple, ++ * is_internal, ++ * with_returning); ++ * #endif ++ * return true; ++ * } ++ * ____________ ++ * ++ * It can invokes specific security subsystem and the callee makes ++ * its decision whether the required access it allowed, or not. ++ * When no security module is available, these hooks have to keep ++ * the default behaivior to keep compatibility. ++ * In this case, pgaceHeapTupleInsert() has to return 'true'. ++ * ++ * Any hook has a comment to show the purpose of itself. ++ * Please look at this one to understand each hooks. ++ */ ++ ++/****************************************************************** ++ * Shows the PGACE guest identifier ++ ******************************************************************/ ++ ++/* ++ * pgaceSecurityFeatureIdentity ++ * ++ * This hook has to return unique identifier of the PGACE guest. ++ * A GUC parameter of 'pgace_security_feature' shows this value. ++ */ ++ ++static inline const char * ++pgaceSecurityFeatureIdentity(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return "selinux"; ++#endif ++ return "nothing"; ++} ++ ++/****************************************************************** ++ * 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. ++ */ ++static inline Size ++pgaceShmemSize(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlShmemSize(); ++#endif ++ 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. ++ */ ++static inline void ++pgaceInitialize(bool is_bootstrap) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlInitialize(is_bootstrap); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceInitialize ++ * ++ * 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. ++ */ ++static inline pid_t ++pgaceStartupWorkerProcess(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlStartupWorkerProcess(); ++#endif ++ return (pid_t) 0; ++} ++ ++/****************************************************************** ++ * SQL proxy hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceProxyQuery ++ * ++ * 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. ++ */ ++static inline List * ++pgaceProxyQuery(List *queryList) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlProxyQuery(queryList); ++#endif ++ return queryList; ++} ++ ++/* ++ * pgaceIsAllowPlannerHook ++ * ++ * The guest can control whether planner_hook is available, or not. ++ * It returns false, if it is not allowed to apply planner_hook. ++ * ++ * The purpose of this hook is to make sure pgace opaque data are delivered ++ * to PlannedStmt::pgaceItem and Scan::pgaceTuplePerms, because they are ++ * copied in standard_planner(). Overriding planner_hook has a possibility ++ * to prevent the guest works correctly. ++ */ ++static inline bool ++pgaceIsAllowPlannerHook(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return false; ++#endif ++ return true; ++} ++ ++/* ++ * pgaceExecutorStart ++ * ++ * This hook is invoked on the head of ExecutorStart(). ++ * ++ * The arguments of this hook are come from the ones of ExecutorStart ++ * as is. ++ */ ++static inline void ++pgaceExecutorStart(QueryDesc *queryDesc, int eflags) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ Assert(queryDesc->plannedstmt != NULL); ++ sepgsqlVerifyQuery(queryDesc->plannedstmt, eflags); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline bool ++pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlExecScan(scan, rel, slot); ++#endif ++ return true; ++} ++ ++/* ++ * pgaceProcessUtility ++ * ++ * This hooks is invoked on the head of ProcessUtility(). ++ */ ++static inline void ++pgaceProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlProcessUtility(parsetree, params, isTopLevel); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceEvaluateParams ++ * ++ * This hook is invoked just before parameter lists are evaluated ++ * at EvaluateParams(). ++ */ ++static inline void ++pgaceEvaluateParams(List *params) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlEvaluateParams(params); ++ return; ++ } ++#endif ++} ++ ++ ++/****************************************************************** ++ * 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. ++ */ ++static inline 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; ++} ++ ++/* ++ * 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. ++ */ ++static inline 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; ++} ++ ++/* ++ * 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. ++ */ ++static inline 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; ++} ++ ++/****************************************************************** ++ * Extended SQL statement hooks ++ ******************************************************************/ ++ ++/* ++ * PGACE framework provides its guest facilities to manage security ++ * attribute of database object, using an extended SQL statement. ++ * ++ * For example: ++ * CREATE TABLE tbl ( ++ * x integer, ++ * y text ++ * ) CONTEXT = 'system_u:object_r:sepgsql_ro_table_t:Classified', ++ * ++ * In SE-PostgreSQL, this statement enables to create a new table ++ * with explicitly specified security attribute by CONTEXT = 'xxx' ++ * clause. We call the clause as a "security attribute modifier". ++ * ++ * The series of hooks enables the guest to handle the given ++ * security attribute and apply it on the specified database ++ * object. ++ * ++ * The guest can apply this feature on the following statement: ++ * ++ * CREATE DATABASE ++ * ALTER DATABASE ++ * CREATE TABLE ++ * ALTER TABLE
++ * ALTER TABLE
ALTER ++ * CREATE FUNCTION ++ * ALTER FUNCTION ++ */ ++ ++/* ++ * pgaceGramSecurityItem ++ * ++ * This hook is invoked during parsing a give query from parser/gram.y, ++ * and it generates a DefElem object which holds explicitly specified ++ * security attribute. If the guest support the feature of security ++ * attribute modifier, this hook has to check whether the given clause ++ * is appropriate, or not. ++ * ++ * In the following exmaple case: ++ * CREATE TABLE tbl ( ++ * x integer, ++ * y text ++ * ) CONTEXT = 'system_u:object_r:sepgsql_ro_table_t:Classified', ++ * ++ * This hook is invoked with "context" as an argument of defname ++ * and "system_u:object_r:sepgsql_ro_table_t:Classified" as an ++ * argument of value, and has to check whether it is appropriate ++ * as a security attribute modifier, or not. ++ * If OK, the hook generates a DefElem object which contains ++ * the given context, and returns it. ++ * ++ * To return NULL means that "This clause is not a security attribute ++ * modifier", then it makes an error. ++ */ ++static inline DefElem * ++pgaceGramSecurityItem(char *defname, char *value) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlGramSecurityItem(defname, value); ++#endif ++ return NULL; ++} ++ ++/* ++ * pgaceIsGramSecurityItem ++ * ++ * This hook checks whether the given DefElem object means security ++ * attribute modifier generated at pgaceGramSecurityItem(), or not. ++ * If OK, it returns true. ++ */ ++static inline bool ++pgaceIsGramSecurityItem(DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlIsGramSecurityItem(defel); ++#endif ++ 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 security attribute modifier generated at ++ * pgaceGramSecurityItem(). ++ */ ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlGramCreateRelation(rel, tuple, defel); ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlGramCreateAttribute(rel, tuple, defel); ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlGramAlterRelation(rel, tuple, defel); ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlGramAlterAttribute(rel, tuple, defel); ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramCreateDatabase(rel, tuple, defel); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramAlterDatabase(rel, tuple, defel); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramCreateFunction(rel, tuple, defel); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGramAlterFunction(rel, tuple, defel); ++ return; ++ } ++#endif ++} ++ ++/****************************************************************** ++ * 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. ++ */ ++static inline void ++pgaceSetDatabaseParam(const char *name, char *argstring) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlSetDatabaseParam(name, argstring); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceGetDatabaseParam ++ * ++ * This hook is invoked just before reffering a GUC variable. ++ * ++ * arguments: ++ * - name is a name of GUC variable. ++ */ ++static inline void ++pgaceGetDatabaseParam(const char *name) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlGetDatabaseParam(name); ++ return; ++ } ++#endif ++} ++ ++/****************************************************************** ++ * FUNCTION related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceCallFunction ++ * ++ * This hook is invoked just before execute a function as a part ++ * of the query. It provides a FmgrInfo object used to execute ++ * function, and the guest can store an opaque data within ++ * FmgrInfo::fn_pgaceItem. ++ */ ++static inline void ++pgaceCallFunction(FmgrInfo *finfo) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlCallFunction(finfo, false); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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.) ++ * ++ * The guest can refer FmgrInfo and TriggerData object to make ++ * its decision. ++ */ ++static inline bool ++pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlCallFunctionTrigger(finfo, tgdata); ++#endif ++ return true; ++} ++ ++/* ++ * pgaceCallFunctionFastPath ++ * ++ * This hook is invoked just before executing a function in ++ * fast path. ++ */ ++static inline void ++pgaceCallFunctionFastPath(FmgrInfo *finfo) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlCallFunction(finfo, true); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgacePreparePlanCheck ++ * ++ * This hook is invoked just before FK/PK constraint checks. ++ * The guest can change its state during FK/PK constraint checks, ++ * and restore it on pgaceRestorePlanCheck(). ++ * If it needs an opaque data, pgace_saved can be used to store ++ * an opaque data. ++ */ ++static inline void ++pgacePreparePlanCheck(Relation rel, Datum *pgace_saved) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlPreparePlanCheck(rel, pgace_saved); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceRestorePlanCheck ++ * ++ * This hook is invoked just after FK/PK constraint checks. ++ * When the guest change something, it can be restored in this hook. ++ */ ++static inline void ++pgaceRestorePlanCheck(Relation rel, Datum pgace_saved) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlRestorePlanCheck(rel, pgace_saved); ++ return; ++ } ++#endif ++} ++ ++/****************************************************************** ++ * 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. ++ */ ++static inline void ++pgaceLockTable(Oid relid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLockTable(relid); ++ return; ++ } ++#endif ++} ++ ++/****************************************************************** ++ * 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 ++ */ ++static inline void ++pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlCopyTable(rel, attNumList, isFrom); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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 ++ */ ++static inline bool ++pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlCopyToTuple(rel, attNumList, tuple); ++#endif ++ 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. ++ */ ++static inline void ++pgaceLoadSharedModule(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLoadSharedModule(filename); ++ return; ++ } ++#endif ++} ++ ++/****************************************************************** ++ * 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. ++ */ ++static inline void ++pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectCreate(rel, tuple); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectDrop(rel, tuple, pgaceItem); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectRead(lodesc, length); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceLargeObjectWrite ++ * ++ * This hook is invoked at the head of lo_write(). ++ */ ++static inline void ++pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectWrite(lodesc, length); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceLargeObjectTruncate ++ * ++ * This hook is invoked at the head of lo_truncate(). ++ */ ++static inline void ++pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectTruncate(lodesc, offset); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceLargeObjectImport ++ * ++ * This hook is invoked just before importing the given file. ++ */ ++static inline void ++pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectImport(loid, fdesc, filename); ++ return; ++ } ++#endif ++} ++ ++/* ++ * pgaceLargeObjectExport ++ * ++ * This hook is invoked just before exporting the given large object. ++ */ ++static inline void ++pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectExport(loid, fdesc, filename); ++ return; ++ } ++#endif ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectGetSecurity(rel, tuple); ++ return; ++ } ++#endif ++ elog(ERROR, "PGACE: There is no guest module."); ++} ++ ++/* ++ * 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. ++ */ ++static inline void ++pgaceLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ { ++ sepgsqlLargeObjectSetSecurity(rel, newtup, oldtup); ++ return; ++ } ++#endif ++ elog(ERROR, "PGACE: There is no guest module."); ++} ++ ++/****************************************************************** ++ * Security Label hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceSecurityAttributeNecessary ++ * ++ * This hook provides a hint to the heap input/output subsystem. ++ * If it returns true, sizeof(Oid) bytes are expanded in HeapTupleHeader ++ * to store security identifier. ++ */ ++static inline bool ++pgaceSecurityAttributeNecessary(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return true; ++#endif ++ 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" ++ */ ++static inline char * ++pgaceTranslateSecurityLabelIn(char *seclabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlTranslateSecurityLabelIn(seclabel); ++#endif ++ return pstrdup("unlabeled"); ++} ++ ++/* ++ * pgaceTranslateSecurityLabelOut ++ * ++ * This hook enables the guest to translate a text representation ++ * of a given security attribute in internal format into cosmetic ++ * external format. ++ */ ++static inline char * ++pgaceTranslateSecurityLabelOut(char *seclabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlTranslateSecurityLabelOut(seclabel); ++#endif ++ return pstrdup("unlabeled"); ++} ++ ++/* ++ * pgaceValidateSecurityLabel ++ * ++ * This hook enables the guest to validate the given security attribute ++ * in raw-internal format. If it is not available, the hook has to ++ * return an alternative security attribute. ++ */ ++static inline char * ++pgaceValidateSecurityLabel(char *seclabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlValidateSecurityLabel(seclabel); ++#endif ++ return seclabel; ++} ++ ++/* ++ * pgaceSecurityLabelOfLabel ++ * ++ * This hook has to return the security attribute of a newly inserted ++ * tuple within pg_security ++ */ ++static inline char * ++pgaceSecurityLabelOfLabel(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsqlIsEnabled()) ++ return sepgsqlSecurityLabelOfLabel(); ++#endif ++ return pstrdup("unlabeled"); ++} ++ ++/****************************************************************** ++ * PGACE common facilities (not a hooks) ++ ******************************************************************/ ++ ++/* GUC parameter support */ ++extern const char *pgaceShowSecurityFeature(void); ++ ++/* Security Label Management */ ++extern void pgacePostBootstrapingMode(void); ++ ++extern Oid pgaceSecurityLabelToSid(char *label); ++ ++extern char *pgaceSidToSecurityLabel(Oid security_id); ++ ++extern Oid pgaceLookupSecurityId(char *label); ++ ++extern char *pgaceLookupSecurityLabel(Oid security_id); ++ ++/* 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); ++ ++/****************************************************************** ++ * Ported utility functions from 8.4devel ++ ******************************************************************/ ++#define CStringGetTextDatum(x) \ ++ (DirectFunctionCall1(textin, CStringGetDatum(x))) ++#define TextDatumGetCString(x) \ ++ (DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(x)))) ++ ++#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 2008-07-11 14:10:51.000000000 +0900 +@@ -0,0 +1,223 @@ ++/* ++ * 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 + +/* system catalogs */ +#include "catalog/pg_security.h" +#include "lib/stringinfo.h" ++#include "nodes/execnodes.h" +#include "nodes/nodes.h" ++#include "nodes/params.h" +#include "nodes/parsenodes.h" -+#include "storage/large_object.h" + +#include +#include +#include + +/* ++ * SE-PostgreSQL modes ++ */ ++extern char *sepostgresql_mode; ++ ++/* + * 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) ++#define SEPGSQL_PERMS_USE (1UL << 0) ++#define SEPGSQL_PERMS_SELECT (1UL << 1) ++#define SEPGSQL_PERMS_UPDATE (1UL << 2) ++#define SEPGSQL_PERMS_INSERT (1UL << 3) ++#define SEPGSQL_PERMS_DELETE (1UL << 4) ++#define SEPGSQL_PERMS_RELABELFROM (1UL << 5) ++#define SEPGSQL_PERMS_RELABELTO (1UL << 6) ++#define SEPGSQL_PERMS_READ (1UL << 7) ++#define SEPGSQL_PERMS_WRITE (1UL << 8) + +/* + * 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); ++extern Size sepgsqlShmemSize(void); ++ ++extern void sepgsqlInitialize(bool is_bootstrap); ++ ++extern pid_t sepgsqlStartupWorkerProcess(void); + +/* SQL proxy hooks */ -+extern List *sepgsqlProxyQuery(Query *query); -+extern void sepgsqlVerifyQuery(PlannedStmt *pstmt); ++extern List *sepgsqlProxyQuery(List *queryList); ++ ++extern void sepgsqlVerifyQuery(PlannedStmt *pstmt, int eflags); ++ ++extern void sepgsqlEvaluateParams(List *params); ++ ++extern void sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel); ++ ++/* ExecScan hooks */ ++extern bool sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot); + +/* 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, ++extern bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, + bool is_internal, bool with_returning); -+extern bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, ++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 */ ++/* 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); ++ ++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); ++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); ++extern void sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check); ++ ++extern bool sepgsqlCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata); ++ ++extern void sepgsqlPreparePlanCheck(Relation rel, Datum *pgace_saved); ++ ++extern void sepgsqlRestorePlanCheck(Relation rel, Datum pgace_saved); + +/* TABLE related hooks */ -+extern void sepgsqlLockTable(Oid relid); -+extern bool sepgsqlAlterTable(Relation rel, AlterTableCmd *cmd); ++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); ++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); ++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); ++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 char *sepgsqlSecurityLabelIn(char *context); -+extern char *sepgsqlSecurityLabelOut(char *context); -+extern char *sepgsqlSecurityLabelCheckValid(char *context); -+extern char *sepgsqlSecurityLabelOfLabel(char *context); ++extern char *sepgsqlTranslateSecurityLabelIn(char *context); + -+/* Extended node type hooks */ -+extern Node *sepgsqlCopyObject(Node *node); -+extern bool sepgsqlOutObject(StringInfo str, Node *node); -+extern void *sepgsqlReadObject(char *token); ++extern char *sepgsqlTranslateSecurityLabelOut(char *context); ++ ++extern char *sepgsqlValidateSecurityLabel(char *context); ++ ++extern char *sepgsqlSecurityLabelOfLabel(void); + +/* + * SE-PostgreSQL core functions -+ * src/backend/security/sepgsql/core.c ++ * 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); ++extern bool sepgsqlIsEnabled(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); ++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 bool sepgsqlAvcPermission(const security_context_t scon, ++ const security_context_t tcon, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname, ++ bool abort); ++ ++extern bool sepgsqlAvcPermissionSid(const security_context_t scon, ++ Oid tsid, ++ security_class_t tclass, ++ access_vector_t perms, ++ const char *objname, ++ bool abort); ++ ++extern security_context_t sepgsqlAvcCreateCon(const security_context_t scon, ++ const security_context_t tcon, ++ security_class_t tclass); ++ ++extern Oid sepgsqlAvcCreateConSid(const security_context_t scon, ++ Oid tsid, ++ security_class_t tclass); + +/* + * SE-PostgreSQL permission evaluation related -+ * src/backend/security/sepgsql/permission.c ++ * 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); ++extern const char *sepgsqlTupleName(Oid relid, HeapTuple tuple); ++ ++extern void sepgsqlSetDefaultContext(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); ++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 /* SEPGSQL_H */ ++#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-06-14 02:36:58.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','0','1') ++#define ERRCODE_SELINUX_AUDIT MAKE_SQLSTATE('S','E', '0','0','2') ++#define ERRCODE_SELINUX_INFO MAKE_SQLSTATE('S','E', '0','0','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 --git a/sepostgresql.init b/sepostgresql.init index d1bb22c..73516a1 100644 --- a/sepostgresql.init +++ b/sepostgresql.init @@ -9,7 +9,7 @@ PGVERSION="8.3.3" PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` -SEPGVERSION="2.869" +SEPGVERSION="2.947" # source function library . /etc/rc.d/init.d/functions diff --git a/sepostgresql.spec b/sepostgresql.spec index fe1821b..7c3600d 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -4,39 +4,52 @@ # Copyright 2007 KaiGai Kohei # ----------------------------------------------------- -# SELinux policy types -%define selinux_variants mls strict targeted - # SE-PostgreSQL status extension +%define selinux_policy_stores targeted mls +# check policy dependency +%define fullset_policy %(rpm -E '%{dist}' | grep -cE '^\.fc[1-9]$') +%if %{fullset_policy} +%define required_policy_version 3.0.6 +%define policy_module_name sepostgresql +%else +%define required_policy_version 3.4.2 +%define policy_module_name sepostgresql-devel +%endif + + + +%{!?ssl:%define ssl 1} Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 8.3.3 -Release: 2.869%{?sepgsql_extension}%{?dist} +Release: 2.947%{?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.3-2.patch -Patch1: sepostgresql-sepgsql-8.3.3-2.patch +Source2: sepostgresql.8 +Source3: sepostgresql.logrotate +Patch0: sepostgresql-sepgsql-8.3.3-2.patch +Patch1: sepostgresql-policy-8.3.3-2.patch Patch2: sepostgresql-pg_dump-8.3.3-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 selinux-policy-devel +BuildRequires: selinux-policy >= %{required_policy_version} +%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,23 +66,11 @@ 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 \ @@ -78,25 +79,29 @@ popd --enable-cassert \ %endif --libdir=%{_libdir}/pgsql \ +%if %{ssl} + --with-openssl \ +%endif --datadir=%{_datadir}/sepgsql \ --with-system-tzdata=/usr/share/zoneinfo # parallel build, if possible make %{?_smp_mflags} +# to create empty .fc file +touch src/backend/security/sepgsql/policy/%{policy_module_name}.fc +make -C src/backend/security/sepgsql/policy %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 +for store in %{selinux_policy_stores} +do + install -d %{buildroot}%{_datadir}/selinux/${store} + install -p -m 644 src/backend/security/sepgsql/policy/%{policy_module_name}.pp.${store} \ + %{buildroot}%{_datadir}/selinux/${store}/%{policy_module_name}.pp +done # avoid to conflict with native postgresql package mv %{buildroot}%{_bindir} %{buildroot}%{_bindir}.orig @@ -124,13 +129,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,13 +151,18 @@ exit 0 /sbin/chkconfig --add %{name} /sbin/ldconfig -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 ${selinuxvariant} -i %{_datadir}/selinux/${selinuxvariant}/%{name}.pp >& /dev/null || : +%if %{fullset_policy} + %{_sbindir}/semodule -s ${store} -r %{policy_module_name} >& /dev/null || : + %{_sbindir}/semodule -s ${store} \ + -i %{_datadir}/selinux/${store}/%{policy_module_name}.pp >& /dev/null || : +%else + if %{_sbindir}/semodule -s ${store} -l | grep -Eq "^%{policy_module_name}"; then + %{_sbindir}/semodule -s ${store} \ + -u %{_datadir}/selinux/${store}/%{policy_module_name}.pp >& /dev/null || : + fi +%endif done # Fix up non-standard file contexts @@ -171,12 +181,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 %{policy_module_name} >& /dev/null || : done /sbin/fixfiles -R %{name} restore || : test -d %{_localstatedir}/lib/sepgsql && /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : @@ -205,12 +212,19 @@ fi %{_datadir}/sepgsql/conversion_create.sql %{_datadir}/sepgsql/information_schema.sql %{_datadir}/sepgsql/sql_features.txt -%attr(644,root,root) %{_datadir}/selinux/*/sepostgresql.pp +%attr(644,root,root) %{_datadir}/selinux/*/%{policy_module_name}.pp %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 +* Fri Jul 11 2008 - 8.3.3-2.944 +- 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