1 /*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Richard P. Curnow 1997-2003
6 * Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 **********************************************************************
22
23 =======================================================================
24
25 Module for parsing various forms of directive and command lines that
26 are common to the configuration file and to the command client.
27
28 */
29
30 #include "config.h"
31
32 #include "sysincl.h"
33
34 #include "cmdparse.h"
35 #include "memory.h"
36 #include "nameserv.h"
37 #include "ntp.h"
38 #include "util.h"
39
40 /* ================================================== */
41
42 int
CPS_ParseNTPSourceAdd(char * line,CPS_NTP_Source * src)43 CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
44 {
45 char *hostname, *cmd;
46 uint32_t ef_type;
47 int n;
48
49 src->port = SRC_DEFAULT_PORT;
50 src->params.minpoll = SRC_DEFAULT_MINPOLL;
51 src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
52 src->params.connectivity = SRC_ONLINE;
53 src->params.auto_offline = 0;
54 src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
55 src->params.burst = 0;
56 src->params.iburst = 0;
57 src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
58 src->params.poll_target = SRC_DEFAULT_POLLTARGET;
59 src->params.version = 0;
60 src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
61 src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
62 src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
63 src->params.filter_length = 0;
64 src->params.interleaved = 0;
65 src->params.sel_options = 0;
66 src->params.nts = 0;
67 src->params.nts_port = SRC_DEFAULT_NTSPORT;
68 src->params.copy = 0;
69 src->params.ext_fields = 0;
70 src->params.authkey = INACTIVE_AUTHKEY;
71 src->params.cert_set = SRC_DEFAULT_CERTSET;
72 src->params.max_delay = SRC_DEFAULT_MAXDELAY;
73 src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
74 src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
75 src->params.min_delay = 0.0;
76 src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
77 src->params.offset = 0.0;
78
79 hostname = line;
80 line = CPS_SplitWord(line);
81
82 if (!*hostname)
83 return 0;
84
85 src->name = hostname;
86
87 /* Parse options */
88 for (; *line; line += n) {
89 cmd = line;
90 line = CPS_SplitWord(line);
91 n = 0;
92
93 if (!strcasecmp(cmd, "auto_offline")) {
94 src->params.auto_offline = 1;
95 } else if (!strcasecmp(cmd, "burst")) {
96 src->params.burst = 1;
97 } else if (!strcasecmp(cmd, "copy")) {
98 src->params.copy = 1;
99 } else if (!strcasecmp(cmd, "iburst")) {
100 src->params.iburst = 1;
101 } else if (!strcasecmp(cmd, "offline")) {
102 src->params.connectivity = SRC_OFFLINE;
103 } else if (!strcasecmp(cmd, "noselect")) {
104 src->params.sel_options |= SRC_SELECT_NOSELECT;
105 } else if (!strcasecmp(cmd, "prefer")) {
106 src->params.sel_options |= SRC_SELECT_PREFER;
107 } else if (!strcasecmp(cmd, "require")) {
108 src->params.sel_options |= SRC_SELECT_REQUIRE;
109 } else if (!strcasecmp(cmd, "trust")) {
110 src->params.sel_options |= SRC_SELECT_TRUST;
111 } else if (!strcasecmp(cmd, "certset")) {
112 if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
113 return 0;
114 } else if (!strcasecmp(cmd, "key")) {
115 if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
116 src->params.authkey == INACTIVE_AUTHKEY)
117 return 0;
118 } else if (!strcasecmp(cmd, "asymmetry")) {
119 if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
120 return 0;
121 } else if (!strcasecmp(cmd, "extfield")) {
122 if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
123 return 0;
124 switch (ef_type) {
125 case NTP_EF_EXP1:
126 src->params.ext_fields |= NTP_EF_FLAG_EXP1;
127 break;
128 default:
129 return 0;
130 }
131 } else if (!strcasecmp(cmd, "filter")) {
132 if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
133 return 0;
134 } else if (!strcasecmp(cmd, "maxdelay")) {
135 if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
136 return 0;
137 } else if (!strcasecmp(cmd, "maxdelayratio")) {
138 if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
139 return 0;
140 } else if (!strcasecmp(cmd, "maxdelaydevratio")) {
141 if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
142 return 0;
143 } else if (!strcasecmp(cmd, "maxpoll")) {
144 if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
145 return 0;
146 } else if (!strcasecmp(cmd, "maxsamples")) {
147 if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
148 return 0;
149 } else if (!strcasecmp(cmd, "maxsources")) {
150 if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
151 return 0;
152 } else if (!strcasecmp(cmd, "mindelay")) {
153 if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
154 return 0;
155 } else if (!strcasecmp(cmd, "minpoll")) {
156 if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
157 return 0;
158 } else if (!strcasecmp(cmd, "minsamples")) {
159 if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
160 return 0;
161 } else if (!strcasecmp(cmd, "minstratum")) {
162 if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
163 return 0;
164 } else if (!strcasecmp(cmd, "nts")) {
165 src->params.nts = 1;
166 } else if (!strcasecmp(cmd, "ntsport")) {
167 if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
168 return 0;
169 } else if (!strcasecmp(cmd, "offset")) {
170 if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
171 return 0;
172 } else if (!strcasecmp(cmd, "port")) {
173 if (sscanf(line, "%d%n", &src->port, &n) != 1)
174 return 0;
175 } else if (!strcasecmp(cmd, "polltarget")) {
176 if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
177 return 0;
178 } else if (!strcasecmp(cmd, "presend")) {
179 if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
180 return 0;
181 } else if (!strcasecmp(cmd, "version")) {
182 if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
183 return 0;
184 } else if (!strcasecmp(cmd, "xleave")) {
185 src->params.interleaved = 1;
186 } else {
187 return 0;
188 }
189 }
190
191 return 1;
192 }
193
194 /* ================================================== */
195
196 int
CPS_ParseAllowDeny(char * line,int * all,IPAddr * ip,int * subnet_bits)197 CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
198 {
199 char *p, *net, *slash;
200 uint32_t a, b, c;
201 int bits, len, n;
202
203 p = CPS_SplitWord(line);
204
205 if (strcmp(line, "all") == 0) {
206 *all = 1;
207 net = p;
208 p = CPS_SplitWord(p);
209 } else {
210 *all = 0;
211 net = line;
212 }
213
214 /* Make sure there are no other arguments */
215 if (*p)
216 return 0;
217
218 /* No specified address or network means all IPv4 and IPv6 addresses */
219 if (!*net) {
220 ip->family = IPADDR_UNSPEC;
221 *subnet_bits = 0;
222 return 1;
223 }
224
225 slash = strchr(net, '/');
226 if (slash) {
227 if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0)
228 return 0;
229 *slash = '\0';
230 } else {
231 bits = -1;
232 }
233
234 if (UTI_StringToIP(net, ip)) {
235 if (bits >= 0)
236 *subnet_bits = bits;
237 else
238 *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
239 return 1;
240 }
241
242 /* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal
243 numbers. This is different than the numbers-and-dots notation accepted
244 by inet_aton()! */
245
246 a = b = c = 0;
247 n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len);
248
249 if (n > 0 && !net[len]) {
250 if (a > 255 || b > 255 || c > 255)
251 return 0;
252
253 ip->family = IPADDR_INET4;
254 ip->addr.in4 = (a << 24) | (b << 16) | (c << 8);
255
256 if (bits >= 0)
257 *subnet_bits = bits;
258 else
259 *subnet_bits = n * 8;
260
261 return 1;
262 }
263
264 /* The last possibility is a hostname */
265 if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) {
266 *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
267 return 1;
268 }
269
270 return 0;
271 }
272
273 /* ================================================== */
274
275 int
CPS_ParseLocal(char * line,int * stratum,int * orphan,double * distance)276 CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
277 {
278 int n;
279 char *cmd;
280
281 *stratum = 10;
282 *distance = 1.0;
283 *orphan = 0;
284
285 while (*line) {
286 cmd = line;
287 line = CPS_SplitWord(line);
288
289 if (!strcasecmp(cmd, "stratum")) {
290 if (sscanf(line, "%d%n", stratum, &n) != 1 ||
291 *stratum >= NTP_MAX_STRATUM || *stratum <= 0)
292 return 0;
293 } else if (!strcasecmp(cmd, "orphan")) {
294 *orphan = 1;
295 n = 0;
296 } else if (!strcasecmp(cmd, "distance")) {
297 if (sscanf(line, "%lf%n", distance, &n) != 1)
298 return 0;
299 } else {
300 return 0;
301 }
302
303 line += n;
304 }
305
306 return 1;
307 }
308
309 /* ================================================== */
310
311 void
CPS_NormalizeLine(char * line)312 CPS_NormalizeLine(char *line)
313 {
314 char *p, *q;
315 int space = 1, first = 1;
316
317 /* Remove white-space at beginning and replace white-spaces with space char */
318 for (p = q = line; *p; p++) {
319 if (isspace((unsigned char)*p)) {
320 if (!space)
321 *q++ = ' ';
322 space = 1;
323 continue;
324 }
325
326 /* Discard comment lines */
327 if (first && strchr("!;#%", *p))
328 break;
329
330 *q++ = *p;
331 space = first = 0;
332 }
333
334 /* Strip trailing space */
335 if (q > line && q[-1] == ' ')
336 q--;
337
338 *q = '\0';
339 }
340
341 /* ================================================== */
342
343 char *
CPS_SplitWord(char * line)344 CPS_SplitWord(char *line)
345 {
346 char *p = line, *q = line;
347
348 /* Skip white-space before the word */
349 while (*q && isspace((unsigned char)*q))
350 q++;
351
352 /* Move the word to the beginning */
353 while (*q && !isspace((unsigned char)*q))
354 *p++ = *q++;
355
356 /* Find the next word */
357 while (*q && isspace((unsigned char)*q))
358 q++;
359
360 *p = '\0';
361
362 /* Return pointer to the next word or NUL */
363 return q;
364 }
365
366 /* ================================================== */
367
368 int
CPS_ParseKey(char * line,uint32_t * id,const char ** type,char ** key)369 CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
370 {
371 char *s1, *s2, *s3, *s4;
372
373 s1 = line;
374 s2 = CPS_SplitWord(s1);
375 s3 = CPS_SplitWord(s2);
376 s4 = CPS_SplitWord(s3);
377
378 /* Require two or three words */
379 if (!*s2 || *s4)
380 return 0;
381
382 if (sscanf(s1, "%"SCNu32, id) != 1)
383 return 0;
384
385 if (*s3) {
386 *type = s2;
387 *key = s3;
388 } else {
389 *type = "MD5";
390 *key = s2;
391 }
392
393 return 1;
394 }
395