2 Unix SMB/CIFS implementation.
3 Copyright (C) Luke Morrison <luc785@hotmail.com> 2013
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.
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.
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/>.
19 #include "lib/replace/system/python.h"
22 #include "param/pyparam.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"
33 #include "../libcli/security/security.h"
35 /* A Python C API module to use LIBGPO */
37 #define GPO_getter(ATTR) \
38 static PyObject* GPO_get_##ATTR(PyObject *self, void *closure) \
40 struct GROUP_POLICY_OBJECT *gpo_ptr \
41 = pytalloc_get_ptr(self); \
44 return PyUnicode_FromString(gpo_ptr->ATTR); \
49 GPO_getter(file_sys_path
)
50 GPO_getter(display_name
)
53 GPO_getter(user_extensions
)
54 GPO_getter(machine_extensions
)
55 #define GPO_setter(ATTR) \
56 static int GPO_set_##ATTR(PyObject *self, PyObject *val, void *closure) \
58 struct GROUP_POLICY_OBJECT *gpo_ptr \
59 = pytalloc_get_ptr(self); \
61 if (!PyUnicode_Check(val)) { \
62 PyErr_Format(PyExc_TypeError, \
63 "Cannot convert input to string"); \
66 if (val != Py_None) { \
67 gpo_ptr->ATTR = talloc_strdup(gpo_ptr, \
68 _PyUnicode_AsString(val)); \
70 gpo_ptr->ATTR = NULL; \
75 GPO_setter(file_sys_path
)
76 GPO_setter(display_name
)
79 GPO_setter(user_extensions
)
80 GPO_setter(machine_extensions
)
81 #define GPO_int_getter(ATTR) \
82 static PyObject* GPO_get_##ATTR(PyObject *self, void *closure) \
84 struct GROUP_POLICY_OBJECT *gpo_ptr \
85 = pytalloc_get_ptr(self); \
87 return PyLong_FromLong(gpo_ptr->ATTR); \
89 GPO_int_getter(options
)
90 GPO_int_getter(version
)
91 GPO_int_getter(link_type
)
92 #define GPO_int_setter(ATTR) \
93 static int GPO_set_##ATTR(PyObject *self, PyObject *val, void *closure) \
95 struct GROUP_POLICY_OBJECT *gpo_ptr \
96 = pytalloc_get_ptr(self); \
98 if (!PyLong_Check(val)) { \
99 PyErr_Format(PyExc_TypeError, \
100 "Cannot convert input to int"); \
103 gpo_ptr->ATTR = PyLong_AsLong(val); \
107 GPO_int_setter(options
)
108 GPO_int_setter(version
)
109 GPO_int_setter(link_type
)
111 static PyObject
*GPO_marshall_get_sec_desc_buf(PyObject
*self
, PyObject
*args
,
114 struct GROUP_POLICY_OBJECT
*gpo_ptr
= pytalloc_get_ptr(self
);
116 uint8_t *data
= NULL
;
119 if (gpo_ptr
->security_descriptor
== NULL
) {
120 PyErr_SetString(PyExc_RuntimeError
, "Uninitialized");
124 status
= marshall_sec_desc(gpo_ptr
, gpo_ptr
->security_descriptor
,
126 if (!NT_STATUS_IS_OK(status
)) {
127 PyErr_Format(PyExc_BufferError
,
128 "marshall_sec_desc_buf failed: %s",
133 return PyBytes_FromStringAndSize((char *)data
, len
);
136 static PyObject
*GPO_unmarshall_set_sec_desc(PyObject
*self
, PyObject
*args
,
139 struct GROUP_POLICY_OBJECT
*gpo_ptr
= pytalloc_get_ptr(self
);
144 if (!PyArg_ParseTuple(args
, "s#", &bytes
, &length
)) {
145 PyErr_Format(PyExc_TypeError
,
146 "Cannot convert input to bytes");
150 gpo_ptr
->security_descriptor
= talloc_zero(gpo_ptr
,
151 struct security_descriptor
);
152 status
= unmarshall_sec_desc(gpo_ptr
, (uint8_t *)bytes
, length
,
153 &gpo_ptr
->security_descriptor
);
154 if (!NT_STATUS_IS_OK(status
)) {
155 PyErr_Format(PyExc_BufferError
,
156 "unmarshall_sec_desc failed: %s",
164 static PyGetSetDef GPO_setters
[] = {
165 {discard_const_p(char, "options"), (getter
)GPO_get_options
,
166 (setter
)GPO_set_options
, NULL
, NULL
},
167 {discard_const_p(char, "version"), (getter
)GPO_get_version
,
168 (setter
)GPO_set_version
, NULL
, NULL
},
169 {discard_const_p(char, "ds_path"), (getter
)GPO_get_ds_path
,
170 (setter
)GPO_set_ds_path
, NULL
, NULL
},
171 {discard_const_p(char, "file_sys_path"), (getter
)GPO_get_file_sys_path
,
172 (setter
)GPO_set_file_sys_path
, NULL
, NULL
},
173 {discard_const_p(char, "display_name"), (getter
)GPO_get_display_name
,
174 (setter
)GPO_set_display_name
, NULL
, NULL
},
175 {discard_const_p(char, "name"), (getter
)GPO_get_name
,
176 (setter
)GPO_set_name
, NULL
, NULL
},
177 {discard_const_p(char, "link"), (getter
)GPO_get_link
,
178 (setter
)GPO_set_link
, NULL
, NULL
},
179 {discard_const_p(char, "link_type"), (getter
)GPO_get_link_type
,
180 (setter
)GPO_set_link_type
, NULL
, NULL
},
181 {discard_const_p(char, "user_extensions"),
182 (getter
)GPO_get_user_extensions
,
183 (setter
)GPO_set_user_extensions
, NULL
, NULL
},
184 {discard_const_p(char, "machine_extensions"),
185 (getter
)GPO_get_machine_extensions
,
186 (setter
)GPO_set_machine_extensions
, NULL
, NULL
},
190 static PyObject
*py_gpo_get_unix_path(PyObject
*self
, PyObject
*args
,
194 const char *cache_dir
= NULL
;
195 PyObject
*ret
= NULL
;
196 char *unix_path
= NULL
;
197 TALLOC_CTX
*frame
= NULL
;
198 static const char *kwlist
[] = {"cache_dir", NULL
};
199 struct GROUP_POLICY_OBJECT
*gpo_ptr \
200 = (struct GROUP_POLICY_OBJECT
*)pytalloc_get_ptr(self
);
202 frame
= talloc_stackframe();
204 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "|s",
205 discard_const_p(char *, kwlist
),
211 cache_dir
= cache_path(talloc_tos(), GPO_CACHE_DIR
);
213 PyErr_SetString(PyExc_MemoryError
,
214 "Failed to determine gpo cache dir");
219 status
= gpo_get_unix_path(frame
, cache_dir
, gpo_ptr
, &unix_path
);
221 if (!NT_STATUS_IS_OK(status
)) {
222 PyErr_Format(PyExc_RuntimeError
,
223 "Failed to determine gpo unix path: %s",
224 get_friendly_nt_error_msg(status
));
228 ret
= PyUnicode_FromString(unix_path
);
235 static PyMethodDef GPO_methods
[] = {
236 {"get_unix_path", PY_DISCARD_FUNC_SIG(PyCFunction
,
237 py_gpo_get_unix_path
),
238 METH_VARARGS
| METH_KEYWORDS
,
240 {"set_sec_desc", PY_DISCARD_FUNC_SIG(PyCFunction
,
241 GPO_unmarshall_set_sec_desc
),
242 METH_VARARGS
, NULL
},
243 {"get_sec_desc_buf", PY_DISCARD_FUNC_SIG(PyCFunction
,
244 GPO_marshall_get_sec_desc_buf
),
249 static int py_gpo_init(PyObject
*self
, PyObject
*args
, PyObject
*kwds
)
251 struct GROUP_POLICY_OBJECT
*gpo_ptr
= pytalloc_get_ptr(self
);
252 const char *name
= NULL
;
253 const char *display_name
= NULL
;
254 enum GPO_LINK_TYPE link_type
= GP_LINK_UNKOWN
;
255 const char *file_sys_path
= NULL
;
257 static const char *kwlist
[] = {
258 "name", "display_name", "link_type", "file_sys_path", NULL
260 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "|ssIs",
261 discard_const_p(char *, kwlist
),
262 &name
, &display_name
, &link_type
,
268 gpo_ptr
->name
= talloc_strdup(gpo_ptr
, name
);
271 gpo_ptr
->display_name
= talloc_strdup(gpo_ptr
, display_name
);
273 gpo_ptr
->link_type
= link_type
;
275 gpo_ptr
->file_sys_path
= talloc_strdup(gpo_ptr
, file_sys_path
);
281 static PyObject
*py_gpo_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwargs
)
283 return pytalloc_new(struct GROUP_POLICY_OBJECT
, type
);
286 static PyTypeObject GPOType
= {
287 PyVarObject_HEAD_INIT(NULL
, 0)
288 .tp_name
= "gpo.GROUP_POLICY_OBJECT",
289 .tp_doc
= "GROUP_POLICY_OBJECT",
290 .tp_getset
= GPO_setters
,
291 .tp_methods
= GPO_methods
,
292 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
293 .tp_new
= py_gpo_new
,
294 .tp_init
= (initproc
)py_gpo_init
,
301 struct cli_credentials
*cli_creds
;
304 static void py_ads_dealloc(ADS
* self
)
306 TALLOC_FREE(self
->ads_ptr
);
307 Py_CLEAR(self
->py_creds
);
308 Py_TYPE(self
)->tp_free((PyObject
*)self
);
311 static PyObject
* py_ads_connect(ADS
*self
, PyObject
*Py_UNUSED(ignored
));
312 static int py_ads_init(ADS
*self
, PyObject
*args
, PyObject
*kwds
)
314 const char *realm
= NULL
;
315 const char *workgroup
= NULL
;
316 const char *ldap_server
= NULL
;
317 PyObject
*lp_obj
= NULL
;
318 PyObject
*py_creds
= NULL
;
319 struct loadparm_context
*lp_ctx
= NULL
;
322 static const char *kwlist
[] = {
323 "ldap_server", "loadparm_context", "credentials", NULL
325 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "sO|O",
326 discard_const_p(char *, kwlist
),
327 &ldap_server
, &lp_obj
, &py_creds
)) {
330 /* keep reference to the credentials. Clear any earlier ones */
331 Py_CLEAR(self
->py_creds
);
332 self
->cli_creds
= NULL
;
333 self
->py_creds
= py_creds
;
334 Py_XINCREF(self
->py_creds
);
336 if (self
->py_creds
) {
337 ok
= py_check_dcerpc_type(self
->py_creds
, "samba.credentials",
343 = PyCredentials_AsCliCredentials(self
->py_creds
);
346 ok
= py_check_dcerpc_type(lp_obj
, "samba.param", "LoadParm");
350 lp_ctx
= pytalloc_get_type(lp_obj
, struct loadparm_context
);
351 if (lp_ctx
== NULL
) {
354 ok
= lp_load_initial_only(lp_ctx
->szConfigFile
);
356 PyErr_Format(PyExc_RuntimeError
, "Could not load config file '%s'",
357 lp_ctx
->szConfigFile
);
361 if (self
->cli_creds
) {
362 realm
= cli_credentials_get_realm(self
->cli_creds
);
363 workgroup
= cli_credentials_get_domain(self
->cli_creds
);
366 workgroup
= lp_workgroup();
369 /* in case __init__ is called more than once */
371 TALLOC_FREE(self
->ads_ptr
);
373 /* always succeeds or crashes */
374 self
->ads_ptr
= ads_init(pytalloc_get_mem_ctx(args
),
383 /* connect. Failure to connect results in an Exception */
384 static PyObject
* py_ads_connect(ADS
*self
,
385 PyObject
*Py_UNUSED(ignored
))
388 TALLOC_CTX
*frame
= talloc_stackframe();
389 if (!self
->ads_ptr
) {
390 PyErr_SetString(PyExc_RuntimeError
, "Uninitialized");
393 ADS_TALLOC_CONST_FREE(self
->ads_ptr
->auth
.user_name
);
394 ADS_TALLOC_CONST_FREE(self
->ads_ptr
->auth
.password
);
395 ADS_TALLOC_CONST_FREE(self
->ads_ptr
->auth
.realm
);
396 if (self
->cli_creds
) {
397 self
->ads_ptr
->auth
.user_name
= talloc_strdup(self
->ads_ptr
,
398 cli_credentials_get_username(self
->cli_creds
));
399 if (self
->ads_ptr
->auth
.user_name
== NULL
) {
403 self
->ads_ptr
->auth
.password
= talloc_strdup(self
->ads_ptr
,
404 cli_credentials_get_password(self
->cli_creds
));
405 if (self
->ads_ptr
->auth
.password
== NULL
) {
409 self
->ads_ptr
->auth
.realm
= talloc_strdup(self
->ads_ptr
,
410 cli_credentials_get_realm(self
->cli_creds
));
411 if (self
->ads_ptr
->auth
.realm
== NULL
) {
415 self
->ads_ptr
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
416 status
= ads_connect_user_creds(self
->ads_ptr
);
420 if (!secrets_init()) {
421 PyErr_SetString(PyExc_RuntimeError
,
422 "secrets_init() failed");
426 self
->ads_ptr
->auth
.user_name
= talloc_asprintf(self
->ads_ptr
,
429 if (self
->ads_ptr
->auth
.user_name
== NULL
) {
434 passwd
= secrets_fetch_machine_password(
435 self
->ads_ptr
->server
.workgroup
, NULL
, NULL
);
436 if (passwd
== NULL
) {
437 PyErr_SetString(PyExc_RuntimeError
,
438 "Failed to fetch the machine account "
443 self
->ads_ptr
->auth
.password
= talloc_strdup(self
->ads_ptr
,
446 if (self
->ads_ptr
->auth
.password
== NULL
) {
450 self
->ads_ptr
->auth
.realm
= talloc_asprintf_strupper_m(
451 self
->ads_ptr
, "%s", self
->ads_ptr
->server
.realm
);
452 if (self
->ads_ptr
->auth
.realm
== NULL
) {
456 self
->ads_ptr
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
457 status
= ads_connect(self
->ads_ptr
);
459 if (!ADS_ERR_OK(status
)) {
460 PyErr_Format(PyExc_RuntimeError
,
461 "ads_connect() failed: %s",
474 /* Parameter mapping and functions for the GP_EXT struct */
477 /* Global methods aka do not need a special pyobject type */
478 static PyObject
*py_gpo_get_sysvol_gpt_version(PyObject
* self
,
481 TALLOC_CTX
*tmp_ctx
= NULL
;
483 char *display_name
= NULL
;
484 uint32_t sysvol_version
= 0;
488 if (!PyArg_ParseTuple(args
, "s", &unix_path
)) {
491 tmp_ctx
= talloc_new(NULL
);
493 return PyErr_NoMemory();
495 status
= gpo_get_sysvol_gpt_version(tmp_ctx
, unix_path
,
498 if (!NT_STATUS_IS_OK(status
)) {
499 PyErr_SetNTSTATUS(status
);
500 TALLOC_FREE(tmp_ctx
);
504 result
= Py_BuildValue("[s,i]", display_name
, sysvol_version
);
505 talloc_free(tmp_ctx
);
510 static ADS_STATUS
find_samaccount(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
511 const char *samaccountname
,
512 uint32_t *uac_ret
, const char **dn_ret
)
515 const char *attrs
[] = { "userAccountControl", NULL
};
517 LDAPMessage
*res
= NULL
;
521 filter
= talloc_asprintf(mem_ctx
, "(sAMAccountName=%s)",
523 if (filter
== NULL
) {
524 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
528 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
529 LDAP_SCOPE_SUBTREE
, filter
, attrs
, &res
);
531 if (!ADS_ERR_OK(status
)) {
535 if (ads_count_replies(ads
, res
) != 1) {
536 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
540 dn
= ads_get_dn(ads
, talloc_tos(), res
);
542 status
= ADS_ERROR(LDAP_NO_MEMORY
);
546 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
547 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
556 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
557 if (*dn_ret
== NULL
) {
558 status
= ADS_ERROR(LDAP_NO_MEMORY
);
564 ads_msgfree(ads
, res
);
569 static PyObject
*py_ads_get_gpo_list(ADS
*self
, PyObject
*args
, PyObject
*kwds
)
571 TALLOC_CTX
*frame
= NULL
;
572 struct GROUP_POLICY_OBJECT
*gpo
= NULL
, *gpo_list
= NULL
;
574 const char *samaccountname
= NULL
;
575 const char *dn
= NULL
;
578 struct security_token
*token
= NULL
;
579 PyObject
*ret
= NULL
;
580 TALLOC_CTX
*gpo_ctx
= NULL
;
584 static const char *kwlist
[] = {"samaccountname", NULL
};
586 PyErr_WarnEx(PyExc_DeprecationWarning
, "The get_gpo_list function"
587 " is deprecated as of Samba 4.19. Please use "
588 "the samba.gp module instead.", 2);
590 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s",
591 discard_const_p(char *, kwlist
),
595 if (!self
->ads_ptr
) {
596 PyErr_SetString(PyExc_RuntimeError
, "Uninitialized");
600 frame
= talloc_stackframe();
602 status
= find_samaccount(self
->ads_ptr
, frame
,
603 samaccountname
, &uac
, &dn
);
604 if (!ADS_ERR_OK(status
)) {
605 PyErr_Format(PyExc_RuntimeError
,
606 "Failed to find samAccountName '%s': %s",
607 samaccountname
, ads_errstr(status
));
611 if (uac
& UF_WORKSTATION_TRUST_ACCOUNT
||
612 uac
& UF_SERVER_TRUST_ACCOUNT
) {
613 flags
|= GPO_LIST_FLAG_MACHINE
;
614 status
= gp_get_machine_token(self
->ads_ptr
, frame
, dn
,
616 if (!ADS_ERR_OK(status
)) {
617 PyErr_Format(PyExc_RuntimeError
,
618 "Failed to get machine token for '%s'(%s): %s",
619 samaccountname
, dn
, ads_errstr(status
));
623 status
= ads_get_sid_token(self
->ads_ptr
, frame
, dn
, &token
);
624 if (!ADS_ERR_OK(status
)) {
625 PyErr_Format(PyExc_RuntimeError
,
626 "Failed to get sid token for '%s'(%s): %s",
627 samaccountname
, dn
, ads_errstr(status
));
632 gpo_ctx
= talloc_new(frame
);
637 status
= ads_get_gpo_list(self
->ads_ptr
, gpo_ctx
, dn
, flags
, token
,
639 if (!ADS_ERR_OK(status
)) {
640 PyErr_Format(PyExc_RuntimeError
,
641 "Failed to fetch GPO list: %s",
646 /* Convert the C linked list into a python list */
648 for (gpo
= gpo_list
; gpo
!= NULL
; gpo
= gpo
->next
) {
653 ret
= PyList_New(list_size
);
658 for (gpo
= gpo_list
; gpo
!= NULL
; gpo
= gpo
->next
) {
659 PyObject
*obj
= pytalloc_reference_ex(&GPOType
,
666 PyList_SetItem(ret
, i
, obj
);
677 static PyMethodDef ADS_methods
[] = {
678 { "connect", (PyCFunction
)py_ads_connect
, METH_NOARGS
,
679 "Connect to the LDAP server" },
681 { "get_gpo_list", PY_DISCARD_FUNC_SIG(PyCFunction
, py_ads_get_gpo_list
),
682 METH_VARARGS
| METH_KEYWORDS
,
688 static PyTypeObject ads_ADSType
= {
689 .tp_name
= "gpo.ADS_STRUCT",
690 .tp_basicsize
= sizeof(ADS
),
691 .tp_new
= PyType_GenericNew
,
692 .tp_dealloc
= (destructor
)py_ads_dealloc
,
693 .tp_flags
= Py_TPFLAGS_DEFAULT
,
694 .tp_doc
= "ADS struct",
695 .tp_methods
= ADS_methods
,
696 .tp_init
= (initproc
)py_ads_init
,
699 static PyMethodDef py_gpo_methods
[] = {
700 {"gpo_get_sysvol_gpt_version",
701 (PyCFunction
)py_gpo_get_sysvol_gpt_version
,
706 static struct PyModuleDef moduledef
= {
707 PyModuleDef_HEAD_INIT
,
709 .m_doc
= "libgpo python bindings",
711 .m_methods
= py_gpo_methods
,
714 /* Will be called by python when loading this module */
717 MODULE_INIT_FUNC(gpo
)
721 debug_setup_talloc_log();
723 /* Instantiate the types */
724 m
= PyModule_Create(&moduledef
);
729 if (PyModule_AddObject(m
, "version",
730 PyUnicode_FromString(SAMBA_VERSION_STRING
)) ) {
734 if (pytalloc_BaseObject_PyType_Ready(&ads_ADSType
) < 0) {
738 Py_INCREF(&ads_ADSType
);
739 if (PyModule_AddObject(m
, "ADS_STRUCT", (PyObject
*)&ads_ADSType
)) {
743 if (pytalloc_BaseObject_PyType_Ready(&GPOType
) < 0) {
747 Py_INCREF((PyObject
*)(void *)&GPOType
);
748 if (PyModule_AddObject(m
, "GROUP_POLICY_OBJECT",
749 (PyObject
*)&GPOType
)) {
753 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val))
755 ADD_FLAGS(GP_LINK_UNKOWN
);
756 ADD_FLAGS(GP_LINK_MACHINE
);
757 ADD_FLAGS(GP_LINK_SITE
);
758 ADD_FLAGS(GP_LINK_DOMAIN
);
759 ADD_FLAGS(GP_LINK_OU
);
760 ADD_FLAGS(GP_LINK_LOCAL
);