README: Add languages to code blocks for highlighting
[vlendec/samba-autobuild/.git] / python / pyglue.c
1 /*
2    Unix SMB/CIFS implementation.
3    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
4    Copyright (C) Matthias Dieter Wallnöfer          2009
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "lib/replace/system/python.h"
21 #include "python/py3compat.h"
22 #include "includes.h"
23 #include "python/modules.h"
24 #include "version.h"
25 #include "param/pyparam.h"
26 #include "lib/socket/netif.h"
27 #include "lib/util/debug.h"
28 #include "librpc/ndr/ndr_private.h"
29 #include "lib/cmdline/cmdline.h"
30 #include "lib/crypto/gkdi.h"
31
32 static PyObject *PyExc_NTSTATUSError;
33 static PyObject *PyExc_WERRORError;
34 static PyObject *PyExc_HRESULTError;
35 static PyObject *PyExc_DsExtendedError;
36
37 static PyObject *py_generate_random_str(PyObject *self, PyObject *args)
38 {
39         Py_ssize_t len;
40         PyObject *ret;
41         char *retstr;
42
43         if (!PyArg_ParseTuple(args, "n", &len)) {
44                 return NULL;
45         }
46         if (len < 0) {
47                 PyErr_Format(PyExc_ValueError,
48                              "random string length should be positive, not %zd",
49                              len);
50                 return NULL;
51         }
52         retstr = generate_random_str(NULL, len);
53         if (retstr == NULL) {
54                 return PyErr_NoMemory();
55         }
56         ret = PyUnicode_FromStringAndSize(retstr, len);
57         talloc_free(retstr);
58         return ret;
59 }
60
61 static PyObject *py_generate_random_password(PyObject *self, PyObject *args)
62 {
63         Py_ssize_t min, max;
64         PyObject *ret;
65         char *retstr;
66
67         if (!PyArg_ParseTuple(args, "nn", &min, &max)) {
68                 return NULL;
69         }
70         if (max < 0 || min < 0) {
71                 /*
72                  * The real range checks happens in generate_random_password().
73                  * Here just filter out any negative numbers.
74                  */
75                 PyErr_Format(PyExc_ValueError,
76                              "invalid range: %zd - %zd",
77                              min, max);
78                 return NULL;
79         }
80
81         retstr = generate_random_password(NULL, min, max);
82         if (retstr == NULL) {
83                 if (errno == EINVAL) {
84                         return PyErr_Format(PyExc_ValueError,
85                                             "invalid range: %zd - %zd",
86                                             min, max);
87                 }
88                 return PyErr_NoMemory();
89         }
90         ret = PyUnicode_FromString(retstr);
91         talloc_free(retstr);
92         return ret;
93 }
94
95 static PyObject *py_generate_random_machine_password(PyObject *self, PyObject *args)
96 {
97         Py_ssize_t min, max;
98         PyObject *ret;
99         char *retstr;
100
101         if (!PyArg_ParseTuple(args, "nn", &min, &max)) {
102                 return NULL;
103         }
104         if (max < 0 || min < 0) {
105                 /*
106                  * The real range checks happens in
107                  * generate_random_machine_password().
108                  * Here we just filter out any negative numbers.
109                  */
110                 PyErr_Format(PyExc_ValueError,
111                              "invalid range: %zd - %zd",
112                              min, max);
113                 return NULL;
114         }
115
116         retstr = generate_random_machine_password(NULL, min, max);
117         if (retstr == NULL) {
118                 if (errno == EINVAL) {
119                         return PyErr_Format(PyExc_ValueError,
120                                             "invalid range: %zd - %zd",
121                                             min, max);
122                 }
123                 return PyErr_NoMemory();
124         }
125         ret = PyUnicode_FromString(retstr);
126         talloc_free(retstr);
127         return ret;
128 }
129
130 static PyObject *py_check_password_quality(PyObject *self, PyObject *args)
131 {
132         char *pass;
133
134         if (!PyArg_ParseTuple(args, "s", &pass)) {
135                 return NULL;
136         }
137
138         return PyBool_FromLong(check_password_quality(pass));
139 }
140
141 static PyObject *py_generate_random_bytes(PyObject *self, PyObject *args)
142 {
143         Py_ssize_t len;
144         PyObject *ret;
145         uint8_t *bytes = NULL;
146
147         if (!PyArg_ParseTuple(args, "n", &len)) {
148                 return NULL;
149         }
150         if (len < 0) {
151                 PyErr_Format(PyExc_ValueError,
152                              "random bytes length should be positive, not %zd",
153                              len);
154                 return NULL;
155         }
156         bytes = talloc_zero_size(NULL, len);
157         if (bytes == NULL) {
158                 PyErr_NoMemory();
159                 return NULL;
160         }
161         generate_random_buffer(bytes, len);
162         ret = PyBytes_FromStringAndSize((const char *)bytes, len);
163         talloc_free(bytes);
164         return ret;
165 }
166
167 static PyObject *py_unix2nttime(PyObject *self, PyObject *args)
168 {
169         time_t t;
170         unsigned int _t;
171         NTTIME nt;
172
173         if (!PyArg_ParseTuple(args, "I", &_t)) {
174                 return NULL;
175         }
176         t = _t;
177
178         unix_to_nt_time(&nt, t);
179
180         return PyLong_FromLongLong((uint64_t)nt);
181 }
182
183 static PyObject *py_nttime2unix(PyObject *self, PyObject *args)
184 {
185         time_t t;
186         NTTIME nt;
187         if (!PyArg_ParseTuple(args, "K", &nt))
188                 return NULL;
189
190         t = nt_time_to_unix(nt);
191
192         return PyLong_FromLong((uint64_t)t);
193 }
194
195 static PyObject *py_float2nttime(PyObject *self, PyObject *args)
196 {
197         double ft = 0;
198         double ft_sec = 0;
199         double ft_nsec = 0;
200         struct timespec ts;
201         NTTIME nt = 0;
202
203         if (!PyArg_ParseTuple(args, "d", &ft)) {
204                 return NULL;
205         }
206
207         ft_sec = (double)(int)ft;
208         ft_nsec = (ft - ft_sec) * 1.0e+9;
209
210         ts.tv_sec = (int)ft_sec;
211         ts.tv_nsec = (int)ft_nsec;
212
213         nt = full_timespec_to_nt_time(&ts);
214
215         return PyLong_FromLongLong((uint64_t)nt);
216 }
217
218 static PyObject *py_nttime2float(PyObject *self, PyObject *args)
219 {
220         double ft = 0;
221         struct timespec ts;
222         const struct timespec ts_zero = { .tv_sec = 0, };
223         NTTIME nt = 0;
224
225         if (!PyArg_ParseTuple(args, "K", &nt)) {
226                 return NULL;
227         }
228
229         ts = nt_time_to_full_timespec(nt);
230         if (is_omit_timespec(&ts)) {
231                 return PyFloat_FromDouble(1.0);
232         }
233         ft = timespec_elapsed2(&ts_zero, &ts);
234
235         return PyFloat_FromDouble(ft);
236 }
237
238 static PyObject *py_nttime2string(PyObject *self, PyObject *args)
239 {
240         PyObject *ret;
241         NTTIME nt;
242         TALLOC_CTX *tmp_ctx;
243         const char *string;
244         if (!PyArg_ParseTuple(args, "K", &nt))
245                 return NULL;
246
247         tmp_ctx = talloc_new(NULL);
248         if (tmp_ctx == NULL) {
249                 PyErr_NoMemory();
250                 return NULL;
251         }
252
253         string = nt_time_string(tmp_ctx, nt);
254         ret =  PyUnicode_FromString(string);
255
256         talloc_free(tmp_ctx);
257
258         return ret;
259 }
260
261 static PyObject *py_set_debug_level(PyObject *self, PyObject *args)
262 {
263         unsigned level;
264         if (!PyArg_ParseTuple(args, "I", &level))
265                 return NULL;
266         debuglevel_set(level);
267         Py_RETURN_NONE;
268 }
269
270 static PyObject *py_get_debug_level(PyObject *self,
271                 PyObject *Py_UNUSED(ignored))
272 {
273         return PyLong_FromLong(debuglevel_get());
274 }
275
276 static PyObject *py_fault_setup(PyObject *self,
277                 PyObject *Py_UNUSED(ignored))
278 {
279         static bool done;
280         if (!done) {
281                 fault_setup();
282                 done = true;
283         }
284         Py_RETURN_NONE;
285 }
286
287 static PyObject *py_is_ntvfs_fileserver_built(PyObject *self,
288                 PyObject *Py_UNUSED(ignored))
289 {
290 #ifdef WITH_NTVFS_FILESERVER
291         Py_RETURN_TRUE;
292 #else
293         Py_RETURN_FALSE;
294 #endif
295 }
296
297 static PyObject *py_is_heimdal_built(PyObject *self,
298                 PyObject *Py_UNUSED(ignored))
299 {
300 #ifdef SAMBA4_USES_HEIMDAL
301         Py_RETURN_TRUE;
302 #else
303         Py_RETURN_FALSE;
304 #endif
305 }
306
307 static PyObject *py_is_ad_dc_built(PyObject *self,
308                 PyObject *Py_UNUSED(ignored))
309 {
310 #ifdef AD_DC_BUILD_IS_ENABLED
311         Py_RETURN_TRUE;
312 #else
313         Py_RETURN_FALSE;
314 #endif
315 }
316
317 static PyObject *py_is_selftest_enabled(PyObject *self,
318                 PyObject *Py_UNUSED(ignored))
319 {
320 #ifdef ENABLE_SELFTEST
321         Py_RETURN_TRUE;
322 #else
323         Py_RETURN_FALSE;
324 #endif
325 }
326
327 static PyObject *py_ndr_token_max_list_size(PyObject *self,
328                 PyObject *Py_UNUSED(ignored))
329 {
330         return PyLong_FromLong(ndr_token_max_list_size());
331 }
332
333 /*
334   return the list of interface IPs we have configured
335   takes an loadparm context, returns a list of IPs in string form
336
337   Does not return addresses on 127.0.0.0/8
338  */
339 static PyObject *py_interface_ips(PyObject *self, PyObject *args)
340 {
341         PyObject *pylist;
342         int count;
343         TALLOC_CTX *tmp_ctx;
344         PyObject *py_lp_ctx;
345         struct loadparm_context *lp_ctx;
346         struct interface *ifaces;
347         int i, ifcount;
348         int all_interfaces = 1;
349
350         if (!PyArg_ParseTuple(args, "O|i", &py_lp_ctx, &all_interfaces))
351                 return NULL;
352
353         tmp_ctx = talloc_new(NULL);
354         if (tmp_ctx == NULL) {
355                 PyErr_NoMemory();
356                 return NULL;
357         }
358
359         lp_ctx = lpcfg_from_py_object(tmp_ctx, py_lp_ctx);
360         if (lp_ctx == NULL) {
361                 talloc_free(tmp_ctx);
362                 return PyErr_NoMemory();
363         }
364
365         load_interface_list(tmp_ctx, lp_ctx, &ifaces);
366
367         count = iface_list_count(ifaces);
368
369         /* first count how many are not loopback addresses */
370         for (ifcount = i = 0; i<count; i++) {
371                 const char *ip = iface_list_n_ip(ifaces, i);
372
373                 if (all_interfaces) {
374                         ifcount++;
375                         continue;
376                 }
377
378                 if (iface_list_same_net(ip, "127.0.0.1", "255.0.0.0")) {
379                         continue;
380                 }
381
382                 if (iface_list_same_net(ip, "169.254.0.0", "255.255.0.0")) {
383                         continue;
384                 }
385
386                 if (iface_list_same_net(ip, "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")) {
387                         continue;
388                 }
389
390                 if (iface_list_same_net(ip, "fe80::", "ffff:ffff:ffff:ffff::")) {
391                         continue;
392                 }
393
394                 ifcount++;
395         }
396
397         pylist = PyList_New(ifcount);
398         for (ifcount = i = 0; i<count; i++) {
399                 const char *ip = iface_list_n_ip(ifaces, i);
400
401                 if (all_interfaces) {
402                         PyList_SetItem(pylist, ifcount, PyUnicode_FromString(ip));
403                         ifcount++;
404                         continue;
405                 }
406
407                 if (iface_list_same_net(ip, "127.0.0.1", "255.0.0.0")) {
408                         continue;
409                 }
410
411                 if (iface_list_same_net(ip, "169.254.0.0", "255.255.0.0")) {
412                         continue;
413                 }
414
415                 if (iface_list_same_net(ip, "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")) {
416                         continue;
417                 }
418
419                 if (iface_list_same_net(ip, "fe80::", "ffff:ffff:ffff:ffff::")) {
420                         continue;
421                 }
422
423                 PyList_SetItem(pylist, ifcount, PyUnicode_FromString(ip));
424                 ifcount++;
425         }
426         talloc_free(tmp_ctx);
427         return pylist;
428 }
429
430 static PyObject *py_strcasecmp_m(PyObject *self, PyObject *args)
431 {
432         char *s1 = NULL;
433         char *s2 = NULL;
434         long cmp_result = 0;
435         if (!PyArg_ParseTuple(args, PYARG_STR_UNI
436                               PYARG_STR_UNI,
437                               "utf8", &s1, "utf8", &s2)) {
438                 return NULL;
439         }
440
441         cmp_result = strcasecmp_m(s1, s2);
442         PyMem_Free(s1);
443         PyMem_Free(s2);
444         return PyLong_FromLong(cmp_result);
445 }
446
447 static PyObject *py_strstr_m(PyObject *self, PyObject *args)
448 {
449         char *s1 = NULL;
450         char *s2 = NULL;
451         char *strstr_ret = NULL;
452         PyObject *result = NULL;
453         if (!PyArg_ParseTuple(args, PYARG_STR_UNI
454                               PYARG_STR_UNI,
455                               "utf8", &s1, "utf8", &s2))
456                 return NULL;
457
458         strstr_ret = strstr_m(s1, s2);
459         if (!strstr_ret) {
460                 PyMem_Free(s1);
461                 PyMem_Free(s2);
462                 Py_RETURN_NONE;
463         }
464         result = PyUnicode_FromString(strstr_ret);
465         PyMem_Free(s1);
466         PyMem_Free(s2);
467         return result;
468 }
469
470 static PyObject *py_get_burnt_commandline(PyObject *self, PyObject *args)
471 {
472         PyObject *cmdline_as_list, *ret;
473         char *burnt_cmdline = NULL;
474         Py_ssize_t i, argc;
475         char **argv = NULL;
476         TALLOC_CTX *frame = talloc_stackframe();
477         bool burnt;
478
479         if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &cmdline_as_list))
480         {
481                 TALLOC_FREE(frame);
482                 return NULL;
483         }
484
485         argc = PyList_GET_SIZE(cmdline_as_list);
486
487         if (argc == 0) {
488                 TALLOC_FREE(frame);
489                 Py_RETURN_NONE;
490         }
491
492         argv = PyList_AsStringList(frame, cmdline_as_list, "sys.argv");
493         if (argv == NULL) {
494                 TALLOC_FREE(frame);
495                 return NULL;
496         }
497
498         burnt = samba_cmdline_burn(argc, argv);
499         if (!burnt) {
500                 TALLOC_FREE(frame);
501                 Py_RETURN_NONE;
502         }
503
504         for (i = 0; i < argc; i++) {
505                 if (i == 0) {
506                         burnt_cmdline = talloc_strdup(frame,
507                                                       argv[i]);
508                 } else {
509                         burnt_cmdline
510                                 = talloc_asprintf_append(burnt_cmdline,
511                                                          " %s",
512                                                          argv[i]);
513                 }
514                 if (burnt_cmdline == NULL) {
515                         PyErr_NoMemory();
516                         TALLOC_FREE(frame);
517                         return NULL;
518                 }
519         }
520
521         ret = PyUnicode_FromString(burnt_cmdline);
522         TALLOC_FREE(frame);
523
524         return ret;
525 }
526
527 static PyMethodDef py_misc_methods[] = {
528         { "generate_random_str", (PyCFunction)py_generate_random_str, METH_VARARGS,
529                 "generate_random_str(len) -> string\n"
530                 "Generate random string with specified length." },
531         { "generate_random_password", (PyCFunction)py_generate_random_password,
532                 METH_VARARGS, "generate_random_password(min, max) -> string\n"
533                 "Generate random password (based on printable ascii characters) "
534                 "with a length >= min and <= max." },
535         { "generate_random_machine_password", (PyCFunction)py_generate_random_machine_password,
536                 METH_VARARGS, "generate_random_machine_password(min, max) -> string\n"
537                 "Generate random password "
538                 "(based on random utf16 characters converted to utf8 or "
539                 "random ascii characters if 'unix charset' is not 'utf8') "
540                 "with a length >= min (at least 14) and <= max (at most 255)." },
541         { "check_password_quality", (PyCFunction)py_check_password_quality,
542                 METH_VARARGS, "check_password_quality(pass) -> bool\n"
543                 "Check password quality against Samba's check_password_quality, "
544                 "the implementation of Microsoft's rules: "
545                 "http://msdn.microsoft.com/en-us/subscriptions/cc786468%28v=ws.10%29.aspx"
546         },
547         { "unix2nttime", (PyCFunction)py_unix2nttime, METH_VARARGS,
548                 "unix2nttime(timestamp) -> nttime" },
549         { "nttime2unix", (PyCFunction)py_nttime2unix, METH_VARARGS,
550                 "nttime2unix(nttime) -> timestamp" },
551         { "float2nttime", (PyCFunction)py_float2nttime, METH_VARARGS,
552                 "pytime2nttime(floattimestamp) -> nttime" },
553         { "nttime2float", (PyCFunction)py_nttime2float, METH_VARARGS,
554                 "nttime2pytime(nttime) -> floattimestamp" },
555         { "nttime2string", (PyCFunction)py_nttime2string, METH_VARARGS,
556                 "nttime2string(nttime) -> string" },
557         { "set_debug_level", (PyCFunction)py_set_debug_level, METH_VARARGS,
558                 "set debug level" },
559         { "get_debug_level", (PyCFunction)py_get_debug_level, METH_NOARGS,
560                 "get debug level" },
561         { "fault_setup", (PyCFunction)py_fault_setup, METH_NOARGS,
562                 "setup the default samba panic handler" },
563         { "interface_ips", (PyCFunction)py_interface_ips, METH_VARARGS,
564                 "interface_ips(lp_ctx[, all_interfaces) -> list_of_ifaces\n"
565                 "\n"
566                 "get interface IP address list"},
567         { "strcasecmp_m", (PyCFunction)py_strcasecmp_m, METH_VARARGS,
568                 "(for testing) compare two strings using Samba's strcasecmp_m()"},
569         { "strstr_m", (PyCFunction)py_strstr_m, METH_VARARGS,
570                 "(for testing) find one string in another with Samba's strstr_m()"},
571         { "is_ntvfs_fileserver_built", (PyCFunction)py_is_ntvfs_fileserver_built, METH_NOARGS,
572                 "is the NTVFS file server built in this installation?" },
573         { "is_heimdal_built", (PyCFunction)py_is_heimdal_built, METH_NOARGS,
574                 "is Samba built with Heimdal Kerberos?" },
575         { "generate_random_bytes",
576                 (PyCFunction)py_generate_random_bytes,
577                 METH_VARARGS,
578                 "generate_random_bytes(len) -> bytes\n"
579                 "Generate random bytes with specified length." },
580         { "is_ad_dc_built", (PyCFunction)py_is_ad_dc_built, METH_NOARGS,
581                 "is Samba built with AD DC?" },
582         { "is_selftest_enabled", (PyCFunction)py_is_selftest_enabled,
583                 METH_NOARGS, "is Samba built with selftest enabled?" },
584         { "ndr_token_max_list_size", (PyCFunction)py_ndr_token_max_list_size,
585                 METH_NOARGS, "How many NDR internal tokens is too many for this build?" },
586         { "get_burnt_commandline", (PyCFunction)py_get_burnt_commandline,
587                 METH_VARARGS, "Return a redacted commandline to feed to setproctitle (None if no redaction required)" },
588         {0}
589 };
590
591 static struct PyModuleDef moduledef = {
592     PyModuleDef_HEAD_INIT,
593     .m_name = "_glue",
594     .m_doc = "Python bindings for miscellaneous Samba functions.",
595     .m_size = -1,
596     .m_methods = py_misc_methods,
597 };
598
599 MODULE_INIT_FUNC(_glue)
600 {
601         PyObject *m;
602         PyObject *py_obj = NULL;
603         int ret;
604
605         debug_setup_talloc_log();
606
607         m = PyModule_Create(&moduledef);
608         if (m == NULL)
609                 return NULL;
610
611         PyModule_AddObject(m, "version",
612                                            PyUnicode_FromString(SAMBA_VERSION_STRING));
613         PyExc_NTSTATUSError = PyErr_NewException("samba.NTSTATUSError", PyExc_RuntimeError, NULL);
614         if (PyExc_NTSTATUSError != NULL) {
615                 Py_INCREF(PyExc_NTSTATUSError);
616                 PyModule_AddObject(m, "NTSTATUSError", PyExc_NTSTATUSError);
617         }
618
619         PyExc_WERRORError = PyErr_NewException("samba.WERRORError", PyExc_RuntimeError, NULL);
620         if (PyExc_WERRORError != NULL) {
621                 Py_INCREF(PyExc_WERRORError);
622                 PyModule_AddObject(m, "WERRORError", PyExc_WERRORError);
623         }
624
625         PyExc_HRESULTError = PyErr_NewException("samba.HRESULTError", PyExc_RuntimeError, NULL);
626         if (PyExc_HRESULTError != NULL) {
627                 Py_INCREF(PyExc_HRESULTError);
628                 PyModule_AddObject(m, "HRESULTError", PyExc_HRESULTError);
629         }
630
631         PyExc_DsExtendedError = PyErr_NewException("samba.DsExtendedError", PyExc_RuntimeError, NULL);
632         if (PyExc_DsExtendedError != NULL) {
633                 Py_INCREF(PyExc_DsExtendedError);
634                 PyModule_AddObject(m, "DsExtendedError", PyExc_DsExtendedError);
635         }
636
637         ret = PyModule_AddIntConstant(m, "GKDI_L1_KEY_ITERATION", gkdi_l1_key_iteration);
638         if (ret) {
639                 Py_DECREF(m);
640                 return NULL;
641         }
642         ret = PyModule_AddIntConstant(m, "GKDI_L2_KEY_ITERATION", gkdi_l2_key_iteration);
643         if (ret) {
644                 Py_DECREF(m);
645                 return NULL;
646         }
647         py_obj = PyLong_FromLongLong(gkdi_key_cycle_duration);
648         if (py_obj == NULL) {
649                 Py_DECREF(m);
650                 return NULL;
651         }
652         ret = PyModule_AddObject(m, "GKDI_KEY_CYCLE_DURATION", py_obj);
653         if (ret) {
654                 Py_DECREF(py_obj);
655                 Py_DECREF(m);
656                 return NULL;
657         }
658         py_obj = PyLong_FromLongLong(gkdi_max_clock_skew);
659         if (py_obj == NULL) {
660                 Py_DECREF(m);
661                 return NULL;
662         }
663         ret = PyModule_AddObject(m, "GKDI_MAX_CLOCK_SKEW", py_obj);
664         if (ret) {
665                 Py_DECREF(py_obj);
666                 Py_DECREF(m);
667                 return NULL;
668         }
669
670         return m;
671 }