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