pysss.c revision d79c604c5080c5f6e2bf2d91b4ae79a562437c74
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder/*
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder Authors:
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder Jakub Hrozek <jhrozek@redhat.com>
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder Copyright (C) 2009 Red Hat
98890889ffb2e8f6f722b00e265a211f13b5a861Corneliu-Claudiu Prodescu
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder This program is free software; you can redistribute it and/or modify
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder it under the terms of the GNU General Public License as published by
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder the Free Software Foundation; either version 3 of the License, or
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder (at your option) any later version.
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder This program is distributed in the hope that it will be useful,
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder but WITHOUT ANY WARRANTY; without even the implied warranty of
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder GNU General Public License for more details.
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder You should have received a copy of the GNU General Public License
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder along with this program. If not, see <http://www.gnu.org/licenses/>.
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder*/
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include <Python.h>
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include <structmember.h>
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include <talloc.h>
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include <pwd.h>
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include <grp.h>
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder#include "util/util.h"
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include "util/sss_python.h"
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include "db/sysdb.h"
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include "tools/tools_util.h"
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include "tools/sss_sync_ops.h"
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder#include "util/crypto/sss_crypto.h"
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder/*
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder * function taken from samba sources tree as of Aug 20 2009,
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder * file source4/lib/ldb/pyldb.c
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder */
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maederstatic char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list,
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder const char *paramname)
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder{
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder char **ret;
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder int i;
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder ret = talloc_array(mem_ctx, char *, PyList_Size(list)+1);
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder for (i = 0; i < PyList_Size(list); i++) {
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder PyObject *item = PyList_GetItem(list, i);
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder if (!PyString_Check(item)) {
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder PyErr_Format(PyExc_TypeError, "%s should be strings", paramname);
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder return NULL;
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder }
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder ret[i] = talloc_strndup(ret, PyString_AsString(item),
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder PyString_Size(item));
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder }
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder ret[i] = NULL;
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder return ret;
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder}
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder/* ======================= sysdb python wrappers ==========================*/
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder/*
b2a02ef0183a671777294e069307bba893ca3ebeChristian Maeder * The sss.password object
d5a8d891500a642ce629854857fc00b7c21aec47Christian Maeder */
typedef struct {
PyObject_HEAD
TALLOC_CTX *mem_ctx;
struct tevent_context *ev;
struct sysdb_ctx *sysdb;
struct confdb_ctx *confdb;
struct sss_domain_info *local;
int lock;
int unlock;
} PySssLocalObject;
/*
* Error reporting
*/
static void PyErr_SetSssErrorWithMessage(int ret, const char *message)
{
PyObject *exc = Py_BuildValue(discard_const_p(char, "(is)"),
ret, message);
PyErr_SetObject(PyExc_IOError, exc);
Py_XDECREF(exc);
}
static void PyErr_SetSssError(int ret)
{
PyErr_SetSssErrorWithMessage(ret, strerror(ret));
}
/*
* Common init of all methods
*/
static struct tools_ctx *init_ctx(PySssLocalObject *self)
{
struct ops_ctx *octx = NULL;
struct tools_ctx *tctx = NULL;
tctx = talloc_zero(self->mem_ctx, struct tools_ctx);
if (tctx == NULL) {
return NULL;
}
tctx->confdb = self->confdb;
tctx->sysdb = self->sysdb;
tctx->local = self->local;
/* tctx->nctx is NULL here, which is OK since we don't parse domains
* in the python bindings (yet?) */
octx = talloc_zero(tctx, struct ops_ctx);
if (octx == NULL) {
PyErr_NoMemory();
return NULL;
}
octx->domain = self->local;
tctx->octx = octx;
return tctx;
}
/*
* Add a user
*/
PyDoc_STRVAR(py_sss_useradd__doc__,
"Add a user named ``username``.\n\n"
":param username: name of the user\n\n"
":param kwargs: Keyword arguments that customize the operation\n\n"
"* useradd can be customized further with keyword arguments:\n"
" * ``uid``: The UID of the user\n"
" * ``gid``: The GID of the user\n"
" * ``gecos``: The comment string\n"
" * ``homedir``: Home directory\n"
" * ``shell``: Login shell\n"
" * ``skel``: Specify an alternative skeleton directory\n"
" * ``create_home``: (bool) Force creation of home directory on or off\n"
" * ``groups``: List of groups the user is member of\n");
static PyObject *py_sss_useradd(PySssLocalObject *self,
PyObject *args,
PyObject *kwds)
{
struct tools_ctx *tctx = NULL;
unsigned long uid = 0;
unsigned long gid = 0;
const char *gecos = NULL;
const char *home = NULL;
const char *shell = NULL;
const char *skel = NULL;
char *username = NULL;
int ret;
const char * const kwlist[] = { "username", "uid", "gid", "gecos",
"homedir", "shell", "skel",
"create_home", "groups", NULL };
PyObject *py_groups = Py_None;
PyObject *py_create_home = Py_None;
int create_home = 0;
bool in_transaction = false;
/* parse arguments */
if (!PyArg_ParseTupleAndKeywords(args, kwds,
discard_const_p(char, "s|kkssssO!O!"),
discard_const_p(char *, kwlist),
&username,
&uid,
&gid,
&gecos,
&home,
&shell,
&skel,
&PyBool_Type,
&py_create_home,
&PyList_Type,
&py_groups)) {
goto fail;
}
tctx = init_ctx(self);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
if (py_groups != Py_None) {
tctx->octx->addgroups = PyList_AsStringList(tctx, py_groups, "groups");
if (!tctx->octx->addgroups) {
PyErr_NoMemory();
return NULL;
}
}
/* user-wise the parameter is only bool - do or don't,
* however we must have a third state - undecided, pick default */
if (py_create_home == Py_True) {
create_home = DO_CREATE_HOME;
} else if (py_create_home == Py_False) {
create_home = DO_NOT_CREATE_HOME;
}
tctx->octx->name = username;
tctx->octx->uid = uid;
/* fill in defaults */
ret = useradd_defaults(tctx,
self->confdb,
tctx->octx, gecos,
home, shell,
create_home,
skel);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
/* Add the user within a transaction */
tctx->error = sysdb_transaction_start(tctx->sysdb);
if (tctx->error != EOK) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = true;
/* useradd */
tctx->error = useradd(tctx, tctx->octx);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
tctx->error = sysdb_transaction_commit(tctx->sysdb);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = false;
/* Create user's home directory and/or mail spool */
if (tctx->octx->create_homedir) {
/* We need to know the UID and GID of the user, if
* sysdb did assign it automatically, do a lookup */
if (tctx->octx->uid == 0 || tctx->octx->gid == 0) {
ret = sysdb_getpwnam_sync(tctx,
tctx->octx->name,
tctx->octx);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
}
ret = create_homedir(tctx->octx->skeldir,
tctx->octx->home,
tctx->octx->uid,
tctx->octx->gid,
tctx->octx->umask);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
/* failure here should not be fatal */
create_mail_spool(tctx,
tctx->octx->name,
tctx->octx->maildir,
tctx->octx->uid,
tctx->octx->gid);
}
talloc_zfree(tctx);
Py_RETURN_NONE;
fail:
if (in_transaction) {
/* We do not handle return value of sysdb_transaction_cancel()
* because we don't want to overwrite previous error code.
*/
sysdb_transaction_cancel(tctx->sysdb);
}
talloc_zfree(tctx);
return NULL;
}
/*
* Delete a user
*/
PyDoc_STRVAR(py_sss_userdel__doc__,
"Remove the user named ``username``.\n\n"
":param username: Name of user being removed\n"
":param kwargs: Keyword arguments that customize the operation\n\n"
"* userdel can be customized further with keyword arguments:\n"
" * ``force``: (bool) Force removal of files not owned by the user\n"
" * ``remove``: (bool) Toggle removing home directory and mail spool\n");
static PyObject *py_sss_userdel(PySssLocalObject *self,
PyObject *args,
PyObject *kwds)
{
struct tools_ctx *tctx = NULL;
char *username = NULL;
int ret;
PyObject *py_remove = Py_None;
int remove_home = 0;
PyObject *py_force = Py_None;
const char * const kwlist[] = { "username", "remove", "force", NULL };
if(!PyArg_ParseTupleAndKeywords(args, kwds,
discard_const_p(char, "s|O!O!"),
discard_const_p(char *, kwlist),
&username,
&PyBool_Type,
&py_remove,
&PyBool_Type,
&py_force)) {
goto fail;
}
tctx = init_ctx(self);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
tctx->octx->name = username;
if (py_remove == Py_True) {
remove_home = DO_REMOVE_HOME;
} else if (py_remove == Py_False) {
remove_home = DO_NOT_REMOVE_HOME;
}
/*
* Fills in defaults for ops_ctx user did not specify.
*/
ret = userdel_defaults(tctx,
tctx->confdb,
tctx->octx,
remove_home);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
ret = run_userdel_cmd(tctx);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
if (tctx->octx->remove_homedir) {
ret = sysdb_getpwnam_sync(tctx,
tctx->octx->name,
tctx->octx);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
}
/* Delete the user */
ret = userdel(tctx, self->sysdb, tctx->octx);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
if (tctx->octx->remove_homedir) {
ret = remove_homedir(tctx,
tctx->octx->home,
tctx->octx->maildir,
tctx->octx->name,
tctx->octx->uid,
(py_force == Py_True));
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
}
talloc_zfree(tctx);
Py_RETURN_NONE;
fail:
talloc_zfree(tctx);
return NULL;
}
/*
* Modify a user
*/
PyDoc_STRVAR(py_sss_usermod__doc__,
"Modify a user.\n\n"
":param username: Name of user being modified\n\n"
":param kwargs: Keyword arguments that customize the operation\n\n"
"* usermod can be customized further with keyword arguments:\n"
" * ``uid``: The UID of the user\n"
" * ``gid``: The GID of the user\n"
" * ``gecos``: The comment string\n"
" * ``homedir``: Home directory\n"
" * ``shell``: Login shell\n"
" * ``addgroups``: List of groups to add the user to\n"
" * ``rmgroups``: List of groups to remove the user from\n"
" * ``lock``: Lock or unlock the account\n");
static PyObject *py_sss_usermod(PySssLocalObject *self,
PyObject *args,
PyObject *kwds)
{
struct tools_ctx *tctx = NULL;
PyObject *py_addgroups = Py_None;
PyObject *py_rmgroups = Py_None;
unsigned long uid = 0;
unsigned long gid = 0;
char *gecos = NULL;
char *home = NULL;
char *shell = NULL;
char *username = NULL;
unsigned long lock = 0;
const char * const kwlist[] = { "username", "uid", "gid", "lock",
"gecos", "homedir", "shell",
"addgroups", "rmgroups", NULL };
bool in_transaction = false;
/* parse arguments */
if (!PyArg_ParseTupleAndKeywords(args, kwds,
discard_const_p(char, "s|kkksssO!O!"),
discard_const_p(char *, kwlist),
&username,
&uid,
&gid,
&lock,
&gecos,
&home,
&shell,
&PyList_Type,
&py_addgroups,
&PyList_Type,
&py_rmgroups)) {
goto fail;
}
tctx = init_ctx(self);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
if (lock && lock != DO_LOCK && lock != DO_UNLOCK) {
PyErr_SetString(PyExc_ValueError,
"Unkown value for lock parameter");
goto fail;
}
if (py_addgroups != Py_None) {
tctx->octx->addgroups = PyList_AsStringList(tctx,
py_addgroups,
"addgroups");
if (!tctx->octx->addgroups) {
return NULL;
}
}
if (py_rmgroups != Py_None) {
tctx->octx->rmgroups = PyList_AsStringList(tctx,
py_rmgroups,
"rmgroups");
if (!tctx->octx->rmgroups) {
return NULL;
}
}
tctx->octx->name = username;
tctx->octx->uid = uid;
tctx->octx->gid = gid;
tctx->octx->gecos = gecos;
tctx->octx->home = home;
tctx->octx->shell = shell;
tctx->octx->lock = lock;
/* Modify the user within a transaction */
tctx->error = sysdb_transaction_start(tctx->sysdb);
if (tctx->error != EOK) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = true;
/* usermod */
tctx->error = usermod(tctx, tctx->octx);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
tctx->error = sysdb_transaction_commit(tctx->sysdb);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = false;
talloc_zfree(tctx);
Py_RETURN_NONE;
fail:
if (in_transaction) {
/* We do not handle return value of sysdb_transaction_cancel()
* because we don't want to overwrite previous error code.
*/
sysdb_transaction_cancel(tctx->sysdb);
}
talloc_zfree(tctx);
return NULL;
}
/*
* Add a group
*/
PyDoc_STRVAR(py_sss_groupadd__doc__,
"Add a group.\n\n"
":param groupname: Name of group being added\n\n"
":param kwargs: Keyword arguments ro customize the operation\n\n"
"* groupmod can be customized further with keyword arguments:\n"
" * ``gid``: The GID of the group\n");
static PyObject *py_sss_groupadd(PySssLocalObject *self,
PyObject *args,
PyObject *kwds)
{
struct tools_ctx *tctx = NULL;
char *groupname;
unsigned long gid = 0;
const char * const kwlist[] = { "groupname", "gid", NULL };
bool in_transaction = false;
/* parse arguments */
if (!PyArg_ParseTupleAndKeywords(args, kwds,
discard_const_p(char, "s|k"),
discard_const_p(char *, kwlist),
&groupname,
&gid)) {
goto fail;
}
tctx = init_ctx(self);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
tctx->octx->name = groupname;
tctx->octx->gid = gid;
/* Add the group within a transaction */
tctx->error = sysdb_transaction_start(tctx->sysdb);
if (tctx->error != EOK) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = true;
/* groupadd */
tctx->error = groupadd(tctx->octx);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
tctx->error = sysdb_transaction_commit(tctx->sysdb);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = false;
talloc_zfree(tctx);
Py_RETURN_NONE;
fail:
if (in_transaction) {
/* We do not handle return value of sysdb_transaction_cancel()
* because we don't want to overwrite previous error code.
*/
sysdb_transaction_cancel(tctx->sysdb);
}
talloc_zfree(tctx);
return NULL;
}
/*
* Delete a group
*/
PyDoc_STRVAR(py_sss_groupdel__doc__,
"Remove a group.\n\n"
":param groupname: Name of group being removed\n");
static PyObject *py_sss_groupdel(PySssLocalObject *self,
PyObject *args,
PyObject *kwds)
{
struct tools_ctx *tctx = NULL;
char *groupname = NULL;
int ret;
if(!PyArg_ParseTuple(args, discard_const_p(char, "s"), &groupname)) {
goto fail;
}
tctx = init_ctx(self);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
tctx->octx->name = groupname;
/* Remove the group */
ret = groupdel(tctx, self->sysdb, tctx->octx);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
talloc_zfree(tctx);
Py_RETURN_NONE;
fail:
talloc_zfree(tctx);
return NULL;
}
/*
* Modify a group
*/
PyDoc_STRVAR(py_sss_groupmod__doc__,
"Modify a group.\n\n"
":param groupname: Name of group being modified\n\n"
":param kwargs: Keyword arguments ro customize the operation\n\n"
"* groupmod can be customized further with keyword arguments:\n"
" * ``gid``: The GID of the group\n\n"
" * ``addgroups``: Groups to add the group to\n\n"
" * ``rmgroups``: Groups to remove the group from\n\n");
static PyObject *py_sss_groupmod(PySssLocalObject *self,
PyObject *args,
PyObject *kwds)
{
struct tools_ctx *tctx = NULL;
PyObject *py_addgroups = Py_None;
PyObject *py_rmgroups = Py_None;
unsigned long gid = 0;
char *groupname = NULL;
const char * const kwlist[] = { "groupname", "gid", "addgroups",
"rmgroups", NULL };
bool in_transaction = false;
/* parse arguments */
if (!PyArg_ParseTupleAndKeywords(args, kwds,
discard_const_p(char, "s|kO!O!"),
discard_const_p(char *, kwlist),
&groupname,
&gid,
&PyList_Type,
&py_addgroups,
&PyList_Type,
&py_rmgroups)) {
goto fail;
}
tctx = init_ctx(self);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
if (py_addgroups != Py_None) {
tctx->octx->addgroups = PyList_AsStringList(tctx,
py_addgroups,
"addgroups");
if (!tctx->octx->addgroups) {
return NULL;
}
}
if (py_rmgroups != Py_None) {
tctx->octx->rmgroups = PyList_AsStringList(tctx,
py_rmgroups,
"rmgroups");
if (!tctx->octx->rmgroups) {
return NULL;
}
}
tctx->octx->name = groupname;
tctx->octx->gid = gid;
/* Modify the group within a transaction */
tctx->error = sysdb_transaction_start(tctx->sysdb);
if (tctx->error != EOK) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = true;
/* groupmod */
tctx->error = groupmod(tctx, tctx->octx);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
tctx->error = sysdb_transaction_commit(tctx->sysdb);
if (tctx->error) {
PyErr_SetSssError(tctx->error);
goto fail;
}
in_transaction = false;
talloc_zfree(tctx);
Py_RETURN_NONE;
fail:
if (in_transaction) {
/* We do not handle return value of sysdb_transaction_cancel()
* because we don't want to overwrite previous error code.
*/
sysdb_transaction_cancel(tctx->sysdb);
}
talloc_zfree(tctx);
return NULL;
}
/*
* Get list of groups user belongs to
*/
PyDoc_STRVAR(py_sss_getgrouplist__doc__,
"Get list of groups user belongs to.\n\n"
"NOTE: The interface uses the system NSS calls and is not limited to "
"users served by the SSSD!\n"
":param username: name of user to get list for\n");
static PyObject *py_sss_getgrouplist(PyObject *self, PyObject *args)
{
char *username = NULL;
gid_t *groups = NULL;
struct passwd *pw;
struct group *gr;
int ngroups;
int ret;
Py_ssize_t i, idx;
PyObject *groups_tuple;
if(!PyArg_ParseTuple(args, discard_const_p(char, "s"), &username)) {
goto fail;
}
pw = getpwnam(username);
if (pw == NULL) {
goto fail;
}
ngroups = 32;
groups = malloc(sizeof(gid_t) * ngroups);
if (groups == NULL) {
goto fail;
}
do {
ret = getgrouplist(username, pw->pw_gid, groups, &ngroups);
if (ret < ngroups) {
gid_t *tmp_groups = realloc(groups, ngroups * sizeof(gid_t));
if (tmp_groups == NULL) {
goto fail;
}
groups = tmp_groups;
}
} while (ret != ngroups);
groups_tuple = PyTuple_New((Py_ssize_t) ngroups);
if (groups_tuple == NULL) {
goto fail;
}
/* Populate a tuple with names of groups
* In unlikely case of group not being able to resolve, skip it
* We also need to resize resulting tuple to avoid empty elements there */
idx = 0;
for (i = 0; i < ngroups; i++) {
gr = getgrgid(groups[i]);
if (gr) {
PyTuple_SetItem(groups_tuple, idx, PyString_FromString(gr->gr_name));
idx++;
}
}
free(groups);
groups = NULL;
if (i != idx) {
_PyTuple_Resize(&groups_tuple, idx);
}
return groups_tuple;
fail:
free(groups);
return NULL;
}
/*** python plumbing begins here ***/
/*
* The sss.local destructor
*/
static void PySssLocalObject_dealloc(PySssLocalObject *self)
{
talloc_free(self->mem_ctx);
self->ob_type->tp_free((PyObject*) self);
}
/*
* The sss.local constructor
*/
static PyObject *PySssLocalObject_new(PyTypeObject *type,
PyObject *args,
PyObject *kwds)
{
TALLOC_CTX *mem_ctx;
PySssLocalObject *self;
char *confdb_path;
int ret;
mem_ctx = talloc_new(NULL);
if (mem_ctx == NULL) {
PyErr_NoMemory();
return NULL;
}
self = (PySssLocalObject *) type->tp_alloc(type, 0);
if (self == NULL) {
talloc_free(mem_ctx);
PyErr_NoMemory();
return NULL;
}
self->mem_ctx = mem_ctx;
confdb_path = talloc_asprintf(self->mem_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
if (confdb_path == NULL) {
talloc_free(mem_ctx);
PyErr_NoMemory();
return NULL;
}
/* Connect to the conf db */
ret = confdb_init(self->mem_ctx, &self->confdb, confdb_path);
if (ret != EOK) {
talloc_free(mem_ctx);
PyErr_SetSssErrorWithMessage(ret,
"Could not initialize connection to the confdb\n");
return NULL;
}
ret = sssd_domain_init(self->mem_ctx, self->confdb, "local",
DB_PATH, &self->local);
if (ret != EOK) {
talloc_free(mem_ctx);
PyErr_SetSssErrorWithMessage(ret,
"Could not initialize connection to the sysdb\n");
return NULL;
}
self->sysdb = self->local->sysdb;
self->lock = DO_LOCK;
self->unlock = DO_UNLOCK;
return (PyObject *) self;
}
/*
* sss.local object methods
*/
static PyMethodDef sss_local_methods[] = {
{ sss_py_const_p(char, "useradd"), (PyCFunction) py_sss_useradd,
METH_KEYWORDS, py_sss_useradd__doc__
},
{ sss_py_const_p(char, "userdel"), (PyCFunction) py_sss_userdel,
METH_KEYWORDS, py_sss_userdel__doc__
},
{ sss_py_const_p(char, "usermod"), (PyCFunction) py_sss_usermod,
METH_KEYWORDS, py_sss_usermod__doc__
},
{ sss_py_const_p(char, "groupadd"), (PyCFunction) py_sss_groupadd,
METH_KEYWORDS, py_sss_groupadd__doc__
},
{ sss_py_const_p(char, "groupdel"), (PyCFunction) py_sss_groupdel,
METH_KEYWORDS, py_sss_groupdel__doc__
},
{ sss_py_const_p(char, "groupmod"), (PyCFunction) py_sss_groupmod,
METH_KEYWORDS, py_sss_groupmod__doc__
},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static PyMemberDef sss_local_members[] = {
{ discard_const_p(char, "lock"), T_INT,
offsetof(PySssLocalObject, lock), RO, NULL},
{ discard_const_p(char, "unlock"), T_INT,
offsetof(PySssLocalObject, unlock), RO, NULL},
{NULL, 0, 0, 0, NULL} /* Sentinel */
};
/*
* sss.local object properties
*/
static PyTypeObject pysss_local_type = {
PyObject_HEAD_INIT(NULL)
.tp_name = sss_py_const_p(char, "sss.local"),
.tp_basicsize = sizeof(PySssLocalObject),
.tp_new = PySssLocalObject_new,
.tp_dealloc = (destructor) PySssLocalObject_dealloc,
.tp_methods = sss_local_methods,
.tp_members = sss_local_members,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = sss_py_const_p(char, "SSS DB manipulation"),
};
/* ==================== obfuscation python wrappers ========================*/
/*
* The sss.local object
*/
typedef struct {
PyObject_HEAD
int aes_256;
} PySssPasswordObject;
PyDoc_STRVAR(py_sss_encrypt__doc__,
"Obfuscate a password\n\n"
":param password: The password to obfuscate\n\n"
":param method: The obfuscation method\n\n");
static PyObject *py_sss_encrypt(PySssPasswordObject *self,
PyObject *args,
PyObject *kwds)
{
char *password = NULL;
int plen; /* may contain NULL bytes */
char *obfpwd = NULL;
TALLOC_CTX *tctx = NULL;
int ret;
int mode;
PyObject *retval = NULL;
/* parse arguments */
if (!PyArg_ParseTuple(args, discard_const_p(char, "s#i"),
&password, &plen, &mode)) {
return NULL;
}
tctx = talloc_new(NULL);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
ret = sss_password_encrypt(tctx, password, plen+1,
mode, &obfpwd);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
retval = Py_BuildValue(sss_py_const_p(char, "s"), obfpwd);
if (retval == NULL) {
goto fail;
}
fail:
talloc_zfree(tctx);
return retval;
}
#if 0
PyDoc_STRVAR(py_sss_decrypt__doc__,
"Deobfuscate a password\n\n"
":param obfpwd: The password to convert back to clear text\n\n");
static PyObject *py_sss_decrypt(PySssPasswordObject *self,
PyObject *args,
PyObject *kwds)
{
char *password = NULL;
char *obfpwd = NULL;
TALLOC_CTX *tctx = NULL;
int ret;
PyObject *retval = NULL;
/* parse arguments */
if (!PyArg_ParseTuple(args, discard_const_p(char, "s"),
&obfpwd)) {
return NULL;
}
tctx = talloc_new(NULL);
if (!tctx) {
PyErr_NoMemory();
return NULL;
}
ret = sss_password_decrypt(tctx, obfpwd, &password);
if (ret != EOK) {
PyErr_SetSssError(ret);
goto fail;
}
retval = Py_BuildValue("s", password);
if (retval == NULL) {
goto fail;
}
fail:
talloc_zfree(tctx);
return retval;
}
#endif
/*
* The sss.password destructor
*/
static void PySssPasswordObject_dealloc(PySssPasswordObject *self)
{
self->ob_type->tp_free((PyObject*) self);
}
/*
* The sss.password constructor
*/
static PyObject *PySssPasswordObject_new(PyTypeObject *type,
PyObject *args,
PyObject *kwds)
{
PySssPasswordObject *self;
self = (PySssPasswordObject *) type->tp_alloc(type, 0);
if (self == NULL) {
PyErr_NoMemory();
return NULL;
}
self->aes_256 = AES_256;
return (PyObject *) self;
}
/*
* sss.password object methods
*/
static PyMethodDef sss_password_methods[] = {
{ sss_py_const_p(char, "encrypt"), (PyCFunction) py_sss_encrypt,
METH_VARARGS | METH_STATIC, py_sss_encrypt__doc__
},
#if 0
{ "decrypt", (PyCFunction) py_sss_decrypt,
METH_VARARGS | METH_STATIC, py_sss_decrypt__doc__
},
#endif
{NULL, NULL, 0, NULL} /* Sentinel */
};
/*
* sss.password object members
*/
static PyMemberDef sss_password_members[] = {
{ discard_const_p(char, "AES_256"), T_INT,
offsetof(PySssPasswordObject, aes_256), RO, NULL},
{NULL, 0, 0, 0, NULL} /* Sentinel */
};
/*
* sss.password object properties
*/
static PyTypeObject pysss_password_type = {
PyObject_HEAD_INIT(NULL)
.tp_name = sss_py_const_p(char, "sss.password"),
.tp_basicsize = sizeof(PySssPasswordObject),
.tp_new = PySssPasswordObject_new,
.tp_dealloc = (destructor) PySssPasswordObject_dealloc,
.tp_methods = sss_password_methods,
.tp_members = sss_password_members,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = sss_py_const_p(char, "SSS password obfuscation"),
};
/* ==================== the sss module initialization =======================*/
/*
* Module methods
*/
static PyMethodDef module_methods[] = {
{"getgrouplist", py_sss_getgrouplist, METH_VARARGS, py_sss_getgrouplist__doc__},
{NULL, NULL, 0, NULL} /* Sentinel */
};
/*
* Module initialization
*/
PyMODINIT_FUNC
initpysss(void)
{
PyObject *m;
if (PyType_Ready(&pysss_local_type) < 0)
return;
if (PyType_Ready(&pysss_password_type) < 0)
return;
m = Py_InitModule(discard_const_p(char, "pysss"), module_methods);
if (m == NULL)
return;
Py_INCREF(&pysss_local_type);
PyModule_AddObject(m, discard_const_p(char, "local"), (PyObject *)&pysss_local_type);
Py_INCREF(&pysss_password_type);
PyModule_AddObject(m, discard_const_p(char, "password"), (PyObject *)&pysss_password_type);
}