conf+cmdparse: check sanity of configured integer values

Verify that integer values specified in the configuration are sane:
interval log2 values are between -32 and 32, ports between 0 and 65535,
stratum between 0 and 16, values that should not be negative are not
negative, numbers that specify large intervals in seconds fit in the
32-bit integer, numbers don't have non-digit characters, etc.
This commit is contained in:
Miroslav Lichvar
2025-03-19 14:35:54 +01:00
parent e694ae769a
commit 8fd386f287
2 changed files with 69 additions and 57 deletions

View File

@@ -39,6 +39,11 @@
/* ================================================== */
#define SSCANF_IN_RANGE(s, f, x, n, min, max) \
(sscanf(s, f, (x), (n)) == 1 && *(x) >= (min) && *(x) <= (max))
/* ================================================== */
CPS_Status
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
@@ -126,7 +131,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
return CPS_InvalidValue;
}
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.filter_length, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "ipv4")) {
src->family = IPADDR_INET4;
@@ -145,13 +150,13 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.maxpoll, &n, -32, 32))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_samples, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_sources, &n, 1, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "mindelay")) {
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
@@ -160,30 +165,30 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_samples, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_stratum, &n, 0, NTP_MAX_STRATUM))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "nts")) {
src->params.nts = 1;
} else if (!strcasecmp(cmd, "ntsport")) {
if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.nts_port, &n, 0, 65535))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "offset")) {
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%d%n", &src->port, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->port, &n, 0, 65535))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.poll_target, &n, 1, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.presend_minpoll, &n, -32, 32))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.version, &n, 1, NTP_VERSION))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
@@ -314,8 +319,7 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", stratum, &n) != 1 ||
*stratum >= NTP_MAX_STRATUM || *stratum <= 0)
if (!SSCANF_IN_RANGE(line, "%d%n", stratum, &n, 1, NTP_MAX_STRATUM - 1))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "orphan")) {
*orphan = 1;

96
conf.c
View File

@@ -50,14 +50,17 @@
#define MAX_CONF_DIRS 10
#define MAX_INCLUDE_LEVEL 10
#define SSCANF_IN_RANGE(s, f, x, n, min, max) \
(sscanf(s, f, (x), (n)) == 1 && *(x) >= (min) && *(x) <= (max))
/* ================================================== */
/* Forward prototypes */
static void parse_string(char *line, char **result);
static void parse_int(char *line, int *result);
static void parse_int(char *line, int *result, int min, int max);
static void parse_double(char *line, double *result);
static void parse_null(char *line, int *result);
static void parse_ints(char *line, ARR_Instance array);
static void parse_ints(char *line, ARR_Instance array, int min, int max);
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
static void parse_authselectmode(char *);
@@ -440,7 +443,7 @@ CNF_Initialise(int r, int client_only)
nts_aeads = ARR_CreateInstance(sizeof (int));
snprintf(buf, sizeof (buf), DEFAULT_NTS_AEADS);
parse_ints(buf, nts_aeads);
parse_ints(buf, nts_aeads, 0, INT_MAX);
nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
nts_server_key_files = ARR_CreateInstance(sizeof (char *));
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
@@ -597,7 +600,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
printf("%s%s%s\n", command, p[0] != '\0' ? " " : "", p);
if (!strcasecmp(command, "acquisitionport")) {
parse_int(p, &acquisition_port);
parse_int(p, &acquisition_port, 0, 65535);
} else if (!strcasecmp(command, "allow")) {
parse_allow_deny(p, ntp_restrictions, 1);
} else if (!strcasecmp(command, "authselectmode")) {
@@ -625,7 +628,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "cmddeny")) {
parse_allow_deny(p, cmd_restrictions, 0);
} else if (!strcasecmp(command, "cmdport")) {
parse_int(p, &cmd_port);
parse_int(p, &cmd_port, 0, 65535);
} else if (!strcasecmp(command, "cmdratelimit")) {
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
&cmd_ratelimit_burst, &cmd_ratelimit_leak, NULL);
@@ -640,7 +643,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "driftfile")) {
parse_driftfile(p);
} else if (!strcasecmp(command, "dscp")) {
parse_int(p, &ntp_dscp);
parse_int(p, &ntp_dscp, 0, 63);
} else if (!strcasecmp(command, "dumpdir")) {
parse_string(p, &dumpdir);
} else if (!strcasecmp(command, "dumponexit")) {
@@ -672,7 +675,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "log")) {
parse_log(p);
} else if (!strcasecmp(command, "logbanner")) {
parse_int(p, &log_banner);
parse_int(p, &log_banner, 0, INT_MAX);
} else if (!strcasecmp(command, "logchange")) {
parse_double(p, &log_change_threshold);
} else if (!strcasecmp(command, "logdir")) {
@@ -694,19 +697,19 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "maxjitter")) {
parse_double(p, &max_jitter);
} else if (!strcasecmp(command, "maxntsconnections")) {
parse_int(p, &nts_server_connections);
parse_int(p, &nts_server_connections, 1, INT_MAX);
} else if (!strcasecmp(command, "maxsamples")) {
parse_int(p, &max_samples);
parse_int(p, &max_samples, 0, INT_MAX);
} else if (!strcasecmp(command, "maxslewrate")) {
parse_double(p, &max_slew_rate);
} else if (!strcasecmp(command, "maxupdateskew")) {
parse_double(p, &max_update_skew);
} else if (!strcasecmp(command, "minsamples")) {
parse_int(p, &min_samples);
parse_int(p, &min_samples, 0, INT_MAX);
} else if (!strcasecmp(command, "minsources")) {
parse_int(p, &min_sources);
parse_int(p, &min_sources, 1, INT_MAX);
} else if (!strcasecmp(command, "nocerttimecheck")) {
parse_int(p, &no_cert_time_check);
parse_int(p, &no_cert_time_check, 0, INT_MAX);
} else if (!strcasecmp(command, "noclientlog")) {
parse_null(p, &no_client_log);
} else if (!strcasecmp(command, "nosystemcert")) {
@@ -714,7 +717,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "ntpsigndsocket")) {
parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "ntsaeads")) {
parse_ints(p, nts_aeads);
parse_ints(p, nts_aeads, 0, INT_MAX);
} else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
@@ -724,13 +727,13 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "ntsntpserver")) {
parse_string(p, &nts_ntp_server);
} else if (!strcasecmp(command, "ntsport")) {
parse_int(p, &nts_server_port);
parse_int(p, &nts_server_port, 0, 65535);
} else if (!strcasecmp(command, "ntsprocesses")) {
parse_int(p, &nts_server_processes);
parse_int(p, &nts_server_processes, 0, 1000);
} else if (!strcasecmp(command, "ntsrefresh")) {
parse_int(p, &nts_refresh);
parse_int(p, &nts_refresh, 0, INT_MAX);
} else if (!strcasecmp(command, "ntsrotate")) {
parse_int(p, &nts_rotate);
parse_int(p, &nts_rotate, 0, INT_MAX);
} else if (!strcasecmp(command, "ntsservercert")) {
parse_ntsserver(p, nts_server_cert_files);
} else if (!strcasecmp(command, "ntsserverkey")) {
@@ -746,18 +749,18 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "pool")) {
parse_source(p, command, 1);
} else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port);
parse_int(p, &ntp_port, 0, 65535);
} else if (!strcasecmp(command, "ptpdomain")) {
parse_int(p, &ptp_domain);
parse_int(p, &ptp_domain, 0, 255);
} else if (!strcasecmp(command, "ptpport")) {
parse_int(p, &ptp_port);
parse_int(p, &ptp_port, 0, 65535);
} else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
&ntp_ratelimit_burst, &ntp_ratelimit_leak, &ntp_ratelimit_kod);
} else if (!strcasecmp(command, "refclock")) {
parse_refclock(p);
} else if (!strcasecmp(command, "refresh")) {
parse_int(p, &refresh);
parse_int(p, &refresh, 0, INT_MAX);
} else if (!strcasecmp(command, "reselectdist")) {
parse_double(p, &reselect_distance);
} else if (!strcasecmp(command, "rtcautotrim")) {
@@ -771,7 +774,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "rtcsync")) {
parse_null(p, &rtc_sync);
} else if (!strcasecmp(command, "sched_priority")) {
parse_int(p, &sched_priority);
parse_int(p, &sched_priority, 0, 100);
} else if (!strcasecmp(command, "server")) {
parse_source(p, command, 1);
} else if (!strcasecmp(command, "smoothtime")) {
@@ -809,12 +812,21 @@ parse_string(char *line, char **result)
/* ================================================== */
static void
parse_int(char *line, int *result)
parse_int(char *line, int *result, int min, int max)
{
char *end;
long r;
check_number_of_args(line, 1);
if (sscanf(line, "%d", result) != 1) {
errno = 0;
r = strtol(line, &end, 10);
if (errno != 0 || *end != '\0')
command_parse_error();
}
if (r < min || r > max)
other_parse_error("Invalid value %ld in %s directive (min %d, max %d)",
r, processed_command, min, max);
*result = r;
}
/* ================================================== */
@@ -840,7 +852,7 @@ parse_null(char *line, int *result)
/* ================================================== */
static void
parse_ints(char *line, ARR_Instance array)
parse_ints(char *line, ARR_Instance array, int min, int max)
{
char *s;
int v;
@@ -850,7 +862,7 @@ parse_ints(char *line, ARR_Instance array)
while (*line) {
s = line;
line = CPS_SplitWord(line);
parse_int(s, &v);
parse_int(s, &v, min, max);
ARR_AppendElement(array, &v);
}
}
@@ -922,7 +934,7 @@ parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak,
while (*line) {
opt = line;
line = CPS_SplitWord(line);
if (sscanf(line, "%d%n", &val, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &val, &n, -32, 32)) {
command_parse_error();
return;
}
@@ -1001,31 +1013,28 @@ parse_refclock(char *line)
if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
break;
} else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &poll, &n, -32, 32))
break;
}
} else if (!strcasecmp(cmd, "dpoll")) {
if (sscanf(line, "%d%n", &dpoll, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &dpoll, &n, -32, 32))
break;
}
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &filter_length, &n, 0, INT_MAX))
break;
}
} else if (!strcasecmp(cmd, "local")) {
n = 0;
local = 1;
} else if (!strcasecmp(cmd, "rate")) {
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &pps_rate, &n, 1, INT_MAX))
break;
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &min_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &min_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "maxlockage")) {
if (sscanf(line, "%d%n", &max_lock_age, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &max_lock_age, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &max_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &max_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "offset")) {
if (sscanf(line, "%lf%n", &offset, &n) != 1)
@@ -1043,8 +1052,7 @@ parse_refclock(char *line)
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
break;
} else if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", &stratum, &n) != 1 ||
stratum >= NTP_MAX_STRATUM || stratum < 0)
if (!SSCANF_IN_RANGE(line, "%d%n", &stratum, &n, 0, NTP_MAX_STRATUM - 1))
break;
} else if (!strcasecmp(cmd, "tai")) {
n = 0;
@@ -1609,17 +1617,17 @@ parse_hwtimestamp(char *line)
line = CPS_SplitWord(line);
if (!strcasecmp(p, "maxsamples")) {
if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->max_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->minpoll, &n, -32, 32))
break;
} else if (!strcasecmp(p, "maxpoll")) {
if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->maxpoll, &n, -32, 32))
break;
maxpoll_set = 1;
} else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->min_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(p, "precision")) {
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)