Hello misc, I wanted to look into Python C extensions and as I also wanted to look at pledge, the following was the result.
$ cat test_pypledge_success.py import pypledge pypledge.pledge("stdio", None) print("Hello OpenBSD!") $ python3.4 test_pypledge_success.py Hello OpenBSD! $ cat test_pypledge_AbortTrap.py import pypledge pypledge.pledge("", None) print("Hello OpenBSD!") $ python3.4 test_pypledge_AbortTrap.py Abort trap (core dumped) (ktrace shows the pledging and the aborting nicely) $ cat test_pypledge_EPERM.py import pypledge pypledge.pledge("stdio rpath", None); pypledge.pledge("stdio rpath dns", None); print("Hello OpenBSD!") $ python 3.4 test_pypledge_EPERM.py Traceback (most recent call last): File "test_pypledge_EPERM.py", line 4, in <module> pypledge.pledge("stdio rpath dns", None); pypledge.PledgeError: EPERM (please notice that throwing errors requires stdio and rpath for python3.4, else throwing the error will get you an abort trap). As Python isn't doing fancy forking and most of the heavy bytecode generating and other maintenance has happened already when I reach the pledge line, this appears to work (I'm actually more interested in the nice whitepath feature for some scripts of mine when it's ready). I don't know if this makes any sense (it probably won't for large projects) or may be of use to anyone as a base for more elaborated stuff, but in case anyone wants to do some experiments or do some test what promises are needed for larger Python stuff (and if its feasible there, you never know what the interpreter is doing in the background), here's the highly experimental module code: #include "/usr/local/include/python3.4m/Python.h" #include <unistd.h> #include <stdio.h> /* Python PledgeError for a more pythonic behaviour. */ static PyObject *PledgeError; /* Visible to the outside, wraps pledge(2). */ static PyObject * pypledge_pledge(PyObject *self, PyObject *args) { PyObject *pyPaths; PyObject *item; PyObject *asciiItem; int arrSize, idx; const char *promises; const char **paths; int result; if(!PyArg_ParseTuple(args, "sO", &promises, &pyPaths)) return NULL; if(pyPaths == Py_None) paths = NULL; else { if(!PySequence_Check(pyPaths)) { PyErr_SetString(PyExc_TypeError, "expected a list"); return NULL; } arrSize = PyObject_Length(pyPaths); if(arrSize == 0) paths = NULL; else { paths = malloc(sizeof(const char*) * arrSize); for(idx = 0; idx < arrSize; idx++) { item = PySequence_GetItem(pyPaths, idx); if(item == NULL) paths[idx] = NULL; else { asciiItem = PyUnicode_AsASCIIString(item); paths[idx] = PyBytes_AsString(asciiItem); } } } } result = pledge(promises, paths); if(result < 0) { if(errno == EFAULT) PyErr_SetString(PledgeError, "EFAULT"); else if(errno == EINVAL) PyErr_SetString(PledgeError, "EINVAL"); else if(errno == ENAMETOOLONG) PyErr_SetString(PledgeError, "ENAMETOOLONG"); else if(errno == EPERM) PyErr_SetString(PledgeError, "EPERM"); else if(errno == E2BIG) PyErr_SetString(PledgeError, "E2BIG"); else PyErr_SetString(PledgeError, "unknown errno from pledge(2)"); free(paths); return NULL; } free(paths); Py_INCREF(Py_None); return Py_None; } /* Methods exported to Python. */ static PyMethodDef PyPledgeMethods[] = { {"pledge", pypledge_pledge, METH_VARARGS, "Restrict system operations."}, {NULL, NULL, 0, NULL} }; /* Module definition */ static struct PyModuleDef pypledge = { PyModuleDef_HEAD_INIT, "pypledge", NULL, -1, PyPledgeMethods }; /* Entry point for Python. */ PyMODINIT_FUNC PyInit_pypledge(void) { PyObject *module; module = PyModule_Create(&pypledge); if(module == NULL) return NULL; PledgeError = PyErr_NewException("pypledge.PledgeError", NULL, NULL); Py_INCREF(PledgeError); PyModule_AddObject(module, "PledgeError", PledgeError); return module; } I didn't expect it to work even for basic examples, so I wanted to share this as I was surprised that it was actually simple up to this point. Best regards, Tobias