diff -rpNU3 base/src/bin/pg_dump/pg_ace_dump.h sepgsql/src/bin/pg_dump/pg_ace_dump.h --- base/src/bin/pg_dump/pg_ace_dump.h 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql/src/bin/pg_dump/pg_ace_dump.h 2008-10-14 15:38:18.000000000 +0900 @@ -0,0 +1,284 @@ +#ifndef PG_ACE_DUMP_H +#define PG_ACE_DUMP_H + +#include "pg_backup.h" +#include "pg_dump.h" + +#define PG_ACE_FEATURE_NOTHING 0 +#define PG_ACE_FEATURE_SELINUX 1 + +#define SELINUX_SYSATTR_NAME "security_context" + +/* + * pg_ace_dumpCheckServerFeature + * + * This hook checks whether the server has required feature, or not. + */ +static inline void +pg_ace_dumpCheckServerFeature(int feature, PGconn *conn) +{ + const char *serv_feature; + + if (feature == PG_ACE_FEATURE_NOTHING) + return; + + serv_feature = PQparameterStatus(conn, "pgace_security_feature"); + if (!serv_feature) + { + fprintf(stderr, "could not get pgace_feature parameter.\n"); + exit(1); + } + + if (feature == PG_ACE_FEATURE_SELINUX) + { + if (strcmp(serv_feature, "selinux") != 0) + { + fprintf(stderr, "server does not have SELinux feature\n"); + exit(1); + } + } +} + +/* + * pg_ace_dumpDatabaseXXXX + * + * These hooks gives a chance to inject a security system column + * on dumping pg_database system catalog. + * A modified part must have ",d." style, and + * its result should be printed to buf. + */ +static inline const char * +pg_ace_dumpDatabaseQuery(int feature) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + return (",d." SELINUX_SYSATTR_NAME); + + return ""; +} + +static inline void +pg_ace_dumpDatabasePrint(int feature, PQExpBuffer buf, + PGresult *res, int index) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + int i_security = PQfnumber(res, SELINUX_SYSATTR_NAME); + char *dbsecurity = PQgetvalue(res, index, i_security); + + if (dbsecurity && dbsecurity[0] != '\0') + appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", dbsecurity); + } +} + +/* + * pg_ace_dumpClassXXXX + * + * These hooks give a chance to inject a security system column + * on dumping pg_class system catalog. The modified part has to + * be formalized to ",c." style. The result + * should be preserved at TableInfo->relsecurity to print later, + * if exist. + */ +static inline const char * +pg_ace_dumpClassQuery(int feature) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + return (",c." SELINUX_SYSATTR_NAME); + + return ""; +} + +static inline char * +pg_ace_dumpClassPreserve(int feature, PGresult *res, int index) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + int attno = PQfnumber(res, SELINUX_SYSATTR_NAME); + char *relcontext; + + if (attno < 0) + return NULL; + + relcontext = PQgetvalue(res, index, attno); + + if (!relcontext || relcontext[0] == '\0') + return NULL; + + return strdup(relcontext); + } + + return NULL; +} + +static inline void +pg_ace_dumpClassPrint(int feature, PQExpBuffer buf, TableInfo *tbinfo) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + char *relcontext = tbinfo->relsecurity; + + if (relcontext) + appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", relcontext); + + return; + } +} + +/* + * pg_ace_dumpAttributeXXXX + * + * These hooks give a chance to inject a security system column + * on dumping pg_attribute system catalog. The modified part has + * to be formalized to ",a." style. The result + * should be preserved at TableInfo->attsecurity[index] to print + * later, if exist. + */ +static inline const char * +pg_ace_dumpAttributeQuery(int feature) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + return (",a." SELINUX_SYSATTR_NAME); + + return ""; +} + +static inline char * +pg_ace_dumpAttributePreserve(int feature, PGresult *res, int index) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + int attno = PQfnumber(res, SELINUX_SYSATTR_NAME); + char *attcontext; + + if (attno < 0) + return NULL; + + attcontext = PQgetvalue(res, index, attno); + if (!attcontext || attcontext[0] == '\0') + return NULL; + + return strdup(attcontext); + } + + return NULL; +} + +static inline void +pg_ace_dumpAttributePrint(int feature, PQExpBuffer buf, + TableInfo *tbinfo, int index) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + char *relcontext = tbinfo->relsecurity; + char *attcontext = tbinfo->attsecurity[index]; + + if (attcontext) + { + if (relcontext && strcmp(relcontext, attcontext) == 0) + return; + + appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", attcontext); + } + return; + } +} + +/* + * pg_ace_dumpProcXXXX + * + * These hooks give a chance to inject a security system column + * on dumping pg_proc system catalog. The modified part has to be + * formalized to "" style. The result should be + * printed later, if exist. + */ +static inline const char * +pg_ace_dumpProcQuery(int feature) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + return ("," SELINUX_SYSATTR_NAME); + + return ""; +} + +static inline void +pg_ace_dumpProcPrint(int feature, PQExpBuffer buf, + PGresult *res, int index) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + int i_selinux = PQfnumber(res, SELINUX_SYSATTR_NAME); + char *prosecurity; + + if (i_selinux < 0) + return; + + prosecurity = PQgetvalue(res, index, i_selinux); + if (prosecurity && prosecurity[0] != '\0') + appendPQExpBuffer(buf, " SECURITY_CONTEXT = '%s'", prosecurity); + } +} + +/* + * pg_ace_dumpTableDataQuery + * + * This hook gives a chance to inject a security attribute system column + * on dumping of user's table. + * It must have "," style. + */ +static inline const char * +pg_ace_dumpTableDataQuery(int feature) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + return ("," SELINUX_SYSATTR_NAME); + + return ""; +} + +/* + * pg_ace_dumpCopyColumnList + * + * This hook gives a chance to inject a security attribute column within + * COPY statement. When a column is added, you have to return true. It + * enables to set needComma 'true', otherwise 'false'. + */ +static inline bool +pg_ace_dumpCopyColumnList(int feature, PQExpBuffer buf) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + appendPQExpBuffer(buf, SELINUX_SYSATTR_NAME); + return true; + } + + return false; +} + +/* + * pg_ace_dumpBlobComments + * + * This hook gives a chance to inject a query to restore a security + * attribute of binary large object. + */ +static inline void +pg_ace_dumpBlobComments(int feature, Archive *AH, PGconn *conn, Oid blobOid) +{ + if (feature == PG_ACE_FEATURE_SELINUX) + { + PGresult *res; + char query[256]; + + snprintf(query, sizeof(query), + "SELECT lo_get_security(%u)", blobOid); + res = PQexec(conn, query); + if (!res) + return; + + if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) + archprintf(AH, "SELECT lo_set_security(%u, '%s');\n", + blobOid, PQgetvalue(res, 0, 0)); + + PQclear(res); + } +} + +#endif diff -rpNU3 base/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c --- base/src/bin/pg_dump/pg_dump.c 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 turn on/off security attribute support */ +static int pg_ace_feature = PG_ACE_FEATURE_NOTHING; static void help(const char *progname); static void expand_schema_name_patterns(SimpleStringList *patterns, @@ -267,6 +270,7 @@ main(int argc, char **argv) {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, {"disable-triggers", no_argument, &disable_triggers, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"security-context", no_argument, &pg_ace_feature, PG_ACE_FEATURE_SELINUX}, {NULL, 0, NULL, 0} }; @@ -419,6 +423,8 @@ main(int argc, char **argv) disable_triggers = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "security-context") == 0) + pg_ace_feature = PG_ACE_FEATURE_SELINUX; else { fprintf(stderr, @@ -549,6 +555,8 @@ main(int argc, char **argv) std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); + pg_ace_dumpCheckServerFeature(pg_ace_feature, g_conn); + /* Set the datestyle to ISO to ensure the dump's portability */ do_sql_command(g_conn, "SET DATESTYLE = ISO"); @@ -771,6 +779,7 @@ help(const char *progname) printf(_(" --use-set-session-authorization\n" " use SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); + printf(_(" --security-context enable to dump security context of SE-PostgreSQL\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); @@ -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", + pg_ace_dumpTableDataQuery(pg_ace_feature), fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } @@ -1774,11 +1784,14 @@ dumpBlobComments(Archive *AH, void *arg) Oid blobOid; char *comment; + blobOid = atooid(PQgetvalue(res, i, 0)); + + pg_ace_dumpBlobComments(pg_ace_feature, AH, g_conn, blobOid); + /* ignore blobs without comments */ if (PQgetisnull(res, i, 1)) continue; - blobOid = atooid(PQgetvalue(res, i, 0)); comment = PQgetvalue(res, i, 1); printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", @@ -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 " + "%s " "from pg_class c " "left join pg_depend d on " "(c.relkind = '%c' and " @@ -2935,6 +2949,7 @@ getTables(int *numTables) "where relkind in ('%c', '%c', '%c', '%c') " "order by c.oid", username_subquery, + pg_ace_dumpClassQuery(pg_ace_feature), RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); @@ -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 = pg_ace_dumpClassPreserve(pg_ace_feature, res, i); /* other fields were zeroed above */ @@ -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 " + "%s " /* security context, if required */ "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t " "on a.atttypid = t.oid " "where a.attrelid = '%u'::pg_catalog.oid " "and a.attnum > 0::pg_catalog.int2 " "order by a.attrelid, a.attnum", + pg_ace_dumpAttributeQuery(pg_ace_feature), tbinfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 70100) @@ -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)); + tbinfo->attsecurity = (char **) malloc(ntups * sizeof(char *)); tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); @@ -4456,6 +4475,8 @@ getTableAttrs(TableInfo *tblinfo, int nu tbinfo->inhAttrs[j] = false; tbinfo->inhAttrDef[j] = false; tbinfo->inhNotNull[j] = false; + + tbinfo->attsecurity[j] = pg_ace_dumpAttributePreserve(pg_ace_feature, res, j); } PQclear(res); @@ -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", + pg_ace_dumpProcQuery(pg_ace_feature), finfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 80100) @@ -6698,6 +6721,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) if (prosecdef[0] == 't') appendPQExpBuffer(q, " SECURITY DEFINER"); + pg_ace_dumpProcPrint(pg_ace_feature, q, res, 0); + /* * COST and ROWS are emitted only if present and not default, so as not to * break backwards-compatibility of the dump without need. Keep this code @@ -8779,6 +8804,8 @@ dumpTableSchema(Archive *fout, TableInfo if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j]) appendPQExpBuffer(q, " NOT NULL"); + pg_ace_dumpAttributePrint(pg_ace_feature, q, tbinfo, j); + actual_atts++; } } @@ -8826,6 +8853,8 @@ dumpTableSchema(Archive *fout, TableInfo if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) appendPQExpBuffer(q, "\nWITH (%s)", tbinfo->reloptions); + pg_ace_dumpClassPrint(pg_ace_feature, q, tbinfo); + appendPQExpBuffer(q, ";\n"); /* Loop dumping statistics and storage statements */ @@ -10243,6 +10272,10 @@ fmtCopyColumnList(const TableInfo *ti) appendPQExpBuffer(q, "("); needComma = false; + + if (pg_ace_dumpCopyColumnList(pg_ace_feature, q)) + needComma = true; + for (i = 0; i < numatts; i++) { if (attisdropped[i]) diff -rpNU3 base/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h --- base/src/bin/pg_dump/pg_dump.h 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 */ char *reloptions; /* options specified by WITH (...) */ + char *relsecurity; /* security attribute of the relation */ bool hasindex; /* does it have any indexes? */ bool hasrules; /* does it have any rules? */ bool hasoids; /* does it have OIDs? */ @@ -262,6 +263,7 @@ typedef struct _tableInfo char *typstorage; /* type storage scheme */ bool *attisdropped; /* true if attr is dropped; don't dump it */ bool *attislocal; /* true if attr has local definition */ + char **attsecurity; /* security attribute of attribute (column) */ /* * Note: we need to store per-attribute notnull, default, and constraint diff -rpNU3 base/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpall.c --- base/src/bin/pg_dump/pg_dumpall.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql/src/bin/pg_dump/pg_dumpall.c 2008-07-11 14:10:51.000000000 +0900 @@ -27,6 +27,7 @@ int optreset; #endif #include "dumputils.h" +#include "pg_ace_dump.h" /* version string we expect back from pg_dump */ @@ -67,6 +68,9 @@ static int disable_triggers = 0; static int use_setsessauth = 0; static int server_version; +/* flag to turn on/off security attribute support */ +static int pg_ace_feature = PG_ACE_FEATURE_NOTHING; + static FILE *OPF; static char *filename = NULL; @@ -119,6 +123,7 @@ main(int argc, char *argv[]) {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, {"disable-triggers", no_argument, &disable_triggers, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"security-context", no_argument, &pg_ace_feature, PG_ACE_FEATURE_SELINUX}, {NULL, 0, NULL, 0} }; @@ -290,6 +295,8 @@ main(int argc, char *argv[]) appendPQExpBuffer(pgdumpopts, " --disable-triggers"); else if (strcmp(optarg, "use-set-session-authorization") == 0) /* no-op, still allowed for compatibility */ ; + else if (strcmp(optarg, "security-context") == 0) + pg_ace_feature = PG_ACE_FEATURE_SELINUX; else { fprintf(stderr, @@ -316,6 +323,8 @@ main(int argc, char *argv[]) appendPQExpBuffer(pgdumpopts, " --disable-triggers"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); + if (pg_ace_feature == PG_ACE_FEATURE_SELINUX) + appendPQExpBuffer(pgdumpopts, " --security-context"); if (optind < argc) { @@ -391,6 +400,8 @@ main(int argc, char *argv[]) } } + pg_ace_dumpCheckServerFeature(pg_ace_feature, conn); + /* * Open the output file if required, otherwise use stdout */ @@ -505,6 +516,7 @@ help(void) printf(_(" --use-set-session-authorization\n" " use SESSION AUTHORIZATION commands instead of\n" " OWNER TO commands\n")); + printf(_(" --security-context enables to dump security context of SE-PostgreSQL\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); @@ -915,16 +927,18 @@ dumpCreateDB(PGconn *conn) fprintf(OPF, "--\n-- Database creation\n--\n\n"); if (server_version >= 80100) - res = executeQuery(conn, + appendPQExpBuffer(buf, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "datistemplate, datacl, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " + "%s " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " - "WHERE datallowconn ORDER BY 1"); + "WHERE datallowconn ORDER BY 1", + pg_ace_dumpDatabaseQuery(pg_ace_feature)); else if (server_version >= 80000) - res = executeQuery(conn, + appendPQExpBuffer(buf, "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " @@ -933,7 +947,7 @@ dumpCreateDB(PGconn *conn) "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70300) - res = executeQuery(conn, + appendPQExpBuffer(buf, "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " @@ -942,7 +956,7 @@ dumpCreateDB(PGconn *conn) "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70100) - res = executeQuery(conn, + appendPQExpBuffer(buf, "SELECT datname, " "coalesce(" "(select usename from pg_shadow where usesysid=datdba), " @@ -958,7 +972,7 @@ dumpCreateDB(PGconn *conn) * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal * with getting a NULL by not printing any OWNER clause. */ - res = executeQuery(conn, + appendPQExpBuffer(buf, "SELECT datname, " "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " @@ -968,6 +982,7 @@ dumpCreateDB(PGconn *conn) "FROM pg_database d " "ORDER BY 1"); } + res = executeQuery(conn, buf->data); for (i = 0; i < PQntuples(res); i++) { @@ -1021,6 +1036,8 @@ dumpCreateDB(PGconn *conn) appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", dbconnlimit); + pg_ace_dumpDatabasePrint(pg_ace_feature, buf, res, i); + appendPQExpBuffer(buf, ";\n"); if (strcmp(dbistemplate, "t") == 0)