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