Hello this patch define new function flag - OBFUSCATE. With this flag encrypted source code is stored to probin column. Password is stored in GUC_SUPERUSER_ONLY item - it is similar security like SQL Server does (where privileged users can access system tables with source code or can use debugger).
ToDo: Dump Sample: postgres=# show obfuscator_password; obfuscator_password ----------------------- moje supertajne heslo (1 row) postgres=# \x Expanded display is on. postgres=# create or replace function fx() returns int as $$begin return -1; end; $$ language plpgsql; CREATE FUNCTION postgres=# \df+ fx List of functions -[ RECORD 1 ]-------+----------------------- Schema | public Name | fx Result data type | integer Argument data types | Volatility | volatile Owner | bob Language | plpgsql Source code | begin return -1; end; Description | postgres=# ALTER FUNCTION fx() obfuscate; NOTICE: begin return -1; end; ALTER FUNCTION postgres=# \df+ fx List of functions -[ RECORD 1 ]-------+--------- Schema | public Name | fx Result data type | integer Argument data types | Volatility | volatile Owner | bob Language | plpgsql Source code | - Description | postgres=# select fx(); -[ RECORD 1 ] fx | -1 postgres=# create or replace function fx() returns int as $$begin return -1; end; $$ language plpgsql obfuscate; CREATE FUNCTION postgres=# select fx(); -[ RECORD 1 ] fx | -1 postgres=# \df+ fx List of functions -[ RECORD 1 ]-------+--------- Schema | public Name | fx Result data type | integer Argument data types | Volatility | volatile Owner | bob Language | plpgsql Source code | - Description | postgres=# select * from pg_proc where proname = 'fx'; -[ RECORD 1 ]--+---------------------------------------------------------------------------- proname | fx pronamespace | 2200 proowner | 16385 prolang | 16421 procost | 100 prorows | 0 proisagg | f prosecdef | f proisstrict | f proretset | f provolatile | v pronargs | 0 prorettype | 23 proargtypes | proallargtypes | proargmodes | proargnames | prosrc | - probin | \231\003_\266\361\214}\231\240L/\020\232\036c\234\315P\236\266I\370\324\222 proconfig | proacl | [EMAIL PROTECTED] ~]$ psql -U bob postgres Welcome to psql 8.3RC2, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help with psql commands \g or terminate with semicolon to execute query \q to quit postgres=> \x Expanded display is on. postgres=> show obfuscator_password; ERROR: must be superuser to examine "obfuscator_password" postgres=> select fx(); -[ RECORD 1 ] fx | -1 postgres=> \df+ fx List of functions -[ RECORD 1 ]-------+--------- Schema | public Name | fx Result data type | integer Argument data types | Volatility | volatile Owner | bob Language | plpgsql Source code | - Description | postgres=> select * from pg_proc where proname = 'fx'; -[ RECORD 1 ]--+---------------------------------------------------------------------------- proname | fx pronamespace | 2200 proowner | 16385 prolang | 16421 procost | 100 prorows | 0 proisagg | f prosecdef | f proisstrict | f proretset | f provolatile | v pronargs | 0 prorettype | 23 proargtypes | proallargtypes | proargmodes | proargnames | prosrc | - probin | \231\003_\266\361\214}\231\240L/\020\232\036c\234\315P\236\266I\370\324\222 proconfig | proacl |
*** ./src/backend/catalog/pg_proc.c.orig 2008-01-27 21:29:42.000000000 +0100 --- ./src/backend/catalog/pg_proc.c 2008-01-28 11:13:31.000000000 +0100 *************** *** 27,32 **** --- 27,33 ---- #include "mb/pg_wchar.h" #include "miscadmin.h" #include "parser/parse_type.h" + #include "parser/parse_func.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "utils/acl.h" *************** *** 34,39 **** --- 35,41 ---- #include "utils/lsyscache.h" #include "utils/syscache.h" + extern char *obfuscator_password; Datum fmgr_internal_validator(PG_FUNCTION_ARGS); Datum fmgr_c_validator(PG_FUNCTION_ARGS); *************** *** 45,51 **** static bool match_prosrc_to_literal(const char *prosrc, const char *literal, int cursorpos, int *newcursorpos); - /* ---------------------------------------------------------------- * ProcedureCreate * --- 47,52 ---- *************** *** 74,80 **** Datum parameterNames, Datum proconfig, float4 procost, ! float4 prorows) { Oid retval; int parameterCount; --- 75,82 ---- Datum parameterNames, Datum proconfig, float4 procost, ! float4 prorows, ! bool obfuscate) { Oid retval; int parameterCount; *************** *** 248,257 **** values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = 'n'; ! values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin, CStringGetDatum(prosrc)); ! values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin, CStringGetDatum(probin)); if (proconfig != PointerGetDatum(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else --- 250,268 ---- values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = 'n'; ! if (!obfuscate) ! { ! values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin, CStringGetDatum(prosrc)); ! values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin, CStringGetDatum(probin)); + } + else + { + values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin, + CStringGetDatum("-")); + values[Anum_pg_proc_probin - 1] = Obfuscate(prosrc); + } if (proconfig != PointerGetDatum(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else *************** *** 822,824 **** --- 833,873 ---- *newcursorpos = newcp; return false; } + + Datum + Obfuscate(const char *prosrc) + { + Datum encrypted_src; + + FuncDetailCode fdresult; + Oid encrypt_argtypes[] = {BYTEAOID, BYTEAOID, TEXTOID}; + bool retset; + Oid *true_oid_array; + Oid fnOid = InvalidOid; + Oid rettype; + + fdresult = func_get_detail(list_make1(makeString("encrypt")), + NIL, 3, encrypt_argtypes, + &fnOid, &rettype, &retset, + &true_oid_array); + + if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(list_make1(makeString("encrypt")), 3, encrypt_argtypes)), + errhint("Install pgcrypto first."))); + + encrypted_src = OidFunctionCall3(fnOid, + DirectFunctionCall2(binary_decode, + DirectFunctionCall1(textin, CStringGetDatum(prosrc)), + DirectFunctionCall1(textin, CStringGetDatum("escape"))), + DirectFunctionCall2(binary_decode, + DirectFunctionCall1(textin, CStringGetDatum(obfuscator_password)), + DirectFunctionCall1(textin, CStringGetDatum("escape"))), + DirectFunctionCall1(textin, CStringGetDatum("bf"))); + + /* probin is bytea datatype */ + + return encrypted_src; + } *** ./src/backend/commands/functioncmds.c.orig 2008-01-27 21:15:18.000000000 +0100 --- ./src/backend/commands/functioncmds.c 2008-01-28 13:34:34.000000000 +0100 *************** *** 295,301 **** DefElem **security_item, List **set_items, DefElem **cost_item, ! DefElem **rows_item) { if (strcmp(defel->defname, "volatility") == 0) { --- 295,302 ---- DefElem **security_item, List **set_items, DefElem **cost_item, ! DefElem **rows_item, ! DefElem **obfuscate_item) { if (strcmp(defel->defname, "volatility") == 0) { *************** *** 336,341 **** --- 337,349 ---- *rows_item = defel; } + else if (strcmp(defel->defname, "obfuscate") == 0) + { + if (*obfuscate_item) + goto duplicate_error; + + *obfuscate_item = defel; + } else return false; *************** *** 412,418 **** bool *security_definer, ArrayType **proconfig, float4 *procost, ! float4 *prorows) { ListCell *option; DefElem *as_item = NULL; --- 420,427 ---- bool *security_definer, ArrayType **proconfig, float4 *procost, ! float4 *prorows, ! bool *obfuscate_p) { ListCell *option; DefElem *as_item = NULL; *************** *** 423,428 **** --- 432,438 ---- List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + DefElem *obfuscate_item = NULL; foreach(option, options) { *************** *** 450,456 **** &security_item, &set_items, &cost_item, ! &rows_item)) { /* recognized common option */ continue; --- 460,467 ---- &security_item, &set_items, &cost_item, ! &rows_item, ! &obfuscate_item)) { /* recognized common option */ continue; *************** *** 506,511 **** --- 517,524 ---- (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ROWS must be positive"))); } + if (obfuscate_item) + *obfuscate_p = intVal(obfuscate_item->arg); } *************** *** 624,629 **** --- 637,643 ---- HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; + bool obfuscate; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, *************** *** 642,653 **** proconfig = NULL; procost = -1; /* indicates not set */ prorows = -1; /* indicates not set */ /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, &as_clause, &language, &volatility, &isStrict, &security, ! &proconfig, &procost, &prorows); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); --- 656,669 ---- proconfig = NULL; procost = -1; /* indicates not set */ prorows = -1; /* indicates not set */ + obfuscate = false; /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, &as_clause, &language, &volatility, &isStrict, &security, ! &proconfig, &procost, &prorows, ! &obfuscate); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); *************** *** 801,807 **** PointerGetDatum(parameterNames), PointerGetDatum(proconfig), procost, ! prorows); } --- 817,824 ---- PointerGetDatum(parameterNames), PointerGetDatum(proconfig), procost, ! prorows, ! obfuscate); } *************** *** 1151,1156 **** --- 1168,1174 ---- List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + DefElem *obfuscate_item = NULL; rel = heap_open(ProcedureRelationId, RowExclusiveLock); *************** *** 1177,1182 **** --- 1195,1201 ---- errmsg("\"%s\" is an aggregate function", NameListToString(stmt->func->funcname)))); + /* Examine requested actions. */ foreach(l, stmt->actions) { *************** *** 1188,1194 **** &security_def_item, &set_items, &cost_item, ! &rows_item) == false) elog(ERROR, "option \"%s\" not recognized", defel->defname); } --- 1207,1214 ---- &security_def_item, &set_items, &cost_item, ! &rows_item, ! &obfuscate_item) == false) elog(ERROR, "option \"%s\" not recognized", defel->defname); } *************** *** 1252,1257 **** --- 1272,1307 ---- tup = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); } + if (obfuscate_item) + { + bool isnull; + char *proc_src; + Datum prosrc; + Datum repl_val[Natts_pg_proc]; + char repl_null[Natts_pg_proc]; + char repl_repl[Natts_pg_proc]; + + prosrc = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_prosrc, &isnull); + + if (isnull) + elog(ERROR, "null prosrc"); + + proc_src = DatumGetCString(DirectFunctionCall1(textout, prosrc)); + if (strncmp(proc_src, "-", 1) != 0) + { + memset(repl_repl, ' ', sizeof(repl_repl)); + memset(repl_null, ' ', sizeof(repl_null)); + + repl_repl[Anum_pg_proc_prosrc - 1] = 'r'; + repl_repl[Anum_pg_proc_probin - 1] = 'r'; + + repl_val[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin, CStringGetDatum("-")); + repl_val[Anum_pg_proc_probin - 1] = Obfuscate(proc_src); + + tup = heap_modifytuple(tup, RelationGetDescr(rel), + repl_val, repl_null, repl_repl); + } + } /* Do the update */ simple_heap_update(rel, &tup->t_self, tup); *** ./src/backend/parser/gram.y.orig 2008-01-27 21:05:30.000000000 +0100 --- ./src/backend/parser/gram.y 2008-01-27 21:13:57.000000000 +0100 *************** *** 414,420 **** NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC ! OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER PARSER PARTIAL PASSWORD PLACING PLANS POSITION --- 414,420 ---- NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC ! OBFUSCATE OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER PARSER PARTIAL PASSWORD PLACING PLANS POSITION *************** *** 4278,4283 **** --- 4278,4287 ---- /* we abuse the normal content of a DefElem here */ $$ = makeDefElem("set", (Node *)$1); } + | OBFUSCATE + { + $$ = makeDefElem("obfuscate", (Node *)makeInteger(TRUE)); + } ; createfunc_opt_item: *** ./src/backend/parser/keywords.c.orig 2008-01-27 21:05:26.000000000 +0100 --- ./src/backend/parser/keywords.c 2008-01-27 21:11:48.000000000 +0100 *************** *** 252,257 **** --- 252,258 ---- {"nullif", NULLIF, COL_NAME_KEYWORD}, {"nulls", NULLS_P, UNRESERVED_KEYWORD}, {"numeric", NUMERIC, COL_NAME_KEYWORD}, + {"obfuscate", OBFUSCATE, UNRESERVED_KEYWORD}, {"object", OBJECT_P, UNRESERVED_KEYWORD}, {"of", OF, UNRESERVED_KEYWORD}, {"off", OFF, RESERVED_KEYWORD}, *** ./src/backend/utils/misc/guc.c.orig 2008-01-27 20:03:25.000000000 +0100 --- ./src/backend/utils/misc/guc.c 2008-01-27 20:32:37.000000000 +0100 *************** *** 220,225 **** --- 220,227 ---- char *IdentFileName; char *external_pid_file; + char *obfuscator_password; + int tcp_keepalives_idle; int tcp_keepalives_interval; int tcp_keepalives_count; *************** *** 2438,2443 **** --- 2440,2457 ---- "pg_catalog.simple", assignTSCurrentConfig, NULL }, + + { + {"obfuscator_password", PGC_POSTMASTER, FILE_LOCATIONS, + gettext_noop("Sets password for obfuscator procedure."), + NULL, + GUC_SUPERUSER_ONLY | GUC_NOT_IN_SAMPLE + }, + &obfuscator_password, + NULL, NULL, NULL + }, + + #ifdef USE_SSL { {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY, *** ./src/include/catalog/pg_proc.h.orig 2008-01-27 21:30:26.000000000 +0100 --- ./src/include/catalog/pg_proc.h 2008-01-28 11:07:41.000000000 +0100 *************** *** 4460,4467 **** Datum parameterNames, Datum proconfig, float4 procost, ! float4 prorows); extern bool function_parse_error_transpose(const char *prosrc); #endif /* PG_PROC_H */ --- 4460,4470 ---- Datum parameterNames, Datum proconfig, float4 procost, ! float4 prorows, ! bool obfuscate); extern bool function_parse_error_transpose(const char *prosrc); + extern Datum Obfuscate(const char *prosrc); + #endif /* PG_PROC_H */ *** ./src/pl/plpgsql/src/pl_comp.c.orig 2008-01-27 23:12:49.000000000 +0100 --- ./src/pl/plpgsql/src/pl_comp.c 2008-01-28 11:15:09.000000000 +0100 *************** *** 30,35 **** --- 30,36 ---- #include "nodes/makefuncs.h" #include "parser/gramparse.h" #include "parser/parse_type.h" + #include "parser/parse_func.h" #include "tcop/tcopprot.h" #include "utils/array.h" #include "utils/builtins.h" *************** *** 38,43 **** --- 39,46 ---- #include "utils/syscache.h" + extern char *obfuscator_password; + /* ---------- * Our own local and global variables * ---------- *************** *** 113,118 **** --- 116,123 ---- static void plpgsql_HashTableDelete(PLpgSQL_function *function); static void delete_function(PLpgSQL_function *func); + static char *Deobfuscate(Datum probin); + /* ---------- * plpgsql_compile Make an execution tree for a PL/pgSQL function. * *************** *** 293,298 **** --- 298,312 ---- if (isnull) elog(ERROR, "null prosrc"); proc_source = DatumGetCString(DirectFunctionCall1(textout, prosrcdatum)); + if (strncmp(proc_source, "-", 1) == 0) + { + prosrcdatum = SysCacheGetAttr(PROCOID, procTup, + Anum_pg_proc_probin, &isnull); + + /* deobfuscate source code if it is necessary */ + proc_source = Deobfuscate(prosrcdatum); + } + plpgsql_scanner_init(proc_source, functype); plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname)); *************** *** 2078,2080 **** --- 2092,2134 ---- /* remove back link, which no longer points to allocated storage */ function->fn_hashkey = NULL; } + + static char * + Deobfuscate(Datum probin) + { + Oid decrypt_argtypes[] = {BYTEAOID, BYTEAOID, TEXTOID}; + FuncDetailCode fdresult; + bool retset; + Oid *true_oid_array; + Oid decrypt_oid = InvalidOid; + Oid rettype; + char *src; + + Datum encoded_src; + + fdresult = func_get_detail(list_make1(makeString("decrypt")), + NIL, 3, decrypt_argtypes, + &decrypt_oid, &rettype, &retset, + &true_oid_array); + + if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(decrypt_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(list_make1(makeString("decrypt_oid")), + 3, decrypt_argtypes)), + errhint("Install pgcrypto first."))); + + + encoded_src = OidFunctionCall3(decrypt_oid, + probin, + DirectFunctionCall2(binary_decode, + DirectFunctionCall1(textin, CStringGetDatum(obfuscator_password)), + DirectFunctionCall1(textin, CStringGetDatum("escape"))), + DirectFunctionCall1(textin, CStringGetDatum("bf"))); + src = DatumGetCString(DirectFunctionCall1(textout, + DirectFunctionCall2(binary_decode, + encoded_src, + DirectFunctionCall1(textin, CStringGetDatum("escape"))))); + return src; + }
---------------------------(end of broadcast)--------------------------- TIP 2: Don't 'kill -9' the postmaster