1 /*
2 Unix SMB/CIFS implementation.
3 Samba utility functions
4
5 Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2010
6 Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <Python.h>
23 #include "python/py3compat.h"
24 #include "includes.h"
25 #include "python/modules.h"
26 #include <pyldb.h>
27 #include <pytalloc.h>
28 #include "libnet.h"
29 #include "auth/credentials/pycredentials.h"
30 #include "libcli/security/security.h"
31 #include "lib/events/events.h"
32 #include "param/pyparam.h"
33 #include "auth/gensec/gensec.h"
34 #include "librpc/rpc/pyrpc_util.h"
35 #include "libcli/resolve/resolve.h"
36 #include "libcli/finddc.h"
37 #include "dsdb/samdb/samdb.h"
38 #include "py_net.h"
39 #include "librpc/rpc/pyrpc_util.h"
40 #include "libcli/drsuapi/drsuapi.h"
41
PyErr_SetDsExtendedError(enum drsuapi_DsExtendedError ext_err,const char * error_description)42 static void PyErr_SetDsExtendedError(enum drsuapi_DsExtendedError ext_err, const char *error_description)
43 {
44 PyObject *mod = NULL;
45 PyObject *error = NULL;
46 mod = PyImport_ImportModule("samba");
47 if (mod) {
48 error = PyObject_GetAttrString(mod, "DsExtendedError");
49 }
50 if (error_description == NULL) {
51 switch (ext_err) {
52 /* Copied out of ndr_drsuapi.c:ndr_print_drsuapi_DsExtendedError() */
53 case DRSUAPI_EXOP_ERR_NONE:
54 error_description = "DRSUAPI_EXOP_ERR_NONE";
55 break;
56 case DRSUAPI_EXOP_ERR_SUCCESS:
57 error_description = "DRSUAPI_EXOP_ERR_SUCCESS";
58 break;
59 case DRSUAPI_EXOP_ERR_UNKNOWN_OP:
60 error_description = "DRSUAPI_EXOP_ERR_UNKNOWN_OP";
61 break;
62 case DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER:
63 error_description = "DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER";
64 break;
65 case DRSUAPI_EXOP_ERR_UPDATE_ERR:
66 error_description = "DRSUAPI_EXOP_ERR_UPDATE_ERR";
67 break;
68 case DRSUAPI_EXOP_ERR_EXCEPTION:
69 error_description = "DRSUAPI_EXOP_ERR_EXCEPTION";
70 break;
71 case DRSUAPI_EXOP_ERR_UNKNOWN_CALLER:
72 error_description = "DRSUAPI_EXOP_ERR_UNKNOWN_CALLER";
73 break;
74 case DRSUAPI_EXOP_ERR_RID_ALLOC:
75 error_description = "DRSUAPI_EXOP_ERR_RID_ALLOC";
76 break;
77 case DRSUAPI_EXOP_ERR_FSMO_OWNER_DELETED:
78 error_description = "DRSUAPI_EXOP_ERR_FSMO_OWNER_DELETED";
79 break;
80 case DRSUAPI_EXOP_ERR_FMSO_PENDING_OP:
81 error_description = "DRSUAPI_EXOP_ERR_FMSO_PENDING_OP";
82 break;
83 case DRSUAPI_EXOP_ERR_MISMATCH:
84 error_description = "DRSUAPI_EXOP_ERR_MISMATCH";
85 break;
86 case DRSUAPI_EXOP_ERR_COULDNT_CONTACT:
87 error_description = "DRSUAPI_EXOP_ERR_COULDNT_CONTACT";
88 break;
89 case DRSUAPI_EXOP_ERR_FSMO_REFUSING_ROLES:
90 error_description = "DRSUAPI_EXOP_ERR_FSMO_REFUSING_ROLES";
91 break;
92 case DRSUAPI_EXOP_ERR_DIR_ERROR:
93 error_description = "DRSUAPI_EXOP_ERR_DIR_ERROR";
94 break;
95 case DRSUAPI_EXOP_ERR_FSMO_MISSING_SETTINGS:
96 error_description = "DRSUAPI_EXOP_ERR_FSMO_MISSING_SETTINGS";
97 break;
98 case DRSUAPI_EXOP_ERR_ACCESS_DENIED:
99 error_description = "DRSUAPI_EXOP_ERR_ACCESS_DENIED";
100 break;
101 case DRSUAPI_EXOP_ERR_PARAM_ERROR:
102 error_description = "DRSUAPI_EXOP_ERR_PARAM_ERROR";
103 break;
104 }
105 }
106 if (error) {
107 PyObject *value =
108 Py_BuildValue(discard_const_p(char, "(i,s)"),
109 ext_err,
110 error_description);
111 PyErr_SetObject(error, value);
112 if (value) {
113 Py_DECREF(value);
114 }
115 Py_DECREF(error);
116 }
117 }
118
py_net_join_member(py_net_Object * self,PyObject * args,PyObject * kwargs)119 static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs)
120 {
121 struct libnet_Join_member r;
122 int _level = 0;
123 NTSTATUS status;
124 PyObject *result;
125 TALLOC_CTX *mem_ctx;
126 const char *kwnames[] = { "domain_name", "netbios_name", "level", "machinepass", NULL };
127
128 ZERO_STRUCT(r);
129
130 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssi|z:Join", discard_const_p(char *, kwnames),
131 &r.in.domain_name, &r.in.netbios_name,
132 &_level,
133 &r.in.account_pass)) {
134 return NULL;
135 }
136 r.in.level = _level;
137
138 mem_ctx = talloc_new(self->mem_ctx);
139 if (mem_ctx == NULL) {
140 PyErr_NoMemory();
141 return NULL;
142 }
143
144 status = libnet_Join_member(self->libnet_ctx, mem_ctx, &r);
145 if (NT_STATUS_IS_ERR(status)) {
146 PyErr_SetNTSTATUS_and_string(status,
147 r.out.error_string
148 ? r.out.error_string
149 : nt_errstr(status));
150 talloc_free(mem_ctx);
151 return NULL;
152 }
153
154 result = Py_BuildValue("sss", r.out.join_password,
155 dom_sid_string(mem_ctx, r.out.domain_sid),
156 r.out.domain_name);
157
158 talloc_free(mem_ctx);
159
160 return result;
161 }
162
163 static const char py_net_join_member_doc[] = "join_member(domain_name, netbios_name, level) -> (join_password, domain_sid, domain_name)\n\n" \
164 "Join the domain with the specified name.";
165
py_net_change_password(py_net_Object * self,PyObject * args,PyObject * kwargs)166 static PyObject *py_net_change_password(py_net_Object *self, PyObject *args, PyObject *kwargs)
167 {
168 union libnet_ChangePassword r;
169 NTSTATUS status;
170 TALLOC_CTX *mem_ctx = NULL;
171 struct tevent_context *ev = NULL;
172 const char *kwnames[] = { "newpassword", "oldpassword", "domain", "username", NULL };
173 const char *newpass = NULL;
174 const char *oldpass = NULL;
175 ZERO_STRUCT(r);
176 if (!PyArg_ParseTupleAndKeywords(args, kwargs, PYARG_STR_UNI
177 "|"PYARG_STR_UNI"ss:change_password",
178 discard_const_p(char *, kwnames),
179 "utf8",
180 &newpass,
181 "utf8",
182 &oldpass,
183 &r.generic.in.domain_name,
184 &r.generic.in.account_name)) {
185 return NULL;
186 }
187
188 r.generic.in.newpassword = newpass;
189 r.generic.in.oldpassword = oldpass;
190
191 r.generic.level = LIBNET_CHANGE_PASSWORD_GENERIC;
192 if (r.generic.in.account_name == NULL) {
193 r.generic.in.account_name
194 = cli_credentials_get_username(self->libnet_ctx->cred);
195 }
196 if (r.generic.in.domain_name == NULL) {
197 r.generic.in.domain_name
198 = cli_credentials_get_domain(self->libnet_ctx->cred);
199 }
200 if (r.generic.in.oldpassword == NULL) {
201 r.generic.in.oldpassword
202 = cli_credentials_get_password(self->libnet_ctx->cred);
203 }
204
205 /* FIXME: we really need to get a context from the caller or we may end
206 * up with 2 event contexts */
207 ev = s4_event_context_init(NULL);
208
209 mem_ctx = talloc_new(ev);
210 if (mem_ctx == NULL) {
211 PyMem_Free(discard_const_p(char, newpass));
212 PyMem_Free(discard_const_p(char, oldpass));
213 PyErr_NoMemory();
214 return NULL;
215 }
216
217 status = libnet_ChangePassword(self->libnet_ctx, mem_ctx, &r);
218
219 PyMem_Free(discard_const_p(char, newpass));
220 PyMem_Free(discard_const_p(char, oldpass));
221
222 if (NT_STATUS_IS_ERR(status)) {
223 PyErr_SetNTSTATUS_and_string(status,
224 r.generic.out.error_string
225 ? r.generic.out.error_string
226 : nt_errstr(status));
227 talloc_free(mem_ctx);
228 return NULL;
229 }
230
231 talloc_free(mem_ctx);
232 Py_RETURN_NONE;
233 }
234
235 static const char py_net_change_password_doc[] = "change_password(newpassword) -> True\n\n" \
236 "Change password for a user. You must supply credential with enough rights to do this.\n\n" \
237 "Sample usage is:\n" \
238 "net.change_password(newpassword=<new_password>)\n";
239
240
py_net_set_password(py_net_Object * self,PyObject * args,PyObject * kwargs)241 static PyObject *py_net_set_password(py_net_Object *self, PyObject *args, PyObject *kwargs)
242 {
243 union libnet_SetPassword r;
244 NTSTATUS status;
245 TALLOC_CTX *mem_ctx;
246 struct tevent_context *ev;
247 const char *kwnames[] = { "account_name", "domain_name", "newpassword", NULL };
248
249 ZERO_STRUCT(r);
250
251 r.generic.level = LIBNET_SET_PASSWORD_GENERIC;
252
253 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss:set_password",
254 discard_const_p(char *, kwnames),
255 &r.generic.in.account_name,
256 &r.generic.in.domain_name,
257 &r.generic.in.newpassword)) {
258 return NULL;
259 }
260
261 /* FIXME: we really need to get a context from the caller or we may end
262 * up with 2 event contexts */
263 ev = s4_event_context_init(NULL);
264
265 mem_ctx = talloc_new(ev);
266 if (mem_ctx == NULL) {
267 PyErr_NoMemory();
268 return NULL;
269 }
270
271 status = libnet_SetPassword(self->libnet_ctx, mem_ctx, &r);
272 if (NT_STATUS_IS_ERR(status)) {
273 PyErr_SetNTSTATUS_and_string(status,
274 r.generic.out.error_string
275 ? r.generic.out.error_string
276 : nt_errstr(status));
277 talloc_free(mem_ctx);
278 return NULL;
279 }
280
281 talloc_free(mem_ctx);
282
283 Py_RETURN_NONE;
284 }
285
286 static const char py_net_set_password_doc[] = "set_password(account_name, domain_name, newpassword) -> True\n\n" \
287 "Set password for a user. You must supply credential with enough rights to do this.\n\n" \
288 "Sample usage is:\n" \
289 "net.set_password(account_name=account_name, domain_name=domain_name, newpassword=new_pass)\n";
290
291
py_net_time(py_net_Object * self,PyObject * args,PyObject * kwargs)292 static PyObject *py_net_time(py_net_Object *self, PyObject *args, PyObject *kwargs)
293 {
294 const char *kwnames[] = { "server_name", NULL };
295 union libnet_RemoteTOD r;
296 NTSTATUS status;
297 TALLOC_CTX *mem_ctx;
298 char timestr[64];
299 PyObject *ret;
300 struct tm *tm;
301
302 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s",
303 discard_const_p(char *, kwnames), &r.generic.in.server_name))
304 return NULL;
305
306 r.generic.level = LIBNET_REMOTE_TOD_GENERIC;
307
308 mem_ctx = talloc_new(NULL);
309 if (mem_ctx == NULL) {
310 PyErr_NoMemory();
311 return NULL;
312 }
313
314 status = libnet_RemoteTOD(self->libnet_ctx, mem_ctx, &r);
315 if (!NT_STATUS_IS_OK(status)) {
316 PyErr_SetNTSTATUS_and_string(status,
317 r.generic.out.error_string
318 ? r.generic.out.error_string
319 : nt_errstr(status));
320 talloc_free(mem_ctx);
321 return NULL;
322 }
323
324 ZERO_STRUCT(timestr);
325 tm = localtime(&r.generic.out.time);
326 strftime(timestr, sizeof(timestr)-1, "%c %Z",tm);
327
328 ret = PyUnicode_FromString(timestr);
329
330 talloc_free(mem_ctx);
331
332 return ret;
333 }
334
335 static const char py_net_time_doc[] = "time(server_name) -> timestr\n"
336 "Retrieve the remote time on a server";
337
py_net_user_create(py_net_Object * self,PyObject * args,PyObject * kwargs)338 static PyObject *py_net_user_create(py_net_Object *self, PyObject *args, PyObject *kwargs)
339 {
340 const char *kwnames[] = { "username", NULL };
341 NTSTATUS status;
342 TALLOC_CTX *mem_ctx;
343 struct libnet_CreateUser r;
344
345 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames),
346 &r.in.user_name))
347 return NULL;
348
349 r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
350
351 mem_ctx = talloc_new(NULL);
352 if (mem_ctx == NULL) {
353 PyErr_NoMemory();
354 return NULL;
355 }
356
357 status = libnet_CreateUser(self->libnet_ctx, mem_ctx, &r);
358 if (!NT_STATUS_IS_OK(status)) {
359 PyErr_SetNTSTATUS_and_string(status,
360 r.out.error_string
361 ? r.out.error_string
362 : nt_errstr(status));
363 talloc_free(mem_ctx);
364 return NULL;
365 }
366
367 talloc_free(mem_ctx);
368
369 Py_RETURN_NONE;
370 }
371
372 static const char py_net_create_user_doc[] = "create_user(username)\n"
373 "Create a new user.";
374
py_net_user_delete(py_net_Object * self,PyObject * args,PyObject * kwargs)375 static PyObject *py_net_user_delete(py_net_Object *self, PyObject *args, PyObject *kwargs)
376 {
377 const char *kwnames[] = { "username", NULL };
378 NTSTATUS status;
379 TALLOC_CTX *mem_ctx;
380 struct libnet_DeleteUser r;
381
382 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames),
383 &r.in.user_name))
384 return NULL;
385
386 r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
387
388 mem_ctx = talloc_new(NULL);
389 if (mem_ctx == NULL) {
390 PyErr_NoMemory();
391 return NULL;
392 }
393
394 status = libnet_DeleteUser(self->libnet_ctx, mem_ctx, &r);
395 if (!NT_STATUS_IS_OK(status)) {
396 PyErr_SetNTSTATUS_and_string(status,
397 r.out.error_string
398 ? r.out.error_string
399 : nt_errstr(status));
400 talloc_free(mem_ctx);
401 return NULL;
402 }
403
404 talloc_free(mem_ctx);
405
406 Py_RETURN_NONE;
407 }
408
409 static const char py_net_delete_user_doc[] = "delete_user(username)\n"
410 "Delete a user.";
411
412 struct replicate_state {
413 void *vampire_state;
414 dcerpc_InterfaceObject *drs_pipe;
415 struct libnet_BecomeDC_StoreChunk chunk;
416 DATA_BLOB gensec_skey;
417 struct libnet_BecomeDC_Partition partition;
418 struct libnet_BecomeDC_Forest forest;
419 struct libnet_BecomeDC_DestDSA dest_dsa;
420 };
421
422 /*
423 setup for replicate_chunk() calls
424 */
py_net_replicate_init(py_net_Object * self,PyObject * args,PyObject * kwargs)425 static PyObject *py_net_replicate_init(py_net_Object *self, PyObject *args, PyObject *kwargs)
426 {
427 const char *kwnames[] = { "samdb", "lp", "drspipe", "invocation_id", NULL };
428 PyObject *py_ldb, *py_lp, *py_drspipe, *py_invocation_id;
429 struct ldb_context *samdb;
430 struct loadparm_context *lp;
431 struct replicate_state *s;
432 NTSTATUS status;
433
434 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO",
435 discard_const_p(char *, kwnames),
436 &py_ldb, &py_lp, &py_drspipe,
437 &py_invocation_id)) {
438 return NULL;
439 }
440
441 s = talloc_zero(NULL, struct replicate_state);
442 if (!s) return NULL;
443
444 lp = lpcfg_from_py_object(s, py_lp);
445 if (lp == NULL) {
446 PyErr_SetString(PyExc_TypeError, "Expected lp object");
447 talloc_free(s);
448 return NULL;
449 }
450
451 samdb = pyldb_Ldb_AsLdbContext(py_ldb);
452 if (samdb == NULL) {
453 PyErr_SetString(PyExc_TypeError, "Expected ldb object");
454 talloc_free(s);
455 return NULL;
456 }
457 if (!py_check_dcerpc_type(py_invocation_id, "samba.dcerpc.misc", "GUID")) {
458
459 talloc_free(s);
460 return NULL;
461 }
462 s->dest_dsa.invocation_id = *pytalloc_get_type(py_invocation_id, struct GUID);
463
464 s->drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe);
465
466 s->vampire_state = libnet_vampire_replicate_init(s, samdb, lp);
467 if (s->vampire_state == NULL) {
468 PyErr_SetString(PyExc_TypeError, "Failed to initialise vampire_state");
469 talloc_free(s);
470 return NULL;
471 }
472
473 status = gensec_session_key(s->drs_pipe->pipe->conn->security_state.generic_state,
474 s,
475 &s->gensec_skey);
476 if (!NT_STATUS_IS_OK(status)) {
477 char *error_string = talloc_asprintf(s,
478 "Unable to get session key from drspipe: %s",
479 nt_errstr(status));
480 PyErr_SetNTSTATUS_and_string(status, error_string);
481 talloc_free(s);
482 return NULL;
483 }
484
485 s->forest.dns_name = samdb_dn_to_dns_domain(s, ldb_get_root_basedn(samdb));
486 s->forest.root_dn_str = ldb_dn_get_linearized(ldb_get_root_basedn(samdb));
487 s->forest.config_dn_str = ldb_dn_get_linearized(ldb_get_config_basedn(samdb));
488 s->forest.schema_dn_str = ldb_dn_get_linearized(ldb_get_schema_basedn(samdb));
489
490 s->chunk.gensec_skey = &s->gensec_skey;
491 s->chunk.partition = &s->partition;
492 s->chunk.forest = &s->forest;
493 s->chunk.dest_dsa = &s->dest_dsa;
494
495 return pytalloc_GenericObject_steal(s);
496 }
497
498
499 /*
500 process one replication chunk
501 */
py_net_replicate_chunk(py_net_Object * self,PyObject * args,PyObject * kwargs)502 static PyObject *py_net_replicate_chunk(py_net_Object *self, PyObject *args, PyObject *kwargs)
503 {
504 const char *kwnames[] = { "state", "level", "ctr",
505 "schema", "req_level", "req",
506 NULL };
507 PyObject *py_state, *py_ctr, *py_schema = Py_None, *py_req = Py_None;
508 struct replicate_state *s;
509 unsigned level;
510 unsigned req_level = 0;
511 WERROR (*chunk_handler)(void *private_data, const struct libnet_BecomeDC_StoreChunk *c);
512 WERROR werr;
513 enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
514 enum drsuapi_DsExtendedOperation exop = DRSUAPI_EXOP_NONE;
515
516 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OIO|OIO",
517 discard_const_p(char *, kwnames),
518 &py_state, &level, &py_ctr,
519 &py_schema, &req_level, &py_req)) {
520 return NULL;
521 }
522
523 s = pytalloc_get_type(py_state, struct replicate_state);
524 if (!s) {
525 return NULL;
526 }
527
528 switch (level) {
529 case 1:
530 if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr1")) {
531 return NULL;
532 }
533 s->chunk.ctr1 = pytalloc_get_ptr(py_ctr);
534 if (s->chunk.ctr1->naming_context != NULL) {
535 s->partition.nc = *s->chunk.ctr1->naming_context;
536 }
537 extended_ret = s->chunk.ctr1->extended_ret;
538 s->partition.more_data = s->chunk.ctr1->more_data;
539 s->partition.source_dsa_guid = s->chunk.ctr1->source_dsa_guid;
540 s->partition.source_dsa_invocation_id = s->chunk.ctr1->source_dsa_invocation_id;
541 s->partition.highwatermark = s->chunk.ctr1->new_highwatermark;
542 break;
543 case 6:
544 if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr6")) {
545 return NULL;
546 }
547 s->chunk.ctr6 = pytalloc_get_ptr(py_ctr);
548 if (s->chunk.ctr6->naming_context != NULL) {
549 s->partition.nc = *s->chunk.ctr6->naming_context;
550 }
551 extended_ret = s->chunk.ctr6->extended_ret;
552 s->partition.more_data = s->chunk.ctr6->more_data;
553 s->partition.source_dsa_guid = s->chunk.ctr6->source_dsa_guid;
554 s->partition.source_dsa_invocation_id = s->chunk.ctr6->source_dsa_invocation_id;
555 s->partition.highwatermark = s->chunk.ctr6->new_highwatermark;
556 break;
557 default:
558 PyErr_Format(PyExc_TypeError, "Bad level %u in replicate_chunk", level);
559 return NULL;
560 }
561
562 s->chunk.req5 = NULL;
563 s->chunk.req8 = NULL;
564 s->chunk.req10 = NULL;
565 if (py_req != Py_None) {
566 switch (req_level) {
567 case 0:
568 break;
569 case 5:
570 if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest5")) {
571 return NULL;
572 }
573
574 s->chunk.req5 = pytalloc_get_ptr(py_req);
575 exop = s->chunk.req5->extended_op;
576 break;
577 case 8:
578 if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest8")) {
579 return NULL;
580 }
581
582 s->chunk.req8 = pytalloc_get_ptr(py_req);
583 exop = s->chunk.req8->extended_op;
584 break;
585 case 10:
586 if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest10")) {
587 return NULL;
588 }
589
590 s->chunk.req10 = pytalloc_get_ptr(py_req);
591 exop = s->chunk.req10->extended_op;
592 break;
593 default:
594 PyErr_Format(PyExc_TypeError, "Bad req_level %u in replicate_chunk", req_level);
595 return NULL;
596 }
597 }
598
599 if (exop != DRSUAPI_EXOP_NONE && extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
600 PyErr_SetDsExtendedError(extended_ret, NULL);
601 return NULL;
602 }
603
604 s->chunk.req_level = req_level;
605
606 chunk_handler = libnet_vampire_cb_store_chunk;
607 if (py_schema) {
608 if (!PyBool_Check(py_schema)) {
609 PyErr_SetString(PyExc_TypeError, "Expected boolean schema");
610 return NULL;
611 }
612 if (py_schema == Py_True) {
613 chunk_handler = libnet_vampire_cb_schema_chunk;
614 }
615 }
616
617 s->chunk.ctr_level = level;
618
619 werr = chunk_handler(s->vampire_state, &s->chunk);
620 if (!W_ERROR_IS_OK(werr)) {
621 char *error_string
622 = talloc_asprintf(NULL,
623 "Failed to process 'chunk' of DRS replicated objects: %s",
624 win_errstr(werr));
625 PyErr_SetWERROR_and_string(werr, error_string);
626 TALLOC_FREE(error_string);
627 return NULL;
628 }
629
630 Py_RETURN_NONE;
631 }
632
633
634 /*
635 just do the decryption of a DRS replicated attribute
636 */
py_net_replicate_decrypt(py_net_Object * self,PyObject * args,PyObject * kwargs)637 static PyObject *py_net_replicate_decrypt(py_net_Object *self, PyObject *args, PyObject *kwargs)
638 {
639 const char *kwnames[] = { "drspipe", "attribute", "rid", NULL };
640 PyObject *py_drspipe, *py_attribute;
641 NTSTATUS status;
642 dcerpc_InterfaceObject *drs_pipe;
643 TALLOC_CTX *frame;
644 TALLOC_CTX *context;
645 DATA_BLOB gensec_skey;
646 unsigned int rid;
647 struct drsuapi_DsReplicaAttribute *attribute;
648 WERROR werr;
649
650 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOI",
651 discard_const_p(char *, kwnames),
652 &py_drspipe,
653 &py_attribute, &rid)) {
654 return NULL;
655 }
656
657 frame = talloc_stackframe();
658
659 if (!py_check_dcerpc_type(py_drspipe,
660 "samba.dcerpc.base",
661 "ClientConnection")) {
662 return NULL;
663 }
664 drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe);
665
666 status = gensec_session_key(drs_pipe->pipe->conn->security_state.generic_state,
667 frame,
668 &gensec_skey);
669 if (!NT_STATUS_IS_OK(status)) {
670 char *error_string
671 = talloc_asprintf(frame,
672 "Unable to get session key from drspipe: %s",
673 nt_errstr(status));
674 PyErr_SetNTSTATUS_and_string(status, error_string);
675 talloc_free(frame);
676 return NULL;
677 }
678
679 if (!py_check_dcerpc_type(py_attribute, "samba.dcerpc.drsuapi",
680 "DsReplicaAttribute")) {
681 return NULL;
682 }
683
684 attribute = pytalloc_get_ptr(py_attribute);
685 context = pytalloc_get_mem_ctx(py_attribute);
686 werr = drsuapi_decrypt_attribute(context, &gensec_skey,
687 rid, 0, attribute);
688 if (!W_ERROR_IS_OK(werr)) {
689 char *error_string = talloc_asprintf(frame,
690 "Unable to get decrypt attribute: %s",
691 win_errstr(werr));
692 PyErr_SetWERROR_and_string(werr, error_string);
693 talloc_free(frame);
694 return NULL;
695 }
696
697 talloc_free(frame);
698
699 Py_RETURN_NONE;
700
701 }
702
703 /*
704 find a DC given a domain name and server type
705 */
py_net_finddc(py_net_Object * self,PyObject * args,PyObject * kwargs)706 static PyObject *py_net_finddc(py_net_Object *self, PyObject *args, PyObject *kwargs)
707 {
708 const char *domain = NULL, *address = NULL;
709 unsigned server_type;
710 NTSTATUS status;
711 struct finddcs *io;
712 TALLOC_CTX *mem_ctx;
713 PyObject *ret;
714 const char * const kwnames[] = { "flags", "domain", "address", NULL };
715
716 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I|zz",
717 discard_const_p(char *, kwnames),
718 &server_type, &domain, &address)) {
719 return NULL;
720 }
721
722 mem_ctx = talloc_new(self->mem_ctx);
723 if (mem_ctx == NULL) {
724 PyErr_NoMemory();
725 return NULL;
726 }
727
728 io = talloc_zero(mem_ctx, struct finddcs);
729 if (io == NULL) {
730 TALLOC_FREE(mem_ctx);
731 PyErr_NoMemory();
732 return NULL;
733 }
734
735 if (domain != NULL) {
736 io->in.domain_name = domain;
737 }
738 if (address != NULL) {
739 io->in.server_address = address;
740 }
741 io->in.minimum_dc_flags = server_type;
742
743 status = finddcs_cldap(io, io,
744 lpcfg_resolve_context(self->libnet_ctx->lp_ctx), self->ev);
745 if (NT_STATUS_IS_ERR(status)) {
746 PyErr_SetNTSTATUS(status);
747 talloc_free(mem_ctx);
748 return NULL;
749 }
750
751 ret = py_return_ndr_struct("samba.dcerpc.nbt", "NETLOGON_SAM_LOGON_RESPONSE_EX",
752 io, &io->out.netlogon.data.nt5_ex);
753 talloc_free(mem_ctx);
754
755 return ret;
756 }
757
758
759 static const char py_net_replicate_init_doc[] = "replicate_init(samdb, lp, drspipe)\n"
760 "Setup for replicate_chunk calls.";
761
762 static const char py_net_replicate_chunk_doc[] = "replicate_chunk(state, level, ctr, schema)\n"
763 "Process replication for one chunk";
764
765 static const char py_net_replicate_decrypt_doc[] = "replicate_decrypt(drs, attribute, rid)\n"
766 "Decrypt (in place) a DsReplicaAttribute replicated with drs.GetNCChanges()";
767
768 static const char py_net_finddc_doc[] = "finddc(flags=server_type, domain=None, address=None)\n"
769 "Find a DC with the specified 'server_type' bits. The 'domain' and/or 'address' have to be used as additional search criteria. Returns the whole netlogon struct";
770
771 static PyMethodDef net_obj_methods[] = {
772 {
773 .ml_name = "join_member",
774 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
775 py_net_join_member),
776 .ml_flags = METH_VARARGS|METH_KEYWORDS,
777 .ml_doc = py_net_join_member_doc
778 },
779 {
780 .ml_name = "change_password",
781 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
782 py_net_change_password),
783 .ml_flags = METH_VARARGS|METH_KEYWORDS,
784 .ml_doc = py_net_change_password_doc
785 },
786 {
787 .ml_name = "set_password",
788 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
789 py_net_set_password),
790 .ml_flags = METH_VARARGS|METH_KEYWORDS,
791 .ml_doc = py_net_set_password_doc
792 },
793 {
794 .ml_name = "time",
795 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, py_net_time),
796 .ml_flags = METH_VARARGS|METH_KEYWORDS,
797 .ml_doc = py_net_time_doc
798 },
799 {
800 .ml_name = "create_user",
801 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
802 py_net_user_create),
803 .ml_flags = METH_VARARGS|METH_KEYWORDS,
804 .ml_doc = py_net_create_user_doc
805 },
806 {
807 .ml_name = "delete_user",
808 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
809 py_net_user_delete),
810 .ml_flags = METH_VARARGS|METH_KEYWORDS,
811 .ml_doc = py_net_delete_user_doc
812 },
813 {
814 .ml_name = "replicate_init",
815 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
816 py_net_replicate_init),
817 .ml_flags = METH_VARARGS|METH_KEYWORDS,
818 .ml_doc = py_net_replicate_init_doc
819 },
820 {
821 .ml_name = "replicate_chunk",
822 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
823 py_net_replicate_chunk),
824 .ml_flags = METH_VARARGS|METH_KEYWORDS,
825 .ml_doc = py_net_replicate_chunk_doc
826 },
827 {
828 .ml_name = "replicate_decrypt",
829 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
830 py_net_replicate_decrypt),
831 .ml_flags = METH_VARARGS|METH_KEYWORDS,
832 .ml_doc = py_net_replicate_decrypt_doc
833 },
834 {
835 .ml_name = "finddc",
836 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
837 py_net_finddc),
838 .ml_flags = METH_VARARGS|METH_KEYWORDS,
839 .ml_doc = py_net_finddc_doc
840 },
841 { .ml_name = NULL }
842 };
843
py_net_dealloc(py_net_Object * self)844 static void py_net_dealloc(py_net_Object *self)
845 {
846 talloc_free(self->mem_ctx);
847 PyObject_Del(self);
848 }
849
net_obj_new(PyTypeObject * type,PyObject * args,PyObject * kwargs)850 static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
851 {
852 PyObject *py_creds, *py_lp = Py_None;
853 const char *kwnames[] = { "creds", "lp", "server", NULL };
854 py_net_Object *ret;
855 struct loadparm_context *lp;
856 const char *server_address = NULL;
857
858 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz",
859 discard_const_p(char *, kwnames), &py_creds, &py_lp,
860 &server_address))
861 return NULL;
862
863 ret = PyObject_New(py_net_Object, type);
864 if (ret == NULL) {
865 return NULL;
866 }
867
868 /* FIXME: we really need to get a context from the caller or we may end
869 * up with 2 event contexts */
870 ret->ev = s4_event_context_init(NULL);
871 ret->mem_ctx = talloc_new(ret->ev);
872
873 lp = lpcfg_from_py_object(ret->mem_ctx, py_lp);
874 if (lp == NULL) {
875 Py_DECREF(ret);
876 return NULL;
877 }
878
879 ret->libnet_ctx = libnet_context_init(ret->ev, lp);
880 if (ret->libnet_ctx == NULL) {
881 PyErr_SetString(PyExc_RuntimeError, "Unable to initialize net");
882 Py_DECREF(ret);
883 return NULL;
884 }
885
886 ret->libnet_ctx->server_address = server_address;
887
888 ret->libnet_ctx->cred = cli_credentials_from_py_object(py_creds);
889 if (ret->libnet_ctx->cred == NULL) {
890 PyErr_SetString(PyExc_TypeError, "Expected credentials object");
891 Py_DECREF(ret);
892 return NULL;
893 }
894
895 return (PyObject *)ret;
896 }
897
898
899 PyTypeObject py_net_Type = {
900 PyVarObject_HEAD_INIT(NULL, 0)
901 .tp_name = "net.Net",
902 .tp_basicsize = sizeof(py_net_Object),
903 .tp_dealloc = (destructor)py_net_dealloc,
904 .tp_methods = net_obj_methods,
905 .tp_new = net_obj_new,
906 };
907
908 static struct PyModuleDef moduledef = {
909 PyModuleDef_HEAD_INIT,
910 .m_name = "net",
911 .m_size = -1,
912 };
913
MODULE_INIT_FUNC(net)914 MODULE_INIT_FUNC(net)
915 {
916 PyObject *m;
917
918 if (PyType_Ready(&py_net_Type) < 0)
919 return NULL;
920
921 m = PyModule_Create(&moduledef);
922 if (m == NULL)
923 return NULL;
924
925 Py_INCREF(&py_net_Type);
926 PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
927 PyModule_AddIntConstant(m, "LIBNET_JOINDOMAIN_AUTOMATIC", LIBNET_JOINDOMAIN_AUTOMATIC);
928 PyModule_AddIntConstant(m, "LIBNET_JOINDOMAIN_SPECIFIED", LIBNET_JOINDOMAIN_SPECIFIED);
929 PyModule_AddIntConstant(m, "LIBNET_JOIN_AUTOMATIC", LIBNET_JOIN_AUTOMATIC);
930 PyModule_AddIntConstant(m, "LIBNET_JOIN_SPECIFIED", LIBNET_JOIN_SPECIFIED);
931
932 return m;
933 }
934