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