Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Copyright (C) Luke Morrison <luc785@hotmail.com> 2013
4 :
5 : This program is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as published by
7 : the Free Software Foundation; either version 3 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public License
16 : along with this program. If not, see <http://www.gnu.org/licenses/>.
17 : */
18 :
19 : #include <Python.h>
20 : #include "includes.h"
21 : #include "version.h"
22 : #include "param/pyparam.h"
23 : #include "gpo.h"
24 : #include "ads.h"
25 : #include "secrets.h"
26 : #include "../libds/common/flags.h"
27 : #include "librpc/rpc/pyrpc_util.h"
28 : #include "auth/credentials/pycredentials.h"
29 : #include "libcli/util/pyerrors.h"
30 : #include "python/py3compat.h"
31 : #include "python/modules.h"
32 : #include <pytalloc.h>
33 :
34 : /* A Python C API module to use LIBGPO */
35 :
36 : #define GPO_getter(ATTR) \
37 : static PyObject* GPO_get_##ATTR(PyObject *self, void *closure) \
38 : { \
39 : struct GROUP_POLICY_OBJECT *gpo_ptr \
40 : = pytalloc_get_ptr(self); \
41 : \
42 : if (gpo_ptr->ATTR) \
43 : return PyUnicode_FromString(gpo_ptr->ATTR); \
44 : else \
45 : Py_RETURN_NONE; \
46 : }
47 0 : GPO_getter(ds_path)
48 0 : GPO_getter(file_sys_path)
49 0 : GPO_getter(display_name)
50 0 : GPO_getter(name)
51 0 : GPO_getter(link)
52 0 : GPO_getter(user_extensions)
53 0 : GPO_getter(machine_extensions)
54 :
55 : static PyGetSetDef GPO_setters[] = {
56 : {discard_const_p(char, "ds_path"), (getter)GPO_get_ds_path, NULL, NULL,
57 : NULL},
58 : {discard_const_p(char, "file_sys_path"), (getter)GPO_get_file_sys_path,
59 : NULL, NULL, NULL},
60 : {discard_const_p(char, "display_name"), (getter)GPO_get_display_name,
61 : NULL, NULL, NULL},
62 : {discard_const_p(char, "name"), (getter)GPO_get_name, NULL, NULL,
63 : NULL},
64 : {discard_const_p(char, "link"), (getter)GPO_get_link, NULL, NULL,
65 : NULL},
66 : {discard_const_p(char, "user_extensions"),
67 : (getter)GPO_get_user_extensions,
68 : NULL, NULL, NULL},
69 : {discard_const_p(char, "machine_extensions"),
70 : (getter)GPO_get_machine_extensions, NULL, NULL, NULL},
71 : {0}
72 : };
73 :
74 0 : static PyObject *py_gpo_get_unix_path(PyObject *self, PyObject *args,
75 : PyObject *kwds)
76 : {
77 : NTSTATUS status;
78 0 : const char *cache_dir = NULL;
79 0 : PyObject *ret = NULL;
80 0 : char *unix_path = NULL;
81 0 : TALLOC_CTX *frame = NULL;
82 : static const char *kwlist[] = {"cache_dir", NULL};
83 0 : struct GROUP_POLICY_OBJECT *gpo_ptr \
84 0 : = (struct GROUP_POLICY_OBJECT *)pytalloc_get_ptr(self);
85 :
86 0 : frame = talloc_stackframe();
87 :
88 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s",
89 : discard_const_p(char *, kwlist),
90 : &cache_dir)) {
91 0 : goto out;
92 : }
93 :
94 0 : if (!cache_dir) {
95 0 : cache_dir = cache_path(talloc_tos(), GPO_CACHE_DIR);
96 0 : if (!cache_dir) {
97 0 : PyErr_SetString(PyExc_MemoryError,
98 : "Failed to determine gpo cache dir");
99 0 : goto out;
100 : }
101 : }
102 :
103 0 : status = gpo_get_unix_path(frame, cache_dir, gpo_ptr, &unix_path);
104 :
105 0 : if (!NT_STATUS_IS_OK(status)) {
106 0 : PyErr_Format(PyExc_RuntimeError,
107 : "Failed to determine gpo unix path: %s",
108 : get_friendly_nt_error_msg(status));
109 0 : goto out;
110 : }
111 :
112 0 : ret = PyUnicode_FromString(unix_path);
113 :
114 0 : out:
115 0 : TALLOC_FREE(frame);
116 0 : return ret;
117 : }
118 :
119 : static PyMethodDef GPO_methods[] = {
120 : {"get_unix_path", PY_DISCARD_FUNC_SIG(PyCFunction,
121 : py_gpo_get_unix_path),
122 : METH_VARARGS | METH_KEYWORDS,
123 : NULL },
124 : {0}
125 : };
126 :
127 : static PyTypeObject GPOType = {
128 : PyVarObject_HEAD_INIT(NULL, 0)
129 : .tp_name = "gpo.GROUP_POLICY_OBJECT",
130 : .tp_doc = "GROUP_POLICY_OBJECT",
131 : .tp_getset = GPO_setters,
132 : .tp_methods = GPO_methods,
133 : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
134 : };
135 :
136 : typedef struct {
137 : PyObject_HEAD
138 : ADS_STRUCT *ads_ptr;
139 : PyObject *py_creds;
140 : struct cli_credentials *cli_creds;
141 : } ADS;
142 :
143 0 : static void py_ads_dealloc(ADS* self)
144 : {
145 0 : TALLOC_FREE(self->ads_ptr);
146 0 : Py_CLEAR(self->py_creds);
147 0 : Py_TYPE(self)->tp_free((PyObject*)self);
148 0 : }
149 :
150 : static PyObject* py_ads_connect(ADS *self, PyObject *Py_UNUSED(ignored));
151 0 : static int py_ads_init(ADS *self, PyObject *args, PyObject *kwds)
152 : {
153 0 : const char *realm = NULL;
154 0 : const char *workgroup = NULL;
155 0 : const char *ldap_server = NULL;
156 0 : PyObject *lp_obj = NULL;
157 0 : PyObject *py_creds = NULL;
158 0 : struct loadparm_context *lp_ctx = NULL;
159 0 : bool ok = false;
160 :
161 : static const char *kwlist[] = {
162 : "ldap_server", "loadparm_context", "credentials", NULL
163 : };
164 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|O",
165 : discard_const_p(char *, kwlist),
166 : &ldap_server, &lp_obj, &py_creds)) {
167 0 : return -1;
168 : }
169 : /* keep reference to the credentials. Clear any earlier ones */
170 0 : Py_CLEAR(self->py_creds);
171 0 : self->cli_creds = NULL;
172 0 : self->py_creds = py_creds;
173 0 : Py_XINCREF(self->py_creds);
174 :
175 0 : if (self->py_creds) {
176 0 : ok = py_check_dcerpc_type(self->py_creds, "samba.credentials",
177 : "Credentials");
178 0 : if (!ok) {
179 0 : return -1;
180 : }
181 : self->cli_creds
182 0 : = PyCredentials_AsCliCredentials(self->py_creds);
183 : }
184 :
185 0 : ok = py_check_dcerpc_type(lp_obj, "samba.param", "LoadParm");
186 0 : if (!ok) {
187 0 : return -1;
188 : }
189 0 : lp_ctx = pytalloc_get_type(lp_obj, struct loadparm_context);
190 0 : if (lp_ctx == NULL) {
191 0 : return -1;
192 : }
193 0 : ok = lp_load_initial_only(lp_ctx->szConfigFile);
194 0 : if (!ok) {
195 0 : PyErr_Format(PyExc_RuntimeError, "Could not load config file '%s'",
196 : lp_ctx->szConfigFile);
197 0 : return -1;
198 : }
199 :
200 0 : if (self->cli_creds) {
201 0 : realm = cli_credentials_get_realm(self->cli_creds);
202 0 : workgroup = cli_credentials_get_domain(self->cli_creds);
203 : } else {
204 0 : realm = lp_realm();
205 0 : workgroup = lp_workgroup();
206 : }
207 :
208 : /* in case __init__ is called more than once */
209 0 : if (self->ads_ptr) {
210 0 : TALLOC_FREE(self->ads_ptr);
211 : }
212 : /* always succeeds or crashes */
213 0 : self->ads_ptr = ads_init(pytalloc_get_mem_ctx(args),
214 : realm,
215 : workgroup,
216 : ldap_server,
217 : ADS_SASL_PLAIN);
218 :
219 0 : return 0;
220 : }
221 :
222 : /* connect. Failure to connect results in an Exception */
223 0 : static PyObject* py_ads_connect(ADS *self,
224 : PyObject *Py_UNUSED(ignored))
225 : {
226 : ADS_STATUS status;
227 0 : TALLOC_CTX *frame = talloc_stackframe();
228 0 : if (!self->ads_ptr) {
229 0 : PyErr_SetString(PyExc_RuntimeError, "Uninitialized");
230 0 : return NULL;
231 : }
232 0 : TALLOC_FREE(self->ads_ptr->auth.user_name);
233 0 : TALLOC_FREE(self->ads_ptr->auth.password);
234 0 : TALLOC_FREE(self->ads_ptr->auth.realm);
235 0 : if (self->cli_creds) {
236 0 : self->ads_ptr->auth.user_name = talloc_strdup(self->ads_ptr,
237 : cli_credentials_get_username(self->cli_creds));
238 0 : if (self->ads_ptr->auth.user_name == NULL) {
239 0 : PyErr_NoMemory();
240 0 : goto err;
241 : }
242 0 : self->ads_ptr->auth.password = talloc_strdup(self->ads_ptr,
243 : cli_credentials_get_password(self->cli_creds));
244 0 : if (self->ads_ptr->auth.password == NULL) {
245 0 : PyErr_NoMemory();
246 0 : goto err;
247 : }
248 0 : self->ads_ptr->auth.realm = talloc_strdup(self->ads_ptr,
249 : cli_credentials_get_realm(self->cli_creds));
250 0 : if (self->ads_ptr->auth.realm == NULL) {
251 0 : PyErr_NoMemory();
252 0 : goto err;
253 : }
254 0 : self->ads_ptr->auth.flags |= ADS_AUTH_USER_CREDS;
255 0 : status = ads_connect_user_creds(self->ads_ptr);
256 : } else {
257 0 : char *passwd = NULL;
258 :
259 0 : if (!secrets_init()) {
260 0 : PyErr_SetString(PyExc_RuntimeError,
261 : "secrets_init() failed");
262 0 : goto err;
263 : }
264 :
265 0 : self->ads_ptr->auth.user_name = talloc_asprintf(self->ads_ptr,
266 : "%s$",
267 : lp_netbios_name());
268 0 : if (self->ads_ptr->auth.user_name == NULL) {
269 0 : PyErr_NoMemory();
270 0 : goto err;
271 : }
272 :
273 0 : passwd = secrets_fetch_machine_password(
274 0 : self->ads_ptr->server.workgroup, NULL, NULL);
275 0 : if (passwd == NULL) {
276 0 : PyErr_SetString(PyExc_RuntimeError,
277 : "Failed to fetch the machine account "
278 : "password");
279 0 : goto err;
280 : }
281 :
282 0 : self->ads_ptr->auth.password = talloc_strdup(self->ads_ptr,
283 : passwd);
284 0 : SAFE_FREE(passwd);
285 0 : if (self->ads_ptr->auth.password == NULL) {
286 0 : PyErr_NoMemory();
287 0 : goto err;
288 : }
289 0 : self->ads_ptr->auth.realm = talloc_asprintf_strupper_m(
290 0 : self->ads_ptr, "%s", self->ads_ptr->server.realm);
291 0 : if (self->ads_ptr->auth.realm == NULL) {
292 0 : PyErr_NoMemory();
293 0 : goto err;
294 : }
295 0 : self->ads_ptr->auth.flags |= ADS_AUTH_USER_CREDS;
296 0 : status = ads_connect(self->ads_ptr);
297 : }
298 0 : if (!ADS_ERR_OK(status)) {
299 0 : PyErr_Format(PyExc_RuntimeError,
300 : "ads_connect() failed: %s",
301 : ads_errstr(status));
302 0 : goto err;
303 : }
304 :
305 0 : TALLOC_FREE(frame);
306 0 : Py_RETURN_TRUE;
307 :
308 0 : err:
309 0 : TALLOC_FREE(frame);
310 0 : return NULL;
311 : }
312 :
313 : /* Parameter mapping and functions for the GP_EXT struct */
314 : void initgpo(void);
315 :
316 : /* Global methods aka do not need a special pyobject type */
317 0 : static PyObject *py_gpo_get_sysvol_gpt_version(PyObject * self,
318 : PyObject * args)
319 : {
320 0 : TALLOC_CTX *tmp_ctx = NULL;
321 : char *unix_path;
322 0 : char *display_name = NULL;
323 0 : uint32_t sysvol_version = 0;
324 : PyObject *result;
325 : NTSTATUS status;
326 :
327 0 : if (!PyArg_ParseTuple(args, "s", &unix_path)) {
328 0 : return NULL;
329 : }
330 0 : tmp_ctx = talloc_new(NULL);
331 0 : if (!tmp_ctx) {
332 0 : return PyErr_NoMemory();
333 : }
334 0 : status = gpo_get_sysvol_gpt_version(tmp_ctx, unix_path,
335 : &sysvol_version,
336 : &display_name);
337 0 : if (!NT_STATUS_IS_OK(status)) {
338 0 : PyErr_SetNTSTATUS(status);
339 0 : TALLOC_FREE(tmp_ctx);
340 0 : return NULL;
341 : }
342 :
343 0 : result = Py_BuildValue("[s,i]", display_name, sysvol_version);
344 0 : talloc_free(tmp_ctx);
345 0 : return result;
346 : }
347 :
348 : #ifdef HAVE_ADS
349 0 : static ADS_STATUS find_samaccount(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
350 : const char *samaccountname,
351 : uint32_t *uac_ret, const char **dn_ret)
352 : {
353 : ADS_STATUS status;
354 0 : const char *attrs[] = { "userAccountControl", NULL };
355 : const char *filter;
356 0 : LDAPMessage *res = NULL;
357 0 : char *dn = NULL;
358 0 : uint32_t uac = 0;
359 :
360 0 : filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
361 : samaccountname);
362 0 : if (filter == NULL) {
363 0 : status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
364 0 : goto out;
365 : }
366 :
367 0 : status = ads_do_search_all(ads, ads->config.bind_path,
368 : LDAP_SCOPE_SUBTREE, filter, attrs, &res);
369 :
370 0 : if (!ADS_ERR_OK(status)) {
371 0 : goto out;
372 : }
373 :
374 0 : if (ads_count_replies(ads, res) != 1) {
375 0 : status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
376 0 : goto out;
377 : }
378 :
379 0 : dn = ads_get_dn(ads, talloc_tos(), res);
380 0 : if (dn == NULL) {
381 0 : status = ADS_ERROR(LDAP_NO_MEMORY);
382 0 : goto out;
383 : }
384 :
385 0 : if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
386 0 : status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
387 0 : goto out;
388 : }
389 :
390 0 : if (uac_ret) {
391 0 : *uac_ret = uac;
392 : }
393 :
394 0 : if (dn_ret) {
395 0 : *dn_ret = talloc_strdup(mem_ctx, dn);
396 0 : if (*dn_ret == NULL) {
397 0 : status = ADS_ERROR(LDAP_NO_MEMORY);
398 0 : goto out;
399 : }
400 : }
401 0 : out:
402 0 : TALLOC_FREE(dn);
403 0 : ads_msgfree(ads, res);
404 :
405 0 : return status;
406 : }
407 :
408 0 : static PyObject *py_ads_get_gpo_list(ADS *self, PyObject *args, PyObject *kwds)
409 : {
410 0 : TALLOC_CTX *frame = NULL;
411 0 : struct GROUP_POLICY_OBJECT *gpo = NULL, *gpo_list = NULL;
412 : ADS_STATUS status;
413 0 : const char *samaccountname = NULL;
414 0 : const char *dn = NULL;
415 0 : uint32_t uac = 0;
416 0 : uint32_t flags = 0;
417 0 : struct security_token *token = NULL;
418 0 : PyObject *ret = NULL;
419 0 : TALLOC_CTX *gpo_ctx = NULL;
420 : size_t list_size;
421 : size_t i;
422 :
423 : static const char *kwlist[] = {"samaccountname", NULL};
424 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds, "s",
425 : discard_const_p(char *, kwlist),
426 : &samaccountname)) {
427 0 : return NULL;
428 : }
429 0 : if (!self->ads_ptr) {
430 0 : PyErr_SetString(PyExc_RuntimeError, "Uninitialized");
431 0 : return NULL;
432 : }
433 :
434 0 : frame = talloc_stackframe();
435 :
436 0 : status = find_samaccount(self->ads_ptr, frame,
437 : samaccountname, &uac, &dn);
438 0 : if (!ADS_ERR_OK(status)) {
439 0 : PyErr_Format(PyExc_RuntimeError,
440 : "Failed to find samAccountName '%s': %s",
441 : samaccountname, ads_errstr(status));
442 0 : goto out;
443 : }
444 :
445 0 : if (uac & UF_WORKSTATION_TRUST_ACCOUNT ||
446 0 : uac & UF_SERVER_TRUST_ACCOUNT) {
447 0 : flags |= GPO_LIST_FLAG_MACHINE;
448 0 : status = gp_get_machine_token(self->ads_ptr, frame, dn,
449 : &token);
450 0 : if (!ADS_ERR_OK(status)) {
451 0 : PyErr_Format(PyExc_RuntimeError,
452 : "Failed to get machine token for '%s'(%s): %s",
453 : samaccountname, dn, ads_errstr(status));
454 0 : goto out;
455 : }
456 : } else {
457 0 : status = ads_get_sid_token(self->ads_ptr, frame, dn, &token);
458 0 : if (!ADS_ERR_OK(status)) {
459 0 : PyErr_Format(PyExc_RuntimeError,
460 : "Failed to get sid token for '%s'(%s): %s",
461 : samaccountname, dn, ads_errstr(status));
462 0 : goto out;
463 : }
464 : }
465 :
466 0 : gpo_ctx = talloc_new(frame);
467 0 : if (!gpo_ctx) {
468 0 : PyErr_NoMemory();
469 0 : goto out;
470 : }
471 0 : status = ads_get_gpo_list(self->ads_ptr, gpo_ctx, dn, flags, token,
472 : &gpo_list);
473 0 : if (!ADS_ERR_OK(status)) {
474 0 : PyErr_Format(PyExc_RuntimeError,
475 : "Failed to fetch GPO list: %s",
476 : ads_errstr(status));
477 0 : goto out;
478 : }
479 :
480 : /* Convert the C linked list into a python list */
481 0 : list_size = 0;
482 0 : for (gpo = gpo_list; gpo != NULL; gpo = gpo->next) {
483 0 : list_size++;
484 : }
485 :
486 0 : i = 0;
487 0 : ret = PyList_New(list_size);
488 0 : if (ret == NULL) {
489 0 : goto out;
490 : }
491 :
492 0 : for (gpo = gpo_list; gpo != NULL; gpo = gpo->next) {
493 0 : PyObject *obj = pytalloc_reference_ex(&GPOType,
494 : gpo_ctx, gpo);
495 0 : if (obj == NULL) {
496 0 : Py_CLEAR(ret);
497 0 : goto out;
498 : }
499 :
500 0 : PyList_SetItem(ret, i, obj);
501 0 : i++;
502 : }
503 :
504 0 : out:
505 0 : TALLOC_FREE(frame);
506 0 : return ret;
507 : }
508 :
509 : #endif
510 :
511 : static PyMethodDef ADS_methods[] = {
512 : { "connect", (PyCFunction)py_ads_connect, METH_NOARGS,
513 : "Connect to the LDAP server" },
514 : #ifdef HAVE_ADS
515 : { "get_gpo_list", PY_DISCARD_FUNC_SIG(PyCFunction, py_ads_get_gpo_list),
516 : METH_VARARGS | METH_KEYWORDS,
517 : NULL },
518 : #endif
519 : {0}
520 : };
521 :
522 : static PyTypeObject ads_ADSType = {
523 : .tp_name = "gpo.ADS_STRUCT",
524 : .tp_basicsize = sizeof(ADS),
525 : .tp_new = PyType_GenericNew,
526 : .tp_dealloc = (destructor)py_ads_dealloc,
527 : .tp_flags = Py_TPFLAGS_DEFAULT,
528 : .tp_doc = "ADS struct",
529 : .tp_methods = ADS_methods,
530 : .tp_init = (initproc)py_ads_init,
531 : };
532 :
533 : static PyMethodDef py_gpo_methods[] = {
534 : {"gpo_get_sysvol_gpt_version",
535 : (PyCFunction)py_gpo_get_sysvol_gpt_version,
536 : METH_VARARGS, NULL},
537 : {0}
538 : };
539 :
540 : static struct PyModuleDef moduledef = {
541 : PyModuleDef_HEAD_INIT,
542 : .m_name = "gpo",
543 : .m_doc = "libgpo python bindings",
544 : .m_size = -1,
545 : .m_methods = py_gpo_methods,
546 : };
547 :
548 : /* Will be called by python when loading this module */
549 : void initgpo(void);
550 :
551 77 : MODULE_INIT_FUNC(gpo)
552 : {
553 : PyObject *m;
554 :
555 77 : debug_setup_talloc_log();
556 :
557 : /* Instantiate the types */
558 77 : m = PyModule_Create(&moduledef);
559 77 : if (m == NULL) {
560 0 : goto err;
561 : }
562 :
563 77 : if (PyModule_AddObject(m, "version",
564 : PyUnicode_FromString(SAMBA_VERSION_STRING)) ) {
565 0 : goto err;
566 : }
567 :
568 77 : if (pytalloc_BaseObject_PyType_Ready(&ads_ADSType) < 0) {
569 0 : goto err;
570 : }
571 :
572 77 : Py_INCREF(&ads_ADSType);
573 77 : if (PyModule_AddObject(m, "ADS_STRUCT", (PyObject *)&ads_ADSType)) {
574 0 : goto err;
575 : }
576 :
577 77 : if (pytalloc_BaseObject_PyType_Ready(&GPOType) < 0) {
578 0 : goto err;
579 : }
580 :
581 77 : Py_INCREF((PyObject *)(void *)&GPOType);
582 77 : if (PyModule_AddObject(m, "GROUP_POLICY_OBJECT",
583 : (PyObject *)&GPOType)) {
584 0 : goto err;
585 : }
586 77 : return m;
587 :
588 0 : err:
589 0 : Py_CLEAR(m);
590 0 : return NULL;
591 : }
|