Date: Mon, 25 May 2026 17:48:22 +0000 From: =?utf-8?Q?Jes=C3=BAs?= Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> To: ports-committers@FreeBSD.org, dev-commits-ports-all@FreeBSD.org, dev-commits-ports-main@FreeBSD.org Subject: git: d310b4123bef - main - security/py-PAM: new port: Python interface to the PAM library Message-ID: <6a148b66.3a9b2.4093382f@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by dtxdf: URL: https://cgit.FreeBSD.org/ports/commit/?id=d310b4123beff9f623f688e831b0b636794968e6 commit d310b4123beff9f623f688e831b0b636794968e6 Author: Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> AuthorDate: 2026-05-25 17:42:52 +0000 Commit: Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> CommitDate: 2026-05-25 17:47:37 +0000 security/py-PAM: new port: Python interface to the PAM library This module makes the PAM (Pluggable Authentication Modules) functions available in Python 3. With this module you can write Python 3 applications that implement authentication services using PAM. WWW: https://packages.debian.org/sid/python3-pam --- security/Makefile | 1 + security/py-PAM/Makefile | 36 ++++ security/py-PAM/distinfo | 3 + security/py-PAM/files/patch-PAMmodule.c | 219 ++++++++++++++++++++++++ security/py-PAM/files/patch-examples_pamtest.py | 69 ++++++++ security/py-PAM/files/patch-setup.py | 19 ++ security/py-PAM/pkg-descr | 3 + 7 files changed, 350 insertions(+) diff --git a/security/Makefile b/security/Makefile index e16c296ee353..72e050a741ef 100644 --- a/security/Makefile +++ b/security/Makefile @@ -880,6 +880,7 @@ SUBDIR += pwdsafety SUBDIR += pwman SUBDIR += pwned-check + SUBDIR += py-PAM SUBDIR += py-SecretStorage SUBDIR += py-YubiOTP SUBDIR += py-acme diff --git a/security/py-PAM/Makefile b/security/py-PAM/Makefile new file mode 100644 index 000000000000..6b931b7b3cd5 --- /dev/null +++ b/security/py-PAM/Makefile @@ -0,0 +1,36 @@ +PORTNAME= PAM +DISTVERSION= 0.4.2 +CATEGORIES= security python +MASTER_SITES= ${MASTER_SITE_DEBIAN} +MASTER_SITE_SUBDIR= pool/main/p/python-pam +PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX} +DISTNAME= python-pam_${DISTVERSION}.orig + +MAINTAINER= dtxdf@FreeBSD.org +COMMENT= Python interface to the PAM library +WWW= https://packages.debian.org/sid/python3-pam + +LICENSE= GPLv3 +LICENSE_FILE= ${WRKSRC}/COPYING + +USES= python shebangfix +USE_PYTHON= allflavors concurrent distutils + +SHEBANG_FILES= examples/pamtest.py + +WRKSRC= ${WRKDIR}/python-pam-${DISTVERSION} + +PLIST_FILES= ${PYTHON_SITELIBDIR}/PAM${PYTHON_TAG}.so + +OPTIONS_DEFINE= EXAMPLES + +EXAMPLES_PLIST_FILES= ${EXAMPLESDIR}/pamtest.py + +do-install-EXAMPLES-on: + @${MKDIR} ${STAGEDIR}${EXAMPLESDIR} + ${INSTALL_SCRIPT} ${WRKSRC}/examples/pamtest.py ${STAGEDIR}${EXAMPLESDIR} + +post-install: + ${STRIP_CMD} ${STAGEDIR}${PYTHON_SITELIBDIR}/PAM${PYTHON_TAG}.so + +.include <bsd.port.mk> diff --git a/security/py-PAM/distinfo b/security/py-PAM/distinfo new file mode 100644 index 000000000000..c9c219b53c31 --- /dev/null +++ b/security/py-PAM/distinfo @@ -0,0 +1,3 @@ +TIMESTAMP = 1779580269 +SHA256 (python-pam_0.4.2.orig.tar.gz) = 9ccce2e494c5869d99b20034fd40e368c35add4ef60ce3a33f5573c49a1e2edf +SIZE (python-pam_0.4.2.orig.tar.gz) = 15115 diff --git a/security/py-PAM/files/patch-PAMmodule.c b/security/py-PAM/files/patch-PAMmodule.c new file mode 100644 index 000000000000..6b45e890c059 --- /dev/null +++ b/security/py-PAM/files/patch-PAMmodule.c @@ -0,0 +1,219 @@ +--- PAMmodule.c.orig 2026-05-24 01:06:49 UTC ++++ PAMmodule.c +@@ -10,7 +10,7 @@ + + #include <Python.h> + #include <security/pam_appl.h> +-#include <security/pam_misc.h> ++#include <security/openpam.h> + + static PyObject *PyPAM_Error; + +@@ -24,7 +24,7 @@ typedef struct { + PyObject *userData; + } PyPAMObject; + +-staticforward PyTypeObject PyPAMObject_Type; ++static PyTypeObject PyPAMObject_Type; + + static void PyPAM_Err(PyPAMObject *self, int result) + { +@@ -60,7 +60,7 @@ static int PyPAM_conv(int num_msg, const struct pam_me + } + + args = Py_BuildValue("(OOO)", self, msgList, self->userData); +- respList = PyEval_CallObject(self->callback, args); ++ respList = PyObject_CallObject(self->callback, args); + Py_DECREF(args); + Py_DECREF(self); + +@@ -80,6 +80,7 @@ static int PyPAM_conv(int num_msg, const struct pam_me + resp_retcode = 0; + if (!PyArg_ParseTuple(respTuple, "si", &resp_text, &resp_retcode)) { + free(*resp); ++ *resp = NULL; + Py_DECREF(respList); + return PAM_CONV_ERR; + } +@@ -92,7 +93,7 @@ static struct pam_conv default_conv = { + } + + static struct pam_conv default_conv = { +- misc_conv, ++ openpam_ttyconv, + NULL + }; + +@@ -101,12 +102,18 @@ static struct pam_conv python_conv = { + NULL + }; + ++#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) ++static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) ++{ ob->ob_type = type; } ++#define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type) ++#endif ++ + static PyObject * PyPAM_pam(PyObject *self, PyObject *args) + { + PyPAMObject *p; + struct pam_conv *spc; + +- PyPAMObject_Type.ob_type = &PyType_Type; ++ Py_SET_TYPE(&PyPAMObject_Type, &PyType_Type); + p = (PyPAMObject *) PyObject_NEW(PyPAMObject, &PyPAMObject_Type); + + if ((spc = (struct pam_conv *) malloc(sizeof(struct pam_conv))) == NULL) { +@@ -313,7 +320,7 @@ static PyObject * PyPAM_set_item(PyObject *self, PyObj + if (item == PAM_SERVICE) _self->service = n_val; + result = pam_set_item(_self->pamh, item, (void *) n_val); + } else { +- PyErr_Clear(); ++ PyErr_Clear(); + if (PyArg_ParseTuple(args, "iO:set_callback", &item, &o_val)) { + if (item == PAM_CONV && !PyCallable_Check(o_val)) { + PyErr_SetString(PyExc_TypeError, "parameter must be a function"); +@@ -489,35 +496,33 @@ static void PyPAM_dealloc(PyPAMObject *self) + PyObject_FREE(self); + } + +-static PyObject * PyPAM_getattr(PyPAMObject *self, char *name) +-{ +- return Py_FindMethod(PyPAMObject_Methods, (PyObject *) self, name); +-} +- + static PyObject * PyPAM_repr(PyPAMObject *self) + { + char buf[1024]; + + snprintf(buf, 1024, "<pam object, service=\"%s\", user=\"%s\", conv=%p, pamh=%p>", + self->service, self->user, self->conv, self->pamh); +- return PyString_FromString(buf); ++ return PyUnicode_FromString(buf); + } + + static PyTypeObject PyPAMObject_Type = { +- PyObject_HEAD_INIT(0) /* Must fill in type value later */ +- 0, ++ PyVarObject_HEAD_INIT(&PyType_Type, 0) /* Must fill in type value later */ + "pam", + sizeof(PyPAMObject), + 0, + (destructor)PyPAM_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ +- (getattrfunc)PyPAM_getattr, /*tp_getattr*/ ++ 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)PyPAM_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ ++ 0, /*hash*/ ++ 0, /*ternary*/ ++ 0, /*another repr*/ ++ (getattrofunc)PyObject_GenericGetAttr, + }; + + static PyMethodDef PyPAM_Methods[] = { +@@ -525,6 +530,16 @@ static PyMethodDef PyPAM_Methods[] = { + {NULL, NULL, 0, NULL} + }; + ++#if PY_MAJOR_VERSION > 2 ++static struct PyModuleDef PyPAM_Module = { ++ PyModuleDef_HEAD_INIT, ++ "PAM", /* name of module */ ++ NULL, /* module documentation */ ++ -1, /* size of per-interpreter state */ ++ PyPAM_Methods ++}; ++#endif ++ + static char PyPAMObject_doc[] = ""; + + /* Convenience routine to export an integer value. +@@ -534,7 +549,11 @@ static void insint(PyObject *d, char *name, int value) + */ + static void insint(PyObject *d, char *name, int value) + { ++#if PY_MAJOR_VERSION > 2 ++ PyObject *v = PyLong_FromLong((long) value); ++#else + PyObject *v = PyInt_FromLong((long) value); ++#endif + + if (!v || PyDict_SetItemString(d, name, v)) + PyErr_Clear(); +@@ -542,20 +561,32 @@ static void insint(PyObject *d, char *name, int value) + Py_XDECREF(v); + } + ++#if PY_MAJOR_VERSION > 2 ++PyMODINIT_FUNC PyInit_PAM(void) ++#else + void initPAM(void) ++#endif + { + PyObject *m, *d; + ++#if PY_MAJOR_VERSION > 2 ++ m = PyModule_Create(&PyPAM_Module); ++#else + m = Py_InitModule("PAM", PyPAM_Methods); ++#endif + d = PyModule_GetDict(m); + + PyPAM_Error = PyErr_NewException("PAM.error", NULL, NULL); + if (PyPAM_Error == NULL) ++#if PY_MAJOR_VERSION > 2 ++ return m; ++#else + return; ++#endif + PyDict_SetItemString(d, "error", PyPAM_Error); + +- PyPAMObject_Type.ob_type = &PyType_Type; + PyPAMObject_Type.tp_doc = PyPAMObject_doc; ++ PyPAMObject_Type.tp_methods = PyPAMObject_Methods, + Py_INCREF(&PyPAMObject_Type); + + insint(d, "PAM_SUCCESS", PAM_SUCCESS); +@@ -579,7 +610,7 @@ void initPAM(void) + insint(d, "PAM_NO_MODULE_DATA", PAM_NO_MODULE_DATA); + insint(d, "PAM_CONV_ERR", PAM_CONV_ERR); + insint(d, "PAM_AUTHTOK_ERR", PAM_AUTHTOK_ERR); +- insint(d, "PAM_AUTHTOK_RECOVER_ERR", PAM_AUTHTOK_RECOVER_ERR); ++ insint(d, "PAM_AUTHTOK_RECOVER_ERR", PAM_AUTHTOK_RECOVERY_ERR); + insint(d, "PAM_AUTHTOK_LOCK_BUSY", PAM_AUTHTOK_LOCK_BUSY); + insint(d, "PAM_AUTHTOK_DISABLE_AGING", PAM_AUTHTOK_DISABLE_AGING); + insint(d, "PAM_TRY_AGAIN", PAM_TRY_AGAIN); +@@ -588,7 +619,7 @@ void initPAM(void) + insint(d, "PAM_AUTHTOK_EXPIRED", PAM_AUTHTOK_EXPIRED); + insint(d, "PAM_MODULE_UNKNOWN", PAM_MODULE_UNKNOWN); + insint(d, "PAM_BAD_ITEM", PAM_BAD_ITEM); +- insint(d, "_PAM_RETURN_VALUES", _PAM_RETURN_VALUES); ++ insint(d, "_PAM_RETURN_VALUES", PAM_NUM_ERRORS); + + insint(d, "PAM_SILENT", PAM_SILENT); + insint(d, "PAM_DISALLOW_NULL_AUTHTOK", PAM_DISALLOW_NULL_AUTHTOK); +@@ -607,7 +638,7 @@ void initPAM(void) + insint(d, "PAM_RUSER", PAM_RUSER); + insint(d, "PAM_USER_PROMPT", PAM_USER_PROMPT); + +- insint(d, "PAM_DATA_SILENT", PAM_DATA_SILENT); ++ insint(d, "PAM_DATA_SILENT", 0); + + insint(d, "PAM_PROMPT_ECHO_OFF", PAM_PROMPT_ECHO_OFF); + insint(d, "PAM_PROMPT_ECHO_ON", PAM_PROMPT_ECHO_ON); +@@ -617,6 +648,10 @@ void initPAM(void) + insint(d, "PAM_RADIO_TYPE", PAM_RADIO_TYPE); + insint(d, "PAM_BINARY_MSG", PAM_BINARY_MSG); + insint(d, "PAM_BINARY_PROMPT", PAM_BINARY_PROMPT); ++#endif ++ ++#if PY_MAJOR_VERSION > 2 ++ return m; + #endif + + } diff --git a/security/py-PAM/files/patch-examples_pamtest.py b/security/py-PAM/files/patch-examples_pamtest.py new file mode 100644 index 000000000000..f70063e4f35a --- /dev/null +++ b/security/py-PAM/files/patch-examples_pamtest.py @@ -0,0 +1,69 @@ +--- examples/pamtest.py.orig 2026-05-24 16:03:37 UTC ++++ examples/pamtest.py +@@ -6,42 +6,42 @@ def pam_conv(auth, query_list, userData): + + def pam_conv(auth, query_list, userData): + +- resp = [] ++ resp = [] + +- for i in range(len(query_list)): +- query, type = query_list[i] +- if type == PAM.PAM_PROMPT_ECHO_ON: +- val = raw_input(query) +- resp.append((val, 0)) +- elif type == PAM.PAM_PROMPT_ECHO_OFF: +- val = getpass(query) +- resp.append((val, 0)) +- elif type == PAM.PAM_PROMPT_ERROR_MSG or type == PAM.PAM_PROMPT_TEXT_INFO: +- print query +- resp.append(('', 0)) +- else: +- return None ++ for i in range(len(query_list)): ++ query, type = query_list[i] ++ if type == PAM.PAM_PROMPT_ECHO_ON: ++ val = raw_input(query) ++ resp.append((val, 0)) ++ elif type == PAM.PAM_PROMPT_ECHO_OFF: ++ val = getpass(query) ++ resp.append((val, 0)) ++ elif type == PAM.PAM_ERROR_MSG or type == PAM.PAM_TEXT_INFO: ++ print(query) ++ resp.append(('', 0)) ++ else: ++ return None + +- return resp ++ return resp + + service = 'passwd' + + if len(sys.argv) == 2: +- user = sys.argv[1] ++ user = sys.argv[1] + else: +- user = None ++ user = None + + auth = PAM.pam() + auth.start(service) + if user != None: +- auth.set_item(PAM.PAM_USER, user) ++ auth.set_item(PAM.PAM_USER, user) + auth.set_item(PAM.PAM_CONV, pam_conv) + try: +- auth.authenticate() +- auth.acct_mgmt() +-except PAM.error, resp: +- print 'Go away! (%s)' % resp ++ auth.authenticate() ++ auth.acct_mgmt() ++except PAM.error as resp: ++ print('Go away! (%s)' % resp) + except: +- print 'Internal error' ++ print('Internal error') + else: +- print 'Good to go!' ++ print('Good to go!') diff --git a/security/py-PAM/files/patch-setup.py b/security/py-PAM/files/patch-setup.py new file mode 100644 index 000000000000..74cad71ab52c --- /dev/null +++ b/security/py-PAM/files/patch-setup.py @@ -0,0 +1,19 @@ +--- setup.py.orig 2026-05-24 01:06:41 UTC ++++ setup.py +@@ -2,13 +2,11 @@ + + """Setup script for the Python-PAM module distribution.""" + +-import distutils +-from distutils.core import setup +-from distutils.extension import Extension ++from setuptools import setup, Extension + + ext = Extension( +- name="PAMmodule", +- libraries=["pam","pam_misc"], ++ name="PAM", ++ libraries=["pam"], + sources=["PAMmodule.c"] + ) + ##print ext.__dict__; sys.exit(1) diff --git a/security/py-PAM/pkg-descr b/security/py-PAM/pkg-descr new file mode 100644 index 000000000000..a0672f1ee34d --- /dev/null +++ b/security/py-PAM/pkg-descr @@ -0,0 +1,3 @@ +This module makes the PAM (Pluggable Authentication Modules) functions +available in Python 3. With this module you can write Python 3 +applications that implement authentication services using PAM.home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a148b66.3a9b2.4093382f>
