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

Reply via email to