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