1 /*
2  * Copyright the NTPsec project contributors
3  * SPDX-License-Identifier: BSD-2-Clause
4  *
5  * Python binding for selected libntp library functions
6  */
7 
8 /* This include has to come early or we get warnings from redefining
9  * _POSIX_C_SOURCE and _XOPEN_SOURCE on some systems.
10  */
11 #define PY_SSIZE_T_CLEAN
12 #include <Python.h>
13 
14 #include "config.h"
15 
16 #include "ntp_machine.h"
17 #include "ntpd.h"
18 #include "ntp_io.h"
19 #include "ntp_fp.h"
20 #include "ntp_stdlib.h"
21 #include "ntp_syslog.h"
22 #include "timespecops.h"
23 
24 #include "ntp_config.h"
25 #include "ntp_assert.h"
26 
27 #include "ntp_control.h"
28 
29 #include "pymodule-mac.h"
30 
31 #include "python_compatibility.h"
32 
33 /* Don't include anything from OpenSSL */
34 
35 const char *progname = "libntpc";
36 
37 /*
38  * Client utility functions
39  */
40 
41 static PyObject *
ntpc_setprogname(PyObject * self,PyObject * args)42 ntpc_setprogname(PyObject *self, PyObject *args)
43 {
44 	char *s;
45 	UNUSED_ARG(self);
46 	if (!PyArg_ParseTuple(args, "s", &s))
47 		return NULL;
48 	progname = strdup(s);
49 
50 	/*
51 	 * This function is only called from clients.  Therefore
52 	 * log to stderr rather than syslog, and suppress logfile
53 	 * impediments.  If we ever want finer-grained control, that
54 	 * will be easily implemented with additional arguments.
55 	 */
56 	syslogit = false;	/* don't log messages to syslog */
57 	termlogit = true;	/* duplicate to stdout/err */
58 	termlogit_pid = false;
59 	msyslog_include_timestamp = false;
60 
61 	Py_RETURN_NONE;
62 }
63 
64 static PyObject *
ntpc_statustoa(PyObject * self,PyObject * args)65 ntpc_statustoa(PyObject *self, PyObject *args)
66 {
67 	int status1, status2;
68 	char *gs;
69 
70 	UNUSED_ARG(self);
71 	if (!PyArg_ParseTuple(args, "ii", &status1, &status2))
72 		return NULL;
73 	gs = statustoa(status1, status2);
74 	return Py_BuildValue("s", gs);
75 }
76 
77 static PyObject *
ntpc_prettydate(PyObject * self,PyObject * args)78 ntpc_prettydate(PyObject *self, PyObject *args)
79 {
80 	char *s;
81 	l_fp ts;
82 
83 	UNUSED_ARG(self);
84 	if (!PyArg_ParseTuple(args, "s", &s))
85 		return NULL;
86 	if (hextolfp(s+2, &ts))
87 		return Py_BuildValue("s", prettydate(ts));
88 	else {
89 		PyErr_SetString(PyExc_ValueError, "ill-formed hex date");
90 		return NULL;
91 	}
92 }
93 
94 static PyObject *
ntpc_lfptofloat(PyObject * self,PyObject * args)95 ntpc_lfptofloat(PyObject *self, PyObject *args)
96 {
97 	char *s;
98 	l_fp ts;
99 	struct timespec tt;
100 
101 	UNUSED_ARG(self);
102 	if (!PyArg_ParseTuple(args, "s", &s))
103 		return NULL;
104 	if (!hextolfp(s+2, &ts)) {
105 		PyErr_SetString(PyExc_ValueError, "ill-formed hex date");
106 		return NULL;
107 	}
108 	tt = lfp_stamp_to_tspec(ts, time(NULL));
109 	return Py_BuildValue("d", tt.tv_sec + tt.tv_nsec * S_PER_NS);
110 }
111 
112 static PyObject *
ntpc_set_tod(PyObject * self,PyObject * args)113 ntpc_set_tod(PyObject *self, PyObject *args)
114 {
115 	struct timespec ts;
116 
117 	UNUSED_ARG(self);
118 	if (!PyArg_ParseTuple(args, "ii", &ts.tv_sec, &ts.tv_nsec))
119 		return NULL;
120 	return Py_BuildValue("d", ntp_set_tod(&ts));
121 }
122 
123 static PyObject *
ntpc_adj_systime(PyObject * self,PyObject * args)124 ntpc_adj_systime(PyObject *self, PyObject *args)
125 {
126 	double adjustment;
127 
128 	UNUSED_ARG(self);
129 	if (!PyArg_ParseTuple(args, "d", &adjustment))
130 		return NULL;
131 	return Py_BuildValue("d", adj_systime(adjustment, adjtime) ? 1 : 0);
132 }
133 
134 static PyObject *
ntpc_step_systime(PyObject * self,PyObject * args)135 ntpc_step_systime(PyObject *self, PyObject *args)
136 {
137 	double adjustment;
138 	doubletime_t full_adjustment;
139 
140 	UNUSED_ARG(self);
141 	/*
142 	 * What we really want is for Python to parse a long double.
143 	 * As this is, it's a potential source of problems in the Python
144 	 * utilities if and when the time difference between the Unix epoch
145 	 * and now exceeds the range of a double.
146 	 */
147 	if (!PyArg_ParseTuple(args, "d", &adjustment))
148 		return NULL;
149 	full_adjustment = adjustment;
150 	return Py_BuildValue("d", step_systime(full_adjustment, ntp_set_tod));
151 }
152 
153 /* --------------------------------------------------------------- */
154 /* Hook for CMAC/HMAC
155  * Not really part of libntp, but this is a handy place to put it.
156  *
157  * The worker parts have been moved to another module because of
158  * name clash between python and OpenSSL.  Both use freefunc.
159  *
160  * All Python stuff here. All OpenSSL stuff in pymodule-mac.c
161  *
162  */
163 
164 
165 /* xx = ntp.ntpc.checkname(name)
166  * returns None if algorithm name is invalid. */
167 
168 static PyObject *
ntpc_checkname(PyObject * self,PyObject * args)169 ntpc_checkname(PyObject *self, PyObject *args)
170 {
171 	const char *name;
172 	UNUSED_ARG(self);
173 	int length;
174 
175 	if (!PyArg_ParseTuple(args, "s", &name))
176 		Py_RETURN_NONE;
177 
178 	length = do_checkname(name);
179 
180 	if (length != 0) return Py_BuildValue("i", 1);
181 
182 	Py_RETURN_NONE;
183 }
184 
185 
186 /* mac = ntp.ntpc.mac(data, key, name) */
187 
188 static PyObject *
ntpc_mac(PyObject * self,PyObject * args)189 ntpc_mac(PyObject *self, PyObject *args)
190 {
191 	UNUSED_ARG(self);
192 	uint8_t *data;
193 	Py_ssize_t datalen;
194 	uint8_t *key;
195 	Py_ssize_t keylen;
196 	char *name;
197 	uint8_t mac[MAX_MAC_LENGTH];
198 	size_t maclen;
199 
200 
201 #if PY_MAJOR_VERSION >= 3
202 	if (!PyArg_ParseTuple(args, "y#y#s",
203 			&data, &datalen, &key, &keylen, &name))
204 #else
205 	if (!PyArg_ParseTuple(args, "s#s#s",
206 			&data, &datalen, &key, &keylen, &name))
207 #endif
208 		Py_RETURN_NONE;
209 
210 	do_mac(name,
211 		data, datalen,
212       		key, keylen,
213  	        mac, &maclen);
214 
215 	if (maclen == 0)
216                 Py_RETURN_NONE;
217 
218 #if PY_MAJOR_VERSION >= 3
219 	return Py_BuildValue("y#", &mac, maclen);
220 #else
221 	return Py_BuildValue("s#", &mac, maclen);
222 #endif
223 }
224 
225 /* List of functions defined in the module */
226 
227 static PyMethodDef ntpc_methods[] = {
228     {"setprogname",    ntpc_setprogname,  	METH_VARARGS,
229      PyDoc_STR("Set program name for logging purposes.")},
230     {"statustoa",      	ntpc_statustoa,  	METH_VARARGS,
231      PyDoc_STR("Status string display from peer status word.")},
232     {"prettydate",     	ntpc_prettydate,  	METH_VARARGS,
233      PyDoc_STR("Convert a time stamp to something readable.")},
234     {"lfptofloat",     	ntpc_lfptofloat,  	METH_VARARGS,
235      PyDoc_STR("NTP l_fp to Python-style float time.")},
236     {"set_tod",     	ntpc_set_tod,   	METH_VARARGS,
237      PyDoc_STR("Set time to nanosecond precision.")},
238     {"adj_systime",    	ntpc_adj_systime,   	METH_VARARGS,
239      PyDoc_STR("Adjust system time by slewing.")},
240     {"step_systime",    ntpc_step_systime,   	METH_VARARGS,
241      PyDoc_STR("Adjust system time by stepping.")},
242     {"checkname",       ntpc_checkname,   	METH_VARARGS,
243      PyDoc_STR("Check if name is a valid algorithm name")},
244     {"mac",             ntpc_mac,   		METH_VARARGS,
245      PyDoc_STR("Compute HMAC or CMAC from data, key, and algorithm name")},
246     {NULL,		NULL, 0, NULL}		/* sentinel */
247 };
248 
249 PyDoc_STRVAR(module_doc,
250 "Python wrapper for selected libntp C library routines.\n\
251 ");
252 
253 /* banishes pointless compiler warnings on various Python versions */
254 extern PyMODINIT_FUNC initntpc(void);
255 extern PyMODINIT_FUNC PyInit_ntpc(void);
256 
257 // cppcheck-suppress unusedFunction
NTPSEC_PY_MODULE_INIT(ntpc)258 NTPSEC_PY_MODULE_INIT(ntpc)
259 {
260 	PyObject *m;
261 
262 	/* Create the module and add the functions */
263 	NTPSEC_PY_MODULE_DEF(m, "ntpc", module_doc, ntpc_methods)
264 
265 	    /* for statustoa() */
266 	    PyModule_AddIntConstant(m, "TYPE_SYS", TYPE_SYS);
267 	PyModule_AddIntConstant(m, "TYPE_PEER", TYPE_PEER);
268 	PyModule_AddIntConstant(m, "TYPE_CLOCK", TYPE_CLOCK);
269 
270 	if (m == NULL)
271 		return NTPSEC_PY_MODULE_ERROR_VAL;
272 
273 	return NTPSEC_PY_MODULE_SUCCESS_VAL(m);
274 }
275