diff options
author | Tomas Mraz <tmraz@redhat.com> | 2011-11-07 17:42:00 +0100 |
---|---|---|
committer | Tomas Mraz <tmraz@redhat.com> | 2011-11-07 17:42:00 +0100 |
commit | a91d98e5cc2955763f76ec29dadc62235331da63 (patch) | |
tree | 8cb3fdd515530bdb6ea8e1720cf7fe0efbf768d9 /python | |
parent | 2aff91b770e617c54d97fd96119f573d9a9c0777 (diff) | |
download | libpwquality-a91d98e5cc2955763f76ec29dadc62235331da63.tar.gz |
Add Python interface for libpwquality.
Diffstat (limited to 'python')
-rw-r--r-- | python/Makefile.am | 16 | ||||
-rw-r--r-- | python/pwquality.c | 396 | ||||
-rwxr-xr-x | python/setup.py | 74 |
3 files changed, 486 insertions, 0 deletions
diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 0000000..acdf7f5 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,16 @@ +# +# Copyright (c) 2011 Red Hat, Inc. +# Copyright (c) 2011 Tomas Mraz <tm@t8m.info> +# + +pythonincdir = $(includedir)/python@PYTHONREV@ +pythonsitedir = $(libdir)/python@PYTHONREV@/site-packages + +CLEANFILES = *~ constants.c pwquality.so + +EXTRA_DIST = pwquality.c setup.py + +pythonsite_SCRIPTS = pwquality.so + +pwquality.so: ../src/pwquality.h pwquality.c setup.py + python setup.py build --build-lib=. diff --git a/python/pwquality.c b/python/pwquality.c new file mode 100644 index 0000000..e60152f --- /dev/null +++ b/python/pwquality.c @@ -0,0 +1,396 @@ +/* + * libpwquality Python bindings + * + * Copyright (c) Red Hat, Inc, 2011 + * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011 + * + * See the end of the file for the License Information + */ + +#include <Python.h> +#include "pwquality.h" + +static PyObject *PWQError; + +typedef struct { + PyObject_HEAD + pwquality_settings_t *pwq; +} PWQSettings; + +static PyObject * +pwqsettings_new(PyTypeObject *type, PyObject *args, PyObject *kwds); +static void +pwqsettings_dealloc(PWQSettings *self); +static PyObject * +pwqsettings_getint(PWQSettings *self, void *setting); +static int +pwqsettings_setint(PWQSettings *self, PyObject *value, void *setting); +static PyObject * +pwqsettings_getstr(PWQSettings *self, void *setting); +static int +pwqsettings_setstr(PWQSettings *self, PyObject *value, void *setting); +static PyObject * +read_config(PWQSettings *self, PyObject *args); +static PyObject * +set_option(PWQSettings *self, PyObject *args); +static PyObject * +generate(PWQSettings *self, PyObject *args); +static PyObject * +check(PWQSettings *self, PyObject *args); + +static PyMethodDef pwqsettings_methods[] = { + { "read_config", (PyCFunction)read_config, METH_VARARGS, + "Read the settings from configuration file" + }, + { "set_option", (PyCFunction)set_option, METH_VARARGS, + "Set option from name=value pair" + }, + { "generate", (PyCFunction)generate, METH_VARARGS, + "Generate password with requested entropy" + }, + { "check", (PyCFunction)check, METH_VARARGS, + "Check whether the password conforms to the requirements and return password strength score" + }, + { NULL } /* Sentinel */ +}; + +static PyGetSetDef pwqsettings_getseters[] = { + { "difok", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Minimum difference from the old password", + (void *)PWQ_SETTING_DIFF_OK + }, + { "minlen", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Minimum length of the new password", + (void *)PWQ_SETTING_MIN_LENGTH + }, + { "dcredit", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Credit for or minimum of digits", + (void *)PWQ_SETTING_DIG_CREDIT + }, + { "ucredit", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Credit for or minimum of uppercase characters", + (void *)PWQ_SETTING_UP_CREDIT + }, + { "lcredit", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Credit for or minimum of lowercase characters", + (void *)PWQ_SETTING_LOW_CREDIT + }, + { "ocredit", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Credit for or minimum of other characters", + (void *)PWQ_SETTING_OTH_CREDIT + }, + { "minclass", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Minimum number of character classes", + (void *)PWQ_SETTING_MIN_CLASS + }, + { "maxrepeat", + (getter)pwqsettings_getint, (setter)pwqsettings_setint, + "Maximum repeated consecutive characters", + (void *)PWQ_SETTING_MAX_REPEAT + }, + { "dictpath", + (getter)pwqsettings_getstr, (setter)pwqsettings_setstr, + "Path to the cracklib dictionary", + (void *)PWQ_SETTING_DICT_PATH + }, + { NULL } /* Sentinel */ +}; + + +static PyTypeObject pwqsettings_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "pwquality.PWQSettings", /* tp_name */ + sizeof(PWQSettings), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pwqsettings_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 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 | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "PWQSettings objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pwqsettings_methods, /* tp_methods */ + 0, /* tp_members */ + pwqsettings_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + pwqsettings_new, /* tp_new */ +}; + +static PyMethodDef pwquality_methods[] = { + { NULL } /* Sentinel */ +}; + +static PyObject * +pwqerror(int rc, void *auxerror) +{ + char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; + PyObject *py_errvalue; + const char *msg; + + msg = pwquality_strerror(buf, sizeof(buf), rc, auxerror); + + if (rc == PWQ_ERROR_MEM_ALLOC) + return PyErr_NoMemory(); + + py_errvalue = Py_BuildValue("is", rc, msg); + if (py_errvalue == NULL) + return NULL; + + if (rc == PWQ_ERROR_UNKNOWN_SETTING || rc == PWQ_ERROR_NON_INT_SETTING + || rc == PWQ_ERROR_NON_STR_SETTING) { + PyErr_SetObject(PyExc_AttributeError, py_errvalue); + } else { + PyErr_SetObject(PWQError, py_errvalue); + } + return NULL; +} + +static PyObject * +pwqsettings_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PWQSettings *self; + + self = (PWQSettings *)type->tp_alloc(type, 0); + if (self) { + self->pwq = pwquality_default_settings(); + if (self->pwq == NULL) { + Py_DECREF(self); + return PyErr_NoMemory(); + } + } + return (PyObject *)self; +} + +static void +pwqsettings_dealloc(PWQSettings *self) +{ + pwquality_free_settings(self->pwq); + self->ob_type->tp_free((PyObject *)self); +} + +static PyObject * +pwqsettings_getint(PWQSettings *self, void *setting) +{ + int value; + int rc; + + if ((rc = pwquality_get_int_value(self->pwq, (int)(ssize_t)setting, &value)) < 0) { + return pwqerror(rc, NULL); + } + return PyInt_FromLong((long)value); +} + +static int +pwqsettings_setint(PWQSettings *self, PyObject *value, void *setting) +{ + long l; + int rc; + + l = PyInt_AsLong(value); + if (PyErr_Occurred() == NULL) { + if ((rc = pwquality_set_int_value(self->pwq, + (int)(ssize_t)setting, (int)l)) < 0) { + pwqerror(rc, NULL); + return -1; + } + return 0; + } + return -1; +} + +static PyObject * +pwqsettings_getstr(PWQSettings *self, void *setting) +{ + const char *value; + int rc; + + if ((rc = pwquality_get_str_value(self->pwq, (int)(ssize_t)setting, &value)) < 0) { + return pwqerror(rc, NULL); + } + if (value == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(value); +} + +static int +pwqsettings_setstr(PWQSettings *self, PyObject *value, void *setting) +{ + const char *s; + int rc; + + if (value == (PyObject *)Py_None) + s = NULL; + else + s = PyString_AsString(value); + + if (PyErr_Occurred() == NULL) { + if ((rc = pwquality_set_str_value(self->pwq, + (int)(ssize_t)setting, s)) < 0) { + pwqerror(rc, NULL); + return -1; + } + return 0; + } + return -1; +} + +static PyObject * +read_config(PWQSettings *self, PyObject *args) +{ + char *cfgfile = NULL; + void *auxerror; + int rc; + + if (!PyArg_ParseTuple(args, "|s", &cfgfile)) + return NULL; + if ((rc = pwquality_read_config(self->pwq, cfgfile, &auxerror)) < 0) { + return pwqerror(rc, auxerror); + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +set_option(PWQSettings *self, PyObject *args) +{ + char *option; + int rc; + + if (!PyArg_ParseTuple(args, "s", &option)) + return NULL; + if ((rc = pwquality_set_option(self->pwq, option)) < 0) { + return pwqerror(rc, NULL); + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +generate(PWQSettings *self, PyObject *args) +{ + int entropy_bits; + char *password; + PyObject *passobj; + int rc; + + if (!PyArg_ParseTuple(args, "i", &entropy_bits)) + return NULL; + if ((rc = pwquality_generate(self->pwq, entropy_bits, &password)) < 0) { + return pwqerror(rc, NULL); + } + + passobj = PyString_FromString(password); + free(password); + return passobj; +} + +static PyObject * +check(PWQSettings *self, PyObject *args) +{ + char *password; + char *oldpassword = NULL; + char *username = NULL; + void *auxerror; + int rc; + + if (!PyArg_ParseTuple(args, "s|ss", &password, &oldpassword, &username)) + return NULL; + if ((rc = pwquality_check(self->pwq, password, oldpassword, + username, &auxerror)) < 0) { + return pwqerror(rc, auxerror); + } + + return PyInt_FromLong((long)rc); +} + +PyMODINIT_FUNC +initpwquality(void) +{ + PyObject *module; + + if (PyType_Ready(&pwqsettings_type) < 0) + return; + + module = Py_InitModule3("pwquality", pwquality_methods, + "Libpwquality wrapper module"); + if (module == NULL) + return; + + PWQError = PyErr_NewException("pwquality.PWQError", NULL, NULL); + if (PWQError == NULL) { + Py_DECREF(module); + return; + } + Py_INCREF(PWQError); + PyModule_AddObject(module, "PWQError", PWQError); + + Py_INCREF(&pwqsettings_type); + PyModule_AddObject(module, "PWQSettings", (PyObject *)&pwqsettings_type); + +#include "constants.c" +} + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/python/setup.py b/python/setup.py new file mode 100755 index 0000000..b9a7900 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# See the end of the file for Copyright and License Information +# + +from distutils.core import setup, Extension +from distutils.command.build_ext import build_ext as _build_ext + +class build_ext(_build_ext): + def genconstants(self, headerfile, outputfile): + hf = open(headerfile, 'r') + of = open(outputfile, 'w') + of.write('/* This file is generated during build time from pwquality.h */\n\n') + for line in hf: + if line.startswith('#define PWQ_'): + s = line.split() + of.write('PyModule_AddIntConstant(module, "%s", %s);\n' % (s[1], s[2])) + + def run(self): + self.genconstants('../src/pwquality.h', 'constants.c') + _build_ext.run(self) + +pwqmodule = Extension('pwquality', + sources = ['pwquality.c'], + include_dirs = ['../src'], + library_dirs = ['../src/.libs'], + libraries = ['pwquality']) + +setup( + name = 'pwquality', + version = '0.9.1', + description = 'Python bindings for the libpwquality library for password quality checking', + author = 'Tomáš Mráz', + author_email = 'tm@t8m.info', + url = 'http://fedorahosted.org/libpwquality', + license = 'BSD or GPL+', + ext_modules = [pwqmodule], + cmdclass = {'build_ext': build_ext} +) + +# Copyright (c) Red Hat, Inc, 2011 +# Copyright (c) Tomas Mraz <tm@t8m.info>, 2011 +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, and the entire permission notice in its entirety, +# including the disclaimer of warranties. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# ALTERNATIVELY, this product may be distributed under the terms of +# the GNU Public License, in which case the provisions of the GPL are +# required INSTEAD OF the above restrictions. (This clause is +# necessary due to a potential bad interaction between the GPL and +# the restrictions contained in a BSD-style copyright.) +# +# THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. |