diff -u postgresql-7.2b2/src/pl/plpython/error.expected postgresql-7.2b2-kbj/src/pl/plpython/error.expected --- postgresql-7.2b2/src/pl/plpython/error.expected Wed May 9 15:54:38 2001 +++ postgresql-7.2b2-kbj/src/pl/plpython/error.expected Sun Nov 11 13:48:59 2001 @@ -1,5 +1,5 @@ SELECT invalid_type_uncaught('rick'); -ERROR: plpython: Call of function `__plpython_procedure_invalid_type_uncaught_1175341' failed. +ERROR: plpython: Call of function `__plpython_procedure_invalid_type_uncaught_112253' failed. plpy.SPIError: Cache lookup for type `test' failed. SELECT invalid_type_caught('rick'); NOTICE: ("Cache lookup for type `test' failed.",) @@ -9,11 +9,32 @@ (1 row) SELECT invalid_type_reraised('rick'); -ERROR: plpython: Call of function `__plpython_procedure_invalid_type_reraised_1175343' failed. +ERROR: plpython: Call of function `__plpython_procedure_invalid_type_reraised_112255' failed. plpy.Error: ("Cache lookup for type `test' failed.",) SELECT valid_type('rick'); valid_type ------------ +(1 row) + +SELECT read_file('/etc/passwd'); +ERROR: plpython: Call of function `__plpython_procedure_read_file_112260' failed. +exceptions.IOError: can't open files in restricted mode +SELECT write_file('/tmp/plpython','This is very bad'); +ERROR: plpython: Call of function `__plpython_procedure_write_file_112261' failed. +exceptions.IOError: can't open files in restricted mode +SELECT getpid(); +ERROR: plpython: Call of function `__plpython_procedure_getpid_112262' failed. +exceptions.AttributeError: getpid +SELECT uname(); +ERROR: plpython: Call of function `__plpython_procedure_uname_112263' failed. +exceptions.AttributeError: uname +SELECT sys_exit(); +ERROR: plpython: Call of function `__plpython_procedure_sys_exit_112264' failed. +exceptions.AttributeError: exit +SELECT sys_argv(); + sys_argv +---------------- + ['RESTRICTED'] (1 row) diff -u postgresql-7.2b2/src/pl/plpython/plpython.c postgresql-7.2b2-kbj/src/pl/plpython/plpython.c --- postgresql-7.2b2/src/pl/plpython/plpython.c Mon Nov 5 12:46:39 2001 +++ postgresql-7.2b2-kbj/src/pl/plpython/plpython.c Thu Nov 15 09:27:20 2001 @@ -188,6 +188,10 @@ static void PLy_init_safe_interp(void); static void PLy_init_plpy(void); +/* Helper functions used during initialization */ +static int populate_methods(PyObject *klass, PyMethodDef *methods); +static PyObject *build_tuple(char* string_list[], int len); + /* error handler. collects the current Python exception, if any, * and appends it to the error and sends it to elog */ @@ -240,6 +244,10 @@ static void PLy_output_tuple_funcs(PLyTypeInfo *, TupleDesc); static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc); +/* RExec methods + */ +static PyObject *PLy_r_open(PyObject *self, PyObject* args); + /* conversion functions */ static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc); @@ -265,35 +273,60 @@ static PyObject *PLy_interp_safe = NULL; static PyObject *PLy_interp_safe_globals = NULL; static PyObject *PLy_importable_modules = NULL; +static PyObject *PLy_ok_posix_names = NULL; +static PyObject *PLy_ok_sys_names = NULL; static PyObject *PLy_procedure_cache = NULL; -char *PLy_importable_modules_list[] = { +static char *PLy_importable_modules_list[] = { "array", "bisect", + "binascii", "calendar", "cmath", + "codecs", "errno", "marshal", "math", "md5", "mpz", "operator", + "pcre", "pickle", "random", "re", + "regex", + "sre", "sha", "string", "StringIO", + "struct", "time", "whrandom", "zlib" }; +static char *PLy_ok_posix_names_list[] = { + /* None for now */ +}; + +static char *PLy_ok_sys_names_list[] = { + "byteeorder", + "copyright", + "getdefaultencoding", + "getrefcount", + "hexrevision", + "maxint", + "maxunicode", + "platform", + "version", + "version_info" +}; + /* Python exceptions */ -PyObject *PLy_exc_error = NULL; -PyObject *PLy_exc_fatal = NULL; -PyObject *PLy_exc_spi_error = NULL; +static PyObject *PLy_exc_error = NULL; +static PyObject *PLy_exc_fatal = NULL; +static PyObject *PLy_exc_spi_error = NULL; /* some globals for the python module */ @@ -1150,12 +1183,6 @@ if ((proc->interp == NULL) || (PyErr_Occurred())) PLy_elog(ERROR, "Unable to create rexec.RExec instance"); - /* - * tweak the list of permitted modules - */ - PyObject_SetAttrString(proc->interp, "ok_builtin_modules", - PLy_importable_modules); - proc->reval = PyObject_GetAttrString(proc->interp, "r_eval"); if ((proc->reval == NULL) || (PyErr_Occurred())) PLy_elog(ERROR, "Unable to get method `r_eval' from rexec.RExec"); @@ -1650,7 +1677,7 @@ static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int); -PyTypeObject PLy_PlanType = { +static PyTypeObject PLy_PlanType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "PLyPlan", /* tp_name */ @@ -1679,13 +1706,13 @@ PLy_plan_doc, /* tp_doc */ }; -PyMethodDef PLy_plan_methods[] = { +static PyMethodDef PLy_plan_methods[] = { {"status", (PyCFunction) PLy_plan_status, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -PySequenceMethods PLy_result_as_sequence = { +static PySequenceMethods PLy_result_as_sequence = { (inquiry) PLy_result_length, /* sq_length */ (binaryfunc) 0, /* sq_concat */ (intargfunc) 0, /* sq_repeat */ @@ -1695,7 +1722,7 @@ (intintobjargproc) PLy_result_ass_slice, /* sq_ass_slice */ }; -PyTypeObject PLy_ResultType = { +static PyTypeObject PLy_ResultType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "PLyResult", /* tp_name */ @@ -1724,7 +1751,7 @@ PLy_result_doc, /* tp_doc */ }; -PyMethodDef PLy_result_methods[] = { +static PyMethodDef PLy_result_methods[] = { {"fetch", (PyCFunction) PLy_result_fetch, METH_VARARGS, NULL,}, {"nrows", (PyCFunction) PLy_result_nrows, METH_VARARGS, NULL}, {"status", (PyCFunction) PLy_result_status, METH_VARARGS, NULL}, @@ -1833,7 +1860,7 @@ /* result object methods */ -static PyObject * +PyObject * PLy_result_new(void) { PLyResultObject *ob; @@ -1853,7 +1880,7 @@ return (PyObject *) ob; } -static void +void PLy_result_dealloc(PyObject * arg) { PLyResultObject *ob = (PLyResultObject *) arg; @@ -1867,19 +1894,19 @@ PyMem_DEL(ob); } -static PyObject * +PyObject * PLy_result_getattr(PyObject * self, char *attr) { return NULL; } -static PyObject * +PyObject * PLy_result_fetch(PyObject * self, PyObject * args) { return NULL; } -static PyObject * +PyObject * PLy_result_nrows(PyObject * self, PyObject * args) { PLyResultObject *ob = (PLyResultObject *) self; @@ -1888,7 +1915,7 @@ return ob->nrows; } -static PyObject * +PyObject * PLy_result_status(PyObject * self, PyObject * args) { PLyResultObject *ob = (PLyResultObject *) self; @@ -2450,13 +2477,33 @@ elog(ERROR, "Unable to init plpy."); } +/* + * New RExec methods + */ + +PyObject* +PLy_r_open(PyObject *self, PyObject* args) +{ + PyErr_SetString(PyExc_IOError, "can't open files in restricted mode"); + return NULL; +} + + +static PyMethodDef PLy_r_exec_methods[] = { + {"r_open", (PyCFunction)PLy_r_open, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + +/* + * Init new RExec + */ + void PLy_init_safe_interp(void) { - PyObject *rmod; + PyObject *rmod, *rexec, *rexec_dict; char *rname = "rexec"; - int i, - imax; + int len; enter(); @@ -2467,19 +2514,93 @@ PyDict_SetItemString(PLy_interp_globals, rname, rmod); PLy_interp_safe = rmod; - imax = sizeof(PLy_importable_modules_list) / sizeof(char *); - PLy_importable_modules = PyTuple_New(imax); - for (i = 0; i < imax; i++) - { - PyObject *m = PyString_FromString(PLy_importable_modules_list[i]); + len = sizeof(PLy_importable_modules_list) / sizeof(char *); + PLy_importable_modules = build_tuple(PLy_importable_modules_list, len); - PyTuple_SetItem(PLy_importable_modules, i, m); - } + len = sizeof(PLy_ok_posix_names_list) / sizeof(char *); + PLy_ok_posix_names = build_tuple(PLy_ok_posix_names_list, len); + + len = sizeof(PLy_ok_sys_names_list) / sizeof(char *); + PLy_ok_sys_names = build_tuple(PLy_ok_sys_names_list, len); PLy_interp_safe_globals = PyDict_New(); if (PLy_interp_safe_globals == NULL) PLy_elog(ERROR, "Unable to create shared global dictionary."); + /* + * get an rexec.RExec class + */ + rexec = PyDict_GetItemString(PyModule_GetDict(rmod), "RExec"); + + if (rexec == NULL || !PyClass_Check(rexec)) + PLy_elog(ERROR, "Unable to get RExec object."); + + + rexec_dict = ((PyClassObject*)rexec)->cl_dict; + + /* + * tweak the list of permitted modules, posix and sys functions + */ + PyDict_SetItemString(rexec_dict, "ok_builtin_modules", PLy_importable_modules); + PyDict_SetItemString(rexec_dict, "ok_posix_names", PLy_ok_posix_names); + PyDict_SetItemString(rexec_dict, "ok_sys_names", PLy_ok_sys_names); + + /* + * change the r_open behavior + */ + if( populate_methods(rexec, PLy_r_exec_methods) ) + PLy_elog(ERROR, "Failed to update RExec methods."); +} + +/* Helper function to build tuples from string lists */ +static +PyObject *build_tuple(char* string_list[], int len) +{ + PyObject *tup = PyTuple_New(len); + int i; + for (i = 0; i < len; i++) + { + PyObject *m = PyString_FromString(string_list[i]); + + PyTuple_SetItem(tup, i, m); + } + return tup; +} + +/* Helper function for populating a class with method wrappers. */ +static int +populate_methods(PyObject *klass, PyMethodDef *methods) +{ + if (!klass || !methods) + return 0; + + for ( ; methods->ml_name; ++methods) { + + /* get a wrapper for the built-in function */ + PyObject *func = PyCFunction_New(methods, NULL); + PyObject *meth; + int status; + + if (!func) + return -1; + + /* turn the function into an unbound method */ + if (!(meth = PyMethod_New(func, NULL, klass))) { + Py_DECREF(func); + return -1; + } + + /* add method to dictionary */ + status = PyDict_SetItemString( ((PyClassObject*)klass)->cl_dict, + methods->ml_name, meth); + Py_DECREF(meth); + Py_DECREF(func); + + /* stop now if an error occurred, otherwise do the next method */ + if (status) + return status; + } + return 0; } Only in postgresql-7.2b2-kbj/src/pl/plpython: plpython.c~ diff -u postgresql-7.2b2/src/pl/plpython/plpython_error.sql postgresql-7.2b2-kbj/src/pl/plpython/plpython_error.sql --- postgresql-7.2b2/src/pl/plpython/plpython_error.sql Wed May 9 15:54:38 2001 +++ postgresql-7.2b2-kbj/src/pl/plpython/plpython_error.sql Sun Nov 11 13:48:27 2001 @@ -7,3 +7,11 @@ SELECT invalid_type_caught('rick'); SELECT invalid_type_reraised('rick'); SELECT valid_type('rick'); + +-- Security sandbox tests +SELECT read_file('/etc/passwd'); +SELECT write_file('/tmp/plpython','This is very bad'); +SELECT getpid(); +SELECT uname(); +SELECT sys_exit(); +SELECT sys_argv(); diff -u postgresql-7.2b2/src/pl/plpython/plpython_function.sql postgresql-7.2b2-kbj/src/pl/plpython/plpython_function.sql --- postgresql-7.2b2/src/pl/plpython/plpython_function.sql Wed May 9 15:54:38 2001 +++ postgresql-7.2b2-kbj/src/pl/plpython/plpython_function.sql Sun Nov 11 13:47:08 2001 @@ -287,5 +287,36 @@ ' LANGUAGE 'plpython'; +CREATE OR REPLACE FUNCTION read_file(text) RETURNS text AS ' + return open(args[0]).read() +' LANGUAGE 'plpython'; +CREATE OR REPLACE FUNCTION write_file(text,text) RETURNS text AS ' + open(args[0],"w").write(args[1]) +' LANGUAGE 'plpython'; + +CREATE OR REPLACE FUNCTION getpid() RETURNS int4 AS ' + import os + return os.getpid() +' LANGUAGE 'plpython'; + +CREATE OR REPLACE FUNCTION uname() RETURNS int4 AS ' + import os + return os.uname() +' LANGUAGE 'plpython'; + +CREATE OR REPLACE FUNCTION sys_exit() RETURNS text AS ' + import sys + return sys.exit() +' LANGUAGE 'plpython'; + +CREATE OR REPLACE FUNCTION sys_argv() RETURNS text AS ' + import sys + return str(sys.argv) +' LANGUAGE 'plpython'; + +CREATE OR REPLACE FUNCTION sys_version() RETURNS text AS ' + import sys + return str(sys.version) +' LANGUAGE 'plpython';