Line data Source code
1 : /*
2 : Authors:
3 : Jakub Hrozek <jhrozek@redhat.com>
4 :
5 : Copyright (C) 2009 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <Python.h>
22 : #include <structmember.h>
23 : #include <talloc.h>
24 : #include <pwd.h>
25 : #include <grp.h>
26 :
27 : #include "util/util.h"
28 : #include "util/sss_python.h"
29 : #include "db/sysdb.h"
30 : #include "tools/tools_util.h"
31 : #include "tools/sss_sync_ops.h"
32 : #include "util/crypto/sss_crypto.h"
33 :
34 : /*
35 : * function taken from samba sources tree as of Aug 20 2009,
36 : * file source4/lib/ldb/pyldb.c
37 : */
38 0 : static char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list,
39 : const char *paramname)
40 : {
41 : char **ret;
42 : int i;
43 :
44 0 : ret = talloc_array(mem_ctx, char *, PyList_Size(list)+1);
45 0 : for (i = 0; i < PyList_Size(list); i++) {
46 : char *itemstr;
47 : Py_ssize_t itemlen;
48 0 : PyObject *item = PyList_GetItem(list, i);
49 : #ifdef IS_PY3K
50 0 : if (!PyUnicode_Check(item)) {
51 : #else
52 0 : if (!PyString_Check(item)) {
53 : #endif
54 0 : PyErr_Format(PyExc_TypeError, "%s should be strings", paramname);
55 0 : return NULL;
56 : }
57 : #ifdef IS_PY3K
58 0 : itemstr = PyUnicode_AsUTF8AndSize(item, &itemlen);
59 : #else
60 0 : itemstr = PyString_AsString(item);
61 0 : itemlen = strlen(itemstr);
62 : #endif
63 0 : ret[i] = talloc_strndup(ret, itemstr, itemlen);
64 : }
65 :
66 0 : ret[i] = NULL;
67 0 : return ret;
68 : }
69 :
70 : /* ======================= sysdb python wrappers ==========================*/
71 :
72 : /*
73 : * The sss.password object
74 : */
75 : typedef struct {
76 : PyObject_HEAD
77 :
78 : TALLOC_CTX *mem_ctx;
79 : struct tevent_context *ev;
80 : struct sysdb_ctx *sysdb;
81 : struct confdb_ctx *confdb;
82 :
83 : struct sss_domain_info *local;
84 :
85 : int lock;
86 : int unlock;
87 : } PySssLocalObject;
88 :
89 : /*
90 : * Error reporting
91 : */
92 0 : static void PyErr_SetSssErrorWithMessage(int ret, const char *message)
93 : {
94 0 : PyObject *exc = Py_BuildValue(discard_const_p(char, "(is)"),
95 : ret, message);
96 :
97 0 : PyErr_SetObject(PyExc_IOError, exc);
98 0 : Py_XDECREF(exc);
99 0 : }
100 :
101 0 : static void PyErr_SetSssError(int ret)
102 : {
103 0 : PyErr_SetSssErrorWithMessage(ret, strerror(ret));
104 0 : }
105 :
106 : /*
107 : * Common init of all methods
108 : */
109 0 : static struct tools_ctx *init_ctx(PySssLocalObject *self)
110 : {
111 0 : struct ops_ctx *octx = NULL;
112 0 : struct tools_ctx *tctx = NULL;
113 :
114 0 : tctx = talloc_zero(self->mem_ctx, struct tools_ctx);
115 0 : if (tctx == NULL) {
116 0 : return NULL;
117 : }
118 :
119 0 : tctx->confdb = self->confdb;
120 0 : tctx->sysdb = self->sysdb;
121 0 : tctx->local = self->local;
122 : /* tctx->nctx is NULL here, which is OK since we don't parse domains
123 : * in the python bindings (yet?) */
124 :
125 0 : octx = talloc_zero(tctx, struct ops_ctx);
126 0 : if (octx == NULL) {
127 0 : PyErr_NoMemory();
128 0 : return NULL;
129 : }
130 0 : octx->domain = self->local;
131 :
132 0 : tctx->octx = octx;
133 0 : return tctx;
134 : }
135 :
136 : /*
137 : * Add a user
138 : */
139 : PyDoc_STRVAR(py_sss_useradd__doc__,
140 : "Add a user named ``username``.\n\n"
141 : ":param username: name of the user\n\n"
142 : ":param kwargs: Keyword arguments that customize the operation\n\n"
143 : "* useradd can be customized further with keyword arguments:\n"
144 : " * ``uid``: The UID of the user\n"
145 : " * ``gid``: The GID of the user\n"
146 : " * ``gecos``: The comment string\n"
147 : " * ``homedir``: Home directory\n"
148 : " * ``shell``: Login shell\n"
149 : " * ``skel``: Specify an alternative skeleton directory\n"
150 : " * ``create_home``: (bool) Force creation of home directory on or off\n"
151 : " * ``groups``: List of groups the user is member of\n");
152 :
153 :
154 0 : static PyObject *py_sss_useradd(PySssLocalObject *self,
155 : PyObject *args,
156 : PyObject *kwds)
157 : {
158 0 : struct tools_ctx *tctx = NULL;
159 0 : unsigned long uid = 0;
160 0 : unsigned long gid = 0;
161 0 : const char *gecos = NULL;
162 0 : const char *home = NULL;
163 0 : const char *shell = NULL;
164 0 : const char *skel = NULL;
165 0 : char *username = NULL;
166 : int ret;
167 0 : const char * const kwlist[] = { "username", "uid", "gid", "gecos",
168 : "homedir", "shell", "skel",
169 : "create_home", "groups", NULL };
170 0 : PyObject *py_groups = Py_None;
171 0 : PyObject *py_create_home = Py_None;
172 0 : int create_home = 0;
173 0 : bool in_transaction = false;
174 :
175 : /* parse arguments */
176 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds,
177 : discard_const_p(char, "s|kkssssO!O!"),
178 : discard_const_p(char *, kwlist),
179 : &username,
180 : &uid,
181 : &gid,
182 : &gecos,
183 : &home,
184 : &shell,
185 : &skel,
186 : &PyBool_Type,
187 : &py_create_home,
188 : &PyList_Type,
189 : &py_groups)) {
190 0 : goto fail;
191 : }
192 :
193 0 : tctx = init_ctx(self);
194 0 : if (!tctx) {
195 0 : PyErr_NoMemory();
196 0 : return NULL;
197 : }
198 :
199 0 : if (py_groups != Py_None) {
200 0 : tctx->octx->addgroups = PyList_AsStringList(tctx, py_groups, "groups");
201 0 : if (!tctx->octx->addgroups) {
202 0 : PyErr_NoMemory();
203 0 : return NULL;
204 : }
205 : }
206 :
207 : /* user-wise the parameter is only bool - do or don't,
208 : * however we must have a third state - undecided, pick default */
209 0 : if (py_create_home == Py_True) {
210 0 : create_home = DO_CREATE_HOME;
211 0 : } else if (py_create_home == Py_False) {
212 0 : create_home = DO_NOT_CREATE_HOME;
213 : }
214 :
215 0 : tctx->octx->name = username;
216 0 : tctx->octx->uid = uid;
217 :
218 : /* fill in defaults */
219 0 : ret = useradd_defaults(tctx,
220 : self->confdb,
221 : tctx->octx, gecos,
222 : home, shell,
223 : create_home,
224 : skel);
225 0 : if (ret != EOK) {
226 0 : PyErr_SetSssError(ret);
227 0 : goto fail;
228 : }
229 :
230 : /* Add the user within a transaction */
231 0 : tctx->error = sysdb_transaction_start(tctx->sysdb);
232 0 : if (tctx->error != EOK) {
233 0 : PyErr_SetSssError(tctx->error);
234 0 : goto fail;
235 : }
236 0 : in_transaction = true;
237 :
238 : /* useradd */
239 0 : tctx->error = useradd(tctx, tctx->octx);
240 0 : if (tctx->error) {
241 0 : PyErr_SetSssError(tctx->error);
242 0 : goto fail;
243 : }
244 :
245 0 : tctx->error = sysdb_transaction_commit(tctx->sysdb);
246 0 : if (tctx->error) {
247 0 : PyErr_SetSssError(tctx->error);
248 0 : goto fail;
249 : }
250 0 : in_transaction = false;
251 :
252 : /* Create user's home directory and/or mail spool */
253 0 : if (tctx->octx->create_homedir) {
254 : /* We need to know the UID and GID of the user, if
255 : * sysdb did assign it automatically, do a lookup */
256 0 : if (tctx->octx->uid == 0 || tctx->octx->gid == 0) {
257 0 : ret = sysdb_getpwnam_sync(tctx,
258 0 : tctx->octx->name,
259 : tctx->octx);
260 0 : if (ret != EOK) {
261 0 : PyErr_SetSssError(ret);
262 0 : goto fail;
263 : }
264 : }
265 :
266 0 : ret = create_homedir(tctx->octx->skeldir,
267 0 : tctx->octx->home,
268 0 : tctx->octx->uid,
269 0 : tctx->octx->gid,
270 0 : tctx->octx->umask);
271 0 : if (ret != EOK) {
272 0 : PyErr_SetSssError(ret);
273 0 : goto fail;
274 : }
275 :
276 : /* failure here should not be fatal */
277 0 : create_mail_spool(tctx,
278 0 : tctx->octx->name,
279 0 : tctx->octx->maildir,
280 0 : tctx->octx->uid,
281 0 : tctx->octx->gid);
282 : }
283 :
284 0 : talloc_zfree(tctx);
285 0 : Py_RETURN_NONE;
286 :
287 : fail:
288 0 : if (in_transaction) {
289 : /* We do not handle return value of sysdb_transaction_cancel()
290 : * because we don't want to overwrite previous error code.
291 : */
292 0 : sysdb_transaction_cancel(tctx->sysdb);
293 : }
294 0 : talloc_zfree(tctx);
295 0 : return NULL;
296 : }
297 :
298 : /*
299 : * Delete a user
300 : */
301 : PyDoc_STRVAR(py_sss_userdel__doc__,
302 : "Remove the user named ``username``.\n\n"
303 : ":param username: Name of user being removed\n"
304 : ":param kwargs: Keyword arguments that customize the operation\n\n"
305 : "* userdel can be customized further with keyword arguments:\n"
306 : " * ``force``: (bool) Force removal of files not owned by the user\n"
307 : " * ``remove``: (bool) Toggle removing home directory and mail spool\n");
308 :
309 0 : static PyObject *py_sss_userdel(PySssLocalObject *self,
310 : PyObject *args,
311 : PyObject *kwds)
312 : {
313 0 : struct tools_ctx *tctx = NULL;
314 0 : char *username = NULL;
315 : int ret;
316 0 : PyObject *py_remove = Py_None;
317 0 : int remove_home = 0;
318 0 : PyObject *py_force = Py_None;
319 0 : const char * const kwlist[] = { "username", "remove", "force", NULL };
320 :
321 0 : if(!PyArg_ParseTupleAndKeywords(args, kwds,
322 : discard_const_p(char, "s|O!O!"),
323 : discard_const_p(char *, kwlist),
324 : &username,
325 : &PyBool_Type,
326 : &py_remove,
327 : &PyBool_Type,
328 : &py_force)) {
329 0 : goto fail;
330 : }
331 :
332 0 : tctx = init_ctx(self);
333 0 : if (!tctx) {
334 0 : PyErr_NoMemory();
335 0 : return NULL;
336 : }
337 :
338 0 : tctx->octx->name = username;
339 :
340 0 : if (py_remove == Py_True) {
341 0 : remove_home = DO_REMOVE_HOME;
342 0 : } else if (py_remove == Py_False) {
343 0 : remove_home = DO_NOT_REMOVE_HOME;
344 : }
345 :
346 : /*
347 : * Fills in defaults for ops_ctx user did not specify.
348 : */
349 0 : ret = userdel_defaults(tctx,
350 : tctx->confdb,
351 : tctx->octx,
352 : remove_home);
353 0 : if (ret != EOK) {
354 0 : PyErr_SetSssError(ret);
355 0 : goto fail;
356 : }
357 :
358 0 : ret = run_userdel_cmd(tctx);
359 0 : if (ret != EOK) {
360 0 : PyErr_SetSssError(ret);
361 0 : goto fail;
362 : }
363 :
364 0 : if (tctx->octx->remove_homedir) {
365 0 : ret = sysdb_getpwnam_sync(tctx,
366 0 : tctx->octx->name,
367 : tctx->octx);
368 0 : if (ret != EOK) {
369 0 : PyErr_SetSssError(ret);
370 0 : goto fail;
371 : }
372 : }
373 :
374 : /* Delete the user */
375 0 : ret = userdel(tctx, self->sysdb, tctx->octx);
376 0 : if (ret != EOK) {
377 0 : PyErr_SetSssError(ret);
378 0 : goto fail;
379 : }
380 :
381 0 : if (tctx->octx->remove_homedir) {
382 0 : ret = remove_homedir(tctx,
383 0 : tctx->octx->home,
384 0 : tctx->octx->maildir,
385 0 : tctx->octx->name,
386 0 : tctx->octx->uid,
387 : (py_force == Py_True));
388 0 : if (ret != EOK) {
389 0 : PyErr_SetSssError(ret);
390 0 : goto fail;
391 : }
392 : }
393 :
394 0 : talloc_zfree(tctx);
395 0 : Py_RETURN_NONE;
396 :
397 : fail:
398 0 : talloc_zfree(tctx);
399 0 : return NULL;
400 : }
401 :
402 : /*
403 : * Modify a user
404 : */
405 : PyDoc_STRVAR(py_sss_usermod__doc__,
406 : "Modify a user.\n\n"
407 : ":param username: Name of user being modified\n\n"
408 : ":param kwargs: Keyword arguments that customize the operation\n\n"
409 : "* usermod can be customized further with keyword arguments:\n"
410 : " * ``uid``: The UID of the user\n"
411 : " * ``gid``: The GID of the user\n"
412 : " * ``gecos``: The comment string\n"
413 : " * ``homedir``: Home directory\n"
414 : " * ``shell``: Login shell\n"
415 : " * ``addgroups``: List of groups to add the user to\n"
416 : " * ``rmgroups``: List of groups to remove the user from\n"
417 : " * ``lock``: Lock or unlock the account\n");
418 :
419 0 : static PyObject *py_sss_usermod(PySssLocalObject *self,
420 : PyObject *args,
421 : PyObject *kwds)
422 : {
423 0 : struct tools_ctx *tctx = NULL;
424 0 : PyObject *py_addgroups = Py_None;
425 0 : PyObject *py_rmgroups = Py_None;
426 0 : unsigned long uid = 0;
427 0 : unsigned long gid = 0;
428 0 : char *gecos = NULL;
429 0 : char *home = NULL;
430 0 : char *shell = NULL;
431 0 : char *username = NULL;
432 0 : unsigned long lock = 0;
433 0 : const char * const kwlist[] = { "username", "uid", "gid", "lock",
434 : "gecos", "homedir", "shell",
435 : "addgroups", "rmgroups", NULL };
436 0 : bool in_transaction = false;
437 :
438 : /* parse arguments */
439 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds,
440 : discard_const_p(char, "s|kkksssO!O!"),
441 : discard_const_p(char *, kwlist),
442 : &username,
443 : &uid,
444 : &gid,
445 : &lock,
446 : &gecos,
447 : &home,
448 : &shell,
449 : &PyList_Type,
450 : &py_addgroups,
451 : &PyList_Type,
452 : &py_rmgroups)) {
453 0 : goto fail;
454 : }
455 :
456 0 : tctx = init_ctx(self);
457 0 : if (!tctx) {
458 0 : PyErr_NoMemory();
459 0 : return NULL;
460 : }
461 :
462 0 : if (lock && lock != DO_LOCK && lock != DO_UNLOCK) {
463 0 : PyErr_SetString(PyExc_ValueError,
464 : "Unkown value for lock parameter");
465 0 : goto fail;
466 : }
467 :
468 0 : if (py_addgroups != Py_None) {
469 0 : tctx->octx->addgroups = PyList_AsStringList(tctx,
470 : py_addgroups,
471 : "addgroups");
472 0 : if (!tctx->octx->addgroups) {
473 0 : return NULL;
474 : }
475 : }
476 :
477 0 : if (py_rmgroups != Py_None) {
478 0 : tctx->octx->rmgroups = PyList_AsStringList(tctx,
479 : py_rmgroups,
480 : "rmgroups");
481 0 : if (!tctx->octx->rmgroups) {
482 0 : return NULL;
483 : }
484 : }
485 :
486 0 : tctx->octx->name = username;
487 0 : tctx->octx->uid = uid;
488 0 : tctx->octx->gid = gid;
489 0 : tctx->octx->gecos = gecos;
490 0 : tctx->octx->home = home;
491 0 : tctx->octx->shell = shell;
492 0 : tctx->octx->lock = lock;
493 :
494 : /* Modify the user within a transaction */
495 0 : tctx->error = sysdb_transaction_start(tctx->sysdb);
496 0 : if (tctx->error != EOK) {
497 0 : PyErr_SetSssError(tctx->error);
498 0 : goto fail;
499 : }
500 0 : in_transaction = true;
501 :
502 : /* usermod */
503 0 : tctx->error = usermod(tctx, tctx->octx);
504 0 : if (tctx->error) {
505 0 : PyErr_SetSssError(tctx->error);
506 0 : goto fail;
507 : }
508 :
509 0 : tctx->error = sysdb_transaction_commit(tctx->sysdb);
510 0 : if (tctx->error) {
511 0 : PyErr_SetSssError(tctx->error);
512 0 : goto fail;
513 : }
514 0 : in_transaction = false;
515 :
516 0 : talloc_zfree(tctx);
517 0 : Py_RETURN_NONE;
518 :
519 : fail:
520 0 : if (in_transaction) {
521 : /* We do not handle return value of sysdb_transaction_cancel()
522 : * because we don't want to overwrite previous error code.
523 : */
524 0 : sysdb_transaction_cancel(tctx->sysdb);
525 : }
526 0 : talloc_zfree(tctx);
527 0 : return NULL;
528 : }
529 :
530 : /*
531 : * Add a group
532 : */
533 : PyDoc_STRVAR(py_sss_groupadd__doc__,
534 : "Add a group.\n\n"
535 : ":param groupname: Name of group being added\n\n"
536 : ":param kwargs: Keyword arguments ro customize the operation\n\n"
537 : "* groupmod can be customized further with keyword arguments:\n"
538 : " * ``gid``: The GID of the group\n");
539 :
540 0 : static PyObject *py_sss_groupadd(PySssLocalObject *self,
541 : PyObject *args,
542 : PyObject *kwds)
543 : {
544 0 : struct tools_ctx *tctx = NULL;
545 : char *groupname;
546 0 : unsigned long gid = 0;
547 0 : const char * const kwlist[] = { "groupname", "gid", NULL };
548 0 : bool in_transaction = false;
549 :
550 : /* parse arguments */
551 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds,
552 : discard_const_p(char, "s|k"),
553 : discard_const_p(char *, kwlist),
554 : &groupname,
555 : &gid)) {
556 0 : goto fail;
557 : }
558 :
559 0 : tctx = init_ctx(self);
560 0 : if (!tctx) {
561 0 : PyErr_NoMemory();
562 0 : return NULL;
563 : }
564 :
565 0 : tctx->octx->name = groupname;
566 0 : tctx->octx->gid = gid;
567 :
568 : /* Add the group within a transaction */
569 0 : tctx->error = sysdb_transaction_start(tctx->sysdb);
570 0 : if (tctx->error != EOK) {
571 0 : PyErr_SetSssError(tctx->error);
572 0 : goto fail;
573 : }
574 0 : in_transaction = true;
575 :
576 : /* groupadd */
577 0 : tctx->error = groupadd(tctx->octx);
578 0 : if (tctx->error) {
579 0 : PyErr_SetSssError(tctx->error);
580 0 : goto fail;
581 : }
582 :
583 0 : tctx->error = sysdb_transaction_commit(tctx->sysdb);
584 0 : if (tctx->error) {
585 0 : PyErr_SetSssError(tctx->error);
586 0 : goto fail;
587 : }
588 0 : in_transaction = false;
589 :
590 0 : talloc_zfree(tctx);
591 0 : Py_RETURN_NONE;
592 :
593 : fail:
594 0 : if (in_transaction) {
595 : /* We do not handle return value of sysdb_transaction_cancel()
596 : * because we don't want to overwrite previous error code.
597 : */
598 0 : sysdb_transaction_cancel(tctx->sysdb);
599 : }
600 0 : talloc_zfree(tctx);
601 0 : return NULL;
602 : }
603 :
604 : /*
605 : * Delete a group
606 : */
607 : PyDoc_STRVAR(py_sss_groupdel__doc__,
608 : "Remove a group.\n\n"
609 : ":param groupname: Name of group being removed\n");
610 :
611 0 : static PyObject *py_sss_groupdel(PySssLocalObject *self,
612 : PyObject *args,
613 : PyObject *kwds)
614 : {
615 0 : struct tools_ctx *tctx = NULL;
616 0 : char *groupname = NULL;
617 : int ret;
618 :
619 0 : if(!PyArg_ParseTuple(args, discard_const_p(char, "s"), &groupname)) {
620 0 : goto fail;
621 : }
622 :
623 0 : tctx = init_ctx(self);
624 0 : if (!tctx) {
625 0 : PyErr_NoMemory();
626 0 : return NULL;
627 : }
628 :
629 0 : tctx->octx->name = groupname;
630 :
631 : /* Remove the group */
632 0 : ret = groupdel(tctx, self->sysdb, tctx->octx);
633 0 : if (ret != EOK) {
634 0 : PyErr_SetSssError(ret);
635 0 : goto fail;
636 : }
637 :
638 0 : talloc_zfree(tctx);
639 0 : Py_RETURN_NONE;
640 :
641 : fail:
642 0 : talloc_zfree(tctx);
643 0 : return NULL;
644 : }
645 :
646 : /*
647 : * Modify a group
648 : */
649 : PyDoc_STRVAR(py_sss_groupmod__doc__,
650 : "Modify a group.\n\n"
651 : ":param groupname: Name of group being modified\n\n"
652 : ":param kwargs: Keyword arguments ro customize the operation\n\n"
653 : "* groupmod can be customized further with keyword arguments:\n"
654 : " * ``gid``: The GID of the group\n\n"
655 : " * ``addgroups``: Groups to add the group to\n\n"
656 : " * ``rmgroups``: Groups to remove the group from\n\n");
657 :
658 0 : static PyObject *py_sss_groupmod(PySssLocalObject *self,
659 : PyObject *args,
660 : PyObject *kwds)
661 : {
662 0 : struct tools_ctx *tctx = NULL;
663 0 : PyObject *py_addgroups = Py_None;
664 0 : PyObject *py_rmgroups = Py_None;
665 0 : unsigned long gid = 0;
666 0 : char *groupname = NULL;
667 0 : const char * const kwlist[] = { "groupname", "gid", "addgroups",
668 : "rmgroups", NULL };
669 0 : bool in_transaction = false;
670 :
671 : /* parse arguments */
672 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds,
673 : discard_const_p(char, "s|kO!O!"),
674 : discard_const_p(char *, kwlist),
675 : &groupname,
676 : &gid,
677 : &PyList_Type,
678 : &py_addgroups,
679 : &PyList_Type,
680 : &py_rmgroups)) {
681 0 : goto fail;
682 : }
683 :
684 0 : tctx = init_ctx(self);
685 0 : if (!tctx) {
686 0 : PyErr_NoMemory();
687 0 : return NULL;
688 : }
689 :
690 0 : if (py_addgroups != Py_None) {
691 0 : tctx->octx->addgroups = PyList_AsStringList(tctx,
692 : py_addgroups,
693 : "addgroups");
694 0 : if (!tctx->octx->addgroups) {
695 0 : return NULL;
696 : }
697 : }
698 :
699 0 : if (py_rmgroups != Py_None) {
700 0 : tctx->octx->rmgroups = PyList_AsStringList(tctx,
701 : py_rmgroups,
702 : "rmgroups");
703 0 : if (!tctx->octx->rmgroups) {
704 0 : return NULL;
705 : }
706 : }
707 :
708 0 : tctx->octx->name = groupname;
709 0 : tctx->octx->gid = gid;
710 :
711 : /* Modify the group within a transaction */
712 0 : tctx->error = sysdb_transaction_start(tctx->sysdb);
713 0 : if (tctx->error != EOK) {
714 0 : PyErr_SetSssError(tctx->error);
715 0 : goto fail;
716 : }
717 0 : in_transaction = true;
718 :
719 : /* groupmod */
720 0 : tctx->error = groupmod(tctx, tctx->octx);
721 0 : if (tctx->error) {
722 0 : PyErr_SetSssError(tctx->error);
723 0 : goto fail;
724 : }
725 :
726 0 : tctx->error = sysdb_transaction_commit(tctx->sysdb);
727 0 : if (tctx->error) {
728 0 : PyErr_SetSssError(tctx->error);
729 0 : goto fail;
730 : }
731 0 : in_transaction = false;
732 :
733 0 : talloc_zfree(tctx);
734 0 : Py_RETURN_NONE;
735 :
736 : fail:
737 0 : if (in_transaction) {
738 : /* We do not handle return value of sysdb_transaction_cancel()
739 : * because we don't want to overwrite previous error code.
740 : */
741 0 : sysdb_transaction_cancel(tctx->sysdb);
742 : }
743 0 : talloc_zfree(tctx);
744 0 : return NULL;
745 : }
746 :
747 : /*
748 : * Get list of groups user belongs to
749 : */
750 : PyDoc_STRVAR(py_sss_getgrouplist__doc__,
751 : "Get list of groups user belongs to.\n\n"
752 : "NOTE: The interface uses the system NSS calls and is not limited to "
753 : "users served by the SSSD!\n"
754 : ":param username: name of user to get list for\n");
755 :
756 0 : static PyObject *py_sss_getgrouplist(PyObject *self, PyObject *args)
757 : {
758 0 : char *username = NULL;
759 0 : gid_t *groups = NULL;
760 : struct passwd *pw;
761 : struct group *gr;
762 : int ngroups;
763 : int ret;
764 : Py_ssize_t i, idx;
765 : PyObject *groups_tuple;
766 :
767 0 : if(!PyArg_ParseTuple(args, discard_const_p(char, "s"), &username)) {
768 0 : goto fail;
769 : }
770 :
771 0 : pw = getpwnam(username);
772 0 : if (pw == NULL) {
773 0 : goto fail;
774 : }
775 :
776 0 : ngroups = 32;
777 0 : groups = malloc(sizeof(gid_t) * ngroups);
778 0 : if (groups == NULL) {
779 0 : goto fail;
780 : }
781 :
782 : do {
783 0 : ret = getgrouplist(username, pw->pw_gid, groups, &ngroups);
784 0 : if (ret < ngroups) {
785 0 : gid_t *tmp_groups = realloc(groups, ngroups * sizeof(gid_t));
786 0 : if (tmp_groups == NULL) {
787 0 : goto fail;
788 : }
789 0 : groups = tmp_groups;
790 : }
791 0 : } while (ret != ngroups);
792 :
793 0 : groups_tuple = PyTuple_New((Py_ssize_t) ngroups);
794 0 : if (groups_tuple == NULL) {
795 0 : goto fail;
796 : }
797 :
798 : /* Populate a tuple with names of groups
799 : * In unlikely case of group not being able to resolve, skip it
800 : * We also need to resize resulting tuple to avoid empty elements there */
801 0 : idx = 0;
802 0 : for (i = 0; i < ngroups; i++) {
803 0 : gr = getgrgid(groups[i]);
804 0 : if (gr) {
805 0 : PyTuple_SetItem(groups_tuple, idx,
806 : #ifdef IS_PY3K
807 0 : PyUnicode_FromString(gr->gr_name)
808 : #else
809 0 : PyString_FromString(gr->gr_name)
810 : #endif
811 : );
812 0 : idx++;
813 : }
814 : }
815 0 : free(groups);
816 0 : groups = NULL;
817 :
818 0 : if (i != idx) {
819 0 : _PyTuple_Resize(&groups_tuple, idx);
820 : }
821 :
822 0 : return groups_tuple;
823 :
824 : fail:
825 0 : free(groups);
826 0 : return NULL;
827 : }
828 :
829 : /*** python plumbing begins here ***/
830 :
831 : /*
832 : * The sss.local destructor
833 : */
834 0 : static void PySssLocalObject_dealloc(PySssLocalObject *self)
835 : {
836 0 : talloc_free(self->mem_ctx);
837 0 : Py_TYPE(self)->tp_free((PyObject *)self);
838 0 : }
839 :
840 : /*
841 : * The sss.local constructor
842 : */
843 0 : static PyObject *PySssLocalObject_new(PyTypeObject *type,
844 : PyObject *args,
845 : PyObject *kwds)
846 : {
847 : TALLOC_CTX *mem_ctx;
848 : PySssLocalObject *self;
849 : char *confdb_path;
850 : int ret;
851 :
852 0 : mem_ctx = talloc_new(NULL);
853 0 : if (mem_ctx == NULL) {
854 0 : PyErr_NoMemory();
855 0 : return NULL;
856 : }
857 :
858 0 : self = (PySssLocalObject *) type->tp_alloc(type, 0);
859 0 : if (self == NULL) {
860 0 : talloc_free(mem_ctx);
861 0 : PyErr_NoMemory();
862 0 : return NULL;
863 : }
864 0 : self->mem_ctx = mem_ctx;
865 :
866 0 : confdb_path = talloc_asprintf(self->mem_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
867 0 : if (confdb_path == NULL) {
868 0 : PyErr_NoMemory();
869 0 : goto fail;
870 : }
871 :
872 : /* Connect to the conf db */
873 0 : ret = confdb_init(self->mem_ctx, &self->confdb, confdb_path);
874 0 : if (ret != EOK) {
875 0 : PyErr_SetSssErrorWithMessage(ret,
876 : "Could not initialize connection to the confdb\n");
877 0 : goto fail;
878 : }
879 :
880 0 : ret = sssd_domain_init(self->mem_ctx, self->confdb, "local",
881 : DB_PATH, &self->local);
882 0 : if (ret != EOK) {
883 0 : PyErr_SetSssErrorWithMessage(ret,
884 : "Could not initialize connection to the sysdb\n");
885 0 : goto fail;
886 : }
887 0 : self->sysdb = self->local->sysdb;
888 :
889 0 : self->lock = DO_LOCK;
890 0 : self->unlock = DO_UNLOCK;
891 :
892 0 : return (PyObject *) self;
893 :
894 : fail:
895 0 : Py_DECREF(self);
896 0 : return NULL;
897 : }
898 :
899 : /*
900 : * sss.local object methods
901 : */
902 : static PyMethodDef sss_local_methods[] = {
903 : { sss_py_const_p(char, "useradd"), (PyCFunction) py_sss_useradd,
904 : METH_KEYWORDS, py_sss_useradd__doc__
905 : },
906 : { sss_py_const_p(char, "userdel"), (PyCFunction) py_sss_userdel,
907 : METH_KEYWORDS, py_sss_userdel__doc__
908 : },
909 : { sss_py_const_p(char, "usermod"), (PyCFunction) py_sss_usermod,
910 : METH_KEYWORDS, py_sss_usermod__doc__
911 : },
912 : { sss_py_const_p(char, "groupadd"), (PyCFunction) py_sss_groupadd,
913 : METH_KEYWORDS, py_sss_groupadd__doc__
914 : },
915 : { sss_py_const_p(char, "groupdel"), (PyCFunction) py_sss_groupdel,
916 : METH_KEYWORDS, py_sss_groupdel__doc__
917 : },
918 : { sss_py_const_p(char, "groupmod"), (PyCFunction) py_sss_groupmod,
919 : METH_KEYWORDS, py_sss_groupmod__doc__
920 : },
921 : {NULL, NULL, 0, NULL} /* Sentinel */
922 : };
923 :
924 : static PyMemberDef sss_local_members[] = {
925 : { discard_const_p(char, "lock"), T_INT,
926 : offsetof(PySssLocalObject, lock), READONLY, NULL},
927 : { discard_const_p(char, "unlock"), T_INT,
928 : offsetof(PySssLocalObject, unlock), READONLY, NULL},
929 : {NULL, 0, 0, 0, NULL} /* Sentinel */
930 : };
931 :
932 : /*
933 : * sss.local object properties
934 : */
935 : static PyTypeObject pysss_local_type = {
936 : PyVarObject_HEAD_INIT(NULL, 0)
937 : .tp_name = sss_py_const_p(char, "sss.local"),
938 : .tp_basicsize = sizeof(PySssLocalObject),
939 : .tp_new = PySssLocalObject_new,
940 : .tp_dealloc = (destructor) PySssLocalObject_dealloc,
941 : .tp_methods = sss_local_methods,
942 : .tp_members = sss_local_members,
943 : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
944 : .tp_doc = sss_py_const_p(char, "SSS DB manipulation"),
945 : };
946 :
947 : /* ==================== obfuscation python wrappers ========================*/
948 :
949 : /*
950 : * The sss.local object
951 : */
952 : typedef struct {
953 : PyObject_HEAD
954 :
955 : int aes_256;
956 : } PySssPasswordObject;
957 :
958 : PyDoc_STRVAR(py_sss_encrypt__doc__,
959 : "Obfuscate a password\n\n"
960 : ":param password: The password to obfuscate\n\n"
961 : ":param method: The obfuscation method\n\n");
962 :
963 0 : static PyObject *py_sss_encrypt(PySssPasswordObject *self,
964 : PyObject *args,
965 : PyObject *kwds)
966 : {
967 0 : char *password = NULL;
968 : int plen; /* may contain NULL bytes */
969 0 : char *obfpwd = NULL;
970 0 : TALLOC_CTX *tctx = NULL;
971 : int ret;
972 : int mode;
973 0 : PyObject *retval = NULL;
974 :
975 : /* parse arguments */
976 0 : if (!PyArg_ParseTuple(args, discard_const_p(char, "s#i"),
977 : &password, &plen, &mode)) {
978 0 : return NULL;
979 : }
980 :
981 0 : tctx = talloc_new(NULL);
982 0 : if (!tctx) {
983 0 : PyErr_NoMemory();
984 0 : return NULL;
985 : }
986 :
987 0 : ret = sss_password_encrypt(tctx, password, plen+1,
988 : mode, &obfpwd);
989 0 : if (ret != EOK) {
990 0 : PyErr_SetSssError(ret);
991 0 : goto fail;
992 : }
993 :
994 0 : retval = Py_BuildValue(sss_py_const_p(char, "s"), obfpwd);
995 0 : if (retval == NULL) {
996 0 : goto fail;
997 : }
998 :
999 : fail:
1000 0 : talloc_zfree(tctx);
1001 0 : return retval;
1002 : }
1003 :
1004 : #if 0
1005 : PyDoc_STRVAR(py_sss_decrypt__doc__,
1006 : "Deobfuscate a password\n\n"
1007 : ":param obfpwd: The password to convert back to clear text\n\n");
1008 :
1009 : static PyObject *py_sss_decrypt(PySssPasswordObject *self,
1010 : PyObject *args,
1011 : PyObject *kwds)
1012 : {
1013 : char *password = NULL;
1014 : char *obfpwd = NULL;
1015 : TALLOC_CTX *tctx = NULL;
1016 : int ret;
1017 : PyObject *retval = NULL;
1018 :
1019 : /* parse arguments */
1020 : if (!PyArg_ParseTuple(args, discard_const_p(char, "s"),
1021 : &obfpwd)) {
1022 : return NULL;
1023 : }
1024 :
1025 : tctx = talloc_new(NULL);
1026 : if (!tctx) {
1027 : PyErr_NoMemory();
1028 : return NULL;
1029 : }
1030 :
1031 : ret = sss_password_decrypt(tctx, obfpwd, &password);
1032 : if (ret != EOK) {
1033 : PyErr_SetSssError(ret);
1034 : goto fail;
1035 : }
1036 :
1037 : retval = Py_BuildValue("s", password);
1038 : if (retval == NULL) {
1039 : goto fail;
1040 : }
1041 :
1042 : fail:
1043 : talloc_zfree(tctx);
1044 : return retval;
1045 : }
1046 : #endif
1047 :
1048 : /*
1049 : * The sss.password destructor
1050 : */
1051 0 : static void PySssPasswordObject_dealloc(PySssPasswordObject *self)
1052 : {
1053 0 : Py_TYPE(self)->tp_free((PyObject*) self);
1054 0 : }
1055 :
1056 : /*
1057 : * The sss.password constructor
1058 : */
1059 0 : static PyObject *PySssPasswordObject_new(PyTypeObject *type,
1060 : PyObject *args,
1061 : PyObject *kwds)
1062 : {
1063 : PySssPasswordObject *self;
1064 :
1065 0 : self = (PySssPasswordObject *) type->tp_alloc(type, 0);
1066 0 : if (self == NULL) {
1067 0 : PyErr_NoMemory();
1068 0 : return NULL;
1069 : }
1070 :
1071 0 : self->aes_256 = AES_256;
1072 :
1073 0 : return (PyObject *) self;
1074 : }
1075 :
1076 : /*
1077 : * sss.password object methods
1078 : */
1079 : static PyMethodDef sss_password_methods[] = {
1080 : { sss_py_const_p(char, "encrypt"), (PyCFunction) py_sss_encrypt,
1081 : METH_VARARGS | METH_STATIC, py_sss_encrypt__doc__
1082 : },
1083 : #if 0
1084 : { "decrypt", (PyCFunction) py_sss_decrypt,
1085 : METH_VARARGS | METH_STATIC, py_sss_decrypt__doc__
1086 : },
1087 : #endif
1088 : {NULL, NULL, 0, NULL} /* Sentinel */
1089 : };
1090 :
1091 : /*
1092 : * sss.password object members
1093 : */
1094 : static PyMemberDef sss_password_members[] = {
1095 : { discard_const_p(char, "AES_256"), T_INT,
1096 : offsetof(PySssPasswordObject, aes_256), READONLY, NULL},
1097 : {NULL, 0, 0, 0, NULL} /* Sentinel */
1098 : };
1099 :
1100 : /*
1101 : * sss.password object properties
1102 : */
1103 : static PyTypeObject pysss_password_type = {
1104 : PyVarObject_HEAD_INIT(NULL, 0)
1105 : .tp_name = sss_py_const_p(char, "sss.password"),
1106 : .tp_basicsize = sizeof(PySssPasswordObject),
1107 : .tp_new = PySssPasswordObject_new,
1108 : .tp_dealloc = (destructor) PySssPasswordObject_dealloc,
1109 : .tp_methods = sss_password_methods,
1110 : .tp_members = sss_password_members,
1111 : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1112 : .tp_doc = sss_py_const_p(char, "SSS password obfuscation"),
1113 : };
1114 :
1115 : /* ==================== the sss module initialization =======================*/
1116 :
1117 : /*
1118 : * Module methods
1119 : */
1120 : static PyMethodDef module_methods[] = {
1121 : {"getgrouplist", py_sss_getgrouplist, METH_VARARGS, py_sss_getgrouplist__doc__},
1122 : {NULL, NULL, 0, NULL} /* Sentinel */
1123 : };
1124 :
1125 : /*
1126 : * Module initialization
1127 : */
1128 : #ifdef IS_PY3K
1129 : static struct PyModuleDef pysssdef = {
1130 : PyModuleDef_HEAD_INIT,
1131 : "pysss",
1132 : NULL,
1133 : -1,
1134 : module_methods,
1135 : NULL,
1136 : NULL,
1137 : NULL,
1138 : NULL
1139 : };
1140 :
1141 : PyMODINIT_FUNC
1142 0 : PyInit_pysss(void)
1143 : #else
1144 : PyMODINIT_FUNC
1145 0 : initpysss(void)
1146 : #endif
1147 : {
1148 : PyObject *m;
1149 :
1150 0 : if (PyType_Ready(&pysss_local_type) < 0)
1151 0 : MODINITERROR;
1152 0 : if (PyType_Ready(&pysss_password_type) < 0)
1153 0 : MODINITERROR;
1154 :
1155 : #ifdef IS_PY3K
1156 0 : m = PyModule_Create(&pysssdef);
1157 : #else
1158 0 : m = Py_InitModule(discard_const_p(char, "pysss"), module_methods);
1159 : #endif
1160 0 : if (m == NULL)
1161 0 : MODINITERROR;
1162 :
1163 0 : Py_INCREF(&pysss_local_type);
1164 0 : PyModule_AddObject(m, discard_const_p(char, "local"), (PyObject *)&pysss_local_type);
1165 0 : Py_INCREF(&pysss_password_type);
1166 0 : PyModule_AddObject(m, discard_const_p(char, "password"), (PyObject *)&pysss_password_type);
1167 :
1168 : #ifdef IS_PY3K
1169 0 : return m;
1170 : #endif
1171 : }
1172 :
|