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