Skip site navigation (1)Skip section navigation (2)
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>