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/>.
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"
34 /* A Python C API module to use LIBGPO */
36 #define GPO_getter(ATTR) \
37 static PyObject* GPO_get_##ATTR(PyObject *self, void *closure) \
39 struct GROUP_POLICY_OBJECT *gpo_ptr \
40 = pytalloc_get_ptr(self); \
43 return PyUnicode_FromString(gpo_ptr->ATTR); \
48 GPO_getter(file_sys_path
)
49 GPO_getter(display_name
)
52 GPO_getter(user_extensions
)
53 GPO_getter(machine_extensions
)
55 static PyGetSetDef GPO_setters
[] = {
56 {discard_const_p(char, "ds_path"), (getter
)GPO_get_ds_path
, NULL
, NULL
,
58 {discard_const_p(char, "file_sys_path"), (getter
)GPO_get_file_sys_path
,
60 {discard_const_p(char, "display_name"), (getter
)GPO_get_display_name
,
62 {discard_const_p(char, "name"), (getter
)GPO_get_name
, NULL
, NULL
,
64 {discard_const_p(char, "link"), (getter
)GPO_get_link
, NULL
, NULL
,
66 {discard_const_p(char, "user_extensions"),
67 (getter
)GPO_get_user_extensions
,
69 {discard_const_p(char, "machine_extensions"),
70 (getter
)GPO_get_machine_extensions
, NULL
, NULL
, NULL
},
74 static PyObject
*py_gpo_get_unix_path(PyObject
*self
, PyObject
*args
,
78 const char *cache_dir
= NULL
;
80 char *unix_path
= NULL
;
81 TALLOC_CTX
*frame
= NULL
;
82 static const char *kwlist
[] = {"cache_dir", NULL
};
83 struct GROUP_POLICY_OBJECT
*gpo_ptr \
84 = (struct GROUP_POLICY_OBJECT
*)pytalloc_get_ptr(self
);
86 frame
= talloc_stackframe();
88 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "|s",
89 discard_const_p(char *, kwlist
),
95 cache_dir
= cache_path(talloc_tos(), GPO_CACHE_DIR
);
97 PyErr_SetString(PyExc_MemoryError
,
98 "Failed to determine gpo cache dir");
103 status
= gpo_get_unix_path(frame
, cache_dir
, gpo_ptr
, &unix_path
);
105 if (!NT_STATUS_IS_OK(status
)) {
106 PyErr_Format(PyExc_RuntimeError
,
107 "Failed to determine gpo unix path: %s",
108 get_friendly_nt_error_msg(status
));
112 ret
= PyUnicode_FromString(unix_path
);
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
,
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
,
140 struct cli_credentials
*cli_creds
;
143 static void py_ads_dealloc(ADS
* self
)
145 TALLOC_FREE(self
->ads_ptr
);
146 Py_CLEAR(self
->py_creds
);
147 Py_TYPE(self
)->tp_free((PyObject
*)self
);
150 static PyObject
* py_ads_connect(ADS
*self
, PyObject
*Py_UNUSED(ignored
));
151 static int py_ads_init(ADS
*self
, PyObject
*args
, PyObject
*kwds
)
153 const char *realm
= NULL
;
154 const char *workgroup
= NULL
;
155 const char *ldap_server
= NULL
;
156 PyObject
*lp_obj
= NULL
;
157 PyObject
*py_creds
= NULL
;
158 struct loadparm_context
*lp_ctx
= NULL
;
161 static const char *kwlist
[] = {
162 "ldap_server", "loadparm_context", "credentials", NULL
164 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "sO|O",
165 discard_const_p(char *, kwlist
),
166 &ldap_server
, &lp_obj
, &py_creds
)) {
169 /* keep reference to the credentials. Clear any earlier ones */
170 Py_CLEAR(self
->py_creds
);
171 self
->cli_creds
= NULL
;
172 self
->py_creds
= py_creds
;
173 Py_XINCREF(self
->py_creds
);
175 if (self
->py_creds
) {
176 ok
= py_check_dcerpc_type(self
->py_creds
, "samba.credentials",
182 = PyCredentials_AsCliCredentials(self
->py_creds
);
185 ok
= py_check_dcerpc_type(lp_obj
, "samba.param", "LoadParm");
189 lp_ctx
= pytalloc_get_type(lp_obj
, struct loadparm_context
);
190 if (lp_ctx
== NULL
) {
193 ok
= lp_load_initial_only(lp_ctx
->szConfigFile
);
195 PyErr_Format(PyExc_RuntimeError
, "Could not load config file '%s'",
196 lp_ctx
->szConfigFile
);
200 if (self
->cli_creds
) {
201 realm
= cli_credentials_get_realm(self
->cli_creds
);
202 workgroup
= cli_credentials_get_domain(self
->cli_creds
);
205 workgroup
= lp_workgroup();
208 /* in case __init__ is called more than once */
210 TALLOC_FREE(self
->ads_ptr
);
212 /* always succeeds or crashes */
213 self
->ads_ptr
= ads_init(pytalloc_get_mem_ctx(args
),
222 /* connect. Failure to connect results in an Exception */
223 static PyObject
* py_ads_connect(ADS
*self
,
224 PyObject
*Py_UNUSED(ignored
))
227 TALLOC_CTX
*frame
= talloc_stackframe();
228 if (!self
->ads_ptr
) {
229 PyErr_SetString(PyExc_RuntimeError
, "Uninitialized");
232 TALLOC_FREE(self
->ads_ptr
->auth
.user_name
);
233 TALLOC_FREE(self
->ads_ptr
->auth
.password
);
234 TALLOC_FREE(self
->ads_ptr
->auth
.realm
);
235 if (self
->cli_creds
) {
236 self
->ads_ptr
->auth
.user_name
= talloc_strdup(self
->ads_ptr
,
237 cli_credentials_get_username(self
->cli_creds
));
238 if (self
->ads_ptr
->auth
.user_name
== NULL
) {
242 self
->ads_ptr
->auth
.password
= talloc_strdup(self
->ads_ptr
,
243 cli_credentials_get_password(self
->cli_creds
));
244 if (self
->ads_ptr
->auth
.password
== NULL
) {
248 self
->ads_ptr
->auth
.realm
= talloc_strdup(self
->ads_ptr
,
249 cli_credentials_get_realm(self
->cli_creds
));
250 if (self
->ads_ptr
->auth
.realm
== NULL
) {
254 self
->ads_ptr
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
255 status
= ads_connect_user_creds(self
->ads_ptr
);
259 if (!secrets_init()) {
260 PyErr_SetString(PyExc_RuntimeError
,
261 "secrets_init() failed");
265 self
->ads_ptr
->auth
.user_name
= talloc_asprintf(self
->ads_ptr
,
268 if (self
->ads_ptr
->auth
.user_name
== NULL
) {
273 passwd
= secrets_fetch_machine_password(
274 self
->ads_ptr
->server
.workgroup
, NULL
, NULL
);
275 if (passwd
== NULL
) {
276 PyErr_SetString(PyExc_RuntimeError
,
277 "Failed to fetch the machine account "
282 self
->ads_ptr
->auth
.password
= talloc_strdup(self
->ads_ptr
,
285 if (self
->ads_ptr
->auth
.password
== NULL
) {
289 self
->ads_ptr
->auth
.realm
= talloc_asprintf_strupper_m(
290 self
->ads_ptr
, "%s", self
->ads_ptr
->server
.realm
);
291 if (self
->ads_ptr
->auth
.realm
== NULL
) {
295 self
->ads_ptr
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
296 status
= ads_connect(self
->ads_ptr
);
298 if (!ADS_ERR_OK(status
)) {
299 PyErr_Format(PyExc_RuntimeError
,
300 "ads_connect() failed: %s",
313 /* Parameter mapping and functions for the GP_EXT struct */
316 /* Global methods aka do not need a special pyobject type */
317 static PyObject
*py_gpo_get_sysvol_gpt_version(PyObject
* self
,
320 TALLOC_CTX
*tmp_ctx
= NULL
;
322 char *display_name
= NULL
;
323 uint32_t sysvol_version
= 0;
327 if (!PyArg_ParseTuple(args
, "s", &unix_path
)) {
330 tmp_ctx
= talloc_new(NULL
);
332 return PyErr_NoMemory();
334 status
= gpo_get_sysvol_gpt_version(tmp_ctx
, unix_path
,
337 if (!NT_STATUS_IS_OK(status
)) {
338 PyErr_SetNTSTATUS(status
);
339 TALLOC_FREE(tmp_ctx
);
343 result
= Py_BuildValue("[s,i]", display_name
, sysvol_version
);
344 talloc_free(tmp_ctx
);
349 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
)
354 const char *attrs
[] = { "userAccountControl", NULL
};
356 LDAPMessage
*res
= NULL
;
360 filter
= talloc_asprintf(mem_ctx
, "(sAMAccountName=%s)",
362 if (filter
== NULL
) {
363 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
367 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
368 LDAP_SCOPE_SUBTREE
, filter
, attrs
, &res
);
370 if (!ADS_ERR_OK(status
)) {
374 if (ads_count_replies(ads
, res
) != 1) {
375 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
379 dn
= ads_get_dn(ads
, talloc_tos(), res
);
381 status
= ADS_ERROR(LDAP_NO_MEMORY
);
385 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
386 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
395 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
396 if (*dn_ret
== NULL
) {
397 status
= ADS_ERROR(LDAP_NO_MEMORY
);
403 ads_msgfree(ads
, res
);
408 static PyObject
*py_ads_get_gpo_list(ADS
*self
, PyObject
*args
, PyObject
*kwds
)
410 TALLOC_CTX
*frame
= NULL
;
411 struct GROUP_POLICY_OBJECT
*gpo
= NULL
, *gpo_list
= NULL
;
413 const char *samaccountname
= NULL
;
414 const char *dn
= NULL
;
417 struct security_token
*token
= NULL
;
418 PyObject
*ret
= NULL
;
419 TALLOC_CTX
*gpo_ctx
= NULL
;
423 static const char *kwlist
[] = {"samaccountname", NULL
};
424 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s",
425 discard_const_p(char *, kwlist
),
429 if (!self
->ads_ptr
) {
430 PyErr_SetString(PyExc_RuntimeError
, "Uninitialized");
434 frame
= talloc_stackframe();
436 status
= find_samaccount(self
->ads_ptr
, frame
,
437 samaccountname
, &uac
, &dn
);
438 if (!ADS_ERR_OK(status
)) {
439 PyErr_Format(PyExc_RuntimeError
,
440 "Failed to find samAccountName '%s': %s",
441 samaccountname
, ads_errstr(status
));
445 if (uac
& UF_WORKSTATION_TRUST_ACCOUNT
||
446 uac
& UF_SERVER_TRUST_ACCOUNT
) {
447 flags
|= GPO_LIST_FLAG_MACHINE
;
448 status
= gp_get_machine_token(self
->ads_ptr
, frame
, dn
,
450 if (!ADS_ERR_OK(status
)) {
451 PyErr_Format(PyExc_RuntimeError
,
452 "Failed to get machine token for '%s'(%s): %s",
453 samaccountname
, dn
, ads_errstr(status
));
457 status
= ads_get_sid_token(self
->ads_ptr
, frame
, dn
, &token
);
458 if (!ADS_ERR_OK(status
)) {
459 PyErr_Format(PyExc_RuntimeError
,
460 "Failed to get sid token for '%s'(%s): %s",
461 samaccountname
, dn
, ads_errstr(status
));
466 gpo_ctx
= talloc_new(frame
);
471 status
= ads_get_gpo_list(self
->ads_ptr
, gpo_ctx
, dn
, flags
, token
,
473 if (!ADS_ERR_OK(status
)) {
474 PyErr_Format(PyExc_RuntimeError
,
475 "Failed to fetch GPO list: %s",
480 /* Convert the C linked list into a python list */
482 for (gpo
= gpo_list
; gpo
!= NULL
; gpo
= gpo
->next
) {
487 ret
= PyList_New(list_size
);
492 for (gpo
= gpo_list
; gpo
!= NULL
; gpo
= gpo
->next
) {
493 PyObject
*obj
= pytalloc_reference_ex(&GPOType
,
500 PyList_SetItem(ret
, i
, obj
);
511 static PyMethodDef ADS_methods
[] = {
512 { "connect", (PyCFunction
)py_ads_connect
, METH_NOARGS
,
513 "Connect to the LDAP server" },
515 { "get_gpo_list", PY_DISCARD_FUNC_SIG(PyCFunction
, py_ads_get_gpo_list
),
516 METH_VARARGS
| METH_KEYWORDS
,
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
,
533 static PyMethodDef py_gpo_methods
[] = {
534 {"gpo_get_sysvol_gpt_version",
535 (PyCFunction
)py_gpo_get_sysvol_gpt_version
,
540 static struct PyModuleDef moduledef
= {
541 PyModuleDef_HEAD_INIT
,
543 .m_doc
= "libgpo python bindings",
545 .m_methods
= py_gpo_methods
,
548 /* Will be called by python when loading this module */
551 MODULE_INIT_FUNC(gpo
)
555 debug_setup_talloc_log();
557 /* Instantiate the types */
558 m
= PyModule_Create(&moduledef
);
563 if (PyModule_AddObject(m
, "version",
564 PyUnicode_FromString(SAMBA_VERSION_STRING
)) ) {
568 if (pytalloc_BaseObject_PyType_Ready(&ads_ADSType
) < 0) {
572 Py_INCREF(&ads_ADSType
);
573 if (PyModule_AddObject(m
, "ADS_STRUCT", (PyObject
*)&ads_ADSType
)) {
577 if (pytalloc_BaseObject_PyType_Ready(&GPOType
) < 0) {
581 Py_INCREF((PyObject
*)(void *)&GPOType
);
582 if (PyModule_AddObject(m
, "GROUP_POLICY_OBJECT",
583 (PyObject
*)&GPOType
)) {