pyrpc: Add py_check_dcerpc_type().
[kai/samba-autobuild/.git] / source4 / libnet / py_net.c
1 /*
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2010
5    Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <Python.h>
22 #include "includes.h"
23 #include "libnet.h"
24 #include "auth/credentials/pycredentials.h"
25 #include "libcli/security/security.h"
26 #include "lib/events/events.h"
27 #include "param/param.h"
28 #include "param/pyparam.h"
29 #include "lib/ldb/pyldb.h"
30 #include "auth/gensec/gensec.h"
31 #include "librpc/rpc/pyrpc.h"
32 #include "librpc/rpc/pyrpc_util.h"
33
34 typedef struct {
35         PyObject_HEAD
36         TALLOC_CTX *mem_ctx;
37         struct libnet_context *libnet_ctx;
38         struct tevent_context *ev;
39 } py_net_Object;
40
41 static PyObject *py_net_join(py_net_Object *self, PyObject *args, PyObject *kwargs)
42 {
43         struct libnet_Join r;
44         NTSTATUS status;
45         PyObject *result;
46         TALLOC_CTX *mem_ctx;
47         const char *kwnames[] = { "domain_name", "netbios_name", "join_type", "level", NULL };
48
49         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssii:Join", discard_const_p(char *, kwnames), 
50                                          &r.in.domain_name, &r.in.netbios_name, 
51                                          &r.in.join_type, &r.in.level))
52                 return NULL;
53
54         mem_ctx = talloc_new(self->mem_ctx);
55
56         status = libnet_Join(self->libnet_ctx, mem_ctx, &r);
57         if (NT_STATUS_IS_ERR(status)) {
58                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
59                 talloc_free(mem_ctx);
60                 return NULL;
61         }
62
63         result = Py_BuildValue("sss", r.out.join_password,
64                                dom_sid_string(mem_ctx, r.out.domain_sid),
65                                r.out.domain_name);
66
67         talloc_free(mem_ctx);
68
69         return result;
70 }
71
72 static const char py_net_join_doc[] = "join(domain_name, netbios_name, join_type, level) -> (join_password, domain_sid, domain_name)\n\n" \
73 "Join the domain with the specified name.";
74
75 static PyObject *py_net_set_password(py_net_Object *self, PyObject *args, PyObject *kwargs)
76 {
77         union libnet_SetPassword r;
78         NTSTATUS status;
79         PyObject *py_creds;
80         TALLOC_CTX *mem_ctx;
81         struct tevent_context *ev;
82         const char *kwnames[] = { "account_name", "domain_name", "newpassword", "credentials", NULL };
83
84         r.generic.level = LIBNET_SET_PASSWORD_GENERIC;
85
86         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sssO:set_password", discard_const_p(char *, kwnames),
87                                          &r.generic.in.account_name, &r.generic.in.domain_name,
88                                          &r.generic.in.newpassword, &py_creds)) {
89                 return NULL;
90         }
91
92         /* FIXME: we really need to get a context from the caller or we may end
93          * up with 2 event contexts */
94         ev = s4_event_context_init(NULL);
95         mem_ctx = talloc_new(ev);
96
97         status = libnet_SetPassword(self->libnet_ctx, mem_ctx, &r);
98         if (NT_STATUS_IS_ERR(status)) {
99                 PyErr_SetString(PyExc_RuntimeError,
100                                 r.generic.out.error_string?r.generic.out.error_string:nt_errstr(status));
101                 talloc_free(mem_ctx);
102                 return NULL;
103         }
104
105         talloc_free(mem_ctx);
106
107         Py_RETURN_NONE;
108 }
109
110 static const char py_net_set_password_doc[] = "set_password(account_name, domain_name, newpassword) -> True\n\n" \
111 "Set password for a user. You must supply credential with enough rights to do this.\n\n" \
112 "Sample usage is:\n" \
113 "net.set_password(account_name=<account_name>,\n" \
114 "                domain_name=domain_name,\n" \
115 "                newpassword=new_pass)\n";
116
117
118 static PyObject *py_net_export_keytab(py_net_Object *self, PyObject *args, PyObject *kwargs)
119 {
120         struct libnet_export_keytab r;
121         TALLOC_CTX *mem_ctx;
122         const char *kwnames[] = { "keytab", NULL };
123         NTSTATUS status;
124
125         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:export_keytab", discard_const_p(char *, kwnames),
126                                          &r.in.keytab_name)) {
127                 return NULL;
128         }
129
130         mem_ctx = talloc_new(self->mem_ctx);
131
132         status = libnet_export_keytab(self->libnet_ctx, mem_ctx, &r);
133         if (NT_STATUS_IS_ERR(status)) {
134                 PyErr_SetString(PyExc_RuntimeError,
135                                 r.out.error_string?r.out.error_string:nt_errstr(status));
136                 talloc_free(mem_ctx);
137                 return NULL;
138         }
139
140         talloc_free(mem_ctx);
141
142         Py_RETURN_NONE;
143 }
144
145 static const char py_net_export_keytab_doc[] = "export_keytab(keytab, name)\n\n"
146 "Export the DC keytab to a keytab file.";
147
148 static PyObject *py_net_time(py_net_Object *self, PyObject *args, PyObject *kwargs)
149 {
150         const char *kwnames[] = { "server_name", NULL };
151         union libnet_RemoteTOD r;
152         NTSTATUS status;
153         TALLOC_CTX *mem_ctx;
154         char timestr[64];
155         PyObject *ret;
156         struct tm *tm;
157
158         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s",
159                 discard_const_p(char *, kwnames), &r.generic.in.server_name))
160                 return NULL;
161
162         r.generic.level                 = LIBNET_REMOTE_TOD_GENERIC;
163
164         mem_ctx = talloc_new(NULL);
165         if (mem_ctx == NULL) {
166                 PyErr_NoMemory();
167                 return NULL;
168         }
169
170         status = libnet_RemoteTOD(self->libnet_ctx, mem_ctx, &r);
171         if (!NT_STATUS_IS_OK(status)) {
172                 PyErr_SetString(PyExc_RuntimeError,
173                                 r.generic.out.error_string?r.generic.out.error_string:nt_errstr(status));
174                 talloc_free(mem_ctx);
175                 return NULL;
176         }
177
178         ZERO_STRUCT(timestr);
179         tm = localtime(&r.generic.out.time);
180         strftime(timestr, sizeof(timestr)-1, "%c %Z",tm);
181         
182         ret = PyString_FromString(timestr);
183
184         talloc_free(mem_ctx);
185
186         return ret;
187 }
188
189 static const char py_net_time_doc[] = "time(server_name) -> timestr\n"
190 "Retrieve the remote time on a server";
191
192 static PyObject *py_net_user_create(py_net_Object *self, PyObject *args, PyObject *kwargs)
193 {
194         const char *kwnames[] = { "username", NULL };
195         NTSTATUS status;
196         TALLOC_CTX *mem_ctx;
197         struct libnet_CreateUser r;
198
199         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), 
200                                                                          &r.in.user_name))
201                 return NULL;
202
203         r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
204
205         mem_ctx = talloc_new(NULL);
206         if (mem_ctx == NULL) {
207                 PyErr_NoMemory();
208                 return NULL;
209         }
210
211         status = libnet_CreateUser(self->libnet_ctx, mem_ctx, &r);
212         if (!NT_STATUS_IS_OK(status)) {
213                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
214                 talloc_free(mem_ctx);
215                 return NULL;
216         }
217
218         talloc_free(mem_ctx);
219         
220         Py_RETURN_NONE;
221 }
222
223 static const char py_net_create_user_doc[] = "create_user(username)\n"
224 "Create a new user.";
225
226 static PyObject *py_net_user_delete(py_net_Object *self, PyObject *args, PyObject *kwargs)
227 {
228         const char *kwnames[] = { "username", NULL };
229         NTSTATUS status;
230         TALLOC_CTX *mem_ctx;
231         struct libnet_DeleteUser r;
232
233         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), 
234                                                                          &r.in.user_name))
235                 return NULL;
236
237         r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
238
239         mem_ctx = talloc_new(NULL);
240         if (mem_ctx == NULL) {
241                 PyErr_NoMemory();
242                 return NULL;
243         }
244
245         status = libnet_DeleteUser(self->libnet_ctx, mem_ctx, &r);
246         if (!NT_STATUS_IS_OK(status)) {
247                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
248                 talloc_free(mem_ctx);
249                 return NULL;
250         }
251
252         talloc_free(mem_ctx);
253         
254         Py_RETURN_NONE;
255 }
256
257 static const char py_net_delete_user_doc[] = "delete_user(username)\n"
258 "Delete a user.";
259
260 static PyObject *py_dom_sid_FromSid(struct dom_sid *sid)
261 {
262         PyObject *mod_security, *dom_sid_Type;
263
264         mod_security = PyImport_ImportModule("samba.dcerpc.security");
265         if (mod_security == NULL)
266                 return NULL;
267
268         dom_sid_Type = PyObject_GetAttrString(mod_security, "dom_sid");
269         if (dom_sid_Type == NULL)
270                 return NULL;
271
272         return py_talloc_reference((PyTypeObject *)dom_sid_Type, sid);
273 }
274
275 static PyObject *py_net_vampire(py_net_Object *self, PyObject *args, PyObject *kwargs)
276 {
277         const char *kwnames[] = { "domain", "target_dir", NULL };
278         NTSTATUS status;
279         TALLOC_CTX *mem_ctx;
280         PyObject *ret;
281         struct libnet_Vampire r;
282
283         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z", discard_const_p(char *, kwnames),
284                                          &r.in.domain_name, &r.in.targetdir)) {
285                 return NULL;
286         }
287
288         r.in.netbios_name  = lpcfg_netbios_name(self->libnet_ctx->lp_ctx);
289         r.out.error_string = NULL;
290
291         mem_ctx = talloc_new(NULL);
292         if (mem_ctx == NULL) {
293                 PyErr_NoMemory();
294                 return NULL;
295         }
296
297         status = libnet_Vampire(self->libnet_ctx, mem_ctx, &r);
298
299         if (!NT_STATUS_IS_OK(status)) {
300                 PyErr_SetString(PyExc_RuntimeError,
301                                 r.out.error_string ? r.out.error_string : nt_errstr(status));
302                 talloc_free(mem_ctx);
303                 return NULL;
304         }
305
306         ret = Py_BuildValue("(sO)", r.out.domain_name, py_dom_sid_FromSid(r.out.domain_sid));
307
308         talloc_free(mem_ctx);
309
310         return ret;
311 }
312
313 struct replicate_state {
314         void *vampire_state;
315         dcerpc_InterfaceObject *drs_pipe;
316         struct libnet_BecomeDC_StoreChunk chunk;
317         DATA_BLOB gensec_skey;
318         struct libnet_BecomeDC_Partition partition;
319         struct libnet_BecomeDC_Forest forest;
320         struct libnet_BecomeDC_DestDSA dest_dsa;
321 };
322
323 /*
324   setup for replicate_chunk() calls
325  */
326 static PyObject *py_net_replicate_init(py_net_Object *self, PyObject *args, PyObject *kwargs)
327 {
328         const char *kwnames[] = { "samdb", "lp", "drspipe", NULL };
329         PyObject *py_ldb, *py_lp, *py_drspipe;
330         struct ldb_context *samdb;
331         struct loadparm_context *lp;
332         struct replicate_state *s;
333         NTSTATUS status;
334
335         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO",
336                                          discard_const_p(char *, kwnames),
337                                          &py_ldb, &py_lp, &py_drspipe)) {
338                 return NULL;
339         }
340
341         s = talloc_zero(NULL, struct replicate_state);
342         if (!s) return NULL;
343
344         lp = lpcfg_from_py_object(s, py_lp);
345         if (lp == NULL) {
346                 PyErr_SetString(PyExc_TypeError, "Expected lp object");
347                 talloc_free(s);
348                 return NULL;
349         }
350
351         samdb = PyLdb_AsLdbContext(py_ldb);
352         if (samdb == NULL) {
353                 PyErr_SetString(PyExc_TypeError, "Expected ldb object");
354                 talloc_free(s);
355                 return NULL;
356         }
357
358         s->drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe);
359
360         s->vampire_state = libnet_vampire_replicate_init(s, samdb, lp);
361         if (s->vampire_state == NULL) {
362                 PyErr_SetString(PyExc_TypeError, "Failed to initialise vampire_state");
363                 talloc_free(s);
364                 return NULL;
365         }
366
367         status = gensec_session_key(s->drs_pipe->pipe->conn->security_state.generic_state,
368                                     &s->gensec_skey);
369         if (!NT_STATUS_IS_OK(status)) {
370                 PyErr_Format(PyExc_RuntimeError, "Unable to get session key from drspipe: %s",
371                              nt_errstr(status));
372                 talloc_free(s);
373                 return NULL;
374         }
375
376         s->chunk.gensec_skey = &s->gensec_skey;
377         s->chunk.partition = &s->partition;
378         s->chunk.forest = &s->forest;
379         s->chunk.dest_dsa = &s->dest_dsa;
380
381         return PyCObject_FromTallocPtr(s);
382 }
383
384
385 /*
386   process one replication chunk
387  */
388 static PyObject *py_net_replicate_chunk(py_net_Object *self, PyObject *args, PyObject *kwargs)
389 {
390         const char *kwnames[] = { "state", "level", "ctr", "schema", NULL };
391         PyObject *py_state, *py_ctr, *py_schema;
392         struct replicate_state *s;
393         unsigned level;
394         NTSTATUS (*chunk_handler)(void *private_data, const struct libnet_BecomeDC_StoreChunk *c);
395         NTSTATUS status;
396
397         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OIO|O",
398                                          discard_const_p(char *, kwnames),
399                                          &py_state, &level, &py_ctr, &py_schema)) {
400                 return NULL;
401         }
402
403         s = talloc_get_type(PyCObject_AsVoidPtr(py_state), struct replicate_state);
404         if (!s) {
405                 PyErr_SetString(PyExc_TypeError, "Expected replication_state");
406                 return NULL;
407         }
408
409         switch (level) {
410         case 1:
411                 if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr1")) {
412                         return NULL;
413                 }
414                 s->chunk.ctr1                         = py_talloc_get_ptr(py_ctr);
415                 s->partition.nc                       = *s->chunk.ctr1->naming_context;
416                 s->partition.more_data                = s->chunk.ctr1->more_data;
417                 s->partition.source_dsa_guid          = s->chunk.ctr1->source_dsa_guid;
418                 s->partition.source_dsa_invocation_id = s->chunk.ctr1->source_dsa_invocation_id;
419                 s->partition.highwatermark            = s->chunk.ctr1->new_highwatermark;
420                 break;
421         case 6:
422                 if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr6")) {
423                         return NULL;
424                 }
425                 s->chunk.ctr6                         = py_talloc_get_ptr(py_ctr);
426                 s->partition.nc                       = *s->chunk.ctr6->naming_context;
427                 s->partition.more_data                = s->chunk.ctr6->more_data;
428                 s->partition.source_dsa_guid          = s->chunk.ctr6->source_dsa_guid;
429                 s->partition.source_dsa_invocation_id = s->chunk.ctr6->source_dsa_invocation_id;
430                 s->partition.highwatermark            = s->chunk.ctr6->new_highwatermark;
431                 break;
432         default:
433                 PyErr_Format(PyExc_TypeError, "Bad level %u in replicate_chunk", level);
434                 return NULL;
435         }
436
437         chunk_handler = libnet_vampire_cb_store_chunk;
438         if (py_schema) {
439                 if (!PyBool_Check(py_schema)) {
440                         PyErr_SetString(PyExc_TypeError, "Expected boolean schema");
441                         return NULL;
442                 }
443                 if (py_schema == Py_True) {
444                         chunk_handler = libnet_vampire_cb_schema_chunk;
445                 }
446         }
447
448         s->chunk.ctr_level = level;
449
450         status = chunk_handler(s->vampire_state, &s->chunk);
451         if (!NT_STATUS_IS_OK(status)) {
452                 PyErr_Format(PyExc_TypeError, "Failed to process chunk: %s", nt_errstr(status));
453                 return NULL;
454         }
455
456         return Py_None;
457 }
458
459 static const char py_net_vampire_doc[] = "vampire(domain, target_dir=None)\n"
460                                          "Vampire a domain.";
461
462 static const char py_net_replicate_init_doc[] = "replicate_init(samdb, lp, drspipe)\n"
463                                          "Setup for replicate_chunk calls.";
464
465 static const char py_net_replicate_chunk_doc[] = "replicate_chunk(state, level, ctr, schema)\n"
466                                          "Process replication for one chunk";
467
468 static PyMethodDef net_obj_methods[] = {
469         {"join", (PyCFunction)py_net_join, METH_VARARGS|METH_KEYWORDS, py_net_join_doc},
470         {"set_password", (PyCFunction)py_net_set_password, METH_VARARGS|METH_KEYWORDS, py_net_set_password_doc},
471         {"export_keytab", (PyCFunction)py_net_export_keytab, METH_VARARGS|METH_KEYWORDS, py_net_export_keytab_doc},
472         {"time", (PyCFunction)py_net_time, METH_VARARGS|METH_KEYWORDS, py_net_time_doc},
473         {"create_user", (PyCFunction)py_net_user_create, METH_VARARGS|METH_KEYWORDS, py_net_create_user_doc},
474         {"delete_user", (PyCFunction)py_net_user_delete, METH_VARARGS|METH_KEYWORDS, py_net_delete_user_doc},
475         {"vampire", (PyCFunction)py_net_vampire, METH_VARARGS|METH_KEYWORDS, py_net_vampire_doc},
476         {"replicate_init", (PyCFunction)py_net_replicate_init, METH_VARARGS|METH_KEYWORDS, py_net_replicate_init_doc},
477         {"replicate_chunk", (PyCFunction)py_net_replicate_chunk, METH_VARARGS|METH_KEYWORDS, py_net_replicate_chunk_doc},
478         { NULL }
479 };
480
481 static void py_net_dealloc(py_net_Object *self)
482 {
483         talloc_free(self->mem_ctx);
484 }
485
486 static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
487 {
488         PyObject *py_creds, *py_lp = Py_None;
489         const char *kwnames[] = { "creds", "lp", NULL };
490         py_net_Object *ret;
491         struct loadparm_context *lp;
492
493         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", 
494                         discard_const_p(char *, kwnames), &py_creds, &py_lp))
495                 return NULL;
496
497         ret = PyObject_New(py_net_Object, type);
498         if (ret == NULL) {
499                 return NULL;
500         }
501
502         /* FIXME: we really need to get a context from the caller or we may end
503          * up with 2 event contexts */
504         ret->ev = s4_event_context_init(NULL);
505         ret->mem_ctx = talloc_new(ret->ev);
506
507         lp = lpcfg_from_py_object(ret->mem_ctx, py_lp);
508         if (lp == NULL) {
509                 Py_DECREF(ret);
510                 return NULL;
511         }
512
513         ret->libnet_ctx = libnet_context_init(ret->ev, lp);
514         if (ret->libnet_ctx == NULL) {
515                 PyErr_SetString(PyExc_RuntimeError, "Unable to initialize net");
516                 Py_DECREF(ret);
517                 return NULL;
518         }
519
520         ret->libnet_ctx->cred = cli_credentials_from_py_object(py_creds);
521         if (ret->libnet_ctx->cred == NULL) {
522                 PyErr_SetString(PyExc_TypeError, "Expected credentials object");
523                 Py_DECREF(ret);
524                 return NULL;
525         }
526
527         return (PyObject *)ret;
528 }
529
530
531 PyTypeObject py_net_Type = {
532         PyObject_HEAD_INIT(NULL) 0,
533         .tp_name = "net.Net",
534         .tp_basicsize = sizeof(py_net_Object),
535         .tp_dealloc = (destructor)py_net_dealloc,
536         .tp_methods = net_obj_methods,
537         .tp_new = net_obj_new,
538 };
539
540 void initnet(void)
541 {
542         PyObject *m;
543
544         if (PyType_Ready(&py_net_Type) < 0)
545                 return;
546
547         m = Py_InitModule3("net", NULL, NULL);
548         if (m == NULL)
549                 return;
550
551         Py_INCREF(&py_net_Type);
552         PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
553         PyModule_AddObject(m, "LIBNET_JOINDOMAIN_AUTOMATIC", PyInt_FromLong(LIBNET_JOINDOMAIN_AUTOMATIC));
554         PyModule_AddObject(m, "LIBNET_JOINDOMAIN_SPECIFIED", PyInt_FromLong(LIBNET_JOINDOMAIN_SPECIFIED));
555         PyModule_AddObject(m, "LIBNET_JOIN_AUTOMATIC", PyInt_FromLong(LIBNET_JOIN_AUTOMATIC));
556         PyModule_AddObject(m, "LIBNET_JOIN_SPECIFIED", PyInt_FromLong(LIBNET_JOIN_SPECIFIED));
557 }