s3:utils: let smbstatus report anonymous signing/encryption explicitly
[samba.git] / source4 / dns_server / pydns.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Python DNS server wrapper
5
6    Copyright (C) 2015 Andrew Bartlett
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 "lib/replace/system/python.h"
23 #include "python/py3compat.h"
24 #include "includes.h"
25 #include "python/modules.h"
26 #include <pyldb.h>
27 #include <pytalloc.h>
28 #include "dns_server/dnsserver_common.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "dsdb/common/util.h"
31 #include "librpc/gen_ndr/ndr_dnsp.h"
32 #include "librpc/rpc/pyrpc_util.h"
33
34 static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
35                                                   uint16_t num_records)
36 {
37         PyObject *py_dns_list;
38         int i;
39         py_dns_list = PyList_New(num_records);
40         if (py_dns_list == NULL) {
41                 return NULL;
42         }
43         for (i = 0; i < num_records; i++) {
44                 PyObject *py_dns_record;
45                 py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
46                 PyList_SetItem(py_dns_list, i, py_dns_record);
47         }
48         return py_dns_list;
49 }
50
51
52 static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
53                                              TALLOC_CTX *mem_ctx,
54                                              struct dnsp_DnssrvRpcRecord **records,
55                                              uint16_t *num_records)
56 {
57         int i;
58         struct dnsp_DnssrvRpcRecord *recs;
59         PY_CHECK_TYPE(&PyList_Type, value, return -1;);
60         recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
61                             PyList_GET_SIZE(value));
62         if (recs == NULL) {
63                 PyErr_NoMemory();
64                 return -1;
65         }
66         for (i = 0; i < PyList_GET_SIZE(value); i++) {
67                 bool type_correct;
68                 PyObject *item = PyList_GET_ITEM(value, i);
69                 type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
70                 if (type_correct == false) {
71                         return -1;
72                 }
73                 if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
74                         PyErr_NoMemory();
75                         return -1;
76                 }
77                 recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
78         }
79         *records = recs;
80         *num_records = PyList_GET_SIZE(value);
81         return 0;
82 }
83
84 static PyObject *py_dsdb_dns_lookup(PyObject *self,
85                                     PyObject *args, PyObject *kwargs)
86 {
87         struct ldb_context *samdb;
88         PyObject *py_ldb, *ret, *pydn;
89         PyObject *py_dns_partition = NULL;
90         PyObject *result = NULL;
91         char *dns_name;
92         TALLOC_CTX *frame;
93         NTSTATUS status;
94         WERROR werr;
95         struct dns_server_zone *zones_list;
96         struct ldb_dn *dn, *dns_partition = NULL;
97         struct dnsp_DnssrvRpcRecord *records;
98         uint16_t num_records;
99         const char * const kwnames[] = { "ldb", "dns_name",
100                                          "dns_partition", NULL };
101
102         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
103                                          discard_const_p(char *, kwnames),
104                                          &py_ldb, &dns_name,
105                                          &py_dns_partition)) {
106                 return NULL;
107         }
108         PyErr_LDB_OR_RAISE(py_ldb, samdb);
109
110         if (py_dns_partition) {
111                 PyErr_LDB_DN_OR_RAISE(py_dns_partition,
112                                       dns_partition);
113         }
114
115         frame = talloc_stackframe();
116
117         status = dns_common_zones(samdb, frame, dns_partition,
118                                   &zones_list);
119         if (!NT_STATUS_IS_OK(status)) {
120                 talloc_free(frame);
121                 PyErr_SetNTSTATUS(status);
122                 return NULL;
123         }
124
125         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
126         if (!W_ERROR_IS_OK(werr)) {
127                 talloc_free(frame);
128                 PyErr_SetWERROR(werr);
129                 return NULL;
130         }
131
132         werr = dns_common_lookup(samdb,
133                                  frame,
134                                  dn,
135                                  &records,
136                                  &num_records,
137                                  NULL);
138         if (!W_ERROR_IS_OK(werr)) {
139                 talloc_free(frame);
140                 PyErr_SetWERROR(werr);
141                 return NULL;
142         }
143
144         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
145         pydn = pyldb_Dn_FromDn(dn, (PyLdbObject *)py_ldb);
146         talloc_free(frame);
147         result = Py_BuildValue("(OO)", pydn, ret);
148         Py_CLEAR(ret);
149         Py_CLEAR(pydn);
150         return result;
151 }
152
153 static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
154 {
155         struct ldb_context *samdb;
156         PyObject *py_dns_el, *ret;
157         PyObject *py_ldb = NULL;
158         TALLOC_CTX *frame;
159         WERROR werr;
160         struct ldb_message_element *dns_el;
161         struct dnsp_DnssrvRpcRecord *records;
162         uint16_t num_records;
163
164         if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
165                 return NULL;
166         }
167
168         PyErr_LDB_OR_RAISE(py_ldb, samdb);
169
170         if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
171                 PyErr_SetString(PyExc_TypeError,
172                                 "ldb MessageElement object required");
173                 return NULL;
174         }
175         dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
176
177         frame = talloc_stackframe();
178
179         werr = dns_common_extract(samdb, dns_el,
180                                   frame,
181                                   &records,
182                                   &num_records);
183         if (!W_ERROR_IS_OK(werr)) {
184                 talloc_free(frame);
185                 PyErr_SetWERROR(werr);
186                 return NULL;
187         }
188
189         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
190         talloc_free(frame);
191         return ret;
192 }
193
194 static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
195 {
196         struct ldb_context *samdb;
197         PyObject *py_ldb, *py_dns_records;
198         char *dns_name;
199         TALLOC_CTX *frame;
200         NTSTATUS status;
201         WERROR werr;
202         int ret;
203         struct dns_server_zone *zones_list;
204         struct ldb_dn *dn;
205         struct dnsp_DnssrvRpcRecord *records;
206         uint16_t num_records;
207
208         /*
209          * TODO: This is a shocking abuse, but matches what the
210          * internal DNS server does, it should be pushed into
211          * dns_common_replace()
212          */
213         static const int serial = 110;
214
215         if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
216                 return NULL;
217         }
218         PyErr_LDB_OR_RAISE(py_ldb, samdb);
219
220         frame = talloc_stackframe();
221
222         status = dns_common_zones(samdb, frame, NULL, &zones_list);
223         if (!NT_STATUS_IS_OK(status)) {
224                 PyErr_SetNTSTATUS(status);
225                 talloc_free(frame);
226                 return NULL;
227         }
228
229         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
230         if (!W_ERROR_IS_OK(werr)) {
231                 PyErr_SetWERROR(werr);
232                 talloc_free(frame);
233                 return NULL;
234         }
235
236         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
237                                                 frame,
238                                                 &records, &num_records);
239         if (ret != 0) {
240                 talloc_free(frame);
241                 return NULL;
242         }
243
244         werr = dns_common_replace(samdb,
245                                   frame,
246                                   dn,
247                                   false, /* Not adding a record */
248                                   serial,
249                                   records,
250                                   num_records);
251         if (!W_ERROR_IS_OK(werr)) {
252                 PyErr_SetWERROR(werr);
253                 talloc_free(frame);
254                 return NULL;
255         }
256
257         talloc_free(frame);
258         Py_RETURN_NONE;
259 }
260
261 static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
262 {
263         struct ldb_context *samdb;
264         PyObject *py_ldb, *py_dn, *py_dns_records;
265         TALLOC_CTX *frame;
266         WERROR werr;
267         int ret;
268         struct ldb_dn *dn;
269         struct dnsp_DnssrvRpcRecord *records;
270         uint16_t num_records;
271
272         /*
273          * TODO: This is a shocking abuse, but matches what the
274          * internal DNS server does, it should be pushed into
275          * dns_common_replace()
276          */
277         static const int serial = 110;
278
279         if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
280                 return NULL;
281         }
282         PyErr_LDB_OR_RAISE(py_ldb, samdb);
283
284         PyErr_LDB_DN_OR_RAISE(py_dn, dn);
285
286         frame = talloc_stackframe();
287
288         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
289                                                 frame,
290                                                 &records, &num_records);
291         if (ret != 0) {
292                 talloc_free(frame);
293                 return NULL;
294         }
295
296         werr = dns_common_replace(samdb,
297                                   frame,
298                                   dn,
299                                   false, /* Not adding a node */
300                                   serial,
301                                   records,
302                                   num_records);
303         if (!W_ERROR_IS_OK(werr)) {
304                 PyErr_SetWERROR(werr);
305                 talloc_free(frame);
306                 return NULL;
307         }
308
309         talloc_free(frame);
310
311         Py_RETURN_NONE;
312 }
313
314
315 static PyObject *py_dsdb_dns_records_match(PyObject *self, PyObject *args)
316 {
317         PyObject *py_recs[2];
318         struct dnsp_DnssrvRpcRecord *rec1;
319         struct dnsp_DnssrvRpcRecord *rec2;
320         size_t i;
321         bool type_correct;
322         bool match;
323
324         if (!PyArg_ParseTuple(args, "OO", &py_recs[0], &py_recs[1])) {
325                 return NULL;
326         }
327
328         for (i = 0; i < 2; i++) {
329                 type_correct = py_check_dcerpc_type(py_recs[i],
330                                                     "samba.dcerpc.dnsp",
331                                                     "DnssrvRpcRecord");
332                 if (! type_correct) {
333                         PyErr_SetString(PyExc_ValueError,
334                                         "DnssrvRpcRecord expected");
335                         return NULL;
336                 }
337         }
338
339         rec1 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[0]);
340         rec2 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[1]);
341
342         match = dns_record_match(rec1, rec2);
343         return PyBool_FromLong(match);
344 }
345
346
347 static PyObject *py_dsdb_dns_unix_to_dns_timestamp(PyObject *self, PyObject *args)
348 {
349         uint32_t timestamp;
350         time_t t;
351         long long lt;
352
353         if (!PyArg_ParseTuple(args, "L", &lt)) {
354                 return NULL;
355         }
356
357         t = lt;
358         if (t != lt) {
359                 /* time_t is presumably 32 bit here */
360                 PyErr_SetString(PyExc_ValueError, "Time out of range");
361                 return NULL;
362         }
363         timestamp = unix_to_dns_timestamp(t);
364         return Py_BuildValue("k", (unsigned long) timestamp);
365 }
366
367 static PyObject *py_dsdb_dns_timestamp_to_nt_time(PyObject *self, PyObject *args)
368 {
369         unsigned long long timestamp;
370         NTSTATUS status;
371         NTTIME nt;
372         if (!PyArg_ParseTuple(args, "K", &timestamp)) {
373                 return NULL;
374         }
375
376         if (timestamp > UINT32_MAX) {
377                 PyErr_SetString(PyExc_ValueError, "Time out of range");
378                 return NULL;
379         }
380         status = dns_timestamp_to_nt_time(&nt, (uint32_t)timestamp);
381         if (!NT_STATUS_IS_OK(status)) {
382                 PyErr_SetString(PyExc_ValueError, "Time out of range");
383                 return NULL;
384         }
385         return Py_BuildValue("L", (long long) nt);
386 }
387
388
389 static PyMethodDef py_dsdb_dns_methods[] = {
390
391         { "lookup", PY_DISCARD_FUNC_SIG(PyCFunction, py_dsdb_dns_lookup),
392                 METH_VARARGS|METH_KEYWORDS,
393                 "Get the DNS database entries for a DNS name"},
394         { "replace", (PyCFunction)py_dsdb_dns_replace,
395                 METH_VARARGS, "Replace the DNS database entries for a DNS name"},
396         { "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn,
397                 METH_VARARGS, "Replace the DNS database entries for a LDB DN"},
398         { "records_match", (PyCFunction)py_dsdb_dns_records_match,
399           METH_VARARGS|METH_KEYWORDS,
400           "Decide whether two records match, according to dns update rules"},
401         { "extract", (PyCFunction)py_dsdb_dns_extract,
402                 METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"},
403         { "unix_to_dns_timestamp", (PyCFunction)py_dsdb_dns_unix_to_dns_timestamp,
404           METH_VARARGS,
405           "Convert a time.time() value to a dns timestamp (hours since 1601)"},
406         { "dns_timestamp_to_nt_time", (PyCFunction)py_dsdb_dns_timestamp_to_nt_time,
407           METH_VARARGS,
408           "Convert a dns timestamp to an NTTIME value"},
409         {0}
410 };
411
412 static struct PyModuleDef moduledef = {
413     PyModuleDef_HEAD_INIT,
414     .m_name = "dsdb_dns",
415     .m_doc = "Python bindings for the DNS objects in the directory service databases.",
416     .m_size = -1,
417     .m_methods = py_dsdb_dns_methods,
418 };
419
420 MODULE_INIT_FUNC(dsdb_dns)
421 {
422         PyObject *m;
423
424         m = PyModule_Create(&moduledef);
425
426         if (m == NULL)
427                 return NULL;
428
429         return m;
430 }