libnet-s4: added replicate() command in pynet
[kamenim/samba.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
30 typedef struct {
31         PyObject_HEAD
32         TALLOC_CTX *mem_ctx;
33         struct libnet_context *libnet_ctx;
34         struct tevent_context *ev;
35 } py_net_Object;
36
37 static PyObject *py_net_join(py_net_Object *self, PyObject *args, PyObject *kwargs)
38 {
39         struct libnet_Join r;
40         NTSTATUS status;
41         PyObject *result;
42         TALLOC_CTX *mem_ctx;
43         const char *kwnames[] = { "domain_name", "netbios_name", "join_type", "level", NULL };
44
45         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssii:Join", discard_const_p(char *, kwnames), 
46                                          &r.in.domain_name, &r.in.netbios_name, 
47                                          &r.in.join_type, &r.in.level))
48                 return NULL;
49
50         mem_ctx = talloc_new(self->mem_ctx);
51
52         status = libnet_Join(self->libnet_ctx, mem_ctx, &r);
53         if (NT_STATUS_IS_ERR(status)) {
54                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
55                 talloc_free(mem_ctx);
56                 return NULL;
57         }
58
59         result = Py_BuildValue("sss", r.out.join_password,
60                                dom_sid_string(mem_ctx, r.out.domain_sid),
61                                r.out.domain_name);
62
63         talloc_free(mem_ctx);
64
65         return result;
66 }
67
68 static const char py_net_join_doc[] = "join(domain_name, netbios_name, join_type, level) -> (join_password, domain_sid, domain_name)\n\n" \
69 "Join the domain with the specified name.";
70
71 static PyObject *py_net_set_password(py_net_Object *self, PyObject *args, PyObject *kwargs)
72 {
73         union libnet_SetPassword r;
74         NTSTATUS status;
75         PyObject *py_creds;
76         TALLOC_CTX *mem_ctx;
77         struct tevent_context *ev;
78         const char *kwnames[] = { "account_name", "domain_name", "newpassword", "credentials", NULL };
79
80         r.generic.level = LIBNET_SET_PASSWORD_GENERIC;
81
82         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sssO:set_password", discard_const_p(char *, kwnames),
83                                          &r.generic.in.account_name, &r.generic.in.domain_name,
84                                          &r.generic.in.newpassword, &py_creds)) {
85                 return NULL;
86         }
87
88         /* FIXME: we really need to get a context from the caller or we may end
89          * up with 2 event contexts */
90         ev = s4_event_context_init(NULL);
91         mem_ctx = talloc_new(ev);
92
93         status = libnet_SetPassword(self->libnet_ctx, mem_ctx, &r);
94         if (NT_STATUS_IS_ERR(status)) {
95                 PyErr_SetString(PyExc_RuntimeError,
96                                 r.generic.out.error_string?r.generic.out.error_string:nt_errstr(status));
97                 talloc_free(mem_ctx);
98                 return NULL;
99         }
100
101         talloc_free(mem_ctx);
102
103         Py_RETURN_NONE;
104 }
105
106 static const char py_net_set_password_doc[] = "set_password(account_name, domain_name, newpassword) -> True\n\n" \
107 "Set password for a user. You must supply credential with enough rights to do this.\n\n" \
108 "Sample usage is:\n" \
109 "net.set_password(account_name=<account_name>,\n" \
110 "                domain_name=domain_name,\n" \
111 "                newpassword=new_pass)\n";
112
113
114 static PyObject *py_net_export_keytab(py_net_Object *self, PyObject *args, PyObject *kwargs)
115 {
116         struct libnet_export_keytab r;
117         TALLOC_CTX *mem_ctx;
118         const char *kwnames[] = { "keytab", NULL };
119         NTSTATUS status;
120
121         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:export_keytab", discard_const_p(char *, kwnames),
122                                          &r.in.keytab_name)) {
123                 return NULL;
124         }
125
126         mem_ctx = talloc_new(self->mem_ctx);
127
128         status = libnet_export_keytab(self->libnet_ctx, mem_ctx, &r);
129         if (NT_STATUS_IS_ERR(status)) {
130                 PyErr_SetString(PyExc_RuntimeError,
131                                 r.out.error_string?r.out.error_string:nt_errstr(status));
132                 talloc_free(mem_ctx);
133                 return NULL;
134         }
135
136         talloc_free(mem_ctx);
137
138         Py_RETURN_NONE;
139 }
140
141 static const char py_net_export_keytab_doc[] = "export_keytab(keytab, name)\n\n"
142 "Export the DC keytab to a keytab file.";
143
144 static PyObject *py_net_time(py_net_Object *self, PyObject *args, PyObject *kwargs)
145 {
146         const char *kwnames[] = { "server_name", NULL };
147         union libnet_RemoteTOD r;
148         NTSTATUS status;
149         TALLOC_CTX *mem_ctx;
150         char timestr[64];
151         PyObject *ret;
152         struct tm *tm;
153
154         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s",
155                 discard_const_p(char *, kwnames), &r.generic.in.server_name))
156                 return NULL;
157
158         r.generic.level                 = LIBNET_REMOTE_TOD_GENERIC;
159
160         mem_ctx = talloc_new(NULL);
161         if (mem_ctx == NULL) {
162                 PyErr_NoMemory();
163                 return NULL;
164         }
165
166         status = libnet_RemoteTOD(self->libnet_ctx, mem_ctx, &r);
167         if (!NT_STATUS_IS_OK(status)) {
168                 PyErr_SetString(PyExc_RuntimeError,
169                                 r.generic.out.error_string?r.generic.out.error_string:nt_errstr(status));
170                 talloc_free(mem_ctx);
171                 return NULL;
172         }
173
174         ZERO_STRUCT(timestr);
175         tm = localtime(&r.generic.out.time);
176         strftime(timestr, sizeof(timestr)-1, "%c %Z",tm);
177         
178         ret = PyString_FromString(timestr);
179
180         talloc_free(mem_ctx);
181
182         return ret;
183 }
184
185 static const char py_net_time_doc[] = "time(server_name) -> timestr\n"
186 "Retrieve the remote time on a server";
187
188 static PyObject *py_net_user_create(py_net_Object *self, PyObject *args, PyObject *kwargs)
189 {
190         const char *kwnames[] = { "username", NULL };
191         NTSTATUS status;
192         TALLOC_CTX *mem_ctx;
193         struct libnet_CreateUser r;
194
195         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), 
196                                                                          &r.in.user_name))
197                 return NULL;
198
199         r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
200
201         mem_ctx = talloc_new(NULL);
202         if (mem_ctx == NULL) {
203                 PyErr_NoMemory();
204                 return NULL;
205         }
206
207         status = libnet_CreateUser(self->libnet_ctx, mem_ctx, &r);
208         if (!NT_STATUS_IS_OK(status)) {
209                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
210                 talloc_free(mem_ctx);
211                 return NULL;
212         }
213
214         talloc_free(mem_ctx);
215         
216         Py_RETURN_NONE;
217 }
218
219 static const char py_net_create_user_doc[] = "create_user(username)\n"
220 "Create a new user.";
221
222 static PyObject *py_net_user_delete(py_net_Object *self, PyObject *args, PyObject *kwargs)
223 {
224         const char *kwnames[] = { "username", NULL };
225         NTSTATUS status;
226         TALLOC_CTX *mem_ctx;
227         struct libnet_DeleteUser r;
228
229         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), 
230                                                                          &r.in.user_name))
231                 return NULL;
232
233         r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
234
235         mem_ctx = talloc_new(NULL);
236         if (mem_ctx == NULL) {
237                 PyErr_NoMemory();
238                 return NULL;
239         }
240
241         status = libnet_DeleteUser(self->libnet_ctx, mem_ctx, &r);
242         if (!NT_STATUS_IS_OK(status)) {
243                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
244                 talloc_free(mem_ctx);
245                 return NULL;
246         }
247
248         talloc_free(mem_ctx);
249         
250         Py_RETURN_NONE;
251 }
252
253 static const char py_net_delete_user_doc[] = "delete_user(username)\n"
254 "Delete a user.";
255
256 static PyObject *py_dom_sid_FromSid(struct dom_sid *sid)
257 {
258         PyObject *mod_security, *dom_sid_Type;
259
260         mod_security = PyImport_ImportModule("samba.dcerpc.security");
261         if (mod_security == NULL)
262                 return NULL;
263
264         dom_sid_Type = PyObject_GetAttrString(mod_security, "dom_sid");
265         if (dom_sid_Type == NULL)
266                 return NULL;
267
268         return py_talloc_reference((PyTypeObject *)dom_sid_Type, sid);
269 }
270
271 static PyObject *py_net_vampire(py_net_Object *self, PyObject *args, PyObject *kwargs)
272 {
273         const char *kwnames[] = { "domain", "target_dir", NULL };
274         NTSTATUS status;
275         TALLOC_CTX *mem_ctx;
276         PyObject *ret;
277         struct libnet_Vampire r;
278
279         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z", discard_const_p(char *, kwnames),
280                                          &r.in.domain_name, &r.in.targetdir)) {
281                 return NULL;
282         }
283
284         r.in.netbios_name  = lpcfg_netbios_name(self->libnet_ctx->lp_ctx);
285         r.out.error_string = NULL;
286
287         mem_ctx = talloc_new(NULL);
288         if (mem_ctx == NULL) {
289                 PyErr_NoMemory();
290                 return NULL;
291         }
292
293         status = libnet_Vampire(self->libnet_ctx, mem_ctx, &r);
294
295         if (!NT_STATUS_IS_OK(status)) {
296                 PyErr_SetString(PyExc_RuntimeError,
297                                 r.out.error_string ? r.out.error_string : nt_errstr(status));
298                 talloc_free(mem_ctx);
299                 return NULL;
300         }
301
302         ret = Py_BuildValue("(sO)", r.out.domain_name, py_dom_sid_FromSid(r.out.domain_sid));
303
304         talloc_free(mem_ctx);
305
306         return ret;
307 }
308
309
310 static PyObject *py_net_replicate(py_net_Object *self, PyObject *args, PyObject *kwargs)
311 {
312         const char *kwnames[] = { "domain", "netbios_name",
313                                   "domain_sid", "realm", "server", "join_password",
314                                   "kvno", "target_dir", NULL };
315         NTSTATUS status;
316         TALLOC_CTX *mem_ctx;
317         struct libnet_Replicate r;
318         unsigned kvno;
319         const char *sidstr;
320
321         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssssssI|z",
322                                          discard_const_p(char *, kwnames),
323                                          &r.in.domain_name,
324                                          &r.in.netbios_name,
325                                          &sidstr,
326                                          &r.in.realm,
327                                          &r.in.server,
328                                          &r.in.join_password,
329                                          &kvno,
330                                          &r.in.targetdir)) {
331                 return NULL;
332         }
333
334         r.out.error_string = NULL;
335
336         mem_ctx = talloc_new(NULL);
337         if (mem_ctx == NULL) {
338                 PyErr_NoMemory();
339                 return NULL;
340         }
341
342         r.in.kvno       = kvno;
343         r.in.domain_sid = dom_sid_parse_talloc(mem_ctx, sidstr);
344
345         if (!r.in.domain_sid) {
346                 PyErr_Format(PyExc_RuntimeError, "Bad domain_sid %s", sidstr);
347                 talloc_free(mem_ctx);
348                 return NULL;
349         }
350
351         status = libnet_Replicate(self->libnet_ctx, mem_ctx, &r);
352
353         if (!NT_STATUS_IS_OK(status)) {
354                 PyErr_SetString(PyExc_RuntimeError,
355                                 r.out.error_string ? r.out.error_string : nt_errstr(status));
356                 talloc_free(mem_ctx);
357                 return NULL;
358         }
359
360         talloc_free(mem_ctx);
361
362         return Py_None;
363 }
364
365 static const char py_net_vampire_doc[] = "vampire(domain, target_dir=None)\n"
366                                          "Vampire a domain.";
367
368 static const char py_net_replicate_doc[] = "replicate(domain, netbios_name, domain_sid, realm, server, join_password, kvno, target_dir=None)\n"
369                                          "Replicate a domain.";
370
371 static PyMethodDef net_obj_methods[] = {
372         {"join", (PyCFunction)py_net_join, METH_VARARGS|METH_KEYWORDS, py_net_join_doc},
373         {"set_password", (PyCFunction)py_net_set_password, METH_VARARGS|METH_KEYWORDS, py_net_set_password_doc},
374         {"export_keytab", (PyCFunction)py_net_export_keytab, METH_VARARGS|METH_KEYWORDS, py_net_export_keytab_doc},
375         {"time", (PyCFunction)py_net_time, METH_VARARGS|METH_KEYWORDS, py_net_time_doc},
376         {"create_user", (PyCFunction)py_net_user_create, METH_VARARGS|METH_KEYWORDS, py_net_create_user_doc},
377         {"delete_user", (PyCFunction)py_net_user_delete, METH_VARARGS|METH_KEYWORDS, py_net_delete_user_doc},
378         {"vampire", (PyCFunction)py_net_vampire, METH_VARARGS|METH_KEYWORDS, py_net_vampire_doc},
379         {"replicate", (PyCFunction)py_net_replicate, METH_VARARGS|METH_KEYWORDS, py_net_replicate_doc},
380         { NULL }
381 };
382
383 static void py_net_dealloc(py_net_Object *self)
384 {
385         talloc_free(self->mem_ctx);
386 }
387
388 static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
389 {
390         PyObject *py_creds, *py_lp = Py_None;
391         const char *kwnames[] = { "creds", "lp", NULL };
392         py_net_Object *ret;
393         struct loadparm_context *lp;
394
395         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", 
396                         discard_const_p(char *, kwnames), &py_creds, &py_lp))
397                 return NULL;
398
399         ret = PyObject_New(py_net_Object, type);
400         if (ret == NULL) {
401                 return NULL;
402         }
403
404         /* FIXME: we really need to get a context from the caller or we may end
405          * up with 2 event contexts */
406         ret->ev = s4_event_context_init(NULL);
407         ret->mem_ctx = talloc_new(ret->ev);
408
409         lp = lpcfg_from_py_object(ret->mem_ctx, py_lp);
410         if (lp == NULL) {
411                 Py_DECREF(ret);
412                 return NULL;
413         }
414
415         ret->libnet_ctx = libnet_context_init(ret->ev, lp);
416         if (ret->libnet_ctx == NULL) {
417                 PyErr_SetString(PyExc_RuntimeError, "Unable to initialize net");
418                 Py_DECREF(ret);
419                 return NULL;
420         }
421
422         ret->libnet_ctx->cred = cli_credentials_from_py_object(py_creds);
423         if (ret->libnet_ctx->cred == NULL) {
424                 PyErr_SetString(PyExc_TypeError, "Expected credentials object");
425                 Py_DECREF(ret);
426                 return NULL;
427         }
428
429         return (PyObject *)ret;
430 }
431
432
433 PyTypeObject py_net_Type = {
434         PyObject_HEAD_INIT(NULL) 0,
435         .tp_name = "net.Net",
436         .tp_basicsize = sizeof(py_net_Object),
437         .tp_dealloc = (destructor)py_net_dealloc,
438         .tp_methods = net_obj_methods,
439         .tp_new = net_obj_new,
440 };
441
442 void initnet(void)
443 {
444         PyObject *m;
445
446         if (PyType_Ready(&py_net_Type) < 0)
447                 return;
448
449         m = Py_InitModule3("net", NULL, NULL);
450         if (m == NULL)
451                 return;
452
453         Py_INCREF(&py_net_Type);
454         PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
455         PyModule_AddObject(m, "LIBNET_JOINDOMAIN_AUTOMATIC", PyInt_FromLong(LIBNET_JOINDOMAIN_AUTOMATIC));
456         PyModule_AddObject(m, "LIBNET_JOINDOMAIN_SPECIFIED", PyInt_FromLong(LIBNET_JOINDOMAIN_SPECIFIED));
457         PyModule_AddObject(m, "LIBNET_JOIN_AUTOMATIC", PyInt_FromLong(LIBNET_JOIN_AUTOMATIC));
458         PyModule_AddObject(m, "LIBNET_JOIN_SPECIFIED", PyInt_FromLong(LIBNET_JOIN_SPECIFIED));
459 }