This patch provides Python ucred support. It may be contributed upstream at some point, but the suitability (or lack thereof) has not yet been determined. diff --git Python-2.6.4/Modules/ucred.c Python-2.6.4/Modules/ucred.c new file mode 100644 --- Python-3.9.1/Modules/ucred.c +++ Python-3.9.1/Modules/ucred.c @@ -0,0 +1,404 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + */ + +#include + +#include +#include +#include +#include +#include + +typedef struct { + PyObject_HEAD + ucred_t *ucred; +} pyucred_t; + +#define pyucred_getlongid(name, type) \ + static PyObject * \ + pyucred_get##name(pyucred_t *uc) \ + { \ + type val; \ + \ + if (uc->ucred == NULL) { \ + errno = EINVAL; \ + PyErr_SetFromErrno(PyExc_OSError); \ + return (NULL); \ + } \ + \ + if ((val = ucred_get##name(uc->ucred)) == -1) { \ + PyErr_SetFromErrno(PyExc_OSError); \ + return (NULL); \ + } \ + \ + return (Py_BuildValue("l", (long)val)); \ + } + +pyucred_getlongid(euid, uid_t) +pyucred_getlongid(ruid, uid_t) +pyucred_getlongid(suid, uid_t) +pyucred_getlongid(egid, gid_t) +pyucred_getlongid(rgid, gid_t) +pyucred_getlongid(sgid, gid_t) +pyucred_getlongid(pid, pid_t) +pyucred_getlongid(projid, projid_t) +pyucred_getlongid(zoneid, zoneid_t) + +static PyObject * +pyucred_getgroups(pyucred_t *uc) +{ + const gid_t *groups; + PyObject *list; + int len; + int i; + + if (uc->ucred == NULL) { + errno = EINVAL; + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + if ((len = ucred_getgroups(uc->ucred, &groups)) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + if ((list = PyList_New(len)) == NULL) + return (NULL); + + for (i = 0; i < len; i++) { + PyObject *gid = Py_BuildValue("l", (long)groups[i]); + if (PyList_SetItem(list, i, gid) == -1) + return (NULL); + } + + return (list); +} + +static PyObject * +pyucred_getlabel(pyucred_t *uc) +{ + m_label_t *label; + PyObject *ret; + char *str; + + if (uc->ucred == NULL) { + errno = EINVAL; + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + label = ucred_getlabel(uc->ucred); + if (label == NULL) + return (Py_BuildValue("s", "")); + + if (label_to_str(label, &str, M_LABEL, DEF_NAMES) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + ret = Py_BuildValue("s", str); + free(str); + return (ret); +} + +static PyObject * +pyucred_getpflags(pyucred_t *uc, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "flags", NULL }; + uint_t flags; + + if (uc->ucred == NULL) { + errno = EINVAL; + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, + &flags)) + return (NULL); + + if ((flags = ucred_getpflags(uc->ucred, flags)) == (uint_t)-1) { + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + return (Py_BuildValue("i", flags)); +} + +static PyObject * +pyucred_has_priv(pyucred_t *uc, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "set", "priv", NULL }; + const priv_set_t *privs; + const char *set; + const char *priv; + + if (uc->ucred == NULL) { + errno = EINVAL; + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, + &set, &priv)) + return (NULL); + + if ((privs = ucred_getprivset(uc->ucred, set)) == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + if (priv_ismember(privs, priv)) { + Py_INCREF(Py_True); + return Py_True; + } + + Py_INCREF(Py_False); + return Py_False; +} + +PyDoc_STRVAR(pyucred_getlabel_doc, + "getlabel() -> string\n" + "\n" + "Return the Trusted Extensions label string, or an " + "empty string if not available. The label string is " + "converted using the default name and M_LABEL (human-readable). " + "Raises OSError. See label_to_str(3TSOL)."); +PyDoc_STRVAR(pyucred_getpflags_doc, + "getpflags(flags) -> int\n" + "\n" + "Return the values of the specified privilege flags."); +PyDoc_STRVAR(pyucred_has_priv_doc, + "has_priv(set, priv) -> bool\n" + "\n" + "Return true if the given privilege is set in the " + "specified set. Raises OSError if the set or privilege is " + "invalid, or a problem occurs.\n" + "\n" + "Currently, the following privilege sets are defined, as " + "described in privileges(5):\n" + "\n" + "Effective\n" + "Permitted\n" + "Inheritable\n" + "Limit\n"); + +static PyMethodDef pyucred_methods[] = { + { "geteuid", (PyCFunction)pyucred_geteuid, METH_NOARGS, + "Return the effective user ID." }, + { "getruid", (PyCFunction)pyucred_getruid, METH_NOARGS, + "Return the real user ID." }, + { "getsuid", (PyCFunction)pyucred_getsuid, METH_NOARGS, + "Return the saved user ID." }, + { "getegid", (PyCFunction)pyucred_getegid, METH_NOARGS, + "Return the effective group ID." }, + { "getrgid", (PyCFunction)pyucred_getrgid, METH_NOARGS, + "Return the real group ID." }, + { "getsgid", (PyCFunction)pyucred_getsgid, METH_NOARGS, + "Return the saved group ID." }, + { "getpid", (PyCFunction)pyucred_getpid, METH_NOARGS, + "Return the effective user ID." }, + { "getprojid", (PyCFunction)pyucred_getprojid, METH_NOARGS, + "Return the project ID." }, + { "getzoneid", (PyCFunction)pyucred_getzoneid, METH_NOARGS, + "Return the zone ID." }, + { "getgroups", (PyCFunction)pyucred_getgroups, METH_NOARGS, + "Return a list of group IDs." }, + { "getlabel", (PyCFunction)pyucred_getlabel, METH_NOARGS, + pyucred_getlabel_doc }, + { "getpflags", (PyCFunction)pyucred_getpflags, + METH_VARARGS|METH_KEYWORDS, pyucred_getpflags_doc }, + { "has_priv", (PyCFunction)pyucred_has_priv, + METH_VARARGS|METH_KEYWORDS, pyucred_has_priv_doc }, + { NULL, NULL } +}; + +static int +pyucred_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + pyucred_t *uc = (pyucred_t *)self; + uc->ucred = NULL; + return (0); +} + +static void +pyucred_dealloc(PyObject *self) +{ + pyucred_t *uc = (pyucred_t *)self; + if (uc->ucred != NULL) + ucred_free(uc->ucred); + Py_TYPE(self)->tp_free(self); +} + +static PyTypeObject pyucred_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "ucred.ucred", /*tp_name*/ + sizeof (pyucred_t), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + pyucred_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "user credentials", /*tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pyucred_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)pyucred_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; + +static PyObject * +pyucred_new(const ucred_t *uc) +{ + pyucred_t *self; + + self = (pyucred_t *)PyObject_CallObject((PyObject *)&pyucred_type, NULL); + + if (self == NULL) + return (NULL); + + self->ucred = (ucred_t *)uc; + + return ((PyObject *)self); +} + +static PyObject * +pyucred_get(PyObject *o, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "pid", NULL }; + ucred_t *ucred = NULL; + int pid; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, + &pid)) + return (NULL); + + ucred = ucred_get(pid); + + if (ucred == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + return (pyucred_new(ucred)); +} + +static PyObject * +pyucred_getpeer(PyObject *o, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "fd", NULL }; + ucred_t *ucred = NULL; + int fd; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, + &fd)) + return (NULL); + + if (getpeerucred(fd, &ucred) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return (NULL); + } + + return (pyucred_new(ucred)); +} + +PyDoc_STRVAR(pyucred_get_doc, + "get(pid) -> ucred\n" + "\n" + "Return the credentials of the specified process ID. " + "Raises OSError. See ucred_get(3C)."); +PyDoc_STRVAR(pyucred_getpeer_doc, + "getpeer(fd) -> ucred\n" + "\n" + "Return the credentials of the peer endpoint of a " + "connection-oriented socket (SOCK_STREAM) or STREAM fd " + "at the time the endpoint was created or the connection " + "was established. Raises OSError. See getpeerucred(3C)."); + +static struct PyMethodDef pyucred_module_methods[] = { + { "get", (PyCFunction) pyucred_get, + METH_VARARGS|METH_KEYWORDS, pyucred_get_doc }, + { "getpeer", (PyCFunction) pyucred_getpeer, + METH_VARARGS|METH_KEYWORDS, pyucred_getpeer_doc }, + { NULL, NULL, 0, NULL } +}; + +PyDoc_STRVAR(pyucred_module_doc, + "This module provides an interface to the user credential access " + "methods, obtainable either by process ID or file descriptor."); + +PyMODINIT_FUNC +PyInit_ucred (void) +{ + PyObject *m; + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "ucred", + pyucred_module_doc, + -1, + pyucred_module_methods, + NULL, + NULL, + NULL, + NULL, + }; + + m = PyModule_Create(&moduledef); + + pyucred_type.tp_new = PyType_GenericNew; + if (PyType_Ready(&pyucred_type) < 0) + return NULL; + + Py_INCREF(&pyucred_type); + + PyModule_AddObject(m, "ucred", (PyObject *)&pyucred_type); + + return m; +} --- Python-3.9.1/setup.py +++ Python-3.9.1/setup.py @@ -1814,6 +1814,13 @@ class PyBuildExt(build_ext): else: self.missing.append('_uuid') + def detect_ucred(self): + # ucred module (Solaris) + ucred_inc = find_file('ucred.h', [], self.inc_dirs) + tsol_inc = find_file('tsol/label.h', [], self.inc_dirs) + if ucred_inc is not None and tsol_inc is not None: + self.add(Extension('ucred', ['ucred.c'], libraries=['tsol'])) + def detect_modules(self): self.configure_compiler() self.init_inc_lib_dirs() @@ -1834,6 +1841,7 @@ class PyBuildExt(build_ext): self.detect_expat_elementtree() self.detect_multibytecodecs() self.detect_decimal() + self.detect_ucred() self.detect_ctypes() self.detect_multiprocessing() if not self.detect_tkinter(): --- Python-3.9.1/Lib/test/ucredtest.py +++ Python-3.9.1/Lib/test/ucredtest.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3.9 + +import ucred +import os + +uc = ucred.get(os.getpid()) + +print("pid = %d" % uc.getpid()) +print("euid = %d" % uc.geteuid()) +print("ruid = %d" % uc.getruid()) +print("suid = %d" % uc.getsuid()) +print("egid = %d" % uc.getegid()) +print("rgid = %d" % uc.getrgid()) +print("sgid = %d" % uc.getsgid()) +print("zoneid = %d" % uc.getzoneid()) +print("projid = %d" % uc.getprojid()) +print("groups = %s" % uc.getgroups()) +print("label = %s" % uc.getlabel()) + +print("getpflags(0x1) = %d" % uc.getpflags(0x1)) +print("getpflags(0x2) = %d" % uc.getpflags(0x2)) +print("has_priv(Effective, proc_fork) = %d" % uc.has_priv("Effective", "proc_fork")) +print("has_priv(Permitted, proc_fork) = %d" % uc.has_priv("Permitted", "proc_fork")) +print("has_priv(Inheritable, proc_fork) = %d" % uc.has_priv("Inheritable", "proc_fork")) +print("has_priv(Limit, file_setid) = %d" % uc.has_priv("Limit", "file_setid")) +print("has_priv(Effective, file_setid) = %d" % uc.has_priv("Effective", "file_setid")) +try: + uc.has_priv("Effective", "proc_bork") +except OSError as e: + print(e) +try: + uc.has_priv("Defective", "proc_fork") +except OSError as e: + print(e) +try: + uc.has_priv("Defective", "proc_bork") +except OSError as e: + print(e) + +del uc +uc = ucred.ucred() +try: + uc.getpid() +except OSError as e: + print(e)