6 * Copyright (c) 1999, 2006 Rob Riggs and tummy.com, Ltd. All rights reserved.
7 * Released under GNU LGPL version 2.1.
10 static char revision[] = "$Id: PAMmodule.c,v 1.3 2007/04/18 03:55:11 rob Exp $";
12 #include <security/pam_appl.h>
13 #include <security/pam_misc.h>
18 static PyObject *PyPAM_Error;
22 struct pam_conv *conv;
27 struct pam_response *response_data;
33 staticforward PyTypeObject PyPAMObject_Type;
35 static void PyPAM_Err(PyPAMObject *self, int result)
40 err_msg = pam_strerror(self->pamh, result);
41 error = Py_BuildValue("(si)", err_msg, result);
42 Py_INCREF(PyPAM_Error);
43 PyErr_SetObject(PyPAM_Error, error);
46 static int PyPAM_conv(int num_msg, const struct pam_message **msg,
47 struct pam_response **resp, void *appdata_ptr)
51 PyPAMObject* self = (PyPAMObject *) appdata_ptr;
52 if (self->callback == NULL)
57 if (NULL != self->response_data) {
58 for (int i = 0; i < self->response_len; i++) {
59 free(self->response_data[0].resp);
61 free(self->response_data);
62 self->response_data = NULL;
63 self->response_len = 0;
66 PyObject* msgList = PyList_New(num_msg);
68 for (int i = 0; i < num_msg; i++) {
69 PyList_SetItem(msgList, i,
70 Py_BuildValue("(si)", msg[i]->msg, msg[i]->msg_style));
73 args = Py_BuildValue("(OO)", self, msgList);
74 PyObject* respList = PyEval_CallObject(self->callback, args);
81 if (!PyList_Check(respList)) {
86 *resp = (struct pam_response *) malloc(
87 PyList_Size(respList) * sizeof(struct pam_response));
89 struct pam_response* spr = *resp;
90 for (int i = 0; i < PyList_Size(respList); i++, spr++) {
91 PyObject* respTuple = PyList_GetItem(respList, i);
94 if (!PyArg_ParseTuple(respTuple, "si", &resp_text, &resp_retcode)) {
99 spr->resp = strdup(resp_text);
100 spr->resp_retcode = resp_retcode;
101 Py_DECREF(respTuple);
104 // Save this so we can free it later.
105 self->response_data = *resp;
106 self->response_len = PyList_Size(respList);
113 static struct pam_conv default_conv = {
118 static struct pam_conv python_conv = {
123 static PyObject * PyPAM_pam(PyObject *self, PyObject *args)
126 struct pam_conv *spc;
128 if (!PyArg_ParseTuple(args, "")) {
129 PyErr_SetString(PyExc_TypeError, "pam() takes no arguments");
133 PyPAMObject_Type.ob_type = &PyType_Type;
134 p = (PyPAMObject *) PyObject_NEW(PyPAMObject, &PyPAMObject_Type);
136 if ((spc = (struct pam_conv *) malloc(sizeof(struct pam_conv))) == NULL) {
137 PyErr_SetString(PyExc_MemoryError, "out of memory");
146 p->callback = Py_None;
147 p->response_data = NULL;
150 p->user_data = Py_None;
152 p->dlh1 = dlopen("libpam.so", RTLD_LAZY | RTLD_GLOBAL);
153 p->dlh2 = dlopen("libpam_misc.so", RTLD_LAZY | RTLD_GLOBAL);
155 return (PyObject *) p;
158 static PyObject * PyPAM_start(PyObject *self, PyObject *args)
161 char *service = NULL, *user = NULL;
162 PyObject *callback = NULL;
163 PyPAMObject *_self = (PyPAMObject *) self;
165 if (!PyArg_ParseTuple(args, "s|zO", &service, &user, &callback)) {
166 PyErr_SetString(PyExc_TypeError, "start(service, [user, [callback]])");
170 if (callback != NULL && !PyCallable_Check(callback)) {
173 "the callback parameter must be a function"
178 if (service) _self->service = strdup(service);
179 if (user) _self->user = strdup(user);
181 Py_DECREF(_self->callback);
184 _self->callback = callback;
185 memcpy(_self->conv, &python_conv, sizeof(struct pam_conv));
186 _self->conv->appdata_ptr = (void *) self;
189 _self->callback = Py_None;
190 memcpy(_self->conv, &default_conv, sizeof(struct pam_conv));
193 result = pam_start(_self->service, _self->user, _self->conv, &_self->pamh);
195 if (result != PAM_SUCCESS) {
196 PyPAM_Err(_self, result);
205 static PyObject * PyPAM_authenticate(PyObject *self, PyObject *args)
207 int result, flags = 0;
208 PyPAMObject* _self = (PyPAMObject*) self;
210 if (!PyArg_ParseTuple(args, "|i", &flags)) {
211 PyErr_SetString(PyExc_TypeError, "parameter must be integer");
215 result = pam_authenticate(_self->pamh, flags);
217 if (result != PAM_SUCCESS) {
218 PyPAM_Err(_self, result);
227 static PyObject * PyPAM_setcred(PyObject *self, PyObject *args)
229 int result, flags = 0;
230 PyPAMObject *_self = (PyPAMObject *) self;
232 if (!PyArg_ParseTuple(args, "i", &flags)) {
233 PyErr_SetString(PyExc_TypeError, "parameter must be integer");
237 result = pam_setcred(_self->pamh, flags);
239 if (result != PAM_SUCCESS) {
240 PyErr_SetString(PyPAM_Error, "Not authenticated");
249 static PyObject * PyPAM_acct_mgmt(PyObject *self, PyObject *args)
251 int result, flags = 0;
252 PyPAMObject *_self = (PyPAMObject *) self;
254 if (!PyArg_ParseTuple(args, "|i", &flags)) {
255 PyErr_SetString(PyExc_TypeError, "parameter must be integer");
259 result = pam_acct_mgmt(_self->pamh, flags);
261 if (result != PAM_SUCCESS) {
262 PyErr_SetString(PyPAM_Error, "Not authenticated");
271 static PyObject * PyPAM_chauthtok(PyObject *self, PyObject *args)
273 int result, flags = 0;
274 PyPAMObject *_self = (PyPAMObject *) self;
276 if (!PyArg_ParseTuple(args, "|i", &flags)) {
277 PyErr_SetString(PyExc_TypeError, "parameter must be integer");
281 result = pam_chauthtok(_self->pamh, flags);
283 if (result != PAM_SUCCESS) {
284 PyErr_SetString(PyPAM_Error, "Not authenticated");
293 static PyObject * PyPAM_open_session(PyObject *self, PyObject *args)
295 int result, flags = 0;
296 PyPAMObject *_self = (PyPAMObject *) self;
298 if (!PyArg_ParseTuple(args, "|i", &flags)) {
299 PyErr_SetString(PyExc_TypeError, "parameter must be integer");
303 result = pam_open_session(_self->pamh, flags);
305 if (result != PAM_SUCCESS) {
306 PyErr_SetString(PyPAM_Error, "Not authenticated");
315 static PyObject * PyPAM_close_session(PyObject *self, PyObject *args)
317 int result, flags = 0;
318 PyPAMObject *_self = (PyPAMObject *) self;
320 if (!PyArg_ParseTuple(args, "|i", &flags)) {
321 PyErr_SetString(PyExc_TypeError, "parameter must be integer");
325 result = pam_close_session(_self->pamh, flags);
327 if (result != PAM_SUCCESS) {
328 PyErr_SetString(PyPAM_Error, "Not authenticated");
337 static PyObject * PyPAM_set_item(PyObject *self, PyObject *args)
342 PyPAMObject *_self = (PyPAMObject *) self;
344 if (PyArg_ParseTuple(args, "is", &item, &s_val)) {
345 n_val = strdup(s_val);
346 if (item == PAM_USER)
348 if (item == PAM_SERVICE)
349 _self->service = n_val;
350 result = pam_set_item(_self->pamh, item, (void *) n_val);
352 // An error occured parsing the tuple. Clear it. Then try to parse
353 // it a different way.
355 if (PyArg_ParseTuple(args, "iO:set_callback", &item, &o_val)) {
356 if (item == PAM_CONV && !PyCallable_Check(o_val)) {
357 PyErr_SetString(PyExc_TypeError, "parameter must be a function");
360 Py_XDECREF(_self->callback);
361 _self->callback = o_val;
362 Py_INCREF(_self->callback);
363 memcpy(_self->conv, &python_conv, sizeof(struct pam_conv));
364 _self->conv->appdata_ptr = (void *) self;
365 result = pam_set_item(_self->pamh, item, (void *) _self->conv);
368 PyErr_SetString(PyExc_TypeError, "bad parameter");
373 if (result != PAM_SUCCESS) {
374 PyPAM_Err(_self, result);
383 static PyObject * PyPAM_get_item(PyObject *self, PyObject *args)
388 PyPAMObject *_self = (PyPAMObject *) self;
390 if (!PyArg_ParseTuple(args, "i", &item)) {
391 PyErr_SetString(PyExc_TypeError, "bad parameter");
395 result = pam_get_item(_self->pamh, item, &val);
397 if (result != PAM_SUCCESS) {
398 PyPAM_Err(_self, result);
402 if (item == PAM_CONV)
403 retval = Py_BuildValue("O:set_callback", val);
405 retval = Py_BuildValue("s", val);
411 static PyObject * PyPAM_putenv(PyObject *self, PyObject *args)
415 PyPAMObject *_self = (PyPAMObject *) self;
417 if (!PyArg_ParseTuple(args, "s", &val)) {
418 PyErr_SetString(PyExc_TypeError, "parameter must be a string");
422 result = pam_putenv(_self->pamh, val);
424 if (result != PAM_SUCCESS) {
425 PyErr_SetString(PyPAM_Error, "Not authenticated");
434 static PyObject * PyPAM_getenv(PyObject *self, PyObject *args)
436 const char *result, *val;
438 PyPAMObject *_self = (PyPAMObject *) self;
440 if (!PyArg_ParseTuple(args, "s", &val)) {
441 PyErr_SetString(PyExc_TypeError, "parameter must be a string");
445 result = pam_getenv(_self->pamh, val);
447 if (result == NULL) {
452 retval = Py_BuildValue("s", result);
457 static PyObject * PyPAM_getenvlist(PyObject *self, PyObject *args)
460 PyObject *retval, *entry;
461 PyPAMObject *_self = (PyPAMObject *) self;
463 result = pam_getenvlist(_self->pamh);
465 if (result == NULL) {
470 retval = PyList_New(0);
472 while ((cp = *(result++)) != NULL) {
473 entry = Py_BuildValue("s", cp);
474 PyList_Append(retval, entry);
481 static PyObject * PyPAM_set_userdata(PyObject *self, PyObject *args)
483 PyPAMObject *_self = (PyPAMObject *) self;
486 if (!PyArg_ParseTuple(args, "O", &user_data)) {
489 "set_userdata() expects exactly 1 argument"
494 Py_DECREF(_self->user_data);
495 Py_INCREF(user_data);
496 _self->user_data = user_data;
502 static PyObject * PyPAM_get_userdata(PyObject *self, PyObject *args)
504 PyPAMObject *_self = (PyPAMObject *) self;
506 if (!PyArg_ParseTuple(args, "")) {
509 "get_userdata() takes no arguments"
514 Py_INCREF(_self->user_data);
515 return _self->user_data;
518 static PyMethodDef PyPAMObject_Methods[] = {
519 {"start", PyPAM_start, METH_VARARGS, NULL},
520 {"authenticate", PyPAM_authenticate, METH_VARARGS, NULL},
521 {"setcred", PyPAM_setcred, METH_VARARGS, NULL},
522 {"acct_mgmt", PyPAM_acct_mgmt, METH_VARARGS, NULL},
523 {"chauthtok", PyPAM_chauthtok, METH_VARARGS, NULL},
524 {"open_session", PyPAM_open_session, METH_VARARGS, NULL},
525 {"close_session", PyPAM_close_session, METH_VARARGS, NULL},
526 {"set_item", PyPAM_set_item, METH_VARARGS, NULL},
527 {"get_item", PyPAM_get_item, METH_VARARGS, NULL},
528 {"putenv", PyPAM_putenv, METH_VARARGS, NULL},
529 {"getenv", PyPAM_getenv, METH_VARARGS, NULL},
530 {"getenvlist", PyPAM_getenvlist, METH_VARARGS, NULL},
531 {"set_userdata", PyPAM_set_userdata, METH_VARARGS, NULL},
532 {"get_userdata", PyPAM_get_userdata, METH_VARARGS, NULL},
533 {NULL, NULL, 0, NULL}
536 static void PyPAM_dealloc(PyPAMObject *self)
541 pam_end(self->pamh, PAM_SUCCESS);
547 static PyObject * PyPAM_getattr(PyPAMObject *self, char *name)
549 return Py_FindMethod(PyPAMObject_Methods, (PyObject *) self, name);
552 static PyObject * PyPAM_repr(PyPAMObject *self)
556 snprintf(buf, 1024, "<pam object, service=\"%s\", user=\"%s\", conv=%p, pamh=%p>",
557 self->service, self->user, self->conv, self->pamh);
558 return PyString_FromString(buf);
561 static PyTypeObject PyPAMObject_Type = {
562 PyObject_HEAD_INIT(0) /* Must fill in type value later */
567 (destructor)PyPAM_dealloc, /*tp_dealloc*/
569 (getattrfunc)PyPAM_getattr, /*tp_getattr*/
572 (reprfunc)PyPAM_repr, /*tp_repr*/
574 0, /*tp_as_sequence*/
578 static PyMethodDef PyPAM_Methods[] = {
579 {"pam", PyPAM_pam, METH_VARARGS, NULL},
580 {NULL, NULL, 0, NULL}
583 static char PyPAMObject_doc[] = "";
585 /* Convenience routine to export an integer value.
587 * Errors are silently ignored, for better or for worse...
588 * Happily borrowed from Python's socketmodule.c
590 static void insint(PyObject *d, char *name, int value)
592 PyObject* v = PyInt_FromLong((long) value);
594 if (!v || PyDict_SetItemString(d, name, v))
604 m = Py_InitModule("PAM", PyPAM_Methods);
605 d = PyModule_GetDict(m);
607 PyPAM_Error = PyErr_NewException("PAM.error", NULL, NULL);
608 if (PyPAM_Error == NULL)
610 PyDict_SetItemString(d, "error", PyPAM_Error);
612 PyPAMObject_Type.ob_type = &PyType_Type;
613 PyPAMObject_Type.tp_doc = PyPAMObject_doc;
614 Py_INCREF(&PyPAMObject_Type);
616 insint(d, "PAM_SUCCESS", PAM_SUCCESS);
617 insint(d, "PAM_OPEN_ERR", PAM_OPEN_ERR);
618 insint(d, "PAM_SYMBOL_ERR", PAM_SYMBOL_ERR);
619 insint(d, "PAM_SERVICE_ERR", PAM_SERVICE_ERR);
620 insint(d, "PAM_SYSTEM_ERR", PAM_SYSTEM_ERR);
621 insint(d, "PAM_BUF_ERR", PAM_BUF_ERR);
622 insint(d, "PAM_PERM_DENIED", PAM_PERM_DENIED);
623 insint(d, "PAM_AUTH_ERR", PAM_AUTH_ERR);
624 insint(d, "PAM_CRED_INSUFFICIENT", PAM_CRED_INSUFFICIENT);
625 insint(d, "PAM_AUTHINFO_UNAVAIL", PAM_AUTHINFO_UNAVAIL);
626 insint(d, "PAM_USER_UNKNOWN", PAM_USER_UNKNOWN);
627 insint(d, "PAM_MAXTRIES", PAM_MAXTRIES);
628 insint(d, "PAM_NEW_AUTHTOK_REQD", PAM_NEW_AUTHTOK_REQD);
629 insint(d, "PAM_ACCT_EXPIRED", PAM_ACCT_EXPIRED);
630 insint(d, "PAM_SESSION_ERR", PAM_SESSION_ERR);
631 insint(d, "PAM_CRED_UNAVAIL", PAM_CRED_UNAVAIL);
632 insint(d, "PAM_CRED_EXPIRED", PAM_CRED_EXPIRED);
633 insint(d, "PAM_CRED_ERR", PAM_CRED_ERR);
634 insint(d, "PAM_NO_MODULE_DATA", PAM_NO_MODULE_DATA);
635 insint(d, "PAM_CONV_ERR", PAM_CONV_ERR);
636 insint(d, "PAM_AUTHTOK_ERR", PAM_AUTHTOK_ERR);
637 insint(d, "PAM_AUTHTOK_RECOVER_ERR", PAM_AUTHTOK_RECOVER_ERR);
638 insint(d, "PAM_AUTHTOK_LOCK_BUSY", PAM_AUTHTOK_LOCK_BUSY);
639 insint(d, "PAM_AUTHTOK_DISABLE_AGING", PAM_AUTHTOK_DISABLE_AGING);
640 insint(d, "PAM_TRY_AGAIN", PAM_TRY_AGAIN);
641 insint(d, "PAM_IGNORE", PAM_IGNORE);
642 insint(d, "PAM_ABORT", PAM_ABORT);
643 insint(d, "PAM_AUTHTOK_EXPIRED", PAM_AUTHTOK_EXPIRED);
644 insint(d, "PAM_MODULE_UNKNOWN", PAM_MODULE_UNKNOWN);
645 insint(d, "PAM_BAD_ITEM", PAM_BAD_ITEM);
646 insint(d, "_PAM_RETURN_VALUES", _PAM_RETURN_VALUES);
648 insint(d, "PAM_SILENT", PAM_SILENT);
649 insint(d, "PAM_DISALLOW_NULL_AUTHTOK", PAM_DISALLOW_NULL_AUTHTOK);
650 insint(d, "PAM_ESTABLISH_CRED", PAM_ESTABLISH_CRED);
651 insint(d, "PAM_DELETE_CRED", PAM_DELETE_CRED);
652 insint(d, "PAM_REINITIALIZE_CRED", PAM_REINITIALIZE_CRED);
653 insint(d, "PAM_REFRESH_CRED", PAM_REFRESH_CRED);
654 insint(d, "PAM_CHANGE_EXPIRED_AUTHTOK", PAM_CHANGE_EXPIRED_AUTHTOK);
656 insint(d, "PAM_SERVICE", PAM_SERVICE);
657 insint(d, "PAM_USER", PAM_USER);
658 insint(d, "PAM_TTY", PAM_TTY);
659 insint(d, "PAM_RHOST", PAM_RHOST);
660 insint(d, "PAM_CONV", PAM_CONV);
661 /* These next two are most likely not needed for client apps */
662 insint(d, "PAM_RUSER", PAM_RUSER);
663 insint(d, "PAM_USER_PROMPT", PAM_USER_PROMPT);
665 insint(d, "PAM_DATA_SILENT", PAM_DATA_SILENT);
667 insint(d, "PAM_PROMPT_ECHO_OFF", PAM_PROMPT_ECHO_OFF);
668 insint(d, "PAM_PROMPT_ECHO_ON", PAM_PROMPT_ECHO_ON);
669 insint(d, "PAM_ERROR_MSG", PAM_ERROR_MSG);
670 insint(d, "PAM_TEXT_INFO", PAM_TEXT_INFO);
672 insint(d, "PAM_RADIO_TYPE", PAM_RADIO_TYPE);
673 insint(d, "PAM_BINARY_MSG", PAM_BINARY_MSG);
674 insint(d, "PAM_BINARY_PROMPT", PAM_BINARY_PROMPT);