1/* 2 Unix SMB/CIFS implementation. 3 Samba utility functions 4 Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "includes.h" 21#include <Python.h> 22#include <structmember.h> 23#include "librpc/rpc/pyrpc.h" 24#include "librpc/rpc/dcerpc.h" 25#include "lib/events/events.h" 26#include "param/pyparam.h" 27#include "auth/credentials/pycredentials.h" 28 29#ifndef Py_RETURN_NONE 30#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None 31#endif 32 33static PyObject *py_dcerpc_run_function(dcerpc_InterfaceObject *iface, 34 const struct PyNdrRpcMethodDef *md, 35 PyObject *args, PyObject *kwargs) 36{ 37 TALLOC_CTX *mem_ctx; 38 NTSTATUS status; 39 void *r; 40 PyObject *result = Py_None; 41 42 if (md->pack_in_data == NULL || md->unpack_out_data == NULL) { 43 PyErr_SetString(PyExc_NotImplementedError, "No marshalling code available yet"); 44 return NULL; 45 } 46 47 mem_ctx = talloc_new(NULL); 48 if (mem_ctx == NULL) { 49 PyErr_NoMemory(); 50 return NULL; 51 } 52 53 r = talloc_zero_size(mem_ctx, md->table->calls[md->opnum].struct_size); 54 if (r == NULL) { 55 PyErr_NoMemory(); 56 return NULL; 57 } 58 59 if (!md->pack_in_data(args, kwargs, r)) { 60 talloc_free(mem_ctx); 61 return NULL; 62 } 63 64 status = md->call(iface->pipe, mem_ctx, r); 65 if (NT_STATUS_IS_ERR(status)) { 66 PyErr_SetDCERPCStatus(iface->pipe, status); 67 talloc_free(mem_ctx); 68 return NULL; 69 } 70 71 result = md->unpack_out_data(r); 72 73 talloc_free(mem_ctx); 74 return result; 75} 76 77static PyObject *py_dcerpc_call_wrapper(PyObject *self, PyObject *args, void *wrapped, PyObject *kwargs) 78{ 79 dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self; 80 const struct PyNdrRpcMethodDef *md = (const struct PyNdrRpcMethodDef *)wrapped; 81 82 return py_dcerpc_run_function(iface, md, args, kwargs); 83} 84 85 86bool PyInterface_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpcMethodDef *mds) 87{ 88 int i; 89 for (i = 0; mds[i].name; i++) { 90 PyObject *ret; 91 struct wrapperbase *wb = (struct wrapperbase *)calloc(sizeof(struct wrapperbase), 1); 92 93 wb->name = discard_const_p(char, mds[i].name); 94 wb->flags = PyWrapperFlag_KEYWORDS; 95 wb->wrapper = (wrapperfunc)py_dcerpc_call_wrapper; 96 wb->doc = discard_const_p(char, mds[i].doc); 97 98 ret = PyDescr_NewWrapper(ifacetype, wb, discard_const_p(void, &mds[i])); 99 100 PyDict_SetItemString(ifacetype->tp_dict, mds[i].name, 101 (PyObject *)ret); 102 } 103 104 return true; 105} 106 107static bool PyString_AsGUID(PyObject *object, struct GUID *uuid) 108{ 109 NTSTATUS status; 110 status = GUID_from_string(PyString_AsString(object), uuid); 111 if (NT_STATUS_IS_ERR(status)) { 112 PyErr_SetNTSTATUS(status); 113 return false; 114 } 115 return true; 116} 117 118static bool ndr_syntax_from_py_object(PyObject *object, struct ndr_syntax_id *syntax_id) 119{ 120 ZERO_STRUCTP(syntax_id); 121 122 if (PyString_Check(object)) { 123 return PyString_AsGUID(object, &syntax_id->uuid); 124 } else if (PyTuple_Check(object)) { 125 if (PyTuple_Size(object) < 1 || PyTuple_Size(object) > 2) { 126 PyErr_SetString(PyExc_ValueError, "Syntax ID tuple has invalid size"); 127 return false; 128 } 129 130 if (!PyString_Check(PyTuple_GetItem(object, 0))) { 131 PyErr_SetString(PyExc_ValueError, "Expected GUID as first element in tuple"); 132 return false; 133 } 134 135 if (!PyString_AsGUID(PyTuple_GetItem(object, 0), &syntax_id->uuid)) 136 return false; 137 138 if (!PyInt_Check(PyTuple_GetItem(object, 1))) { 139 PyErr_SetString(PyExc_ValueError, "Expected version as second element in tuple"); 140 return false; 141 } 142 143 syntax_id->if_version = PyInt_AsLong(PyTuple_GetItem(object, 1)); 144 return true; 145 } 146 147 PyErr_SetString(PyExc_TypeError, "Expected UUID or syntax id tuple"); 148 return false; 149} 150 151static PyObject *py_iface_server_name(PyObject *obj, void *closure) 152{ 153 const char *server_name; 154 dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj; 155 156 server_name = dcerpc_server_name(iface->pipe); 157 if (server_name == NULL) 158 Py_RETURN_NONE; 159 160 return PyString_FromString(server_name); 161} 162 163static PyObject *py_ndr_syntax_id(struct ndr_syntax_id *syntax_id) 164{ 165 PyObject *ret; 166 char *uuid_str; 167 168 uuid_str = GUID_string(NULL, &syntax_id->uuid); 169 if (uuid_str == NULL) 170 return NULL; 171 172 ret = Py_BuildValue("(s,i)", uuid_str, syntax_id->if_version); 173 174 talloc_free(uuid_str); 175 176 return ret; 177} 178 179static PyObject *py_iface_abstract_syntax(PyObject *obj, void *closure) 180{ 181 dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj; 182 183 return py_ndr_syntax_id(&iface->pipe->syntax); 184} 185 186static PyObject *py_iface_transfer_syntax(PyObject *obj, void *closure) 187{ 188 dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj; 189 190 return py_ndr_syntax_id(&iface->pipe->transfer_syntax); 191} 192 193static PyGetSetDef dcerpc_interface_getsetters[] = { 194 { discard_const_p(char, "server_name"), py_iface_server_name, NULL, 195 discard_const_p(char, "name of the server, if connected over SMB") }, 196 { discard_const_p(char, "abstract_syntax"), py_iface_abstract_syntax, NULL, 197 discard_const_p(char, "syntax id of the abstract syntax") }, 198 { discard_const_p(char, "transfer_syntax"), py_iface_transfer_syntax, NULL, 199 discard_const_p(char, "syntax id of the transfersyntax") }, 200 { NULL } 201}; 202 203static PyMemberDef dcerpc_interface_members[] = { 204 { discard_const_p(char, "request_timeout"), T_INT, 205 offsetof(struct dcerpc_pipe, request_timeout), 0, 206 discard_const_p(char, "request timeout, in seconds") }, 207 { NULL } 208}; 209 210void PyErr_SetDCERPCStatus(struct dcerpc_pipe *p, NTSTATUS status) 211{ 212 if (p != NULL && NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { 213 const char *errstr = dcerpc_errstr(NULL, p->last_fault_code); 214 PyErr_SetObject(PyExc_RuntimeError, 215 Py_BuildValue("(i,s)", p->last_fault_code, 216 errstr)); 217 } else { 218 PyErr_SetNTSTATUS(status); 219 } 220} 221 222static PyObject *py_iface_request(PyObject *self, PyObject *args, PyObject *kwargs) 223{ 224 dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self; 225 int opnum; 226 DATA_BLOB data_in, data_out; 227 NTSTATUS status; 228 char *in_data; 229 int in_length; 230 PyObject *ret; 231 PyObject *object = NULL; 232 struct GUID object_guid; 233 TALLOC_CTX *mem_ctx = talloc_new(NULL); 234 const char *kwnames[] = { "opnum", "data", "object", NULL }; 235 236 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "is#|O:request", 237 discard_const_p(char *, kwnames), &opnum, &in_data, &in_length, &object)) { 238 return NULL; 239 } 240 241 data_in.data = (uint8_t *)talloc_memdup(mem_ctx, in_data, in_length); 242 data_in.length = in_length; 243 244 ZERO_STRUCT(data_out); 245 246 if (object != NULL && !PyString_AsGUID(object, &object_guid)) { 247 return NULL; 248 } 249 250 status = dcerpc_request(iface->pipe, object?&object_guid:NULL, 251 opnum, mem_ctx, &data_in, &data_out); 252 253 if (NT_STATUS_IS_ERR(status)) { 254 PyErr_SetDCERPCStatus(iface->pipe, status); 255 talloc_free(mem_ctx); 256 return NULL; 257 } 258 259 ret = PyString_FromStringAndSize((char *)data_out.data, data_out.length); 260 261 talloc_free(mem_ctx); 262 return ret; 263} 264 265static PyObject *py_iface_alter_context(PyObject *self, PyObject *args, PyObject *kwargs) 266{ 267 dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self; 268 NTSTATUS status; 269 const char *kwnames[] = { "abstract_syntax", "transfer_syntax", NULL }; 270 PyObject *py_abstract_syntax = Py_None, *py_transfer_syntax = Py_None; 271 struct ndr_syntax_id abstract_syntax, transfer_syntax; 272 273 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:alter_context", 274 discard_const_p(char *, kwnames), &py_abstract_syntax, 275 &py_transfer_syntax)) { 276 return NULL; 277 } 278 279 if (!ndr_syntax_from_py_object(py_abstract_syntax, &abstract_syntax)) 280 return NULL; 281 282 if (py_transfer_syntax == Py_None) { 283 transfer_syntax = ndr_transfer_syntax; 284 } else { 285 if (!ndr_syntax_from_py_object(py_transfer_syntax, 286 &transfer_syntax)) 287 return NULL; 288 } 289 290 status = dcerpc_alter_context(iface->pipe, iface->pipe, &abstract_syntax, 291 &transfer_syntax); 292 293 if (NT_STATUS_IS_ERR(status)) { 294 PyErr_SetDCERPCStatus(iface->pipe, status); 295 return NULL; 296 } 297 298 Py_RETURN_NONE; 299} 300 301PyObject *py_dcerpc_interface_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs, const struct ndr_interface_table *table) 302{ 303 dcerpc_InterfaceObject *ret; 304 const char *binding_string; 305 struct cli_credentials *credentials; 306 struct loadparm_context *lp_ctx = NULL; 307 PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None, *py_basis = Py_None; 308 TALLOC_CTX *mem_ctx = NULL; 309 struct tevent_context *event_ctx; 310 NTSTATUS status; 311 312 const char *kwnames[] = { 313 "binding", "lp_ctx", "credentials", "basis_connection", NULL 314 }; 315 316 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO:samr", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials, &py_basis)) { 317 return NULL; 318 } 319 320 lp_ctx = lp_from_py_object(py_lp_ctx); 321 if (lp_ctx == NULL) { 322 PyErr_SetString(PyExc_TypeError, "Expected loadparm context"); 323 return NULL; 324 } 325 326 status = dcerpc_init(lp_ctx); 327 if (!NT_STATUS_IS_OK(status)) { 328 PyErr_SetNTSTATUS(status); 329 return NULL; 330 } 331 credentials = cli_credentials_from_py_object(py_credentials); 332 if (credentials == NULL) { 333 PyErr_SetString(PyExc_TypeError, "Expected credentials"); 334 return NULL; 335 } 336 ret = PyObject_New(dcerpc_InterfaceObject, type); 337 338 event_ctx = event_context_init(mem_ctx); 339 340 if (py_basis != Py_None) { 341 struct dcerpc_pipe *base_pipe; 342 343 if (!PyObject_TypeCheck(py_basis, &dcerpc_InterfaceType)) { 344 PyErr_SetString(PyExc_ValueError, "basis_connection must be a DCE/RPC connection"); 345 talloc_free(mem_ctx); 346 return NULL; 347 } 348 349 base_pipe = ((dcerpc_InterfaceObject *)py_basis)->pipe; 350 351 status = dcerpc_secondary_context(base_pipe, &ret->pipe, table); 352 } else { 353 status = dcerpc_pipe_connect(NULL, &ret->pipe, binding_string, 354 table, credentials, event_ctx, lp_ctx); 355 } 356 if (NT_STATUS_IS_ERR(status)) { 357 PyErr_SetNTSTATUS(status); 358 talloc_free(mem_ctx); 359 return NULL; 360 } 361 362 ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC; 363 return (PyObject *)ret; 364} 365 366static PyMethodDef dcerpc_interface_methods[] = { 367 { "request", (PyCFunction)py_iface_request, METH_VARARGS|METH_KEYWORDS, "S.request(opnum, data, object=None) -> data\nMake a raw request" }, 368 { "alter_context", (PyCFunction)py_iface_alter_context, METH_VARARGS|METH_KEYWORDS, "S.alter_context(syntax)\nChange to a different interface" }, 369 { NULL, NULL, 0, NULL }, 370}; 371 372 373static void dcerpc_interface_dealloc(PyObject* self) 374{ 375 dcerpc_InterfaceObject *interface = (dcerpc_InterfaceObject *)self; 376 talloc_free(interface->pipe); 377 PyObject_Del(self); 378} 379 380static PyObject *dcerpc_interface_new(PyTypeObject *self, PyObject *args, PyObject *kwargs) 381{ 382 dcerpc_InterfaceObject *ret; 383 const char *binding_string; 384 struct cli_credentials *credentials; 385 struct loadparm_context *lp_ctx = NULL; 386 PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None; 387 TALLOC_CTX *mem_ctx = NULL; 388 struct tevent_context *event_ctx; 389 NTSTATUS status; 390 391 PyObject *syntax, *py_basis = Py_None; 392 const char *kwnames[] = { 393 "binding", "syntax", "lp_ctx", "credentials", "basis_connection", NULL 394 }; 395 struct ndr_interface_table *table; 396 397 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OOO:connect", discard_const_p(char *, kwnames), &binding_string, &syntax, &py_lp_ctx, &py_credentials, &py_basis)) { 398 return NULL; 399 } 400 401 lp_ctx = lp_from_py_object(py_lp_ctx); 402 if (lp_ctx == NULL) { 403 PyErr_SetString(PyExc_TypeError, "Expected loadparm context"); 404 return NULL; 405 } 406 407 credentials = cli_credentials_from_py_object(py_credentials); 408 if (credentials == NULL) { 409 PyErr_SetString(PyExc_TypeError, "Expected credentials"); 410 return NULL; 411 } 412 ret = PyObject_New(dcerpc_InterfaceObject, &dcerpc_InterfaceType); 413 414 event_ctx = s4_event_context_init(mem_ctx); 415 416 /* Create a dummy interface table struct. TODO: In the future, we should rather just allow 417 * connecting without requiring an interface table. 418 */ 419 420 table = talloc_zero(mem_ctx, struct ndr_interface_table); 421 422 if (table == NULL) { 423 PyErr_SetString(PyExc_MemoryError, "Allocating interface table"); 424 return NULL; 425 } 426 427 if (!ndr_syntax_from_py_object(syntax, &table->syntax_id)) { 428 return NULL; 429 } 430 431 ret->pipe = NULL; 432 433 if (py_basis != Py_None) { 434 struct dcerpc_pipe *base_pipe; 435 436 if (!PyObject_TypeCheck(py_basis, &dcerpc_InterfaceType)) { 437 PyErr_SetString(PyExc_ValueError, "basis_connection must be a DCE/RPC connection"); 438 talloc_free(mem_ctx); 439 return NULL; 440 } 441 442 base_pipe = ((dcerpc_InterfaceObject *)py_basis)->pipe; 443 444 status = dcerpc_secondary_context(base_pipe, &ret->pipe, 445 table); 446 ret->pipe = talloc_steal(NULL, ret->pipe); 447 } else { 448 status = dcerpc_pipe_connect(NULL, &ret->pipe, binding_string, 449 table, credentials, event_ctx, lp_ctx); 450 } 451 452 if (NT_STATUS_IS_ERR(status)) { 453 PyErr_SetDCERPCStatus(ret->pipe, status); 454 talloc_free(mem_ctx); 455 return NULL; 456 } 457 ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC; 458 return (PyObject *)ret; 459} 460 461PyTypeObject dcerpc_InterfaceType = { 462 PyObject_HEAD_INIT(NULL) 0, 463 .tp_name = "dcerpc.ClientConnection", 464 .tp_basicsize = sizeof(dcerpc_InterfaceObject), 465 .tp_dealloc = dcerpc_interface_dealloc, 466 .tp_getset = dcerpc_interface_getsetters, 467 .tp_members = dcerpc_interface_members, 468 .tp_methods = dcerpc_interface_methods, 469 .tp_doc = "ClientConnection(binding, syntax, lp_ctx=None, credentials=None) -> connection\n" 470"\n" 471"binding should be a DCE/RPC binding string (for example: ncacn_ip_tcp:127.0.0.1)\n" 472"syntax should be a tuple with a GUID and version number of an interface\n" 473"lp_ctx should be a path to a smb.conf file or a param.LoadParm object\n" 474"credentials should be a credentials.Credentials object.\n\n", 475 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 476 .tp_new = dcerpc_interface_new, 477}; 478 479void initbase(void) 480{ 481 PyObject *m; 482 483 if (PyType_Ready(&dcerpc_InterfaceType) < 0) 484 return; 485 486 m = Py_InitModule3("base", NULL, "DCE/RPC protocol implementation"); 487 if (m == NULL) 488 return; 489 490 Py_INCREF((PyObject *)&dcerpc_InterfaceType); 491 PyModule_AddObject(m, "ClientConnection", (PyObject *)&dcerpc_InterfaceType); 492} 493