47f296afcdd023d9f1e679ace070a420e0635443
[sharpe/samba-autobuild/.git] / source4 / param / provision.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2009
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
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 "python/py3compat.h"
23 #include <ldb.h>
24 #include <pyldb.h>
25 #include "includes.h"
26 #include "librpc/ndr/libndr.h"
27 #include "param/provision.h"
28 #include "param/secrets.h"
29 #include <pytalloc.h>
30 #include "python/modules.h"
31 #include "param/pyparam.h"
32 #include "dynconfig/dynconfig.h"
33
34 static bool dict_insert(PyObject* dict,
35                         const char* key,
36                         PyObject* value)
37 {
38         if (PyDict_SetItemString(dict, key, value) == -1) {
39                 Py_XDECREF(value);
40                 return false;
41         }
42         Py_XDECREF(value);
43         return true;
44 }
45
46 static PyObject *provision_module(void)
47 {
48         PyObject *name = PyStr_FromString("samba.provision");
49         PyObject *mod = NULL;
50         if (name == NULL)
51                 return NULL;
52         mod = PyImport_Import(name);
53         Py_CLEAR(name);
54         return mod;
55 }
56
57 static PyObject *schema_module(void)
58 {
59         PyObject *name = PyStr_FromString("samba.schema");
60         PyObject *mod = NULL;
61         if (name == NULL)
62                 return NULL;
63         mod = PyImport_Import(name);
64         Py_CLEAR(name);
65         return mod;
66 }
67
68 static PyObject *ldb_module(void)
69 {
70         PyObject *name = PyStr_FromString("ldb");
71         PyObject *mod = NULL;
72         if (name == NULL)
73                 return NULL;
74         mod = PyImport_Import(name);
75         Py_CLEAR(name);
76         return mod;
77 }
78
79 static PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx)
80 {
81         PyLdbObject *ret;
82         PyObject *ldb_mod = ldb_module();
83         PyTypeObject *ldb_ctx_type;
84         if (ldb_mod == NULL)
85                 return NULL;
86
87         ldb_ctx_type = (PyTypeObject *)PyObject_GetAttrString(ldb_mod, "Ldb");
88
89         ret = (PyLdbObject *)ldb_ctx_type->tp_alloc(ldb_ctx_type, 0);
90         if (ret == NULL) {
91                 PyErr_NoMemory();
92                 Py_XDECREF(ldb_ctx_type);
93                 return NULL;
94         }
95         ret->mem_ctx = talloc_new(NULL);
96         ret->ldb_ctx = talloc_reference(ret->mem_ctx, ldb_ctx);
97         Py_XDECREF(ldb_ctx_type);
98         return (PyObject *)ret;
99 }
100
101 NTSTATUS provision_bare(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
102                         struct provision_settings *settings, 
103                         struct provision_result *result)
104 {
105         const char *configfile;
106         PyObject *provision_mod = NULL, *provision_dict = NULL;
107         PyObject *provision_fn = NULL, *py_result = NULL;
108         PyObject *parameters = NULL, *py_lp_ctx = NULL, *py_domaindn = NULL;
109
110         struct ldb_context *samdb;
111         NTSTATUS status = NT_STATUS_OK;
112         
113         DEBUG(0,("Provision for Become-DC test using python\n"));
114
115         Py_Initialize();
116         py_update_path(); /* Put the samba path at the start of sys.path */
117
118         provision_mod = provision_module();
119
120         if (provision_mod == NULL) {
121                 PyErr_Print();
122                 DEBUG(0, ("Unable to import provision Python module.\n"));
123                 return NT_STATUS_UNSUCCESSFUL;
124         }
125
126         provision_dict = PyModule_GetDict(provision_mod);
127
128         if (provision_dict == NULL) {
129                 DEBUG(0, ("Unable to get dictionary for provision module\n"));
130                 return NT_STATUS_UNSUCCESSFUL;
131         }
132
133         provision_fn = PyDict_GetItemString(provision_dict, "provision_become_dc");
134         if (provision_fn == NULL) {
135                 PyErr_Print();
136                 DEBUG(0, ("Unable to get provision_become_dc function\n"));
137                 return NT_STATUS_UNSUCCESSFUL;
138         }
139         
140         DEBUG(0,("New Server in Site[%s]\n", 
141                  settings->site_name));
142
143         DEBUG(0,("DSA Instance [%s]\n"
144                 "\tinvocationId[%s]\n",
145                 settings->ntds_dn_str,
146                 settings->invocation_id == NULL?"None":GUID_string(mem_ctx, settings->invocation_id)));
147
148         DEBUG(0,("Paths under targetdir[%s]\n",
149                  settings->targetdir));
150         parameters = PyDict_New();
151
152         configfile = lpcfg_configfile(lp_ctx);
153         if (configfile != NULL) {
154                 if (!dict_insert(parameters, "smbconf",
155                                  PyStr_FromString(configfile))) {
156                         status = NT_STATUS_UNSUCCESSFUL;
157                         goto out;
158                 }
159         }
160
161         if (!dict_insert(parameters,
162                          "rootdn",
163                          PyStr_FromString(settings->root_dn_str))) {
164                 status = NT_STATUS_UNSUCCESSFUL;
165                 goto out;
166         }
167         if (settings->targetdir != NULL) {
168                 if (!dict_insert(parameters,
169                                  "targetdir",
170                                  PyStr_FromString(settings->targetdir))) {
171                         status = NT_STATUS_UNSUCCESSFUL;
172                         goto out;
173                 }
174         }
175         if (!dict_insert(parameters,
176                          "hostname",
177                          PyStr_FromString(settings->netbios_name))) {
178                 status = NT_STATUS_UNSUCCESSFUL;
179                 goto out;
180         }
181         if (!dict_insert(parameters,
182                          "domain",
183                          PyStr_FromString(settings->domain))) {
184                 status = NT_STATUS_UNSUCCESSFUL;
185                 goto out;
186         }
187         if (!dict_insert(parameters,
188                          "realm",
189                          PyStr_FromString(settings->realm))) {
190                 status = NT_STATUS_UNSUCCESSFUL;
191                 goto out;
192         }
193         if (settings->root_dn_str) {
194                 if (!dict_insert(parameters,
195                                  "rootdn",
196                                  PyStr_FromString(settings->root_dn_str))) {
197                         status = NT_STATUS_UNSUCCESSFUL;
198                         goto out;
199                 }
200         }
201
202         if (settings->domain_dn_str) {
203                 if (!dict_insert(parameters,
204                                  "domaindn",
205                                  PyStr_FromString(settings->domain_dn_str))) {
206                         status = NT_STATUS_UNSUCCESSFUL;
207                         goto out;
208                 }
209         }
210
211         if (settings->schema_dn_str) {
212                 if (!dict_insert(parameters,
213                                  "schemadn",
214                                  PyStr_FromString(settings->schema_dn_str))) {
215                         status = NT_STATUS_UNSUCCESSFUL;
216                         goto out;
217                 }
218         }
219         if (settings->config_dn_str) {
220                 if (!dict_insert(parameters,
221                                  "configdn",
222                                  PyStr_FromString(settings->config_dn_str))) {
223                         status = NT_STATUS_UNSUCCESSFUL;
224                         goto out;
225                 }
226         }
227         if (settings->server_dn_str) {
228                 if (!dict_insert(parameters,
229                                  "serverdn",
230                                  PyStr_FromString(settings->server_dn_str))) {
231                         status = NT_STATUS_UNSUCCESSFUL;
232                         goto out;
233                 }
234         }
235         if (settings->site_name) {
236                 if (!dict_insert(parameters,
237                                  "sitename",
238                                   PyStr_FromString(settings->site_name))) {
239                         status = NT_STATUS_UNSUCCESSFUL;
240                         goto out;
241                 }
242         }
243
244         if (!dict_insert(parameters,
245                          "machinepass",
246                          PyStr_FromString(settings->machine_password))){
247                 status = NT_STATUS_UNSUCCESSFUL;
248                 goto out;
249         }
250
251         if (!dict_insert(parameters,
252                          "debuglevel",
253                          PyInt_FromLong(DEBUGLEVEL))) {
254                 status = NT_STATUS_UNSUCCESSFUL;
255                 goto out;
256         }
257
258         if (!dict_insert(parameters,
259                          "use_ntvfs",
260                          PyInt_FromLong(settings->use_ntvfs))) {
261                 status = NT_STATUS_UNSUCCESSFUL;
262                 goto out;
263         }
264
265         py_result = PyEval_CallObjectWithKeywords(provision_fn, NULL, parameters);
266
267         if (py_result == NULL) {
268                 status = NT_STATUS_UNSUCCESSFUL;
269                 goto out;
270         }
271
272         py_domaindn = PyObject_GetAttrString(py_result, "domaindn");
273         result->domaindn = talloc_strdup(mem_ctx, PyStr_AsString(py_domaindn));
274
275         /* FIXME paths */
276         py_lp_ctx = PyObject_GetAttrString(py_result, "lp");
277         if (py_lp_ctx == NULL) {
278                 DEBUG(0, ("Missing 'lp' attribute"));
279                 status = NT_STATUS_UNSUCCESSFUL;
280                 goto out;
281         }
282         result->lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
283
284         samdb = pyldb_Ldb_AsLdbContext(PyObject_GetAttrString(py_result, "samdb"));
285         if (samdb == NULL) {
286                 DEBUG(0, ("Missing 'samdb' attribute"));
287                 status = NT_STATUS_UNSUCCESSFUL;
288                 goto out;
289         }
290         result->samdb = samdb;
291         status = NT_STATUS_OK;
292 out:
293         Py_CLEAR(parameters);
294         Py_CLEAR(provision_mod);
295         Py_CLEAR(provision_fn);
296         Py_CLEAR(provision_dict);
297         Py_CLEAR(py_result);
298         Py_CLEAR(py_lp_ctx);
299         Py_CLEAR(py_domaindn);
300         if (!NT_STATUS_IS_OK(status)) {
301                 PyErr_Print();
302                 PyErr_Clear();
303         }
304         return status;
305 }
306
307 static PyObject *py_dom_sid_FromSid(struct dom_sid *sid)
308 {
309         PyObject *mod_security = NULL, *dom_sid_Type = NULL, *result = NULL;
310
311         mod_security = PyImport_ImportModule("samba.dcerpc.security");
312         if (mod_security == NULL) {
313                 return NULL;
314         }
315
316         dom_sid_Type = PyObject_GetAttrString(mod_security, "dom_sid");
317         if (dom_sid_Type == NULL) {
318                 Py_DECREF(mod_security);
319                 return NULL;
320         }
321
322         result = pytalloc_reference((PyTypeObject *)dom_sid_Type, sid);
323         Py_DECREF(mod_security);
324         Py_DECREF(dom_sid_Type);
325         return result;
326 }
327
328 NTSTATUS provision_store_self_join(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
329                                    struct tevent_context *event_ctx,
330                                    struct provision_store_self_join_settings *settings,
331                                    const char **error_string)
332 {
333         int ret;
334         PyObject *provision_mod = NULL, *provision_dict = NULL;
335         PyObject *provision_fn = NULL, *py_result = NULL;
336         PyObject *parameters = NULL, *py_sid = NULL;
337         struct ldb_context *ldb = NULL;
338         TALLOC_CTX *tmp_mem = talloc_new(mem_ctx);
339
340         NTSTATUS status = NT_STATUS_OK;
341         *error_string = NULL;
342
343         if (!tmp_mem) {
344                 status = NT_STATUS_UNSUCCESSFUL;
345                 goto out;
346         }
347
348         /* Open the secrets database */
349         ldb = secrets_db_connect(tmp_mem, lp_ctx);
350         if (!ldb) {
351                 *error_string
352                         = talloc_asprintf(mem_ctx, 
353                                           "Could not open secrets database");
354                 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
355                 goto out;
356         }
357
358         ret = ldb_transaction_start(ldb);
359
360         if (ret != LDB_SUCCESS) {
361                 *error_string
362                         = talloc_asprintf(mem_ctx, 
363                                           "Could not start transaction on secrets database: %s", ldb_errstring(ldb));
364                 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
365                 goto out;
366         }
367
368         Py_Initialize();
369         py_update_path(); /* Put the samba path at the start of sys.path */
370         provision_mod = provision_module();
371
372         if (provision_mod == NULL) {
373                 *error_string
374                         = talloc_asprintf(mem_ctx, "Unable to import provision Python module.");
375                 status = NT_STATUS_UNSUCCESSFUL;
376                 goto out;
377         }
378
379         provision_dict = PyModule_GetDict(provision_mod);
380
381         if (provision_dict == NULL) {
382                 *error_string
383                         = talloc_asprintf(mem_ctx, "Unable to get dictionary for provision module");
384                 status = NT_STATUS_UNSUCCESSFUL;
385                 goto out;
386         }
387
388         provision_fn = PyDict_GetItemString(provision_dict, "secretsdb_self_join");
389         if (provision_fn == NULL) {
390                 *error_string
391                         = talloc_asprintf(mem_ctx, "Unable to get provision_become_dc function");
392                 status = NT_STATUS_UNSUCCESSFUL;
393                 goto out;
394         }
395
396         parameters = PyDict_New();
397
398         if(!dict_insert(parameters,
399                         "secretsdb",
400                         PyLdb_FromLdbContext(ldb))){
401                 status = NT_STATUS_UNSUCCESSFUL;
402                 goto out;
403         }
404         if (!dict_insert(parameters,
405                          "domain",
406                          PyStr_FromString(settings->domain_name))) {
407                 status = NT_STATUS_UNSUCCESSFUL;
408                 goto out;
409         }
410         if (settings->realm != NULL) {
411                 if (!dict_insert(parameters,
412                                  "realm",
413                                  PyStr_FromString(settings->realm))) {
414                         status = NT_STATUS_UNSUCCESSFUL;
415                         goto out;
416                 }
417         }
418         if (!dict_insert(parameters,
419                          "machinepass",
420                          PyStr_FromString(settings->machine_password))) {
421                 status = NT_STATUS_UNSUCCESSFUL;
422                 goto out;
423         }
424         if (!dict_insert(parameters,
425                          "netbiosname",
426                          PyStr_FromString(settings->netbios_name))) {
427                 status = NT_STATUS_UNSUCCESSFUL;
428                 goto out;
429         }
430
431         py_sid = py_dom_sid_FromSid(settings->domain_sid);
432         if (py_sid == NULL) {
433                 status = NT_STATUS_UNSUCCESSFUL;
434                 goto out;
435         }
436
437         if (!dict_insert(parameters,
438                          "domainsid",
439                          py_sid)) {
440                 status = NT_STATUS_UNSUCCESSFUL;
441                 goto out;
442         }
443
444         if (!dict_insert(parameters,
445                          "secure_channel_type",
446                          PyInt_FromLong(settings->secure_channel_type))) {
447                 status = NT_STATUS_UNSUCCESSFUL;
448                 goto out;
449         }
450
451         if (!dict_insert(parameters,
452                          "key_version_number",
453                          PyInt_FromLong(settings->key_version_number))) {
454                 status = NT_STATUS_UNSUCCESSFUL;
455                 goto out;
456         }
457
458         py_result = PyEval_CallObjectWithKeywords(provision_fn, NULL, parameters);
459
460         if (py_result == NULL) {
461                 ldb_transaction_cancel(ldb);
462                 status = NT_STATUS_UNSUCCESSFUL;
463                 goto out;
464         }
465
466         ret = ldb_transaction_commit(ldb);
467         if (ret != LDB_SUCCESS) {
468                 *error_string
469                         = talloc_asprintf(mem_ctx, 
470                                           "Could not commit transaction on secrets database: %s", ldb_errstring(ldb));
471                 status = NT_STATUS_INTERNAL_DB_ERROR;
472                 goto out;
473         }
474
475         status = NT_STATUS_OK;
476 out:
477         talloc_free(tmp_mem);
478         Py_CLEAR(parameters);
479         Py_CLEAR(provision_mod);
480         Py_CLEAR(provision_fn);
481         Py_CLEAR(provision_dict);
482         Py_CLEAR(py_result);
483         Py_CLEAR(py_sid);
484         if (!NT_STATUS_IS_OK(status)) {
485                 PyErr_Print();
486                 PyErr_Clear();
487         }
488         return status;
489 }
490
491
492 struct ldb_context *provision_get_schema(TALLOC_CTX *mem_ctx,
493                                          struct loadparm_context *lp_ctx,
494                                          const char *schema_dn,
495                                          DATA_BLOB *override_prefixmap)
496 {
497         PyObject *schema_mod, *schema_dict, *schema_fn, *py_result, *parameters;
498         PyObject *py_ldb = NULL;
499         struct ldb_context *ldb_result = NULL;
500         Py_Initialize();
501         py_update_path(); /* Put the samba path at the start of sys.path */
502
503         schema_mod = schema_module();
504
505         if (schema_mod == NULL) {
506                 PyErr_Print();
507                 DEBUG(0, ("Unable to import schema Python module.\n"));
508                 return NULL;
509         }
510
511         schema_dict = PyModule_GetDict(schema_mod);
512
513         if (schema_dict == NULL) {
514                 DEBUG(0, ("Unable to get dictionary for schema module\n"));
515                 return NULL;
516         }
517
518         schema_fn = PyDict_GetItemString(schema_dict, "ldb_with_schema");
519         if (schema_fn == NULL) {
520                 PyErr_Print();
521                 DEBUG(0, ("Unable to get schema_get_ldb function\n"));
522                 return NULL;
523         }
524         
525         parameters = PyDict_New();
526
527         if (schema_dn) {
528                 if (!dict_insert(parameters,
529                                  "schemadn",
530                                  PyStr_FromString(schema_dn))) {
531                         return NULL;
532                 }
533         }
534
535         if (override_prefixmap) {
536                 if (!dict_insert(parameters,
537                                  "override_prefixmap",
538                                  PyBytes_FromStringAndSize(
539                                         (const char *)override_prefixmap->data,
540                                         override_prefixmap->length))) {
541                         return NULL;
542                 }
543         }
544
545         py_result = PyEval_CallObjectWithKeywords(schema_fn, NULL, parameters);
546
547         Py_DECREF(parameters);
548
549         if (py_result == NULL) {
550                 PyErr_Print();
551                 PyErr_Clear();
552                 return NULL;
553         }
554
555         py_ldb = PyObject_GetAttrString(py_result, "ldb");
556         Py_DECREF(py_result);
557         ldb_result = pyldb_Ldb_AsLdbContext(py_ldb);
558         /*
559          * #TODO #FIXME There is a leak here !!!
560          * Looks like ldb is a new object returned from schema_fn
561          * We extract the ldb_ctx from that which rightly
562          * will be destoryed when py_ldb is decremented.
563          * Presently the ldb_context is returned (but the owning
564          * object is leaked)
565         */
566         return ldb_result;
567 }