Index: src/pl/plpython/error.expected =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plpython/error.expected,v retrieving revision 1.1 diff -c -r1.1 error.expected *** src/pl/plpython/error.expected 2001/05/09 19:54:38 1.1 --- src/pl/plpython/error.expected 2001/11/15 18:38:01 *************** *** 1,19 **** SELECT invalid_type_uncaught('rick'); ! ERROR: plpython: Call of function `__plpython_procedure_invalid_type_uncaught_1175341' failed. plpy.SPIError: Cache lookup for type `test' failed. SELECT invalid_type_caught('rick'); ! NOTICE: ("Cache lookup for type `test' failed.",) ! invalid_type_caught ! --------------------- ! ! (1 row) ! SELECT invalid_type_reraised('rick'); ! ERROR: plpython: Call of function `__plpython_procedure_invalid_type_reraised_1175343' failed. ! plpy.Error: ("Cache lookup for type `test' failed.",) SELECT valid_type('rick'); valid_type ------------ (1 row) --- 1,36 ---- SELECT invalid_type_uncaught('rick'); ! NOTICE: plpython: in function __plpython_procedure_invalid_type_uncaught_49801: plpy.SPIError: Cache lookup for type `test' failed. SELECT invalid_type_caught('rick'); ! NOTICE: plpython: in function __plpython_procedure_invalid_type_caught_49802: ! plpy.SPIError: Cache lookup for type `test' failed. SELECT invalid_type_reraised('rick'); ! NOTICE: plpython: in function __plpython_procedure_invalid_type_reraised_49803: ! plpy.SPIError: 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_49809' 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_49810' failed. + exceptions.IOError: can't open files in restricted mode + SELECT getpid(); + ERROR: plpython: Call of function `__plpython_procedure_getpid_49811' failed. + exceptions.AttributeError: getpid + SELECT uname(); + ERROR: plpython: Call of function `__plpython_procedure_uname_49812' failed. + exceptions.AttributeError: uname + SELECT sys_exit(); + ERROR: plpython: Call of function `__plpython_procedure_sys_exit_49813' failed. + exceptions.AttributeError: exit + SELECT sys_argv(); + sys_argv + ---------------- + ['RESTRICTED'] (1 row) Index: src/pl/plpython/plpython.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plpython/plpython.c,v retrieving revision 1.12 diff -c -r1.12 plpython.c *** src/pl/plpython/plpython.c 2001/11/05 17:46:39 1.12 --- src/pl/plpython/plpython.c 2001/11/15 18:38:03 *************** *** 188,193 **** --- 188,197 ---- 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 */ *************** *** 199,204 **** --- 203,212 ---- PLy_exception_set(PyObject *, const char *,...) __attribute__((format(printf, 2, 3))); + /* Get the innermost python procedure called from the backend. + */ + static char *PLy_procedure_name(PLyProcedure *); + /* some utility functions */ static void *PLy_malloc(size_t); *************** *** 240,245 **** --- 248,257 ---- 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); *************** *** 255,260 **** --- 267,277 ---- static int PLy_first_call = 1; static volatile int PLy_call_level = 0; + /* + * Last function called by postgres backend + */ + static PLyProcedure *PLy_last_procedure = NULL; + /* this gets modified in plpython_call_handler and PLy_elog. * test it any old where, but do NOT modify it anywhere except * those two functions *************** *** 265,275 **** static PyObject *PLy_interp_safe = NULL; static PyObject *PLy_interp_safe_globals = NULL; static PyObject *PLy_importable_modules = NULL; static PyObject *PLy_procedure_cache = NULL; ! char *PLy_importable_modules_list[] = { "array", "bisect", "calendar", "cmath", "errno", --- 282,295 ---- 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; ! static char *PLy_importable_modules_list[] = { "array", "bisect", + "binascii", "calendar", "cmath", "errno", *************** *** 278,299 **** "md5", "mpz", "operator", "pickle", "random", "re", "sha", "string", "StringIO", "time", "whrandom", "zlib" }; /* Python exceptions */ ! PyObject *PLy_exc_error = NULL; ! PyObject *PLy_exc_fatal = NULL; ! PyObject *PLy_exc_spi_error = NULL; /* some globals for the python module */ --- 298,339 ---- "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", + "version", + "version_info" + }; + /* Python exceptions */ ! 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 */ *************** *** 334,340 **** fmgr_info_cxt(functionId, finfo, TopMemoryContext); } - Datum plpython_call_handler(PG_FUNCTION_ARGS) { --- 374,379 ---- *************** *** 366,373 **** } else PLy_restart_in_progress += 1; ! if (proc) Py_DECREF(proc->me); RERAISE_EXC(); } --- 405,414 ---- } else PLy_restart_in_progress += 1; ! if (proc) ! { Py_DECREF(proc->me); + } RERAISE_EXC(); } *************** *** 853,863 **** --- 894,908 ---- PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs) { PyObject *rv; + PLyProcedure *current; enter(); + current = PLy_last_procedure; + PLy_last_procedure = proc; PyDict_SetItemString(proc->globals, kargs, vargs); rv = PyObject_CallFunction(proc->reval, "O", proc->code); + PLy_last_procedure = current; if ((rv == NULL) || (PyErr_Occurred())) { *************** *** 1150,1161 **** 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"); --- 1195,1200 ---- *************** *** 1632,1640 **** --- 1671,1682 ---- static PyObject *PLy_result_new(void); static void PLy_result_dealloc(PyObject *); static PyObject *PLy_result_getattr(PyObject *, char *); + #if 0 + /* Appear to be unused */ static PyObject *PLy_result_fetch(PyObject *, PyObject *); static PyObject *PLy_result_nrows(PyObject *, PyObject *); static PyObject *PLy_result_status(PyObject *, PyObject *); + #endif static int PLy_result_length(PyObject *); static PyObject *PLy_result_item(PyObject *, int); static PyObject *PLy_result_slice(PyObject *, int, int); *************** *** 1650,1656 **** static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int); ! PyTypeObject PLy_PlanType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "PLyPlan", /* tp_name */ --- 1692,1698 ---- static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int); ! static PyTypeObject PLy_PlanType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "PLyPlan", /* tp_name */ *************** *** 1679,1691 **** PLy_plan_doc, /* tp_doc */ }; ! PyMethodDef PLy_plan_methods[] = { {"status", (PyCFunction) PLy_plan_status, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; ! PySequenceMethods PLy_result_as_sequence = { (inquiry) PLy_result_length, /* sq_length */ (binaryfunc) 0, /* sq_concat */ (intargfunc) 0, /* sq_repeat */ --- 1721,1733 ---- PLy_plan_doc, /* tp_doc */ }; ! static PyMethodDef PLy_plan_methods[] = { {"status", (PyCFunction) PLy_plan_status, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; ! static PySequenceMethods PLy_result_as_sequence = { (inquiry) PLy_result_length, /* sq_length */ (binaryfunc) 0, /* sq_concat */ (intargfunc) 0, /* sq_repeat */ *************** *** 1695,1701 **** (intintobjargproc) PLy_result_ass_slice, /* sq_ass_slice */ }; ! PyTypeObject PLy_ResultType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "PLyResult", /* tp_name */ --- 1737,1743 ---- (intintobjargproc) PLy_result_ass_slice, /* sq_ass_slice */ }; ! static PyTypeObject PLy_ResultType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "PLyResult", /* tp_name */ *************** *** 1723,1736 **** 0, /* tp_xxx4 */ PLy_result_doc, /* tp_doc */ }; ! ! 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}, {NULL, NULL, 0, NULL} }; ! static PyMethodDef PLy_methods[] = { /* --- 1765,1779 ---- 0, /* tp_xxx4 */ PLy_result_doc, /* tp_doc */ }; ! #if 0 ! /* Appear to be unused */ ! 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}, {NULL, NULL, 0, NULL} }; ! #endif static PyMethodDef PLy_methods[] = { /* *************** *** 1833,1839 **** /* result object methods */ ! static PyObject * PLy_result_new(void) { PLyResultObject *ob; --- 1876,1882 ---- /* result object methods */ ! PyObject * PLy_result_new(void) { PLyResultObject *ob; *************** *** 1853,1859 **** return (PyObject *) ob; } ! static void PLy_result_dealloc(PyObject * arg) { PLyResultObject *ob = (PLyResultObject *) arg; --- 1896,1902 ---- return (PyObject *) ob; } ! void PLy_result_dealloc(PyObject * arg) { PLyResultObject *ob = (PLyResultObject *) arg; *************** *** 1867,1885 **** PyMem_DEL(ob); } ! static PyObject * PLy_result_getattr(PyObject * self, char *attr) { return NULL; } ! ! static PyObject * PLy_result_fetch(PyObject * self, PyObject * args) { return NULL; } ! static PyObject * PLy_result_nrows(PyObject * self, PyObject * args) { PLyResultObject *ob = (PLyResultObject *) self; --- 1910,1929 ---- PyMem_DEL(ob); } ! PyObject * PLy_result_getattr(PyObject * self, char *attr) { return NULL; } ! #if 0 ! /* Appear to be unused */ ! PyObject * PLy_result_fetch(PyObject * self, PyObject * args) { return NULL; } ! PyObject * PLy_result_nrows(PyObject * self, PyObject * args) { PLyResultObject *ob = (PLyResultObject *) self; *************** *** 1888,1894 **** return ob->nrows; } ! static PyObject * PLy_result_status(PyObject * self, PyObject * args) { PLyResultObject *ob = (PLyResultObject *) self; --- 1932,1938 ---- return ob->nrows; } ! PyObject * PLy_result_status(PyObject * self, PyObject * args) { PLyResultObject *ob = (PLyResultObject *) self; *************** *** 1896,1902 **** Py_INCREF(ob->status); return ob->status; } ! int PLy_result_length(PyObject * arg) { --- 1940,1946 ---- Py_INCREF(ob->status); return ob->status; } ! #endif int PLy_result_length(PyObject * arg) { *************** *** 1991,1997 **** if (!PyErr_Occurred()) PyErr_SetString(PLy_exc_spi_error, "Unknown error in PLy_spi_prepare."); ! return NULL; } if (list != NULL) --- 2035,2042 ---- if (!PyErr_Occurred()) PyErr_SetString(PLy_exc_spi_error, "Unknown error in PLy_spi_prepare."); ! PLy_elog(NOTICE,"in function %s:",PLy_procedure_name(PLy_last_procedure)); ! RERAISE_EXC(); } if (list != NULL) *************** *** 2187,2193 **** if (!PyErr_Occurred()) PyErr_SetString(PLy_exc_error, "Unknown error in PLy_spi_execute_plan"); ! return NULL; } if (nargs) --- 2232,2239 ---- if (!PyErr_Occurred()) PyErr_SetString(PLy_exc_error, "Unknown error in PLy_spi_execute_plan"); ! PLy_elog(NOTICE,"in function %s:",PLy_procedure_name(PLy_last_procedure)); ! RERAISE_EXC(); } if (nargs) *************** *** 2249,2264 **** if (TRAP_EXC()) { RESTORE_EXC(); - if ((!PLy_restart_in_progress) && (!PyErr_Occurred())) PyErr_SetString(PLy_exc_spi_error, "Unknown error in PLy_spi_execute_query."); ! return NULL; } rv = SPI_exec(query, limit); RESTORE_EXC(); - if (rv < 0) { PLy_exception_set(PLy_exc_spi_error, --- 2295,2309 ---- if (TRAP_EXC()) { RESTORE_EXC(); if ((!PLy_restart_in_progress) && (!PyErr_Occurred())) PyErr_SetString(PLy_exc_spi_error, "Unknown error in PLy_spi_execute_query."); ! PLy_elog(NOTICE,"in function %s:",PLy_procedure_name(PLy_last_procedure)); ! RERAISE_EXC(); } rv = SPI_exec(query, limit); RESTORE_EXC(); if (rv < 0) { PLy_exception_set(PLy_exc_spi_error, *************** *** 2311,2317 **** "Unknown error in PLy_spi_execute_fetch_result"); Py_DECREF(result); PLy_typeinfo_dealloc(&args); ! return NULL; } if (rows) --- 2356,2362 ---- "Unknown error in PLy_spi_execute_fetch_result"); Py_DECREF(result); PLy_typeinfo_dealloc(&args); ! RERAISE_EXC(); } if (rows) *************** *** 2450,2462 **** elog(ERROR, "Unable to init plpy."); } void PLy_init_safe_interp(void) { ! PyObject *rmod; char *rname = "rexec"; ! int i, ! imax; enter(); --- 2495,2527 ---- 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, *rexec, *rexec_dict; char *rname = "rexec"; ! int len; enter(); *************** *** 2467,2488 **** 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]); ! PyTuple_SetItem(PLy_importable_modules, i, m); ! } PLy_interp_safe_globals = PyDict_New(); if (PLy_interp_safe_globals == NULL) PLy_elog(ERROR, "Unable to create shared global dictionary."); } /* the python interface to the elog function * don't confuse these with PLy_elog */ --- 2532,2627 ---- PyDict_SetItemString(PLy_interp_globals, rname, rmod); PLy_interp_safe = rmod; ! len = sizeof(PLy_importable_modules_list) / sizeof(char *); ! PLy_importable_modules = build_tuple(PLy_importable_modules_list, len); ! 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; + } + + /* the python interface to the elog function * don't confuse these with PLy_elog */ *************** *** 2566,2572 **** * hideously. */ elog(FATAL, "plpython: Aiieee, elog threw an unknown exception!"); ! return NULL; } elog(level, sv); --- 2705,2711 ---- * hideously. */ elog(FATAL, "plpython: Aiieee, elog threw an unknown exception!"); ! RERAISE_EXC(); } elog(level, sv); *************** *** 2583,2588 **** --- 2722,2739 ---- return Py_None; } + + /* Get the last procedure name called by the backend ( the innermost, + * If a plpython procedure call calls the backend and the backend calls + * another plpython procedure ) + */ + + char *PLy_procedure_name(PLyProcedure *proc) + { + if ( proc == NULL ) + return ""; + return proc->proname; + } /* output a python traceback/exception via the postgresql elog * function. not pretty. Index: src/pl/plpython/plpython_error.sql =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plpython/plpython_error.sql,v retrieving revision 1.1 diff -c -r1.1 plpython_error.sql *** src/pl/plpython/plpython_error.sql 2001/05/09 19:54:38 1.1 --- src/pl/plpython/plpython_error.sql 2001/11/15 18:38:03 *************** *** 7,9 **** --- 7,17 ---- 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(); Index: src/pl/plpython/plpython_function.sql =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plpython/plpython_function.sql,v retrieving revision 1.1 diff -c -r1.1 plpython_function.sql *** src/pl/plpython/plpython_function.sql 2001/05/09 19:54:38 1.1 --- src/pl/plpython/plpython_function.sql 2001/11/15 18:38:03 *************** *** 257,262 **** --- 257,268 ---- return None ' LANGUAGE 'plpython'; + /* Flat out syntax error + */ + CREATE FUNCTION sql_syntax_error() RETURNS text + AS + 'plpy.execute("syntax error")' + LANGUAGE 'plpython'; /* check the handling of uncaught python exceptions */ *************** *** 287,291 **** ' LANGUAGE 'plpython'; ! --- 293,328 ---- ' 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';