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