py3: Remove PyStr_FromString() compatability macro
[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 "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
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
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
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
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
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
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
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  */
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  */
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  */
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  */
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
844 static void py_net_dealloc(py_net_Object *self)
845 {
846         talloc_free(self->mem_ctx);
847         PyObject_Del(self);
848 }
849
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
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 }