/* Python interface to cli_mdssvc Copyright (C) Ralph Boehme 2019 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "lib/replace/system/python.h" #include <pytalloc.h> #include "includes.h" #include "python/py3compat.h" #include "python/modules.h" #include "lib/util/talloc_stack.h" #include "lib/util/tevent_ntstatus.h" #include "librpc/rpc/rpc_common.h" #include "librpc/rpc/pyrpc_util.h" #include "rpc_client/cli_mdssvc.h" #include "rpc_client/cli_mdssvc_private.h" static PyObject *search_get_results(PyObject *self, PyObject *args, PyObject *kwargs) { TALLOC_CTX *frame = talloc_stackframe(); const char * const kwnames[] = {"pipe", NULL}; PyObject *pypipe = NULL; PyObject *result = NULL; dcerpc_InterfaceObject *pipe = NULL; struct tevent_req *req = NULL; struct mdscli_search_ctx *search = NULL; uint64_t *cnids = NULL; size_t i; size_t ncnids; NTSTATUS status; int ret; bool ok; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", discard_const_p(char *, kwnames), &pypipe)) { PyErr_SetString(PyExc_RuntimeError, "Failed to parse args"); goto out; } ok = py_check_dcerpc_type(pypipe, "samba.dcerpc.base", "ClientConnection"); if (!ok) { goto out; } pipe = (dcerpc_InterfaceObject *)pypipe; search = pytalloc_get_type(self, struct mdscli_search_ctx); if (search == NULL) { goto out; } /* * We must use the async send/recv versions in order to pass the correct * tevent context, here and any other place we call mdscli_* * functions. Using the sync version we would be polling a temporary * event context, but unfortunately the s4 Python RPC bindings dispatch * events through * * dcerpc_bh_raw_call_send() * -> dcerpc_request_send() * -> dcerpc_schedule_io_trigger() * -> dcerpc_send_request() * -> tstream_writev_queue_send() * * on an hardcoded event context allocated via * * py_dcerpc_interface_init_helper() * -> dcerpc_pipe_connect() */ again: req = mdscli_get_results_send(frame, pipe->ev, search); if (req == NULL) { PyErr_NoMemory(); goto out; } if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) { PyErr_SetNTSTATUS(status); goto out; } status = mdscli_get_results_recv(req, frame, &cnids); TALLOC_FREE(req); if (NT_STATUS_EQUAL(status, NT_STATUS_PENDING)) { sleep(1); goto again; } if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES)) { PyErr_SetNTSTATUS(status); goto out; } result = Py_BuildValue("[]"); ncnids = talloc_array_length(cnids); for (i = 0; i < ncnids; i++) { char *path = NULL; PyObject *pypath = NULL; req = mdscli_get_path_send(frame, pipe->ev, search->mdscli_ctx, cnids[i]); if (req == NULL) { PyErr_NoMemory(); Py_DECREF(result); result = NULL; goto out; } if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) { PyErr_SetNTSTATUS(status); Py_DECREF(result); result = NULL; goto out; } status = mdscli_get_path_recv(req, frame, &path); TALLOC_FREE(req); PyErr_NTSTATUS_NOT_OK_RAISE(status); pypath = PyUnicode_FromString(path); if (pypath == NULL) { PyErr_NoMemory(); Py_DECREF(result); result = NULL; goto out; } ret = PyList_Append(result, pypath); Py_DECREF(pypath); if (ret == -1) { PyErr_SetString(PyExc_RuntimeError, "list append failed"); Py_DECREF(result); result = NULL; goto out; } } out: talloc_free(frame); return result; } static PyObject *search_close(PyObject *self, PyObject *args, PyObject *kwargs) { TALLOC_CTX *frame = talloc_stackframe(); const char * const kwnames[] = {"pipe", NULL}; PyObject *pypipe = NULL; dcerpc_InterfaceObject *pipe = NULL; struct tevent_req *req = NULL; struct mdscli_search_ctx *search = NULL; NTSTATUS status; bool ok; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", discard_const_p(char *, kwnames), &pypipe)) { PyErr_SetString(PyExc_RuntimeError, "Failed to parse args"); goto fail; } ok = py_check_dcerpc_type(pypipe, "samba.dcerpc.base", "ClientConnection"); if (!ok) { goto fail; } pipe = (dcerpc_InterfaceObject *)pypipe; search = pytalloc_get_type(self, struct mdscli_search_ctx); if (search == NULL) { goto fail; } req = mdscli_close_search_send(frame, pipe->ev, &search); if (req == NULL) { PyErr_NoMemory(); goto fail; } if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) { PyErr_SetNTSTATUS(status); goto fail; } status = mdscli_close_search_recv(req); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES)) { PyErr_SetNTSTATUS(status); goto fail; } TALLOC_FREE(req); talloc_free(frame); Py_INCREF(Py_None); return Py_None; fail: talloc_free(frame); return NULL; } static PyMethodDef search_methods[] = { { .ml_name = "get_results", .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_get_results), .ml_flags = METH_VARARGS|METH_KEYWORDS, .ml_doc = "", }, { .ml_name = "close", .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_close), .ml_flags = METH_VARARGS|METH_KEYWORDS, .ml_doc = "", }, {0}, }; static PyObject *search_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { TALLOC_CTX *frame = talloc_stackframe(); struct mdscli_search_ctx *search = NULL; PyObject *self = NULL; search = talloc_zero(frame, struct mdscli_search_ctx); if (search == NULL) { PyErr_NoMemory(); talloc_free(frame); return NULL; } self = pytalloc_steal(type, search); talloc_free(frame); return self; } static PyTypeObject search_type = { .tp_name = "mdscli.ctx.search", .tp_new = search_new, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "search([....]) -> mdssvc client search context\n", .tp_methods = search_methods, }; static PyObject *conn_sharepath(PyObject *self, PyObject *unused) { TALLOC_CTX *frame = talloc_stackframe(); struct mdscli_ctx *ctx = NULL; char *sharepath = NULL; PyObject *result = NULL; ctx = pytalloc_get_type(self, struct mdscli_ctx); if (ctx == NULL) { goto fail; } sharepath = mdscli_get_basepath(frame, ctx); if (sharepath == NULL) { PyErr_NoMemory(); goto fail; } result = PyUnicode_FromString(sharepath); fail: talloc_free(frame); return result; } static PyObject *conn_search(PyObject *self, PyObject *args, PyObject *kwargs) { TALLOC_CTX *frame = talloc_stackframe(); PyObject *pypipe = NULL; dcerpc_InterfaceObject *pipe = NULL; struct mdscli_ctx *ctx = NULL; PyObject *result = NULL; char *query = NULL; char *basepath = NULL; struct tevent_req *req = NULL; struct mdscli_search_ctx *search = NULL; const char * const kwnames[] = { "pipe", "query", "basepath", NULL }; NTSTATUS status; bool ok; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oss", discard_const_p(char *, kwnames), &pypipe, &query, &basepath)) { PyErr_SetString(PyExc_RuntimeError, "Failed to parse args"); goto fail; } ok = py_check_dcerpc_type(pypipe, "samba.dcerpc.base", "ClientConnection"); if (!ok) { goto fail; } pipe = (dcerpc_InterfaceObject *)pypipe; ctx = pytalloc_get_type(self, struct mdscli_ctx); if (ctx == NULL) { goto fail; } req = mdscli_search_send(frame, pipe->ev, ctx, query, basepath, false); if (req == NULL) { PyErr_NoMemory(); goto fail; } if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) { PyErr_SetNTSTATUS(status); goto fail; } status = mdscli_search_recv(req, frame, &search); PyErr_NTSTATUS_IS_ERR_RAISE(status); result = pytalloc_steal(&search_type, search); fail: talloc_free(frame); return result; } static PyObject *conn_disconnect(PyObject *self, PyObject *args, PyObject *kwargs) { TALLOC_CTX *frame = talloc_stackframe(); PyObject *pypipe = NULL; dcerpc_InterfaceObject *pipe = NULL; struct mdscli_ctx *ctx = NULL; struct tevent_req *req = NULL; const char * const kwnames[] = {"pipe", NULL}; NTSTATUS status; bool ok; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", discard_const_p(char *, kwnames), &pypipe)) { PyErr_SetString(PyExc_RuntimeError, "Failed to parse args"); goto fail; } ok = py_check_dcerpc_type(pypipe, "samba.dcerpc.base", "ClientConnection"); if (!ok) { goto fail; } pipe = (dcerpc_InterfaceObject *)pypipe; ctx = pytalloc_get_type(self, struct mdscli_ctx); if (ctx == NULL) { goto fail; } req = mdscli_disconnect_send(frame, pipe->ev, ctx); if (req == NULL) { PyErr_NoMemory(); goto fail; } if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) { PyErr_SetNTSTATUS(status); goto fail; } status = mdscli_disconnect_recv(req); PyErr_NTSTATUS_IS_ERR_RAISE(status); talloc_free(frame); Py_INCREF(Py_None); return Py_None; fail: talloc_free(frame); return NULL; } static PyMethodDef conn_methods[] = { { .ml_name = "sharepath", .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_sharepath), .ml_flags = METH_NOARGS, .ml_doc = "mdscli.conn.sharepath(...) -> get share basepath", }, { .ml_name = "search", .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_search), .ml_flags = METH_VARARGS|METH_KEYWORDS, .ml_doc = "mdscli.conn.search(...) -> run mdssvc query", }, { .ml_name = "disconnect", .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_disconnect), .ml_flags = METH_VARARGS|METH_KEYWORDS, .ml_doc = "mdscli.conn.disconnect(...) -> disconnect", }, {0}, }; static PyObject *conn_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { TALLOC_CTX *frame = talloc_stackframe(); const char * const kwnames[] = { "pipe", "share", "mountpoint", NULL }; PyObject *pypipe = NULL; dcerpc_InterfaceObject *pipe = NULL; struct tevent_req *req = NULL; char *share = NULL; char *mountpoint = NULL; struct mdscli_ctx *ctx = NULL; PyObject *self = NULL; NTSTATUS status; bool ok; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oss", discard_const_p(char *, kwnames), &pypipe, &share, &mountpoint)) { PyErr_SetString(PyExc_RuntimeError, "Failed to parse args"); goto fail; } ok = py_check_dcerpc_type(pypipe, "samba.dcerpc.base", "ClientConnection"); if (!ok) { goto fail; } pipe = (dcerpc_InterfaceObject *)pypipe; req = mdscli_connect_send(frame, pipe->ev, pipe->binding_handle, share, mountpoint); if (req == NULL) { PyErr_NoMemory(); goto fail; } if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) { PyErr_SetNTSTATUS(status); goto fail; } status = mdscli_connect_recv(req, frame, &ctx); PyErr_NTSTATUS_IS_ERR_RAISE(status); self = pytalloc_steal(type, ctx); fail: talloc_free(frame); return self; } static PyTypeObject conn_type = { .tp_name = "mdscli.conn", .tp_new = conn_new, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "conn([....]) -> mdssvc connection\n", .tp_methods = conn_methods, }; static PyMethodDef mdscli_methods[] = { {0}, }; static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, .m_name = "mdscli", .m_doc = "RPC mdssvc client", .m_size = -1, .m_methods = mdscli_methods, }; MODULE_INIT_FUNC(mdscli) { TALLOC_CTX *frame = talloc_stackframe(); PyObject *m = NULL; int ret; ret = pytalloc_BaseObject_PyType_Ready(&conn_type); if (ret < 0) { TALLOC_FREE(frame); return NULL; } ret = pytalloc_BaseObject_PyType_Ready(&search_type); if (ret < 0) { TALLOC_FREE(frame); return NULL; } m = PyModule_Create(&moduledef); if (m == NULL) { TALLOC_FREE(frame); return NULL; } Py_INCREF(&conn_type); PyModule_AddObject(m, "conn", (PyObject *)&conn_type); Py_INCREF(&search_type); PyModule_AddObject(m, "search", (PyObject *)&search_type); TALLOC_FREE(frame); return m; }